假设我正在为一个不受我控制的大型遗留代码库编写一个 Python 插件。该代码库公开了一个具有多个函数的对象,我可以从我的插件代码中调用这些函数:
def plugin_entrypoint(ctx: LegacyCodebaseObject):
...
这些函数之一会导致插件控制器以错误条件终止插件:
def plugin_entrypoint(ctx: LegacyCodebaseObject):
...
ctx.fail(reason="foo", ...) # never returns
但是,作为遗留代码库,这些函数都没有类型注释。我的插件 is 类型注释,并且我使用 mypy,这会引起烦恼,因为 mypy 认为
ctx.fail()
可能会返回:
def plugin_entrypoint(ctx: LegacyCodebaseObject):
...
try:
some_data = some_function()
except Exception as e:
ctx.fail(reason=f"Failed to prepare data: {e}") # never returns
other_function(some_data) # mypy complains that some_data might be referenced before assignment
是否可以在我的代码中以 mypy 能够理解的方式注释
LegacyCodebaseObject.fail()
?
我可以简单地将
ctx.fail()
包装到我自己的函数中,并用 Never type 进行注释,但这将是不符合语法的,而且我的插件将无法通过代码审查。
一种方法是利用
TYPE_CHECKING
和继承。
我们假设遗留代码如下所示:
# Deep inside the legacy codebase
class LegacyCodebaseObject:
def foo(self):
...
def bar(self, baz, *, qux):
...
def fail(self):
raise Exception
在
TYPE_CHECKING
分支中,使用别名将其导入到您的代码中,然后使用正确的名称创建一个子类:
if TYPE_CHECKING:
from legacy_codebase import LegacyCodebaseObject as _LegacyCodebaseObject
class LegacyCodebaseObject(_LegacyCodebaseObject):
# Correct type hints here
def fail(self, reason: str, **kwargs: Any) -> Never:
...
# You can do the same for other methods as well
...
在另一个分支上,只需正常导入即可:
else:
from legacy_codebase import LegacyCodebaseObject