28.3. venv — 创建虚拟环境 — Python 文档

来自菜鸟教程
Python/docs/3.6/library/venv
跳转至:导航、​搜索

28.3. venv — 创建虚拟环境

3.3 版中的新功能。


源代码: :source:`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 二进制文件/二进制文件的副本/符号链接(适用于平台或环境创建时使用的参数)。 它还创建了一个(最初为空的)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 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 选项一起运行,则设置为 truefalse 否则。

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

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

一旦创建了虚拟环境,就可以使用虚拟环境二进制目录中的脚本“激活”它。 脚本的调用是特定于平台的 ( 必须替换为包含虚拟环境的目录的路径):

平台 激活虚拟环境的命令
位置 bash/zsh $ 来源 /bin/激活
$ . /bin/activate.fish
csh/tcsh $ 来源 /bin/activate.csh
视窗 命令行工具 C:\> \Scripts\activate.bat
电源外壳 PS C:\> \Scripts\Activate.ps1

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

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

3.4 新功能:fishcsh 激活脚本。


笔记

虚拟环境是一种 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 中工作时,用户可以通过在虚拟环境的可执行文件目录(确切的文件名取决于 shell)中运行 activate 脚本来激活虚拟环境,该脚本将虚拟环境的可执行文件目录添加到PATH 正在运行的 shell 的环境变量。 在其他情况下应该不需要激活虚拟环境——安装到虚拟环境中的脚本有一个“shebang”行,它指向虚拟环境的 Python 解释器。 这意味着无论 PATH 的值如何,脚本都将与该解释器一起运行。 在 Windows 上,如果您安装了适用于 Windows 的 Python Launcher,则支持“shebang”行处理(这已在 3.3 中添加到 Python 中 - 有关更多详细信息,请参阅 PEP 397)。 因此,在 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 站点包应该对环境可用(默认为 False)。

  • clear – 一个布尔值,如果为真,将在创建环境之前删除任何现有目标目录的内容。

  • symlinks – 一个布尔值,指示是否尝试符号链接 Python 二进制文件(以及任何必要的 DLL 或其他二进制文件,例如 pythonw.exe),而不是复制。

  • upgrade - 一个布尔值,如果为真,将使用正在运行的 Python 升级现有环境 - 在 Python 就地升级时使用(默认为 False)。

  • with_pip – 一个布尔值,如果为真,则确保 pip 安装在虚拟环境中。 这将 ensurepip--default-pip 选项一起使用。

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

3.4 版变更:增加了with_pip参数

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

第三方虚拟环境工具的创建者可以免费使用提供的 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. 扩展的例子EnvBuilder

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

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)

该脚本也可用于下载 在线