1
- import collections
2
1
import os
3
- import os .path
4
2
import subprocess
5
3
import sys
6
4
import sysconfig
7
5
import tempfile
6
+ from contextlib import nullcontext
8
7
from importlib import resources
8
+ from pathlib import Path
9
+ from shutil import copy2
9
10
10
11
11
12
__all__ = ["version" , "bootstrap" ]
12
- _PACKAGE_NAMES = ('pip' ,)
13
13
_PIP_VERSION = "23.3.2"
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' ))
22
14
23
15
# Directory of system wheel packages. Some Linux distribution packaging
24
16
# policies recommend against bundling dependencies. For example, Fedora
25
17
# installs wheel packages in the /usr/share/python-wheels/ directory and don't
26
18
# 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
+
28
24
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
29
30
30
- def _find_packages (path ):
31
- packages = {}
31
+ dist_matching_wheels = _WHEEL_PKG_DIR .glob ('pip-*.whl' )
32
32
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
+ )
76
62
77
63
78
64
def _run_pip (args , additional_paths = None ):
@@ -105,7 +91,7 @@ def version():
105
91
"""
106
92
Returns a string specifying the bundled version of pip.
107
93
"""
108
- return _get_packages ()[ 'pip' ]. version
94
+ return _get_pip_version ()
109
95
110
96
111
97
def _disable_pip_configuration_settings ():
@@ -167,24 +153,10 @@ def _bootstrap(*, root=None, upgrade=False, user=False,
167
153
with tempfile .TemporaryDirectory () as tmpdir :
168
154
# Put our bundled wheels into a temporary directory and construct the
169
155
# 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 )
188
160
189
161
# Construct the arguments to be passed to the pip command
190
162
args = ["install" , "--no-cache-dir" , "--no-index" , "--find-links" , tmpdir ]
@@ -197,7 +169,8 @@ def _bootstrap(*, root=None, upgrade=False, user=False,
197
169
if verbosity :
198
170
args += ["-" + "v" * verbosity ]
199
171
200
- return _run_pip ([* args , * _PACKAGE_NAMES ], additional_paths )
172
+ return _run_pip ([* args , "pip" ], [os .fsdecode (tmp_wheel_path )])
173
+
201
174
202
175
def _uninstall_helper (* , verbosity = 0 ):
203
176
"""Helper to support a clean default uninstall process on Windows
@@ -227,7 +200,7 @@ def _uninstall_helper(*, verbosity=0):
227
200
if verbosity :
228
201
args += ["-" + "v" * verbosity ]
229
202
230
- return _run_pip ([* args , * reversed ( _PACKAGE_NAMES ) ])
203
+ return _run_pip ([* args , "pip" ])
231
204
232
205
233
206
def _main (argv = None ):
0 commit comments