我在运行时使用importlib导入模块。 这些模块是我的应用程序的插件,必须实现一个或多个模块级功能。 我已经开始向我的应用程序添加类型注释,我从mypy声明中收到错误
模块没有属性“generate_configuration”
其中“generate_configuration”是模块函数之一。
在此示例中,模块只需要在其中包含generate_configuration函数。 该函数采用单个dict参数。
def generate_configuration(data: Dict[str, DataFrame]) -> None: ...我一直在寻找如何指定模块的接口,但我能找到的只是类接口。 有人能指点我一些文件说明如何做到这一点? 我的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)I am using importlib to import modules at runtime. These modules are plugins for my application and must implement 1 or more module-level functions. I have started adding type annotations to my applications and I get an error from mypy stating
Module has no attribute "generate_configuration"
where "generate_configuration" is one of the module functions.
In this example, the module is only required to have a generate_configuration function in it. The function takes a single dict argument.
def generate_configuration(data: Dict[str, DataFrame]) -> None: ...I have been searching around for how to specify the interface of a module but all I can find are class interfaces. Can someone point me to some documentation showing how to do this? My google-fu is failing me on this one.
The code that loads this module is shown below. The error is generated by the last line.
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的类型注释只返回types.ModuleType
从类型来源 :
def import_module(name: str, package: Optional[str] = ...) -> types.ModuleType: ...这意味着plugin_module的显示类型是Module - 它没有您的特定属性。
由于mypy是一个静态分析工具,因此无法知道该导入的返回值是否具有特定的接口。
这是我的建议:
为你的模块创建一个类型接口(它不需要实例化,它只是帮助我解决问题)
class ModuleInterface: @staticmethod def generate_configuration(data: Dict[str, DataFrame]) -> None: ...创建一个导入模块的函数,你可能需要使用__import__ # type: ignore ,但如果使用__import__而不是__import__ ,你可以避免这个限制
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')The type annotation for importlib.import_module simply returns types.ModuleType
From the typeshed source:
def import_module(name: str, package: Optional[str] = ...) -> types.ModuleType: ...This means that the revealed type of plugin_module is Module -- which doesn't have your specific attributes.
Since mypy is a static analysis tool, it can't know that the return value of that import has a specific interface.
Here's my suggestion:
Make a type interface for your module (it doesn't have to be instantiated, it'll just help mypy figure things out)
class ModuleInterface: @staticmethod def generate_configuration(data: Dict[str, DataFrame]) -> None: ...Make a function which imports your module, you may need to sprinkle # type: ignore, though if you use __import__ instead of import_module you may be able to avoid this limitation
def import_module_with_interface(modname: str) -> ModuleInterface: return __import__(modname, fromlist=['_trash']) # might need to ignore the type hereEnjoy the types :)
The sample code I used to verify this idea:
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')更多推荐
发布评论