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

Python 增强提案

PEP 405 – Python 虚拟环境

作者:
Carl Meyer <carl at oddbird.net>
BDFL 代表:
Alyssa Coghlan
状态:
最终
类型:
标准跟踪
主题:
打包
创建:
2011年6月13日
Python 版本:
3.3
历史记录:
2011年10月24日,2011年10月28日,2012年3月6日,2012年5月24日
决议:
Python-Dev 消息

目录

摘要

本 PEP 提出向 Python 添加一种用于轻量级“虚拟环境”的机制,这些虚拟环境拥有自己的站点目录,可以选择与系统站点目录隔离。每个虚拟环境都有自己的 Python 二进制文件(允许创建具有不同 Python 版本的环境),并且可以在其站点目录中拥有自己独立的一组已安装的 Python 包,但与基础安装的 Python 共享标准库。

动机

Python 虚拟环境的实用性已经通过现有第三方虚拟环境工具(主要是 Ian Bicking 的 virtualenv)的普及而得到充分证明。虚拟环境已被广泛用于依赖项管理和隔离、无需系统管理员访问权限即可轻松安装和使用 Python 包,以及跨多个 Python 版本自动测试 Python 软件等用途。

现有的虚拟环境工具存在缺乏 Python 本身行为支持的问题。诸如 rvirtualenv 之类的工具,不将 Python 二进制文件复制到虚拟环境中,无法提供与系统站点目录的可靠隔离。Virtualenv 会复制 Python 二进制文件,因此它被迫复制 Python 的 site 模块的大部分内容,并手动将不断变化的标准库模块集符号链接/复制到虚拟环境中,以便在每次启动时执行复杂的引导操作。(Virtualenv 必须复制二进制文件才能提供隔离,因为 Python 会在搜索 sys.prefix 之前取消对符号链接的可执行文件的引用。)

Python 唯一的内置虚拟环境解决方案 PYTHONHOME 环境变量需要将整个标准库复制/符号链接到每个环境中。复制整个标准库不是一种轻量级解决方案,并且对符号链接的跨平台支持仍然不一致(即使在支持符号链接的 Windows 平台上,创建它们通常也需要管理员权限)。

与 Python 集成的虚拟环境机制,并借鉴多年来使用现有第三方工具的经验,可以降低维护成本,提高可靠性,并更容易让所有 Python 用户使用。

规范

当执行 Python 二进制文件时,它会尝试确定其前缀(将其存储在 sys.prefix 中),然后使用该前缀查找标准库和其他关键文件,并由 site 模块确定站点包目录的位置。目前,前缀的查找方式(假设未设置 PYTHONHOME)是首先遍历文件系统树,查找表示标准库存在的标记文件(os.py),如果未找到,则回退到二进制文件中硬编码的构建时前缀。

本 PEP 提出在此搜索中添加一个新的第一步。如果在 Python 可执行文件旁边或其上方一个目录中找到 pyvenv.cfg 文件(如果可执行文件是符号链接,则不会取消引用它),则会扫描此文件以查找 key = value 形式的行。如果找到 home 键,则表示 Python 二进制文件属于虚拟环境,并且 home 键的值是包含用于创建此虚拟环境的 Python 可执行文件的目录。

在这种情况下,使用 home 键的值作为有效 Python 二进制文件位置继续正常的查找前缀,找到基础安装的前缀。sys.base_prefix 设置为此值,而 sys.prefix 设置为包含 pyvenv.cfg 的目录。

(如果未找到 pyvenv.cfg 或不包含 home 键,则查找前缀会照常继续,并且 sys.prefix 将等于 sys.base_prefix。)

此外,添加了 sys.base_exec_prefix,并在处理 sys.exec_prefix 时进行类似处理。(sys.exec_prefix 等效于 sys.prefix,但用于特定于平台的文件;默认情况下,它与 sys.prefix 的值相同。)

sitesysconfig 标准库模块已修改,以便根据 sys.base_prefix / sys.base_exec_prefix 查找标准库和头文件,而站点包目录(在 sysconfig 术语中为“purelib”和“platlib”)仍然根据 sys.prefix / sys.exec_prefix 查找。

因此,最简单的 Python 虚拟环境仅包含 Python 二进制文件的副本或符号链接以及一个 pyvenv.cfg 文件和一个 site-packages 目录。

与系统 site-packages 隔离

默认情况下,虚拟环境与系统级 site-packages 目录完全隔离。

如果 pyvenv.cfg 文件还包含一个键 include-system-site-packages,其值为 true(不区分大小写),则 site 模块还将在虚拟环境站点目录之后将系统站点目录添加到 sys.path 中。因此,系统安装的包仍然可以导入,但安装在虚拟环境中的同名包将优先使用。

PEP 370 用户级 site-packages 被认为是 venv 目的的系统 site-packages 的一部分:它们在隔离的 venv 中不可用,但在 include-system-site-packages = true venv 中可用。

创建虚拟环境

本 PEP 还提出向标准库添加一个新的 venv 模块,该模块实现了虚拟环境的创建。可以使用 -m 标记执行此模块。

python3 -m venv /path/to/new/virtual/environment

还提供了一个已安装的 pyvenv 脚本,以使操作更方便。

pyvenv /path/to/new/virtual/environment

运行此命令将创建目标目录(创建任何尚不存在的父目录),并在其中放置一个 pyvenv.cfg 文件,其中包含一个指向运行该命令的 Python 安装的 home 键。它还会创建一个包含 python3 可执行文件的副本(或符号链接)以及来自 packaging 标准库模块的 pysetup3 脚本的 bin/(或 Windows 上的 Scripts)子目录(以方便从 PyPI 向新 venv 安装包)。并创建一个(最初为空)的 lib/pythonX.Y/site-packages(或 Windows 上的 Lib\site-packages)子目录。

如果目标目录已存在,则会引发错误,除非提供了 --clear 选项,在这种情况下,将删除目标目录,并照常继续创建虚拟环境。

创建的 pyvenv.cfg 文件还包含 include-system-site-packages 键,如果使用 --system-site-packages 选项运行 pyvenv,则将其设置为 true,否则默认为 false

可以向 pyvenv 提供多个路径,在这种情况下,将根据给定的选项在每个提供的路径上创建相同的 venv。

venv 模块还在 venv 的 binScripts 目录中放置了适用于 POSIX 和 Windows 系统的“shell 激活脚本”。这些脚本只是将虚拟环境的 bin(或 Scripts)目录添加到用户 shell PATH 的前面。对于虚拟环境的使用,这并非严格必要(因为可以使用 venv 的 python 二进制文件或脚本的显式路径),但这很方便。

为了允许pysetup和其他Python包管理器以与安装到正常Python安装相同的方式将包安装到虚拟环境中,并避免在sysconfig中对虚拟环境进行特殊处理(除了在适当的地方使用sys.base_prefix代替sys.prefix之外),内部虚拟环境布局模仿了每个平台上Python安装本身的布局。因此,POSIX系统上典型的虚拟环境布局如下所示

pyvenv.cfg
bin/python3
bin/python
bin/pysetup3
include/
lib/python3.3/site-packages/

而在Windows系统上

pyvenv.cfg
Scripts/python.exe
Scripts/python3.dll
Scripts/pysetup3.exe
Scripts/pysetup3-script.py
        ... other DLLs and pyds...
Include/
Lib/site-packages/

安装到虚拟环境中的第三方包,其Python模块将放置在site-packages目录中,其可执行文件将放置在bin/Scripts中。

注意

在正常的Windows系统级安装中,Python二进制文件本身不会位于“Scripts/”子目录中,就像在默认的venv布局中那样。这在虚拟环境中非常有用,因为用户只需将单个目录添加到其shell的PATH中即可有效地“激活”虚拟环境。

注意

在Windows上,还需要将编译后的stdlib模块中的DLL和pyd文件复制或符号链接到env中,因为如果venv是从非系统范围的Python安装创建的,当从venv运行Python时,Windows将无法找到Python安装的这些文件的副本。

Sysconfig 安装方案和用户站点

这种方法明确选择不为venv引入新的sysconfig安装方案。相反,通过修改sys.prefix,我们确保基于sys.prefix位置的现有安装方案将在venv中正常工作。安装到其他安装方案(例如,用户站点方案)中,其路径不相对sys.prefix,将完全不受venv影响。

可以考虑创建基于虚拟特定sysconfig方案的Python虚拟环境的替代实现,但它不太健壮,因为它需要更多代码来了解它是在虚拟环境中运行还是不在虚拟环境中运行。

包含文件

当前的virtualenv处理包含文件的方式如下

在POSIX系统上,如果已安装的Python的包含文件位于${base_prefix}/include/pythonX.X中,则virtualenv创建${venv}/include/并将${base_prefix}/include/pythonX.X符号链接到${venv}/include/pythonX.X。在Windows上,如果Python的包含文件位于{{ sys.prefix }}/Include中,并且符号链接不可靠,则virtualenv将{{ sys.prefix }}/Include复制到${venv}/Include。这确保了在虚拟环境中构建和安装的扩展模块始终可以在相对于sys.prefix的预期位置找到所需的Python头文件。

当扩展模块安装自己的头文件时,此解决方案并不理想,因为这些头文件的默认安装位置可能是指向可能不可写的系统目录的符号链接。一个安装程序pip通过将头文件安装到非标准位置${venv}/include/site/pythonX.X/来明确解决此问题,因为在Python中目前没有用于站点特定包含目录的标准抽象。

本PEP提出了一种略有不同的方法,尽管它具有基本相同的效果以及相同的一组优点和缺点。我们不是将包含文件符号链接或复制到venv中,而是简单地修改sysconfig方案,以便始终相对于base_prefix而不是prefix查找头文件。(我们还在venv中创建了一个include/目录,以便安装程序在环境中安装包含文件时有地方存放)。

更好地处理distutils/packaging中的包含文件,以及由此带来的pyvenv,是一个可能需要其自身未来PEP的领域。目前,我们建议virtualenv的行为迄今为止在实践中已被证明至少“足够好”。

API

上面描述的高级方法使用了简单的API,该API为第三方虚拟环境创建者提供了根据其需求自定义环境创建的机制。

venv模块包含一个EnvBuilder类,该类在实例化时接受以下关键字参数

  • system_site_packages - 一个布尔值,指示系统Python site-packages是否应可用于环境。默认为False
  • clear - 一个布尔值,如果为真,则会删除任何现有的目标目录,而不是引发异常。默认为False
  • symlinks - 一个布尔值,指示是否尝试对Python二进制文件(以及任何必要的DLL或其他二进制文件,例如pythonw.exe)进行符号链接,而不是复制。默认为False

实例化的env-builder有一个create方法,它将目标目录的路径(相对于当前目录的绝对路径或相对路径)作为必需参数,该目标目录将包含虚拟环境。create方法在指定目录中创建环境,或引发适当的异常。

venv模块还提供了一个模块级create函数作为便利功能

def create(env_dir,
           system_site_packages=False, clear=False, use_symlinks=False):
    builder = EnvBuilder(
        system_site_packages=system_site_packages,
        clear=clear,
        use_symlinks=use_symlinks)
    builder.create(env_dir)

第三方虚拟环境工具的创建者可以自由地使用提供的EnvBuilder类作为基类。

createEnvBuilder类的方法说明了可用于自定义的挂钩

def create(self, env_dir):
    """
    Create a virtualized Python environment in a directory.

    :param env_dir: The target directory to create an environment in.

    """
    env_dir = os.path.abspath(env_dir)
    context = self.create_directories(env_dir)
    self.create_configuration(context)
    self.setup_python(context)
    self.post_setup(context)

每个方法create_directoriescreate_configurationsetup_pythonpost_setup都可以被覆盖。这些方法的功能是

  • create_directories - 创建环境目录和所有必要的目录,并返回一个上下文对象。这只是一个属性(例如路径)的持有者,供其他方法使用。
  • create_configuration - 在环境中创建pyvenv.cfg配置文件。
  • setup_python - 在环境中创建Python可执行文件的副本(以及在Windows下创建DLL)。
  • post_setup - 一个(默认为无操作)挂钩方法,可以在第三方子类中覆盖它,以便在虚拟环境中预安装包或安装脚本。

此外,EnvBuilder提供了一个实用程序方法,可以从子类中的post_setup调用它,以帮助将自定义脚本安装到虚拟环境中。该方法install_scripts接受context对象(见上文)和目录路径作为参数。该目录应包含子目录“common”、“posix”、“nt”,每个子目录都包含要放置到环境中bin目录的脚本。“common”和对应于os.name的目录的内容在执行一些占位符文本替换后被复制

  • __VENV_DIR__被替换为环境目录的绝对路径。
  • __VENV_NAME__被替换为环境名称(环境目录的最后一个路径段)。
  • __VENV_BIN_NAME__被替换为bin目录的名称(binScripts)。
  • __VENV_PYTHON__被替换为环境可执行文件的绝对路径。

DistributeEnvBuilder参考实现中的子类说明了如何在实践中使用自定义挂钩来预安装Distribute到虚拟环境中。预计DistributeEnvBuilder不会真正添加到Python核心,但它使参考实现更易于立即用于测试和探索目的。

向后兼容性

sys.prefix 的含义拆分

任何类似这样的虚拟环境工具(试图隔离site-packages,同时仍然使用基本Python的标准库,而无需将其符号链接到虚拟环境)都建议在两个不同的含义(以及其他含义)之间进行区分,这两个含义目前都包含在sys.prefix中:“标准库在哪里?”和“第三方模块应安装到哪个site-packages位置?”这两个问题的答案。

可以通过为前缀或后缀引入一个新的sys属性来处理这种拆分。这两种选择都可能导致与假设sys.prefix具有其他含义编写的软件存在一些向后兼容性问题。(此类软件最好使用sitesysconfig模块中的API来回答这些问题,而不是直接使用sys.prefix,在这种情况下,不存在向后兼容性问题,但在实践中,有时会使用sys.prefix。)

sys.prefix文档将其描述为“一个字符串,给出安装平台无关Python文件的特定于站点的目录前缀”,并特别提到在sys.prefix下找到的标准库和头文件。它没有提到site-packages

维护此已记录的定义意味着让sys.prefix指向基本系统安装(标准库和头文件所在的位置),并在sys中引入一个新值(例如sys.site_prefix)来指向site-packages的前缀。这将维护sys.prefix的已记录语义,但如果第三方代码使用sys.prefix而不是sys.site_prefix或合适的site API来查找site-packages目录,则可能会破坏隔离。

最值得注意的案例可能是setuptools及其分支distribute,它们主要使用distutilssysconfig API,但确实直接使用sys.prefix来构建用于预检的站点目录列表,其中pth文件可以有效地放置。

否则,Google Code Search 会显示出软件包之间使用情况的大致均匀混合,一些软件包使用sys.prefix来构建site-packages路径,而另一些软件包则使用它来例如从代码执行跟踪中消除标准库。

尽管需要修改sys.prefix的已记录定义,但本PEP更倾向于让sys.prefix指向虚拟环境(其中找到site-packages),并引入sys.base_prefix来指向标准库和Python头文件。此选择的理由

  • 最好是在更强的虚拟环境隔离方面出错。
  • Virtualenv已修改sys.prefix以指向虚拟环境,并且在实践中这并没有问题。
  • setuptools/distribute不需要进行任何修改。

对其他 Python 实现的影响

本PEP的大多数更改发生在标准库中,标准库由其他Python实现共享,并且不应出现任何问题。

其他Python实现将需要复制解释器引导程序的新sys.prefix查找行为,包括定位和解析pyvenv.cfg文件(如果存在)。

参考实现

参考实现可以在CPython Mercurial存储库的克隆中找到。要测试它,请构建并运行bin/pyvenv /path/to/new/venv以创建虚拟环境。


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

上次修改时间:2023-10-11 12:05:51 GMT