|
44 | 44 | from __future__ import annotations |
45 | 45 |
|
46 | 46 | import abc |
47 | | -import imp |
48 | 47 | import importlib |
| 48 | +import importlib.util |
49 | 49 | import inspect |
50 | 50 | import itertools |
51 | 51 | import os |
| 52 | +import sys |
52 | 53 | from typing import Optional |
53 | 54 |
|
54 | 55 | # TODO: refactor along with usage in sopel.__init__ in py3.8+ world |
|
57 | 58 | from sopel import __version__ as release, loader |
58 | 59 | from . import exceptions |
59 | 60 |
|
60 | | -try: |
61 | | - reload = importlib.reload |
62 | | -except AttributeError: |
63 | | - # py2: no reload function |
64 | | - # TODO: imp is deprecated, to be removed when py2 support is dropped |
65 | | - reload = imp.reload |
66 | | - |
67 | 61 |
|
68 | 62 | class AbstractPluginHandler(abc.ABC): |
69 | 63 | """Base class for plugin handlers. |
@@ -334,7 +328,7 @@ def reload(self): |
334 | 328 |
|
335 | 329 | This method assumes the plugin is already loaded. |
336 | 330 | """ |
337 | | - self._module = reload(self._module) |
| 331 | + self._module = importlib.reload(self._module) |
338 | 332 |
|
339 | 333 | def is_loaded(self): |
340 | 334 | return self._module is not None |
@@ -435,45 +429,31 @@ def __init__(self, filename): |
435 | 429 |
|
436 | 430 | if good_file: |
437 | 431 | name = os.path.basename(filename)[:-3] |
438 | | - module_type = imp.PY_SOURCE |
| 432 | + spec = importlib.util.spec_from_file_location( |
| 433 | + name, |
| 434 | + filename, |
| 435 | + ) |
439 | 436 | elif good_dir: |
440 | 437 | name = os.path.basename(filename) |
441 | | - module_type = imp.PKG_DIRECTORY |
| 438 | + spec = importlib.util.spec_from_file_location( |
| 439 | + name, |
| 440 | + os.path.join(filename, '__init__.py'), |
| 441 | + submodule_search_locations=filename, |
| 442 | + ) |
442 | 443 | else: |
443 | 444 | raise exceptions.PluginError('Invalid Sopel plugin: %s' % filename) |
444 | 445 |
|
445 | 446 | self.filename = filename |
446 | 447 | self.path = filename |
447 | | - self.module_type = module_type |
| 448 | + self.module_spec = spec |
448 | 449 |
|
449 | 450 | super().__init__(name) |
450 | 451 |
|
451 | 452 | def _load(self): |
452 | | - # The current implementation uses `imp.load_module` to perform the |
453 | | - # load action, which also reloads the module. However, `imp` is |
454 | | - # deprecated in Python 3, so that might need to be changed when the |
455 | | - # support for Python 2 is dropped. |
456 | | - # |
457 | | - # However, the solution for Python 3 is non-trivial, since the |
458 | | - # `importlib` built-in module does not have a similar function, |
459 | | - # therefore requires to dive into its public internals |
460 | | - # (``importlib.machinery`` and ``importlib.util``). |
461 | | - # |
462 | | - # All of that is doable, but represents a lot of work. As long as |
463 | | - # Python 2 is supported, we can keep it for now. |
464 | | - # |
465 | | - # TODO: switch to ``importlib`` when Python2 support is dropped. |
466 | | - if self.module_type == imp.PY_SOURCE: |
467 | | - with open(self.path) as mod: |
468 | | - description = ('.py', 'U', self.module_type) |
469 | | - mod = imp.load_module(self.name, mod, self.path, description) |
470 | | - elif self.module_type == imp.PKG_DIRECTORY: |
471 | | - description = ('', '', self.module_type) |
472 | | - mod = imp.load_module(self.name, None, self.path, description) |
473 | | - else: |
474 | | - raise TypeError('Unsupported module type') |
475 | | - |
476 | | - return mod |
| 453 | + module = importlib.util.module_from_spec(self.module_spec) |
| 454 | + sys.modules[self.name] = module |
| 455 | + self.module_spec.loader.exec_module(module) |
| 456 | + return module |
477 | 457 |
|
478 | 458 | def get_meta_description(self): |
479 | 459 | """Retrieve a meta description for the plugin. |
|
0 commit comments