前言

本文大部分内容来自PEP 405 Python Virtual Environments

虚拟环境存在的原因

  • python版本太多(导致其第三方的包也有众多版本),且有大版本py2和py3之分
  • 有的第三方包需要依赖特定版本的包

开发者对便捷切换不同版本,解决不同版本包依赖的需求,催生了一系列的virtual environment工具。

现存常见的虚拟环境工具有virtualenv, venv, pipenv。以下主要讲venv, 穿插其他两个第三方虚拟环境工具。

venv

venv的优势在于轻量级,且作为标准库的一部分,天生便得到了语言设计上的支持,而不是像第三方库一样,需要顺应语言的设计。(出于稳定性与可靠性的考虑,应该首选venv

virtualenv文档的首页指出,从python 3.3开始,virtualenv的一个子集被集成到python标准库venv下,venv有选择的挑选并实现了virtualenv的部分特性。
venvvirtualenv相比,有以下缺点:

  • virtualenv慢,因为没有使用app-data(下载第三方包时没有使用缓存)
  • 没有virutalenv那样强的扩展性
  • 不能够给任意版本创建虚拟环境,仅支持python 3.3及更高版本
  • 不能通过pip升级
  • 没有足够丰富的可编程API

此外,需要参考PEP 405中标准库加入venv的动机:

Existing virtual environment tools suffer from lack of support from the behavior of Python itself. Tools such as rvirtualenv , which do not copy the Python binary into the virtual environment, cannot provide reliable isolation from system site directories. Virtualenv, which does copy the Python binary, is forced to duplicate much of Python’s site module and manually symlink/copy an ever-changing set of standard-library modules into the virtual environment in order to perform a delicate boot-strapping dance at every startup. (Virtualenv must copy the binary in order to provide isolation, as Python dereferences a symlinked executable before searching for sys.prefix.)

The PYTHONHOME environment variable, Python’s only existing built-in solution for virtual environments, requires copying/symlinking the entire standard library into every environment. Copying the whole standard library is not a lightweight solution, and cross-platform support for symlinks remains inconsistent (even on Windows platforms that do support them, creating them often requires administrator privileges).

运行原理

未加入venv前, 一个python程序运行时会发生如下事情:

  • python二进制文件运行时会定位它的prefix(存储在sys.prefix), 并通过prefix寻找标准库的和其他关键文件的位置,通过site定位site-packages的位置
  • 假设没有设置PYTHONHOME, 可以通过在系统文件树中搜索标志性文件如os.py来确定prefix,如果没有找到标志性文件,则将prefix设置为编译时硬编码在二进制文件中的prefix

为了新增venv, PEP提议新增一步到上述的过程,即:

  • 如果venv生成的虚拟环境的文件夹中的pyvenv.cfg文件存在且和python executable相邻或者是和python executable所在的文件夹是兄弟(参考后文venv的目录结构),通过pyvenv.cfg(文件内容参考下文pyvenv.cfg)确定了用来构建虚拟环境的系统python executable文件路径,并将其设置为sys.base_prefix的值, 将sys.prefix设置为pyvenv.cfg所在的文件夹。
  • 如果pyvenv.cfg不存在或其中没有home选项,则sys.prefix将设置为sys.base_prefix,然后接下去的步骤保持和未加入venv前的步骤一致。
  • 类比于sys.prefix, sys.exec_prefix也新增加了sys.base_exec_prefix

通过sys.base_prefix, sys.base_exec_prefix, sys_prefix即可确定标准库和第三方库所在。基于上述规则,则可以构建出一个与系统python环境相隔离的虚拟环境。

venv使用方法:

1
python3 -m venv /path/virutal_environment_name

1
pyenv /path/virtual_environment_name

其中path为用户根据需求所选定虚拟环境所在文件路径,virutal_environment_name为虚拟环境名。运行命令后将在对应路径创建virtual_environment_name文件夹,并生成虚拟环境。 windows下文件结构为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
virtual_environment_name\
    include\
    Lib\
        \site-packages
    Scripts\
        activate
        activate.bat
        deactivate.bat
        pip.exe
        python.exe
        ...
    pyvenv.cfg

linux下为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
virtual_environment_name\
    include\
    lib\
        \pythonx.y
            \site-packages
    bin\
        activate
        deactivate
        pip
        python
        ...
    pyvenv.cfg

其中, pyenv.cfg为配置文件,Scripts为从系统python复制的各个相关的python二进制可运行文件。

注意事项

虚拟环境中并不存在所有必须的文件,使用venv生成的虚拟环境不包括头文件,标准库,DLLs(这些都是使用系统的)。这样便存在一个问题,当虚拟环境的python executable是采取复制这一形式时,若系统的python升级了,可能导致虚拟环境运行代码时出错(因为所写的代码可能和标准库不匹配了)。可以使用--upgrade选项进行解决。

pyenv.cfg

包含以下几个选项:

  • home, 指明虚拟环境所复制的系统python executable的路径
  • include-system-site-packages, 含义见下文
  • version

include-system-site-packages选项

include-system-site-packagestrue表明会使用系统中的python的site-packags。此外,重要的一点是搜索路径的先后顺序,当该选项为true时,会先搜索虚拟环境中安装的包,其次才会去系统site-packages中寻找。

生成虚拟环境时复制python executable而不是symlinks的原因

  • 不是所有的windows平台都支持symlinks, 且即使支持也需要管理员权限
  • On OS X framework builds of Python, sys.executable is just a stub that executes the real Python binary. Symlinking this stub does not work; it must be copied. 在支持symlinks的windows系统下可以使用--symlinks选项。

EnvBuilder提供定制化

待续

virtualenv

PEP 405virtualenv的评价:

On POSIX systems where the installed Python’s include files are found in ${base_prefix}/include/pythonX.X, virtualenv creates ${venv}/include/ and symlinks ${base_prefix}/include/pythonX.X to ${venv}/include/pythonX.X. On Windows, where Python’s include files are found in {{ sys.prefix }}/Include and symlinks are not reliably available, virtualenv copies {{ sys.prefix }}/Include to ${venv}/Include. This ensures that extension modules built and installed within the virtualenv will always find the Python header files they need in the expected location relative to sys.prefix. This solution is not ideal when an extension module installs its own header files, as the default installation location for those header files may be a symlink to a system directory that may not be writable. One installer, pip, explicitly works around this by installing header files to a nonstandard location ${venv}/include/site/pythonX.X/, as in Python there’s currently no standard abstraction for a site-specific include directory.

因为使用太少,不做过多评价,待续

pipenv

(我对pipenv的认知还停留在其莫名奇妙的卡住不动上 使用太少,不做过多评价,待续

pyenv

待续

virtualenvwrapper

待续

poetry

待续