Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 561 – 分发和打包类型信息

作者:
Emma Harper Smith <emma at python.org>
状态:
最终版
类型:
标准跟踪
主题:
打包, 类型提示
创建日期:
2017年9月9日
Python 版本:
3.7
发布历史:
2017年9月10日,2017年9月12日,2017年10月6日,2017年10月26日,2018年4月12日

目录

重要

本PEP是一份历史文档:有关最新的规范和文档,请参阅库中的类型信息。规范的类型提示规范维护在类型提示规范站点;运行时类型提示行为在CPython文档中描述。

×

有关如何提议更改类型规范的信息,请参阅类型规范更新过程

摘要

PEP 484向Python引入了类型提示,旨在使其逐步且易于采用。目前,类型信息必须手动分发。本PEP提供了一种标准化方法,可利用现有工具以最少的工作量打包和分发类型信息,并为类型检查器解析模块和收集此信息以进行类型检查提供了顺序。

基本原理

目前,包作者希望分发具有内联类型信息的代码。此外,维护者希望分发存根文件以保持Python 2兼容性,同时使用较新的注解语法。但是,目前没有标准方法来分发带有类型信息的包。此外,如果有人希望私下发布存根文件,唯一可用的方法是通过设置MYPYPATH或等效项来手动指向存根。如果包可以公开发布,则可以将其添加到typeshed [1]。然而,这无法扩展,并给typeshed的维护者带来了负担。此外,它将存根中的错误修复与使用typeshed的工具的发布联系在一起。

PEP 484有一个关于分发类型提示信息的简短章节。在本章节中,PEP建议使用shared/typehints/pythonX.Y/来分发存根文件。然而,为每个第三方库手动添加存根文件路径无法扩展。人们采取的最简单方法是将site-packages添加到他们的MYPYPATH中,但这会导致类型检查器在高度动态的包(例如sqlalchemy和Django)上失败。

术语定义

“MAY”、“MUST”、“SHOULD”和“SHOULD NOT”的定义应按RFC 2119中的描述解释。

“内联” - 类型是运行时代码的一部分,使用PEP 526PEP 3107语法(文件名以.py结尾)。

“存根” - 仅包含类型信息,不包含运行时代码的文件(文件名以.pyi结尾)。

“发行版”是用于发布和分发版本的打包文件。(PEP 426

“模块”是一个包含Python运行时代码或存根类型信息的文件。

“包”是一个或多个目录,用于命名空间Python模块。(请注意包和发行版之间的区别。虽然大多数发行版以它们安装的一个包命名,但有些发行版安装多个包。)

规范

支持包中类型提示有多种动机和方法。本PEP认识到用户希望创建的三种类型的包

  1. 包维护者希望内联添加类型信息。
  2. 包维护者希望通过存根添加类型信息。
  3. 第三方或包维护者希望共享包的存根文件,但维护者不希望将其包含在包的源代码中。

本PEP旨在支持所有这三种情况,并使其易于添加到打包和部署中。

此规范的两个主要部分是打包规范和用于解析模块类型信息的解析顺序。类型检查规范旨在取代shared/typehints/pythonX.Y/ PEP 484规范

新的第三方存根库SHOULD通过本PEP中提出的第三方打包方法分发存根,而不是将其添加到typeshed。Typeshed将继续使用,但如果找到维护者,typeshed中的第三方存根MAY被拆分为自己的包。

打包类型信息

为了使类型信息的打包和分发尽可能简单容易,打包和分发通过现有框架完成。

希望支持其代码类型检查的包维护者必须在其支持类型提示的包中添加一个名为py.typed的标记文件。此标记递归适用:如果顶级包包含它,则其所有子包也必须支持类型检查。为了将此文件与包一起安装,维护者可以使用现有的打包选项,例如distutils中的package_data,如下所示。

Distutils选项示例

setup(
    ...,
    package_data = {
        'foopkg': ['py.typed'],
    },
    ...,
    )

对于命名空间包(参见PEP 420),py.typed文件应位于命名空间的子模块中,以避免冲突和提高清晰度。

本PEP不支持将类型信息作为仅模块发行版或命名空间包中单文件模块的一部分进行分发。

单文件模块应重构为一个包,并如上所述指示该包支持类型提示。

仅存根包

对于希望分发包含所有类型信息的存根文件的包维护者,最好将*.pyi存根文件与相应的*.py文件并排放置。但是,存根文件也可以放置在单独的包中并单独分发。如果第三方希望分发存根文件,这种方法也可能很有用。存根包的名称必须遵循foopkg-stubs的方案,用于名为foopkg的包的类型存根。请注意,对于仅存根包,不需要添加py.typed标记,因为名称*-stubs足以表明它是类型信息的来源。

鼓励寻求分发存根文件的第三方联系包维护者,讨论与包一起分发的问题。如果维护者不希望维护或打包存根文件或内联类型信息,则可以创建第三方仅存根包。

此外,仅存根发行版SHOULD通过正常的依赖关系数据指明其支持的运行时包版本。例如,存根包flyingcircus-stubs可以通过基于distutils的工具中的install_requires或其他打包工具中的等效项来指明它支持的运行时flyingcircus发行版的版本。请注意,在pip 9.0中,如果您更新flyingcircus-stubs,它将更新flyingcircus。在pip 9.0中,您可以使用--upgrade-strategy=only-if-needed标志。在pip 10.0中,这是默认行为。

对于命名空间包(参见PEP 420),仅存根包应仅在根命名空间包上使用-stubs后缀。所有仅存根命名空间包应省略__init__.pyi文件。对于仅存根包,py.typed标记文件不是必需的,但与具有内联类型的包类似,如果使用,它们应位于命名空间的子模块中,以避免冲突和提高清晰度。

例如,如果pentagonhexagon是单独的发行版,安装在命名空间包shapes.polygons中,则相应的仅类型发行版应生成如下所示的包

shapes-stubs
└── polygons
    └── pentagon
        └── __init__.pyi

shapes-stubs
└── polygons
    └── hexagon
        └── __init__.pyi

类型检查器模块解析顺序

以下是支持此PEP的类型检查器SHOULD解析包含类型信息的模块的顺序

  1. 手动放在路径开头的存根或Python源代码。类型检查器SHOULD提供此功能,以允许用户完全控制使用哪些存根,并修补包中损坏的存根/内联类型。在mypy中,可以使用$MYPYPATH环境变量来实现这一点。
  2. 用户代码 - 类型检查器正在运行的文件。
  3. 存根包 - 这些包应该取代任何已安装的内联包。它们可以在包foopkgfoopkg-stubs中找到。
  4. 带有py.typed标记文件的包 - 如果没有覆盖已安装的包,*并且*它选择进行类型检查,则SHOULD使用与包捆绑的类型(无论它们是在.pyi类型存根文件中还是内联在.py文件中)。
  5. Typeshed(如果使用) - 提供标准库类型和一些第三方库。

如果类型检查器在步骤3中识别出没有所需模块的仅存根命名空间包,它们应继续执行步骤4/5。类型检查器应通过缺少__init__.pyi来识别命名空间包。这允许不同的子包独立选择内联或仅存根。

类型检查器检查的Python版本与它们运行的版本不同时,MUST在那个Python版本的site-packages/dist-packages中找到类型信息。这可以通过例如pythonX.Y -c 'import site; print(site.getsitepackages())'进行查询。还建议类型检查器允许用户指向特定的Python二进制文件,以防它不在路径中。

部分存根包

许多存根包将只完成库类型接口的一部分,尤其是在初期。为了类型检查和代码编辑器的利益,包可以是“部分的”。这意味着在存根包中未找到的模块应该在上述模块解析顺序的第四和第五部分中查找,即内联包和typeshed。

类型检查器应合并存根包和运行时包或typeshed目录。这可以看作是将存根包复制到相应的运行时包或typeshed文件夹的同一目录中,并对组合的目录结构进行类型检查的功能等效。因此,类型检查器必须保持正常的解析顺序,即在*.py文件之前检查*.pyi文件。

如果存根包发行版是部分的,它必须在py.typed文件中包含partial\n。对于在命名空间包(参见PEP 420)中分发的存根包,py.typed文件应位于命名空间的子模块中。

类型检查器应将存根包中的命名空间包视为不完整,因为多个发行版可能会填充它们。存根包发行版中命名空间包内的常规包被视为完整,除非包含带有partial\npy.typed文件。

实施

所提议的指示支持类型提示的方案完全向后兼容,并且无需修改包工具。一个带有内联类型的示例包可在[typed_package]找到,以及一个[stub_package]。一个示例包检查器[pkg_checker],它读取已安装包的元数据并报告其状态,例如未类型化、内联类型化或存根包。

mypy 类型检查器有一个 PEP 561 搜索的实现,可以在 mypy 文档中阅读 [4]

[numpy-stubs] 是一个用于 numpy 发行版的真实仅存根包的例子。

致谢

如果没有 Ivan Levkivskyi、Jelle Zijlstra、Alyssa Coghlan、Daniel F Moisset、Andrey Vlasovskikh、Nathaniel Smith 和 Guido van Rossum 的想法、反馈和支持,这个 PEP 是不可能实现的。

版本历史

  • 2023-01-13
    • 澄清模块解析顺序的第4步适用于任何带有py.typed标记文件的包(而不仅仅是内联包)。
  • 2021-09-20
    • 澄清仅存根命名空间包的期望和类型检查器行为
    • 澄清命名空间包中单文件模块的处理。
  • 2018-07-09
    • 添加指向示例仅存根包的链接
  • 2018-06-19
    • 部分存根包可以查看 typeshed 以及运行时包
  • 2018-05-15
    • 添加部分存根包规范。
  • 2018-04-09
    • 添加对 mypy 实现的引用
    • 澄清存根包优先级。
  • 2018-02-02
    • 将仅存根包后缀更改为-stubs而不是_stubs。
    • 请注意,仅存根包不需要py.typed。
    • 添加关于pip和升级存根包的说明。
  • 2017-11-12
    • 重写为仅使用现有工具
    • 无需在元数据中指示类型信息的种类
    • 标记文件名称从.typeinfo更改为py.typed
  • 2017-11-10
    • 规范重写为使用包元数据而不是发行版元数据。
    • 删除仅存根包并合并到第三方包规范中。
    • 删除类型检查器考虑检查运行时版本的建议
    • 实现已更新以反映PEP的更改。
  • 2017-10-26
    • 添加了实现参考。
    • 添加了致谢和版本历史。
  • 2017-10-06
    • 重写为使用 .distinfo/METADATA 而不是 distutils 特定命令。
    • 澄清第三方存根包的版本控制。
  • 2017-09-11
    • 添加了有关当前解决方案和typeshed的信息。
    • 澄清基本原理。

参考资料


来源:https://github.com/python/peps/blob/main/peps/pep-0561.rst

最后修改:2025-02-01 08:55:40 GMT