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的二进制目录中的脚本“激活”。脚本的调用是平台特定的:
平台 | 贝壳 | 命令激活虚拟环境 |
---|---|---|
Posix | bash / zsh | $ source |
鱼 | $。 | |
csh / tcsh | $ source | |
视窗 | cmd.exe | C:\> |
PowerShell | PS C:\> |
您不具体需要来激活环境;激活只是将venv的二进制目录添加到你的路径,所以“python”调用venv的Python解释器,你可以运行安装的脚本,而不必使用他们的完整路径。但是,安装在venv中的所有脚本都应该可以在不激活的情况下运行,并且可以自动运行venv的Python。
您可以通过在shell中键入“deactivate”来停用venv。确切的机制是特定于平台的:例如,Bash激活脚本定义了一个“deactivate”函数,而在Windows上有称为deactivate.bat
和Deactivate.ps1
新版本3.4: fish
和csh
激活脚本。
注意
虚拟环境(也称为venv
)是一个Python环境,使得安装在其中的Python解释器,库和脚本与安装在其他虚拟环境中的Python解释器,库和脚本隔离,安装在一个“系统”Python,即其中一个作为操作系统的一部分安装。
venv是一个目录树,它包含Python可执行文件和其他文件,这些文件指示它是一个venv。
常用的安装工具,例如Setuptools
和pip
,可以正常工作当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.prefix
与sys.base_prefix
相同,sys.exec_prefix
与sys.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)¶ 创建环境目录和所有必需的目录,并返回上下文对象。这只是属性(例如路径)的持有者,供其他方法使用。只要指定
clear
或upgrade
允许在现有环境目录上操作,就允许这些目录存在。
-
create_configuration
(context)¶ 在环境中创建
pyvenv.cfg
配置文件。
-
setup_python
(context)¶ 在环境中创建Python可执行文件(以及在Windows,DLL下)的副本。在POSIX系统上,如果使用特定的可执行文件
python3.x
,将会创建指向该可执行文件的符号链接python
和python3
,除非具有这些名称的文件已存在。
-
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目录的名称(bin
或Scripts
)。__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)
此脚本也可以在在线下载。