Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Doc/library/venv.rst
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ creation according to their needs, the :class:`EnvBuilder` class.

.. class:: EnvBuilder(system_site_packages=False, clear=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
instantiation:
Expand All @@ -123,12 +123,17 @@ creation according to their needs, the :class:`EnvBuilder` class.
(defaults to ``None`` which means directory name of the environment would
be used).

* ``upgrade_deps`` -- Update the base venv modules to the latest on PyPI

.. versionchanged:: 3.4
Added the ``with_pip`` parameter

.. versionadded:: 3.6
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
provided ``EnvBuilder`` class as a base class.

Expand Down
7 changes: 6 additions & 1 deletion Doc/using/venv-create.inc
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ your :ref:`Python installation <using-on-windows>`::
The command, if run with ``-h``, will show the available options::

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 ...]

Creates virtual Python environments in one or more target directories.
Expand All @@ -60,10 +60,15 @@ The command, if run with ``-h``, will show the available options::
environment (pip is bootstrapped by default)
--prompt PROMPT Provides an alternative prompt prefix for this
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
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
Installs pip by default, added the ``--without-pip`` and ``--copies``
options
Expand Down
24 changes: 23 additions & 1 deletion Lib/test/test_venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
from test.support import (captured_stdout, captured_stderr, requires_zlib,
can_symlink, EnvironmentVarGuard, rmtree,
import_module)
import threading
import unittest
import venv
from unittest.mock import patch

try:
import ctypes
Expand Down Expand Up @@ -131,6 +131,28 @@ def test_prompt(self):
self.assertEqual(context.prompt, '(My prompt) ')
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
def test_prefixes(self):
"""
Expand Down
34 changes: 29 additions & 5 deletions Lib/venv/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import sysconfig
import types


CORE_VENV_DEPS = ('pip', 'setuptools')
logger = logging.getLogger(__name__)


Expand All @@ -38,16 +40,19 @@ class EnvBuilder:
:param with_pip: If True, ensure pip is installed in the virtual
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,
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.clear = clear
self.symlinks = symlinks
self.upgrade = upgrade
self.with_pip = with_pip
self.prompt = prompt
self.upgrade_deps = upgrade_deps

def create(self, env_dir):
"""
Expand All @@ -74,6 +79,8 @@ def create(self, env_dir):
# restore it and rewrite the configuration
self.system_site_packages = True
self.create_configuration(context)
if self.upgrade_deps:
self.upgrade_dependencies(context)

def clear_directory(self, path):
for fn in os.listdir(path):
Expand Down Expand Up @@ -105,7 +112,6 @@ def create_if_needed(d):
prompt = self.prompt if self.prompt is not None else context.env_name
context.prompt = '(%s) ' % prompt
create_if_needed(env_dir)
env = os.environ
executable = getattr(sys, '_base_executable', sys.executable)
dirname, exename = os.path.split(os.path.abspath(executable))
context.executable = executable
Expand Down Expand Up @@ -363,13 +369,25 @@ def install_scripts(self, context, path):
f.write(data)
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,
symlinks=False, with_pip=False, prompt=None):
symlinks=False, with_pip=False, prompt=None, upgrade_deps=False):
"""Create a virtual environment in a directory."""
builder = EnvBuilder(system_site_packages=system_site_packages,
clear=clear, symlinks=symlinks, with_pip=with_pip,
prompt=prompt)
prompt=prompt, upgrade_deps=upgrade_deps)
builder.create(env_dir)

def main(args=None):
Expand Down Expand Up @@ -432,6 +450,11 @@ def main(args=None):
parser.add_argument('--prompt',
help='Provides an alternative prompt prefix for '
'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)
if options.upgrade and options.clear:
raise ValueError('you cannot supply --upgrade and --clear together.')
Expand All @@ -440,7 +463,8 @@ def main(args=None):
symlinks=options.symlinks,
upgrade=options.upgrade,
with_pip=options.with_pip,
prompt=options.prompt)
prompt=options.prompt,
upgrade_deps=options.upgrade_deps)
for d in options.dirs:
builder.create(d)

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