目录

反向移植

Python 的许多新增功能都提供了针对旧版本 Python 的反向移植。以下是一些使用反向移植的技巧。

  • 反向移植是一个非常轻量级的依赖项,因为消除它的方法之一就是升级 Python。
  • 当你放弃旧版本的 Python 时,反向移植将不再是依赖项。
  • 如果一个包已纳入标准库,那么它应该设计良好、文档齐全,并且可能是人们学习的知识。
  • 反向移植不会被新版本的 Python 打破,因为你不会在新版本的 Python 上使用反向移植。

使用反向移植的规则如下。

条件需求

将它有条件地添加到你的需求中。这看起来像这样。

[project]
dependencies = [
    "importlib_metadata>=4.6; python_version<'3.10'",
    "importlib_resources; python_version<'3.9'",
    "typing_extensions>=4.6; python_version<'3.11'",
]

条件使用

始终使用以下习语有条件地使用反向移植。

import sys

if sys.version_info < (3, 10):
    import importlib_metadata as metadata
else:
    from importlib import metadata

永远不要使用 try/except 来处理反向移植。上述习语具有以下优点。

  • 条件导入的原因在代码中表达。你无需添加注释来解释需要此操作来支持 X.Y 版本的 Python;它就在代码中,供读者查看。
  • MyPy 等静态分析工具可以理解此检查,并会正确处理。
  • pyupgrade 和 Ruff 的 pyupgrade 等静态自动修正器会在你升级 Python 版本时自动删除无用的分支。你也可以手动查看 git grep "sys.version_info" 的输出以清理这些内容。
  • 你可以选择要切换到的特定版本的 Python,即使导入在更早的时候就可用。在本例中,import.metadata 在 3.8 中添加,但重要修复程序在 3.10 中发布。
  • 它与你的条件需求相匹配。

文件中的位置

将所有条件反向移植放置在通用位置是一种很好的做法。以下是一个建议:将所有导入放在 src/<package>/_compat 中,采用标准库结构。这在你的代码库中提供了非常简洁、可搜索的导入,看起来与正常使用类似。

例如,你可以有一个文件 src/<package>/_compat/typing.py,其内容如下。

from __future__ import annotations

import sys

if sys.version_info < (3, 10):
    from typing_extensions import TypeAlias
else:
    from typing import TypeAlias

if sys.version_info < (3, 11):
    from typing_extensions import Self, assert_never
else:
    from typing import Self, assert_never

__all__ = ["TypeAlias", "Self", "assert_never"]


def __dir__() -> list[str]:
    return __all__

Ruff 需要知道你是否正在重新导出 typing/typing_extensions,因此请确保在 pyproject.toml 中将 typing-modules = ["<package>._compat.typing"] 添加到 Ruff 的配置中。

类型依赖

虽然通常不需要这样做,但你可以通过用 typing.TYPE_CHECKING 来保护导入,从而在运行时避免使用 typing_extensions 反向移植。 typing_extensions 是一个第一方反向移植,并且非常常用,因此你的依赖项很可能已经拉取了它。但是,如果你真的想要最大程度地减少依赖项,你可以在你的类型反向移植重新导出文件中执行此操作。

常用反向移植包

  • typing_extensionstyping 中的新功能首先在这里添加。
  • importlib_metadata:在 3.8 中作为 importlib.metadata 添加,在 3.10 中进行了重要更新(并且不再是临时添加)。
  • importlib_resources:在 3.7 中作为 importlib.resources 添加,在 3.9 中进行了重要更新(添加了 files,这是推荐的公共 API!)。
  • tomli:在 3.11 中作为 tomllib 添加。当 TOML 1.1 发布时,它可能会再次变得重要。(请注意,toml_w 不在标准库中)。
  • exceptiongroup:3.11 中的一个新的内置函数(ExceptionGroup)。
  • tz-data:3.9 中 zoneinfo 的第一方 PyPI 版本,但具有更新的时区信息。