pip 25.2__py3-none-any.whl → 25.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pip/__init__.py +1 -1
- pip/_internal/__init__.py +0 -0
- pip/_internal/build_env.py +71 -3
- pip/_internal/cache.py +1 -1
- pip/_internal/cli/cmdoptions.py +43 -71
- pip/_internal/cli/parser.py +3 -3
- pip/_internal/cli/req_command.py +46 -26
- pip/_internal/commands/download.py +4 -7
- pip/_internal/commands/install.py +19 -14
- pip/_internal/commands/lock.py +3 -6
- pip/_internal/commands/wheel.py +5 -10
- pip/_internal/configuration.py +1 -2
- pip/_internal/distributions/sdist.py +13 -14
- pip/_internal/exceptions.py +20 -3
- pip/_internal/index/package_finder.py +3 -3
- pip/_internal/metadata/__init__.py +7 -2
- pip/_internal/metadata/importlib/_dists.py +8 -2
- pip/_internal/models/link.py +1 -1
- pip/_internal/models/wheel.py +5 -66
- pip/_internal/network/cache.py +6 -11
- pip/_internal/network/lazy_wheel.py +5 -3
- pip/_internal/operations/build/wheel.py +4 -4
- pip/_internal/operations/build/wheel_editable.py +4 -4
- pip/_internal/operations/prepare.py +7 -1
- pip/_internal/pyproject.py +2 -61
- pip/_internal/req/__init__.py +1 -3
- pip/_internal/req/constructors.py +42 -38
- pip/_internal/req/req_file.py +0 -1
- pip/_internal/req/req_install.py +32 -141
- pip/_internal/resolution/resolvelib/candidates.py +20 -11
- pip/_internal/resolution/resolvelib/factory.py +31 -0
- pip/_internal/resolution/resolvelib/provider.py +9 -0
- pip/_internal/resolution/resolvelib/reporter.py +21 -8
- pip/_internal/resolution/resolvelib/resolver.py +2 -6
- pip/_internal/self_outdated_check.py +11 -3
- pip/_internal/utils/filesystem.py +12 -0
- pip/_internal/utils/unpacking.py +25 -0
- pip/_internal/wheel_builder.py +23 -96
- pip/_vendor/README.rst +180 -0
- pip/_vendor/cachecontrol/LICENSE.txt +13 -0
- pip/_vendor/certifi/LICENSE +20 -0
- pip/_vendor/certifi/__init__.py +1 -1
- pip/_vendor/certifi/cacert.pem +62 -40
- pip/_vendor/dependency_groups/LICENSE.txt +9 -0
- pip/_vendor/distlib/LICENSE.txt +284 -0
- pip/_vendor/distro/LICENSE +202 -0
- pip/_vendor/idna/LICENSE.md +31 -0
- pip/_vendor/msgpack/COPYING +14 -0
- pip/_vendor/msgpack/__init__.py +2 -2
- pip/_vendor/packaging/LICENSE +3 -0
- pip/_vendor/packaging/LICENSE.APACHE +177 -0
- pip/_vendor/packaging/LICENSE.BSD +23 -0
- pip/_vendor/pkg_resources/LICENSE +17 -0
- pip/_vendor/platformdirs/LICENSE +21 -0
- pip/_vendor/platformdirs/api.py +1 -1
- pip/_vendor/platformdirs/macos.py +10 -8
- pip/_vendor/platformdirs/version.py +16 -3
- pip/_vendor/pygments/LICENSE +25 -0
- pip/_vendor/pyproject_hooks/LICENSE +21 -0
- pip/_vendor/requests/LICENSE +175 -0
- pip/_vendor/requests/__version__.py +2 -2
- pip/_vendor/requests/adapters.py +17 -40
- pip/_vendor/requests/sessions.py +1 -1
- pip/_vendor/resolvelib/LICENSE +13 -0
- pip/_vendor/resolvelib/__init__.py +1 -1
- pip/_vendor/resolvelib/resolvers/abstract.py +3 -3
- pip/_vendor/resolvelib/resolvers/resolution.py +5 -0
- pip/_vendor/rich/LICENSE +19 -0
- pip/_vendor/rich/style.py +7 -11
- pip/_vendor/tomli/LICENSE +21 -0
- pip/_vendor/tomli/__init__.py +1 -1
- pip/_vendor/tomli/_parser.py +28 -21
- pip/_vendor/tomli/_re.py +8 -5
- pip/_vendor/tomli_w/LICENSE +21 -0
- pip/_vendor/truststore/LICENSE +21 -0
- pip/_vendor/truststore/__init__.py +1 -1
- pip/_vendor/truststore/_api.py +14 -6
- pip/_vendor/truststore/_openssl.py +3 -1
- pip/_vendor/urllib3/LICENSE.txt +21 -0
- pip/_vendor/vendor.txt +8 -8
- {pip-25.2.dist-info → pip-25.3.dist-info}/METADATA +9 -10
- {pip-25.2.dist-info → pip-25.3.dist-info}/RECORD +106 -90
- {pip-25.2.dist-info → pip-25.3.dist-info}/WHEEL +1 -2
- pip-25.3.dist-info/entry_points.txt +4 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/AUTHORS.txt +9 -0
- pip/_internal/operations/build/metadata_legacy.py +0 -73
- pip/_internal/operations/build/wheel_legacy.py +0 -119
- pip/_internal/operations/install/editable_legacy.py +0 -48
- pip/_internal/utils/setuptools_build.py +0 -149
- pip-25.2.dist-info/entry_points.txt +0 -3
- pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE-HEADER +0 -3
- pip-25.2.dist-info/top_level.txt +0 -1
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/LICENSE.txt +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/certifi/LICENSE +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/distlib/LICENSE.txt +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/distro/LICENSE +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/idna/LICENSE.md +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/msgpack/COPYING +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE.APACHE +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE.BSD +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/pkg_resources/LICENSE +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/platformdirs/LICENSE +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/pygments/LICENSE +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/requests/LICENSE +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/resolvelib/LICENSE +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/rich/LICENSE +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/tomli/LICENSE +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/tomli_w/LICENSE +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/truststore/LICENSE +0 -0
- {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/urllib3/LICENSE.txt +0 -0
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from collections import defaultdict
|
|
4
|
+
from collections.abc import Mapping
|
|
4
5
|
from logging import getLogger
|
|
5
6
|
from typing import Any
|
|
6
7
|
|
|
7
8
|
from pip._vendor.resolvelib.reporters import BaseReporter
|
|
8
9
|
|
|
9
|
-
from .base import Candidate, Requirement
|
|
10
|
+
from .base import Candidate, Constraint, Requirement
|
|
10
11
|
|
|
11
12
|
logger = getLogger(__name__)
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class PipReporter(BaseReporter[Requirement, Candidate, str]):
|
|
15
|
-
def __init__(self) -> None:
|
|
16
|
+
def __init__(self, constraints: Mapping[str, Constraint] | None = None) -> None:
|
|
16
17
|
self.reject_count_by_package: defaultdict[str, int] = defaultdict(int)
|
|
18
|
+
self._constraints = constraints or {}
|
|
17
19
|
|
|
18
20
|
self._messages_at_reject_count = {
|
|
19
21
|
1: (
|
|
@@ -35,25 +37,36 @@ class PipReporter(BaseReporter[Requirement, Candidate, str]):
|
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
def rejecting_candidate(self, criterion: Any, candidate: Candidate) -> None:
|
|
40
|
+
"""Report a candidate being rejected.
|
|
41
|
+
|
|
42
|
+
Logs both the rejection count message (if applicable) and details about
|
|
43
|
+
the requirements and constraints that caused the rejection.
|
|
44
|
+
"""
|
|
38
45
|
self.reject_count_by_package[candidate.name] += 1
|
|
39
46
|
|
|
40
47
|
count = self.reject_count_by_package[candidate.name]
|
|
41
|
-
if count
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
message = self._messages_at_reject_count[count]
|
|
45
|
-
logger.info("INFO: %s", message.format(package_name=candidate.name))
|
|
48
|
+
if count in self._messages_at_reject_count:
|
|
49
|
+
message = self._messages_at_reject_count[count]
|
|
50
|
+
logger.info("INFO: %s", message.format(package_name=candidate.name))
|
|
46
51
|
|
|
47
52
|
msg = "Will try a different candidate, due to conflict:"
|
|
48
53
|
for req_info in criterion.information:
|
|
49
54
|
req, parent = req_info.requirement, req_info.parent
|
|
50
|
-
# Inspired by Factory.get_installation_error
|
|
51
55
|
msg += "\n "
|
|
52
56
|
if parent:
|
|
53
57
|
msg += f"{parent.name} {parent.version} depends on "
|
|
54
58
|
else:
|
|
55
59
|
msg += "The user requested "
|
|
56
60
|
msg += req.format_for_error()
|
|
61
|
+
|
|
62
|
+
# Add any relevant constraints
|
|
63
|
+
if self._constraints:
|
|
64
|
+
name = candidate.name
|
|
65
|
+
constraint = self._constraints.get(name)
|
|
66
|
+
if constraint and constraint.specifier:
|
|
67
|
+
constraint_text = f"{name}{constraint.specifier}"
|
|
68
|
+
msg += f"\n The user requested (constraint) {constraint_text}"
|
|
69
|
+
|
|
57
70
|
logger.debug(msg)
|
|
58
71
|
|
|
59
72
|
|
|
@@ -87,7 +87,8 @@ class Resolver(BaseResolver):
|
|
|
87
87
|
if "PIP_RESOLVER_DEBUG" in os.environ:
|
|
88
88
|
reporter: BaseReporter[Requirement, Candidate, str] = PipDebuggingReporter()
|
|
89
89
|
else:
|
|
90
|
-
reporter = PipReporter()
|
|
90
|
+
reporter = PipReporter(constraints=provider.constraints)
|
|
91
|
+
|
|
91
92
|
resolver: RLResolver[Requirement, Candidate, str] = RLResolver(
|
|
92
93
|
provider,
|
|
93
94
|
reporter,
|
|
@@ -180,11 +181,6 @@ class Resolver(BaseResolver):
|
|
|
180
181
|
|
|
181
182
|
req_set.add_named_requirement(ireq)
|
|
182
183
|
|
|
183
|
-
reqs = req_set.all_requirements
|
|
184
|
-
self.factory.preparer.prepare_linked_requirements_more(reqs)
|
|
185
|
-
for req in reqs:
|
|
186
|
-
req.prepared = True
|
|
187
|
-
req.needs_more_preparation = False
|
|
188
184
|
return req_set
|
|
189
185
|
|
|
190
186
|
def get_installation_order(
|
|
@@ -27,7 +27,12 @@ from pip._internal.utils.entrypoints import (
|
|
|
27
27
|
get_best_invocation_for_this_pip,
|
|
28
28
|
get_best_invocation_for_this_python,
|
|
29
29
|
)
|
|
30
|
-
from pip._internal.utils.filesystem import
|
|
30
|
+
from pip._internal.utils.filesystem import (
|
|
31
|
+
adjacent_tmp_file,
|
|
32
|
+
check_path_owner,
|
|
33
|
+
copy_directory_permissions,
|
|
34
|
+
replace,
|
|
35
|
+
)
|
|
31
36
|
from pip._internal.utils.misc import (
|
|
32
37
|
ExternallyManagedEnvironment,
|
|
33
38
|
check_externally_managed,
|
|
@@ -100,13 +105,15 @@ class SelfCheckState:
|
|
|
100
105
|
if not self._statefile_path:
|
|
101
106
|
return
|
|
102
107
|
|
|
108
|
+
statefile_directory = os.path.dirname(self._statefile_path)
|
|
109
|
+
|
|
103
110
|
# Check to make sure that we own the directory
|
|
104
|
-
if not check_path_owner(
|
|
111
|
+
if not check_path_owner(statefile_directory):
|
|
105
112
|
return
|
|
106
113
|
|
|
107
114
|
# Now that we've ensured the directory is owned by this user, we'll go
|
|
108
115
|
# ahead and make sure that all our directories are created.
|
|
109
|
-
ensure_dir(
|
|
116
|
+
ensure_dir(statefile_directory)
|
|
110
117
|
|
|
111
118
|
state = {
|
|
112
119
|
# Include the key so it's easy to tell which pip wrote the
|
|
@@ -120,6 +127,7 @@ class SelfCheckState:
|
|
|
120
127
|
|
|
121
128
|
with adjacent_tmp_file(self._statefile_path) as f:
|
|
122
129
|
f.write(text.encode())
|
|
130
|
+
copy_directory_permissions(statefile_directory, f)
|
|
123
131
|
|
|
124
132
|
try:
|
|
125
133
|
# Since we have a prefix-specific state file, we can just
|
|
@@ -150,3 +150,15 @@ def directory_size(path: str) -> int | float:
|
|
|
150
150
|
|
|
151
151
|
def format_directory_size(path: str) -> str:
|
|
152
152
|
return format_size(directory_size(path))
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def copy_directory_permissions(directory: str, target_file: BinaryIO) -> None:
|
|
156
|
+
mode = (
|
|
157
|
+
os.stat(directory).st_mode & 0o666 # select read/write permissions of directory
|
|
158
|
+
| 0o600 # set owner read/write permissions
|
|
159
|
+
)
|
|
160
|
+
# Change permissions only if there is no risk of following a symlink.
|
|
161
|
+
if os.chmod in os.supports_fd:
|
|
162
|
+
os.chmod(target_file.fileno(), mode)
|
|
163
|
+
elif os.chmod in os.supports_follow_symlinks:
|
|
164
|
+
os.chmod(target_file.name, mode, follow_symlinks=False)
|
pip/_internal/utils/unpacking.py
CHANGED
|
@@ -248,6 +248,20 @@ def untar_file(filename: str, location: str) -> None:
|
|
|
248
248
|
tar.close()
|
|
249
249
|
|
|
250
250
|
|
|
251
|
+
def is_symlink_target_in_tar(tar: tarfile.TarFile, tarinfo: tarfile.TarInfo) -> bool:
|
|
252
|
+
"""Check if the file pointed to by the symbolic link is in the tar archive"""
|
|
253
|
+
linkname = os.path.join(os.path.dirname(tarinfo.name), tarinfo.linkname)
|
|
254
|
+
|
|
255
|
+
linkname = os.path.normpath(linkname)
|
|
256
|
+
linkname = linkname.replace("\\", "/")
|
|
257
|
+
|
|
258
|
+
try:
|
|
259
|
+
tar.getmember(linkname)
|
|
260
|
+
return True
|
|
261
|
+
except KeyError:
|
|
262
|
+
return False
|
|
263
|
+
|
|
264
|
+
|
|
251
265
|
def _untar_without_filter(
|
|
252
266
|
filename: str,
|
|
253
267
|
location: str,
|
|
@@ -255,6 +269,9 @@ def _untar_without_filter(
|
|
|
255
269
|
leading: bool,
|
|
256
270
|
) -> None:
|
|
257
271
|
"""Fallback for Python without tarfile.data_filter"""
|
|
272
|
+
# NOTE: This function can be removed once pip requires CPython ≥ 3.12.
|
|
273
|
+
# PEP 706 added tarfile.data_filter, made tarfile extraction operations more secure.
|
|
274
|
+
# This feature is fully supported from CPython 3.12 onward.
|
|
258
275
|
for member in tar.getmembers():
|
|
259
276
|
fn = member.name
|
|
260
277
|
if leading:
|
|
@@ -269,6 +286,14 @@ def _untar_without_filter(
|
|
|
269
286
|
if member.isdir():
|
|
270
287
|
ensure_dir(path)
|
|
271
288
|
elif member.issym():
|
|
289
|
+
if not is_symlink_target_in_tar(tar, member):
|
|
290
|
+
message = (
|
|
291
|
+
"The tar file ({}) has a file ({}) trying to install "
|
|
292
|
+
"outside target directory ({})"
|
|
293
|
+
)
|
|
294
|
+
raise InstallationError(
|
|
295
|
+
message.format(filename, member.name, member.linkname)
|
|
296
|
+
)
|
|
272
297
|
try:
|
|
273
298
|
tar._extract_member(member, path)
|
|
274
299
|
except Exception as exc:
|
pip/_internal/wheel_builder.py
CHANGED
|
@@ -5,8 +5,8 @@ from __future__ import annotations
|
|
|
5
5
|
import logging
|
|
6
6
|
import os.path
|
|
7
7
|
import re
|
|
8
|
-
import shutil
|
|
9
8
|
from collections.abc import Iterable
|
|
9
|
+
from tempfile import TemporaryDirectory
|
|
10
10
|
|
|
11
11
|
from pip._vendor.packaging.utils import canonicalize_name, canonicalize_version
|
|
12
12
|
from pip._vendor.packaging.version import InvalidVersion, Version
|
|
@@ -18,13 +18,9 @@ from pip._internal.models.link import Link
|
|
|
18
18
|
from pip._internal.models.wheel import Wheel
|
|
19
19
|
from pip._internal.operations.build.wheel import build_wheel_pep517
|
|
20
20
|
from pip._internal.operations.build.wheel_editable import build_wheel_editable
|
|
21
|
-
from pip._internal.operations.build.wheel_legacy import build_wheel_legacy
|
|
22
21
|
from pip._internal.req.req_install import InstallRequirement
|
|
23
22
|
from pip._internal.utils.logging import indent_log
|
|
24
23
|
from pip._internal.utils.misc import ensure_dir, hash_file
|
|
25
|
-
from pip._internal.utils.setuptools_build import make_setuptools_clean_args
|
|
26
|
-
from pip._internal.utils.subprocess import call_subprocess
|
|
27
|
-
from pip._internal.utils.temp_dir import TempDirectory
|
|
28
24
|
from pip._internal.utils.urls import path_to_url
|
|
29
25
|
from pip._internal.vcs import vcs
|
|
30
26
|
|
|
@@ -43,37 +39,12 @@ def _contains_egg_info(s: str) -> bool:
|
|
|
43
39
|
return bool(_egg_info_re.search(s))
|
|
44
40
|
|
|
45
41
|
|
|
46
|
-
def _should_build(
|
|
47
|
-
req: InstallRequirement,
|
|
48
|
-
) -> bool:
|
|
49
|
-
"""Return whether an InstallRequirement should be built into a wheel."""
|
|
50
|
-
assert not req.constraint
|
|
51
|
-
|
|
52
|
-
if req.is_wheel:
|
|
53
|
-
return False
|
|
54
|
-
|
|
55
|
-
assert req.source_dir
|
|
56
|
-
|
|
57
|
-
if req.editable:
|
|
58
|
-
# we only build PEP 660 editable requirements
|
|
59
|
-
return req.supports_pyproject_editable
|
|
60
|
-
|
|
61
|
-
return True
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def should_build_for_install_command(
|
|
65
|
-
req: InstallRequirement,
|
|
66
|
-
) -> bool:
|
|
67
|
-
return _should_build(req)
|
|
68
|
-
|
|
69
|
-
|
|
70
42
|
def _should_cache(
|
|
71
43
|
req: InstallRequirement,
|
|
72
44
|
) -> bool | None:
|
|
73
45
|
"""
|
|
74
46
|
Return whether a built InstallRequirement can be stored in the persistent
|
|
75
|
-
wheel cache, assuming the wheel cache is available
|
|
76
|
-
has determined a wheel needs to be built.
|
|
47
|
+
wheel cache, assuming the wheel cache is available.
|
|
77
48
|
"""
|
|
78
49
|
if req.editable or not req.source_dir:
|
|
79
50
|
# never cache editable requirements
|
|
@@ -118,7 +89,7 @@ def _get_cache_dir(
|
|
|
118
89
|
def _verify_one(req: InstallRequirement, wheel_path: str) -> None:
|
|
119
90
|
canonical_name = canonicalize_name(req.name or "")
|
|
120
91
|
w = Wheel(os.path.basename(wheel_path))
|
|
121
|
-
if
|
|
92
|
+
if w.name != canonical_name:
|
|
122
93
|
raise InvalidWheelFilename(
|
|
123
94
|
f"Wheel has unexpected file name: expected {canonical_name!r}, "
|
|
124
95
|
f"got {w.name!r}",
|
|
@@ -148,8 +119,6 @@ def _build_one(
|
|
|
148
119
|
req: InstallRequirement,
|
|
149
120
|
output_dir: str,
|
|
150
121
|
verify: bool,
|
|
151
|
-
build_options: list[str],
|
|
152
|
-
global_options: list[str],
|
|
153
122
|
editable: bool,
|
|
154
123
|
) -> str | None:
|
|
155
124
|
"""Build one wheel.
|
|
@@ -170,9 +139,7 @@ def _build_one(
|
|
|
170
139
|
|
|
171
140
|
# Install build deps into temporary directory (PEP 518)
|
|
172
141
|
with req.build_env:
|
|
173
|
-
wheel_path = _build_one_inside_env(
|
|
174
|
-
req, output_dir, build_options, global_options, editable
|
|
175
|
-
)
|
|
142
|
+
wheel_path = _build_one_inside_env(req, output_dir, editable)
|
|
176
143
|
if wheel_path and verify:
|
|
177
144
|
try:
|
|
178
145
|
_verify_one(req, wheel_path)
|
|
@@ -185,45 +152,25 @@ def _build_one(
|
|
|
185
152
|
def _build_one_inside_env(
|
|
186
153
|
req: InstallRequirement,
|
|
187
154
|
output_dir: str,
|
|
188
|
-
build_options: list[str],
|
|
189
|
-
global_options: list[str],
|
|
190
155
|
editable: bool,
|
|
191
156
|
) -> str | None:
|
|
192
|
-
with
|
|
157
|
+
with TemporaryDirectory(dir=output_dir) as wheel_directory:
|
|
193
158
|
assert req.name
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
"Ignoring --build-option when building %s using PEP 517", req.name
|
|
204
|
-
)
|
|
205
|
-
if editable:
|
|
206
|
-
wheel_path = build_wheel_editable(
|
|
207
|
-
name=req.name,
|
|
208
|
-
backend=req.pep517_backend,
|
|
209
|
-
metadata_directory=req.metadata_directory,
|
|
210
|
-
tempd=temp_dir.path,
|
|
211
|
-
)
|
|
212
|
-
else:
|
|
213
|
-
wheel_path = build_wheel_pep517(
|
|
214
|
-
name=req.name,
|
|
215
|
-
backend=req.pep517_backend,
|
|
216
|
-
metadata_directory=req.metadata_directory,
|
|
217
|
-
tempd=temp_dir.path,
|
|
218
|
-
)
|
|
159
|
+
assert req.metadata_directory
|
|
160
|
+
assert req.pep517_backend
|
|
161
|
+
if editable:
|
|
162
|
+
wheel_path = build_wheel_editable(
|
|
163
|
+
name=req.name,
|
|
164
|
+
backend=req.pep517_backend,
|
|
165
|
+
metadata_directory=req.metadata_directory,
|
|
166
|
+
wheel_directory=wheel_directory,
|
|
167
|
+
)
|
|
219
168
|
else:
|
|
220
|
-
wheel_path =
|
|
169
|
+
wheel_path = build_wheel_pep517(
|
|
221
170
|
name=req.name,
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
build_options=build_options,
|
|
226
|
-
tempd=temp_dir.path,
|
|
171
|
+
backend=req.pep517_backend,
|
|
172
|
+
metadata_directory=req.metadata_directory,
|
|
173
|
+
wheel_directory=wheel_directory,
|
|
227
174
|
)
|
|
228
175
|
|
|
229
176
|
if wheel_path is not None:
|
|
@@ -231,7 +178,11 @@ def _build_one_inside_env(
|
|
|
231
178
|
dest_path = os.path.join(output_dir, wheel_name)
|
|
232
179
|
try:
|
|
233
180
|
wheel_hash, length = hash_file(wheel_path)
|
|
234
|
-
|
|
181
|
+
# We can do a replace here because wheel_path is guaranteed to
|
|
182
|
+
# be in the same filesystem as output_dir. This will perform an
|
|
183
|
+
# atomic rename, which is necessary to avoid concurrency issues
|
|
184
|
+
# when populating the cache.
|
|
185
|
+
os.replace(wheel_path, dest_path)
|
|
235
186
|
logger.info(
|
|
236
187
|
"Created wheel for %s: filename=%s size=%d sha256=%s",
|
|
237
188
|
req.name,
|
|
@@ -247,35 +198,13 @@ def _build_one_inside_env(
|
|
|
247
198
|
req.name,
|
|
248
199
|
e,
|
|
249
200
|
)
|
|
250
|
-
# Ignore return, we can't do anything else useful.
|
|
251
|
-
if not req.use_pep517:
|
|
252
|
-
_clean_one_legacy(req, global_options)
|
|
253
201
|
return None
|
|
254
202
|
|
|
255
203
|
|
|
256
|
-
def _clean_one_legacy(req: InstallRequirement, global_options: list[str]) -> bool:
|
|
257
|
-
clean_args = make_setuptools_clean_args(
|
|
258
|
-
req.setup_py_path,
|
|
259
|
-
global_options=global_options,
|
|
260
|
-
)
|
|
261
|
-
|
|
262
|
-
logger.info("Running setup.py clean for %s", req.name)
|
|
263
|
-
try:
|
|
264
|
-
call_subprocess(
|
|
265
|
-
clean_args, command_desc="python setup.py clean", cwd=req.source_dir
|
|
266
|
-
)
|
|
267
|
-
return True
|
|
268
|
-
except Exception:
|
|
269
|
-
logger.error("Failed cleaning build dir for %s", req.name)
|
|
270
|
-
return False
|
|
271
|
-
|
|
272
|
-
|
|
273
204
|
def build(
|
|
274
205
|
requirements: Iterable[InstallRequirement],
|
|
275
206
|
wheel_cache: WheelCache,
|
|
276
207
|
verify: bool,
|
|
277
|
-
build_options: list[str],
|
|
278
|
-
global_options: list[str],
|
|
279
208
|
) -> BuildResult:
|
|
280
209
|
"""Build wheels.
|
|
281
210
|
|
|
@@ -300,8 +229,6 @@ def build(
|
|
|
300
229
|
req,
|
|
301
230
|
cache_dir,
|
|
302
231
|
verify,
|
|
303
|
-
build_options,
|
|
304
|
-
global_options,
|
|
305
232
|
req.editable and req.permit_editable_wheels,
|
|
306
233
|
)
|
|
307
234
|
if wheel_file:
|
pip/_vendor/README.rst
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
================
|
|
2
|
+
Vendoring Policy
|
|
3
|
+
================
|
|
4
|
+
|
|
5
|
+
* Vendored libraries **MUST** not be modified except as required to
|
|
6
|
+
successfully vendor them.
|
|
7
|
+
* Vendored libraries **MUST** be released copies of libraries available on
|
|
8
|
+
PyPI.
|
|
9
|
+
* Vendored libraries **MUST** be available under a license that allows
|
|
10
|
+
them to be integrated into ``pip``, which is released under the MIT license.
|
|
11
|
+
* Vendored libraries **MUST** be accompanied with LICENSE files.
|
|
12
|
+
* The versions of libraries vendored in pip **MUST** be reflected in
|
|
13
|
+
``pip/_vendor/vendor.txt``.
|
|
14
|
+
* Vendored libraries **MUST** function without any build steps such as ``2to3``
|
|
15
|
+
or compilation of C code, practically this limits to single source 2.x/3.x and
|
|
16
|
+
pure Python.
|
|
17
|
+
* Any modifications made to libraries **MUST** be noted in
|
|
18
|
+
``pip/_vendor/README.rst`` and their corresponding patches **MUST** be
|
|
19
|
+
included ``tools/vendoring/patches``.
|
|
20
|
+
* Vendored libraries should have corresponding ``vendored()`` entries in
|
|
21
|
+
``pip/_vendor/__init__.py``.
|
|
22
|
+
|
|
23
|
+
Rationale
|
|
24
|
+
=========
|
|
25
|
+
|
|
26
|
+
Historically pip has not had any dependencies except for ``setuptools`` itself,
|
|
27
|
+
choosing instead to implement any functionality it needed to prevent needing
|
|
28
|
+
a dependency. However, starting with pip 1.5, we began to replace code that was
|
|
29
|
+
implemented inside of pip with reusable libraries from PyPI. This brought the
|
|
30
|
+
typical benefits of reusing libraries instead of reinventing the wheel like
|
|
31
|
+
higher quality and more battle tested code, centralization of bug fixes
|
|
32
|
+
(particularly security sensitive ones), and better/more features for less work.
|
|
33
|
+
|
|
34
|
+
However, there are several issues with having dependencies in the traditional
|
|
35
|
+
way (via ``install_requires``) for pip. These issues are:
|
|
36
|
+
|
|
37
|
+
**Fragility**
|
|
38
|
+
When pip depends on another library to function then if for whatever reason
|
|
39
|
+
that library either isn't installed or an incompatible version is installed
|
|
40
|
+
then pip ceases to function. This is of course true for all Python
|
|
41
|
+
applications, however for every application *except* for pip the way you fix
|
|
42
|
+
it is by re-running pip. Obviously, when pip can't run, you can't use pip to
|
|
43
|
+
fix pip, so you're left having to manually resolve dependencies and
|
|
44
|
+
installing them by hand.
|
|
45
|
+
|
|
46
|
+
**Making other libraries uninstallable**
|
|
47
|
+
One of pip's current dependencies is the ``requests`` library, for which pip
|
|
48
|
+
requires a fairly recent version to run. If pip depended on ``requests`` in
|
|
49
|
+
the traditional manner, then we'd either have to maintain compatibility with
|
|
50
|
+
every ``requests`` version that has ever existed (and ever will), OR allow
|
|
51
|
+
pip to render certain versions of ``requests`` uninstallable. (The second
|
|
52
|
+
issue, although technically true for any Python application, is magnified by
|
|
53
|
+
pip's ubiquity; pip is installed by default in Python, in ``pyvenv``, and in
|
|
54
|
+
``virtualenv``.)
|
|
55
|
+
|
|
56
|
+
**Security**
|
|
57
|
+
This might seem puzzling at first glance, since vendoring has a tendency to
|
|
58
|
+
complicate updating dependencies for security updates, and that holds true
|
|
59
|
+
for pip. However, given the *other* reasons for avoiding dependencies, the
|
|
60
|
+
alternative is for pip to reinvent the wheel itself. This is what pip did
|
|
61
|
+
historically. It forced pip to re-implement its own HTTPS verification
|
|
62
|
+
routines as a workaround for the Python standard library's lack of SSL
|
|
63
|
+
validation, which resulted in similar bugs in the validation routine in
|
|
64
|
+
``requests`` and ``urllib3``, except that they had to be discovered and
|
|
65
|
+
fixed independently. Even though we're vendoring, reusing libraries keeps
|
|
66
|
+
pip more secure by relying on the great work of our dependencies, *and*
|
|
67
|
+
allowing for faster, easier security fixes by simply pulling in newer
|
|
68
|
+
versions of dependencies.
|
|
69
|
+
|
|
70
|
+
**Bootstrapping**
|
|
71
|
+
Currently most popular methods of installing pip rely on pip's
|
|
72
|
+
self-contained nature to install pip itself. These tools work by bundling a
|
|
73
|
+
copy of pip, adding it to ``sys.path``, and then executing that copy of pip.
|
|
74
|
+
This is done instead of implementing a "mini installer" (to reduce
|
|
75
|
+
duplication); pip already knows how to install a Python package, and is far
|
|
76
|
+
more battle-tested than any "mini installer" could ever possibly be.
|
|
77
|
+
|
|
78
|
+
Many downstream redistributors have policies against this kind of bundling, and
|
|
79
|
+
instead opt to patch the software they distribute to debundle it and make it
|
|
80
|
+
rely on the global versions of the software that they already have packaged
|
|
81
|
+
(which may have its own patches applied to it). We (the pip team) would prefer
|
|
82
|
+
it if pip was *not* debundled in this manner due to the above reasons and
|
|
83
|
+
instead we would prefer it if pip would be left intact as it is now.
|
|
84
|
+
|
|
85
|
+
In the longer term, if someone has a *portable* solution to the above problems,
|
|
86
|
+
other than the bundling method we currently use, that doesn't add additional
|
|
87
|
+
problems that are unreasonable then we would be happy to consider, and possibly
|
|
88
|
+
switch to said method. This solution must function correctly across all of the
|
|
89
|
+
situation that we expect pip to be used and not mandate some external mechanism
|
|
90
|
+
such as OS packages.
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
Modifications
|
|
94
|
+
=============
|
|
95
|
+
|
|
96
|
+
* ``setuptools`` is completely stripped to only keep ``pkg_resources``.
|
|
97
|
+
* ``pkg_resources`` has been modified to import its dependencies from
|
|
98
|
+
``pip._vendor``, and to use the vendored copy of ``platformdirs``
|
|
99
|
+
rather than ``appdirs``.
|
|
100
|
+
* ``packaging`` has been modified to import its dependencies from
|
|
101
|
+
``pip._vendor``.
|
|
102
|
+
* ``CacheControl`` has been modified to import its dependencies from
|
|
103
|
+
``pip._vendor``.
|
|
104
|
+
* ``requests`` has been modified to import its other dependencies from
|
|
105
|
+
``pip._vendor`` and to *not* load ``simplejson`` (all platforms) and
|
|
106
|
+
``pyopenssl`` (Windows).
|
|
107
|
+
* ``platformdirs`` has been modified to import its submodules from ``pip._vendor.platformdirs``.
|
|
108
|
+
|
|
109
|
+
Automatic Vendoring
|
|
110
|
+
===================
|
|
111
|
+
|
|
112
|
+
Vendoring is automated via the `vendoring <https://pypi.org/project/vendoring/>`_ tool from the content of
|
|
113
|
+
``pip/_vendor/vendor.txt`` and the different patches in
|
|
114
|
+
``tools/vendoring/patches``.
|
|
115
|
+
Launch it via ``vendoring sync . -v`` (requires ``vendoring>=0.2.2``).
|
|
116
|
+
Tool configuration is done via ``pyproject.toml``.
|
|
117
|
+
|
|
118
|
+
To update the vendored library versions, we have a session defined in ``nox``.
|
|
119
|
+
The command to upgrade everything is::
|
|
120
|
+
|
|
121
|
+
nox -s vendoring -- --upgrade-all --skip urllib3 --skip setuptools
|
|
122
|
+
|
|
123
|
+
At the time of writing (April 2025) we do not upgrade ``urllib3`` because the
|
|
124
|
+
next version is a major upgrade and will be handled as an independent PR. We also
|
|
125
|
+
do not upgrade ``setuptools``, because we only rely on ``pkg_resources``, and
|
|
126
|
+
tracking every ``setuptools`` change is unnecessary for our needs.
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
Managing Local Patches
|
|
130
|
+
======================
|
|
131
|
+
|
|
132
|
+
The ``vendoring`` tool automatically applies our local patches, but updating,
|
|
133
|
+
the patches sometimes no longer apply cleanly. In that case, the update will
|
|
134
|
+
fail. To resolve this, take the following steps:
|
|
135
|
+
|
|
136
|
+
1. Revert any incomplete changes in the revendoring branch, to ensure you have
|
|
137
|
+
a clean starting point.
|
|
138
|
+
2. Run the revendoring of the library with a problem again: ``nox -s vendoring
|
|
139
|
+
-- --upgrade <library_name>``.
|
|
140
|
+
3. This will fail again, but you will have the original source in your working
|
|
141
|
+
directory. Review the existing patch against the source, and modify the patch
|
|
142
|
+
to reflect the new version of the source. If you ``git add`` the changes the
|
|
143
|
+
vendoring made, you can modify the source to reflect the patch file and then
|
|
144
|
+
generate a new patch with ``git diff``.
|
|
145
|
+
4. Now, revert everything *except* the patch file changes. Leave the modified
|
|
146
|
+
patch file unstaged but saved in the working tree.
|
|
147
|
+
5. Re-run the vendoring. This time, it should pick up the changed patch file
|
|
148
|
+
and apply it cleanly. The patch file changes will be committed along with the
|
|
149
|
+
revendoring, so the new commit should be ready to test and publish as a PR.
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
Debundling
|
|
153
|
+
==========
|
|
154
|
+
|
|
155
|
+
As mentioned in the rationale, we, the pip team, would prefer it if pip was not
|
|
156
|
+
debundled (other than optionally ``pip/_vendor/requests/cacert.pem``) and that
|
|
157
|
+
pip was left intact. However, if you insist on doing so, we have a
|
|
158
|
+
semi-supported method (that we don't test in our CI) and requires a bit of
|
|
159
|
+
extra work on your end in order to solve the problems described above.
|
|
160
|
+
|
|
161
|
+
1. Delete everything in ``pip/_vendor/`` **except** for
|
|
162
|
+
``pip/_vendor/__init__.py`` and ``pip/_vendor/vendor.txt``.
|
|
163
|
+
2. Generate wheels for each of pip's dependencies (and any of their
|
|
164
|
+
dependencies) using your patched copies of these libraries. These must be
|
|
165
|
+
placed somewhere on the filesystem that pip can access (``pip/_vendor`` is
|
|
166
|
+
the default assumption).
|
|
167
|
+
3. Modify ``pip/_vendor/__init__.py`` so that the ``DEBUNDLED`` variable is
|
|
168
|
+
``True``.
|
|
169
|
+
4. Upon installation, the ``INSTALLER`` file in pip's own ``dist-info``
|
|
170
|
+
directory should be set to something other than ``pip``, so that pip
|
|
171
|
+
can detect that it wasn't installed using itself.
|
|
172
|
+
5. *(optional)* If you've placed the wheels in a location other than
|
|
173
|
+
``pip/_vendor/``, then modify ``pip/_vendor/__init__.py`` so that the
|
|
174
|
+
``WHEEL_DIR`` variable points to the location you've placed them.
|
|
175
|
+
6. *(optional)* Update the ``pip_self_version_check`` logic to use the
|
|
176
|
+
appropriate logic for determining the latest available version of pip and
|
|
177
|
+
prompt the user with the correct upgrade message.
|
|
178
|
+
|
|
179
|
+
Note that partial debundling is **NOT** supported. You need to prepare wheels
|
|
180
|
+
for all dependencies for successful debundling.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright 2012-2021 Eric Larson
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
This package contains a modified version of ca-bundle.crt:
|
|
2
|
+
|
|
3
|
+
ca-bundle.crt -- Bundle of CA Root Certificates
|
|
4
|
+
|
|
5
|
+
This is a bundle of X.509 certificates of public Certificate Authorities
|
|
6
|
+
(CA). These were automatically extracted from Mozilla's root certificates
|
|
7
|
+
file (certdata.txt). This file can be found in the mozilla source tree:
|
|
8
|
+
https://hg.mozilla.org/mozilla-central/file/tip/security/nss/lib/ckfw/builtins/certdata.txt
|
|
9
|
+
It contains the certificates in PEM format and therefore
|
|
10
|
+
can be directly used with curl / libcurl / php_curl, or with
|
|
11
|
+
an Apache+mod_ssl webserver for SSL client authentication.
|
|
12
|
+
Just configure this file as the SSLCACertificateFile.#
|
|
13
|
+
|
|
14
|
+
***** BEGIN LICENSE BLOCK *****
|
|
15
|
+
This Source Code Form is subject to the terms of the Mozilla Public License,
|
|
16
|
+
v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain
|
|
17
|
+
one at http://mozilla.org/MPL/2.0/.
|
|
18
|
+
|
|
19
|
+
***** END LICENSE BLOCK *****
|
|
20
|
+
@(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $
|
pip/_vendor/certifi/__init__.py
CHANGED