开发者

Dynamic imports + relative imports in Python 3

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']开发者_C百科
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?


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']
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜