Skip to main content

28.3. venv —创建虚拟环境

3.3 新版功能.

源代码: Lib/venv/


venv 模块支持使用自己的站点目录创建轻量级的“虚拟环境”,可选地与系统站点目录隔离。每个虚拟环境都有自己的Python二进制(允许创建具有各种Python版本的环境),并且可以在其网站目录中拥有自己独立的一组已安装的Python包。

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

注解

pyvenv 脚本从Python 3.6开始被弃用,倾向于使用 python3 -m venv 来帮助防止任何潜在的混淆,虚拟环境将基于哪个Python解释器。

28.3.1. 创建虚拟环境

虚拟环境 的创建通过执行命令 venv 来完成:

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

运行此命令将创建目标目录(创建任何尚不存在的父目录),并将 pyvenv.cfg 文件放在其中,并使用 home 键指向运行命令的Python安装。它还创建一个 bin (或Windows上的 Scripts)子目录,其中包含 python 二进制文件的副本(如果是Windows,则为二进制文件)。它还创建一个(最初为空) lib/pythonX.Y/site-packages 子目录(在Windows上,这是 Lib\site-packages)。

3.6 版后已移除: pyvenv 是为Python 3.3和3.4创建虚拟环境的推荐工具,是 在Python 3.6中弃用

在 3.5 版更改: 现在建议使用 venv 创建虚拟环境。

在Windows上,按如下所示调用 venv 命令:

c:\>c:\Python35\python -m venv c:\path\to\myenv

或者,如果您为 Python安装 配置了 PATHPATHEXT 变量:

c:\>python -m venv myenv c:\path\to\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)

Once an environment has been created, you may wish to activate it, e.g. by
sourcing an activate script in its bin directory.

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

在 3.4 版更改: 在早期版本中,如果目标目录已存在,则会出现错误,除非提供了 --clear--upgrade 选项。现在,如果指定了现有目录,则删除其内容,并如同新创建的那样处理目录。

创建的 pyvenv.cfg 文件还包括 include-system-site-packages 密钥,如果 venv 使用 --system-site-packages 选项运行,则设置为 true,否则为 false

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

可以向 venv 给出多个路径,在这种情况下,将根据给定的选项在每个提供的路径处创建相同的虚拟环境。

一旦创建了虚拟环境,就可以使用虚拟环境的二进制目录中的脚本来“激活”虚拟环境。脚本的调用是特定于平台的:

平台

贝壳

命令激活虚拟环境

Posix

bash/zsh

$ source <venv>/bin/activate

 

$。 <venv>/bin/activate.fish

 

csh/tcsh

$ source <venv>/bin/activate.csh

视窗

cmd.exe

C:\> <venv> \ Scripts \ activate.bat

 

电源外壳

PS C:\> <venv> \ Scripts \ Activate.ps1

你没有特定的 need 激活环境;激活只是将虚拟环境的二进制目录添加到您的路径,以便“python”调用虚拟环境的Python解释器,并且您可以运行安装的脚本,而无需使用其完整路径。但是,安装在虚拟环境中的所有脚本都应该可以在不激活的情况下运行,并且可以自动运行虚拟环境的Python。

您可以通过在shell中键入“deactivate”来停用虚拟环境。确切的机制是特定于平台的:例如,Bash激活脚本定义了一个“停用”功能,而在Windows上有创建虚拟环境时安装的独立脚本 deactivate.batDeactivate.ps1

3.4 新版功能: fishcsh 激活脚本。

注解

虚拟环境是一个Python环境,使得安装在其中的Python解释器,库和脚本与安装在其他虚拟环境中的Python解释器,库和脚本隔离,并且(默认情况下)安装在“系统”Python中的任何库,作为您的操作系统的一部分。

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

常见的安装工具(如 Setuptoolspip)按照预期与虚拟环境一起工作。换句话说,当虚拟环境处于活动状态时,他们将Python包安装到虚拟环境中,而不需要明确地告知这样做。

当虚拟环境处于活动状态(即虚拟环境的Python解释器正在运行)时,属性 sys.prefixsys.exec_prefix 指向虚拟环境的基本目录,而 sys.base_prefixsys.base_exec_prefix 指向非虚拟环境Python安装,创建虚拟环境。如果虚拟环境不活动,则 sys.prefixsys.base_prefix 相同,sys.exec_prefixsys.base_exec_prefix 相同(它们都指向非虚拟环境Python安装)。

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

当在命令shell中工作时,用户可以通过在虚拟环境的可执行文件目录中运行 activate 脚本(准确的文件名取决于shell)来使虚拟环境变为活动状态,该脚本将可执行文件的虚拟环境目录添加到 PATH 环境变量运行shell。在其他情况下不需要激活虚拟环境 - 安装到虚拟环境中的脚本具有指向虚拟环境的Python解释器的“shebang”行。这意味着脚本将与该解释器一起运行,而不管 PATH 的值。在Windows上,如果您安装了适用于Windows的Python Launcher(这已在3.3中添加到Python - 更多详细信息,请参阅 PEP 397),则支持“shebang”行处理。因此,在Windows资源管理器窗口中双击已安装的脚本应该使用正确的解释器运行脚本,而不需要在 PATH 中对其虚拟环境进行任何引用。

28.3.2. API

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

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

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安装在虚拟环境中。这使用带有 --default-pip 选项的 ensurepip

  • prompt - 在虚拟环境激活后使用的字符串(默认为 None,这意味着将使用环境的目录名)。

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

3.6 新版功能: 添加了 prompt 参数

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

返回的环境构造器是一个具有方法 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. 扩展 EnvBuilder 的示例

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

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 virtual environment.

    :param nodist: If True, setuptools and pip are not installed into the
                   created virtual environment.
    :param nopip: If True, pip is not installed into the created
                  virtual 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
        virtual environment being created.

        :param context: The information for the virtual 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 virtual environment'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 virtual environment
        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 virtual environment.

        :param context: The information for the virtual 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 virtual environment.

        :param context: The information for the virtual 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 in which to create the
                                 'virtual environment.')
        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 '
                                               'virtual environment '
                                               'directory if it already '
                                               'exists, before virtual '
                                               'environment creation.')
        parser.add_argument('--upgrade', default=False, action='store_true',
                            dest='upgrade', help='Upgrade the virtual '
                                                 '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)

此脚本也可以下载 线上