python - 在重新加载不同目录中以编程方式导入的文件时找不到 ModuleSpec
问题描述
在 CLI 中实现重新加载功能时,我偶然发现了这个我似乎无法解决的奇怪错误。要重新创建它,首先,在与 CWD 不同的目录中创建一个虚拟文件。对于此示例,我将其命名为test.py
.
使用此处的配方,以编程方式导入它,但确保 CWD 是独立的,并且test.py
使用 CWD 作为“主干”无法到达路径。
spec = importlib.util.spec_from_file_location('test', <path>)
module = importlib.util.module_from_spec(spec)
sys.modules['test'] = module
spec.loader.exec_module(module)
尝试使用以下命令重新加载模块时发生错误importlib.reload
:
>>> importlib.reload(module)
File "C:\Program Files\Python38\lib\importlib\__init__.py", line 168, in reload
raise ModuleNotFoundError(f"spec not found for the module {name!r}", name=name)
ModuleNotFoundError: spec not found for the module 'test'
我稍微检查了一下,importlib.__init__.reload
这似乎是调用_frozen_importlib._bootstrap._find_spec
。引发错误是因为未找到模块规范,但该属性__spec__
在重新加载之前存在。None
由于代码的性质,规范设置为:
spec = module.__spec__ = _bootstrap._find_spec(name, pkgpath, target)
if spec is None:
raise ModuleNotFoundError(f"spec not found for the module {name!r}", name=name)
有没有办法让我重新加载以编程方式导入的模块?我确实需要保留使用文件路径导入的文件,因为它所属的程序允许用户编写可以放置在文件系统中任何位置的“插件”。这是我的代码的导入机制中主要发生的事情的简化版本。
解决方案
使用适当的最新版本的 python (>=3.4),您可以添加一个MetaPathFinder
tosys.meta_path
来告诉 python 在哪里可以找到模块规范。如果你这样做,你可以避免显式的模块加载配方,并且importlib.reload
应该可以正常工作。例子:
class ModuleFinder(importlib.abc.MetaPathFinder):
def __init__(self, path_map:dict):
self.path_map = path_map
def find_spec(self, fullname, path, target=None):
if not fullname in self.path_map:
return None
return importlib.util.spec_from_file_location(fullname, self.path_map[fullname])
def find_module(self, fullname, path):
return None # No need to implement, backward compatibility only
sys.meta_path.append(ModuleFinder(my_module_paths))
my_module_paths
一些字典映射模块名称到位置在哪里。
有关更多详细信息:https ://docs.python.org/3/library/importlib.html#setting-up-an-importer (实际上这就在您找到配方的下方)