Skip to content Skip to sidebar Skip to footer

Dynamically Import Module From Memory In Python 3 Using Hooks

What I want to achieve is exactly what this this answer proposes, however in Python 3. The code below works fine in Python 2: import sys import imp modules = { 'my_module': '''cla

Solution 1:

The short answer is that you forgot to translate the latter half of the exec statement from the code sample. That causes the exec to be applied in the context of the load_module method — not the new_module; so specify the context:

exec(self._modules[fullname], new_module.__dict__)

However, using a Python versioned 3.4 or higher, you become subject to PEP 451 (the introduction of module specs), as well as the deprecation of the imp module, in favor of importlib. Particularly:

  • The imp.new_module(name) function is replaced by importlib.util.module_from_spec(spec).
  • An abstract base class for meta path finder objects is supplied: importlib.abc.MetaPathFinder.
  • And such finder objects now use find_spec rather than find_module.

Here is a very close reimplementation of the code sample.

import importlib
import sys
import types


class StringLoader(importlib.abc.Loader):

    def __init__(self, modules):
        self._modules = modules

    def has_module(self, fullname):
        return (fullname in self._modules)

    def create_module(self, spec):
        if self.has_module(spec.name):
            module = types.ModuleType(spec.name)
            exec(self._modules[spec.name], module.__dict__)
            return module

    def exec_module(self, module):
        pass


class StringFinder(importlib.abc.MetaPathFinder):

    def __init__(self, loader):
        self._loader = loader

    def find_spec(self, fullname, path, target=None):
        if self._loader.has_module(fullname):
            return importlib.machinery.ModuleSpec(fullname, self._loader)


if __name__ == '__main__':
    modules = {
        'my_module': """
    BAZ = 42

    class Foo:
        def __init__(self, *args: str):
            self.args = args
        def bar(self):
            return ', '.join(self.args)
    """}

    finder = StringFinder(StringLoader(modules))
    sys.meta_path.append(finder)

    import my_module
    foo = my_module.Foo('Hello', 'World!')
    print(foo.bar())
    print(my_module.BAZ)

Post a Comment for "Dynamically Import Module From Memory In Python 3 Using Hooks"