Dynamic imports + relative imports in Python 3
Question: I have a **Python 3** project where I'm dynamically importing modules from
disk, using `imp.load_module`. But, I've run into an problem where relative
imports fail, when the relative import occurs within a dynamically imported
module.

From what I've read, I came to the conclusion that only `__file__`,
`__path__`, `__package__`, and `__name__` were used by the default importer
when determining the path of an import. Yet, I've verified these in the code
below, and it still fails when dynamically imported. (It works when imported
in the interpreter with an updated `sys.path`)

    
    
    # File structure:
    # [root]
    #  ├─ __init__.py
    #  ├─ board.py
    #  └─ test.py
    

****

    
    
    # Contents of 'board.py':
    import os, sys
    import root  # Already imported... just need a reference
    
    ROOT_DIR = os.path.dirname(root.__file__)
    assert root is sys.modules['root']
    assert root.__package__ is None
    assert root.__name__ == 'root'
    assert root.__file__ == os.path.join(ROOT_DIR, '__init__.py')
    assert not hasattr(root, '__path__')
    
    xx = object()
    assert xx is sys.modules['root.board'].xx
    assert __package__ is None
    assert __name__ == 'root.board'
    assert __file__ == os.path.join(ROOT_DIR, 'board.py')
    assert not hasattr(sys.modules['root.board'], '__path__')
    
    assert os.path.isfile(os.path.join(ROOT_DIR, 'test.py'))
    from . import test  # ImportError('cannot import name test',)
    

But if I hack `sys.path` and reimport the current package just before the
failed import, it works:

    
    
    oldroot = root
    del sys.modules['root']
    sys.path.append(os.path.dirname(ROOT_DIR))
    import root
    from . import test  # No error here
    

And further, the four golden attributes mentioned above are the same in both
the new and old packages:

    
    
    assert oldroot.__package__ == root.__package__
    assert oldroot.__name__ == root.__name__
    assert oldroot.__file__ == root.__file__
    assert not hasattr(root, '__path__')
    

Which means that `__package__`, `__name__`, `__file__`, and `__path__` can't
be the full story. Are there any other attributes that Python uses to locate
imports? What am I overlooking that would cause the import to fail?


Answer: The lack of a `root.__path__` value is suspicious (it means Python doesn't
consider `root` to be a package).

The fact that both `load_module()` calls are being made with a type value of
`1` (a module) instead of the first one having a type of `5` (a package) is
also highly dubious.

Are you using `imp.find_module()` to work out the correct inputs to the
`imp.load_module()` call?

For a package, the input to `imp.load_module()` should look something like
this:

    
    
    # demo is a package in the current directory for this example
    >>> info = imp.find_module('demo')
    >>> info
    (None, 'demo', ('', '', 5))
    >>> demo = imp.load_module('demo', *info)
    >>> demo.__path__
    ['demo']
    

