|
11 | 11 | import inspect |
12 | 12 | import itertools |
13 | 13 | import collections |
14 | | -import pkgutil |
15 | 14 | import shutil |
16 | 15 | from io import StringIO |
17 | 16 | import pickle |
|
29 | 28 | import addonAPIVersion |
30 | 29 | from . import addonVersionCheck |
31 | 30 | from .addonVersionCheck import isAddonCompatible |
| 31 | +import importlib |
| 32 | +from types import ModuleType |
32 | 33 | import extensionPoints |
| 34 | +from keyword import iskeyword |
33 | 35 |
|
34 | 36 |
|
35 | 37 | MANIFEST_FILENAME = "manifest.ini" |
@@ -475,25 +477,28 @@ def _getPathForInclusionInPackage(self, package): |
475 | 477 | extension_path = os.path.join(self.path, package.__name__) |
476 | 478 | return extension_path |
477 | 479 |
|
478 | | - def loadModule(self, name): |
| 480 | + def loadModule(self, name: str) -> ModuleType: |
479 | 481 | """ loads a python module from the addon directory |
480 | 482 | @param name: the module name |
481 | | - @type name: string |
482 | | - @returns the python module with C{name} |
483 | | - @rtype python module |
484 | 483 | """ |
485 | | - log.debug("Importing module %s from plugin %s", name, self.name) |
486 | | - importer = pkgutil.ImpImporter(self.path) |
487 | | - loader = importer.find_module(name) |
488 | | - if not loader: |
489 | | - return None |
| 484 | + splitName = name.split('.') |
| 485 | + moduleName = splitName[-1] |
| 486 | + path = os.path.join(self.path, *splitName[:-1]) |
| 487 | + if any(n for n in splitName if not n.isidentifier() or iskeyword(n)): |
| 488 | + raise ValueError(f"{name} is an invalid python module name") |
| 489 | + log.debug(f"Importing module {name} from plugin {self!r}") |
490 | 490 | # Create a qualified full name to avoid modules with the same name on sys.modules. |
491 | | - fullname = "addons.%s.%s" % (self.name, name) |
492 | | - try: |
493 | | - return loader.load_module(fullname) |
494 | | - except ImportError: |
495 | | - # in this case return None, any other error throw to be handled elsewhere |
| 491 | + fullName = f"addons.{self.name}.{name}" |
| 492 | + if fullName in sys.modules: |
| 493 | + return importlib.import_module(fullName) |
| 494 | + spec = importlib.machinery.PathFinder.find_spec(fullName, [path]) |
| 495 | + if not spec: |
496 | 496 | return None |
| 497 | + mod = importlib.util.module_from_spec(spec) |
| 498 | + sys.modules[fullName] = mod |
| 499 | + if spec.loader: |
| 500 | + spec.loader.exec_module(mod) |
| 501 | + return mod |
497 | 502 |
|
498 | 503 | def getTranslationsInstance(self, domain='nvda'): |
499 | 504 | """ Gets the gettext translation instance for this add-on. |
|
0 commit comments