Skenvy
Skenvy

Reputation: 1410

Dynamically create modules inside __init__ if they don't exist

I would like to dynamically create and import modules inside an inner __init__.py file, if one or several of a set of indexed submodules doesn't exist.

I have a set of module layers, say;

top_module/
    __init__.py
    sub_module/
        __init__.py
        a1/
            __init__.py
            n1.py
            b1/
                __init__.py
                b1.py
            b2/
                __init__.py
                b2.py
            b3/
                __init__.py
                b3.py
        a2/
            __init__.py
            a2.py
            b*/...
        a*/...

Where the top_module/__init__.py does a from .sub_module import *.

From within the top_module/sub_module/__init__.py, say I have several of these a* folders that are just indexed iteratively. I know that I can do something like this to iterate importing over an index for those modules that exist;

from importlib import import_module

for a in range(some_max_a):
    import_module(f'.a{a}', package='top_module.sub_module')

And that I can do something like this to just ignore modules that don't exist yet;

from importlib import import_module

for a in range(some_max_a):
    try:
        import_module(f'.a{a}', package='top_module.sub_module')
    except ModuleNotFoundError:
        pass

What I would like to be able to do is dynamically create and import these modules if they don't exist.

What I have so far is

from importlib import import_module
from sys import modules
from types import ModuleType

PKG = 'top_module.sub_module'

for a in range(some_max_a):
    try:
        import_module(f'.a{a}', package=PKG)
    except ModuleNotFoundError:
        modules[f'{PKG}.a{a}'] = ModuleType(f'{PKG}.a{a}')
        for b in range(some_max_b):
            modules[f'{PKG}.a{a}.b{b}'] = ModuleType(f'{PKG}.a{a}.b{b}')
            def function_all_should_have(*args, **kwargs):
                raise NotImplementedError
            modules[f'{PKG}.a{a}.b{b}'].function_all_should_have = function_all_should_have
        import_module(f'{PKG}.a{a}')

I've tried with and without the {PKG} in the import_module call and or the ModuleType call.

If I import the created package, I can see that all the modules that I'm expecting this to create exist in the sys.modules, but trying to access any of them with a call to something like top_module.sub_module.a3.b3.function_all_should_have() yields an error along the lines of

AttributeError: module 'top_module.sub_module' has no attribute 'a3'.

Yet I can see that there is a top_module.sub_module.a3 module along with all the top_module.sub_module.a3.b* modules.

I'm not really sure why the modules would be created and exist in sys.modules but be unreachable after importing.

If there's no easy answer I could just go back to my second example and pass if the modules don't exist, but I would still like to understand what's happening here. The only closest question I could find to this was dynamic module creation.

Upvotes: 0

Views: 83

Answers (0)

Related Questions