Search code examples
pythonweb2py

How do I import a submodule with web2py?


I'm trying to do a dynamic import (using "__import__()") of a submodule in web2py and it doesn't seem to be finding the submodule.

Here's an example path: web2py/app/modules/module.py <-- This works web2py/app/modules/module_folder/submodule.py <-- This won't get spotted.

Right now as a workaround I'm using 'exec()' but I'd rather not do that.


Answers to questions:

"Do you have __init__.py in module_folder?"

Yep.

"What exactly is the line of code you write to import web2py/app/modules/module_folder/submodule.py?"

mapping_module = __import__('communication_templates.mappings.%s' % module_name)

"what is the error?"

<type 'exceptions.AttributeError'> 'module' object has no attribute 'mapping_dict'

Explanation: I'm trying to get a variable named 'mapping_dict' from the module once I've loaded it.


Solution

  • The problem here is that __import__ does not do the most intuitive thing (neither does import btw).

    Here's what happens:

    >>> import sys
    >>> x = __import__('package1.package2.module')
    >>> x
    <module 'package1' from 'C:\\Temp\\package1\\__init__.py'>
    

    Even though package1.package2.module was imported, the variable returned is actually package1.

    So, to access something that is in package1.package2.module, one has to dig down again:

    >>> x.package2.module.Class
    <class 'package1.package2.module.Class'>
    

    Is it then the same as importing just package1?

    Not really:

    >>> x = __import__('package1')
    >>> x
    <module 'package1' from 'C:\\Temp\\package1\\__init__.py'>
    >>> x.package2.module.Class
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: module 'package1' has no attribute 'package2'
    

    Why?

    Well, __import__ is the same as `import, that is, these are the same:

    package1 = __import__('package1.package2.module')
    # is the same as:
    import package1.package2.module
    

    And:

    package1 = __import__('package1')
    # is the same as:
    import package1
    

    In each of those cases, you get just one local variable package1.

    In the case of importing package1.package2.module, package2 is also imported and stored in package2 attribute of package package1 etc.

    Solution

    To access package1.package2.module.Class, if package1.package2.module is in a string:

    >>> s = 'package1.package2.module'
    >>> module = __import__(s)
    >>> for submodule_name in s.split('.')[1:]:
    ...     module = getattr(module, submodule_name)
    ...
    >>> module.Class
    <class 'package1.package2.module.Class'>