无法让 Mypy 使用 __init__.py 别名
Can't make Mypy work with __init__.py aliases
下面是我的问题的最小示例:
[test/__init__.py]
from test.test1 import Test1
from test.test2 import Test2
[test/test1.py]
class Test1:
pass
[test/test2.py]
from test import Test1
class Test2:
pass
模块或 __init__.py 的 Mypy 输出:
test/test2.py:1: error: Module 'test' has no attribute 'Test1'
代码本身在 Python 2 和 Python 3 上都运行良好。
这是因为你的代码有一个导入周期——为了让mypy解析test/__init__.py
,它需要准确理解Test2
是什么. (毕竟,如果您决定稍后在 file/call 它的方法之一中使用 Test2
怎么办?然后 mypy 需要知道输出是什么)。
所以,它点击导入,有效地暂停,然后跳转到试图理解 test/test2.py
正在做什么*。
但是在 test/test2.py
中,我们 运行 遇到了完全相同的问题——我们看到了导入,需要跳回 test/__init__.py
才能理解什么 Test1
是...但是我们还没有完成对那个文件的解析!
这是 mypy 与 Python 运行time 不同的地方,仅供参考 -- mypy 一次只能解析整个文件,但是 Python 运行time 将 实际上暂停执行到 运行 test/test2.py
。这意味着当您执行 from test import Test1
时,test
模块有一个部分完整的符号 space,它恰好当前仅包含 Test1
,而不是同时包含 Test1
和 Test2
,这就是为什么您的代码在 运行 时间运行的原因。
在这种情况下,解决方法是将 test/test2.py
中的导入修改为:
from test.test1 import Test1
这打破了导入周期。
*这实际上不是 mypy 所做的 -- 它实际上所做的是尝试通过首先识别所有 strongly connected components (SCC) 来解决导入周期 -- 每个 SCC 基本上都是一个导入周期。
然后应用一些试探法来确定 SCC 中文件的处理顺序,但这是一个不完善的过程,无法解决所有导入周期。
例如,在您的情况下,无论我们先处理 test
还是 test.test2
,我们都会 运行 进入问题。
您可以通过使用 -v
标志(对于详细模式)重新 运行ning mypy 来查看 mypy 识别的 SCC。
您可以在源代码中找到有关 mypy 使用的算法的更多详细信息 here. Specific details about the import cycle resolution algorithm can be found a little lower down, here。
(我怀疑整个评论有点 out-of-date/incomplete,仅供参考——还有其他几个与 mypy 的静默导入机制及其增量模式机制相关的皱纹没有真正解释。)
可以找到有关 mypy 用于订购 SCC 的确切启发式的信息 here。 (基本上,我们用于导入某些内容的确切语法可以提供一些关于应该如何排序 SCC 的提示)。
下面是我的问题的最小示例:
[test/__init__.py]
from test.test1 import Test1
from test.test2 import Test2
[test/test1.py]
class Test1:
pass
[test/test2.py]
from test import Test1
class Test2:
pass
模块或 __init__.py 的 Mypy 输出:
test/test2.py:1: error: Module 'test' has no attribute 'Test1'
代码本身在 Python 2 和 Python 3 上都运行良好。
这是因为你的代码有一个导入周期——为了让mypy解析test/__init__.py
,它需要准确理解Test2
是什么. (毕竟,如果您决定稍后在 file/call 它的方法之一中使用 Test2
怎么办?然后 mypy 需要知道输出是什么)。
所以,它点击导入,有效地暂停,然后跳转到试图理解 test/test2.py
正在做什么*。
但是在 test/test2.py
中,我们 运行 遇到了完全相同的问题——我们看到了导入,需要跳回 test/__init__.py
才能理解什么 Test1
是...但是我们还没有完成对那个文件的解析!
这是 mypy 与 Python 运行time 不同的地方,仅供参考 -- mypy 一次只能解析整个文件,但是 Python 运行time 将 实际上暂停执行到 运行 test/test2.py
。这意味着当您执行 from test import Test1
时,test
模块有一个部分完整的符号 space,它恰好当前仅包含 Test1
,而不是同时包含 Test1
和 Test2
,这就是为什么您的代码在 运行 时间运行的原因。
在这种情况下,解决方法是将 test/test2.py
中的导入修改为:
from test.test1 import Test1
这打破了导入周期。
*这实际上不是 mypy 所做的 -- 它实际上所做的是尝试通过首先识别所有 strongly connected components (SCC) 来解决导入周期 -- 每个 SCC 基本上都是一个导入周期。
然后应用一些试探法来确定 SCC 中文件的处理顺序,但这是一个不完善的过程,无法解决所有导入周期。
例如,在您的情况下,无论我们先处理 test
还是 test.test2
,我们都会 运行 进入问题。
您可以通过使用 -v
标志(对于详细模式)重新 运行ning mypy 来查看 mypy 识别的 SCC。
您可以在源代码中找到有关 mypy 使用的算法的更多详细信息 here. Specific details about the import cycle resolution algorithm can be found a little lower down, here。
(我怀疑整个评论有点 out-of-date/incomplete,仅供参考——还有其他几个与 mypy 的静默导入机制及其增量模式机制相关的皱纹没有真正解释。)
可以找到有关 mypy 用于订购 SCC 的确切启发式的信息 here。 (基本上,我们用于导入某些内容的确切语法可以提供一些关于应该如何排序 SCC 的提示)。