我一辈子都无法让MyPy查找未与源代码共存的 stub 。这是我的项目结构:

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不会标记任何错误,因为它没有读取 stub 文件。如果我将 stub 文件foo.pyi移到与foo.py并置的目录的根项目中,它将正确标记,这向我指示MYPYPATH未被拾取或未正确定义。

我也尝试在配置文件mypy_path中设置mypy.ini:
[mypy]
python_version = 3.7
mypy_path = /full/path/to/trymypy/stubs

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

完全被困在这里。这是一个(非常)简单的示例,似乎它应该按照MyPy的说明工作。我已经用尽变量以进行试验。

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

最佳答案

长话短说:您可能希望将foo.pyi文件与trymypy一起移到顶层foo.py文件夹中,而不是使用一个单独的“stubs”目录。

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

第一个问题是 stub 的文件夹结构需要反射(reflect)基础代码的结构方式。因此,由于要执行from trymypy.foo import blah,因此需要将文件夹结构调整为如下所示:

trymypy/
|- stubs/
|  |- trymypy/
|  |  |- __init__.pyi
|  \  \- foo.pyi
|- __init__.py
|- usefoo.py
\- foo.py

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

更大的第二个问题是,取决于您对mypy的调用方式,您的 stub 最终可能会被遮蔽并被忽略。例如,如果您从mypy -p trymypy文件夹外部运行trymypy,mypy将首先解析每个trymypy及其直接包含的两个子模块(trymypy.__init__trymypy.usefootrymypy.foo)。

而且,一旦加载了trymypy.foo,mypy便不会再尝试重新加载它,这意味着它永远不会打扰检查您指定的 stub 。

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

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

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

幸运的是,此修复非常简单:只需将foo.pyi文件移动到顶级trymypy文件夹中,如下所示:
trymypy/
|- __init__.py
|- usefoo.py
|- foo.py
\- foo.pyi

现在,无论以什么顺序导入什么内容,由于两个文件都位于同一目录中,因此mypy始终会同时找到foo.pyfoo.pyi。并且,每当pypyi文件位于同一目录中时,pyi文件始终会获胜(而py文件将被忽略)。

关于这个新的文件夹结构,您可能有两个后续问题:
  • 是否可以使用foo.py中存在的类型提示来对foo.pyi的内容进行类型检查?

    答案是否定的,目前没有办法。如果存在foo.pyi,则mypy会完全忽略foo.py的正文。尽管有人对adding support for this feature感兴趣,所以您也许可以订阅链接的Github问题进行更新。
  • 创建一个单独的“ stub ”文件夹,最终在这里没有用。那么什么时候有用?

    答案是,当您要为第三方库添加 stub 时,它主要有用。我实际上对“为您自己的代码添加 stub ”工作流程没有很多经验,但是我的理解是,通常以上述方式“内联”此类 stub 。
  • 关于python - 如何正确设置MYPYPATH来为mypy拾取 stub ?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55437746/

    10-12 20:05