28.3. venv — 虚拟环境的创建

版本3.3中的新功能。

源代码: Lib / venv /

venv 模块与它们自己的网站目录,选择分离系统站点目录,为创建轻量级的"虚拟环境"提供支持。每个虚拟环境都有他自己的python二进制文件(允许在里面创建另一个版本的python),同时有自己独立的开发环境,可以在里面安装各种只在这个环境里面使用的包

有关Python虚拟环境的更多信息,请参见 PEP 405

28.3.1. Creating virtual environments

创建 虚拟环境 是通过执行 pyvenv 脚本︰

pyvenv /path/to/new/virtual/environment

运行此命令将创建目标目录 (创建任何不存在的父目录) 和 pyvenv.cfg 文件置于具有指向 Python 安装在运行命令从 首页 键。它还创建一个 bin (或 Windows 脚本) 子目录包含 python 的二进制文件 (或在 Windows 的情况下的二进制文件) 的副本。它还创建 (最初为空) lib/pythonX.Y/site-packages 子目录 (在 Windows 上,这是 Lib\site 软件包)。

在Windows上,如果没有相关的PATH和PATHEXT设置,则可能需要调用pyvenv脚本:

c:\Temp>c:\Python35\python c:\Python35\Tools\Scripts\pyvenv.py myenv

或等效地:

c:\Temp>c:\Python35\python -m venv myenv

该命令如果以-h运行,将显示可用的选项:

usage: venv [-h] [--system-site-packages] [--symlinks | --copies] [--clear]
            [--upgrade] [--without-pip]
            ENV_DIR [ENV_DIR ...]

Creates virtual Python environments in one or more target directories.

positional arguments:
  ENV_DIR             A directory to create the environment in.

optional arguments:
  -h, --help             show this help message and exit
  --system-site-packages Give the virtual environment access to the system
                         site-packages dir.
  --symlinks             Try to use symlinks rather than copies, when symlinks
                         are not the default for the platform.
  --copies               Try to use copies rather than symlinks, even when
                         symlinks are the default for the platform.
  --clear                Delete the contents of the environment directory if it
                         already exists, before environment creation.
  --upgrade              Upgrade the environment directory to use this version
                         of Python, assuming Python has been upgraded in-place.
  --without-pip          Skips installing or upgrading pip in the virtual
                         environment (pip is bootstrapped by default)

根据如何调用venv功能,使用消息可以稍微改变,例如,引用pyvenv而不是venv

在版本3.4中更改:默认安装pip,添加了--without-pip--copies选项

在版本3.4中已更改:在早期版本中,如果目标目录已存在,则会出现错误,除非--clear--upgrade现在,如果指定了现有目录,则删除其内容,并且处理该目录,就好像它是新创建的。

The created pyvenv.cfg file also includes the include-system-site-packages key, set to true if venv is run with the --system-site-packages option, false otherwise.

除非给出--without-pip选项,否则将调用ensurepip以将pip引导到虚拟环境中。

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

一旦创建了venv,它可以使用venv的二进制目录中的脚本“激活”。脚本的调用是平台特定的:

平台贝壳命令激活虚拟环境
Posixbash / zsh$ source / bin / activate
$。/bin/activate.fish
csh / tcsh$ source /bin/activate.csh
视窗cmd.exeC:\> \ Scripts \ activate.bat
PowerShellPS C:\> \ Scripts \ Activate.ps1

您不具体需要来激活环境;激活只是将venv的二进制目录添加到你的路径,所以“python”调用venv的Python解释器,你可以运行安装的脚本,而不必使用他们的完整路径。但是,安装在venv中的所有脚本都应该可以在不激活的情况下运行,并且可以自动运行venv的Python。

您可以通过在shell中键入“deactivate”来停用venv。确切的机制是特定于平台的:例如,Bash激活脚本定义了一个“deactivate”函数,而在Windows上有称为deactivate.batDeactivate.ps1

新版本3.4: fishcsh激活脚本。

注意

虚拟环境(也称为venv)是一个Python环境,使得安装在其中的Python解释器,库和脚本与安装在其他虚拟环境中的Python解释器,库和脚本隔离,安装在一个“系统”Python,即其中一个作为操作系统的一部分安装。

venv是一个目录树,它包含Python可执行文件和其他文件,这些文件指示它是一个venv。

常用的安装工具,例如Setuptoolspip,可以正常工作当venv处于活动状态时,他们将Python软件包安装到venv中,而不需要明确地通知。

当活动时(即the venv’s Python interpreter is running), the attributes sys.prefix and sys.exec_prefix point to the base directory of the venv, whereas sys.base_prefix and sys.base_exec_prefix point to the non-venv Python installation which was used to create the venv. 如果venv不活动,则sys.prefixsys.base_prefix相同,sys.exec_prefixsys.base_exec_prefix(他们都指向一个非venv Python安装)。

当venv处于活动状态时,将更改所有distutils配置文件的任何更改安装路径的选项都将被忽略,以防止项目无意中安装在虚拟环境之外。

When working in a command shell, users can make a venv active by running an activate script in the venv’s executables directory (the precise filename is shell-dependent), which prepends the venv’s directory for executables to the PATH environment variable for the running shell. 在其他情况下应该没有必要激活venv - 脚本安装到venvs有一个shebang线指向venv的Python解释器。这意味着脚本将与该解释器一起运行,而不管PATH的值。在Windows上,如果您安装了适用于Windows的Python Launcher(在3.3中已添加到Python中,请参阅 PEP 397以获取更多详细信息),才支持shebang行处理。因此,在Windows资源管理器窗口中双击已安装的脚本应该使用正确的解释器运行脚本,而不需要在PATH中对其venv进行任何引用。

28.3.2. API

上面描述的高级方法使用简单的API,其为第三方虚拟环境创建者提供用于根据他们的需要定制环境创建的机制,EnvBuilder类。

class venv.EnvBuilder(system_site_packages=False, clear=False, symlinks=False, upgrade=False, with_pip=False)

EnvBuilder类在实例化时接受以下关键字参数:

  • system_site_packages - 一个布尔值,指示系统Python site-packages应该可用于环境(默认为False)。
  • clear - 一个布尔值,如果为true,将在创建环境之前删除任何现有目标目录的内容。
  • symlinks - 一个布尔值,指示是否尝试符号链接Python二进制文件(以及任何必需的DLL或其他二进制文件,例如。pythonw.exe),而不是复制。在Linux和Unix系统上默认为True,但在Windows上为False
  • upgrade - 一个布尔值,如果为true,将使用正在运行的Python升级现有环境,以便在该Python已就地升级时使用(默认为False) 。
  • with_pip - 一个布尔值,如果为true,则确保pip安装在虚拟环境中。这使用ensurepip--default-pip选项。

在版本3.4中已更改:添加了with_pip参数

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

返回的env-builder是一个对象,它有一个方法create

create(env_dir)

此方法将包含虚拟环境的目标目录的路径(绝对或相对于当前目录)作为必需参数。create方法将在指定的目录中创建环境,或引入适当的异常。

EnvBuilder类的create方法说明了可用于子类自定义的钩子:

def create(self, env_dir):
    """
    Create a virtualized Python environment in a directory.
    env_dir is the target directory to create an environment in.
    """
    env_dir = os.path.abspath(env_dir)
    context = self.ensure_directories(env_dir)
    self.create_configuration(context)
    self.setup_python(context)
    self.setup_scripts(context)
    self.post_setup(context)

ensure_directories()create_configuration()setup_python()setup_scripts()post_setup()可以覆盖。

ensure_directories(env_dir)

创建环境目录和所有必需的目录,并返回上下文对象。这只是属性(例如路径)的持有者,供其他方法使用。只要指定clearupgrade允许在现有环境目录上操作,就允许这些目录存在。

create_configuration(context)

在环境中创建pyvenv.cfg配置文件。

setup_python(context)

在环境中创建Python可执行文件(以及在Windows,DLL下)的副本。在POSIX系统上,如果使用特定的可执行文件python3.x,将会创建指向该可执行文件的符号链接pythonpython3,除非具有这些名称的文件已存在。

setup_scripts(context)

在虚拟环境中安装适用于平台的激活脚本。

post_setup(context)

可以在第三方实现中覆盖以在虚拟环境中预安装软件包或执行其他后创建步骤的占位符方法。

此外,EnvBuilder提供了可以从setup_scripts()post_setup()在子类中调用的实用程序方法,以帮助将自定义脚本虚拟环境。

install_scripts(context, path)

path是应包含子目录“common”,“posix”,“nt”的目录的路径,每个目录包含指向环境中bin目录的脚本。在对占位符进行一些文本替换后,会复制“common”和与os.name对应的目录的内容:

  • __VENV_DIR__将替换为环境目录的绝对路径。
  • __VENV_NAME__替换为环境名称(环境目录的最终路径段)。
  • __VENV_PROMPT__被替换为提示符(环绕名称由圆括号括起来并带有以下空格)
  • __VENV_BIN_NAME__将替换为bin目录的名称(binScripts)。
  • __VENV_PYTHON__将替换为环境的可执行文件的绝对路径。

允许存在目录(用于在升级现有环境时)。

还有一个模块级的方便功能:

venv.create(env_dir, system_site_packages=False, clear=False, symlinks=False, with_pip=False)

使用给定的关键字参数创建EnvBuilder,并使用env_dir参数调用其create()方法。

在版本3.4中已更改:添加了with_pip参数

28.3.3. An example of extending EnvBuilder

以下脚本显示了如何通过实现一个子类来扩展EnvBuilder,该子类安装了setuptools和pip到创建的venv中:

import os
import os.path
from subprocess import Popen, PIPE
import sys
from threading import Thread
from urllib.parse import urlparse
from urllib.request import urlretrieve
import venv

class ExtendedEnvBuilder(venv.EnvBuilder):
    """
    This builder installs setuptools and pip so that you can pip or
    easy_install other packages into the created environment.

    :param nodist: If True, setuptools and pip are not installed into the
                   created environment.
    :param nopip: If True, pip is not installed into the created
                  environment.
    :param progress: If setuptools or pip are installed, the progress of the
                     installation can be monitored by passing a progress
                     callable. If specified, it is called with two
                     arguments: a string indicating some progress, and a
                     context indicating where the string is coming from.
                     The context argument can have one of three values:
                     'main', indicating that it is called from virtualize()
                     itself, and 'stdout' and 'stderr', which are obtained
                     by reading lines from the output streams of a subprocess
                     which is used to install the app.

                     If a callable is not specified, default progress
                     information is output to sys.stderr.
    """

    def __init__(self, *args, **kwargs):
        self.nodist = kwargs.pop('nodist', False)
        self.nopip = kwargs.pop('nopip', False)
        self.progress = kwargs.pop('progress', None)
        self.verbose = kwargs.pop('verbose', False)
        super().__init__(*args, **kwargs)

    def post_setup(self, context):
        """
        Set up any packages which need to be pre-installed into the
        environment being created.

        :param context: The information for the environment creation request
                        being processed.
        """
        os.environ['VIRTUAL_ENV'] = context.env_dir
        if not self.nodist:
            self.install_setuptools(context)
        # Can't install pip without setuptools
        if not self.nopip and not self.nodist:
            self.install_pip(context)

    def reader(self, stream, context):
        """
        Read lines from a subprocess' output stream and either pass to a progress
        callable (if specified) or write progress information to sys.stderr.
        """
        progress = self.progress
        while True:
            s = stream.readline()
            if not s:
                break
            if progress is not None:
                progress(s, context)
            else:
                if not self.verbose:
                    sys.stderr.write('.')
                else:
                    sys.stderr.write(s.decode('utf-8'))
                sys.stderr.flush()
        stream.close()

    def install_script(self, context, name, url):
        _, _, path, _, _, _ = urlparse(url)
        fn = os.path.split(path)[-1]
        binpath = context.bin_path
        distpath = os.path.join(binpath, fn)
        # Download script into the env's binaries folder
        urlretrieve(url, distpath)
        progress = self.progress
        if self.verbose:
            term = '\n'
        else:
            term = ''
        if progress is not None:
            progress('Installing %s ...%s' % (name, term), 'main')
        else:
            sys.stderr.write('Installing %s ...%s' % (name, term))
            sys.stderr.flush()
        # Install in the env
        args = [context.env_exe, fn]
        p = Popen(args, stdout=PIPE, stderr=PIPE, cwd=binpath)
        t1 = Thread(target=self.reader, args=(p.stdout, 'stdout'))
        t1.start()
        t2 = Thread(target=self.reader, args=(p.stderr, 'stderr'))
        t2.start()
        p.wait()
        t1.join()
        t2.join()
        if progress is not None:
            progress('done.', 'main')
        else:
            sys.stderr.write('done.\n')
        # Clean up - no longer needed
        os.unlink(distpath)

    def install_setuptools(self, context):
        """
        Install setuptools in the environment.

        :param context: The information for the environment creation request
                        being processed.
        """
        url = 'https://bitbucket.org/pypa/setuptools/downloads/ez_setup.py'
        self.install_script(context, 'setuptools', url)
        # clear up the setuptools archive which gets downloaded
        pred = lambda o: o.startswith('setuptools-') and o.endswith('.tar.gz')
        files = filter(pred, os.listdir(context.bin_path))
        for f in files:
            f = os.path.join(context.bin_path, f)
            os.unlink(f)

    def install_pip(self, context):
        """
        Install pip in the environment.

        :param context: The information for the environment creation request
                        being processed.
        """
        url = 'https://raw.github.com/pypa/pip/master/contrib/get-pip.py'
        self.install_script(context, 'pip', url)

def main(args=None):
    compatible = True
    if sys.version_info < (3, 3):
        compatible = False
    elif not hasattr(sys, 'base_prefix'):
        compatible = False
    if not compatible:
        raise ValueError('This script is only for use with '
                         'Python 3.3 or later')
    else:
        import argparse

        parser = argparse.ArgumentParser(prog=__name__,
                                         description='Creates virtual Python '
                                                     'environments in one or '
                                                     'more target '
                                                     'directories.')
        parser.add_argument('dirs', metavar='ENV_DIR', nargs='+',
                            help='A directory to create the environment in.')
        parser.add_argument('--no-setuptools', default=False,
                            action='store_true', dest='nodist',
                            help="Don't install setuptools or pip in the "
                                 "virtual environment.")
        parser.add_argument('--no-pip', default=False,
                            action='store_true', dest='nopip',
                            help="Don't install pip in the virtual "
                                 "environment.")
        parser.add_argument('--system-site-packages', default=False,
                            action='store_true', dest='system_site',
                            help='Give the virtual environment access to the '
                                 'system site-packages dir.')
        if os.name == 'nt':
            use_symlinks = False
        else:
            use_symlinks = True
        parser.add_argument('--symlinks', default=use_symlinks,
                            action='store_true', dest='symlinks',
                            help='Try to use symlinks rather than copies, '
                                 'when symlinks are not the default for '
                                 'the platform.')
        parser.add_argument('--clear', default=False, action='store_true',
                            dest='clear', help='Delete the contents of the '
                                               'environment directory if it '
                                               'already exists, before '
                                               'environment creation.')
        parser.add_argument('--upgrade', default=False, action='store_true',
                            dest='upgrade', help='Upgrade the environment '
                                               'directory to use this version '
                                               'of Python, assuming Python '
                                               'has been upgraded in-place.')
        parser.add_argument('--verbose', default=False, action='store_true',
                            dest='verbose', help='Display the output '
                                               'from the scripts which '
                                               'install setuptools and pip.')
        options = parser.parse_args(args)
        if options.upgrade and options.clear:
            raise ValueError('you cannot supply --upgrade and --clear together.')
        builder = ExtendedEnvBuilder(system_site_packages=options.system_site,
                                       clear=options.clear,
                                       symlinks=options.symlinks,
                                       upgrade=options.upgrade,
                                       nodist=options.nodist,
                                       nopip=options.nopip,
                                       verbose=options.verbose)
        for d in options.dirs:
            builder.create(d)

if __name__ == '__main__':
    rc = 1
    try:
        main()
        rc = 0
    except Exception as e:
        print('Error: %s' % e, file=sys.stderr)
    sys.exit(rc)

此脚本也可以在在线下载。