mypy importlib 模块函数
mypy importlib module functions
我正在使用 importlib 在运行时导入模块。这些模块是我的应用程序的插件,必须实现 1 个或多个模块级功能。我已经开始向我的应用程序添加类型注释,但我从 mypy 中收到一条错误消息
Module has no attribute "generate_configuration"
其中 "generate_configuration" 是模块函数之一。
在这个例子中,模块只需要有一个generate_configuration函数。该函数接受一个字典参数。
def generate_configuration(data: Dict[str, DataFrame]) -> None: ...
我一直在寻找如何指定模块的接口,但我能找到的只有 class 个接口。有人可以指出一些说明如何执行此操作的文档吗?我的 google-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_module
的类型注释只是 returns types.ModuleType
def import_module(name: str, package: Optional[str] = ...) -> types.ModuleType: ...
这意味着 plugin_module
的显示类型是 Module
-- 它没有您的特定属性。
由于mypy
是一个静态分析工具,它无法知道那个import的return值有特定的接口。
这是我的建议:
为你的模块创建一个类型接口(它不必被实例化,它只会帮助 mypy 解决问题)
class ModuleInterface:
@staticmethod
def generate_configuration(data: Dict[str, DataFrame]) -> None: ...
创建一个导入模块的函数,您可能需要添加 # type: ignore
,但如果您使用 __import__
而不是 import_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')
我做了更多研究,最终确定了一个稍微不同的解决方案,它使用 typing.cast。
解决方案仍然使用 中的静态方法定义。
from typing import Dict
from pandas import DataFrame
class ConfigurationGenerationPlugin(ModuleType):
@staticmethod
def generate_configuration(directory: str, points_list: Dict[str, DataFrame]) -> None: ...
导入模块的代码然后使用 typing.cast() 设置正确的类型。
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)
我不确定我对必须将 ConfigurationGenerationPlugin class 或 cast() 调用添加到代码中只是为了让 mypy 开心的感觉如何。但是,我暂时要坚持下去。
我正在使用 importlib 在运行时导入模块。这些模块是我的应用程序的插件,必须实现 1 个或多个模块级功能。我已经开始向我的应用程序添加类型注释,但我从 mypy 中收到一条错误消息
Module has no attribute "generate_configuration"
其中 "generate_configuration" 是模块函数之一。
在这个例子中,模块只需要有一个generate_configuration函数。该函数接受一个字典参数。
def generate_configuration(data: Dict[str, DataFrame]) -> None: ...
我一直在寻找如何指定模块的接口,但我能找到的只有 class 个接口。有人可以指出一些说明如何执行此操作的文档吗?我的 google-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_module
的类型注释只是 returns types.ModuleType
def import_module(name: str, package: Optional[str] = ...) -> types.ModuleType: ...
这意味着 plugin_module
的显示类型是 Module
-- 它没有您的特定属性。
由于mypy
是一个静态分析工具,它无法知道那个import的return值有特定的接口。
这是我的建议:
为你的模块创建一个类型接口(它不必被实例化,它只会帮助 mypy 解决问题)
class ModuleInterface: @staticmethod def generate_configuration(data: Dict[str, DataFrame]) -> None: ...
创建一个导入模块的函数,您可能需要添加
# type: ignore
,但如果您使用__import__
而不是import_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')
我做了更多研究,最终确定了一个稍微不同的解决方案,它使用 typing.cast。
解决方案仍然使用
from typing import Dict
from pandas import DataFrame
class ConfigurationGenerationPlugin(ModuleType):
@staticmethod
def generate_configuration(directory: str, points_list: Dict[str, DataFrame]) -> None: ...
导入模块的代码然后使用 typing.cast() 设置正确的类型。
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)
我不确定我对必须将 ConfigurationGenerationPlugin class 或 cast() 调用添加到代码中只是为了让 mypy 开心的感觉如何。但是,我暂时要坚持下去。