跳到主要内容

CEP 17 - repodata 中可选的 Python site-packages 路径

标题repodata 中可选的 Python site-packages 路径
状态已接受
作者Jonathan Helmus <jjhelmus@gmail.com>
创建于2024 年 9 月 13 日
更新于2024 年 9 月 13 日
讨论https://github.com/conda/ceps/pull/90
实施https://github.com/conda/conda/pull/14256

为避免混淆,本文档将使用样式化的 python 来表示具有该名称的 conda 包。其他使用 “Python” 的情况后将跟有限定词,例如 “编程语言” 或 “解释器”。Python 编程语言的特定实现将通过名称来指代,例如 CPython 或 PyPy。

摘要

我们提议在 repodata.json 中添加一个新的可选字段,python 包可以使用该字段来指定安装 noarch: python 包时的目标路径。

背景

在 conda 生态系统中,名为 python 的包直接或通过依赖项提供 Python 编程语言的解释器。此解释器通常是 CPython,但也可能是替代实现,例如 PyPy 或 GraalPy。

conda、mamba 和 pixi 等工具支持将 noarch: python 包安装到 conda 环境中。其他 conda 包的内容根据其在包中的路径链接到目标环境,而 noarch: python 包的 “site-packages” 目录中的文件则链接到目标环境的 site-packages 目录中。

在 conda 24.7.1 和许多其他版本中,环境中 site-packages 目录的位置在 Windows 上是 Lib/site-packages,在其他平台上是 lib/pythonX.Y/site-packages,其中 X 和 Y 是环境中安装的 python 包的主版本号和次版本号。这些路径是 CPython 的默认路径,但对于 Python 编程语言的替代实现或 CPython 的某些配置而言,这些路径是不正确的。例如,PyPy 在 POSIX 系统上使用 lib/pypyX.Y/site-packages

在这些情况下,在 python 包中使用笨拙的解决方法(通常是符号链接)来支持 noarch: python 包的安装。在下一节中,将提出一个新的字段,该字段允许 python 包显式声明 site-packages 目录的位置,从而避免对这些解决方法的需求。

规范

可选字段 python_site_packages_path 可以作为字符串包含在 conda 包的 info/index.json 文件中,以及相应的包的 repodata.json 条目中。定义后,此字段指定 Python 解释器的 site-packages 目录相对于环境根目录的路径。如果将包含此字段的 python 包安装到环境中,则所有安装到环境中的 noarch: python 包都会将其 “site-packages” 目录中的文件链接到此字段指定的路径。

值 “null” 可用于指示未指定此字段。在这种情况下,可以不将该字段包含在 repodata.json 中。

如果此字段缺失或指定为 null,则将使用默认的 site-packages 路径。如果环境的目标平台是 Windows,则此路径为 Lib/site-packages,在其他平台上则为 lib/pythonX.Y/site-packages,其中 X 和 Y 是环境中安装的 python 包的主版本号和次版本号。这与 conda、mamba 和 pixi 的当前行为一致。

此字段应仅包含在 python 包的元数据中,但工具不得因其他名称的包包含此字段而失败,而应忽略该条目。

为了避免不必要地增加 repodata.json 的大小,强烈建议仅在需要时才将此字段包含在 python 包中,即 site-packages 目录不是默认值的情况下。具有其他名称的包或使用默认 site-packages 路径的 python 包不应包含此字段。

此路径不得指向环境根目录之外的位置(例如,它不能向上导航目录或为绝对路径)。如果包指定的路径违反此要求,则不得安装该包,并且应向用户显示相应的错误。

一种可能的检查 python_site_packages_path 字段有效性的方法是使用以下函数

def is_valid(target_prefix: str, python_site_packages_path: str) -> bool:
target_prefix = os.path.realpath(target_prefix)
full_path = os.path.realpath(os.path.join(target_prefix, python_site_packages_path))
test_prefix = os.path.commonpath((target_prefix, full_path))
return test_prefix == target_prefix

当索引包含此可选字段的包时,python_site_packages_path 字段将包含在包的 repodata 条目中。例如,使用 free-threading 构建配置的 python-3.13.0rc1 包的 repodata 条目可能如下所示

    "python-3.13.0rc1-haa6bb3f_0_cpython_cp313t.tar.bz2": {
"build": "haa6bb3f_0_cpython_cp313t",
"build_number": 0,
"depends": [
"bzip2 >=1.0.8,<2.0a0",
"expat >=2.6.2,<3.0a0",
"libffi >=3.4,<4.0a0",
"ncurses >=6.4,<7.0a0",
"openssl >=3.0.14,<4.0a0",
"readline >=8.1.2,<9.0a0",
"sqlite >=3.45.3,<4.0a0",
"tk >=8.6.14,<8.7.0a0",
"tzdata",
"xz >=5.4.6,<6.0a0",
"zlib >=1.2.13,<1.3.0a0"
],
"license": "PSF-2.0",
"license_family": "PSF",
"timestamp": 1722610680768,
"track_features": "free-threading",
"md5": "c09289eb86239e1221533457d861f1a3",
"name": "python",
"size": 16332720,
"subdir": osx-arm64,
"version": "3.13.0rc1",
"sha256": "fa0ae22c13450fe6c30c754ee5efbd7fe7e7533b878d7be96e74de56211d19df",
"python_site_packages_path": "lib/python3.13t/site-packages"
},

此字段也将包含在 repodata 衍生产品中,例如 current_repodata.json 或分片 repodata(如果适用)。

由于此字段存在于 repodata.json 中,因此可以对其进行热修复以纠正错误或遗漏。这允许现有的 python 包追溯指定其 site-packages 目录的位置。

动机

此提案的动机是 CPython 3.13 的 free-threading 配置,它在 POSIX 系统上使用不同的 site-packages 位置。具体而言,free-threading 构建使用 lib/python3.13t/site-packages

这些路径之间的符号链接或 sitecustomize.py 中的逻辑允许安装到不正确的 site-packages 目录中的包运行,但这并非理想。

有关更多详细信息和讨论,请参阅 conda issue 14053

尽管这是由 CPython 的 free-threading 配置驱动的,但相同的根本问题也发生在许多非 CPython 实现中。这包括 PyPy 和 GraalPy,它们对 site-packages 使用与 CPython 不同的路径。conda-forge 频道中针对这两者的 conda 包都使用符号链接来解决将文件链接到不正确的 site-packages 目录的工具。

安全性

由于此提案可能会更改文件的安装位置,因此值得考虑此更改会带来哪些(如果有)安全实施。

主要的安全性问题是,通过允许 python 包指定文件的安装位置,恶意包可能会写入或覆盖包含恶意内容的文件。

如果文件只能安装到安装包的环境中,则攻击造成的损害是有限的,因为恶意包已经可以将文件安装到环境中的任何位置。

例如,恶意 python 包可以指定无效的 site-packages 路径,在这种情况下,环境中安装的 noarch: python 包将无法工作。这很不方便,但不是安全问题,应通过删除或禁用有问题的包来解决。

恶意包还可以指定一个 site-packages 路径,该路径会导致 noarch: python 包中的文件填充或替换环境中的关键文件。但是这种类型的攻击已经可能发生,实际上,python 包本身可以包含这些文件。

更令人担忧的是包将文件写入环境之外的可能性。这可能会用恶意内容替换关键系统文件。由于存在这种风险,因此环境之外的路径作为 python_site_packages_path 条目无效。工具必须检查该字段是否有效,并在无效时报错。

向后兼容性

此更改保持向后兼容性。所有现有的 python 包都不包含此字段。未指定此字段时的行为与 conda、mamba 和 pixi 当前的行为一致。唯一的行为更改发生在包含此字段时。

由于现有版本的 conda、mamba 和 pixi 不支持此字段,因此 python 包应继续提供解决方法,以允许将文件安装到不正确的 site-packages 目录中,直到此提案得到实施并在足够长的时间内可用为止。

其他章节

讨论和考虑了许多替代方案来解决此问题。这些包括

  • 使用符号链接或 sitecustomize.py 文件来允许链接到不正确的 site-packages 目录。这是一种针对该问题的解决方法,而不是修复。
  • 查询解释器以确定 site-packages 目录。这被拒绝了,因为它需要在安装步骤中运行解释器,并且要求在链接 noarch: python 包之前安装并运行 python 包。
  • 使用 pythonpython_abi 包的构建字符串来确定 site-packages 位置。这是一种获取信息的间接方法,可以更有效地显式指定该信息。
  • 将此信息存储在另一个元数据文件中,例如 about.jsonpaths.json。这种方法的缺点是数据无法热修复,并且在下载并解压缩 python 包之前,site-packages 目录的路径是未知的,这可能会限制链接包的顺序。

有关更多替代方案和讨论,请参阅 conda issue 14053

所有 CEP 均明确采用 CC0 1.0 Universal