Skip to content

Commit 4acdbf1

Browse files
cooperleesambv
authored andcommitted
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
1 parent ca7b504 commit 4acdbf1

File tree

5 files changed

+65
-8
lines changed

5 files changed

+65
-8
lines changed

Doc/library/venv.rst

+6-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ creation according to their needs, the :class:`EnvBuilder` class.
9797

9898
.. class:: EnvBuilder(system_site_packages=False, clear=False, \
9999
symlinks=False, upgrade=False, with_pip=False, \
100-
prompt=None)
100+
prompt=None, upgrade_deps=False)
101101

102102
The :class:`EnvBuilder` class accepts the following keyword arguments on
103103
instantiation:
@@ -123,12 +123,17 @@ creation according to their needs, the :class:`EnvBuilder` class.
123123
(defaults to ``None`` which means directory name of the environment would
124124
be used).
125125

126+
* ``upgrade_deps`` -- Update the base venv modules to the latest on PyPI
127+
126128
.. versionchanged:: 3.4
127129
Added the ``with_pip`` parameter
128130

129131
.. versionadded:: 3.6
130132
Added the ``prompt`` parameter
131133

134+
.. versionadded:: 3.8
135+
Added the ``upgrade_deps`` parameter
136+
132137
Creators of third-party virtual environment tools will be free to use the
133138
provided ``EnvBuilder`` class as a base class.
134139

Doc/using/venv-create.inc

+6-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ your :ref:`Python installation <using-on-windows>`::
3535
The command, if run with ``-h``, will show the available options::
3636
3737
usage: venv [-h] [--system-site-packages] [--symlinks | --copies] [--clear]
38-
[--upgrade] [--without-pip] [--prompt PROMPT]
38+
[--upgrade] [--without-pip] [--prompt PROMPT] [--upgrade-deps]
3939
ENV_DIR [ENV_DIR ...]
4040
4141
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::
6060
environment (pip is bootstrapped by default)
6161
--prompt PROMPT Provides an alternative prompt prefix for this
6262
environment.
63+
--upgrade-deps Upgrade core dependencies: pip setuptools to the
64+
latest version in PyPI
6365
6466
Once an environment has been created, you may wish to activate it, e.g. by
6567
sourcing an activate script in its bin directory.
6668
69+
.. versionchanged:: 3.8
70+
Add ``--upgrade-deps`` option to upgrade pip + setuptools to the latest on PyPI
71+
6772
.. versionchanged:: 3.4
6873
Installs pip by default, added the ``--without-pip`` and ``--copies``
6974
options

Lib/test/test_venv.py

+23-1
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
from test.support import (captured_stdout, captured_stderr, requires_zlib,
1717
can_symlink, EnvironmentVarGuard, rmtree,
1818
import_module)
19-
import threading
2019
import unittest
2120
import venv
21+
from unittest.mock import patch
2222

2323
try:
2424
import ctypes
@@ -131,6 +131,28 @@ def test_prompt(self):
131131
self.assertEqual(context.prompt, '(My prompt) ')
132132
self.assertIn("prompt = 'My prompt'\n", data)
133133

134+
def test_upgrade_dependencies(self):
135+
builder = venv.EnvBuilder()
136+
bin_path = 'Scripts' if sys.platform == 'win32' else 'bin'
137+
pip_exe = 'pip.exe' if sys.platform == 'win32' else 'pip'
138+
with tempfile.TemporaryDirectory() as fake_env_dir:
139+
140+
def pip_cmd_checker(cmd):
141+
self.assertEqual(
142+
cmd,
143+
[
144+
os.path.join(fake_env_dir, bin_path, pip_exe),
145+
'install',
146+
'-U',
147+
'pip',
148+
'setuptools'
149+
]
150+
)
151+
152+
fake_context = builder.ensure_directories(fake_env_dir)
153+
with patch('venv.subprocess.check_call', pip_cmd_checker):
154+
builder.upgrade_dependencies(fake_context)
155+
134156
@requireVenvCreate
135157
def test_prefixes(self):
136158
"""

Lib/venv/__init__.py

+29-5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import sysconfig
1313
import types
1414

15+
16+
CORE_VENV_DEPS = ('pip', 'setuptools')
1517
logger = logging.getLogger(__name__)
1618

1719

@@ -38,16 +40,19 @@ class EnvBuilder:
3840
:param with_pip: If True, ensure pip is installed in the virtual
3941
environment
4042
:param prompt: Alternative terminal prefix for the environment.
43+
:param upgrade_deps: Update the base venv modules to the latest on PyPI
4144
"""
4245

4346
def __init__(self, system_site_packages=False, clear=False,
44-
symlinks=False, upgrade=False, with_pip=False, prompt=None):
47+
symlinks=False, upgrade=False, with_pip=False, prompt=None,
48+
upgrade_deps=False):
4549
self.system_site_packages = system_site_packages
4650
self.clear = clear
4751
self.symlinks = symlinks
4852
self.upgrade = upgrade
4953
self.with_pip = with_pip
5054
self.prompt = prompt
55+
self.upgrade_deps = upgrade_deps
5156

5257
def create(self, env_dir):
5358
"""
@@ -74,6 +79,8 @@ def create(self, env_dir):
7479
# restore it and rewrite the configuration
7580
self.system_site_packages = True
7681
self.create_configuration(context)
82+
if self.upgrade_deps:
83+
self.upgrade_dependencies(context)
7784

7885
def clear_directory(self, path):
7986
for fn in os.listdir(path):
@@ -105,7 +112,6 @@ def create_if_needed(d):
105112
prompt = self.prompt if self.prompt is not None else context.env_name
106113
context.prompt = '(%s) ' % prompt
107114
create_if_needed(env_dir)
108-
env = os.environ
109115
executable = getattr(sys, '_base_executable', sys.executable)
110116
dirname, exename = os.path.split(os.path.abspath(executable))
111117
context.executable = executable
@@ -363,13 +369,25 @@ def install_scripts(self, context, path):
363369
f.write(data)
364370
shutil.copymode(srcfile, dstfile)
365371

372+
def upgrade_dependencies(self, context):
373+
logger.debug(
374+
f'Upgrading {CORE_VENV_DEPS} packages in {context.bin_path}'
375+
)
376+
if sys.platform == 'win32':
377+
pip_exe = os.path.join(context.bin_path, 'pip.exe')
378+
else:
379+
pip_exe = os.path.join(context.bin_path, 'pip')
380+
cmd = [pip_exe, 'install', '-U']
381+
cmd.extend(CORE_VENV_DEPS)
382+
subprocess.check_call(cmd)
383+
366384

367385
def create(env_dir, system_site_packages=False, clear=False,
368-
symlinks=False, with_pip=False, prompt=None):
386+
symlinks=False, with_pip=False, prompt=None, upgrade_deps=False):
369387
"""Create a virtual environment in a directory."""
370388
builder = EnvBuilder(system_site_packages=system_site_packages,
371389
clear=clear, symlinks=symlinks, with_pip=with_pip,
372-
prompt=prompt)
390+
prompt=prompt, upgrade_deps=upgrade_deps)
373391
builder.create(env_dir)
374392

375393
def main(args=None):
@@ -432,6 +450,11 @@ def main(args=None):
432450
parser.add_argument('--prompt',
433451
help='Provides an alternative prompt prefix for '
434452
'this environment.')
453+
parser.add_argument('--upgrade-deps', default=False, action='store_true',
454+
dest='upgrade_deps',
455+
help='Upgrade core dependencies: {} to the latest '
456+
'version in PyPI'.format(
457+
' '.join(CORE_VENV_DEPS)))
435458
options = parser.parse_args(args)
436459
if options.upgrade and options.clear:
437460
raise ValueError('you cannot supply --upgrade and --clear together.')
@@ -440,7 +463,8 @@ def main(args=None):
440463
symlinks=options.symlinks,
441464
upgrade=options.upgrade,
442465
with_pip=options.with_pip,
443-
prompt=options.prompt)
466+
prompt=options.prompt,
467+
upgrade_deps=options.upgrade_deps)
444468
for d in options.dirs:
445469
builder.create(d)
446470

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add ``--upgrade-deps`` to venv module. Patch by Cooper Ry Lees

0 commit comments

Comments
 (0)