使用"当前目录"导入模块;进口

时间:2016-08-06 09:52:09

标签: python import python-3.5 python-importlib

我有以下代码动态加载模块:

def load_module(absolute_path):
    import importlib.util
    module_name, _ = os.path.splitext(os.path.split(absolute_path)[-1])
    try:
        py_mod = imp.load_source(module_name, absolute_path)
    except ImportError:
        module_root = os.path.dirname(absolute_path)
        print("Could not directly load module, including dir: {}".format(module_root))
        spec = importlib.util.spec_from_file_location(
            module_name, absolute_path, submodule_search_locations=[module_root, "."])
        py_mod = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(py_mod)
    return py_mod

除了尝试在同一文件夹中导入脚本(而不是具有相同名称的包的一部分)之外,它的效果非常好。例如,脚本a.py正在执行import b。它会导致错误ImportError: No module named 'b'(这在Python 3中很常见)。

但我真的想找到解决这个问题的方法吗?它可以通过预先解决来解决:

import sys
sys.path.append(".")

脚本" a"。

虽然我希望它可以通过以下方式解决:

submodule_search_locations=[module_root, "."]

哦,是的,理由是我还想支持导入不是正确的软件包/模块的模块,而只需要一些可以在解释器中运行的脚本。

可重现代码

〜/示例/ a.py

import b

〜/示例/ b.py

print("hi")

〜/ somewhere_else / main.py(与a / b不同)

import sys, os, imp, importlib.util

def load_module(absolute_path) ...

load_module(sys.argv[1])

然后在命令行上运行:

cd ~/somewhere_else
python3.5 main.py /home/me/example/a.py

会产生ImportError: No module named 'b'

以下代码解决了这个问题,但当然我们不能手动将sys.path内容放在所有脚本中。

〜/ example / a.py(2)

import sys
sys.path.append(".")
import b

我真的希望别人可能有一个我没有想到的解决方案。

附录

def load_module(absolute_path):
    import importlib.util
    module_name, _ = os.path.splitext(os.path.split(absolute_path)[-1])
    try:
        py_mod = imp.load_source(module_name, absolute_path)
    except ImportError as e:
        if "No module named" not in e.msg:
            raise e

        missing_module = e.name
        module_root = os.path.dirname(absolute_path)

        if missing_module + ".py" not in os.listdir(module_root):
            msg = "Could not find '{}' in '{}'"
            raise ImportError(msg.format(missing_module, module_root))

        print("Could not directly load module, including dir: {}".format(module_root))
        sys.path.append(module_root)
        spec = importlib.util.spec_from_file_location(module_name, absolute_path)
        py_mod = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(py_mod)
    return py_mod

2 个答案:

答案 0 :(得分:2)

你在这里使用动态导入并不重要;同样的问题适用于任何代码,假设当前目录在路径上。这些脚本负责确保当前目录本身位于路径上。

使用'.' global将目录添加到路径中,而不是使用__file__(当前工作目录):

import os.path
import sys

HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)

当<{1}}(可能为detecting it was a transient import that failed)时,可以重新启动您的动态导入,os.path.dirname(absolute_path)添加sys.path,但这是一个因为你无法区分缺失的依赖关系和假设ImportError的模块,所以要做出巨大的飞跃。

答案 1 :(得分:0)

就我而言,有效的是 importlib.import_module('file_basename') — 没有指定任何包。请参阅下面的试错与成功:

>>> importlib.import_module('.trainer', '.')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 961, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 973, in _find_and_load_unlocked
ModuleNotFoundError: No module named '.'

>>> importlib.import_module('.trainer', None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.8/importlib/__init__.py", line 122, in import_module
    raise TypeError(msg.format(name))
TypeError: the 'package' argument is required to perform a relative import for '.trainer'

>>> importlib.import_module('.trainer', '')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.8/importlib/__init__.py", line 122, in import_module
    raise TypeError(msg.format(name))
TypeError: the 'package' argument is required to perform a relative import for '.trainer'

>>> importlib.import_module('trainer')
<module 'trainer' from '/home/ubuntu/project1/trainer.py'>