pip 25.2__py3-none-any.whl → 26.0__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 +265 -8
- pip/_internal/cache.py +1 -1
- pip/_internal/cli/base_command.py +11 -0
- pip/_internal/cli/cmdoptions.py +200 -71
- pip/_internal/cli/index_command.py +20 -0
- pip/_internal/cli/main.py +11 -6
- pip/_internal/cli/main_parser.py +3 -1
- pip/_internal/cli/parser.py +96 -36
- pip/_internal/cli/progress_bars.py +4 -2
- pip/_internal/cli/req_command.py +126 -30
- pip/_internal/commands/cache.py +24 -0
- pip/_internal/commands/completion.py +2 -1
- pip/_internal/commands/download.py +12 -11
- pip/_internal/commands/index.py +13 -6
- pip/_internal/commands/install.py +55 -43
- pip/_internal/commands/list.py +14 -16
- pip/_internal/commands/lock.py +19 -14
- pip/_internal/commands/wheel.py +13 -23
- pip/_internal/configuration.py +1 -2
- pip/_internal/distributions/sdist.py +13 -14
- pip/_internal/exceptions.py +96 -6
- pip/_internal/index/collector.py +2 -3
- pip/_internal/index/package_finder.py +87 -21
- pip/_internal/locations/__init__.py +1 -2
- pip/_internal/locations/_sysconfig.py +4 -1
- pip/_internal/metadata/__init__.py +7 -2
- pip/_internal/metadata/importlib/_dists.py +8 -2
- pip/_internal/models/link.py +18 -14
- pip/_internal/models/release_control.py +92 -0
- pip/_internal/models/selection_prefs.py +6 -3
- pip/_internal/models/wheel.py +5 -66
- pip/_internal/network/auth.py +6 -2
- pip/_internal/network/cache.py +6 -11
- pip/_internal/network/download.py +4 -5
- pip/_internal/network/lazy_wheel.py +5 -3
- pip/_internal/network/session.py +14 -10
- pip/_internal/operations/build/wheel.py +4 -4
- pip/_internal/operations/build/wheel_editable.py +4 -4
- pip/_internal/operations/install/wheel.py +1 -2
- pip/_internal/operations/prepare.py +9 -4
- pip/_internal/pyproject.py +2 -61
- pip/_internal/req/__init__.py +1 -3
- pip/_internal/req/constructors.py +45 -39
- pip/_internal/req/pep723.py +41 -0
- pip/_internal/req/req_file.py +10 -2
- pip/_internal/req/req_install.py +32 -141
- pip/_internal/resolution/resolvelib/candidates.py +20 -11
- pip/_internal/resolution/resolvelib/factory.py +43 -1
- pip/_internal/resolution/resolvelib/provider.py +9 -0
- pip/_internal/resolution/resolvelib/reporter.py +21 -8
- pip/_internal/resolution/resolvelib/requirements.py +7 -3
- pip/_internal/resolution/resolvelib/resolver.py +2 -6
- pip/_internal/self_outdated_check.py +17 -16
- pip/_internal/utils/datetime.py +18 -0
- pip/_internal/utils/filesystem.py +52 -1
- pip/_internal/utils/logging.py +34 -2
- pip/_internal/utils/misc.py +18 -12
- pip/_internal/utils/pylock.py +116 -0
- pip/_internal/utils/unpacking.py +26 -1
- pip/_internal/vcs/versioncontrol.py +3 -1
- pip/_internal/wheel_builder.py +23 -96
- pip/_vendor/README.rst +180 -0
- pip/_vendor/cachecontrol/LICENSE.txt +13 -0
- pip/_vendor/cachecontrol/__init__.py +6 -3
- pip/_vendor/cachecontrol/adapter.py +0 -1
- pip/_vendor/cachecontrol/controller.py +1 -1
- pip/_vendor/cachecontrol/filewrapper.py +3 -1
- pip/_vendor/certifi/LICENSE +20 -0
- pip/_vendor/certifi/__init__.py +1 -1
- pip/_vendor/certifi/cacert.pem +62 -372
- 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/idna/codec.py +1 -1
- pip/_vendor/idna/core.py +1 -1
- pip/_vendor/idna/idnadata.py +72 -6
- pip/_vendor/idna/package_data.py +1 -1
- pip/_vendor/idna/uts46data.py +891 -731
- 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/packaging/__init__.py +1 -1
- pip/_vendor/packaging/_elffile.py +0 -1
- pip/_vendor/packaging/_manylinux.py +36 -36
- pip/_vendor/packaging/_musllinux.py +1 -1
- pip/_vendor/packaging/_parser.py +22 -10
- pip/_vendor/packaging/_structures.py +8 -0
- pip/_vendor/packaging/_tokenizer.py +23 -25
- pip/_vendor/packaging/licenses/__init__.py +13 -11
- pip/_vendor/packaging/licenses/_spdx.py +41 -1
- pip/_vendor/packaging/markers.py +64 -38
- pip/_vendor/packaging/metadata.py +143 -27
- pip/_vendor/packaging/pylock.py +635 -0
- pip/_vendor/packaging/requirements.py +5 -10
- pip/_vendor/packaging/specifiers.py +219 -170
- pip/_vendor/packaging/tags.py +15 -20
- pip/_vendor/packaging/utils.py +19 -24
- pip/_vendor/packaging/version.py +315 -105
- 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/platformdirs/windows.py +7 -1
- 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 +11 -11
- {pip-25.2.dist-info → pip-26.0.dist-info}/METADATA +10 -11
- {pip-25.2.dist-info → pip-26.0.dist-info}/RECORD +158 -139
- {pip-25.2.dist-info → pip-26.0.dist-info}/WHEEL +1 -2
- pip-26.0.dist-info/entry_points.txt +4 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/AUTHORS.txt +27 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/idna/LICENSE.md +1 -1
- pip/_internal/models/pylock.py +0 -188
- 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-26.0.dist-info}/licenses/LICENSE.txt +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/certifi/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/distlib/LICENSE.txt +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/distro/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/msgpack/COPYING +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE.APACHE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE.BSD +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/pkg_resources/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/platformdirs/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/pygments/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/requests/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/resolvelib/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/rich/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/tomli/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/tomli_w/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/truststore/LICENSE +0 -0
- {pip-25.2.dist-info → pip-26.0.dist-info}/licenses/src/pip/_vendor/urllib3/LICENSE.txt +0 -0
pip/_internal/models/link.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import datetime
|
|
3
4
|
import functools
|
|
4
5
|
import itertools
|
|
5
6
|
import logging
|
|
@@ -7,15 +8,16 @@ import os
|
|
|
7
8
|
import posixpath
|
|
8
9
|
import re
|
|
9
10
|
import urllib.parse
|
|
11
|
+
import urllib.request
|
|
10
12
|
from collections.abc import Mapping
|
|
11
13
|
from dataclasses import dataclass
|
|
12
14
|
from typing import (
|
|
13
|
-
TYPE_CHECKING,
|
|
14
15
|
Any,
|
|
15
16
|
NamedTuple,
|
|
16
17
|
)
|
|
17
18
|
|
|
18
|
-
from pip._internal.
|
|
19
|
+
from pip._internal.exceptions import InvalidEggFragment
|
|
20
|
+
from pip._internal.utils.datetime import parse_iso_datetime
|
|
19
21
|
from pip._internal.utils.filetypes import WHEEL_EXTENSION
|
|
20
22
|
from pip._internal.utils.hashes import Hashes
|
|
21
23
|
from pip._internal.utils.misc import (
|
|
@@ -26,9 +28,6 @@ from pip._internal.utils.misc import (
|
|
|
26
28
|
)
|
|
27
29
|
from pip._internal.utils.urls import path_to_url, url_to_path
|
|
28
30
|
|
|
29
|
-
if TYPE_CHECKING:
|
|
30
|
-
from pip._internal.index.collector import IndexContent
|
|
31
|
-
|
|
32
31
|
logger = logging.getLogger(__name__)
|
|
33
32
|
|
|
34
33
|
|
|
@@ -207,6 +206,7 @@ class Link:
|
|
|
207
206
|
"requires_python",
|
|
208
207
|
"yanked_reason",
|
|
209
208
|
"metadata_file_data",
|
|
209
|
+
"upload_time",
|
|
210
210
|
"cache_link_parsing",
|
|
211
211
|
"egg_fragment",
|
|
212
212
|
]
|
|
@@ -214,17 +214,17 @@ class Link:
|
|
|
214
214
|
def __init__(
|
|
215
215
|
self,
|
|
216
216
|
url: str,
|
|
217
|
-
comes_from: str |
|
|
217
|
+
comes_from: str | None = None,
|
|
218
218
|
requires_python: str | None = None,
|
|
219
219
|
yanked_reason: str | None = None,
|
|
220
220
|
metadata_file_data: MetadataFile | None = None,
|
|
221
|
+
upload_time: datetime.datetime | None = None,
|
|
221
222
|
cache_link_parsing: bool = True,
|
|
222
223
|
hashes: Mapping[str, str] | None = None,
|
|
223
224
|
) -> None:
|
|
224
225
|
"""
|
|
225
226
|
:param url: url of the resource pointed to (href of the link)
|
|
226
|
-
:param comes_from:
|
|
227
|
-
or string.
|
|
227
|
+
:param comes_from: URL or string indicating where the link was found.
|
|
228
228
|
:param requires_python: String containing the `Requires-Python`
|
|
229
229
|
metadata field, specified in PEP 345. This may be specified by
|
|
230
230
|
a data-requires-python attribute in the HTML link tag, as
|
|
@@ -239,6 +239,8 @@ class Link:
|
|
|
239
239
|
no such metadata is provided. This argument, if not None, indicates
|
|
240
240
|
that a separate metadata file exists, and also optionally supplies
|
|
241
241
|
hashes for that file.
|
|
242
|
+
:param upload_time: upload time of the file, or None if the information
|
|
243
|
+
is not available from the server.
|
|
242
244
|
:param cache_link_parsing: A flag that is used elsewhere to determine
|
|
243
245
|
whether resources retrieved from this link should be cached. PyPI
|
|
244
246
|
URLs should generally have this set to False, for example.
|
|
@@ -272,6 +274,7 @@ class Link:
|
|
|
272
274
|
self.requires_python = requires_python if requires_python else None
|
|
273
275
|
self.yanked_reason = yanked_reason
|
|
274
276
|
self.metadata_file_data = metadata_file_data
|
|
277
|
+
self.upload_time = upload_time
|
|
275
278
|
|
|
276
279
|
self.cache_link_parsing = cache_link_parsing
|
|
277
280
|
self.egg_fragment = self._egg_fragment()
|
|
@@ -300,6 +303,11 @@ class Link:
|
|
|
300
303
|
if metadata_info is None:
|
|
301
304
|
metadata_info = file_data.get("dist-info-metadata")
|
|
302
305
|
|
|
306
|
+
if upload_time_data := file_data.get("upload-time"):
|
|
307
|
+
upload_time = parse_iso_datetime(upload_time_data)
|
|
308
|
+
else:
|
|
309
|
+
upload_time = None
|
|
310
|
+
|
|
303
311
|
# The metadata info value may be a boolean, or a dict of hashes.
|
|
304
312
|
if isinstance(metadata_info, dict):
|
|
305
313
|
# The file exists, and hashes have been supplied
|
|
@@ -325,6 +333,7 @@ class Link:
|
|
|
325
333
|
yanked_reason=yanked_reason,
|
|
326
334
|
hashes=hashes,
|
|
327
335
|
metadata_file_data=metadata_file_data,
|
|
336
|
+
upload_time=upload_time,
|
|
328
337
|
)
|
|
329
338
|
|
|
330
339
|
@classmethod
|
|
@@ -474,12 +483,7 @@ class Link:
|
|
|
474
483
|
# an optional extras specifier. Anything else is invalid.
|
|
475
484
|
project_name = match.group(1)
|
|
476
485
|
if not self._project_name_re.match(project_name):
|
|
477
|
-
|
|
478
|
-
reason=f"{self} contains an egg fragment with a non-PEP 508 name.",
|
|
479
|
-
replacement="to use the req @ url syntax, and remove the egg fragment",
|
|
480
|
-
gone_in="25.3",
|
|
481
|
-
issue=13157,
|
|
482
|
-
)
|
|
486
|
+
raise InvalidEggFragment(self, project_name)
|
|
483
487
|
|
|
484
488
|
return project_name
|
|
485
489
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
|
|
5
|
+
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
|
|
6
|
+
|
|
7
|
+
from pip._internal.exceptions import CommandError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# TODO: add slots=True when Python 3.9 is dropped
|
|
11
|
+
@dataclass
|
|
12
|
+
class ReleaseControl:
|
|
13
|
+
"""Helper for managing which release types can be installed."""
|
|
14
|
+
|
|
15
|
+
all_releases: set[str] = field(default_factory=set)
|
|
16
|
+
only_final: set[str] = field(default_factory=set)
|
|
17
|
+
_order: list[tuple[str, str]] = field(
|
|
18
|
+
init=False, default_factory=list, compare=False, repr=False
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
def handle_mutual_excludes(
|
|
22
|
+
self, value: str, target: set[str], other: set[str], attr_name: str
|
|
23
|
+
) -> None:
|
|
24
|
+
"""Parse and apply release control option value.
|
|
25
|
+
|
|
26
|
+
Processes comma-separated package names or special values `:all:` and `:none:`.
|
|
27
|
+
|
|
28
|
+
When adding packages to target, they're removed from other to maintain mutual
|
|
29
|
+
exclusivity between all_releases and only_final. All operations are tracked in
|
|
30
|
+
order so that the original command-line argument sequence can be reconstructed
|
|
31
|
+
when passing options to build subprocesses.
|
|
32
|
+
"""
|
|
33
|
+
if value.startswith("-"):
|
|
34
|
+
raise CommandError(
|
|
35
|
+
"--all-releases / --only-final option requires 1 argument."
|
|
36
|
+
)
|
|
37
|
+
new = value.split(",")
|
|
38
|
+
while ":all:" in new:
|
|
39
|
+
other.clear()
|
|
40
|
+
target.clear()
|
|
41
|
+
target.add(":all:")
|
|
42
|
+
# Track :all: in order
|
|
43
|
+
self._order.append((attr_name, ":all:"))
|
|
44
|
+
del new[: new.index(":all:") + 1]
|
|
45
|
+
# Without a none, we want to discard everything as :all: covers it
|
|
46
|
+
if ":none:" not in new:
|
|
47
|
+
return
|
|
48
|
+
for name in new:
|
|
49
|
+
if name == ":none:":
|
|
50
|
+
target.clear()
|
|
51
|
+
# Track :none: in order
|
|
52
|
+
self._order.append((attr_name, ":none:"))
|
|
53
|
+
continue
|
|
54
|
+
name = canonicalize_name(name)
|
|
55
|
+
other.discard(name)
|
|
56
|
+
target.add(name)
|
|
57
|
+
# Track package-specific setting in order
|
|
58
|
+
self._order.append((attr_name, name))
|
|
59
|
+
|
|
60
|
+
def get_ordered_args(self) -> list[tuple[str, str]]:
|
|
61
|
+
"""
|
|
62
|
+
Get ordered list of (flag_name, value) tuples for reconstructing CLI args.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
List of tuples where each tuple is (attribute_name, value).
|
|
66
|
+
The attribute_name is either 'all_releases' or 'only_final'.
|
|
67
|
+
|
|
68
|
+
Example:
|
|
69
|
+
[("all_releases", ":all:"), ("only_final", "simple")]
|
|
70
|
+
would be reconstructed as:
|
|
71
|
+
["--all-releases", ":all:", "--only-final", "simple"]
|
|
72
|
+
"""
|
|
73
|
+
return self._order[:]
|
|
74
|
+
|
|
75
|
+
def allows_prereleases(self, canonical_name: NormalizedName) -> bool | None:
|
|
76
|
+
"""
|
|
77
|
+
Determine if pre-releases are allowed for a package.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
True: Pre-releases are allowed (package in all_releases)
|
|
81
|
+
False: Only final releases allowed (package in only_final)
|
|
82
|
+
None: No specific setting, use default behavior
|
|
83
|
+
"""
|
|
84
|
+
if canonical_name in self.all_releases:
|
|
85
|
+
return True
|
|
86
|
+
elif canonical_name in self.only_final:
|
|
87
|
+
return False
|
|
88
|
+
elif ":all:" in self.all_releases:
|
|
89
|
+
return True
|
|
90
|
+
elif ":all:" in self.only_final:
|
|
91
|
+
return False
|
|
92
|
+
return None
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from pip._internal.models.format_control import FormatControl
|
|
4
|
+
from pip._internal.models.release_control import ReleaseControl
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
# TODO: This needs Python 3.10's improved slots support for dataclasses
|
|
@@ -13,7 +14,7 @@ class SelectionPreferences:
|
|
|
13
14
|
|
|
14
15
|
__slots__ = [
|
|
15
16
|
"allow_yanked",
|
|
16
|
-
"
|
|
17
|
+
"release_control",
|
|
17
18
|
"format_control",
|
|
18
19
|
"prefer_binary",
|
|
19
20
|
"ignore_requires_python",
|
|
@@ -26,7 +27,7 @@ class SelectionPreferences:
|
|
|
26
27
|
def __init__(
|
|
27
28
|
self,
|
|
28
29
|
allow_yanked: bool,
|
|
29
|
-
|
|
30
|
+
release_control: ReleaseControl | None = None,
|
|
30
31
|
format_control: FormatControl | None = None,
|
|
31
32
|
prefer_binary: bool = False,
|
|
32
33
|
ignore_requires_python: bool | None = None,
|
|
@@ -35,6 +36,8 @@ class SelectionPreferences:
|
|
|
35
36
|
|
|
36
37
|
:param allow_yanked: Whether files marked as yanked (in the sense
|
|
37
38
|
of PEP 592) are permitted to be candidates for install.
|
|
39
|
+
:param release_control: A ReleaseControl object or None. Used to control
|
|
40
|
+
whether pre-releases are allowed for specific packages.
|
|
38
41
|
:param format_control: A FormatControl object or None. Used to control
|
|
39
42
|
the selection of source packages / binary packages when consulting
|
|
40
43
|
the index and links.
|
|
@@ -47,7 +50,7 @@ class SelectionPreferences:
|
|
|
47
50
|
ignore_requires_python = False
|
|
48
51
|
|
|
49
52
|
self.allow_yanked = allow_yanked
|
|
50
|
-
self.
|
|
53
|
+
self.release_control = release_control
|
|
51
54
|
self.format_control = format_control
|
|
52
55
|
self.prefer_binary = prefer_binary
|
|
53
56
|
self.ignore_requires_python = ignore_requires_python
|
pip/_internal/models/wheel.py
CHANGED
|
@@ -4,91 +4,30 @@ name that have meaning.
|
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
-
import re
|
|
8
7
|
from collections.abc import Iterable
|
|
9
8
|
|
|
10
9
|
from pip._vendor.packaging.tags import Tag
|
|
11
|
-
from pip._vendor.packaging.utils import BuildTag, parse_wheel_filename
|
|
12
10
|
from pip._vendor.packaging.utils import (
|
|
13
11
|
InvalidWheelFilename as _PackagingInvalidWheelFilename,
|
|
14
12
|
)
|
|
13
|
+
from pip._vendor.packaging.utils import parse_wheel_filename
|
|
15
14
|
|
|
16
15
|
from pip._internal.exceptions import InvalidWheelFilename
|
|
17
|
-
from pip._internal.utils.deprecation import deprecated
|
|
18
16
|
|
|
19
17
|
|
|
20
18
|
class Wheel:
|
|
21
19
|
"""A wheel file"""
|
|
22
20
|
|
|
23
|
-
legacy_wheel_file_re = re.compile(
|
|
24
|
-
r"""^(?P<namever>(?P<name>[^\s-]+?)-(?P<ver>[^\s-]*?))
|
|
25
|
-
((-(?P<build>\d[^-]*?))?-(?P<pyver>[^\s-]+?)-(?P<abi>[^\s-]+?)-(?P<plat>[^\s-]+?)
|
|
26
|
-
\.whl|\.dist-info)$""",
|
|
27
|
-
re.VERBOSE,
|
|
28
|
-
)
|
|
29
|
-
|
|
30
21
|
def __init__(self, filename: str) -> None:
|
|
31
22
|
self.filename = filename
|
|
32
23
|
|
|
33
|
-
# To make mypy happy specify type hints that can come from either
|
|
34
|
-
# parse_wheel_filename or the legacy_wheel_file_re match.
|
|
35
|
-
self.name: str
|
|
36
|
-
self._build_tag: BuildTag | None = None
|
|
37
|
-
|
|
38
24
|
try:
|
|
39
25
|
wheel_info = parse_wheel_filename(filename)
|
|
40
|
-
self.name, _version, self._build_tag, self.file_tags = wheel_info
|
|
41
|
-
self.version = str(_version)
|
|
42
26
|
except _PackagingInvalidWheelFilename as e:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
deprecated(
|
|
49
|
-
reason=(
|
|
50
|
-
f"Wheel filename {filename!r} is not correctly normalised. "
|
|
51
|
-
"Future versions of pip will raise the following error:\n"
|
|
52
|
-
f"{e.args[0]}\n\n"
|
|
53
|
-
),
|
|
54
|
-
replacement=(
|
|
55
|
-
"to rename the wheel to use a correctly normalised "
|
|
56
|
-
"name (this may require updating the version in "
|
|
57
|
-
"the project metadata)"
|
|
58
|
-
),
|
|
59
|
-
gone_in="25.3",
|
|
60
|
-
issue=12938,
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
self.name = legacy_wheel_info.group("name").replace("_", "-")
|
|
64
|
-
self.version = legacy_wheel_info.group("ver").replace("_", "-")
|
|
65
|
-
|
|
66
|
-
# Generate the file tags from the legacy wheel filename
|
|
67
|
-
pyversions = legacy_wheel_info.group("pyver").split(".")
|
|
68
|
-
abis = legacy_wheel_info.group("abi").split(".")
|
|
69
|
-
plats = legacy_wheel_info.group("plat").split(".")
|
|
70
|
-
self.file_tags = frozenset(
|
|
71
|
-
Tag(interpreter=py, abi=abi, platform=plat)
|
|
72
|
-
for py in pyversions
|
|
73
|
-
for abi in abis
|
|
74
|
-
for plat in plats
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
@property
|
|
78
|
-
def build_tag(self) -> BuildTag:
|
|
79
|
-
if self._build_tag is not None:
|
|
80
|
-
return self._build_tag
|
|
81
|
-
|
|
82
|
-
# Parse the build tag from the legacy wheel filename
|
|
83
|
-
legacy_wheel_info = self.legacy_wheel_file_re.match(self.filename)
|
|
84
|
-
assert legacy_wheel_info is not None, "guaranteed by filename validation"
|
|
85
|
-
build_tag = legacy_wheel_info.group("build")
|
|
86
|
-
match = re.match(r"^(\d+)(.*)$", build_tag)
|
|
87
|
-
assert match is not None, "guaranteed by filename validation"
|
|
88
|
-
build_tag_groups = match.groups()
|
|
89
|
-
self._build_tag = (int(build_tag_groups[0]), build_tag_groups[1])
|
|
90
|
-
|
|
91
|
-
return self._build_tag
|
|
27
|
+
raise InvalidWheelFilename(e.args[0]) from None
|
|
28
|
+
|
|
29
|
+
self.name, _version, self.build_tag, self.file_tags = wheel_info
|
|
30
|
+
self.version = str(_version)
|
|
92
31
|
|
|
93
32
|
def get_formatted_file_tags(self) -> list[str]:
|
|
94
33
|
"""Return the wheel's tags as a sorted list of strings."""
|
pip/_internal/network/auth.py
CHANGED
|
@@ -20,7 +20,6 @@ from pathlib import Path
|
|
|
20
20
|
from typing import Any, NamedTuple
|
|
21
21
|
|
|
22
22
|
from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth
|
|
23
|
-
from pip._vendor.requests.models import Request, Response
|
|
24
23
|
from pip._vendor.requests.utils import get_netrc_auth
|
|
25
24
|
|
|
26
25
|
from pip._internal.utils.logging import getLogger
|
|
@@ -33,6 +32,10 @@ from pip._internal.utils.misc import (
|
|
|
33
32
|
)
|
|
34
33
|
from pip._internal.vcs.versioncontrol import AuthInfo
|
|
35
34
|
|
|
35
|
+
if typing.TYPE_CHECKING:
|
|
36
|
+
from pip._vendor.requests import PreparedRequest
|
|
37
|
+
from pip._vendor.requests.models import Response
|
|
38
|
+
|
|
36
39
|
logger = getLogger(__name__)
|
|
37
40
|
|
|
38
41
|
KEYRING_DISABLED = False
|
|
@@ -437,8 +440,9 @@ class MultiDomainBasicAuth(AuthBase):
|
|
|
437
440
|
|
|
438
441
|
return url, username, password
|
|
439
442
|
|
|
440
|
-
def __call__(self, req:
|
|
443
|
+
def __call__(self, req: PreparedRequest) -> PreparedRequest:
|
|
441
444
|
# Get credentials for this request
|
|
445
|
+
assert req.url is not None
|
|
442
446
|
url, username, password = self._get_url_and_credentials(req.url)
|
|
443
447
|
|
|
444
448
|
# Set the url of the request to the url without any credentials
|
pip/_internal/network/cache.py
CHANGED
|
@@ -13,7 +13,11 @@ from pip._vendor.cachecontrol.cache import SeparateBodyBaseCache
|
|
|
13
13
|
from pip._vendor.cachecontrol.caches import SeparateBodyFileCache
|
|
14
14
|
from pip._vendor.requests.models import Response
|
|
15
15
|
|
|
16
|
-
from pip._internal.utils.filesystem import
|
|
16
|
+
from pip._internal.utils.filesystem import (
|
|
17
|
+
adjacent_tmp_file,
|
|
18
|
+
copy_directory_permissions,
|
|
19
|
+
replace,
|
|
20
|
+
)
|
|
17
21
|
from pip._internal.utils.misc import ensure_dir
|
|
18
22
|
|
|
19
23
|
|
|
@@ -82,16 +86,7 @@ class SafeFileCache(SeparateBodyBaseCache):
|
|
|
82
86
|
writer_func(f)
|
|
83
87
|
# Inherit the read/write permissions of the cache directory
|
|
84
88
|
# to enable multi-user cache use-cases.
|
|
85
|
-
|
|
86
|
-
os.stat(self.directory).st_mode
|
|
87
|
-
& 0o666 # select read/write permissions of cache directory
|
|
88
|
-
| 0o600 # set owner read/write permissions
|
|
89
|
-
)
|
|
90
|
-
# Change permissions only if there is no risk of following a symlink.
|
|
91
|
-
if os.chmod in os.supports_fd:
|
|
92
|
-
os.chmod(f.fileno(), mode)
|
|
93
|
-
elif os.chmod in os.supports_follow_symlinks:
|
|
94
|
-
os.chmod(f.name, mode, follow_symlinks=False)
|
|
89
|
+
copy_directory_permissions(self.directory, f)
|
|
95
90
|
|
|
96
91
|
replace(f.name, path)
|
|
97
92
|
|
|
@@ -167,14 +167,13 @@ class Downloader:
|
|
|
167
167
|
self,
|
|
168
168
|
session: PipSession,
|
|
169
169
|
progress_bar: BarType,
|
|
170
|
-
resume_retries: int,
|
|
171
170
|
) -> None:
|
|
172
|
-
assert (
|
|
173
|
-
resume_retries >= 0
|
|
174
|
-
), "Number of max resume retries must be bigger or equal to zero"
|
|
175
171
|
self._session = session
|
|
176
172
|
self._progress_bar = progress_bar
|
|
177
|
-
self._resume_retries = resume_retries
|
|
173
|
+
self._resume_retries = session.resume_retries
|
|
174
|
+
assert (
|
|
175
|
+
self._resume_retries >= 0
|
|
176
|
+
), "Number of max resume retries must be bigger or equal to zero"
|
|
178
177
|
|
|
179
178
|
def batch(
|
|
180
179
|
self, links: Iterable[Link], location: str
|
|
@@ -11,7 +11,7 @@ from tempfile import NamedTemporaryFile
|
|
|
11
11
|
from typing import Any
|
|
12
12
|
from zipfile import BadZipFile, ZipFile
|
|
13
13
|
|
|
14
|
-
from pip._vendor.packaging.utils import
|
|
14
|
+
from pip._vendor.packaging.utils import NormalizedName
|
|
15
15
|
from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response
|
|
16
16
|
|
|
17
17
|
from pip._internal.metadata import BaseDistribution, MemoryWheel, get_wheel_distribution
|
|
@@ -23,7 +23,9 @@ class HTTPRangeRequestUnsupported(Exception):
|
|
|
23
23
|
pass
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
def dist_from_wheel_url(
|
|
26
|
+
def dist_from_wheel_url(
|
|
27
|
+
name: NormalizedName, url: str, session: PipSession
|
|
28
|
+
) -> BaseDistribution:
|
|
27
29
|
"""Return a distribution object from the given wheel URL.
|
|
28
30
|
|
|
29
31
|
This uses HTTP range requests to only fetch the portion of the wheel
|
|
@@ -37,7 +39,7 @@ def dist_from_wheel_url(name: str, url: str, session: PipSession) -> BaseDistrib
|
|
|
37
39
|
wheel = MemoryWheel(zf.name, zf) # type: ignore
|
|
38
40
|
# After context manager exit, wheel.name
|
|
39
41
|
# is an invalid file by intention.
|
|
40
|
-
return get_wheel_distribution(wheel,
|
|
42
|
+
return get_wheel_distribution(wheel, name)
|
|
41
43
|
|
|
42
44
|
|
|
43
45
|
class LazyZipOverHTTP:
|
pip/_internal/network/session.py
CHANGED
|
@@ -50,8 +50,8 @@ from pip._internal.utils.urls import url_to_path
|
|
|
50
50
|
if TYPE_CHECKING:
|
|
51
51
|
from ssl import SSLContext
|
|
52
52
|
|
|
53
|
+
from pip._vendor.urllib3 import ProxyManager
|
|
53
54
|
from pip._vendor.urllib3.poolmanager import PoolManager
|
|
54
|
-
from pip._vendor.urllib3.proxymanager import ProxyManager
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
logger = logging.getLogger(__name__)
|
|
@@ -212,11 +212,12 @@ class LocalFSAdapter(BaseAdapter):
|
|
|
212
212
|
self,
|
|
213
213
|
request: PreparedRequest,
|
|
214
214
|
stream: bool = False,
|
|
215
|
-
timeout: float | tuple[float, float] | None = None,
|
|
215
|
+
timeout: float | tuple[float, float] | tuple[float, None] | None = None,
|
|
216
216
|
verify: bool | str = True,
|
|
217
|
-
cert: str | tuple[str, str] | None = None,
|
|
217
|
+
cert: bytes | str | tuple[bytes | str, bytes | str] | None = None,
|
|
218
218
|
proxies: Mapping[str, str] | None = None,
|
|
219
219
|
) -> Response:
|
|
220
|
+
assert request.url is not None
|
|
220
221
|
pathname = url_to_path(request.url)
|
|
221
222
|
|
|
222
223
|
resp = Response()
|
|
@@ -237,13 +238,13 @@ class LocalFSAdapter(BaseAdapter):
|
|
|
237
238
|
resp.headers = CaseInsensitiveDict(
|
|
238
239
|
{
|
|
239
240
|
"Content-Type": content_type,
|
|
240
|
-
"Content-Length": stats.st_size,
|
|
241
|
+
"Content-Length": str(stats.st_size),
|
|
241
242
|
"Last-Modified": modified,
|
|
242
243
|
}
|
|
243
244
|
)
|
|
244
245
|
|
|
245
246
|
resp.raw = open(pathname, "rb")
|
|
246
|
-
resp.close = resp.raw.close
|
|
247
|
+
resp.close = resp.raw.close # type: ignore[method-assign]
|
|
247
248
|
|
|
248
249
|
return resp
|
|
249
250
|
|
|
@@ -277,7 +278,7 @@ class _SSLContextAdapterMixin:
|
|
|
277
278
|
) -> PoolManager:
|
|
278
279
|
if self._ssl_context is not None:
|
|
279
280
|
pool_kwargs.setdefault("ssl_context", self._ssl_context)
|
|
280
|
-
return super().init_poolmanager( # type: ignore[misc]
|
|
281
|
+
return super().init_poolmanager( # type: ignore[misc, no-any-return]
|
|
281
282
|
connections=connections,
|
|
282
283
|
maxsize=maxsize,
|
|
283
284
|
block=block,
|
|
@@ -289,7 +290,7 @@ class _SSLContextAdapterMixin:
|
|
|
289
290
|
# context here too. https://github.com/pypa/pip/issues/13288
|
|
290
291
|
if self._ssl_context is not None:
|
|
291
292
|
proxy_kwargs.setdefault("ssl_context", self._ssl_context)
|
|
292
|
-
return super().proxy_manager_for(proxy, **proxy_kwargs) # type: ignore[misc]
|
|
293
|
+
return super().proxy_manager_for(proxy, **proxy_kwargs) # type: ignore[misc, no-any-return]
|
|
293
294
|
|
|
294
295
|
|
|
295
296
|
class HTTPAdapter(_SSLContextAdapterMixin, _BaseHTTPAdapter):
|
|
@@ -329,6 +330,7 @@ class PipSession(requests.Session):
|
|
|
329
330
|
self,
|
|
330
331
|
*args: Any,
|
|
331
332
|
retries: int = 0,
|
|
333
|
+
resume_retries: int = 0,
|
|
332
334
|
cache: str | None = None,
|
|
333
335
|
trusted_hosts: Sequence[str] = (),
|
|
334
336
|
index_urls: list[str] | None = None,
|
|
@@ -350,7 +352,7 @@ class PipSession(requests.Session):
|
|
|
350
352
|
self.headers["User-Agent"] = user_agent()
|
|
351
353
|
|
|
352
354
|
# Attach our Authentication handler to the session
|
|
353
|
-
self.auth = MultiDomainBasicAuth(index_urls=index_urls)
|
|
355
|
+
self.auth: MultiDomainBasicAuth = MultiDomainBasicAuth(index_urls=index_urls)
|
|
354
356
|
|
|
355
357
|
# Create our urllib3.Retry instance which will allow us to customize
|
|
356
358
|
# how we handle retries.
|
|
@@ -370,6 +372,7 @@ class PipSession(requests.Session):
|
|
|
370
372
|
# order to prevent hammering the service.
|
|
371
373
|
backoff_factor=0.25,
|
|
372
374
|
) # type: ignore
|
|
375
|
+
self.resume_retries = resume_retries
|
|
373
376
|
|
|
374
377
|
# Our Insecure HTTPAdapter disables HTTPS validation. It does not
|
|
375
378
|
# support caching so we'll use it for all http:// URLs.
|
|
@@ -383,8 +386,9 @@ class PipSession(requests.Session):
|
|
|
383
386
|
# we can't validate the response of an insecurely/untrusted fetched
|
|
384
387
|
# origin, and we don't want someone to be able to poison the cache and
|
|
385
388
|
# require manual eviction from the cache to fix it.
|
|
389
|
+
self._trusted_host_adapter: InsecureCacheControlAdapter | InsecureHTTPAdapter
|
|
386
390
|
if cache:
|
|
387
|
-
secure_adapter = CacheControlAdapter(
|
|
391
|
+
secure_adapter: _BaseHTTPAdapter = CacheControlAdapter(
|
|
388
392
|
cache=SafeFileCache(cache),
|
|
389
393
|
max_retries=retries,
|
|
390
394
|
ssl_context=ssl_context,
|
|
@@ -518,7 +522,7 @@ class PipSession(requests.Session):
|
|
|
518
522
|
|
|
519
523
|
return False
|
|
520
524
|
|
|
521
|
-
def request(self, method: str, url: str, *args: Any, **kwargs: Any) -> Response:
|
|
525
|
+
def request(self, method: str, url: str, *args: Any, **kwargs: Any) -> Response: # type: ignore[override]
|
|
522
526
|
# Allow setting a default timeout on a session
|
|
523
527
|
kwargs.setdefault("timeout", self.timeout)
|
|
524
528
|
# Allow setting a default proxies on a session
|
|
@@ -14,7 +14,7 @@ def build_wheel_pep517(
|
|
|
14
14
|
name: str,
|
|
15
15
|
backend: BuildBackendHookCaller,
|
|
16
16
|
metadata_directory: str,
|
|
17
|
-
|
|
17
|
+
wheel_directory: str,
|
|
18
18
|
) -> str | None:
|
|
19
19
|
"""Build one InstallRequirement using the PEP 517 build process.
|
|
20
20
|
|
|
@@ -22,17 +22,17 @@ def build_wheel_pep517(
|
|
|
22
22
|
"""
|
|
23
23
|
assert metadata_directory is not None
|
|
24
24
|
try:
|
|
25
|
-
logger.debug("Destination directory: %s",
|
|
25
|
+
logger.debug("Destination directory: %s", wheel_directory)
|
|
26
26
|
|
|
27
27
|
runner = runner_with_spinner_message(
|
|
28
28
|
f"Building wheel for {name} (pyproject.toml)"
|
|
29
29
|
)
|
|
30
30
|
with backend.subprocess_runner(runner):
|
|
31
31
|
wheel_name = backend.build_wheel(
|
|
32
|
-
|
|
32
|
+
wheel_directory=wheel_directory,
|
|
33
33
|
metadata_directory=metadata_directory,
|
|
34
34
|
)
|
|
35
35
|
except Exception:
|
|
36
36
|
logger.error("Failed building wheel for %s", name)
|
|
37
37
|
return None
|
|
38
|
-
return os.path.join(
|
|
38
|
+
return os.path.join(wheel_directory, wheel_name)
|
|
@@ -14,7 +14,7 @@ def build_wheel_editable(
|
|
|
14
14
|
name: str,
|
|
15
15
|
backend: BuildBackendHookCaller,
|
|
16
16
|
metadata_directory: str,
|
|
17
|
-
|
|
17
|
+
wheel_directory: str,
|
|
18
18
|
) -> str | None:
|
|
19
19
|
"""Build one InstallRequirement using the PEP 660 build process.
|
|
20
20
|
|
|
@@ -22,7 +22,7 @@ def build_wheel_editable(
|
|
|
22
22
|
"""
|
|
23
23
|
assert metadata_directory is not None
|
|
24
24
|
try:
|
|
25
|
-
logger.debug("Destination directory: %s",
|
|
25
|
+
logger.debug("Destination directory: %s", wheel_directory)
|
|
26
26
|
|
|
27
27
|
runner = runner_with_spinner_message(
|
|
28
28
|
f"Building editable for {name} (pyproject.toml)"
|
|
@@ -30,7 +30,7 @@ def build_wheel_editable(
|
|
|
30
30
|
with backend.subprocess_runner(runner):
|
|
31
31
|
try:
|
|
32
32
|
wheel_name = backend.build_editable(
|
|
33
|
-
|
|
33
|
+
wheel_directory=wheel_directory,
|
|
34
34
|
metadata_directory=metadata_directory,
|
|
35
35
|
)
|
|
36
36
|
except HookMissing as e:
|
|
@@ -44,4 +44,4 @@ def build_wheel_editable(
|
|
|
44
44
|
except Exception:
|
|
45
45
|
logger.error("Failed building editable for %s", name)
|
|
46
46
|
return None
|
|
47
|
-
return os.path.join(
|
|
47
|
+
return os.path.join(wheel_directory, wheel_name)
|
|
@@ -411,8 +411,7 @@ class PipScriptMaker(ScriptMaker):
|
|
|
411
411
|
import sys
|
|
412
412
|
from %(module)s import %(import_name)s
|
|
413
413
|
if __name__ == '__main__':
|
|
414
|
-
|
|
415
|
-
sys.argv[0] = sys.argv[0][:-4]
|
|
414
|
+
sys.argv[0] = sys.argv[0].removesuffix('.exe')
|
|
416
415
|
sys.exit(%(func)s())
|
|
417
416
|
"""
|
|
418
417
|
)
|
|
@@ -225,7 +225,7 @@ def _check_download_dir(
|
|
|
225
225
|
class RequirementPreparer:
|
|
226
226
|
"""Prepares a Requirement"""
|
|
227
227
|
|
|
228
|
-
def __init__(
|
|
228
|
+
def __init__(
|
|
229
229
|
self,
|
|
230
230
|
*,
|
|
231
231
|
build_dir: str,
|
|
@@ -243,7 +243,6 @@ class RequirementPreparer:
|
|
|
243
243
|
lazy_wheel: bool,
|
|
244
244
|
verbosity: int,
|
|
245
245
|
legacy_resolver: bool,
|
|
246
|
-
resume_retries: int,
|
|
247
246
|
) -> None:
|
|
248
247
|
super().__init__()
|
|
249
248
|
|
|
@@ -251,7 +250,7 @@ class RequirementPreparer:
|
|
|
251
250
|
self.build_dir = build_dir
|
|
252
251
|
self.build_tracker = build_tracker
|
|
253
252
|
self._session = session
|
|
254
|
-
self._download = Downloader(session, progress_bar
|
|
253
|
+
self._download = Downloader(session, progress_bar)
|
|
255
254
|
self.finder = finder
|
|
256
255
|
|
|
257
256
|
# Where still-packed archives should be written to. If None, they are
|
|
@@ -444,7 +443,7 @@ class RequirementPreparer:
|
|
|
444
443
|
return None
|
|
445
444
|
|
|
446
445
|
wheel = Wheel(link.filename)
|
|
447
|
-
name =
|
|
446
|
+
name = wheel.name
|
|
448
447
|
logger.info(
|
|
449
448
|
"Obtaining dependency information from %s %s",
|
|
450
449
|
name,
|
|
@@ -531,6 +530,12 @@ class RequirementPreparer:
|
|
|
531
530
|
metadata_dist = self._fetch_metadata_only(req)
|
|
532
531
|
if metadata_dist is not None:
|
|
533
532
|
req.needs_more_preparation = True
|
|
533
|
+
req.set_dist(metadata_dist)
|
|
534
|
+
# Ensure download_info is available even in dry-run mode
|
|
535
|
+
if req.download_info is None:
|
|
536
|
+
req.download_info = direct_url_from_link(
|
|
537
|
+
req.link, req.source_dir
|
|
538
|
+
)
|
|
534
539
|
return metadata_dist
|
|
535
540
|
|
|
536
541
|
# None of the optimizations worked, fully prepare the requirement
|