我如何正确设置 MYPYPATH 以获取 mypy 的存根?

Ril*_*bbs 9 python python-3.x mypy

我一生都无法让 MyPy 找到不与其源代码共存的存根。这是我的项目结构:

trymypy/
|- stubs/
|  \- foo.pyi
|- __init__.py
|- usefoo.py
\- foo.py
Run Code Online (Sandbox Code Playgroud)
trymypy/
|- stubs/
|  \- foo.pyi
|- __init__.py
|- usefoo.py
\- foo.py
Run Code Online (Sandbox Code Playgroud)
# foo.py
def foofunc(x):
    return str(x)
Run Code Online (Sandbox Code Playgroud)
# usefoo.py
from trymypy.foo import foofunc

print(foofunc(5) + 5)
Run Code Online (Sandbox Code Playgroud)

我已将MYPYPATH环境变量设置为/full/path/to/trymypy/stubs,以便 MyPy 应该在stubs目录中查找我的.pyi文件。

这不应该通过类型检查。MyPy 应该将错误标记为:

../trymypy/usefoo.py:3: error: Unsupported operand types for + ("str" and "int")
Run Code Online (Sandbox Code Playgroud)

相反,MyPy 不会标记任何错误,因为它没有读取存根文件。如果我将存根文件移动foo.pyi到目录的根项目中,并与 位于同一位置foo.py,它会正确标记,这向我表明MYPYPATH没有被拾取,或者没有被正确定义。

我也试过mypy_path在配置文件中设置mypy.ini

[mypy]
python_version = 3.7
mypy_path = /full/path/to/trymypy/stubs
Run Code Online (Sandbox Code Playgroud)

其他配置选项mypy.ini确实会被选中(例如python_version),因此 MyPy 正在查看该文件并读取它。

完全被困在这里。这是一个(非常)简单的示例,看起来它应该像 MyPy 所记录的那样工作。我已经用完了变量来进行实验以使其正常工作。

我正在使用 Python 3.7 并在仅安装了 mypy 的虚拟环境中工作。

Mic*_*x2a 9

长话短说:与其拥有一个单独的“存根”目录,不如将您的foo.pyi文件移动到顶级trymypy文件夹中,与foo.py.


长话短说,您的设置目前存在两个问题。

第一个问题是存根的文件夹结构需要反映底层代码的结构方式。所以既然你想做from trymypy.foo import blah,你需要调整你的文件夹结构,看起来像这样:

trymypy/
|- stubs/
|  |- trymypy/
|  |  |- __init__.pyi
|  \  \- foo.pyi
|- __init__.py
|- usefoo.py
\- foo.py
Run Code Online (Sandbox Code Playgroud)

您应该继续将 mypypath 设置为指向trymypy/stubs. 您可以使用绝对路径或相对路径。

更大的第二个问题是,您的存根最终可能会被隐藏和忽略,具体取决于您调用 mypy 的准确程度。例如,如果你运行mypy -p trymypy从外部trymypy文件夹,mypy将通过解析每次启动trymypy和两个子模块直接包含(trymypy.__init__trymypy.usefoo,和trymypy.foo)。

一旦trymypy.foo加载完毕,mypy 就不会再尝试重新加载它,这意味着它永远不会费心检查您指定的存根。

但是,如果您尝试对单个文件(例如mypy -m trymypy.usefoo, mypy -p trymypy.usefoo)进行类型检查,mypy 将不会尝试加载其中的所有内容trymypy,这意味着它可以使用典型的导入解析规则找到存根。

您可以通过传入-v标志来确认所有这些行为,该标志以详细模式运行 mypy 并准确打印出加载的内容。请务必.mypy_cache在每次运行前删除该目录。

注意:老实说,我实际上不知道这种行为差异是故意的还是 mypy 中的错误。导入规则非常微妙。


值得庆幸的是,修复很简单:只需将您的foo.pyi文件移动到顶级trymypy文件夹中,如下所示:

trymypy/
|- __init__.py
|- usefoo.py
|- foo.py
\- foo.pyi
Run Code Online (Sandbox Code Playgroud)

现在,无论以什么顺序导入,mypy 总是会同时找到foo.pyfoo.pyi,因为两个文件都位于同一目录中。并且每当 apypyi文件位于同一目录中时,pyi文件总是胜出(并且忽略 py 文件)。


关于这个新的文件夹结构,您可能有两个后续问题:

  1. 有没有办法对foo.py使用中存在的类型提示的内容进行类型检查foo.pyi

    答案是否定的,目前没有办法。如果您foo.pyi在场,则foo.pymypy 基本上完全忽略了of 的主体。不过,有人有兴趣添加对此功能的支持,因此您可以订阅链接的 Github 问题以获取更新。

  2. 创建一个单独的“存根”文件夹最终在这里没有用。因此,当它有用吗?

    答案是,当您想为第三方库添加存根时,它主要有用。实际上,我对“为您自己的代码添加存根”工作流程没有太多经验,但我的理解是此类存根通常以上述方式“内联”。