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
的值相同。)
site
和 sysconfig
标准库模块已修改,以便根据 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 的 bin
或 Scripts
目录中放置了适用于 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虚拟环境的替代实现,但它不太健壮,因为它需要更多代码来了解它是在虚拟环境中运行还是不在虚拟环境中运行。
复制与软链接
本PEP中的技术通常与复制或符号链接的Python二进制文件(以及Windows上的其他必需的DLL)一起很好地工作。在可能的情况下,符号链接是首选,因为在底层Python安装升级的情况下,在venv中复制的Python可执行文件可能会与已安装的标准库不同步,需要手动升级。
符号链接存在一些跨平台的困难
- 并非所有Windows版本都支持符号链接,即使在支持的版本上,创建它们通常也需要管理员权限。
- 在OS X框架版本的Python中,sys.executable只是一个执行真实Python二进制文件的存根。对这个存根进行符号链接不起作用;它必须被复制。(幸运的是,存根也很小,并且不会因Python的错误修复升级而更改,因此复制它不是问题)。
因此,本PEP建议在所有平台上对二进制文件进行符号链接,除了Windows和OS X框架版本。提供了一个--symlink
选项,以便在支持它们的Windows版本上强制使用符号链接,如果具有相应的权限。(此选项对OS X框架版本没有影响,因为符号链接永远无法在其中工作,并且没有优势)。
在Windows上,如果未使用--symlink
,则意味着如果底层Python安装进行了升级,则应更新venv中的Python二进制文件和DLL,否则可能会与升级后的标准库不匹配。pyvenv脚本接受一个--upgrade
选项,以便轻松地对现有venv执行此升级。
包含文件
当前的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
类作为基类。
该create
EnvBuilder
类的方法说明了可用于自定义的挂钩
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_directories
、create_configuration
、setup_python
和post_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目录的名称(bin
或Scripts
)。__VENV_PYTHON__
被替换为环境可执行文件的绝对路径。
该DistributeEnvBuilder
参考实现中的子类说明了如何在实践中使用自定义挂钩来预安装Distribute到虚拟环境中。预计DistributeEnvBuilder
不会真正添加到Python核心,但它使参考实现更易于立即用于测试和探索目的。
向后兼容性
sys.prefix
的含义拆分
任何类似这样的虚拟环境工具(试图隔离site-packages,同时仍然使用基本Python的标准库,而无需将其符号链接到虚拟环境)都建议在两个不同的含义(以及其他含义)之间进行区分,这两个含义目前都包含在sys.prefix
中:“标准库在哪里?”和“第三方模块应安装到哪个site-packages位置?”这两个问题的答案。
可以通过为前缀或后缀引入一个新的sys
属性来处理这种拆分。这两种选择都可能导致与假设sys.prefix
具有其他含义编写的软件存在一些向后兼容性问题。(此类软件最好使用site
和sysconfig
模块中的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,它们主要使用distutils
和sysconfig
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