Implemented PEP 405 (Python virtual environments).
This commit is contained in:
parent
f2bdc3690a
commit
7ded1f0f69
|
@ -23,3 +23,4 @@ The list of modules described in this chapter is:
|
|||
unittest.mock-examples.rst
|
||||
2to3.rst
|
||||
test.rst
|
||||
venv.rst
|
||||
|
|
|
@ -29,6 +29,26 @@ always available.
|
|||
command line, see the :mod:`fileinput` module.
|
||||
|
||||
|
||||
.. data:: base_exec_prefix
|
||||
|
||||
Set during Python startup, before ``site.py`` is run, to the same value as
|
||||
:data:`exec_prefix`. If not running in a virtual environment, the values
|
||||
will stay the same; if ``site.py`` finds that a virtual environment is in
|
||||
use, the values of :data:`prefix` and :data:`exec_prefix` will be changed to
|
||||
point to the virtual environment, whereas :data:`base_prefix` and
|
||||
:data:`base_exec_prefix` will remain pointing to the base Python
|
||||
installation (the one which the virtual environment was created from).
|
||||
|
||||
.. data:: base_prefix
|
||||
|
||||
Set during Python startup, before ``site.py`` is run, to the same value as
|
||||
:data:`prefix`. If not running in a virtual environment, the values
|
||||
will stay the same; if ``site.py`` finds that a virtual environment is in
|
||||
use, the values of :data:`prefix` and :data:`exec_prefix` will be changed to
|
||||
point to the virtual environment, whereas :data:`base_prefix` and
|
||||
:data:`base_exec_prefix` will remain pointing to the base Python
|
||||
installation (the one which the virtual environment was created from).
|
||||
|
||||
.. data:: byteorder
|
||||
|
||||
An indicator of the native byte order. This will have the value ``'big'`` on
|
||||
|
@ -199,6 +219,10 @@ always available.
|
|||
installed in :file:`{exec_prefix}/lib/python{X.Y}/lib-dynload`, where *X.Y*
|
||||
is the version number of Python, for example ``3.2``.
|
||||
|
||||
.. note:: If a virtual environment is in effect, this value will be changed
|
||||
in ``site.py`` to point to the virtual environment. The value for the
|
||||
Python installation will still be available, via :data:`base_exec_prefix`.
|
||||
|
||||
|
||||
.. data:: executable
|
||||
|
||||
|
@ -775,6 +799,10 @@ always available.
|
|||
stored in :file:`{prefix}/include/python{X.Y}`, where *X.Y* is the version
|
||||
number of Python, for example ``3.2``.
|
||||
|
||||
.. note:: If a virtual environment is in effect, this value will be changed
|
||||
in ``site.py`` to point to the virtual environment. The value for the
|
||||
Python installation will still be available, via :data:`base_prefix`.
|
||||
|
||||
|
||||
.. data:: ps1
|
||||
ps2
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
:mod:`venv` --- Creation of virtual environments
|
||||
================================================
|
||||
|
||||
.. module:: venv
|
||||
:synopsis: Creation of virtual environments.
|
||||
.. moduleauthor:: Vinay Sajip <vinay_sajip@yahoo.co.uk>
|
||||
.. sectionauthor:: Vinay Sajip <vinay_sajip@yahoo.co.uk>
|
||||
|
||||
|
||||
.. index:: pair: Environments; virtual
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
**Source code:** :source:`Lib/venv.py`
|
||||
|
||||
--------------
|
||||
|
||||
The :mod:`venv` module provides support for creating lightweight
|
||||
"virtual environments" with their own site directories, optionally
|
||||
isolated from system site directories. Each virtual environment has
|
||||
its own Python binary (allowing creation of environments with various
|
||||
Python versions) and can have its own independent set of installed
|
||||
Python packages in its site directories.
|
||||
|
||||
Creating virtual environments
|
||||
-----------------------------
|
||||
|
||||
Creation of virtual environments is simplest executing the ``pyvenv``
|
||||
script::
|
||||
|
||||
pyvenv /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 the
|
||||
command was run from. It also creates a ``bin`` (or ``Scripts`` on
|
||||
Windows) subdirectory containing a copy of the ``python`` binary (or
|
||||
binaries, in the case of Windows) and the ``pysetup3`` script (to
|
||||
facilitate easy installation of packages from PyPI into the new virtualenv).
|
||||
It also creates an (initially empty) ``lib/pythonX.Y/site-packages``
|
||||
subdirectory (on Windows, this is ``Lib\site-packages``).
|
||||
|
||||
.. highlight:: none
|
||||
|
||||
On Windows, you may have to invoke the ``pyvenv`` script as follows, if you
|
||||
don't have the relevant PATH and PATHEXT settings::
|
||||
|
||||
c:\Temp>c:\Python33\python c:\Python33\Tools\Scripts\pyvenv.py myenv
|
||||
|
||||
or equivalently::
|
||||
|
||||
c:\Temp>c:\Python33\python -m venv myenv
|
||||
|
||||
The command, if run with ``-h``, will show the available options::
|
||||
|
||||
usage: pyvenv [-h] [--system-site-packages] [--symlink] [--clear]
|
||||
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 access to the global site-packages dir to the
|
||||
virtual environment.
|
||||
--symlink Attempt to symlink rather than copy.
|
||||
--clear Delete the environment directory if it already exists.
|
||||
If not specified and the directory exists, an error is
|
||||
raised.
|
||||
|
||||
|
||||
If the target directory already exists an error will be raised, unless
|
||||
the ``--clear`` option was provided, in which case the target
|
||||
directory will be deleted and virtual environment creation will
|
||||
proceed as usual.
|
||||
|
||||
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.
|
||||
|
||||
Multiple paths can be given to ``pyvenv``, in which case an identical
|
||||
virtualenv will be created, according to the given options, at each
|
||||
provided path.
|
||||
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
The high-level method described above makes use of a simple API which provides
|
||||
mechanisms for third-party virtual environment creators to customize
|
||||
environment creation according to their needs.
|
||||
|
||||
The :class:`EnvBuilder` class accepts the following keyword arguments on
|
||||
instantiation:
|
||||
|
||||
* ``system_site_packages`` - A Boolean value indicating that the
|
||||
system Python site-packages should be available to the
|
||||
environment (defaults to ``False``).
|
||||
|
||||
* ``clear`` - A Boolean value which, if True, will delete any
|
||||
existing target directory instead of raising an exception
|
||||
(defaults to ``False``).
|
||||
|
||||
* ``symlinks`` - A Boolean value indicating whether to attempt
|
||||
to symlink the Python binary (and any necessary DLLs or other
|
||||
binaries, e.g. ``pythonw.exe``), rather than copying. Defaults to
|
||||
``True`` on Linux and Unix systems, but ``False`` on Windows and
|
||||
Mac OS X.
|
||||
|
||||
The returned env-builder is an object which has a method, ``create``,
|
||||
which takes as required argument the path (absolute or relative to the current
|
||||
directory) of the target directory which is to contain the virtual environment.
|
||||
The ``create`` method will either create the environment in the specified
|
||||
directory, or raise an appropriate exception.
|
||||
|
||||
Creators of third-party virtual environment tools will be free to use
|
||||
the provided ``EnvBuilder`` class as a base class.
|
||||
|
||||
.. highlight:: python
|
||||
|
||||
The ``venv`` module will also provide a module-level function as a
|
||||
convenience::
|
||||
|
||||
def create(env_dir,
|
||||
system_site_packages=False, clear=False, symlinks=False):
|
||||
builder = EnvBuilder(
|
||||
system_site_packages=system_site_packages,
|
||||
clear=clear,
|
||||
symlinks=symlinks)
|
||||
builder.create(env_dir)
|
||||
|
||||
The ``create`` method of the ``EnvBuilder`` class illustrates the
|
||||
hooks available for subclass customization::
|
||||
|
||||
def create(self, env_dir):
|
||||
"""
|
||||
Create a virtualized Python environment in a directory.
|
||||
|
||||
:param env_dir: The target directory to create an environment in.
|
||||
|
||||
"""
|
||||
env_dir = os.path.abspath(env_dir)
|
||||
context = self.create_directories(env_dir)
|
||||
self.create_configuration(context)
|
||||
self.setup_python(context)
|
||||
self.setup_scripts(context)
|
||||
self.post_setup(context)
|
||||
|
||||
Each of the methods ``create_directories``, ``create_configuration``,
|
||||
``setup_python``, ``setup_scripts`` and ``post_setup`` can be
|
||||
overridden. The functions of these methods are:
|
||||
|
||||
* ``create_directories`` - creates the environment directory and
|
||||
all necessary directories, and returns a context object. This is
|
||||
just a holder for attributes (such as paths), for use by the
|
||||
other methods.
|
||||
|
||||
* ``create_configuration`` - creates the ``pyvenv.cfg``
|
||||
configuration file in the environment.
|
||||
|
||||
* ``setup_python`` - creates a copy of the Python executable (and,
|
||||
under Windows, DLLs) in the environment.
|
||||
|
||||
* ``setup_scripts`` - Installs activation scripts appropriate to the
|
||||
platform into the virtual environment.
|
||||
|
||||
* ``post_setup`` - A placeholder method which can be overridden
|
||||
in third party implementations to pre-install packages in the
|
||||
virtual environment or perform other post-creation steps.
|
||||
|
||||
In addition, ``EnvBuilder`` provides an ``install_scripts`` utility
|
||||
method that can be called from ``setup_scripts`` or ``post_setup`` in
|
||||
subclasses to assist in installing custom scripts into the virtual
|
||||
environment. The method accepts as arguments the context object (see
|
||||
above) and a path to a directory. The directory should contain
|
||||
subdirectories "common", "posix", "nt", each containing scripts
|
||||
destined for the bin directory in the environment. The contents of
|
||||
"common" and the directory corresponding to ``os.name`` are copied
|
||||
after some text replacement of placeholders:
|
||||
|
||||
* ``__VENV_DIR__`` is replaced with the absolute path of the
|
||||
environment directory.
|
||||
|
||||
* ``__VENV_NAME__`` is replaced with the environment name (final path
|
||||
segment of environment directory).
|
||||
|
||||
* ``__VENV_BIN_NAME__`` is replaced with the name of the bin directory
|
||||
(either ``bin`` or ``Scripts``).
|
||||
|
||||
* ``__VENV_PYTHON__`` is replaced with the absolute path of the
|
||||
environment's executable.
|
|
@ -18,6 +18,8 @@ from .errors import DistutilsPlatformError
|
|||
# These are needed in a couple of spots, so just compute them once.
|
||||
PREFIX = os.path.normpath(sys.prefix)
|
||||
EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
|
||||
BASE_PREFIX = os.path.normpath(sys.base_prefix)
|
||||
BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix)
|
||||
|
||||
# Path to the base directory of the project. On Windows the binary may
|
||||
# live in project/PCBuild9. If we're dealing with an x64 Windows build,
|
||||
|
@ -39,11 +41,18 @@ if os.name == "nt" and "\\pcbuild\\amd64" in project_base[-14:].lower():
|
|||
# different (hard-wired) directories.
|
||||
# Setup.local is available for Makefile builds including VPATH builds,
|
||||
# Setup.dist is available on Windows
|
||||
def _python_build():
|
||||
def _is_python_source_dir(d):
|
||||
for fn in ("Setup.dist", "Setup.local"):
|
||||
if os.path.isfile(os.path.join(project_base, "Modules", fn)):
|
||||
if os.path.isfile(os.path.join(d, "Modules", fn)):
|
||||
return True
|
||||
return False
|
||||
_sys_home = getattr(sys, '_home', None)
|
||||
if _sys_home and os.name == 'nt' and _sys_home.lower().endswith('pcbuild'):
|
||||
_sys_home = os.path.dirname(_sys_home)
|
||||
def _python_build():
|
||||
if _sys_home:
|
||||
return _is_python_source_dir(_sys_home)
|
||||
return _is_python_source_dir(project_base)
|
||||
python_build = _python_build()
|
||||
|
||||
# Calculate the build qualifier flags if they are defined. Adding the flags
|
||||
|
@ -74,11 +83,11 @@ def get_python_inc(plat_specific=0, prefix=None):
|
|||
otherwise, this is the path to platform-specific header files
|
||||
(namely pyconfig.h).
|
||||
|
||||
If 'prefix' is supplied, use it instead of sys.prefix or
|
||||
sys.exec_prefix -- i.e., ignore 'plat_specific'.
|
||||
If 'prefix' is supplied, use it instead of sys.base_prefix or
|
||||
sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
|
||||
"""
|
||||
if prefix is None:
|
||||
prefix = plat_specific and EXEC_PREFIX or PREFIX
|
||||
prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX
|
||||
if os.name == "posix":
|
||||
if python_build:
|
||||
# Assume the executable is in the build directory. The
|
||||
|
@ -86,11 +95,12 @@ def get_python_inc(plat_specific=0, prefix=None):
|
|||
# the build directory may not be the source directory, we
|
||||
# must use "srcdir" from the makefile to find the "Include"
|
||||
# directory.
|
||||
base = os.path.dirname(os.path.abspath(sys.executable))
|
||||
base = _sys_home or os.path.dirname(os.path.abspath(sys.executable))
|
||||
if plat_specific:
|
||||
return base
|
||||
else:
|
||||
incdir = os.path.join(get_config_var('srcdir'), 'Include')
|
||||
incdir = os.path.join(_sys_home or get_config_var('srcdir'),
|
||||
'Include')
|
||||
return os.path.normpath(incdir)
|
||||
python_dir = 'python' + get_python_version() + build_flags
|
||||
return os.path.join(prefix, "include", python_dir)
|
||||
|
@ -115,10 +125,13 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
|
|||
containing standard Python library modules; otherwise, return the
|
||||
directory for site-specific modules.
|
||||
|
||||
If 'prefix' is supplied, use it instead of sys.prefix or
|
||||
sys.exec_prefix -- i.e., ignore 'plat_specific'.
|
||||
If 'prefix' is supplied, use it instead of sys.base_prefix or
|
||||
sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
|
||||
"""
|
||||
if prefix is None:
|
||||
if standard_lib:
|
||||
prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX
|
||||
else:
|
||||
prefix = plat_specific and EXEC_PREFIX or PREFIX
|
||||
|
||||
if os.name == "posix":
|
||||
|
@ -232,9 +245,9 @@ def get_config_h_filename():
|
|||
"""Return full pathname of installed pyconfig.h file."""
|
||||
if python_build:
|
||||
if os.name == "nt":
|
||||
inc_dir = os.path.join(project_base, "PC")
|
||||
inc_dir = os.path.join(_sys_home or project_base, "PC")
|
||||
else:
|
||||
inc_dir = project_base
|
||||
inc_dir = _sys_home or project_base
|
||||
else:
|
||||
inc_dir = get_python_inc(plat_specific=1)
|
||||
if get_python_version() < '2.2':
|
||||
|
@ -248,7 +261,8 @@ def get_config_h_filename():
|
|||
def get_makefile_filename():
|
||||
"""Return full pathname of installed Makefile from the Python build."""
|
||||
if python_build:
|
||||
return os.path.join(os.path.dirname(sys.executable), "Makefile")
|
||||
return os.path.join(_sys_home or os.path.dirname(sys.executable),
|
||||
"Makefile")
|
||||
lib_dir = get_python_lib(plat_specific=0, standard_lib=1)
|
||||
config_file = 'config-{}{}'.format(get_python_version(), build_flags)
|
||||
return os.path.join(lib_dir, config_file, 'Makefile')
|
||||
|
|
|
@ -55,7 +55,7 @@ __all__ = ['NullTranslations', 'GNUTranslations', 'Catalog',
|
|||
'dgettext', 'dngettext', 'gettext', 'ngettext',
|
||||
]
|
||||
|
||||
_default_localedir = os.path.join(sys.prefix, 'share', 'locale')
|
||||
_default_localedir = os.path.join(sys.base_prefix, 'share', 'locale')
|
||||
|
||||
|
||||
def c2py(plural):
|
||||
|
|
|
@ -120,7 +120,7 @@ class EditorWindow(object):
|
|||
|
||||
def __init__(self, flist=None, filename=None, key=None, root=None):
|
||||
if EditorWindow.help_url is None:
|
||||
dochome = os.path.join(sys.prefix, 'Doc', 'index.html')
|
||||
dochome = os.path.join(sys.base_prefix, 'Doc', 'index.html')
|
||||
if sys.platform.count('linux'):
|
||||
# look for html docs in a couple of standard places
|
||||
pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3]
|
||||
|
@ -131,13 +131,13 @@ class EditorWindow(object):
|
|||
dochome = os.path.join(basepath, pyver,
|
||||
'Doc', 'index.html')
|
||||
elif sys.platform[:3] == 'win':
|
||||
chmfile = os.path.join(sys.prefix, 'Doc',
|
||||
chmfile = os.path.join(sys.base_prefix, 'Doc',
|
||||
'Python%s.chm' % _sphinx_version())
|
||||
if os.path.isfile(chmfile):
|
||||
dochome = chmfile
|
||||
elif macosxSupport.runningAsOSXApp():
|
||||
# documentation is stored inside the python framework
|
||||
dochome = os.path.join(sys.prefix,
|
||||
dochome = os.path.join(sys.base_prefix,
|
||||
'Resources/English.lproj/Documentation/index.html')
|
||||
dochome = os.path.normpath(dochome)
|
||||
if os.path.isfile(dochome):
|
||||
|
|
|
@ -182,7 +182,10 @@ class build_ext(Command):
|
|||
# the 'libs' directory is for binary installs - we assume that
|
||||
# must be the *native* platform. But we don't really support
|
||||
# cross-compiling via a binary install anyway, so we let it go.
|
||||
self.library_dirs.append(os.path.join(sys.exec_prefix, 'libs'))
|
||||
# Note that we must use sys.base_exec_prefix here rather than
|
||||
# exec_prefix, since the Python libs are not copied to a virtual
|
||||
# environment.
|
||||
self.library_dirs.append(os.path.join(sys.base_exec_prefix, 'libs'))
|
||||
if self.debug:
|
||||
self.build_temp = os.path.join(self.build_temp, "Debug")
|
||||
else:
|
||||
|
|
|
@ -369,7 +369,7 @@ class Doc:
|
|||
|
||||
docloc = os.environ.get("PYTHONDOCS", self.PYTHONDOCS)
|
||||
|
||||
basedir = os.path.join(sys.exec_prefix, "lib",
|
||||
basedir = os.path.join(sys.base_exec_prefix, "lib",
|
||||
"python%d.%d" % sys.version_info[:2])
|
||||
if (isinstance(object, type(os)) and
|
||||
(object.__name__ in ('errno', 'exceptions', 'gc', 'imp',
|
||||
|
|
88
Lib/site.py
88
Lib/site.py
|
@ -13,6 +13,19 @@ prefixes directly, as well as with lib/site-packages appended. The
|
|||
resulting directories, if they exist, are appended to sys.path, and
|
||||
also inspected for path configuration files.
|
||||
|
||||
If a file named "pyvenv.cfg" exists one directory above sys.executable,
|
||||
sys.prefix and sys.exec_prefix are set to that directory and
|
||||
it is also checked for site-packages and site-python (sys.prefix and
|
||||
sys.exec_prefix will always be the "real" prefixes of the Python
|
||||
installation). If "pyvenv.cfg" (a bootstrap configuration file) contains
|
||||
the key "include-system-site-packages" set to anything other than "false"
|
||||
(case-insensitive), the system-level prefixes will still also be
|
||||
searched for site-packages; otherwise they won't.
|
||||
|
||||
All of the resulting site-specific directories, if they exist, are
|
||||
appended to sys.path, and also inspected for path configuration
|
||||
files.
|
||||
|
||||
A path configuration file is a file whose name has the form
|
||||
<package>.pth; its contents are additional directories (one per line)
|
||||
to be added to sys.path. Non-existing directories (or
|
||||
|
@ -54,6 +67,7 @@ ImportError exception, it is silently ignored.
|
|||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import builtins
|
||||
|
||||
# Prefixes for site-packages; add additional prefixes like /usr/local here
|
||||
|
@ -179,6 +193,7 @@ def addsitedir(sitedir, known_paths=None):
|
|||
sitedir, sitedircase = makepath(sitedir)
|
||||
if not sitedircase in known_paths:
|
||||
sys.path.append(sitedir) # Add path component
|
||||
known_paths.add(sitedircase)
|
||||
try:
|
||||
names = os.listdir(sitedir)
|
||||
except os.error:
|
||||
|
@ -266,18 +281,21 @@ def addusersitepackages(known_paths):
|
|||
addsitedir(user_site, known_paths)
|
||||
return known_paths
|
||||
|
||||
def getsitepackages():
|
||||
def getsitepackages(prefixes=None):
|
||||
"""Returns a list containing all global site-packages directories
|
||||
(and possibly site-python).
|
||||
|
||||
For each directory present in the global ``PREFIXES``, this function
|
||||
will find its `site-packages` subdirectory depending on the system
|
||||
environment, and will return a list of full paths.
|
||||
For each directory present in ``prefixes`` (or the global ``PREFIXES``),
|
||||
this function will find its `site-packages` subdirectory depending on the
|
||||
system environment, and will return a list of full paths.
|
||||
"""
|
||||
sitepackages = []
|
||||
seen = set()
|
||||
|
||||
for prefix in PREFIXES:
|
||||
if prefixes is None:
|
||||
prefixes = PREFIXES
|
||||
|
||||
for prefix in prefixes:
|
||||
if not prefix or prefix in seen:
|
||||
continue
|
||||
seen.add(prefix)
|
||||
|
@ -303,9 +321,9 @@ def getsitepackages():
|
|||
sys.version[:3], "site-packages"))
|
||||
return sitepackages
|
||||
|
||||
def addsitepackages(known_paths):
|
||||
def addsitepackages(known_paths, prefixes=None):
|
||||
"""Add site-packages (and possibly site-python) to sys.path"""
|
||||
for sitedir in getsitepackages():
|
||||
for sitedir in getsitepackages(prefixes):
|
||||
if os.path.isdir(sitedir):
|
||||
addsitedir(sitedir, known_paths)
|
||||
|
||||
|
@ -475,6 +493,61 @@ def aliasmbcs():
|
|||
encodings.aliases.aliases[enc] = 'mbcs'
|
||||
|
||||
|
||||
CONFIG_LINE = re.compile(r'^(?P<key>(\w|[-_])+)\s*=\s*(?P<value>.*)\s*$')
|
||||
|
||||
def venv(known_paths):
|
||||
global PREFIXES, ENABLE_USER_SITE
|
||||
|
||||
env = os.environ
|
||||
if sys.platform == 'darwin' and '__PYTHONV_LAUNCHER__' in env:
|
||||
executable = os.environ['__PYTHONV_LAUNCHER__']
|
||||
else:
|
||||
executable = sys.executable
|
||||
executable_dir, executable_name = os.path.split(executable)
|
||||
site_prefix = os.path.dirname(executable_dir)
|
||||
sys._home = None
|
||||
if sys.platform == 'win32':
|
||||
executable_name = os.path.splitext(executable_name)[0]
|
||||
conf_basename = 'pyvenv.cfg'
|
||||
candidate_confs = [
|
||||
conffile for conffile in (
|
||||
os.path.join(executable_dir, conf_basename),
|
||||
os.path.join(site_prefix, conf_basename)
|
||||
)
|
||||
if os.path.isfile(conffile)
|
||||
]
|
||||
|
||||
if candidate_confs:
|
||||
virtual_conf = candidate_confs[0]
|
||||
system_site = "true"
|
||||
with open(virtual_conf) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
m = CONFIG_LINE.match(line)
|
||||
if m:
|
||||
d = m.groupdict()
|
||||
key, value = d['key'].lower(), d['value']
|
||||
if key == 'include-system-site-packages':
|
||||
system_site = value.lower()
|
||||
elif key == 'home':
|
||||
sys._home = value
|
||||
|
||||
sys.prefix = sys.exec_prefix = site_prefix
|
||||
|
||||
# Doing this here ensures venv takes precedence over user-site
|
||||
addsitepackages(known_paths, [sys.prefix])
|
||||
|
||||
# addsitepackages will process site_prefix again if its in PREFIXES,
|
||||
# but that's ok; known_paths will prevent anything being added twice
|
||||
if system_site == "true":
|
||||
PREFIXES.insert(0, sys.prefix)
|
||||
else:
|
||||
PREFIXES = [sys.prefix]
|
||||
ENABLE_USER_SITE = False
|
||||
|
||||
return known_paths
|
||||
|
||||
|
||||
def execsitecustomize():
|
||||
"""Run custom site specific code, if available."""
|
||||
try:
|
||||
|
@ -517,6 +590,7 @@ def main():
|
|||
|
||||
abs_paths()
|
||||
known_paths = removeduppaths()
|
||||
known_paths = venv(known_paths)
|
||||
if ENABLE_USER_SITE is None:
|
||||
ENABLE_USER_SITE = check_enableusersite()
|
||||
known_paths = addusersitepackages(known_paths)
|
||||
|
|
|
@ -1021,7 +1021,7 @@ class Popen(object):
|
|||
if not os.path.exists(w9xpopen):
|
||||
# Eeek - file-not-found - possibly an embedding
|
||||
# situation - see if we can locate it in sys.exec_prefix
|
||||
w9xpopen = os.path.join(os.path.dirname(sys.exec_prefix),
|
||||
w9xpopen = os.path.join(os.path.dirname(sys.base_exec_prefix),
|
||||
"w9xpopen.exe")
|
||||
if not os.path.exists(w9xpopen):
|
||||
raise RuntimeError("Cannot locate w9xpopen.exe, which is "
|
||||
|
|
|
@ -36,41 +36,41 @@ statedir = /var
|
|||
# User resource directory
|
||||
local = ~/.local/{distribution.name}
|
||||
|
||||
stdlib = {base}/lib/python{py_version_short}
|
||||
stdlib = {installed_base}/lib/python{py_version_short}
|
||||
platstdlib = {platbase}/lib/python{py_version_short}
|
||||
purelib = {base}/lib/python{py_version_short}/site-packages
|
||||
platlib = {platbase}/lib/python{py_version_short}/site-packages
|
||||
include = {base}/include/python{py_version_short}{abiflags}
|
||||
platinclude = {platbase}/include/python{py_version_short}{abiflags}
|
||||
include = {installed_base}/include/python{py_version_short}{abiflags}
|
||||
platinclude = {installed_platbase}/include/python{py_version_short}{abiflags}
|
||||
data = {base}
|
||||
|
||||
[posix_home]
|
||||
stdlib = {base}/lib/python
|
||||
stdlib = {installed_base}/lib/python
|
||||
platstdlib = {base}/lib/python
|
||||
purelib = {base}/lib/python
|
||||
platlib = {base}/lib/python
|
||||
include = {base}/include/python
|
||||
platinclude = {base}/include/python
|
||||
include = {installed_base}/include/python
|
||||
platinclude = {installed_base}/include/python
|
||||
scripts = {base}/bin
|
||||
data = {base}
|
||||
|
||||
[nt]
|
||||
stdlib = {base}/Lib
|
||||
stdlib = {installed_base}/Lib
|
||||
platstdlib = {base}/Lib
|
||||
purelib = {base}/Lib/site-packages
|
||||
platlib = {base}/Lib/site-packages
|
||||
include = {base}/Include
|
||||
platinclude = {base}/Include
|
||||
include = {installed_base}/Include
|
||||
platinclude = {installed_base}/Include
|
||||
scripts = {base}/Scripts
|
||||
data = {base}
|
||||
|
||||
[os2]
|
||||
stdlib = {base}/Lib
|
||||
stdlib = {installed_base}/Lib
|
||||
platstdlib = {base}/Lib
|
||||
purelib = {base}/Lib/site-packages
|
||||
platlib = {base}/Lib/site-packages
|
||||
include = {base}/Include
|
||||
platinclude = {base}/Include
|
||||
include = {installed_base}/Include
|
||||
platinclude = {installed_base}/Include
|
||||
scripts = {base}/Scripts
|
||||
data = {base}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import os
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
from os.path import pardir, realpath
|
||||
from configparser import RawConfigParser
|
||||
|
||||
|
@ -61,13 +62,15 @@ def _expand_globals(config):
|
|||
|
||||
_expand_globals(_SCHEMES)
|
||||
|
||||
# FIXME don't rely on sys.version here, its format is an implementatin detail
|
||||
# FIXME don't rely on sys.version here, its format is an implementation detail
|
||||
# of CPython, use sys.version_info or sys.hexversion
|
||||
_PY_VERSION = sys.version.split()[0]
|
||||
_PY_VERSION_SHORT = sys.version[:3]
|
||||
_PY_VERSION_SHORT_NO_DOT = _PY_VERSION[0] + _PY_VERSION[2]
|
||||
_PREFIX = os.path.normpath(sys.prefix)
|
||||
_BASE_PREFIX = os.path.normpath(sys.base_prefix)
|
||||
_EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
|
||||
_BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix)
|
||||
_CONFIG_VARS = None
|
||||
_USER_BASE = None
|
||||
|
||||
|
@ -94,14 +97,22 @@ if os.name == "nt" and "\\pc\\v" in _PROJECT_BASE[-10:].lower():
|
|||
if os.name == "nt" and "\\pcbuild\\amd64" in _PROJECT_BASE[-14:].lower():
|
||||
_PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir))
|
||||
|
||||
|
||||
def is_python_build():
|
||||
def _is_python_source_dir(d):
|
||||
for fn in ("Setup.dist", "Setup.local"):
|
||||
if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)):
|
||||
if os.path.isfile(os.path.join(d, "Modules", fn)):
|
||||
return True
|
||||
return False
|
||||
|
||||
_PYTHON_BUILD = is_python_build()
|
||||
_sys_home = getattr(sys, '_home', None)
|
||||
if _sys_home and os.name == 'nt' and _sys_home.lower().endswith('pcbuild'):
|
||||
_sys_home = os.path.dirname(_sys_home)
|
||||
|
||||
def is_python_build(check_home=False):
|
||||
if check_home and _sys_home:
|
||||
return _is_python_source_dir(_sys_home)
|
||||
return _is_python_source_dir(_PROJECT_BASE)
|
||||
|
||||
_PYTHON_BUILD = is_python_build(True)
|
||||
|
||||
if _PYTHON_BUILD:
|
||||
for scheme in ('posix_prefix', 'posix_home'):
|
||||
|
@ -312,7 +323,7 @@ def _parse_makefile(filename, vars=None):
|
|||
def get_makefile_filename():
|
||||
"""Return the path of the Makefile."""
|
||||
if _PYTHON_BUILD:
|
||||
return os.path.join(_PROJECT_BASE, "Makefile")
|
||||
return os.path.join(_sys_home or _PROJECT_BASE, "Makefile")
|
||||
if hasattr(sys, 'abiflags'):
|
||||
config_dir_name = 'config-%s%s' % (_PY_VERSION_SHORT, sys.abiflags)
|
||||
else:
|
||||
|
@ -412,9 +423,9 @@ def get_config_h_filename():
|
|||
"""Return the path of pyconfig.h."""
|
||||
if _PYTHON_BUILD:
|
||||
if os.name == "nt":
|
||||
inc_dir = os.path.join(_PROJECT_BASE, "PC")
|
||||
inc_dir = os.path.join(_sys_home or _PROJECT_BASE, "PC")
|
||||
else:
|
||||
inc_dir = _PROJECT_BASE
|
||||
inc_dir = _sys_home or _PROJECT_BASE
|
||||
else:
|
||||
inc_dir = get_path('platinclude')
|
||||
return os.path.join(inc_dir, 'pyconfig.h')
|
||||
|
@ -472,7 +483,9 @@ def get_config_vars(*args):
|
|||
_CONFIG_VARS['py_version'] = _PY_VERSION
|
||||
_CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT
|
||||
_CONFIG_VARS['py_version_nodot'] = _PY_VERSION[0] + _PY_VERSION[2]
|
||||
_CONFIG_VARS['installed_base'] = _BASE_PREFIX
|
||||
_CONFIG_VARS['base'] = _PREFIX
|
||||
_CONFIG_VARS['installed_platbase'] = _BASE_EXEC_PREFIX
|
||||
_CONFIG_VARS['platbase'] = _EXEC_PREFIX
|
||||
_CONFIG_VARS['projectbase'] = _PROJECT_BASE
|
||||
try:
|
||||
|
|
|
@ -564,7 +564,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
|
|||
random.shuffle(selected)
|
||||
if trace:
|
||||
import trace, tempfile
|
||||
tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix,
|
||||
tracer = trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix,
|
||||
tempfile.gettempdir()],
|
||||
trace=False, count=True)
|
||||
|
||||
|
|
|
@ -228,7 +228,7 @@ def test_main(verbose=None):
|
|||
|
||||
def test_coverage(coverdir):
|
||||
trace = support.import_module('trace')
|
||||
tracer=trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix,],
|
||||
tracer=trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix,],
|
||||
trace=0, count=1)
|
||||
tracer.run('reload(cmd);test_main()')
|
||||
r=tracer.results()
|
||||
|
|
|
@ -2543,7 +2543,7 @@ import sys, re, io
|
|||
|
||||
def test_coverage(coverdir):
|
||||
trace = support.import_module('trace')
|
||||
tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix,],
|
||||
tracer = trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix,],
|
||||
trace=0, count=1)
|
||||
tracer.run('test_main()')
|
||||
r = tracer.results()
|
||||
|
|
|
@ -189,6 +189,8 @@ class ProcessTestCase(BaseTestCase):
|
|||
p.wait()
|
||||
self.assertEqual(p.stderr, None)
|
||||
|
||||
@unittest.skipIf(sys.base_prefix != sys.prefix,
|
||||
'Test is not venv-compatible')
|
||||
def test_executable_with_cwd(self):
|
||||
python_dir = os.path.dirname(os.path.realpath(sys.executable))
|
||||
p = subprocess.Popen(["somethingyoudonthave", "-c",
|
||||
|
@ -197,6 +199,8 @@ class ProcessTestCase(BaseTestCase):
|
|||
p.wait()
|
||||
self.assertEqual(p.returncode, 47)
|
||||
|
||||
@unittest.skipIf(sys.base_prefix != sys.prefix,
|
||||
'Test is not venv-compatible')
|
||||
@unittest.skipIf(sysconfig.is_python_build(),
|
||||
"need an installed Python. See #7774")
|
||||
def test_executable_without_cwd(self):
|
||||
|
|
|
@ -419,6 +419,7 @@ class SysModuleTest(unittest.TestCase):
|
|||
self.assertIsInstance(sys.builtin_module_names, tuple)
|
||||
self.assertIsInstance(sys.copyright, str)
|
||||
self.assertIsInstance(sys.exec_prefix, str)
|
||||
self.assertIsInstance(sys.base_exec_prefix, str)
|
||||
self.assertIsInstance(sys.executable, str)
|
||||
self.assertEqual(len(sys.float_info), 11)
|
||||
self.assertEqual(sys.float_info.radix, 2)
|
||||
|
@ -450,6 +451,7 @@ class SysModuleTest(unittest.TestCase):
|
|||
self.assertEqual(sys.maxunicode, 0x10FFFF)
|
||||
self.assertIsInstance(sys.platform, str)
|
||||
self.assertIsInstance(sys.prefix, str)
|
||||
self.assertIsInstance(sys.base_prefix, str)
|
||||
self.assertIsInstance(sys.version, str)
|
||||
vi = sys.version_info
|
||||
self.assertIsInstance(vi[:], tuple)
|
||||
|
@ -541,6 +543,8 @@ class SysModuleTest(unittest.TestCase):
|
|||
out = p.communicate()[0].strip()
|
||||
self.assertEqual(out, b'?')
|
||||
|
||||
@unittest.skipIf(sys.base_prefix != sys.prefix,
|
||||
'Test is not venv-compatible')
|
||||
def test_executable(self):
|
||||
# sys.executable should be absolute
|
||||
self.assertEqual(os.path.abspath(sys.executable), sys.executable)
|
||||
|
|
|
@ -260,12 +260,17 @@ class TestSysConfig(unittest.TestCase):
|
|||
# the global scheme mirrors the distinction between prefix and
|
||||
# exec-prefix but not the user scheme, so we have to adapt the paths
|
||||
# before comparing (issue #9100)
|
||||
adapt = sys.prefix != sys.exec_prefix
|
||||
adapt = sys.base_prefix != sys.base_exec_prefix
|
||||
for name in ('stdlib', 'platstdlib', 'purelib', 'platlib'):
|
||||
global_path = get_path(name, 'posix_prefix')
|
||||
if adapt:
|
||||
global_path = global_path.replace(sys.exec_prefix, sys.prefix)
|
||||
base = base.replace(sys.exec_prefix, sys.prefix)
|
||||
global_path = global_path.replace(sys.exec_prefix, sys.base_prefix)
|
||||
base = base.replace(sys.exec_prefix, sys.base_prefix)
|
||||
elif sys.base_prefix != sys.prefix:
|
||||
# virtual environment? Likewise, we have to adapt the paths
|
||||
# before comparing
|
||||
global_path = global_path.replace(sys.base_prefix, sys.prefix)
|
||||
base = base.replace(sys.base_prefix, sys.prefix)
|
||||
user_path = get_path(name, 'posix_user')
|
||||
self.assertEqual(user_path, global_path.replace(base, user, 1))
|
||||
|
||||
|
|
|
@ -316,8 +316,8 @@ class TestCoverage(unittest.TestCase):
|
|||
# Ignore all files, nothing should be traced nor printed
|
||||
libpath = os.path.normpath(os.path.dirname(os.__file__))
|
||||
# sys.prefix does not work when running from a checkout
|
||||
tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix, libpath],
|
||||
trace=0, count=1)
|
||||
tracer = trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix,
|
||||
libpath], trace=0, count=1)
|
||||
with captured_stdout() as stdout:
|
||||
self._coverage(tracer)
|
||||
if os.path.exists(TESTFN):
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2011 by Vinay Sajip. All Rights Reserved.
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software and its
|
||||
# documentation for any purpose and without fee is hereby granted,
|
||||
# provided that the above copyright notice appear in all copies and that
|
||||
# both that copyright notice and this permission notice appear in
|
||||
# supporting documentation, and that the name of Vinay Sajip
|
||||
# not be used in advertising or publicity pertaining to distribution
|
||||
# of the software without specific, written prior permission.
|
||||
# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
|
||||
# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
||||
# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
|
||||
# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
||||
# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
"""Test harness for the venv module. Run all tests.
|
||||
|
||||
Copyright (C) 2011 Vinay Sajip. All Rights Reserved.
|
||||
"""
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
from test.support import (captured_stdout, captured_stderr, run_unittest,
|
||||
can_symlink)
|
||||
import unittest
|
||||
import venv
|
||||
|
||||
class BaseTest(unittest.TestCase):
|
||||
"""Base class for venv tests."""
|
||||
|
||||
def setUp(self):
|
||||
self.env_dir = tempfile.mkdtemp()
|
||||
if os.name == 'nt':
|
||||
self.bindir = 'Scripts'
|
||||
self.ps3name = 'pysetup3-script.py'
|
||||
self.lib = ('Lib',)
|
||||
self.include = 'Include'
|
||||
self.exe = 'python.exe'
|
||||
else:
|
||||
self.bindir = 'bin'
|
||||
self.ps3name = 'pysetup3'
|
||||
self.lib = ('lib', 'python%s' % sys.version[:3])
|
||||
self.include = 'include'
|
||||
self.exe = 'python'
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.env_dir)
|
||||
|
||||
def run_with_capture(self, func, *args, **kwargs):
|
||||
with captured_stdout() as output:
|
||||
with captured_stderr() as error:
|
||||
func(*args, **kwargs)
|
||||
return output.getvalue(), error.getvalue()
|
||||
|
||||
def get_env_file(self, *args):
|
||||
return os.path.join(self.env_dir, *args)
|
||||
|
||||
def get_text_file_contents(self, *args):
|
||||
with open(self.get_env_file(*args), 'r') as f:
|
||||
result = f.read()
|
||||
return result
|
||||
|
||||
class BasicTest(BaseTest):
|
||||
"""Test venv module functionality."""
|
||||
|
||||
def test_defaults(self):
|
||||
"""
|
||||
Test the create function with default arguments.
|
||||
"""
|
||||
def isdir(*args):
|
||||
fn = self.get_env_file(*args)
|
||||
self.assertTrue(os.path.isdir(fn))
|
||||
|
||||
shutil.rmtree(self.env_dir)
|
||||
self.run_with_capture(venv.create, self.env_dir)
|
||||
isdir(self.bindir)
|
||||
isdir(self.include)
|
||||
isdir(*self.lib)
|
||||
data = self.get_text_file_contents('pyvenv.cfg')
|
||||
if sys.platform == 'darwin' and ('__PYTHONV_LAUNCHER__'
|
||||
in os.environ):
|
||||
executable = os.environ['__PYTHONV_LAUNCHER__']
|
||||
else:
|
||||
executable = sys.executable
|
||||
path = os.path.dirname(executable)
|
||||
self.assertIn('home = %s' % path, data)
|
||||
data = self.get_text_file_contents(self.bindir, self.ps3name)
|
||||
self.assertTrue(data.startswith('#!%s%s' % (self.env_dir, os.sep)))
|
||||
fn = self.get_env_file(self.bindir, self.exe)
|
||||
self.assertTrue(os.path.exists(fn))
|
||||
|
||||
def test_overwrite_existing(self):
|
||||
"""
|
||||
Test control of overwriting an existing environment directory.
|
||||
"""
|
||||
self.assertRaises(ValueError, venv.create, self.env_dir)
|
||||
builder = venv.EnvBuilder(clear=True)
|
||||
builder.create(self.env_dir)
|
||||
|
||||
def test_isolation(self):
|
||||
"""
|
||||
Test isolation from system site-packages
|
||||
"""
|
||||
for ssp, s in ((True, 'true'), (False, 'false')):
|
||||
builder = venv.EnvBuilder(clear=True, system_site_packages=ssp)
|
||||
builder.create(self.env_dir)
|
||||
data = self.get_text_file_contents('pyvenv.cfg')
|
||||
self.assertIn('include-system-site-packages = %s\n' % s, data)
|
||||
|
||||
@unittest.skipUnless(can_symlink(), 'Needs symlinks')
|
||||
def test_symlinking(self):
|
||||
"""
|
||||
Test symlinking works as expected
|
||||
"""
|
||||
for usl in (False, True):
|
||||
builder = venv.EnvBuilder(clear=True, symlinks=usl)
|
||||
if (usl and sys.platform == 'darwin' and
|
||||
'__PYTHONV_LAUNCHER__' in os.environ):
|
||||
self.assertRaises(ValueError, builder.create, self.env_dir)
|
||||
else:
|
||||
builder.create(self.env_dir)
|
||||
fn = self.get_env_file(self.bindir, self.exe)
|
||||
# Don't test when False, because e.g. 'python' is always
|
||||
# symlinked to 'python3.3' in the env, even when symlinking in
|
||||
# general isn't wanted.
|
||||
if usl:
|
||||
self.assertTrue(os.path.islink(fn))
|
||||
|
||||
def test_main():
|
||||
run_unittest(BasicTest)
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_main()
|
|
@ -46,10 +46,10 @@ else:
|
|||
s = "\\" + s[3:]
|
||||
return s
|
||||
|
||||
prefix = os.path.join(sys.prefix,"tcl")
|
||||
prefix = os.path.join(sys.base_prefix,"tcl")
|
||||
if not os.path.exists(prefix):
|
||||
# devdir/../tcltk/lib
|
||||
prefix = os.path.join(sys.prefix, os.path.pardir, "tcltk", "lib")
|
||||
prefix = os.path.join(sys.base_prefix, os.path.pardir, "tcltk", "lib")
|
||||
prefix = os.path.abspath(prefix)
|
||||
# if this does not exist, no further search is needed
|
||||
if os.path.exists(prefix):
|
||||
|
|
|
@ -39,8 +39,8 @@ Sample use, programmatically
|
|||
|
||||
# create a Trace object, telling it what to ignore, and whether to
|
||||
# do tracing or line-counting or both.
|
||||
tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix,], trace=0,
|
||||
count=1)
|
||||
tracer = trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix,],
|
||||
trace=0, count=1)
|
||||
# run the new command using the given tracer
|
||||
tracer.run('main()')
|
||||
# make a report, placing output in /tmp
|
||||
|
@ -749,10 +749,10 @@ def main(argv=None):
|
|||
# should I also call expanduser? (after all, could use $HOME)
|
||||
|
||||
s = s.replace("$prefix",
|
||||
os.path.join(sys.prefix, "lib",
|
||||
os.path.join(sys.base_prefix, "lib",
|
||||
"python" + sys.version[:3]))
|
||||
s = s.replace("$exec_prefix",
|
||||
os.path.join(sys.exec_prefix, "lib",
|
||||
os.path.join(sys.base_exec_prefix, "lib",
|
||||
"python" + sys.version[:3]))
|
||||
s = os.path.normpath(s)
|
||||
ignore_dirs.append(s)
|
||||
|
|
|
@ -0,0 +1,502 @@
|
|||
# Copyright (C) 2011-2012 Vinay Sajip.
|
||||
#
|
||||
# Use with a Python executable built from the Python fork at
|
||||
#
|
||||
# https://bitbucket.org/vinay.sajip/pythonv/ as follows:
|
||||
#
|
||||
# python -m venv env_dir
|
||||
#
|
||||
# You'll need an Internet connection (needed to download distribute_setup.py).
|
||||
#
|
||||
# The script will change to the environment's binary directory and run
|
||||
#
|
||||
# ./python distribute_setup.py
|
||||
#
|
||||
# after which you can change to the environment's directory and do some
|
||||
# installations, e.g.
|
||||
#
|
||||
# source bin/activate.sh
|
||||
# pysetup3 install setuptools-git
|
||||
# pysetup3 install Pygments
|
||||
# pysetup3 install Jinja2
|
||||
# pysetup3 install SQLAlchemy
|
||||
# pysetup3 install coverage
|
||||
#
|
||||
# Note that on Windows, distributions which include C extensions (e.g. coverage)
|
||||
# may fail due to lack of a suitable C compiler.
|
||||
#
|
||||
import base64
|
||||
import io
|
||||
import logging
|
||||
import os
|
||||
import os.path
|
||||
import shutil
|
||||
import sys
|
||||
import zipfile
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Context:
|
||||
"""
|
||||
Holds information about a current virtualisation request.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class EnvBuilder:
|
||||
"""
|
||||
This class exists to allow virtual environment creation to be
|
||||
customised. The constructor parameters determine the builder's
|
||||
behaviour when called upon to create a virtual environment.
|
||||
|
||||
By default, the builder makes the system (global) site-packages dir
|
||||
available to the created environment.
|
||||
|
||||
By default, the creation process uses symlinks wherever possible.
|
||||
|
||||
:param system_site_packages: If True, the system (global) site-packages
|
||||
dir is available to created environments.
|
||||
:param clear: If True and the target directory exists, it is deleted.
|
||||
Otherwise, if the target directory exists, an error is
|
||||
raised.
|
||||
:param symlinks: If True, attempt to symlink rather than copy files into
|
||||
virtual environment.
|
||||
:param upgrade: If True, upgrade an existing virtual environment.
|
||||
"""
|
||||
|
||||
def __init__(self, system_site_packages=False, clear=False,
|
||||
symlinks=False, upgrade=False):
|
||||
self.system_site_packages = system_site_packages
|
||||
self.clear = clear
|
||||
self.symlinks = symlinks
|
||||
self.upgrade = upgrade
|
||||
|
||||
def create(self, env_dir):
|
||||
"""
|
||||
Create a virtual environment in a directory.
|
||||
|
||||
:param env_dir: The target directory to create an environment in.
|
||||
|
||||
"""
|
||||
if (self.symlinks and
|
||||
sys.platform == 'darwin' and
|
||||
'Library/Framework' in sys.base_prefix):
|
||||
# Symlinking the stub executable in an OSX framework build will
|
||||
# result in a broken virtual environment.
|
||||
raise ValueError(
|
||||
"Symlinking is not supported on OSX framework Python.")
|
||||
env_dir = os.path.abspath(env_dir)
|
||||
context = self.ensure_directories(env_dir)
|
||||
self.create_configuration(context)
|
||||
self.setup_python(context)
|
||||
if not self.upgrade:
|
||||
self.setup_scripts(context)
|
||||
self.post_setup(context)
|
||||
|
||||
def ensure_directories(self, env_dir):
|
||||
"""
|
||||
Create the directories for the environment.
|
||||
|
||||
Returns a context object which holds paths in the environment,
|
||||
for use by subsequent logic.
|
||||
"""
|
||||
|
||||
def create_if_needed(d):
|
||||
if not os.path.exists(d):
|
||||
os.makedirs(d)
|
||||
|
||||
if os.path.exists(env_dir) and not (self.clear or self.upgrade):
|
||||
raise ValueError('Directory exists: %s' % env_dir)
|
||||
if os.path.exists(env_dir) and self.clear:
|
||||
shutil.rmtree(env_dir)
|
||||
context = Context()
|
||||
context.env_dir = env_dir
|
||||
context.env_name = os.path.split(env_dir)[1]
|
||||
context.prompt = '(%s) ' % context.env_name
|
||||
create_if_needed(env_dir)
|
||||
env = os.environ
|
||||
if sys.platform == 'darwin' and '__PYTHONV_LAUNCHER__' in env:
|
||||
executable = os.environ['__PYTHONV_LAUNCHER__']
|
||||
else:
|
||||
executable = sys.executable
|
||||
dirname, exename = os.path.split(os.path.abspath(executable))
|
||||
context.executable = executable
|
||||
context.python_dir = dirname
|
||||
context.python_exe = exename
|
||||
if sys.platform == 'win32':
|
||||
binname = 'Scripts'
|
||||
incpath = 'Include'
|
||||
libpath = os.path.join(env_dir, 'Lib', 'site-packages')
|
||||
else:
|
||||
binname = 'bin'
|
||||
incpath = 'include'
|
||||
libpath = os.path.join(env_dir, 'lib', 'python%d.%d' % sys.version_info[:2], 'site-packages')
|
||||
context.inc_path = path = os.path.join(env_dir, incpath)
|
||||
create_if_needed(path)
|
||||
create_if_needed(libpath)
|
||||
context.bin_path = binpath = os.path.join(env_dir, binname)
|
||||
context.bin_name = binname
|
||||
context.env_exe = os.path.join(binpath, exename)
|
||||
create_if_needed(binpath)
|
||||
return context
|
||||
|
||||
def create_configuration(self, context):
|
||||
"""
|
||||
Create a configuration file indicating where the environment's Python
|
||||
was copied from, and whether the system site-packages should be made
|
||||
available in the environment.
|
||||
|
||||
:param context: The information for the environment creation request
|
||||
being processed.
|
||||
"""
|
||||
context.cfg_path = path = os.path.join(context.env_dir, 'pyvenv.cfg')
|
||||
with open(path, 'w', encoding='utf-8') as f:
|
||||
f.write('home = %s\n' % context.python_dir)
|
||||
if self.system_site_packages:
|
||||
incl = 'true'
|
||||
else:
|
||||
incl = 'false'
|
||||
f.write('include-system-site-packages = %s\n' % incl)
|
||||
f.write('version = %d.%d.%d\n' % sys.version_info[:3])
|
||||
|
||||
if os.name == 'nt':
|
||||
def include_binary(self, f):
|
||||
if f.endswith(('.pyd', '.dll')):
|
||||
result = True
|
||||
else:
|
||||
result = f.startswith('python') and f.endswith('.exe')
|
||||
return result
|
||||
|
||||
def symlink_or_copy(self, src, dst):
|
||||
"""
|
||||
Try symlinking a file, and if that fails, fall back to copying.
|
||||
"""
|
||||
force_copy = not self.symlinks
|
||||
if not force_copy:
|
||||
try:
|
||||
if not os.path.islink(dst): # can't link to itself!
|
||||
os.symlink(src, dst)
|
||||
except Exception: # may need to use a more specific exception
|
||||
logger.warning('Unable to symlink %r to %r', src, dst)
|
||||
force_copy = True
|
||||
if force_copy:
|
||||
shutil.copyfile(src, dst)
|
||||
|
||||
def setup_python(self, context):
|
||||
"""
|
||||
Set up a Python executable in the environment.
|
||||
|
||||
:param context: The information for the environment creation request
|
||||
being processed.
|
||||
"""
|
||||
binpath = context.bin_path
|
||||
exename = context.python_exe
|
||||
path = context.env_exe
|
||||
copier = self.symlink_or_copy
|
||||
copier(context.executable, path)
|
||||
dirname = context.python_dir
|
||||
if os.name != 'nt':
|
||||
if not os.path.islink(path):
|
||||
os.chmod(path, 0o755)
|
||||
path = os.path.join(binpath, 'python')
|
||||
if not os.path.exists(path):
|
||||
os.symlink(exename, path)
|
||||
else:
|
||||
subdir = 'DLLs'
|
||||
include = self.include_binary
|
||||
files = [f for f in os.listdir(dirname) if include(f)]
|
||||
for f in files:
|
||||
src = os.path.join(dirname, f)
|
||||
dst = os.path.join(binpath, f)
|
||||
if dst != context.env_exe: # already done, above
|
||||
copier(src, dst)
|
||||
dirname = os.path.join(dirname, subdir)
|
||||
if os.path.isdir(dirname):
|
||||
files = [f for f in os.listdir(dirname) if include(f)]
|
||||
for f in files:
|
||||
src = os.path.join(dirname, f)
|
||||
dst = os.path.join(binpath, f)
|
||||
copier(src, dst)
|
||||
# copy init.tcl over
|
||||
for root, dirs, files in os.walk(context.python_dir):
|
||||
if 'init.tcl' in files:
|
||||
tcldir = os.path.basename(root)
|
||||
tcldir = os.path.join(context.env_dir, 'Lib', tcldir)
|
||||
os.makedirs(tcldir)
|
||||
src = os.path.join(root, 'init.tcl')
|
||||
dst = os.path.join(tcldir, 'init.tcl')
|
||||
shutil.copyfile(src, dst)
|
||||
break
|
||||
|
||||
def setup_scripts(self, context):
|
||||
"""
|
||||
Set up scripts into the created environment from a directory.
|
||||
|
||||
This method installs the default scripts into the environment
|
||||
being created. You can prevent the default installation by overriding
|
||||
this method if you really need to, or if you need to specify
|
||||
a different location for the scripts to install. By default, the
|
||||
'scripts' directory in the venv package is used as the source of
|
||||
scripts to install.
|
||||
"""
|
||||
path = os.path.abspath(os.path.dirname(__file__))
|
||||
path = os.path.join(path, 'scripts')
|
||||
self.install_scripts(context, path)
|
||||
|
||||
def post_setup(self, context):
|
||||
"""
|
||||
Hook for post-setup modification of the venv. Subclasses may install
|
||||
additional packages or scripts here, add activation shell scripts, etc.
|
||||
|
||||
:param context: The information for the environment creation request
|
||||
being processed.
|
||||
"""
|
||||
pass
|
||||
|
||||
def replace_variables(self, text, context):
|
||||
"""
|
||||
Replace variable placeholders in script text with context-specific
|
||||
variables.
|
||||
|
||||
Return the text passed in , but with variables replaced.
|
||||
|
||||
:param text: The text in which to replace placeholder variables.
|
||||
:param context: The information for the environment creation request
|
||||
being processed.
|
||||
"""
|
||||
text = text.replace('__VENV_DIR__', context.env_dir)
|
||||
text = text.replace('__VENV_NAME__', context.prompt)
|
||||
text = text.replace('__VENV_BIN_NAME__', context.bin_name)
|
||||
text = text.replace('__VENV_PYTHON__', context.env_exe)
|
||||
return text
|
||||
|
||||
def install_scripts(self, context, path):
|
||||
"""
|
||||
Install scripts into the created environment from a directory.
|
||||
|
||||
:param context: The information for the environment creation request
|
||||
being processed.
|
||||
:param path: Absolute pathname of a directory containing script.
|
||||
Scripts in the 'common' subdirectory of this directory,
|
||||
and those in the directory named for the platform
|
||||
being run on, are installed in the created environment.
|
||||
Placeholder variables are replaced with environment-
|
||||
specific values.
|
||||
"""
|
||||
binpath = context.bin_path
|
||||
plen = len(path)
|
||||
for root, dirs, files in os.walk(path):
|
||||
if root == path: # at top-level, remove irrelevant dirs
|
||||
for d in dirs[:]:
|
||||
if d not in ('common', os.name):
|
||||
dirs.remove(d)
|
||||
continue # ignore files in top level
|
||||
for f in files:
|
||||
srcfile = os.path.join(root, f)
|
||||
suffix = root[plen:].split(os.sep)[2:]
|
||||
if not suffix:
|
||||
dstdir = binpath
|
||||
else:
|
||||
dstdir = os.path.join(binpath, *suffix)
|
||||
if not os.path.exists(dstdir):
|
||||
os.makedirs(dstdir)
|
||||
dstfile = os.path.join(dstdir, f)
|
||||
with open(srcfile, 'rb') as f:
|
||||
data = f.read()
|
||||
if srcfile.endswith('.exe'):
|
||||
mode = 'wb'
|
||||
else:
|
||||
mode = 'w'
|
||||
data = data.decode('utf-8')
|
||||
data = self.replace_variables(data, context)
|
||||
with open(dstfile, mode) as f:
|
||||
f.write(data)
|
||||
os.chmod(dstfile, 0o755)
|
||||
|
||||
|
||||
# This class will not be included in Python core; it's here for now to
|
||||
# facilitate experimentation and testing, and as proof-of-concept of what could
|
||||
# be done by external extension tools.
|
||||
class DistributeEnvBuilder(EnvBuilder):
|
||||
"""
|
||||
By default, this builder installs Distribute so that you can pip or
|
||||
easy_install other packages into the created environment.
|
||||
|
||||
:param nodist: If True, Distribute is not installed into the created
|
||||
environment.
|
||||
:param progress: If Distribute is 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 Distribute.
|
||||
|
||||
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.progress = kwargs.pop("progress", None)
|
||||
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.
|
||||
"""
|
||||
if not self.nodist:
|
||||
self.install_distribute(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:
|
||||
sys.stderr.write('.')
|
||||
#sys.stderr.write(s.decode('utf-8'))
|
||||
sys.stderr.flush()
|
||||
stream.close()
|
||||
|
||||
def install_distribute(self, context):
|
||||
"""
|
||||
Install Distribute in the environment.
|
||||
|
||||
:param context: The information for the environment creation request
|
||||
being processed.
|
||||
"""
|
||||
from subprocess import Popen, PIPE
|
||||
from threading import Thread
|
||||
from urllib.request import urlretrieve
|
||||
|
||||
url = 'http://python-distribute.org/distribute_setup.py'
|
||||
binpath = context.bin_path
|
||||
distpath = os.path.join(binpath, 'distribute_setup.py')
|
||||
# Download Distribute in the env
|
||||
urlretrieve(url, distpath)
|
||||
progress = self.progress
|
||||
if progress is not None:
|
||||
progress('Installing distribute', 'main')
|
||||
else:
|
||||
sys.stderr.write('Installing distribute ')
|
||||
sys.stderr.flush()
|
||||
# Install Distribute in the env
|
||||
args = [context.env_exe, 'distribute_setup.py']
|
||||
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 create(env_dir, system_site_packages=False, clear=False, symlinks=False):
|
||||
"""
|
||||
Create a virtual environment in a directory.
|
||||
|
||||
By default, makes the system (global) site-packages dir available to
|
||||
the created environment.
|
||||
|
||||
:param env_dir: The target directory to create an environment in.
|
||||
:param system_site_packages: If True, the system (global) site-packages
|
||||
dir is available to the environment.
|
||||
:param clear: If True and the target directory exists, it is deleted.
|
||||
Otherwise, if the target directory exists, an error is
|
||||
raised.
|
||||
:param symlinks: If True, attempt to symlink rather than copy files into
|
||||
virtual environment.
|
||||
"""
|
||||
# XXX This should be changed to EnvBuilder.
|
||||
builder = DistributeEnvBuilder(system_site_packages=system_site_packages,
|
||||
clear=clear, symlinks=symlinks)
|
||||
builder.create(env_dir)
|
||||
|
||||
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 (pythonv variant)')
|
||||
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.')
|
||||
# XXX This option will be removed.
|
||||
parser.add_argument('--no-distribute', default=False,
|
||||
action='store_true', dest='nodist',
|
||||
help="Don't install Distribute 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' or (sys.platform == 'darwin' and
|
||||
'Library/Framework' in sys.base_prefix):
|
||||
use_symlinks = False
|
||||
else:
|
||||
use_symlinks = True
|
||||
parser.add_argument('--symlinks', default=use_symlinks,
|
||||
action='store_true', dest='symlinks',
|
||||
help="Attempt to symlink rather than copy.")
|
||||
parser.add_argument('--clear', default=False, action='store_true',
|
||||
dest='clear', help='Delete the environment '
|
||||
'directory if it already '
|
||||
'exists. If not specified and '
|
||||
'the directory exists, an error'
|
||||
' is raised.')
|
||||
parser.add_argument('--upgrade', default=False, action='store_true',
|
||||
dest='upgrade', help='Upgrade the environment '
|
||||
'directory to use this version '
|
||||
'of Python, assuming it has been '
|
||||
'upgraded in-place.')
|
||||
options = parser.parse_args(args)
|
||||
if options.upgrade and options.clear:
|
||||
raise ValueError('you cannot supply --upgrade and --clear together.')
|
||||
# XXX This will be changed to EnvBuilder
|
||||
builder = DistributeEnvBuilder(system_site_packages=options.system_site,
|
||||
clear=options.clear,
|
||||
symlinks=options.symlinks,
|
||||
upgrade=options.upgrade,
|
||||
nodist=options.nodist)
|
||||
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)
|
|
@ -0,0 +1,10 @@
|
|||
import sys
|
||||
from . import main
|
||||
|
||||
rc = 1
|
||||
try:
|
||||
main()
|
||||
rc = 0
|
||||
except Exception as e:
|
||||
print('Error: %s' % e, file=sys.stderr)
|
||||
sys.exit(rc)
|
|
@ -0,0 +1,34 @@
|
|||
$env:VIRTUAL_ENV="__VENV_DIR__"
|
||||
|
||||
# Revert to original values
|
||||
if (Test-Path function:_OLD_VIRTUAL_PROMPT) {
|
||||
copy-item function:_OLD_VIRTUAL_PROMPT function:prompt
|
||||
remove-item function:_OLD_VIRTUAL_PROMPT
|
||||
}
|
||||
|
||||
if (Test-Path env:_OLD_VIRTUAL_PYTHONHOME) {
|
||||
copy-item env:_OLD_VIRTUAL_PYTHONHOME env:PYTHONHOME
|
||||
remove-item env:_OLD_VIRTUAL_PYTHONHOME
|
||||
}
|
||||
|
||||
if (Test-Path env:_OLD_VIRTUAL_PATH) {
|
||||
copy-item env:_OLD_VIRTUAL_PATH env:PATH
|
||||
remove-item env:_OLD_VIRTUAL_PATH
|
||||
}
|
||||
|
||||
# Set the prompt to include the env name
|
||||
copy-item function:prompt function:_OLD_VIRTUAL_PROMPT
|
||||
function prompt {
|
||||
Write-Host -NoNewline -ForegroundColor Green [__VENV_NAME__]
|
||||
_OLD_VIRTUAL_PROMPT
|
||||
}
|
||||
|
||||
# Clear PYTHONHOME
|
||||
if (Test-Path env:PYTHONHOME) {
|
||||
copy-item env:PYTHONHOME env:_OLD_VIRTUAL_PYTHONHOME
|
||||
remove-item env:PYTHONHOME
|
||||
}
|
||||
|
||||
# Add the venv to the PATH
|
||||
copy-item env:PATH env:_OLD_VIRTUAL_PATH
|
||||
$env:PATH = "$env:VIRTUAL_ENV\__VENV_BIN_NAME__;$env:PATH"
|
|
@ -0,0 +1,19 @@
|
|||
# Revert to original values
|
||||
if (Test-Path function:_OLD_VIRTUAL_PROMPT) {
|
||||
copy-item function:_OLD_VIRTUAL_PROMPT function:prompt
|
||||
remove-item function:_OLD_VIRTUAL_PROMPT
|
||||
}
|
||||
|
||||
if (Test-Path env:_OLD_VIRTUAL_PYTHONHOME) {
|
||||
copy-item env:_OLD_VIRTUAL_PYTHONHOME env:PYTHONHOME
|
||||
remove-item env:_OLD_VIRTUAL_PYTHONHOME
|
||||
}
|
||||
|
||||
if (Test-Path env:_OLD_VIRTUAL_PATH) {
|
||||
copy-item env:_OLD_VIRTUAL_PATH env:PATH
|
||||
remove-item env:_OLD_VIRTUAL_PATH
|
||||
}
|
||||
|
||||
if (Test-Path env:VIRTUAL_ENV) {
|
||||
remove-item env:VIRTUAL_ENV
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
@echo off
|
||||
set VIRTUAL_ENV=__VENV_DIR__
|
||||
|
||||
if not defined PROMPT (
|
||||
set PROMPT=$P$G
|
||||
)
|
||||
|
||||
if defined _OLD_VIRTUAL_PROMPT (
|
||||
set PROMPT=%_OLD_VIRTUAL_PROMPT%
|
||||
)
|
||||
|
||||
if defined _OLD_VIRTUAL_PYTHONHOME (
|
||||
set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%
|
||||
)
|
||||
|
||||
set _OLD_VIRTUAL_PROMPT=%PROMPT%
|
||||
set PROMPT=__VENV_NAME__%PROMPT%
|
||||
|
||||
if defined PYTHONHOME (
|
||||
set _OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME%
|
||||
set PYTHONHOME=
|
||||
)
|
||||
|
||||
if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH%; goto SKIPPATH
|
||||
|
||||
set _OLD_VIRTUAL_PATH=%PATH%
|
||||
|
||||
:SKIPPATH
|
||||
set PATH=%VIRTUAL_ENV%\__VENV_BIN_NAME__;%PATH%
|
||||
|
||||
:END
|
|
@ -0,0 +1,17 @@
|
|||
@echo off
|
||||
|
||||
if defined _OLD_VIRTUAL_PROMPT (
|
||||
set PROMPT=%_OLD_VIRTUAL_PROMPT%
|
||||
)
|
||||
set _OLD_VIRTUAL_PROMPT=
|
||||
|
||||
if defined _OLD_VIRTUAL_PYTHONHOME (
|
||||
set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%
|
||||
set _OLD_VIRTUAL_PYTHONHOME=
|
||||
)
|
||||
|
||||
if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH%
|
||||
|
||||
set _OLD_VIRTUAL_PATH=
|
||||
|
||||
:END
|
|
@ -0,0 +1,11 @@
|
|||
#!__VENV_PYTHON__
|
||||
if __name__ == '__main__':
|
||||
rc = 1
|
||||
try:
|
||||
import sys, re, packaging.run
|
||||
sys.argv[0] = re.sub('-script.pyw?$', '', sys.argv[0])
|
||||
rc = packaging.run.main() # None interpreted as 0
|
||||
except Exception:
|
||||
# use syntax which works with either 2.x or 3.x
|
||||
sys.stderr.write('%s\n' % sys.exc_info()[1])
|
||||
sys.exit(rc)
|
Binary file not shown.
|
@ -0,0 +1,76 @@
|
|||
# This file must be used with "source bin/activate" *from bash*
|
||||
# you cannot run it directly
|
||||
|
||||
deactivate () {
|
||||
# reset old environment variables
|
||||
if [ -n "$_OLD_VIRTUAL_PATH" ] ; then
|
||||
PATH="$_OLD_VIRTUAL_PATH"
|
||||
export PATH
|
||||
unset _OLD_VIRTUAL_PATH
|
||||
fi
|
||||
if [ -n "$_OLD_VIRTUAL_PYTHONHOME" ] ; then
|
||||
PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME"
|
||||
export PYTHONHOME
|
||||
unset _OLD_VIRTUAL_PYTHONHOME
|
||||
fi
|
||||
|
||||
# This should detect bash and zsh, which have a hash command that must
|
||||
# be called to get it to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then
|
||||
hash -r
|
||||
fi
|
||||
|
||||
if [ -n "$_OLD_VIRTUAL_PS1" ] ; then
|
||||
PS1="$_OLD_VIRTUAL_PS1"
|
||||
export PS1
|
||||
unset _OLD_VIRTUAL_PS1
|
||||
fi
|
||||
|
||||
unset VIRTUAL_ENV
|
||||
if [ ! "$1" = "nondestructive" ] ; then
|
||||
# Self destruct!
|
||||
unset -f deactivate
|
||||
fi
|
||||
}
|
||||
|
||||
# unset irrelavent variables
|
||||
deactivate nondestructive
|
||||
|
||||
VIRTUAL_ENV="__VENV_DIR__"
|
||||
export VIRTUAL_ENV
|
||||
|
||||
_OLD_VIRTUAL_PATH="$PATH"
|
||||
PATH="$VIRTUAL_ENV/__VENV_BIN_NAME__:$PATH"
|
||||
export PATH
|
||||
|
||||
# unset PYTHONHOME if set
|
||||
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
|
||||
# could use `if (set -u; : $PYTHONHOME) ;` in bash
|
||||
if [ -n "$PYTHONHOME" ] ; then
|
||||
_OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME"
|
||||
unset PYTHONHOME
|
||||
fi
|
||||
|
||||
if [ -z "$VIRTUAL_ENV_DISABLE_PROMPT" ] ; then
|
||||
_OLD_VIRTUAL_PS1="$PS1"
|
||||
if [ "x__VENV_NAME__" != x ] ; then
|
||||
PS1="__VENV_NAME__$PS1"
|
||||
else
|
||||
if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then
|
||||
# special case for Aspen magic directories
|
||||
# see http://www.zetadev.com/software/aspen/
|
||||
PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1"
|
||||
else
|
||||
PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1"
|
||||
fi
|
||||
fi
|
||||
export PS1
|
||||
fi
|
||||
|
||||
# This should detect bash and zsh, which have a hash command that must
|
||||
# be called to get it to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then
|
||||
hash -r
|
||||
fi
|
|
@ -0,0 +1,11 @@
|
|||
#!__VENV_PYTHON__
|
||||
if __name__ == '__main__':
|
||||
rc = 1
|
||||
try:
|
||||
import sys, re, packaging.run
|
||||
sys.argv[0] = re.sub('-script.pyw?$', '', sys.argv[0])
|
||||
rc = packaging.run.main() # None interpreted as 0
|
||||
except Exception:
|
||||
# use syntax which works with either 2.x or 3.x
|
||||
sys.stderr.write('%s\n' % sys.exc_info()[1])
|
||||
sys.exit(rc)
|
|
@ -72,7 +72,7 @@ installunixtools:
|
|||
for fn in python3 pythonw3 idle3 pydoc3 python3-config \
|
||||
python$(VERSION) pythonw$(VERSION) idle$(VERSION) \
|
||||
pydoc$(VERSION) python$(VERSION)-config 2to3 \
|
||||
2to3-$(VERSION) ;\
|
||||
2to3-$(VERSION) pyvenv pyvenv-$(VERSION) ;\
|
||||
do \
|
||||
ln -fs "$(prefix)/bin/$${fn}" "$(DESTDIR)$(FRAMEWORKUNIXTOOLSPREFIX)/bin/$${fn}" ;\
|
||||
done
|
||||
|
@ -93,7 +93,7 @@ altinstallunixtools:
|
|||
$(INSTALL) -d -m $(DIRMODE) "$(DESTDIR)$(FRAMEWORKUNIXTOOLSPREFIX)/bin" ;\
|
||||
fi
|
||||
for fn in python$(VERSION) pythonw$(VERSION) idle$(VERSION) \
|
||||
pydoc$(VERSION) python$(VERSION)-config 2to3-$(VERSION);\
|
||||
pydoc$(VERSION) python$(VERSION)-config 2to3-$(VERSION) pyvenv-$(VERSION) ;\
|
||||
do \
|
||||
ln -fs "$(prefix)/bin/$${fn}" "$(DESTDIR)$(FRAMEWORKUNIXTOOLSPREFIX)/bin/$${fn}" ;\
|
||||
done
|
||||
|
|
|
@ -150,6 +150,18 @@ setup_spawnattr(posix_spawnattr_t* spawnattr)
|
|||
int
|
||||
main(int argc, char **argv) {
|
||||
char* exec_path = get_python_path();
|
||||
static char path[PATH_MAX * 2];
|
||||
static char real_path[PATH_MAX * 2];
|
||||
int status;
|
||||
uint32_t size = PATH_MAX * 2;
|
||||
|
||||
/* Set the original executable path in the environment. */
|
||||
status = _NSGetExecutablePath(path, &size);
|
||||
if (status == 0) {
|
||||
if (realpath(path, real_path) != NULL) {
|
||||
setenv("__PYTHONV_LAUNCHER__", real_path, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Let argv[0] refer to the new interpreter. This is needed to
|
||||
|
|
|
@ -947,6 +947,8 @@ bininstall: altbininstall
|
|||
(cd $(DESTDIR)$(BINDIR); $(LN) -s 2to3-$(VERSION) 2to3)
|
||||
-rm -f $(DESTDIR)$(BINDIR)/pysetup3
|
||||
(cd $(DESTDIR)$(BINDIR); $(LN) -s pysetup$(VERSION) pysetup3)
|
||||
-rm -f $(DESTDIR)$(BINDIR)/pyvenv
|
||||
(cd $(DESTDIR)$(BINDIR); $(LN) -s pyvenv-$(VERSION) pyvenv)
|
||||
|
||||
# Install the manual page
|
||||
maninstall:
|
||||
|
@ -1038,6 +1040,7 @@ LIBSUBDIRS= tkinter tkinter/test tkinter/test/test_tkinter \
|
|||
turtledemo \
|
||||
multiprocessing multiprocessing/dummy \
|
||||
unittest unittest/test unittest/test/testmock \
|
||||
venv venv/scripts venv/scripts/posix \
|
||||
curses pydoc_data $(MACHDEPS)
|
||||
libinstall: build_all $(srcdir)/Lib/$(PLATDIR) $(srcdir)/Modules/xxmodule.c
|
||||
@for i in $(SCRIPTDIR) $(LIBDEST); \
|
||||
|
|
|
@ -260,6 +260,59 @@ absolutize(wchar_t *path)
|
|||
wcscpy(path, buffer);
|
||||
}
|
||||
|
||||
/* search for a prefix value in an environment file. If found, copy it
|
||||
to the provided buffer, which is expected to be no more than MAXPATHLEN
|
||||
bytes long.
|
||||
*/
|
||||
|
||||
static int
|
||||
find_env_config_value(FILE * env_file, const wchar_t * key, wchar_t * value)
|
||||
{
|
||||
int result = 0; /* meaning not found */
|
||||
char buffer[MAXPATHLEN*2+1]; /* allow extra for key, '=', etc. */
|
||||
|
||||
fseek(env_file, 0, SEEK_SET);
|
||||
while (!feof(env_file)) {
|
||||
char * p = fgets(buffer, MAXPATHLEN*2, env_file);
|
||||
wchar_t tmpbuffer[MAXPATHLEN*2+1];
|
||||
PyObject * decoded;
|
||||
int n;
|
||||
|
||||
if (p == NULL)
|
||||
break;
|
||||
n = strlen(p);
|
||||
if (p[n - 1] != '\n') {
|
||||
/* line has overflowed - bail */
|
||||
break;
|
||||
}
|
||||
if (p[0] == '#') /* Comment - skip */
|
||||
continue;
|
||||
decoded = PyUnicode_DecodeUTF8(buffer, n, "surrogateescape");
|
||||
if (decoded != NULL) {
|
||||
Py_ssize_t k;
|
||||
wchar_t * state;
|
||||
k = PyUnicode_AsWideChar(decoded,
|
||||
tmpbuffer, MAXPATHLEN * 2);
|
||||
Py_DECREF(decoded);
|
||||
if (k >= 0) {
|
||||
wchar_t * tok = wcstok(tmpbuffer, L" \t\r\n", &state);
|
||||
if ((tok != NULL) && !wcscmp(tok, key)) {
|
||||
tok = wcstok(NULL, L" \t", &state);
|
||||
if ((tok != NULL) && !wcscmp(tok, L"=")) {
|
||||
tok = wcstok(NULL, L"\r\n", &state);
|
||||
if (tok != NULL) {
|
||||
wcsncpy(value, tok, MAXPATHLEN);
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* search_for_prefix requires that argv0_path be no more than MAXPATHLEN
|
||||
bytes long.
|
||||
*/
|
||||
|
@ -565,6 +618,39 @@ calculate_path(void)
|
|||
MAXPATHLEN bytes long.
|
||||
*/
|
||||
|
||||
/* Search for an environment configuration file, first in the
|
||||
executable's directory and then in the parent directory.
|
||||
If found, open it for use when searching for prefixes.
|
||||
*/
|
||||
|
||||
{
|
||||
wchar_t tmpbuffer[MAXPATHLEN+1];
|
||||
wchar_t *env_cfg = L"pyvenv.cfg";
|
||||
FILE * env_file = NULL;
|
||||
|
||||
wcscpy(tmpbuffer, argv0_path);
|
||||
joinpath(tmpbuffer, env_cfg);
|
||||
env_file = _Py_wfopen(tmpbuffer, L"r");
|
||||
if (env_file == NULL) {
|
||||
errno = 0;
|
||||
reduce(tmpbuffer);
|
||||
reduce(tmpbuffer);
|
||||
joinpath(tmpbuffer, env_cfg);
|
||||
env_file = _Py_wfopen(tmpbuffer, L"r");
|
||||
if (env_file == NULL) {
|
||||
errno = 0;
|
||||
}
|
||||
}
|
||||
if (env_file != NULL) {
|
||||
/* Look for a 'home' variable and set argv0_path to it, if found */
|
||||
if (find_env_config_value(env_file, L"home", tmpbuffer)) {
|
||||
wcscpy(argv0_path, tmpbuffer);
|
||||
}
|
||||
fclose(env_file);
|
||||
env_file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(pfound = search_for_prefix(argv0_path, home, _prefix))) {
|
||||
if (!Py_FrozenFlag)
|
||||
fprintf(stderr,
|
||||
|
|
|
@ -423,6 +423,53 @@ get_progpath(void)
|
|||
progpath[0] = '\0';
|
||||
}
|
||||
|
||||
static int
|
||||
find_env_config_value(FILE * env_file, const wchar_t * key, wchar_t * value)
|
||||
{
|
||||
int result = 0; /* meaning not found */
|
||||
char buffer[MAXPATHLEN*2+1]; /* allow extra for key, '=', etc. */
|
||||
|
||||
fseek(env_file, 0, SEEK_SET);
|
||||
while (!feof(env_file)) {
|
||||
char * p = fgets(buffer, MAXPATHLEN*2, env_file);
|
||||
wchar_t tmpbuffer[MAXPATHLEN*2+1];
|
||||
PyObject * decoded;
|
||||
int n;
|
||||
|
||||
if (p == NULL)
|
||||
break;
|
||||
n = strlen(p);
|
||||
if (p[n - 1] != '\n') {
|
||||
/* line has overflowed - bail */
|
||||
break;
|
||||
}
|
||||
if (p[0] == '#') /* Comment - skip */
|
||||
continue;
|
||||
decoded = PyUnicode_DecodeUTF8(buffer, n, "surrogateescape");
|
||||
if (decoded != NULL) {
|
||||
Py_ssize_t k;
|
||||
k = PyUnicode_AsWideChar(decoded,
|
||||
tmpbuffer, MAXPATHLEN * 2);
|
||||
Py_DECREF(decoded);
|
||||
if (k >= 0) {
|
||||
wchar_t * tok = wcstok(tmpbuffer, L" \t\r\n");
|
||||
if ((tok != NULL) && !wcscmp(tok, key)) {
|
||||
tok = wcstok(NULL, L" \t");
|
||||
if ((tok != NULL) && !wcscmp(tok, L"=")) {
|
||||
tok = wcstok(NULL, L"\r\n");
|
||||
if (tok != NULL) {
|
||||
wcsncpy(value, tok, MAXPATHLEN);
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
calculate_path(void)
|
||||
{
|
||||
|
@ -457,6 +504,40 @@ calculate_path(void)
|
|||
/* progpath guaranteed \0 terminated in MAXPATH+1 bytes. */
|
||||
wcscpy(argv0_path, progpath);
|
||||
reduce(argv0_path);
|
||||
|
||||
/* Search for an environment configuration file, first in the
|
||||
executable's directory and then in the parent directory.
|
||||
If found, open it for use when searching for prefixes.
|
||||
*/
|
||||
|
||||
{
|
||||
wchar_t tmpbuffer[MAXPATHLEN+1];
|
||||
wchar_t *env_cfg = L"pyvenv.cfg";
|
||||
FILE * env_file = NULL;
|
||||
|
||||
wcscpy(tmpbuffer, argv0_path);
|
||||
join(tmpbuffer, env_cfg);
|
||||
env_file = _Py_wfopen(tmpbuffer, L"r");
|
||||
if (env_file == NULL) {
|
||||
errno = 0;
|
||||
reduce(tmpbuffer);
|
||||
reduce(tmpbuffer);
|
||||
join(tmpbuffer, env_cfg);
|
||||
env_file = _Py_wfopen(tmpbuffer, L"r");
|
||||
if (env_file == NULL) {
|
||||
errno = 0;
|
||||
}
|
||||
}
|
||||
if (env_file != NULL) {
|
||||
/* Look for a 'home' variable and set argv0_path to it, if found */
|
||||
if (find_env_config_value(env_file, L"home", tmpbuffer)) {
|
||||
wcscpy(argv0_path, tmpbuffer);
|
||||
}
|
||||
fclose(env_file);
|
||||
env_file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (pythonhome == NULL || *pythonhome == '\0') {
|
||||
if (search_for_prefix(argv0_path, LANDMARK))
|
||||
pythonhome = prefix;
|
||||
|
|
|
@ -1528,6 +1528,10 @@ _PySys_Init(void)
|
|||
PyUnicode_FromWideChar(Py_GetPrefix(), -1));
|
||||
SET_SYS_FROM_STRING("exec_prefix",
|
||||
PyUnicode_FromWideChar(Py_GetExecPrefix(), -1));
|
||||
SET_SYS_FROM_STRING("base_prefix",
|
||||
PyUnicode_FromWideChar(Py_GetPrefix(), -1));
|
||||
SET_SYS_FROM_STRING("base_exec_prefix",
|
||||
PyUnicode_FromWideChar(Py_GetExecPrefix(), -1));
|
||||
SET_SYS_FROM_STRING("maxsize",
|
||||
PyLong_FromSsize_t(PY_SSIZE_T_MAX));
|
||||
SET_SYS_FROM_STRING("float_info",
|
||||
|
|
|
@ -1122,6 +1122,7 @@ def add_files(db):
|
|||
lib.add_file("2to3.py", src="2to3")
|
||||
lib.add_file("pydoc3.py", src="pydoc3")
|
||||
lib.add_file("pysetup3.py", src="pysetup3")
|
||||
lib.add_file("pyvenv.py", src="pyvenv")
|
||||
if have_tcl:
|
||||
lib.start_component("pydocgui.pyw", tcltk, keyfile="pydocgui.pyw")
|
||||
lib.add_file("pydocgui.pyw")
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/env python3
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
rc = 1
|
||||
try:
|
||||
import venv
|
||||
venv.main()
|
||||
rc = 0
|
||||
except Exception as e:
|
||||
print('Error: %s' % e, file=sys.stderr)
|
||||
sys.exit(rc)
|
7
setup.py
7
setup.py
|
@ -431,7 +431,7 @@ class PyBuildExt(build_ext):
|
|||
for directory in reversed(options.dirs):
|
||||
add_dir_to_list(dir_list, directory)
|
||||
|
||||
if os.path.normpath(sys.prefix) != '/usr' \
|
||||
if os.path.normpath(sys.base_prefix) != '/usr' \
|
||||
and not sysconfig.get_config_var('PYTHONFRAMEWORK'):
|
||||
# OSX note: Don't add LIBDIR and INCLUDEDIR to building a framework
|
||||
# (PYTHONFRAMEWORK is set) to avoid # linking problems when
|
||||
|
@ -1978,7 +1978,7 @@ class PyBuildScripts(build_scripts):
|
|||
newoutfiles = []
|
||||
newupdated_files = []
|
||||
for filename in outfiles:
|
||||
if filename.endswith('2to3'):
|
||||
if filename.endswith(('2to3', 'pyvenv')):
|
||||
newfilename = filename + fullversion
|
||||
else:
|
||||
newfilename = filename + minoronly
|
||||
|
@ -2046,7 +2046,8 @@ def main():
|
|||
# check the PyBuildScripts command above, and change the links
|
||||
# created by the bininstall target in Makefile.pre.in
|
||||
scripts = ["Tools/scripts/pydoc3", "Tools/scripts/idle3",
|
||||
"Tools/scripts/2to3", "Tools/scripts/pysetup3"]
|
||||
"Tools/scripts/2to3", "Tools/scripts/pysetup3",
|
||||
"Tools/scripts/pyvenv"]
|
||||
)
|
||||
|
||||
# --install-platlib
|
||||
|
|
Loading…
Reference in New Issue