mypy条件导入的“不兼容导入”错误

问题描述 投票:0回答:1

我有以下代码

try:
    from mypackage.optional.xxx import f1, f2
except ImportError:
    from mypackage.optional.yyy import f1, f2

[xxxyyy模块提供相同的功能,但是功能的编码非常不同,并且基于不同的外部库(它们是我的程序包的可选依赖项),接受不同的输入类型。

不幸的是mypy在抱怨:

error: Incompatible import of "f1" (imported name has type "Callable[[Arg(Any, 'yyyarg1')], Any]", local name has type "Callable[[Arg(Any, 'xxxarg1')], Any]")

我该如何解决这个问题?有条件地导入相同功能(即具有相似签名的相同功能名称)的最佳方法是什么?

python python-import typing mypy python-packaging
1个回答
0
投票

这里的问题是mypy对这两种导入有些挑剔-在满足mypy之前,两个库需要具有相同的API。

这包括任何参数名称,因为关键字参数是一件很重要的事情:执行f1(xxxarg1=blah)对于第一个导入有效,但对后者无效。

(此特定情况的解决方法是(a)使您的参数具有相同的名称,(b)使用仅在python 3.8+中可用的positional-only arguments,或(c)给您的参数名称加上两个下划线前缀这是将参数声明为仅位置的mypy特定方式-但此策略适用于所有Python版本。)

就我个人而言,我认为使两个函数的签名相同是最好的选择,因为它有助于最大程度地减少代码中存在细微错误的可能性/减少所需的测试量。

但是如果将您的API修改为相同是不可行的,则可以抑制该错误,也可以尝试使mypy通过以下方式使您的导入类型更精确地进行检查:

  • typing.TYPE_CHECKING变量,在运行时始终为False,但被mypy视为始终为true
  • ...还有--always-true/--always-false命令行标志,可以让mypy假定某些变量始终为true或false。
  • 总共,您知道可以采用三种不同的方法:

方法1:在第二次导入时抑制任何错误

[首先,如果两个库具有几乎相同的API,并且您不关心两者之间的任何细微差别,则一种策略可能是仅忽略后者的导入,这将使mypy抑制源自该最终版本的任何错误。行。

对所有其他行的类型检查将不受影响,这意味着mypy将继续假定--always-true/--always-falsef1是从xxx导入的。

f2

此类型忽略选项可能是最实用的方法。

方法2:明确选择第一个导入,而忽略第二个

或者,如果您不喜欢忽略任何内容,则可以通过执行以下操作使mypy完全忽略该导入:

try:
    from mypackage.optional.xxx import f1, f2
except ImportError:
    from mypackage.optional.yyy import f1, f2  # type: ignore

执行from typing import TYPE_CHECKING if TYPE_CHECKING: # Ignored at runtime, but not by mypy from mypackage.optional.xxx import f1, f2 else: # Ignored by mypy, but not at runtime try: from mypackage.optional.xxx import f1, f2 except ImportError: from mypackage.optional.yyy import f1, f2 也可以,尽管它使代码更加隐秘。

要注意的重要一点是,此方法和忽略类型的方法在类型安全性/不安全性方面完全相同。如果您想更清楚地了解自己在做什么或不惜一切代价避免忽略,则可以选择这种方法。

方法3:对两个变量进行类型检查

第三个也是最后一个选项是运行mypy两次,每个库使用if False: ... else: ...标志运行一次。这将是最安全的类型和最严格的选项。

例如,您可以做:

--always-true/--always-false

...然后同时运行from typing import TYPE_CHECKING # Actual runtime logic if not TYPE_CHECKING: # Ignored by mypy, but not at runtime try: from mypackage.optional.xxx import f1, f2 USES_XXX = True except ImportError: from mypackage.optional.yyy import f1, f2 USES_XXX = False # For the benefit of mypy if TYPE_CHECKING: if USES_XXX: from mypackage.optional.xxx import f1, f2 else: from mypackage.optional.yyy import f1, f2 mypy --always-true=USES_XXX your_code

© www.soinside.com 2019 - 2024. All rights reserved.