Commit 4acdbf11 authored by Cooper Lees's avatar Cooper Lees Committed by Łukasz Langa

bpo-34556: Add --upgrade-deps to venv module (#13100)

Add --upgrade-deps to venv module
- This allows for pip + setuptools to be automatically upgraded to the latest version on PyPI
- Update documentation to represent this change

bpo-34556: Add --upgrade to venv module
parent ca7b504a
...@@ -97,7 +97,7 @@ creation according to their needs, the :class:`EnvBuilder` class. ...@@ -97,7 +97,7 @@ creation according to their needs, the :class:`EnvBuilder` class.
.. class:: EnvBuilder(system_site_packages=False, clear=False, \ .. class:: EnvBuilder(system_site_packages=False, clear=False, \
symlinks=False, upgrade=False, with_pip=False, \ symlinks=False, upgrade=False, with_pip=False, \
prompt=None) prompt=None, upgrade_deps=False)
The :class:`EnvBuilder` class accepts the following keyword arguments on The :class:`EnvBuilder` class accepts the following keyword arguments on
instantiation: instantiation:
...@@ -123,12 +123,17 @@ creation according to their needs, the :class:`EnvBuilder` class. ...@@ -123,12 +123,17 @@ creation according to their needs, the :class:`EnvBuilder` class.
(defaults to ``None`` which means directory name of the environment would (defaults to ``None`` which means directory name of the environment would
be used). be used).
* ``upgrade_deps`` -- Update the base venv modules to the latest on PyPI
.. versionchanged:: 3.4 .. versionchanged:: 3.4
Added the ``with_pip`` parameter Added the ``with_pip`` parameter
.. versionadded:: 3.6 .. versionadded:: 3.6
Added the ``prompt`` parameter Added the ``prompt`` parameter
.. versionadded:: 3.8
Added the ``upgrade_deps`` parameter
Creators of third-party virtual environment tools will be free to use the Creators of third-party virtual environment tools will be free to use the
provided ``EnvBuilder`` class as a base class. provided ``EnvBuilder`` class as a base class.
......
...@@ -35,7 +35,7 @@ your :ref:`Python installation <using-on-windows>`:: ...@@ -35,7 +35,7 @@ your :ref:`Python installation <using-on-windows>`::
The command, if run with ``-h``, will show the available options:: The command, if run with ``-h``, will show the available options::
usage: venv [-h] [--system-site-packages] [--symlinks | --copies] [--clear] usage: venv [-h] [--system-site-packages] [--symlinks | --copies] [--clear]
[--upgrade] [--without-pip] [--prompt PROMPT] [--upgrade] [--without-pip] [--prompt PROMPT] [--upgrade-deps]
ENV_DIR [ENV_DIR ...] ENV_DIR [ENV_DIR ...]
Creates virtual Python environments in one or more target directories. Creates virtual Python environments in one or more target directories.
...@@ -60,10 +60,15 @@ The command, if run with ``-h``, will show the available options:: ...@@ -60,10 +60,15 @@ The command, if run with ``-h``, will show the available options::
environment (pip is bootstrapped by default) environment (pip is bootstrapped by default)
--prompt PROMPT Provides an alternative prompt prefix for this --prompt PROMPT Provides an alternative prompt prefix for this
environment. environment.
--upgrade-deps Upgrade core dependencies: pip setuptools to the
latest version in PyPI
Once an environment has been created, you may wish to activate it, e.g. by Once an environment has been created, you may wish to activate it, e.g. by
sourcing an activate script in its bin directory. sourcing an activate script in its bin directory.
.. versionchanged:: 3.8
Add ``--upgrade-deps`` option to upgrade pip + setuptools to the latest on PyPI
.. versionchanged:: 3.4 .. versionchanged:: 3.4
Installs pip by default, added the ``--without-pip`` and ``--copies`` Installs pip by default, added the ``--without-pip`` and ``--copies``
options options
......
...@@ -16,9 +16,9 @@ import tempfile ...@@ -16,9 +16,9 @@ import tempfile
from test.support import (captured_stdout, captured_stderr, requires_zlib, from test.support import (captured_stdout, captured_stderr, requires_zlib,
can_symlink, EnvironmentVarGuard, rmtree, can_symlink, EnvironmentVarGuard, rmtree,
import_module) import_module)
import threading
import unittest import unittest
import venv import venv
from unittest.mock import patch
try: try:
import ctypes import ctypes
...@@ -131,6 +131,28 @@ class BasicTest(BaseTest): ...@@ -131,6 +131,28 @@ class BasicTest(BaseTest):
self.assertEqual(context.prompt, '(My prompt) ') self.assertEqual(context.prompt, '(My prompt) ')
self.assertIn("prompt = 'My prompt'\n", data) self.assertIn("prompt = 'My prompt'\n", data)
def test_upgrade_dependencies(self):
builder = venv.EnvBuilder()
bin_path = 'Scripts' if sys.platform == 'win32' else 'bin'
pip_exe = 'pip.exe' if sys.platform == 'win32' else 'pip'
with tempfile.TemporaryDirectory() as fake_env_dir:
def pip_cmd_checker(cmd):
self.assertEqual(
cmd,
[
os.path.join(fake_env_dir, bin_path, pip_exe),
'install',
'-U',
'pip',
'setuptools'
]
)
fake_context = builder.ensure_directories(fake_env_dir)
with patch('venv.subprocess.check_call', pip_cmd_checker):
builder.upgrade_dependencies(fake_context)
@requireVenvCreate @requireVenvCreate
def test_prefixes(self): def test_prefixes(self):
""" """
......
...@@ -12,6 +12,8 @@ import sys ...@@ -12,6 +12,8 @@ import sys
import sysconfig import sysconfig
import types import types
CORE_VENV_DEPS = ('pip', 'setuptools')
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -38,16 +40,19 @@ class EnvBuilder: ...@@ -38,16 +40,19 @@ class EnvBuilder:
:param with_pip: If True, ensure pip is installed in the virtual :param with_pip: If True, ensure pip is installed in the virtual
environment environment
:param prompt: Alternative terminal prefix for the environment. :param prompt: Alternative terminal prefix for the environment.
:param upgrade_deps: Update the base venv modules to the latest on PyPI
""" """
def __init__(self, system_site_packages=False, clear=False, def __init__(self, system_site_packages=False, clear=False,
symlinks=False, upgrade=False, with_pip=False, prompt=None): symlinks=False, upgrade=False, with_pip=False, prompt=None,
upgrade_deps=False):
self.system_site_packages = system_site_packages self.system_site_packages = system_site_packages
self.clear = clear self.clear = clear
self.symlinks = symlinks self.symlinks = symlinks
self.upgrade = upgrade self.upgrade = upgrade
self.with_pip = with_pip self.with_pip = with_pip
self.prompt = prompt self.prompt = prompt
self.upgrade_deps = upgrade_deps
def create(self, env_dir): def create(self, env_dir):
""" """
...@@ -74,6 +79,8 @@ class EnvBuilder: ...@@ -74,6 +79,8 @@ class EnvBuilder:
# restore it and rewrite the configuration # restore it and rewrite the configuration
self.system_site_packages = True self.system_site_packages = True
self.create_configuration(context) self.create_configuration(context)
if self.upgrade_deps:
self.upgrade_dependencies(context)
def clear_directory(self, path): def clear_directory(self, path):
for fn in os.listdir(path): for fn in os.listdir(path):
...@@ -105,7 +112,6 @@ class EnvBuilder: ...@@ -105,7 +112,6 @@ class EnvBuilder:
prompt = self.prompt if self.prompt is not None else context.env_name prompt = self.prompt if self.prompt is not None else context.env_name
context.prompt = '(%s) ' % prompt context.prompt = '(%s) ' % prompt
create_if_needed(env_dir) create_if_needed(env_dir)
env = os.environ
executable = getattr(sys, '_base_executable', sys.executable) executable = getattr(sys, '_base_executable', sys.executable)
dirname, exename = os.path.split(os.path.abspath(executable)) dirname, exename = os.path.split(os.path.abspath(executable))
context.executable = executable context.executable = executable
...@@ -363,13 +369,25 @@ class EnvBuilder: ...@@ -363,13 +369,25 @@ class EnvBuilder:
f.write(data) f.write(data)
shutil.copymode(srcfile, dstfile) shutil.copymode(srcfile, dstfile)
def upgrade_dependencies(self, context):
logger.debug(
f'Upgrading {CORE_VENV_DEPS} packages in {context.bin_path}'
)
if sys.platform == 'win32':
pip_exe = os.path.join(context.bin_path, 'pip.exe')
else:
pip_exe = os.path.join(context.bin_path, 'pip')
cmd = [pip_exe, 'install', '-U']
cmd.extend(CORE_VENV_DEPS)
subprocess.check_call(cmd)
def create(env_dir, system_site_packages=False, clear=False, def create(env_dir, system_site_packages=False, clear=False,
symlinks=False, with_pip=False, prompt=None): symlinks=False, with_pip=False, prompt=None, upgrade_deps=False):
"""Create a virtual environment in a directory.""" """Create a virtual environment in a directory."""
builder = EnvBuilder(system_site_packages=system_site_packages, builder = EnvBuilder(system_site_packages=system_site_packages,
clear=clear, symlinks=symlinks, with_pip=with_pip, clear=clear, symlinks=symlinks, with_pip=with_pip,
prompt=prompt) prompt=prompt, upgrade_deps=upgrade_deps)
builder.create(env_dir) builder.create(env_dir)
def main(args=None): def main(args=None):
...@@ -432,6 +450,11 @@ def main(args=None): ...@@ -432,6 +450,11 @@ def main(args=None):
parser.add_argument('--prompt', parser.add_argument('--prompt',
help='Provides an alternative prompt prefix for ' help='Provides an alternative prompt prefix for '
'this environment.') 'this environment.')
parser.add_argument('--upgrade-deps', default=False, action='store_true',
dest='upgrade_deps',
help='Upgrade core dependencies: {} to the latest '
'version in PyPI'.format(
' '.join(CORE_VENV_DEPS)))
options = parser.parse_args(args) options = parser.parse_args(args)
if options.upgrade and options.clear: if options.upgrade and options.clear:
raise ValueError('you cannot supply --upgrade and --clear together.') raise ValueError('you cannot supply --upgrade and --clear together.')
...@@ -440,7 +463,8 @@ def main(args=None): ...@@ -440,7 +463,8 @@ def main(args=None):
symlinks=options.symlinks, symlinks=options.symlinks,
upgrade=options.upgrade, upgrade=options.upgrade,
with_pip=options.with_pip, with_pip=options.with_pip,
prompt=options.prompt) prompt=options.prompt,
upgrade_deps=options.upgrade_deps)
for d in options.dirs: for d in options.dirs:
builder.create(d) builder.create(d)
......
Add ``--upgrade-deps`` to venv module. Patch by Cooper Ry Lees
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment