我运行了一个简单的实验,创建了一个非常简单的 python 包,其中包含以下文件:
在文件夹
my_package
:
# example.py
def foo(number: int, text: str) -> None:
print(number)
print(text)
空
__init__.py
在根文件夹中,有一个
setup.py
文件:
from setuptools import setup
setup(
name='ExamplePackage',
version='0.1.0',
author='An Awesome Coder',
author_email='[email protected]',
packages=['my_package'],
description='An awesome package that does something',
)
使用
python ./setup.py bdist_wheel
构建包后,我已将 .whl
文件复制到另一个 python 项目,运行 pip install ExamplePackage-0.1.0-py3-none-any.whl
并创建以下文件 main.py
:
from my_package.example import foo
# No mypy errors at all!
x = foo()
def internal_foo(number: int, text: str) -> None:
print(number)
print(text)
# Missing positional arguments "number", "text" in call to "internal_foo" [call-arg]
# "internal_foo" does not return a value [func-returns-value]
y = internal_foo()
我的
mypy.ini
看起来像这样:
[mypy]
ignore_missing_imports = True
show_error_codes = True
strict = True
check_untyped_defs = True
raise_exceptions = True
我很难理解为什么 mypy 可以轻松推断导入的
foo
函数的签名,但不会显示任何有关错误用法的错误
任何帮助将不胜感激
根据PEP-561(规范包类型数据使用的Python标准),有两种声明包类型的通用方法。
第一个(适用于您的情况)是
py.typed
标记。它是一个名为 py.typed
的空文件,放置在包根目录中(与最顶层 __init__.py
所在的位置相同;对于命名空间包,更喜欢将 py.typed
添加到每个子模块以避免不一致的解释)。它声明包包含内联类型信息。
重要的是不要忘记将其添加为包数据。您需要将其添加为
MANIFEST.in
条目 + 启用它所需的任何配置 - 对于基于 pyproject.toml
的软件包没有任何内容,include_package_data = True
用于 setup.py
和 setup.cfg
,对于非 setuptools 发行版还有其他内容(类似?) (诗歌、轻快、孵化或出于某种原因你需要的任何东西)。或者,它可以在 [tool.setuptools.package-data]
的 pyproject.toml
部分、package_data
中的 setup.py
、[options.package_data]
中的 setup.cfg
以及其他构建系统的类似声明中声明。还有其他选项,例如带有适当插件的 VCS 集成(来自 .gitignore
和类似文件),请参阅 setuptools 文档 here 或您选择的系统的文档。
另一个受支持的解决方案是使用存根文件 - 这些是带有
.pyi
扩展名的单独文件,仅提供函数/方法和变量/属性的类型。它们具有更高的优先级(例如,如果同时存在存根和 py.typed
,则使用存根类型),但这种情况不应该发生(如果代码可以内联注释,则不需要单独的存根)。存根包被命名为 <package>-stubs
。还有“并排”方法,允许将 .py
和 .pyi
彼此靠近。