Search code examples
pythondynamicimportmodulepython-importlib

Python use importlib to import a module from a package directory


I'm trying to import PySide2 dynamically with importlib, because statically with import isn't sufficient for my application. I need importlib because in the end it will be used with pyinstaller to create a single executable that can import PySide2 dynamically, not just from the single executable.

I've copied an entire PySide2 package directory that was downloaded with pip. This PySide2 version is older than the one that I'm using by default, and when the "PySide2" directory is present in my source directory, I want to use it instead.

Based on the answer in this post, I have the following code to perform the import:

import os, sys
print("Import test")
if os.path.isdir('./PySide2/'):
    print('Importing local PySide2')
    import importlib.util

    MODULE_PATH = "./PySide2/__init__.py"
    MODULE_NAME = "PySide2"

    spec = importlib.util.spec_from_file_location(MODULE_NAME, MODULE_PATH)
    print("spec.name is " + str(spec.name))
    module = importlib.util.module_from_spec(spec)
    sys.modules[spec.name] = module 
    print(sys.modules)
    spec.loader.exec_module(module)

    print(PySide2.__version__)
else:
    print('Importing system PySide2')
    import PySide2
    print(PySide2.__version__)

But when I run it, I get NameError: name 'PySide2' is not defined, even though I see in sys.modules that PySide2 is present (scroll all the way to the right in the output below to see it is present):

$ python importTest.py
Import test
Importing local PySide2
spec.name is PySide2
{'sys': <module 'sys' (built-in)>, 'builtins': <module 'builtins' (built-in)>, '_frozen_importlib': <module 'importlib._bootstrap' (frozen)>, '_imp': <module '_imp' (built-in)>, '_thread': <module '_thread' (built-in)>, '_warnings': <module '_warnings' (built-in)>, '_weakref': <module '_weakref' (built-in)>, 'zipimport': <module 'zipimport' (built-in)>, '_frozen_importlib_external': <module 'importlib._bootstrap_external' (frozen)>, '_io': <module 'io' (built-in)>, 'marshal': <module 'marshal' (built-in)>, 'posix': <module 'posix' (built-in)>, 'encodings': <module 'encodings' from '/usr/lib/python3.7/encodings/__init__.py'>, 'codecs': <module 'codecs' from '/usr/lib/python3.7/codecs.py'>, '_codecs': <module '_codecs' (built-in)>, 'encodings.aliases': <module 'encodings.aliases' from '/usr/lib/python3.7/encodings/aliases.py'>, 'encodings.utf_8': <module 'encodings.utf_8' from '/usr/lib/python3.7/encodings/utf_8.py'>, '_signal': <module '_signal' (built-in)>, '__main__': <module '__main__' from 'importTest.py'>, 'encodings.latin_1': <module 'encodings.latin_1' from '/usr/lib/python3.7/encodings/latin_1.py'>, 'io': <module 'io' from '/usr/lib/python3.7/io.py'>, 'abc': <module 'abc' from '/usr/lib/python3.7/abc.py'>, '_abc': <module '_abc' (built-in)>, 'site': <module 'site' from '/usr/lib/python3.7/site.py'>, 'os': <module 'os' from '/usr/lib/python3.7/os.py'>, 'stat': <module 'stat' from '/usr/lib/python3.7/stat.py'>, '_stat': <module '_stat' (built-in)>, 'posixpath': <module 'posixpath' from '/usr/lib/python3.7/posixpath.py'>, 'genericpath': <module 'genericpath' from '/usr/lib/python3.7/genericpath.py'>, 'os.path': <module 'posixpath' from '/usr/lib/python3.7/posixpath.py'>, '_collections_abc': <module '_collections_abc' from '/usr/lib/python3.7/_collections_abc.py'>, '_sitebuiltins': <module '_sitebuiltins' from '/usr/lib/python3.7/_sitebuiltins.py'>, 'sitecustomize': <module 'sitecustomize' from '/usr/lib/python3.7/sitecustomize.py'>, 'importlib': <module 'importlib' from '/usr/lib/python3.7/importlib/__init__.py'>, 'importlib._bootstrap': <module 'importlib._bootstrap' (frozen)>, 'importlib._bootstrap_external': <module 'importlib._bootstrap_external' (frozen)>, 'types': <module 'types' from '/usr/lib/python3.7/types.py'>, 'warnings': <module 'warnings' from '/usr/lib/python3.7/warnings.py'>, 'importlib.util': <module 'importlib.util' from '/usr/lib/python3.7/importlib/util.py'>, 'importlib.abc': <module 'importlib.abc' from '/usr/lib/python3.7/importlib/abc.py'>, 'importlib.machinery': <module 'importlib.machinery' from '/usr/lib/python3.7/importlib/machinery.py'>, 'contextlib': <module 'contextlib' from '/usr/lib/python3.7/contextlib.py'>, 'collections': <module 'collections' from '/usr/lib/python3.7/collections/__init__.py'>, 'operator': <module 'operator' from '/usr/lib/python3.7/operator.py'>, '_operator': <module '_operator' (built-in)>, 'keyword': <module 'keyword' from '/usr/lib/python3.7/keyword.py'>, 'heapq': <module 'heapq' from '/usr/lib/python3.7/heapq.py'>, '_heapq': <module '_heapq' (built-in)>, 'itertools': <module 'itertools' (built-in)>, 'reprlib': <module 'reprlib' from '/usr/lib/python3.7/reprlib.py'>, '_collections': <module '_collections' (built-in)>, 'functools': <module 'functools' from '/usr/lib/python3.7/functools.py'>, '_functools': <module '_functools' (built-in)>, 'PySide2': <module 'PySide2' from './PySide2/__init__.py'>}
Traceback (most recent call last):
  File "importTest.py", line 17, in <module>
    print(PySide2.__version__)
NameError: name 'PySide2' is not defined

I don't understand why it says "name 'PySide2' is not define" when it appears in the sys.modules.


Solution

  • From the documentation, NameError is raised when a local or global name is not found.

    For the standard import statement, it will do following things:

    • Find & Load the module
    • Update sys.modules
    • Update global dict with a variable name pointing to actual module object for import foo, update multiple variables when it's import * from something (also check __all__ of the module)

    Here you've already done the first 2 steps manually, you only need to add:

    PySide2 = module