1- import collections
21import os
3- import os .path
42import subprocess
53import sys
64import sysconfig
75import tempfile
6+ from contextlib import nullcontext
87from importlib import resources
8+ from pathlib import Path
9+ from shutil import copy2
910
1011
1112__all__ = ["version" , "bootstrap" ]
12- _PACKAGE_NAMES = ('pip' ,)
13- _PIP_VERSION = "23.2.1"
14- _PROJECTS = [
15- ("pip" , _PIP_VERSION , "py3" ),
16- ]
17-
18- # Packages bundled in ensurepip._bundled have wheel_name set.
19- # Packages from WHEEL_PKG_DIR have wheel_path set.
20- _Package = collections .namedtuple ('Package' ,
21- ('version' , 'wheel_name' , 'wheel_path' ))
13+ _PIP_VERSION = "25.2"
2214
2315# Directory of system wheel packages. Some Linux distribution packaging
2416# policies recommend against bundling dependencies. For example, Fedora
2517# installs wheel packages in the /usr/share/python-wheels/ directory and don't
2618# install the ensurepip._bundled package.
27- _WHEEL_PKG_DIR = sysconfig .get_config_var ('WHEEL_PKG_DIR' )
19+ if (_pkg_dir := sysconfig .get_config_var ('WHEEL_PKG_DIR' )) is not None :
20+ _WHEEL_PKG_DIR = Path (_pkg_dir ).resolve ()
21+ else :
22+ _WHEEL_PKG_DIR = None
23+
2824
25+ def _find_wheel_pkg_dir_pip ():
26+ if _WHEEL_PKG_DIR is None :
27+ # NOTE: The compile-time `WHEEL_PKG_DIR` is unset so there is no place
28+ # NOTE: for looking up the wheels.
29+ return None
2930
30- def _find_packages (path ):
31- packages = {}
31+ dist_matching_wheels = _WHEEL_PKG_DIR .glob ('pip-*.whl' )
3232 try :
33- filenames = os .listdir (path )
34- except OSError :
35- # Ignore: path doesn't exist or permission error
36- filenames = ()
37- # Make the code deterministic if a directory contains multiple wheel files
38- # of the same package, but don't attempt to implement correct version
39- # comparison since this case should not happen.
40- filenames = sorted (filenames )
41- for filename in filenames :
42- # filename is like 'pip-21.2.4-py3-none-any.whl'
43- if not filename .endswith (".whl" ):
44- continue
45- for name in _PACKAGE_NAMES :
46- prefix = name + '-'
47- if filename .startswith (prefix ):
48- break
49- else :
50- continue
51-
52- # Extract '21.2.4' from 'pip-21.2.4-py3-none-any.whl'
53- version = filename .removeprefix (prefix ).partition ('-' )[0 ]
54- wheel_path = os .path .join (path , filename )
55- packages [name ] = _Package (version , None , wheel_path )
56- return packages
57-
58-
59- def _get_packages ():
60- global _PACKAGES , _WHEEL_PKG_DIR
61- if _PACKAGES is not None :
62- return _PACKAGES
63-
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
33+ last_matching_dist_wheel = sorted (dist_matching_wheels )[- 1 ]
34+ except IndexError :
35+ # NOTE: `WHEEL_PKG_DIR` does not contain any wheel files for `pip`.
36+ return None
37+
38+ return nullcontext (last_matching_dist_wheel )
39+
40+
41+ def _get_pip_whl_path_ctx ():
42+ # Prefer pip from the wheel package directory, if present.
43+ if (alternative_pip_wheel_path := _find_wheel_pkg_dir_pip ()) is not None :
44+ return alternative_pip_wheel_path
45+
46+ return resources .as_file (
47+ resources .files ('ensurepip' )
48+ / '_bundled'
49+ / f'pip-{ _PIP_VERSION } -py3-none-any.whl'
50+ )
51+
52+
53+ def _get_pip_version ():
54+ with _get_pip_whl_path_ctx () as bundled_wheel_path :
55+ wheel_name = bundled_wheel_path .name
56+ return (
57+ # Extract '21.2.4' from 'pip-21.2.4-py3-none-any.whl'
58+ wheel_name .
59+ removeprefix ('pip-' ).
60+ partition ('-' )[0 ]
61+ )
7662
7763
7864def _run_pip (args , additional_paths = None ):
@@ -105,7 +91,7 @@ def version():
10591 """
10692 Returns a string specifying the bundled version of pip.
10793 """
108- return _get_packages ()[ 'pip' ]. version
94+ return _get_pip_version ()
10995
11096
11197def _disable_pip_configuration_settings ():
@@ -167,24 +153,10 @@ def _bootstrap(*, root=None, upgrade=False, user=False,
167153 with tempfile .TemporaryDirectory () as tmpdir :
168154 # Put our bundled wheels into a temporary directory and construct the
169155 # 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 )
156+ tmpdir_path = Path (tmpdir )
157+ with _get_pip_whl_path_ctx () as bundled_wheel_path :
158+ tmp_wheel_path = tmpdir_path / bundled_wheel_path .name
159+ copy2 (bundled_wheel_path , tmp_wheel_path )
188160
189161 # Construct the arguments to be passed to the pip command
190162 args = ["install" , "--no-cache-dir" , "--no-index" , "--find-links" , tmpdir ]
@@ -197,7 +169,8 @@ def _bootstrap(*, root=None, upgrade=False, user=False,
197169 if verbosity :
198170 args += ["-" + "v" * verbosity ]
199171
200- return _run_pip ([* args , * _PACKAGE_NAMES ], additional_paths )
172+ return _run_pip ([* args , "pip" ], [os .fsdecode (tmp_wheel_path )])
173+
201174
202175def _uninstall_helper (* , verbosity = 0 ):
203176 """Helper to support a clean default uninstall process on Windows
@@ -227,7 +200,7 @@ def _uninstall_helper(*, verbosity=0):
227200 if verbosity :
228201 args += ["-" + "v" * verbosity ]
229202
230- return _run_pip ([* args , * reversed ( _PACKAGE_NAMES ) ])
203+ return _run_pip ([* args , "pip" ])
231204
232205
233206def _main (argv = None ):
0 commit comments