Skip to content

Commit 7ded1f0

Browse files
committed
Implemented PEP 405 (Python virtual environments).
1 parent f2bdc36 commit 7ded1f0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1454
-66
lines changed

Doc/library/development.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ The list of modules described in this chapter is:
2323
unittest.mock-examples.rst
2424
2to3.rst
2525
test.rst
26+
venv.rst

Doc/library/sys.rst

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,26 @@ always available.
2929
command line, see the :mod:`fileinput` module.
3030

3131

32+
.. data:: base_exec_prefix
33+
34+
Set during Python startup, before ``site.py`` is run, to the same value as
35+
:data:`exec_prefix`. If not running in a virtual environment, the values
36+
will stay the same; if ``site.py`` finds that a virtual environment is in
37+
use, the values of :data:`prefix` and :data:`exec_prefix` will be changed to
38+
point to the virtual environment, whereas :data:`base_prefix` and
39+
:data:`base_exec_prefix` will remain pointing to the base Python
40+
installation (the one which the virtual environment was created from).
41+
42+
.. data:: base_prefix
43+
44+
Set during Python startup, before ``site.py`` is run, to the same value as
45+
:data:`prefix`. If not running in a virtual environment, the values
46+
will stay the same; if ``site.py`` finds that a virtual environment is in
47+
use, the values of :data:`prefix` and :data:`exec_prefix` will be changed to
48+
point to the virtual environment, whereas :data:`base_prefix` and
49+
:data:`base_exec_prefix` will remain pointing to the base Python
50+
installation (the one which the virtual environment was created from).
51+
3252
.. data:: byteorder
3353

3454
An indicator of the native byte order. This will have the value ``'big'`` on
@@ -199,6 +219,10 @@ always available.
199219
installed in :file:`{exec_prefix}/lib/python{X.Y}/lib-dynload`, where *X.Y*
200220
is the version number of Python, for example ``3.2``.
201221

222+
.. note:: If a virtual environment is in effect, this value will be changed
223+
in ``site.py`` to point to the virtual environment. The value for the
224+
Python installation will still be available, via :data:`base_exec_prefix`.
225+
202226

203227
.. data:: executable
204228

@@ -775,6 +799,10 @@ always available.
775799
stored in :file:`{prefix}/include/python{X.Y}`, where *X.Y* is the version
776800
number of Python, for example ``3.2``.
777801

802+
.. note:: If a virtual environment is in effect, this value will be changed
803+
in ``site.py`` to point to the virtual environment. The value for the
804+
Python installation will still be available, via :data:`base_prefix`.
805+
778806

779807
.. data:: ps1
780808
ps2

Doc/library/venv.rst

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
:mod:`venv` --- Creation of virtual environments
2+
================================================
3+
4+
.. module:: venv
5+
:synopsis: Creation of virtual environments.
6+
.. moduleauthor:: Vinay Sajip <vinay_sajip@yahoo.co.uk>
7+
.. sectionauthor:: Vinay Sajip <vinay_sajip@yahoo.co.uk>
8+
9+
10+
.. index:: pair: Environments; virtual
11+
12+
.. versionadded:: 3.3
13+
14+
**Source code:** :source:`Lib/venv.py`
15+
16+
--------------
17+
18+
The :mod:`venv` module provides support for creating lightweight
19+
"virtual environments" with their own site directories, optionally
20+
isolated from system site directories. Each virtual environment has
21+
its own Python binary (allowing creation of environments with various
22+
Python versions) and can have its own independent set of installed
23+
Python packages in its site directories.
24+
25+
Creating virtual environments
26+
-----------------------------
27+
28+
Creation of virtual environments is simplest executing the ``pyvenv``
29+
script::
30+
31+
pyvenv /path/to/new/virtual/environment
32+
33+
Running this command creates the target directory (creating any parent
34+
directories that don't exist already) and places a ``pyvenv.cfg`` file
35+
in it with a ``home`` key pointing to the Python installation the
36+
command was run from. It also creates a ``bin`` (or ``Scripts`` on
37+
Windows) subdirectory containing a copy of the ``python`` binary (or
38+
binaries, in the case of Windows) and the ``pysetup3`` script (to
39+
facilitate easy installation of packages from PyPI into the new virtualenv).
40+
It also creates an (initially empty) ``lib/pythonX.Y/site-packages``
41+
subdirectory (on Windows, this is ``Lib\site-packages``).
42+
43+
.. highlight:: none
44+
45+
On Windows, you may have to invoke the ``pyvenv`` script as follows, if you
46+
don't have the relevant PATH and PATHEXT settings::
47+
48+
c:\Temp>c:\Python33\python c:\Python33\Tools\Scripts\pyvenv.py myenv
49+
50+
or equivalently::
51+
52+
c:\Temp>c:\Python33\python -m venv myenv
53+
54+
The command, if run with ``-h``, will show the available options::
55+
56+
usage: pyvenv [-h] [--system-site-packages] [--symlink] [--clear]
57+
ENV_DIR [ENV_DIR ...]
58+
59+
Creates virtual Python environments in one or more target directories.
60+
61+
positional arguments:
62+
ENV_DIR A directory to create the environment in.
63+
64+
optional arguments:
65+
-h, --help show this help message and exit
66+
--system-site-packages Give access to the global site-packages dir to the
67+
virtual environment.
68+
--symlink Attempt to symlink rather than copy.
69+
--clear Delete the environment directory if it already exists.
70+
If not specified and the directory exists, an error is
71+
raised.
72+
73+
74+
If the target directory already exists an error will be raised, unless
75+
the ``--clear`` option was provided, in which case the target
76+
directory will be deleted and virtual environment creation will
77+
proceed as usual.
78+
79+
The created ``pyvenv.cfg`` file also includes the
80+
``include-system-site-packages`` key, set to ``true`` if ``venv`` is
81+
run with the ``--system-site-packages`` option, ``false`` otherwise.
82+
83+
Multiple paths can be given to ``pyvenv``, in which case an identical
84+
virtualenv will be created, according to the given options, at each
85+
provided path.
86+
87+
88+
API
89+
---
90+
91+
The high-level method described above makes use of a simple API which provides
92+
mechanisms for third-party virtual environment creators to customize
93+
environment creation according to their needs.
94+
95+
The :class:`EnvBuilder` class accepts the following keyword arguments on
96+
instantiation:
97+
98+
* ``system_site_packages`` - A Boolean value indicating that the
99+
system Python site-packages should be available to the
100+
environment (defaults to ``False``).
101+
102+
* ``clear`` - A Boolean value which, if True, will delete any
103+
existing target directory instead of raising an exception
104+
(defaults to ``False``).
105+
106+
* ``symlinks`` - A Boolean value indicating whether to attempt
107+
to symlink the Python binary (and any necessary DLLs or other
108+
binaries, e.g. ``pythonw.exe``), rather than copying. Defaults to
109+
``True`` on Linux and Unix systems, but ``False`` on Windows and
110+
Mac OS X.
111+
112+
The returned env-builder is an object which has a method, ``create``,
113+
which takes as required argument the path (absolute or relative to the current
114+
directory) of the target directory which is to contain the virtual environment.
115+
The ``create`` method will either create the environment in the specified
116+
directory, or raise an appropriate exception.
117+
118+
Creators of third-party virtual environment tools will be free to use
119+
the provided ``EnvBuilder`` class as a base class.
120+
121+
.. highlight:: python
122+
123+
The ``venv`` module will also provide a module-level function as a
124+
convenience::
125+
126+
def create(env_dir,
127+
system_site_packages=False, clear=False, symlinks=False):
128+
builder = EnvBuilder(
129+
system_site_packages=system_site_packages,
130+
clear=clear,
131+
symlinks=symlinks)
132+
builder.create(env_dir)
133+
134+
The ``create`` method of the ``EnvBuilder`` class illustrates the
135+
hooks available for subclass customization::
136+
137+
def create(self, env_dir):
138+
"""
139+
Create a virtualized Python environment in a directory.
140+
141+
:param env_dir: The target directory to create an environment in.
142+
143+
"""
144+
env_dir = os.path.abspath(env_dir)
145+
context = self.create_directories(env_dir)
146+
self.create_configuration(context)
147+
self.setup_python(context)
148+
self.setup_scripts(context)
149+
self.post_setup(context)
150+
151+
Each of the methods ``create_directories``, ``create_configuration``,
152+
``setup_python``, ``setup_scripts`` and ``post_setup`` can be
153+
overridden. The functions of these methods are:
154+
155+
* ``create_directories`` - creates the environment directory and
156+
all necessary directories, and returns a context object. This is
157+
just a holder for attributes (such as paths), for use by the
158+
other methods.
159+
160+
* ``create_configuration`` - creates the ``pyvenv.cfg``
161+
configuration file in the environment.
162+
163+
* ``setup_python`` - creates a copy of the Python executable (and,
164+
under Windows, DLLs) in the environment.
165+
166+
* ``setup_scripts`` - Installs activation scripts appropriate to the
167+
platform into the virtual environment.
168+
169+
* ``post_setup`` - A placeholder method which can be overridden
170+
in third party implementations to pre-install packages in the
171+
virtual environment or perform other post-creation steps.
172+
173+
In addition, ``EnvBuilder`` provides an ``install_scripts`` utility
174+
method that can be called from ``setup_scripts`` or ``post_setup`` in
175+
subclasses to assist in installing custom scripts into the virtual
176+
environment. The method accepts as arguments the context object (see
177+
above) and a path to a directory. The directory should contain
178+
subdirectories "common", "posix", "nt", each containing scripts
179+
destined for the bin directory in the environment. The contents of
180+
"common" and the directory corresponding to ``os.name`` are copied
181+
after some text replacement of placeholders:
182+
183+
* ``__VENV_DIR__`` is replaced with the absolute path of the
184+
environment directory.
185+
186+
* ``__VENV_NAME__`` is replaced with the environment name (final path
187+
segment of environment directory).
188+
189+
* ``__VENV_BIN_NAME__`` is replaced with the name of the bin directory
190+
(either ``bin`` or ``Scripts``).
191+
192+
* ``__VENV_PYTHON__`` is replaced with the absolute path of the
193+
environment's executable.

Lib/distutils/sysconfig.py

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
# These are needed in a couple of spots, so just compute them once.
1919
PREFIX = os.path.normpath(sys.prefix)
2020
EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
21+
BASE_PREFIX = os.path.normpath(sys.base_prefix)
22+
BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix)
2123

2224
# Path to the base directory of the project. On Windows the binary may
2325
# live in project/PCBuild9. If we're dealing with an x64 Windows build,
@@ -39,11 +41,18 @@
3941
# different (hard-wired) directories.
4042
# Setup.local is available for Makefile builds including VPATH builds,
4143
# Setup.dist is available on Windows
42-
def _python_build():
44+
def _is_python_source_dir(d):
4345
for fn in ("Setup.dist", "Setup.local"):
44-
if os.path.isfile(os.path.join(project_base, "Modules", fn)):
46+
if os.path.isfile(os.path.join(d, "Modules", fn)):
4547
return True
4648
return False
49+
_sys_home = getattr(sys, '_home', None)
50+
if _sys_home and os.name == 'nt' and _sys_home.lower().endswith('pcbuild'):
51+
_sys_home = os.path.dirname(_sys_home)
52+
def _python_build():
53+
if _sys_home:
54+
return _is_python_source_dir(_sys_home)
55+
return _is_python_source_dir(project_base)
4756
python_build = _python_build()
4857

4958
# Calculate the build qualifier flags if they are defined. Adding the flags
@@ -74,23 +83,24 @@ def get_python_inc(plat_specific=0, prefix=None):
7483
otherwise, this is the path to platform-specific header files
7584
(namely pyconfig.h).
7685
77-
If 'prefix' is supplied, use it instead of sys.prefix or
78-
sys.exec_prefix -- i.e., ignore 'plat_specific'.
86+
If 'prefix' is supplied, use it instead of sys.base_prefix or
87+
sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
7988
"""
8089
if prefix is None:
81-
prefix = plat_specific and EXEC_PREFIX or PREFIX
90+
prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX
8291
if os.name == "posix":
8392
if python_build:
8493
# Assume the executable is in the build directory. The
8594
# pyconfig.h file should be in the same directory. Since
8695
# the build directory may not be the source directory, we
8796
# must use "srcdir" from the makefile to find the "Include"
8897
# directory.
89-
base = os.path.dirname(os.path.abspath(sys.executable))
98+
base = _sys_home or os.path.dirname(os.path.abspath(sys.executable))
9099
if plat_specific:
91100
return base
92101
else:
93-
incdir = os.path.join(get_config_var('srcdir'), 'Include')
102+
incdir = os.path.join(_sys_home or get_config_var('srcdir'),
103+
'Include')
94104
return os.path.normpath(incdir)
95105
python_dir = 'python' + get_python_version() + build_flags
96106
return os.path.join(prefix, "include", python_dir)
@@ -115,11 +125,14 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
115125
containing standard Python library modules; otherwise, return the
116126
directory for site-specific modules.
117127
118-
If 'prefix' is supplied, use it instead of sys.prefix or
119-
sys.exec_prefix -- i.e., ignore 'plat_specific'.
128+
If 'prefix' is supplied, use it instead of sys.base_prefix or
129+
sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
120130
"""
121131
if prefix is None:
122-
prefix = plat_specific and EXEC_PREFIX or PREFIX
132+
if standard_lib:
133+
prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX
134+
else:
135+
prefix = plat_specific and EXEC_PREFIX or PREFIX
123136

124137
if os.name == "posix":
125138
libpython = os.path.join(prefix,
@@ -232,9 +245,9 @@ def get_config_h_filename():
232245
"""Return full pathname of installed pyconfig.h file."""
233246
if python_build:
234247
if os.name == "nt":
235-
inc_dir = os.path.join(project_base, "PC")
248+
inc_dir = os.path.join(_sys_home or project_base, "PC")
236249
else:
237-
inc_dir = project_base
250+
inc_dir = _sys_home or project_base
238251
else:
239252
inc_dir = get_python_inc(plat_specific=1)
240253
if get_python_version() < '2.2':
@@ -248,7 +261,8 @@ def get_config_h_filename():
248261
def get_makefile_filename():
249262
"""Return full pathname of installed Makefile from the Python build."""
250263
if python_build:
251-
return os.path.join(os.path.dirname(sys.executable), "Makefile")
264+
return os.path.join(_sys_home or os.path.dirname(sys.executable),
265+
"Makefile")
252266
lib_dir = get_python_lib(plat_specific=0, standard_lib=1)
253267
config_file = 'config-{}{}'.format(get_python_version(), build_flags)
254268
return os.path.join(lib_dir, config_file, 'Makefile')

Lib/gettext.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
'dgettext', 'dngettext', 'gettext', 'ngettext',
5656
]
5757

58-
_default_localedir = os.path.join(sys.prefix, 'share', 'locale')
58+
_default_localedir = os.path.join(sys.base_prefix, 'share', 'locale')
5959

6060

6161
def c2py(plural):

Lib/idlelib/EditorWindow.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ class EditorWindow(object):
120120

121121
def __init__(self, flist=None, filename=None, key=None, root=None):
122122
if EditorWindow.help_url is None:
123-
dochome = os.path.join(sys.prefix, 'Doc', 'index.html')
123+
dochome = os.path.join(sys.base_prefix, 'Doc', 'index.html')
124124
if sys.platform.count('linux'):
125125
# look for html docs in a couple of standard places
126126
pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3]
@@ -131,13 +131,13 @@ def __init__(self, flist=None, filename=None, key=None, root=None):
131131
dochome = os.path.join(basepath, pyver,
132132
'Doc', 'index.html')
133133
elif sys.platform[:3] == 'win':
134-
chmfile = os.path.join(sys.prefix, 'Doc',
134+
chmfile = os.path.join(sys.base_prefix, 'Doc',
135135
'Python%s.chm' % _sphinx_version())
136136
if os.path.isfile(chmfile):
137137
dochome = chmfile
138138
elif macosxSupport.runningAsOSXApp():
139139
# documentation is stored inside the python framework
140-
dochome = os.path.join(sys.prefix,
140+
dochome = os.path.join(sys.base_prefix,
141141
'Resources/English.lproj/Documentation/index.html')
142142
dochome = os.path.normpath(dochome)
143143
if os.path.isfile(dochome):

Lib/packaging/command/build_ext.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,10 @@ def finalize_options(self):
182182
# the 'libs' directory is for binary installs - we assume that
183183
# must be the *native* platform. But we don't really support
184184
# cross-compiling via a binary install anyway, so we let it go.
185-
self.library_dirs.append(os.path.join(sys.exec_prefix, 'libs'))
185+
# Note that we must use sys.base_exec_prefix here rather than
186+
# exec_prefix, since the Python libs are not copied to a virtual
187+
# environment.
188+
self.library_dirs.append(os.path.join(sys.base_exec_prefix, 'libs'))
186189
if self.debug:
187190
self.build_temp = os.path.join(self.build_temp, "Debug")
188191
else:

0 commit comments

Comments
 (0)