用于向后移植Python功能的命名空间
项目描述
就在几分钟前,我的手指悬停在键盘上方,准备将重要的match_hostname()函数(没有它,安全套接字层实际上并不真正安全!)从Python 3.2版本的ssl标准库向后移植到更早版本的Python。突然,我停了下来:我将如何命名在包索引中创建的新发行版来存放这个小型函数呢?
似乎为这种权宜之计占用包索引中的整个顶级名称是一种耻辱,直到有一天旧的Python版本被淘汰。
因此,我构思了这个backports命名空间包。它预留了一个命名空间,我们可以在其中愉快地放置所有我们希望从后续Python版本中剪切和粘贴的各种功能。我希望这会带来两个好处
它应该在包索引中提供更大的合理性,并带来一些组织。
当您准备将Python应用程序移植到Python的新版本时,您可以在代码中搜索任何命名backports包的import语句,并删除已到达您要升级的Python版本的特性的backports。
我曾考虑要求所有backports包在导入时发出警告,如果它们检测到它们正在运行的Python版本已经获得了它们提供的功能,但我认为这对真实用户来说会有些不友好,因为今天最广泛使用的Python版本默认仍然显示警告。
构建您自己的backports模块
将您自己的模块放置在 backports 命名空间内只需要几个简单步骤。首先,按照以下方式设置您的项目:
project/pyproject.toml project/backports/ project/backports/__init__.py <--- OPTIONAL - see below! project/backports/yourpkg/ project/backports/yourpkg/__init__.py project/backports/yourpkg/foo.py project/backports/yourpkg/bar.py
这样就将您的包放置在了 backports 命名空间内,因此您的包及其模块可以按照以下方式导入:
import backports.yourpkg import backports.yourpkg.foo
对于只支持 Python 3.3 或更高版本的项目的 backports.__init__.py 文件是可选的。如果省略,该包将成为 PEP 420 “原生”命名空间包。
对于支持 Python 3.2 或更早版本的项目的 backports.__init__.py 文件至关重要,其内容必须如下所示:
# A Python "namespace package" https://pythonlang.cn/dev/peps/pep-0382/ # This always goes inside of a namespace package's __init__.py from pkgutil import extend_path __path__ = extend_path(__path__, __name__)
如果省略此代码,则模块将被视为 import backports 的标准包,从而掩盖其他 backports 包的导入,并导致错误。
一个实现 pkgutil 风格命名空间的包的实时示例可以从 Python 包索引下载
http://pypi.python.org/pypi/backports.ssl_match_hostname/3.2a3
目前没有原生 backports 命名空间包的工作示例。有关详细信息,请参阅 backports#1。
如果功能已存在怎么办?
我还没有决定的问题是,如果一个 backports 包发现自己在一个足够现代的 Python 版本上,它是否应该简单地从标准库中导入其功能的“真实”版本,而不是提供替代方案。我的猜测是这并不是一个好主意,因为如果——出于某种原因——在回退中微调的代码和现代标准库中的官方代码之间存在不兼容性,那么当开发者在尝试移除回退时遇到这种破坏性,而不是因为用户尝试在更现代的 Python 版本上运行他们的程序时遇到这种破坏性,这对开发者来说会更好。