Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit b9fe227

Browse filesBrowse files
committed
Get rid of the ensurepip infra for many wheels
This is a refactoring change that aims to simplify ``ensurepip``. Before it, this module had legacy infrastructure that made an assumption that ``ensurepip`` would be provisioning more then just a single wheel. That assumption is no longer true since [[1]][[2]][[3]]. In this change, the improvement is done around removing unnecessary loops and supporting structures to change the assumptions to expect only the bundled or replacement ``pip`` wheel. [1]: python@ece20db [2]: python#101039 [3]: python#95299
1 parent e55aab9 commit b9fe227
Copy full SHA for b9fe227

File tree

Expand file treeCollapse file tree

3 files changed

+101
-67
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+101
-67
lines changed

‎Lib/ensurepip/__init__.py

Copy file name to clipboardExpand all lines: Lib/ensurepip/__init__.py
+49-51Lines changed: 49 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,13 @@
55
import sys
66
import sysconfig
77
import tempfile
8+
from contextlib import suppress
9+
from functools import cache
810
from importlib import resources
911

1012

1113
__all__ = ["version", "bootstrap"]
12-
_PACKAGE_NAMES = ('pip',)
1314
_PIP_VERSION = "23.2.1"
14-
_PROJECTS = [
15-
("pip", _PIP_VERSION, "py3"),
16-
]
1715

1816
# Packages bundled in ensurepip._bundled have wheel_name set.
1917
# Packages from WHEEL_PKG_DIR have wheel_path set.
@@ -27,8 +25,13 @@
2725
_WHEEL_PKG_DIR = sysconfig.get_config_var('WHEEL_PKG_DIR')
2826

2927

30-
def _find_packages(path):
31-
packages = {}
28+
def _find_packages(path: str | None) -> _Package:
29+
if path is None:
30+
raise LookupError(
31+
'The compile-time `WHEEL_PKG_DIR` is unset so there is '
32+
'no place for looking up the wheels.',
33+
)
34+
3235
try:
3336
filenames = os.listdir(path)
3437
except OSError:
@@ -38,41 +41,39 @@ def _find_packages(path):
3841
# of the same package, but don't attempt to implement correct version
3942
# comparison since this case should not happen.
4043
filenames = sorted(filenames)
44+
pip_pkg = None
4145
for filename in filenames:
4246
# filename is like 'pip-21.2.4-py3-none-any.whl'
4347
if not filename.endswith(".whl"):
4448
continue
45-
for name in _PACKAGE_NAMES:
46-
prefix = name + '-'
47-
if filename.startswith(prefix):
48-
break
49-
else:
49+
if not filename.startswith('pip-'):
5050
continue
5151

5252
# Extract '21.2.4' from 'pip-21.2.4-py3-none-any.whl'
53-
version = filename.removeprefix(prefix).partition('-')[0]
53+
discovered_pip_pkg_version = filename.removeprefix(
54+
'pip-',
55+
).partition('-')[0]
5456
wheel_path = os.path.join(path, filename)
55-
packages[name] = _Package(version, None, wheel_path)
56-
return packages
57+
pip_pkg = _Package(discovered_pip_pkg_version, None, wheel_path)
58+
59+
if pip_pkg is None:
60+
raise LookupError(
61+
'`WHEEL_PKG_DIR` does not contain any wheel files for `pip`.',
62+
)
63+
64+
return pip_pkg
5765

5866

59-
def _get_packages():
60-
global _PACKAGES, _WHEEL_PKG_DIR
61-
if _PACKAGES is not None:
62-
return _PACKAGES
67+
@cache
68+
def _get_usable_pip_package() -> _Package:
69+
wheel_name = f"pip-{_PIP_VERSION}-py3-none-any.whl"
70+
pip_pkg = _Package(_PIP_VERSION, wheel_name, None)
6371

64-
packages = {}
65-
for name, version, py_tag in _PROJECTS:
66-
wheel_name = f"{name}-{version}-{py_tag}-none-any.whl"
67-
packages[name] = _Package(version, wheel_name, None)
68-
if _WHEEL_PKG_DIR:
69-
dir_packages = _find_packages(_WHEEL_PKG_DIR)
70-
# only used the wheel package directory if all packages are found there
71-
if all(name in dir_packages for name in _PACKAGE_NAMES):
72-
packages = dir_packages
73-
_PACKAGES = packages
74-
return packages
75-
_PACKAGES = None
72+
with suppress(LookupError):
73+
# only use the wheel package directory if all packages are found there
74+
pip_pkg = _find_packages(_WHEEL_PKG_DIR)
75+
76+
return pip_pkg
7677

7778

7879
def _run_pip(args, additional_paths=None):
@@ -105,7 +106,7 @@ def version():
105106
"""
106107
Returns a string specifying the bundled version of pip.
107108
"""
108-
return _get_packages()['pip'].version
109+
return _get_usable_pip_package().version
109110

110111

111112
def _disable_pip_configuration_settings():
@@ -167,24 +168,21 @@ def _bootstrap(*, root=None, upgrade=False, user=False,
167168
with tempfile.TemporaryDirectory() as tmpdir:
168169
# Put our bundled wheels into a temporary directory and construct the
169170
# additional paths that need added to sys.path
170-
additional_paths = []
171-
for name, package in _get_packages().items():
172-
if package.wheel_name:
173-
# Use bundled wheel package
174-
wheel_name = package.wheel_name
175-
wheel_path = resources.files("ensurepip") / "_bundled" / wheel_name
176-
whl = wheel_path.read_bytes()
177-
else:
178-
# Use the wheel package directory
179-
with open(package.wheel_path, "rb") as fp:
180-
whl = fp.read()
181-
wheel_name = os.path.basename(package.wheel_path)
182-
183-
filename = os.path.join(tmpdir, wheel_name)
184-
with open(filename, "wb") as fp:
185-
fp.write(whl)
186-
187-
additional_paths.append(filename)
171+
package = _get_usable_pip_package()
172+
if package.wheel_name:
173+
# Use bundled wheel package
174+
wheel_name = package.wheel_name
175+
wheel_path = resources.files("ensurepip") / "_bundled" / wheel_name
176+
whl = wheel_path.read_bytes()
177+
else:
178+
# Use the wheel package directory
179+
with open(package.wheel_path, "rb") as fp:
180+
whl = fp.read()
181+
wheel_name = os.path.basename(package.wheel_path)
182+
183+
filename = os.path.join(tmpdir, wheel_name)
184+
with open(filename, "wb") as fp:
185+
fp.write(whl)
188186

189187
# Construct the arguments to be passed to the pip command
190188
args = ["install", "--no-cache-dir", "--no-index", "--find-links", tmpdir]
@@ -197,7 +195,7 @@ def _bootstrap(*, root=None, upgrade=False, user=False,
197195
if verbosity:
198196
args += ["-" + "v" * verbosity]
199197

200-
return _run_pip([*args, *_PACKAGE_NAMES], additional_paths)
198+
return _run_pip([*args, "pip"], [filename])
201199

202200
def _uninstall_helper(*, verbosity=0):
203201
"""Helper to support a clean default uninstall process on Windows
@@ -227,7 +225,7 @@ def _uninstall_helper(*, verbosity=0):
227225
if verbosity:
228226
args += ["-" + "v" * verbosity]
229227

230-
return _run_pip([*args, *reversed(_PACKAGE_NAMES)])
228+
return _run_pip([*args, "pip"])
231229

232230

233231
def _main(argv=None):

‎Lib/test/test_ensurepip.py

Copy file name to clipboardExpand all lines: Lib/test/test_ensurepip.py
+49-16Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,19 @@
66
import test.support
77
import unittest
88
import unittest.mock
9+
from pathlib import Path
910

1011
import ensurepip
1112
import ensurepip._uninstall
1213

1314

1415
class TestPackages(unittest.TestCase):
16+
def setUp(self):
17+
ensurepip._get_usable_pip_package.cache_clear()
18+
19+
def tearDown(self):
20+
ensurepip._get_usable_pip_package.cache_clear()
21+
1522
def touch(self, directory, filename):
1623
fullname = os.path.join(directory, filename)
1724
open(fullname, "wb").close()
@@ -20,42 +27,44 @@ def test_version(self):
2027
# Test version()
2128
with tempfile.TemporaryDirectory() as tmpdir:
2229
self.touch(tmpdir, "pip-1.2.3b1-py2.py3-none-any.whl")
23-
with (unittest.mock.patch.object(ensurepip, '_PACKAGES', None),
24-
unittest.mock.patch.object(ensurepip, '_WHEEL_PKG_DIR', tmpdir)):
30+
with unittest.mock.patch.object(
31+
ensurepip, '_WHEEL_PKG_DIR', tmpdir,
32+
):
2533
self.assertEqual(ensurepip.version(), '1.2.3b1')
2634

2735
def test_get_packages_no_dir(self):
2836
# Test _get_packages() without a wheel package directory
29-
with (unittest.mock.patch.object(ensurepip, '_PACKAGES', None),
30-
unittest.mock.patch.object(ensurepip, '_WHEEL_PKG_DIR', None)):
31-
packages = ensurepip._get_packages()
37+
with unittest.mock.patch.object(ensurepip, '_WHEEL_PKG_DIR', None):
38+
pip_pkg = ensurepip._get_usable_pip_package()
3239

33-
# when bundled wheel packages are used, we get _PIP_VERSION
40+
# when bundled pip wheel package is used, we get _PIP_VERSION
3441
self.assertEqual(ensurepip._PIP_VERSION, ensurepip.version())
3542

36-
# use bundled wheel packages
37-
self.assertIsNotNone(packages['pip'].wheel_name)
43+
# use bundled pip wheel package
44+
self.assertIsNotNone(pip_pkg.wheel_name)
3845

3946
def test_get_packages_with_dir(self):
4047
# Test _get_packages() with a wheel package directory
48+
older_pip_filename = "pip-1.2.3-py2.py3-none-any.whl"
4149
pip_filename = "pip-20.2.2-py2.py3-none-any.whl"
4250

4351
with tempfile.TemporaryDirectory() as tmpdir:
52+
self.touch(tmpdir, older_pip_filename)
4453
self.touch(tmpdir, pip_filename)
4554
# not used, make sure that it's ignored
4655
self.touch(tmpdir, "wheel-0.34.2-py2.py3-none-any.whl")
56+
# not used, make sure that it's ignored
57+
self.touch(tmpdir, "non-whl")
4758

48-
with (unittest.mock.patch.object(ensurepip, '_PACKAGES', None),
49-
unittest.mock.patch.object(ensurepip, '_WHEEL_PKG_DIR', tmpdir)):
50-
packages = ensurepip._get_packages()
59+
with unittest.mock.patch.object(
60+
ensurepip, '_WHEEL_PKG_DIR', tmpdir,
61+
):
62+
pip_pkg = ensurepip._get_usable_pip_package()
5163

52-
self.assertEqual(packages['pip'].version, '20.2.2')
53-
self.assertEqual(packages['pip'].wheel_path,
64+
self.assertEqual(pip_pkg.version, '20.2.2')
65+
self.assertEqual(pip_pkg.wheel_path,
5466
os.path.join(tmpdir, pip_filename))
5567

56-
# wheel package is ignored
57-
self.assertEqual(sorted(packages), ['pip'])
58-
5968

6069
class EnsurepipMixin:
6170

@@ -93,6 +102,30 @@ def test_basic_bootstrapping(self):
93102
additional_paths = self.run_pip.call_args[0][1]
94103
self.assertEqual(len(additional_paths), 1)
95104

105+
106+
def test_replacement_wheel_bootstrapping(self):
107+
ensurepip._get_usable_pip_package.cache_clear()
108+
109+
pip_wheel_name = (
110+
f'pip-{ensurepip._PIP_VERSION !s}-'
111+
'py3-none-any.whl'
112+
)
113+
114+
with tempfile.TemporaryDirectory() as tmpdir:
115+
tmp_path = Path(tmpdir)
116+
tmp_wheel_path = tmp_path / pip_wheel_name
117+
tmp_wheel_path.touch()
118+
119+
with unittest.mock.patch.object(
120+
ensurepip, '_WHEEL_PKG_DIR', tmpdir,
121+
):
122+
ensurepip.bootstrap()
123+
124+
ensurepip._get_usable_pip_package.cache_clear()
125+
126+
additional_paths = self.run_pip.call_args[0][1]
127+
self.assertEqual(Path(additional_paths[-1]).name, pip_wheel_name)
128+
96129
def test_bootstrapping_with_root(self):
97130
ensurepip.bootstrap(root="/foo/bar/")
98131

+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Simplified ``ensurepip`` to stop assuming that it can provision multiple
2+
wheels. The refreshed implementation now expects to only provision
3+
a ``pip`` wheel and no other distribution packages.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.