Python 3.x mypy importlib模块函数
我使用importlib在运行时导入模块。这些模块是我的应用程序的插件,必须实现一个或多个模块级功能。我已经开始在我的应用程序中添加类型注释,我从mypy语句中得到一个错误 模块没有“生成配置”属性 其中,“生成配置”是模块功能之一 在本例中,模块中只需要有generate_配置功能。该函数接受一个dict参数Python 3.x mypy importlib模块函数,python-3.x,mypy,Python 3.x,Mypy,我使用importlib在运行时导入模块。这些模块是我的应用程序的插件,必须实现一个或多个模块级功能。我已经开始在我的应用程序中添加类型注释,我从mypy语句中得到一个错误 模块没有“生成配置”属性 其中,“生成配置”是模块功能之一 在本例中,模块中只需要有generate_配置功能。该函数接受一个dict参数 def generate_configuration(data: Dict[str, DataFrame]) -> None: ... 我一直在寻找如何指定模块的接口,但我能找到
def generate_configuration(data: Dict[str, DataFrame]) -> None: ...
我一直在寻找如何指定模块的接口,但我能找到的只是类接口。有人能给我指一些说明如何做到这一点的文档吗?我的谷歌fu在这一点上让我失望
加载此模块的代码如下所示。错误由最后一行生成
plugin_directory = os.path.join(os.path.abspath(directory), 'Configuration-Generation-Plugins')
plugins = (
module_file
for module_file in Path(plugin_directory).glob('*.py')
)
sys.path.insert(0, plugin_directory)
for plugin in plugins:
plugin_module = import_module(plugin.stem)
plugin_module.generate_configuration(directory, points_list)
importlib.import\u模块的类型注释仅返回types.ModuleType
发件人:
这意味着插件模块
的显示类型是模块
——它没有您的特定属性
由于mypy
是一个静态分析工具,它无法知道该导入的返回值是否有特定的接口
以下是我的建议:
为您的模块创建一个类型接口(它不必实例化,它只会帮助mypy解决问题)
制作一个导入模块的函数,您可能需要添加#type:ignore
,但如果您使用uuu import\uuuuu
而不是import\u module
,则可能可以避免此限制
def import_module_with_interface(modname: str) -> ModuleInterface:
return __import__(modname, fromlist=['_trash']) # might need to ignore the type here
喜欢这些类型:)
我用来验证这个想法的示例代码:
class ModuleInterface:
@staticmethod
def compute_foo(bar: str) -> str: ...
def import_module_with_interface(modname: str) -> ModuleInterface:
return __import__(modname, fromlist=['_trash'])
def myf() -> None:
mod = import_module_with_interface('test2')
# mod.compute_foo() # test.py:12: error: Too few arguments for "compute_foo" of "ModuleInterface"
mod.compute_foo('hi')
我做了更多的研究,最终确定了一个稍微不同的解决方案,它使用
解决方案仍然使用中的静态方法定义
导入模块的代码然后使用()设置正确的类型
plugin_directory = os.path.join(os.path.abspath(directory), 'Configuration-Generation-Plugins')
plugins = (
module_file
for module_file in Path(plugin_directory).glob('*.py')
if not module_file.stem.startswith('lib')
)
sys.path.insert(0, plugin_directory)
for plugin in plugins:
plugin_module = cast(ConfigurationGenerationPlugin, import_module(plugin.stem))
plugin_module.generate_configuration(directory, points_list)
为了让mypy开心,我不知道我对不得不向代码中添加ConfigurationGenerationPlugin类或cast()调用有何感受。但是,我现在将坚持使用它。如果没有看到相关代码,我们很难知道如何帮助您?你的函数类型签名是什么样的?相邻的几行是什么?@AnthonySottile我添加了更多的细节和函数签名。对不起,原来没有说得更清楚。你能不能也添加错误指向的那行?谢谢你指导我完成这项工作。我已经添加了生成错误的代码。谢谢。这为我指明了一个新的方向。但是,我没有创建类型化函数来导入插件模块,而是添加了一个内联类型声明。我会将您的回答标记为正确答案,并在下面添加一个新的回答,显示我的最终解决方案。
class ModuleInterface:
@staticmethod
def compute_foo(bar: str) -> str: ...
def import_module_with_interface(modname: str) -> ModuleInterface:
return __import__(modname, fromlist=['_trash'])
def myf() -> None:
mod = import_module_with_interface('test2')
# mod.compute_foo() # test.py:12: error: Too few arguments for "compute_foo" of "ModuleInterface"
mod.compute_foo('hi')
from typing import Dict
from pandas import DataFrame
class ConfigurationGenerationPlugin(ModuleType):
@staticmethod
def generate_configuration(directory: str, points_list: Dict[str, DataFrame]) -> None: ...
plugin_directory = os.path.join(os.path.abspath(directory), 'Configuration-Generation-Plugins')
plugins = (
module_file
for module_file in Path(plugin_directory).glob('*.py')
if not module_file.stem.startswith('lib')
)
sys.path.insert(0, plugin_directory)
for plugin in plugins:
plugin_module = cast(ConfigurationGenerationPlugin, import_module(plugin.stem))
plugin_module.generate_configuration(directory, points_list)