diff options
-rw-r--r-- | Doc/library/venv.rst | 15 | ||||
-rw-r--r-- | Doc/using/venv-create.inc | 10 | ||||
-rw-r--r-- | Lib/test/test_venv.py | 54 | ||||
-rw-r--r-- | Lib/venv/__init__.py | 34 | ||||
-rw-r--r-- | Misc/NEWS | 5 |
5 files changed, 105 insertions, 13 deletions
diff --git a/Doc/library/venv.rst b/Doc/library/venv.rst index 042ed8e..efc2137 100644 --- a/Doc/library/venv.rst +++ b/Doc/library/venv.rst @@ -85,7 +85,8 @@ 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. -.. class:: EnvBuilder(system_site_packages=False, clear=False, symlinks=False, upgrade=False) +.. class:: EnvBuilder(system_site_packages=False, clear=False, \ + symlinks=False, upgrade=False, with_pip=False) The :class:`EnvBuilder` class accepts the following keyword arguments on instantiation: @@ -105,6 +106,12 @@ creation according to their needs, the :class:`EnvBuilder` class. environment with the running Python - for use when that Python has been upgraded in-place (defaults to ``False``). + * ``with_pip`` -- a Boolean value which, if True, ensures pip is + installed in the virtual environment + + .. versionchanged:: 3.4 + Added the ``with_pip`` parameter + Creators of third-party virtual environment tools will be free to use the provided ``EnvBuilder`` class as a base class. @@ -201,11 +208,15 @@ creation according to their needs, the :class:`EnvBuilder` class. There is also a module-level convenience function: -.. function:: create(env_dir, system_site_packages=False, clear=False, symlinks=False) +.. function:: create(env_dir, system_site_packages=False, clear=False, \ + symlinks=False, with_pip=False) Create an :class:`EnvBuilder` with the given keyword arguments, and call its :meth:`~EnvBuilder.create` method with the *env_dir* argument. + .. versionchanged:: 3.4 + Added the ``with_pip`` parameter + An example of extending ``EnvBuilder`` -------------------------------------- diff --git a/Doc/using/venv-create.inc b/Doc/using/venv-create.inc index 706ac5d..868bbc8 100644 --- a/Doc/using/venv-create.inc +++ b/Doc/using/venv-create.inc @@ -25,7 +25,7 @@ or equivalently:: The command, if run with ``-h``, will show the available options:: usage: pyvenv [-h] [--system-site-packages] [--symlinks] [--clear] - [--upgrade] ENV_DIR [ENV_DIR ...] + [--upgrade] [--without-pip] ENV_DIR [ENV_DIR ...] Creates virtual Python environments in one or more target directories. @@ -43,6 +43,11 @@ The command, if run with ``-h``, will show the available options:: raised. --upgrade Upgrade the environment directory to use this version of Python, assuming Python has been upgraded in-place. + --without-pip Skips installing or upgrading pip in the virtual + environment (pip is bootstrapped by default) + +.. versionchanged:: 3.4 + Installs pip by default, added the ``--without-pip`` option If the target directory already exists an error will be raised, unless the ``--clear`` or ``--upgrade`` option was provided. @@ -51,6 +56,9 @@ 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. +Unless the ``--without-pip`` option is given, :mod:`ensurepip` will be +invoked to bootstrap ``pip`` into the virtual environment. + 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. diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index dbbe157..6047f87 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -16,6 +16,10 @@ from test.support import (captured_stdout, captured_stderr, run_unittest, import unittest import venv +skipInVenv = unittest.skipIf(sys.prefix != sys.base_prefix, + 'Test not appropriate in a venv') + + class BaseTest(unittest.TestCase): """Base class for venv tests.""" @@ -83,8 +87,7 @@ class BasicTest(BaseTest): print(' %r' % os.listdir(bd)) self.assertTrue(os.path.exists(fn), 'File %r should exist.' % fn) - @unittest.skipIf(sys.prefix != sys.base_prefix, 'Test not appropriate ' - 'in a venv') + @skipInVenv def test_prefixes(self): """ Test that the prefix values are as expected. @@ -217,8 +220,7 @@ class BasicTest(BaseTest): # run the test, the pyvenv.cfg in the venv created in the test will # point to the venv being used to run the test, and we lose the link # to the source build - so Python can't initialise properly. - @unittest.skipIf(sys.prefix != sys.base_prefix, 'Test not appropriate ' - 'in a venv') + @skipInVenv def test_executable(self): """ Test that the sys.executable value is as expected. @@ -247,8 +249,50 @@ class BasicTest(BaseTest): out, err = p.communicate() self.assertEqual(out.strip(), envpy.encode()) + +@skipInVenv +class EnsurePipTest(BaseTest): + """Test venv module installation of pip.""" + + def test_no_pip_by_default(self): + shutil.rmtree(self.env_dir) + self.run_with_capture(venv.create, self.env_dir) + envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe) + try_import = 'try:\n import pip\nexcept ImportError:\n print("OK")' + cmd = [envpy, '-c', try_import] + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out, err = p.communicate() + self.assertEqual(err, b"") + self.assertEqual(out.strip(), b"OK") + + def test_explicit_no_pip(self): + shutil.rmtree(self.env_dir) + self.run_with_capture(venv.create, self.env_dir, with_pip=False) + envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe) + try_import = 'try:\n import pip\nexcept ImportError:\n print("OK")' + cmd = [envpy, '-c', try_import] + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out, err = p.communicate() + self.assertEqual(err, b"") + self.assertEqual(out.strip(), b"OK") + + def test_with_pip(self): + shutil.rmtree(self.env_dir) + self.run_with_capture(venv.create, self.env_dir, with_pip=True) + envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe) + cmd = [envpy, '-m', 'pip', '--version'] + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out, err = p.communicate() + self.assertEqual(err, b"") + self.assertTrue(out.startswith(b"pip")) + self.assertIn(self.env_dir.encode(), out) + + def test_main(): - run_unittest(BasicTest) + run_unittest(BasicTest, EnsurePipTest) if __name__ == "__main__": test_main() diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py index a57db0e..2991c66 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -24,10 +24,13 @@ optional arguments: raised. --upgrade Upgrade the environment directory to use this version of Python, assuming Python has been upgraded in-place. + --without-pip Skips installing or upgrading pip in the virtual + environment (pip is bootstrapped by default) """ import logging import os import shutil +import subprocess import sys import sysconfig import types @@ -56,14 +59,17 @@ class EnvBuilder: :param symlinks: If True, attempt to symlink rather than copy files into virtual environment. :param upgrade: If True, upgrade an existing virtual environment. + :param with_pip: If True, ensure pip is installed in the virtual + environment """ def __init__(self, system_site_packages=False, clear=False, - symlinks=False, upgrade=False): + symlinks=False, upgrade=False, with_pip=False): self.system_site_packages = system_site_packages self.clear = clear self.symlinks = symlinks self.upgrade = upgrade + self.with_pip = with_pip def create(self, env_dir): """ @@ -76,6 +82,8 @@ class EnvBuilder: context = self.ensure_directories(env_dir) self.create_configuration(context) self.setup_python(context) + if self.with_pip: + self._setup_pip(context) if not self.upgrade: self.setup_scripts(context) self.post_setup(context) @@ -224,6 +232,12 @@ class EnvBuilder: shutil.copyfile(src, dst) break + def _setup_pip(self, context): + """Installs or upgrades pip in a virtual environment""" + cmd = [context.env_exe, '-m', 'ensurepip', '--upgrade', + '--default-pip'] + subprocess.check_output(cmd) + def setup_scripts(self, context): """ Set up scripts into the created environment from a directory. @@ -317,7 +331,8 @@ class EnvBuilder: shutil.copymode(srcfile, dstfile) -def create(env_dir, system_site_packages=False, clear=False, symlinks=False): +def create(env_dir, system_site_packages=False, clear=False, + symlinks=False, with_pip=False): """ Create a virtual environment in a directory. @@ -333,9 +348,11 @@ def create(env_dir, system_site_packages=False, clear=False, symlinks=False): raised. :param symlinks: If True, attempt to symlink rather than copy files into virtual environment. + :param with_pip: If True, ensure pip is installed in the virtual + environment """ builder = EnvBuilder(system_site_packages=system_site_packages, - clear=clear, symlinks=symlinks) + clear=clear, symlinks=symlinks, with_pip=with_pip) builder.create(env_dir) def main(args=None): @@ -390,12 +407,19 @@ def main(args=None): 'directory to use this version ' 'of Python, assuming Python ' 'has been upgraded in-place.') + parser.add_argument('--without-pip', dest='with_pip', + default=True, action='store_false', + help='Skips installing or upgrading pip in the ' + 'virtual environment (pip is bootstrapped ' + 'by default)') options = parser.parse_args(args) if options.upgrade and options.clear: raise ValueError('you cannot supply --upgrade and --clear together.') builder = EnvBuilder(system_site_packages=options.system_site, - clear=options.clear, symlinks=options.symlinks, - upgrade=options.upgrade) + clear=options.clear, + symlinks=options.symlinks, + upgrade=options.upgrade, + with_pip=options.with_pip) for d in options.dirs: builder.create(d) @@ -65,6 +65,8 @@ Core and Builtins Library ------- +- Issue #19552: venv now supports bootstrapping pip into virtual environments + - Issue #17134: Finalize interface to Windows' certificate store. Cert and CRL enumeration are now two functions. enum_certificates() also returns purpose flags as set of OIDs. @@ -378,6 +380,9 @@ Build Tools/Demos ----------- +- Issue #19552: pyvenv now bootstraps pip into virtual environments by + default (pass --without-pip to request the old behaviour) + - Issue #19390: Argument Clinic no longer accepts malformed Python and C ids. |