venv --- 創(chuàng)建虛擬環(huán)境?

3.3 新版功能.

源碼: Lib/venv/


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

有關(guān) Python 虛擬環(huán)境的更多信息,請(qǐng)參閱 PEP 405

創(chuàng)建虛擬環(huán)境?

通過執(zhí)行 venv 指令來創(chuàng)建一個(gè) 虛擬環(huán)境:

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

運(yùn)行此命令將創(chuàng)建目標(biāo)目錄(父目錄若不存在也將創(chuàng)建),并放置一個(gè) pyvenv.cfg 文件在其中,文件中有一個(gè) home 鍵,它的值指向運(yùn)行此命令的 Python 安裝(目標(biāo)目錄的常用名稱是 .venv)。它還會(huì)創(chuàng)建一個(gè) bin 子目錄(在 Windows 上是 Scripts),其中包含 Python 二進(jìn)制文件的副本或符號(hào)鏈接(視創(chuàng)建環(huán)境時(shí)使用的平臺(tái)或參數(shù)而定)。它還會(huì)創(chuàng)建一個(gè)(初始為空的) lib/pythonX.Y/site-packages 子目錄(在 Windows 上是 Lib\site-packages)。如果指定了一個(gè)現(xiàn)有的目錄,這個(gè)目錄就將被重新使用。

3.6 版后已移除: pyvenv 是 Python 3.3 和 3.4 中創(chuàng)建虛擬環(huán)境的推薦工具,不過 在 Python 3.6 中已棄用。

在 3.5 版更改: 現(xiàn)在推薦使用 venv 來創(chuàng)建虛擬環(huán)境。

在 Windows 上,調(diào)用 venv 命令如下:

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

或者,如果已經(jīng)為 Python 安裝 配置好 PATHPATHEXT 變量:

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

本命令如果以 -h 參數(shù)運(yùn)行,將顯示可用的選項(xiàng):

usage: venv [-h] [--system-site-packages] [--symlinks | --copies] [--clear]
            [--upgrade] [--without-pip] [--prompt PROMPT] [--upgrade-deps]
            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.
  --upgrade-deps        Upgrade core dependencies: pip setuptools to the
                        latest version in PyPI

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

在 3.9 版更改: 添加 --upgrade-deps 選項(xiàng),用于將 pip + setuptools 升級(jí)到 PyPI 上的最新版本

在 3.4 版更改: 默認(rèn)安裝 pip,并添加 --without-pip--copies 選項(xiàng)

在 3.4 版更改: 在早期版本中,如果目標(biāo)目錄已存在,將引發(fā)錯(cuò)誤,除非使用了 --clear--upgrade 選項(xiàng)。

備注

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

備注

在 Microsoft Windows 上,為了啟用 Activate.ps1 腳本,可能需要修改用戶的執(zhí)行策略??梢赃\(yùn)行以下 PowerShell 命令來執(zhí)行此操作:

PS C:> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

參閱 About Execution Policies 以獲取更多信息。

生成的 pyvenv.cfg 文件還包括 include-system-site-packages 鍵,如果運(yùn)行 venv 時(shí)帶有 --system-site-packages 選項(xiàng),則鍵值為 true,否則為 false。

除非采用 --without-pip 選項(xiàng),否則將會(huì)調(diào)用 ensurepippip 引導(dǎo)到虛擬環(huán)境中。

可以向 venv 傳入多個(gè)路徑,此時(shí)將根據(jù)給定的選項(xiàng),在所給的每個(gè)路徑上創(chuàng)建相同的虛擬環(huán)境。

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

平臺(tái)

Shell

用于激活虛擬環(huán)境的命令

POSIX

bash/zsh

$ source <venv>/bin/activate

fish

$ source <venv>/bin/activate.fish

csh/tcsh

$ source <venv>/bin/activate.csh

PowerShell Core

$ <venv>/bin/Activate.ps1

Windows

cmd.exe

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

PowerShell

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

當(dāng)一個(gè)虛擬環(huán)境被激活時(shí),VIRTUAL_ENV 環(huán)境變量會(huì)被設(shè)為該虛擬環(huán)境的路徑。 這可被用來檢測(cè)程序是否運(yùn)行在虛擬環(huán)境中。

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

在 shell 中輸入 "deactivate" 可以退出虛擬環(huán)境。具體機(jī)制取決于不同平臺(tái),并且是內(nèi)部實(shí)現(xiàn)(通常使用腳本或 shell 函數(shù))。

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

3.8 新版功能: 在 POSIX 上安裝 PowerShell 激活腳本,以支持 PowerShell Core。

備注

虛擬環(huán)境是一個(gè) Python 環(huán)境,安裝到其中的 Python 解釋器、庫(kù)和腳本與其他虛擬環(huán)境中的內(nèi)容是隔離的,且(默認(rèn))與“系統(tǒng)級(jí)” Python(操作系統(tǒng)的一部分)中安裝的庫(kù)是隔離的。

虛擬環(huán)境是一個(gè)目錄樹,其中包含 Python 可執(zhí)行文件和其他文件,其他文件指示了這是一個(gè)是虛擬環(huán)境。

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

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

當(dāng)虛擬環(huán)境被激活,所有 distutils 配置文件中更改安裝路徑的選項(xiàng)都會(huì)被忽略,以防止無意中將項(xiàng)目安裝在虛擬環(huán)境之外。

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

API?

上述的高級(jí)方法使用了一個(gè)簡(jiǎn)單的 API,該 API 提供了一種機(jī)制,第三方虛擬環(huán)境創(chuàng)建者可以根據(jù)其需求自定義環(huán)境創(chuàng)建過程,該 API 為 EnvBuilder 類。

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

EnvBuilder 類在實(shí)例化時(shí)接受以下關(guān)鍵字參數(shù):

  • system_site_packages -- 一個(gè)布爾值,要求系統(tǒng) Python 的 site-packages 對(duì)環(huán)境可用(默認(rèn)為 False)。

  • clear -- 一個(gè)布爾值,如果為 true,則在創(chuàng)建環(huán)境前將刪除目標(biāo)目錄的現(xiàn)有內(nèi)容。

  • symlinks -- 一個(gè)布爾值,指示是否嘗試符號(hào)鏈接 Python 二進(jìn)制文件,而不是進(jìn)行復(fù)制。

  • upgrade -- 一個(gè)布爾值,如果為 true,則將使用當(dāng)前運(yùn)行的 Python 去升級(jí)一個(gè)現(xiàn)有的環(huán)境,這主要在原位置的 Python 更新后使用(默認(rèn)為 False)。

  • with_pip -- 一個(gè)布爾值,如果為 true,則確保在虛擬環(huán)境中已安裝 pip。這使用的是帶有 --default-pip 選項(xiàng)的 ensurepip。

  • prompt -- 激活虛擬環(huán)境后顯示的提示符(默認(rèn)為 None,表示使用環(huán)境所在的目錄名稱)。如果使用了 "." 這一特殊字符串,則使用當(dāng)前目錄的基本名稱作為提示符。

  • upgrade_deps -- 將基本 venv 模塊更新為 PyPI 上的最新版本。

在 3.4 版更改: 添加 with_pip 參數(shù)

3.6 新版功能: 添加 prompt 參數(shù)

3.9 新版功能: 添加 upgrade_deps 參數(shù)

第三方虛擬環(huán)境工具的創(chuàng)建者可以自由地將此處提供的 EnvBuilder 類作為基類。

返回的 env-builder 是一個(gè)對(duì)象,包含一個(gè) create 方法:

create(env_dir)?

指定要建立虛擬環(huán)境的目標(biāo)目錄(絕對(duì)路徑或相對(duì)于當(dāng)前路徑)來創(chuàng)建虛擬環(huán)境。create 方法將在指定目錄中創(chuàng)建環(huán)境,或者引發(fā)對(duì)應(yīng)的異常。

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)

每個(gè)方法 ensure_directories(), create_configuration(), setup_python(), setup_scripts()post_setup() 都可以被重寫。

ensure_directories(env_dir)?

Creates the environment directory and all necessary subdirectories that don't already exist, and returns a context object. This context object is just a holder for attributes (such as paths) for use by the other methods. If the EnvBuilder is created with the arg clear=True, contents of the environment directory will be cleared and then all necessary subdirectories will be recreated.

在 3.11 版更改: The venv sysconfig installation scheme is used to construct the paths of the created directories.

create_configuration(context)?

在環(huán)境中創(chuàng)建 pyvenv.cfg 配置文件。

setup_python(context)?

在環(huán)境中創(chuàng)建 Python 可執(zhí)行文件的拷貝或符號(hào)鏈接。在 POSIX 系統(tǒng)上,如果給定了可執(zhí)行文件 python3.x,將創(chuàng)建指向該可執(zhí)行文件的 pythonpython3 符號(hào)鏈接,除非相同名稱的文件已經(jīng)存在。

setup_scripts(context)?

將適用于平臺(tái)的激活腳本安裝到虛擬環(huán)境中。

upgrade_dependencies(context)?

升級(jí)環(huán)境中 venv 依賴的核心軟件包(當(dāng)前為 pipsetuptools)。通過在環(huán)境中使用 pip 可執(zhí)行文件來完成。

3.9 新版功能.

post_setup(context)?

占位方法,可以在第三方實(shí)現(xiàn)中重寫,用于在虛擬環(huán)境中預(yù)安裝軟件包,或是其他創(chuàng)建后要執(zhí)行的步驟。

在 3.7.2 版更改: Windows 現(xiàn)在為 python[w].exe 使用重定向腳本,而不是復(fù)制實(shí)際的二進(jìn)制文件。僅在 3.7.2 中,除非運(yùn)行的是源碼樹中的構(gòu)建,否則 setup_python() 不會(huì)執(zhí)行任何操作。

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

此外,EnvBuilder 提供了如下實(shí)用方法,可以從子類的 setup_scripts()post_setup() 調(diào)用,用來將自定義腳本安裝到虛擬環(huán)境中。

install_scripts(context, path)?

path 是一個(gè)目錄的路徑,該目錄應(yīng)包含子目錄 "common", "posix", "nt",每個(gè)子目錄存有發(fā)往對(duì)應(yīng)環(huán)境中 bin 目錄的腳本。在下列占位符替換完畢后,將復(fù)制 "common" 的內(nèi)容和與 os.name 對(duì)應(yīng)的子目錄:

  • __VENV_DIR__ 會(huì)被替換為環(huán)境目錄的絕對(duì)路徑。

  • __VENV_NAME__ 會(huì)被替換為環(huán)境名稱(環(huán)境目錄的最后一個(gè)字段)。

  • __VENV_PROMPT__ 會(huì)被替換為提示符(用括號(hào)括起來的環(huán)境名稱緊跟著一個(gè)空格)。

  • __VENV_BIN_NAME__ 會(huì)被替換為 bin 目錄的名稱( binScripts )。

  • __VENV_PYTHON__ 會(huì)被替換為環(huán)境可執(zhí)行文件的絕對(duì)路徑。

允許目錄已存在(用于升級(jí)現(xiàn)有環(huán)境時(shí))。

有一個(gè)方便實(shí)用的模塊級(jí)別的函數(shù):

venv.create(env_dir, system_site_packages=False, clear=False, symlinks=False, with_pip=False, prompt=None, upgrade_deps=False)?

通過關(guān)鍵詞參數(shù)來創(chuàng)建一個(gè) EnvBuilder,并且使用 env_dir 參數(shù)來調(diào)用它的 create() 方法。

3.3 新版功能.

在 3.4 版更改: 添加 with_pip 參數(shù)

在 3.6 版更改: 添加 prompt 參數(shù)

在 3.9 版更改: 添加 upgrade_deps 參數(shù)

一個(gè)擴(kuò)展 EnvBuilder 的例子?

下面的腳本展示了如何通過實(shí)現(xiàn)一個(gè)子類來擴(kuò)展 EnvBuilder。這個(gè)子類會(huì)安裝 setuptotols 和 pip 的到被創(chuàng)建的虛擬環(huán)境中。

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://bootstrap.pypa.io/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)

這個(gè)腳本同樣可以 在線下載。