venv --- 創建虛擬環境?

3.3 新版功能.

源碼: Lib/venv/


venv 模塊支持使用自己的站點目錄創建輕量級“虛擬環境”,可選擇與系統站點目錄隔離。每個虛擬環境都有自己的 Python 二進制文件(與用于創建此環境的二進制文件的版本相匹配),并且可以在其站點目錄中擁有自己獨立的已安裝 Python 軟件包集。

有關 Python 虛擬環境的更多信息,請參閱 PEP 405

注解

從 Python 3.6 開始,不推薦使用 pyvenv 腳本,而是使用 python3 -m venv 來幫助防止任何關于虛擬環境將基于哪個 Python 解釋器的混淆。

創建虛擬環境?

通過執行 venv 指令來創建一個 虛擬環境:

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

Running this command creates the target directory (creating any parent directories that don't exist already) and places a pyvenv.cfg file in it with a home key pointing to the Python installation from which the command was run. It also creates a bin (or Scripts on Windows) subdirectory containing a copy/symlink of the Python binary/binaries (as appropriate for the platform or arguments used at environment creation time). It also creates an (initially empty) lib/pythonX.Y/site-packages subdirectory (on Windows, this is Lib\site-packages). If an existing directory is specified, it will be re-used.

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] [--prompt PROMPT]
            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)
  --prompt PROMPT       Provides an alternative prompt prefix for this
                        environment.

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 選項。

注解

雖然 Windows 支持符號鏈接,但不推薦使用它們。特別注意,在文件資源管理器中雙擊 python.exe 將立即解析符號鏈接,并忽略虛擬環境。

生成的 pyvenv.cfg 文件還包括 include-system-site-packages 鍵,如果運行 venv 帶有 --system-site-packages 選項,則鍵值為 true,否則為 false

除非采用 --without-pip 選項,否則將會調用 ensurepippip 引導到虛擬環境中。

可以向 venv 傳入多個路徑,此時將根據給定的選項,在所給的每個路徑上創建相同的虛擬環境。

創建虛擬環境后,可以使用虛擬環境的二進制目錄中的腳本來“激活”該環境。不同平臺調用的腳本是不同的(須將 <venv> 替換為包含虛擬環境的目錄路徑):

平臺

Shell

用于激活虛擬環境的命令

Posix

bash/zsh

$ source <venv>/bin/activate

fish

$ . <venv>/bin/activate.fish

csh/tcsh

$ source <venv>/bin/activate.csh

Windows

cmd.exe

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

PowerShell

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

激活環境不是 必須 的,激活只是將虛擬環境的二進制目錄添加到搜索路徑中,這樣 "python" 命令將調用虛擬環境的 Python 解釋器,可以運行其中已安裝的腳本,而不必輸入其完整路徑。但是,安裝在虛擬環境中的所有腳本都應在不激活的情況下可運行,并自動與虛擬環境的 Python 一起運行。

在 shell 中輸入 "deactivate" 可以退出虛擬環境。具體機制取決于不同平臺,并且是內部實現(通常使用腳本或 shell 函數)。

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

注解

虛擬環境是一個 Python 環境,安裝到其中的 Python 解釋器、庫和腳本與其他虛擬環境中的內容是隔離的,且(默認)與“系統級” Python(操作系統的一部分)中安裝的庫是隔離的。

虛擬環境是一個目錄樹,其中包含 Python 可執行文件和其他文件,其他文件指示了這是一個是虛擬環境。

常用安裝工具如 setuptoolspip 可以在虛擬環境中按預期工作。換句話說,當虛擬環境被激活,它們就會將 Python 軟件包安裝到虛擬環境中,無需明確指示。

當虛擬環境被激活(即虛擬環境的 Python 解釋器正在運行),屬性 sys.prefixsys.exec_prefix 指向的是虛擬環境的基礎目錄,而 sys.base_prefixsys.base_exec_prefix 指向非虛擬環境的 Python 安裝,即曾用于創建虛擬環境的那個 Python 安裝。如果虛擬環境沒有被激活,則 sys.prefixsys.base_prefix 相同,且 sys.exec_prefixsys.base_exec_prefix 相同(它們均指向非虛擬環境的 Python 安裝)。

當虛擬環境被激活,所有 distutils 配置文件中更改安裝路徑的選項都會被忽略,以防止無意中將項目安裝在虛擬環境之外。

在命令行 shell 中工作時,用戶可以運行虛擬環境可執行文件目錄中的 activate 腳本來激活虛擬環境(調用該文件的確切文件名和命令取決于 shell),這會將虛擬環境的可執行文件目錄添加到當前 shell 的 PATH 環境變量。在其他情況下,無需激活虛擬環境。安裝到虛擬環境中的腳本有 "shebang" 行,指向虛擬環境的 Python 解釋器。這意味著無論 PATH 的值如何,腳本都將與該解釋器一起運行。在 Windows 上,如果已安裝 Python Launcher for Windows,則支持處理 "shebang" 行(此功能在 Python 3.3 中添加,詳情請參閱 PEP 397)。這樣,在 Windows 資源管理器中雙擊已安裝的腳本,應該會使用正確的解釋器運行該腳本,而在 PATH 中無需指向其虛擬環境。

API?

上述的高級方法使用了一個簡單的 API,該 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 二進制文件,而不是進行復制。

  • upgrade -- 一個布爾值,如果為 true,則將使用當前運行的 Python 去升級一個現有的環境,這主要在原位置的 Python 更新后使用(默認為 False)。

  • with_pip -- 一個布爾值,如果為 true,則確保在虛擬環境中已安裝 pip。這使用的是帶有 --default-pip 選項的 ensurepip

  • prompt -- a String to be used after virtual environment is activated (defaults to None which means directory name of the environment would be used).

在 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 可執行文件的拷貝或符號鏈接。在 POSIX 系統上,如果給定了可執行文件 python3.x,將創建指向該可執行文件的 pythonpython3 符號鏈接,除非相同名稱的文件已經存在。

setup_scripts(context)?

將適用于平臺的激活腳本安裝到虛擬環境中。

post_setup(context)?

占位方法,可以在第三方實現中重寫,用于在虛擬環境中預安裝軟件包,或是其他創建后要執行的步驟。

在 3.7.2 版更改: Windows 現在為 python[w].exe 使用重定向腳本,而不是復制實際的二進制文件。僅在 3.7.2 中,除非運行的是源碼樹中的構建,否則 setup_python() 不會執行任何操作。

在 3.7.3 版更改: Windows 將重定向腳本復制為 setup_python() 的一部分而非 setup_scripts()。在 3.7.2 中不是這種情況。使用符號鏈接時,將鏈接至原始可執行文件。

此外,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, prompt=None)?

通過關鍵詞參數來創建一個 EnvBuilder,并且使用 env_dir 參數來調用它的 create() 方法。

3.3 新版功能.

在 3.4 版更改: 添加 with_pip 參數

在 3.6 版更改: 添加 prompt 參數

一個擴展 EnvBuilder 的例子?

下面的腳本展示了如何通過實現一個子類來擴展 EnvBuilder。這個子類會安裝 setuptotols 和 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)

這個腳本同樣可以 在線下載