如何正确设置 MYPYPATH 以获取 mypy 的存根?
问题:如何正确设置 MYPYPATH 以获取 mypy 的存根?
我一辈子都无法让 MyPy 找到不与其源代码位于同一位置的存根。这是我的项目的结构:
trymypy/
|- stubs/
| \- foo.pyi
|- __init__.py
|- usefoo.py
\- foo.py
# foo.py
def foofunc(x):
return str(x)
# usefoo.py
from trymypy.foo import foofunc
print(foofunc(5) + 5)
# stubs/foo.pyi
def foofunc(x: int) -> str: ...
我已将MYPYPATH环境变量设置为/full/path/to/trymypy/stubs,因此 MyPy 应该在stubs目录中查找我的.pyi文件。
这不应该通过类型检查。 MyPy 应该这样标记错误:
../trymypy/usefoo.py:3: error: Unsupported operand types for + ("str" and "int")
相反,MyPy 不会标记任何错误,因为它没有读取存根文件。如果我将存根文件foo.pyi移动到目录的根项目,与foo.py位于同一位置,它会正确标记,这表明MYPYPATH没有被拾取,或者没有被正确定义。
我还尝试在配置文件mypy.ini中设置mypy_path:
[mypy]
python_version = 3.7
mypy_path = /full/path/to/trymypy/stubs
mypy.ini中的其他配置选项确实被选中(例如python_version),因此 MyPy 正在查看该文件并读取它。
完全被这里难住了。这是一个(非常)简单的示例,看起来它应该按照 MyPy 的文档工作。我已经用完了变量来进行实验以使其正常工作。
我正在使用 Python 3.7 并在仅安装了 mypy 的虚拟环境中工作。
解答
长话短说:您可能希望将foo.pyi文件与foo.py一起移动到顶级trymypy文件夹中,而不是拥有一个单独的“存根”目录。
长话短说,您的设置目前存在两个问题。
第一个问题是存根的文件夹结构需要反映底层代码的结构方式。所以既然你想做from trymypy.foo import blah,你需要调整你的文件夹结构看起来像这样:
trymypy/
|- stubs/
| |- trymypy/
| | |- __init__.pyi
| \ \- foo.pyi
|- __init__.py
|- usefoo.py
\- foo.py
您应该继续将 mypypath 设置为指向trymypy/stubs。您可以使用绝对路径或相对路径。
更大的第二个问题是您的存根最终可能会被隐藏和忽略,具体取决于您调用 mypy 的准确程度。例如,如果您从trymypy文件夹外部运行mypy -p 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
现在,无论按什么顺序导入,mypy 总是会同时找到foo.py和foo.pyi,因为这两个文件位于同一个目录中。并且每当py和pyi文件位于同一目录中时,pyi文件总是胜出(并且 py 文件被忽略)。
关于这个新的文件夹结构,您可能有两个后续问题:
- 有没有办法使用
foo.pyi中的类型提示对foo.py的内容进行类型检查?
答案是否定的,目前没有办法。如果存在foo.pyi,则 mypy 基本上完全忽略了foo.py的主体。有人对添加对此功能的支持感兴趣,因此您也许可以订阅链接的 Github 问题以获取更新。
- 创建一个单独的“存根”文件夹最终在这里没有用。那么它什么时候有用呢?
答案是,当您想为第三方库添加存根时,它主要有用。实际上,我对“为您自己的代码添加存根”工作流程没有太多经验,但我的理解是,这些存根通常以上述方式“内联”。
更多推荐

所有评论(0)