Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8fbdb09

Browse files
committed
Close #19552: venv and pyvenv ensurepip integration
1 parent 0b61ef6 commit 8fbdb09

5 files changed

Lines changed: 105 additions & 13 deletions

File tree

Doc/library/venv.rst

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ The high-level method described above makes use of a simple API which provides
8585
mechanisms for third-party virtual environment creators to customize environment
8686
creation according to their needs, the :class:`EnvBuilder` class.
8787

88-
.. class:: EnvBuilder(system_site_packages=False, clear=False, symlinks=False, upgrade=False)
88+
.. class:: EnvBuilder(system_site_packages=False, clear=False, \
89+
symlinks=False, upgrade=False, with_pip=False)
8990

9091
The :class:`EnvBuilder` class accepts the following keyword arguments on
9192
instantiation:
@@ -105,6 +106,12 @@ creation according to their needs, the :class:`EnvBuilder` class.
105106
environment with the running Python - for use when that Python has been
106107
upgraded in-place (defaults to ``False``).
107108

109+
* ``with_pip`` -- a Boolean value which, if True, ensures pip is
110+
installed in the virtual environment
111+
112+
.. versionchanged:: 3.4
113+
Added the ``with_pip`` parameter
114+
108115

109116
Creators of third-party virtual environment tools will be free to use the
110117
provided ``EnvBuilder`` class as a base class.
@@ -201,11 +208,15 @@ creation according to their needs, the :class:`EnvBuilder` class.
201208

202209
There is also a module-level convenience function:
203210

204-
.. function:: create(env_dir, system_site_packages=False, clear=False, symlinks=False)
211+
.. function:: create(env_dir, system_site_packages=False, clear=False, \
212+
symlinks=False, with_pip=False)
205213

206214
Create an :class:`EnvBuilder` with the given keyword arguments, and call its
207215
:meth:`~EnvBuilder.create` method with the *env_dir* argument.
208216

217+
.. versionchanged:: 3.4
218+
Added the ``with_pip`` parameter
219+
209220
An example of extending ``EnvBuilder``
210221
--------------------------------------
211222

Doc/using/venv-create.inc

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ or equivalently::
2525
The command, if run with ``-h``, will show the available options::
2626

2727
usage: pyvenv [-h] [--system-site-packages] [--symlinks] [--clear]
28-
[--upgrade] ENV_DIR [ENV_DIR ...]
28+
[--upgrade] [--without-pip] ENV_DIR [ENV_DIR ...]
2929

3030
Creates virtual Python environments in one or more target directories.
3131

@@ -43,6 +43,11 @@ The command, if run with ``-h``, will show the available options::
4343
raised.
4444
--upgrade Upgrade the environment directory to use this version
4545
of Python, assuming Python has been upgraded in-place.
46+
--without-pip Skips installing or upgrading pip in the virtual
47+
environment (pip is bootstrapped by default)
48+
49+
.. versionchanged:: 3.4
50+
Installs pip by default, added the ``--without-pip`` option
4651

4752
If the target directory already exists an error will be raised, unless
4853
the ``--clear`` or ``--upgrade`` option was provided.
@@ -51,6 +56,9 @@ The created ``pyvenv.cfg`` file also includes the
5156
``include-system-site-packages`` key, set to ``true`` if ``venv`` is
5257
run with the ``--system-site-packages`` option, ``false`` otherwise.
5358

59+
Unless the ``--without-pip`` option is given, :mod:`ensurepip` will be
60+
invoked to bootstrap ``pip`` into the virtual environment.
61+
5462
Multiple paths can be given to ``pyvenv``, in which case an identical
5563
virtualenv will be created, according to the given options, at each
5664
provided path.

Lib/test/test_venv.py

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
import unittest
1717
import venv
1818

19+
skipInVenv = unittest.skipIf(sys.prefix != sys.base_prefix,
20+
'Test not appropriate in a venv')
21+
22+
1923
class BaseTest(unittest.TestCase):
2024
"""Base class for venv tests."""
2125

@@ -83,8 +87,7 @@ def test_defaults(self):
8387
print(' %r' % os.listdir(bd))
8488
self.assertTrue(os.path.exists(fn), 'File %r should exist.' % fn)
8589

86-
@unittest.skipIf(sys.prefix != sys.base_prefix, 'Test not appropriate '
87-
'in a venv')
90+
@skipInVenv
8891
def test_prefixes(self):
8992
"""
9093
Test that the prefix values are as expected.
@@ -217,8 +220,7 @@ def test_symlinking(self):
217220
# run the test, the pyvenv.cfg in the venv created in the test will
218221
# point to the venv being used to run the test, and we lose the link
219222
# to the source build - so Python can't initialise properly.
220-
@unittest.skipIf(sys.prefix != sys.base_prefix, 'Test not appropriate '
221-
'in a venv')
223+
@skipInVenv
222224
def test_executable(self):
223225
"""
224226
Test that the sys.executable value is as expected.
@@ -247,8 +249,50 @@ def test_executable_symlinks(self):
247249
out, err = p.communicate()
248250
self.assertEqual(out.strip(), envpy.encode())
249251

252+
253+
@skipInVenv
254+
class EnsurePipTest(BaseTest):
255+
"""Test venv module installation of pip."""
256+
257+
def test_no_pip_by_default(self):
258+
shutil.rmtree(self.env_dir)
259+
self.run_with_capture(venv.create, self.env_dir)
260+
envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
261+
try_import = 'try:\n import pip\nexcept ImportError:\n print("OK")'
262+
cmd = [envpy, '-c', try_import]
263+
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
264+
stderr=subprocess.PIPE)
265+
out, err = p.communicate()
266+
self.assertEqual(err, b"")
267+
self.assertEqual(out.strip(), b"OK")
268+
269+
def test_explicit_no_pip(self):
270+
shutil.rmtree(self.env_dir)
271+
self.run_with_capture(venv.create, self.env_dir, with_pip=False)
272+
envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
273+
try_import = 'try:\n import pip\nexcept ImportError:\n print("OK")'
274+
cmd = [envpy, '-c', try_import]
275+
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
276+
stderr=subprocess.PIPE)
277+
out, err = p.communicate()
278+
self.assertEqual(err, b"")
279+
self.assertEqual(out.strip(), b"OK")
280+
281+
def test_with_pip(self):
282+
shutil.rmtree(self.env_dir)
283+
self.run_with_capture(venv.create, self.env_dir, with_pip=True)
284+
envpy = os.path.join(os.path.realpath(self.env_dir), self.bindir, self.exe)
285+
cmd = [envpy, '-m', 'pip', '--version']
286+
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
287+
stderr=subprocess.PIPE)
288+
out, err = p.communicate()
289+
self.assertEqual(err, b"")
290+
self.assertTrue(out.startswith(b"pip"))
291+
self.assertIn(self.env_dir.encode(), out)
292+
293+
250294
def test_main():
251-
run_unittest(BasicTest)
295+
run_unittest(BasicTest, EnsurePipTest)
252296

253297
if __name__ == "__main__":
254298
test_main()

Lib/venv/__init__.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,13 @@
2424
raised.
2525
--upgrade Upgrade the environment directory to use this version
2626
of Python, assuming Python has been upgraded in-place.
27+
--without-pip Skips installing or upgrading pip in the virtual
28+
environment (pip is bootstrapped by default)
2729
"""
2830
import logging
2931
import os
3032
import shutil
33+
import subprocess
3134
import sys
3235
import sysconfig
3336
import types
@@ -56,14 +59,17 @@ class EnvBuilder:
5659
:param symlinks: If True, attempt to symlink rather than copy files into
5760
virtual environment.
5861
:param upgrade: If True, upgrade an existing virtual environment.
62+
:param with_pip: If True, ensure pip is installed in the virtual
63+
environment
5964
"""
6065

6166
def __init__(self, system_site_packages=False, clear=False,
62-
symlinks=False, upgrade=False):
67+
symlinks=False, upgrade=False, with_pip=False):
6368
self.system_site_packages = system_site_packages
6469
self.clear = clear
6570
self.symlinks = symlinks
6671
self.upgrade = upgrade
72+
self.with_pip = with_pip
6773

6874
def create(self, env_dir):
6975
"""
@@ -76,6 +82,8 @@ def create(self, env_dir):
7682
context = self.ensure_directories(env_dir)
7783
self.create_configuration(context)
7884
self.setup_python(context)
85+
if self.with_pip:
86+
self._setup_pip(context)
7987
if not self.upgrade:
8088
self.setup_scripts(context)
8189
self.post_setup(context)
@@ -224,6 +232,12 @@ def setup_python(self, context):
224232
shutil.copyfile(src, dst)
225233
break
226234

235+
def _setup_pip(self, context):
236+
"""Installs or upgrades pip in a virtual environment"""
237+
cmd = [context.env_exe, '-m', 'ensurepip', '--upgrade',
238+
'--default-pip']
239+
subprocess.check_output(cmd)
240+
227241
def setup_scripts(self, context):
228242
"""
229243
Set up scripts into the created environment from a directory.
@@ -317,7 +331,8 @@ def install_scripts(self, context, path):
317331
shutil.copymode(srcfile, dstfile)
318332

319333

320-
def create(env_dir, system_site_packages=False, clear=False, symlinks=False):
334+
def create(env_dir, system_site_packages=False, clear=False,
335+
symlinks=False, with_pip=False):
321336
"""
322337
Create a virtual environment in a directory.
323338
@@ -333,9 +348,11 @@ def create(env_dir, system_site_packages=False, clear=False, symlinks=False):
333348
raised.
334349
:param symlinks: If True, attempt to symlink rather than copy files into
335350
virtual environment.
351+
:param with_pip: If True, ensure pip is installed in the virtual
352+
environment
336353
"""
337354
builder = EnvBuilder(system_site_packages=system_site_packages,
338-
clear=clear, symlinks=symlinks)
355+
clear=clear, symlinks=symlinks, with_pip=with_pip)
339356
builder.create(env_dir)
340357

341358
def main(args=None):
@@ -390,12 +407,19 @@ def main(args=None):
390407
'directory to use this version '
391408
'of Python, assuming Python '
392409
'has been upgraded in-place.')
410+
parser.add_argument('--without-pip', dest='with_pip',
411+
default=True, action='store_false',
412+
help='Skips installing or upgrading pip in the '
413+
'virtual environment (pip is bootstrapped '
414+
'by default)')
393415
options = parser.parse_args(args)
394416
if options.upgrade and options.clear:
395417
raise ValueError('you cannot supply --upgrade and --clear together.')
396418
builder = EnvBuilder(system_site_packages=options.system_site,
397-
clear=options.clear, symlinks=options.symlinks,
398-
upgrade=options.upgrade)
419+
clear=options.clear,
420+
symlinks=options.symlinks,
421+
upgrade=options.upgrade,
422+
with_pip=options.with_pip)
399423
for d in options.dirs:
400424
builder.create(d)
401425

Misc/NEWS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ Core and Builtins
6565
Library
6666
-------
6767

68+
- Issue #19552: venv now supports bootstrapping pip into virtual environments
69+
6870
- Issue #17134: Finalize interface to Windows' certificate store. Cert and
6971
CRL enumeration are now two functions. enum_certificates() also returns
7072
purpose flags as set of OIDs.
@@ -378,6 +380,9 @@ Build
378380
Tools/Demos
379381
-----------
380382

383+
- Issue #19552: pyvenv now bootstraps pip into virtual environments by
384+
default (pass --without-pip to request the old behaviour)
385+
381386
- Issue #19390: Argument Clinic no longer accepts malformed Python
382387
and C ids.
383388

0 commit comments

Comments
 (0)