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
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import dataclasses
|
|
4
|
+
import logging
|
|
5
|
+
import re
|
|
6
|
+
from collections.abc import Mapping, Sequence
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from typing import (
|
|
10
|
+
TYPE_CHECKING,
|
|
11
|
+
Any,
|
|
12
|
+
Callable,
|
|
13
|
+
Protocol,
|
|
14
|
+
TypeVar,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from .markers import Marker
|
|
18
|
+
from .specifiers import SpecifierSet
|
|
19
|
+
from .utils import NormalizedName, is_normalized_name
|
|
20
|
+
from .version import Version
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
from typing_extensions import Self
|
|
26
|
+
|
|
27
|
+
_logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"Package",
|
|
31
|
+
"PackageArchive",
|
|
32
|
+
"PackageDirectory",
|
|
33
|
+
"PackageSdist",
|
|
34
|
+
"PackageVcs",
|
|
35
|
+
"PackageWheel",
|
|
36
|
+
"Pylock",
|
|
37
|
+
"PylockUnsupportedVersionError",
|
|
38
|
+
"PylockValidationError",
|
|
39
|
+
"is_valid_pylock_path",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
_T = TypeVar("_T")
|
|
43
|
+
_T2 = TypeVar("_T2")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class _FromMappingProtocol(Protocol): # pragma: no cover
|
|
47
|
+
@classmethod
|
|
48
|
+
def _from_dict(cls, d: Mapping[str, Any]) -> Self: ...
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
_FromMappingProtocolT = TypeVar("_FromMappingProtocolT", bound=_FromMappingProtocol)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
_PYLOCK_FILE_NAME_RE = re.compile(r"^pylock\.([^.]+)\.toml$")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def is_valid_pylock_path(path: Path) -> bool:
|
|
58
|
+
"""Check if the given path is a valid pylock file path."""
|
|
59
|
+
return path.name == "pylock.toml" or bool(_PYLOCK_FILE_NAME_RE.match(path.name))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _toml_key(key: str) -> str:
|
|
63
|
+
return key.replace("_", "-")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _toml_value(key: str, value: Any) -> Any: # noqa: ANN401
|
|
67
|
+
if isinstance(value, (Version, Marker, SpecifierSet)):
|
|
68
|
+
return str(value)
|
|
69
|
+
if isinstance(value, Sequence) and key == "environments":
|
|
70
|
+
return [str(v) for v in value]
|
|
71
|
+
return value
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _toml_dict_factory(data: list[tuple[str, Any]]) -> dict[str, Any]:
|
|
75
|
+
return {
|
|
76
|
+
_toml_key(key): _toml_value(key, value)
|
|
77
|
+
for key, value in data
|
|
78
|
+
if value is not None
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _get(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T | None:
|
|
83
|
+
"""Get a value from the dictionary and verify it's the expected type."""
|
|
84
|
+
if (value := d.get(key)) is None:
|
|
85
|
+
return None
|
|
86
|
+
if not isinstance(value, expected_type):
|
|
87
|
+
raise PylockValidationError(
|
|
88
|
+
f"Unexpected type {type(value).__name__} "
|
|
89
|
+
f"(expected {expected_type.__name__})",
|
|
90
|
+
context=key,
|
|
91
|
+
)
|
|
92
|
+
return value
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _get_required(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T:
|
|
96
|
+
"""Get a required value from the dictionary and verify it's the expected type."""
|
|
97
|
+
if (value := _get(d, expected_type, key)) is None:
|
|
98
|
+
raise _PylockRequiredKeyError(key)
|
|
99
|
+
return value
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _get_sequence(
|
|
103
|
+
d: Mapping[str, Any], expected_item_type: type[_T], key: str
|
|
104
|
+
) -> Sequence[_T] | None:
|
|
105
|
+
"""Get a list value from the dictionary and verify it's the expected items type."""
|
|
106
|
+
if (value := _get(d, Sequence, key)) is None: # type: ignore[type-abstract]
|
|
107
|
+
return None
|
|
108
|
+
if isinstance(value, (str, bytes)):
|
|
109
|
+
# special case: str and bytes are Sequences, but we want to reject it
|
|
110
|
+
raise PylockValidationError(
|
|
111
|
+
f"Unexpected type {type(value).__name__} (expected Sequence)",
|
|
112
|
+
context=key,
|
|
113
|
+
)
|
|
114
|
+
for i, item in enumerate(value):
|
|
115
|
+
if not isinstance(item, expected_item_type):
|
|
116
|
+
raise PylockValidationError(
|
|
117
|
+
f"Unexpected type {type(item).__name__} "
|
|
118
|
+
f"(expected {expected_item_type.__name__})",
|
|
119
|
+
context=f"{key}[{i}]",
|
|
120
|
+
)
|
|
121
|
+
return value
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _get_as(
|
|
125
|
+
d: Mapping[str, Any],
|
|
126
|
+
expected_type: type[_T],
|
|
127
|
+
target_type: Callable[[_T], _T2],
|
|
128
|
+
key: str,
|
|
129
|
+
) -> _T2 | None:
|
|
130
|
+
"""Get a value from the dictionary, verify it's the expected type,
|
|
131
|
+
and convert to the target type.
|
|
132
|
+
|
|
133
|
+
This assumes the target_type constructor accepts the value.
|
|
134
|
+
"""
|
|
135
|
+
if (value := _get(d, expected_type, key)) is None:
|
|
136
|
+
return None
|
|
137
|
+
try:
|
|
138
|
+
return target_type(value)
|
|
139
|
+
except Exception as e:
|
|
140
|
+
raise PylockValidationError(e, context=key) from e
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _get_required_as(
|
|
144
|
+
d: Mapping[str, Any],
|
|
145
|
+
expected_type: type[_T],
|
|
146
|
+
target_type: Callable[[_T], _T2],
|
|
147
|
+
key: str,
|
|
148
|
+
) -> _T2:
|
|
149
|
+
"""Get a required value from the dict, verify it's the expected type,
|
|
150
|
+
and convert to the target type."""
|
|
151
|
+
if (value := _get_as(d, expected_type, target_type, key)) is None:
|
|
152
|
+
raise _PylockRequiredKeyError(key)
|
|
153
|
+
return value
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _get_sequence_as(
|
|
157
|
+
d: Mapping[str, Any],
|
|
158
|
+
expected_item_type: type[_T],
|
|
159
|
+
target_item_type: Callable[[_T], _T2],
|
|
160
|
+
key: str,
|
|
161
|
+
) -> list[_T2] | None:
|
|
162
|
+
"""Get list value from dictionary and verify expected items type."""
|
|
163
|
+
if (value := _get_sequence(d, expected_item_type, key)) is None:
|
|
164
|
+
return None
|
|
165
|
+
result = []
|
|
166
|
+
try:
|
|
167
|
+
for item in value:
|
|
168
|
+
typed_item = target_item_type(item)
|
|
169
|
+
result.append(typed_item)
|
|
170
|
+
except Exception as e:
|
|
171
|
+
raise PylockValidationError(e, context=f"{key}[{len(result)}]") from e
|
|
172
|
+
return result
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _get_object(
|
|
176
|
+
d: Mapping[str, Any], target_type: type[_FromMappingProtocolT], key: str
|
|
177
|
+
) -> _FromMappingProtocolT | None:
|
|
178
|
+
"""Get a dictionary value from the dictionary and convert it to a dataclass."""
|
|
179
|
+
if (value := _get(d, Mapping, key)) is None: # type: ignore[type-abstract]
|
|
180
|
+
return None
|
|
181
|
+
try:
|
|
182
|
+
return target_type._from_dict(value)
|
|
183
|
+
except Exception as e:
|
|
184
|
+
raise PylockValidationError(e, context=key) from e
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _get_sequence_of_objects(
|
|
188
|
+
d: Mapping[str, Any], target_item_type: type[_FromMappingProtocolT], key: str
|
|
189
|
+
) -> list[_FromMappingProtocolT] | None:
|
|
190
|
+
"""Get a list value from the dictionary and convert its items to a dataclass."""
|
|
191
|
+
if (value := _get_sequence(d, Mapping, key)) is None: # type: ignore[type-abstract]
|
|
192
|
+
return None
|
|
193
|
+
result: list[_FromMappingProtocolT] = []
|
|
194
|
+
try:
|
|
195
|
+
for item in value:
|
|
196
|
+
typed_item = target_item_type._from_dict(item)
|
|
197
|
+
result.append(typed_item)
|
|
198
|
+
except Exception as e:
|
|
199
|
+
raise PylockValidationError(e, context=f"{key}[{len(result)}]") from e
|
|
200
|
+
return result
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _get_required_sequence_of_objects(
|
|
204
|
+
d: Mapping[str, Any], target_item_type: type[_FromMappingProtocolT], key: str
|
|
205
|
+
) -> Sequence[_FromMappingProtocolT]:
|
|
206
|
+
"""Get a required list value from the dictionary and convert its items to a
|
|
207
|
+
dataclass."""
|
|
208
|
+
if (result := _get_sequence_of_objects(d, target_item_type, key)) is None:
|
|
209
|
+
raise _PylockRequiredKeyError(key)
|
|
210
|
+
return result
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _validate_normalized_name(name: str) -> NormalizedName:
|
|
214
|
+
"""Validate that a string is a NormalizedName."""
|
|
215
|
+
if not is_normalized_name(name):
|
|
216
|
+
raise PylockValidationError(f"Name {name!r} is not normalized")
|
|
217
|
+
return NormalizedName(name)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _validate_path_url(path: str | None, url: str | None) -> None:
|
|
221
|
+
if not path and not url:
|
|
222
|
+
raise PylockValidationError("path or url must be provided")
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _validate_hashes(hashes: Mapping[str, Any]) -> Mapping[str, Any]:
|
|
226
|
+
if not hashes:
|
|
227
|
+
raise PylockValidationError("At least one hash must be provided")
|
|
228
|
+
if not all(isinstance(hash_val, str) for hash_val in hashes.values()):
|
|
229
|
+
raise PylockValidationError("Hash values must be strings")
|
|
230
|
+
return hashes
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class PylockValidationError(Exception):
|
|
234
|
+
"""Raised when when input data is not spec-compliant."""
|
|
235
|
+
|
|
236
|
+
context: str | None = None
|
|
237
|
+
message: str
|
|
238
|
+
|
|
239
|
+
def __init__(
|
|
240
|
+
self,
|
|
241
|
+
cause: str | Exception,
|
|
242
|
+
*,
|
|
243
|
+
context: str | None = None,
|
|
244
|
+
) -> None:
|
|
245
|
+
if isinstance(cause, PylockValidationError):
|
|
246
|
+
if cause.context:
|
|
247
|
+
self.context = (
|
|
248
|
+
f"{context}.{cause.context}" if context else cause.context
|
|
249
|
+
)
|
|
250
|
+
else:
|
|
251
|
+
self.context = context
|
|
252
|
+
self.message = cause.message
|
|
253
|
+
else:
|
|
254
|
+
self.context = context
|
|
255
|
+
self.message = str(cause)
|
|
256
|
+
|
|
257
|
+
def __str__(self) -> str:
|
|
258
|
+
if self.context:
|
|
259
|
+
return f"{self.message} in {self.context!r}"
|
|
260
|
+
return self.message
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
class _PylockRequiredKeyError(PylockValidationError):
|
|
264
|
+
def __init__(self, key: str) -> None:
|
|
265
|
+
super().__init__("Missing required value", context=key)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
class PylockUnsupportedVersionError(PylockValidationError):
|
|
269
|
+
"""Raised when encountering an unsupported `lock_version`."""
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
@dataclass(frozen=True, init=False)
|
|
273
|
+
class PackageVcs:
|
|
274
|
+
type: str
|
|
275
|
+
url: str | None = None
|
|
276
|
+
path: str | None = None
|
|
277
|
+
requested_revision: str | None = None
|
|
278
|
+
commit_id: str # type: ignore[misc]
|
|
279
|
+
subdirectory: str | None = None
|
|
280
|
+
|
|
281
|
+
def __init__(
|
|
282
|
+
self,
|
|
283
|
+
*,
|
|
284
|
+
type: str,
|
|
285
|
+
url: str | None = None,
|
|
286
|
+
path: str | None = None,
|
|
287
|
+
requested_revision: str | None = None,
|
|
288
|
+
commit_id: str,
|
|
289
|
+
subdirectory: str | None = None,
|
|
290
|
+
) -> None:
|
|
291
|
+
# In Python 3.10+ make dataclass kw_only=True and remove __init__
|
|
292
|
+
object.__setattr__(self, "type", type)
|
|
293
|
+
object.__setattr__(self, "url", url)
|
|
294
|
+
object.__setattr__(self, "path", path)
|
|
295
|
+
object.__setattr__(self, "requested_revision", requested_revision)
|
|
296
|
+
object.__setattr__(self, "commit_id", commit_id)
|
|
297
|
+
object.__setattr__(self, "subdirectory", subdirectory)
|
|
298
|
+
|
|
299
|
+
@classmethod
|
|
300
|
+
def _from_dict(cls, d: Mapping[str, Any]) -> Self:
|
|
301
|
+
package_vcs = cls(
|
|
302
|
+
type=_get_required(d, str, "type"),
|
|
303
|
+
url=_get(d, str, "url"),
|
|
304
|
+
path=_get(d, str, "path"),
|
|
305
|
+
requested_revision=_get(d, str, "requested-revision"),
|
|
306
|
+
commit_id=_get_required(d, str, "commit-id"),
|
|
307
|
+
subdirectory=_get(d, str, "subdirectory"),
|
|
308
|
+
)
|
|
309
|
+
_validate_path_url(package_vcs.path, package_vcs.url)
|
|
310
|
+
return package_vcs
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
@dataclass(frozen=True, init=False)
|
|
314
|
+
class PackageDirectory:
|
|
315
|
+
path: str
|
|
316
|
+
editable: bool | None = None
|
|
317
|
+
subdirectory: str | None = None
|
|
318
|
+
|
|
319
|
+
def __init__(
|
|
320
|
+
self,
|
|
321
|
+
*,
|
|
322
|
+
path: str,
|
|
323
|
+
editable: bool | None = None,
|
|
324
|
+
subdirectory: str | None = None,
|
|
325
|
+
) -> None:
|
|
326
|
+
# In Python 3.10+ make dataclass kw_only=True and remove __init__
|
|
327
|
+
object.__setattr__(self, "path", path)
|
|
328
|
+
object.__setattr__(self, "editable", editable)
|
|
329
|
+
object.__setattr__(self, "subdirectory", subdirectory)
|
|
330
|
+
|
|
331
|
+
@classmethod
|
|
332
|
+
def _from_dict(cls, d: Mapping[str, Any]) -> Self:
|
|
333
|
+
return cls(
|
|
334
|
+
path=_get_required(d, str, "path"),
|
|
335
|
+
editable=_get(d, bool, "editable"),
|
|
336
|
+
subdirectory=_get(d, str, "subdirectory"),
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
@dataclass(frozen=True, init=False)
|
|
341
|
+
class PackageArchive:
|
|
342
|
+
url: str | None = None
|
|
343
|
+
path: str | None = None
|
|
344
|
+
size: int | None = None
|
|
345
|
+
upload_time: datetime | None = None
|
|
346
|
+
hashes: Mapping[str, str] # type: ignore[misc]
|
|
347
|
+
subdirectory: str | None = None
|
|
348
|
+
|
|
349
|
+
def __init__(
|
|
350
|
+
self,
|
|
351
|
+
*,
|
|
352
|
+
url: str | None = None,
|
|
353
|
+
path: str | None = None,
|
|
354
|
+
size: int | None = None,
|
|
355
|
+
upload_time: datetime | None = None,
|
|
356
|
+
hashes: Mapping[str, str],
|
|
357
|
+
subdirectory: str | None = None,
|
|
358
|
+
) -> None:
|
|
359
|
+
# In Python 3.10+ make dataclass kw_only=True and remove __init__
|
|
360
|
+
object.__setattr__(self, "url", url)
|
|
361
|
+
object.__setattr__(self, "path", path)
|
|
362
|
+
object.__setattr__(self, "size", size)
|
|
363
|
+
object.__setattr__(self, "upload_time", upload_time)
|
|
364
|
+
object.__setattr__(self, "hashes", hashes)
|
|
365
|
+
object.__setattr__(self, "subdirectory", subdirectory)
|
|
366
|
+
|
|
367
|
+
@classmethod
|
|
368
|
+
def _from_dict(cls, d: Mapping[str, Any]) -> Self:
|
|
369
|
+
package_archive = cls(
|
|
370
|
+
url=_get(d, str, "url"),
|
|
371
|
+
path=_get(d, str, "path"),
|
|
372
|
+
size=_get(d, int, "size"),
|
|
373
|
+
upload_time=_get(d, datetime, "upload-time"),
|
|
374
|
+
hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract]
|
|
375
|
+
subdirectory=_get(d, str, "subdirectory"),
|
|
376
|
+
)
|
|
377
|
+
_validate_path_url(package_archive.path, package_archive.url)
|
|
378
|
+
return package_archive
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
@dataclass(frozen=True, init=False)
|
|
382
|
+
class PackageSdist:
|
|
383
|
+
name: str | None = None
|
|
384
|
+
upload_time: datetime | None = None
|
|
385
|
+
url: str | None = None
|
|
386
|
+
path: str | None = None
|
|
387
|
+
size: int | None = None
|
|
388
|
+
hashes: Mapping[str, str] # type: ignore[misc]
|
|
389
|
+
|
|
390
|
+
def __init__(
|
|
391
|
+
self,
|
|
392
|
+
*,
|
|
393
|
+
name: str | None = None,
|
|
394
|
+
upload_time: datetime | None = None,
|
|
395
|
+
url: str | None = None,
|
|
396
|
+
path: str | None = None,
|
|
397
|
+
size: int | None = None,
|
|
398
|
+
hashes: Mapping[str, str],
|
|
399
|
+
) -> None:
|
|
400
|
+
# In Python 3.10+ make dataclass kw_only=True and remove __init__
|
|
401
|
+
object.__setattr__(self, "name", name)
|
|
402
|
+
object.__setattr__(self, "upload_time", upload_time)
|
|
403
|
+
object.__setattr__(self, "url", url)
|
|
404
|
+
object.__setattr__(self, "path", path)
|
|
405
|
+
object.__setattr__(self, "size", size)
|
|
406
|
+
object.__setattr__(self, "hashes", hashes)
|
|
407
|
+
|
|
408
|
+
@classmethod
|
|
409
|
+
def _from_dict(cls, d: Mapping[str, Any]) -> Self:
|
|
410
|
+
package_sdist = cls(
|
|
411
|
+
name=_get(d, str, "name"),
|
|
412
|
+
upload_time=_get(d, datetime, "upload-time"),
|
|
413
|
+
url=_get(d, str, "url"),
|
|
414
|
+
path=_get(d, str, "path"),
|
|
415
|
+
size=_get(d, int, "size"),
|
|
416
|
+
hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract]
|
|
417
|
+
)
|
|
418
|
+
_validate_path_url(package_sdist.path, package_sdist.url)
|
|
419
|
+
return package_sdist
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
@dataclass(frozen=True, init=False)
|
|
423
|
+
class PackageWheel:
|
|
424
|
+
name: str | None = None
|
|
425
|
+
upload_time: datetime | None = None
|
|
426
|
+
url: str | None = None
|
|
427
|
+
path: str | None = None
|
|
428
|
+
size: int | None = None
|
|
429
|
+
hashes: Mapping[str, str] # type: ignore[misc]
|
|
430
|
+
|
|
431
|
+
def __init__(
|
|
432
|
+
self,
|
|
433
|
+
*,
|
|
434
|
+
name: str | None = None,
|
|
435
|
+
upload_time: datetime | None = None,
|
|
436
|
+
url: str | None = None,
|
|
437
|
+
path: str | None = None,
|
|
438
|
+
size: int | None = None,
|
|
439
|
+
hashes: Mapping[str, str],
|
|
440
|
+
) -> None:
|
|
441
|
+
# In Python 3.10+ make dataclass kw_only=True and remove __init__
|
|
442
|
+
object.__setattr__(self, "name", name)
|
|
443
|
+
object.__setattr__(self, "upload_time", upload_time)
|
|
444
|
+
object.__setattr__(self, "url", url)
|
|
445
|
+
object.__setattr__(self, "path", path)
|
|
446
|
+
object.__setattr__(self, "size", size)
|
|
447
|
+
object.__setattr__(self, "hashes", hashes)
|
|
448
|
+
|
|
449
|
+
@classmethod
|
|
450
|
+
def _from_dict(cls, d: Mapping[str, Any]) -> Self:
|
|
451
|
+
package_wheel = cls(
|
|
452
|
+
name=_get(d, str, "name"),
|
|
453
|
+
upload_time=_get(d, datetime, "upload-time"),
|
|
454
|
+
url=_get(d, str, "url"),
|
|
455
|
+
path=_get(d, str, "path"),
|
|
456
|
+
size=_get(d, int, "size"),
|
|
457
|
+
hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract]
|
|
458
|
+
)
|
|
459
|
+
_validate_path_url(package_wheel.path, package_wheel.url)
|
|
460
|
+
return package_wheel
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
@dataclass(frozen=True, init=False)
|
|
464
|
+
class Package:
|
|
465
|
+
name: NormalizedName
|
|
466
|
+
version: Version | None = None
|
|
467
|
+
marker: Marker | None = None
|
|
468
|
+
requires_python: SpecifierSet | None = None
|
|
469
|
+
dependencies: Sequence[Mapping[str, Any]] | None = None
|
|
470
|
+
vcs: PackageVcs | None = None
|
|
471
|
+
directory: PackageDirectory | None = None
|
|
472
|
+
archive: PackageArchive | None = None
|
|
473
|
+
index: str | None = None
|
|
474
|
+
sdist: PackageSdist | None = None
|
|
475
|
+
wheels: Sequence[PackageWheel] | None = None
|
|
476
|
+
attestation_identities: Sequence[Mapping[str, Any]] | None = None
|
|
477
|
+
tool: Mapping[str, Any] | None = None
|
|
478
|
+
|
|
479
|
+
def __init__(
|
|
480
|
+
self,
|
|
481
|
+
*,
|
|
482
|
+
name: NormalizedName,
|
|
483
|
+
version: Version | None = None,
|
|
484
|
+
marker: Marker | None = None,
|
|
485
|
+
requires_python: SpecifierSet | None = None,
|
|
486
|
+
dependencies: Sequence[Mapping[str, Any]] | None = None,
|
|
487
|
+
vcs: PackageVcs | None = None,
|
|
488
|
+
directory: PackageDirectory | None = None,
|
|
489
|
+
archive: PackageArchive | None = None,
|
|
490
|
+
index: str | None = None,
|
|
491
|
+
sdist: PackageSdist | None = None,
|
|
492
|
+
wheels: Sequence[PackageWheel] | None = None,
|
|
493
|
+
attestation_identities: Sequence[Mapping[str, Any]] | None = None,
|
|
494
|
+
tool: Mapping[str, Any] | None = None,
|
|
495
|
+
) -> None:
|
|
496
|
+
# In Python 3.10+ make dataclass kw_only=True and remove __init__
|
|
497
|
+
object.__setattr__(self, "name", name)
|
|
498
|
+
object.__setattr__(self, "version", version)
|
|
499
|
+
object.__setattr__(self, "marker", marker)
|
|
500
|
+
object.__setattr__(self, "requires_python", requires_python)
|
|
501
|
+
object.__setattr__(self, "dependencies", dependencies)
|
|
502
|
+
object.__setattr__(self, "vcs", vcs)
|
|
503
|
+
object.__setattr__(self, "directory", directory)
|
|
504
|
+
object.__setattr__(self, "archive", archive)
|
|
505
|
+
object.__setattr__(self, "index", index)
|
|
506
|
+
object.__setattr__(self, "sdist", sdist)
|
|
507
|
+
object.__setattr__(self, "wheels", wheels)
|
|
508
|
+
object.__setattr__(self, "attestation_identities", attestation_identities)
|
|
509
|
+
object.__setattr__(self, "tool", tool)
|
|
510
|
+
|
|
511
|
+
@classmethod
|
|
512
|
+
def _from_dict(cls, d: Mapping[str, Any]) -> Self:
|
|
513
|
+
package = cls(
|
|
514
|
+
name=_get_required_as(d, str, _validate_normalized_name, "name"),
|
|
515
|
+
version=_get_as(d, str, Version, "version"),
|
|
516
|
+
requires_python=_get_as(d, str, SpecifierSet, "requires-python"),
|
|
517
|
+
dependencies=_get_sequence(d, Mapping, "dependencies"), # type: ignore[type-abstract]
|
|
518
|
+
marker=_get_as(d, str, Marker, "marker"),
|
|
519
|
+
vcs=_get_object(d, PackageVcs, "vcs"),
|
|
520
|
+
directory=_get_object(d, PackageDirectory, "directory"),
|
|
521
|
+
archive=_get_object(d, PackageArchive, "archive"),
|
|
522
|
+
index=_get(d, str, "index"),
|
|
523
|
+
sdist=_get_object(d, PackageSdist, "sdist"),
|
|
524
|
+
wheels=_get_sequence_of_objects(d, PackageWheel, "wheels"),
|
|
525
|
+
attestation_identities=_get_sequence(d, Mapping, "attestation-identities"), # type: ignore[type-abstract]
|
|
526
|
+
tool=_get(d, Mapping, "tool"), # type: ignore[type-abstract]
|
|
527
|
+
)
|
|
528
|
+
distributions = bool(package.sdist) + len(package.wheels or [])
|
|
529
|
+
direct_urls = (
|
|
530
|
+
bool(package.vcs) + bool(package.directory) + bool(package.archive)
|
|
531
|
+
)
|
|
532
|
+
if distributions > 0 and direct_urls > 0:
|
|
533
|
+
raise PylockValidationError(
|
|
534
|
+
"None of vcs, directory, archive must be set if sdist or wheels are set"
|
|
535
|
+
)
|
|
536
|
+
if distributions == 0 and direct_urls != 1:
|
|
537
|
+
raise PylockValidationError(
|
|
538
|
+
"Exactly one of vcs, directory, archive must be set "
|
|
539
|
+
"if sdist and wheels are not set"
|
|
540
|
+
)
|
|
541
|
+
try:
|
|
542
|
+
for i, attestation_identity in enumerate( # noqa: B007
|
|
543
|
+
package.attestation_identities or []
|
|
544
|
+
):
|
|
545
|
+
_get_required(attestation_identity, str, "kind")
|
|
546
|
+
except Exception as e:
|
|
547
|
+
raise PylockValidationError(
|
|
548
|
+
e, context=f"attestation-identities[{i}]"
|
|
549
|
+
) from e
|
|
550
|
+
return package
|
|
551
|
+
|
|
552
|
+
@property
|
|
553
|
+
def is_direct(self) -> bool:
|
|
554
|
+
return not (self.sdist or self.wheels)
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
@dataclass(frozen=True, init=False)
|
|
558
|
+
class Pylock:
|
|
559
|
+
"""A class representing a pylock file."""
|
|
560
|
+
|
|
561
|
+
lock_version: Version
|
|
562
|
+
environments: Sequence[Marker] | None = None
|
|
563
|
+
requires_python: SpecifierSet | None = None
|
|
564
|
+
extras: Sequence[NormalizedName] | None = None
|
|
565
|
+
dependency_groups: Sequence[str] | None = None
|
|
566
|
+
default_groups: Sequence[str] | None = None
|
|
567
|
+
created_by: str # type: ignore[misc]
|
|
568
|
+
packages: Sequence[Package] # type: ignore[misc]
|
|
569
|
+
tool: Mapping[str, Any] | None = None
|
|
570
|
+
|
|
571
|
+
def __init__(
|
|
572
|
+
self,
|
|
573
|
+
*,
|
|
574
|
+
lock_version: Version,
|
|
575
|
+
environments: Sequence[Marker] | None = None,
|
|
576
|
+
requires_python: SpecifierSet | None = None,
|
|
577
|
+
extras: Sequence[NormalizedName] | None = None,
|
|
578
|
+
dependency_groups: Sequence[str] | None = None,
|
|
579
|
+
default_groups: Sequence[str] | None = None,
|
|
580
|
+
created_by: str,
|
|
581
|
+
packages: Sequence[Package],
|
|
582
|
+
tool: Mapping[str, Any] | None = None,
|
|
583
|
+
) -> None:
|
|
584
|
+
# In Python 3.10+ make dataclass kw_only=True and remove __init__
|
|
585
|
+
object.__setattr__(self, "lock_version", lock_version)
|
|
586
|
+
object.__setattr__(self, "environments", environments)
|
|
587
|
+
object.__setattr__(self, "requires_python", requires_python)
|
|
588
|
+
object.__setattr__(self, "extras", extras)
|
|
589
|
+
object.__setattr__(self, "dependency_groups", dependency_groups)
|
|
590
|
+
object.__setattr__(self, "default_groups", default_groups)
|
|
591
|
+
object.__setattr__(self, "created_by", created_by)
|
|
592
|
+
object.__setattr__(self, "packages", packages)
|
|
593
|
+
object.__setattr__(self, "tool", tool)
|
|
594
|
+
|
|
595
|
+
@classmethod
|
|
596
|
+
def _from_dict(cls, d: Mapping[str, Any]) -> Self:
|
|
597
|
+
pylock = cls(
|
|
598
|
+
lock_version=_get_required_as(d, str, Version, "lock-version"),
|
|
599
|
+
environments=_get_sequence_as(d, str, Marker, "environments"),
|
|
600
|
+
extras=_get_sequence_as(d, str, _validate_normalized_name, "extras"),
|
|
601
|
+
dependency_groups=_get_sequence(d, str, "dependency-groups"),
|
|
602
|
+
default_groups=_get_sequence(d, str, "default-groups"),
|
|
603
|
+
created_by=_get_required(d, str, "created-by"),
|
|
604
|
+
requires_python=_get_as(d, str, SpecifierSet, "requires-python"),
|
|
605
|
+
packages=_get_required_sequence_of_objects(d, Package, "packages"),
|
|
606
|
+
tool=_get(d, Mapping, "tool"), # type: ignore[type-abstract]
|
|
607
|
+
)
|
|
608
|
+
if not Version("1") <= pylock.lock_version < Version("2"):
|
|
609
|
+
raise PylockUnsupportedVersionError(
|
|
610
|
+
f"pylock version {pylock.lock_version} is not supported"
|
|
611
|
+
)
|
|
612
|
+
if pylock.lock_version > Version("1.0"):
|
|
613
|
+
_logger.warning(
|
|
614
|
+
"pylock minor version %s is not supported", pylock.lock_version
|
|
615
|
+
)
|
|
616
|
+
return pylock
|
|
617
|
+
|
|
618
|
+
@classmethod
|
|
619
|
+
def from_dict(cls, d: Mapping[str, Any], /) -> Self:
|
|
620
|
+
"""Create and validate a Pylock instance from a TOML dictionary.
|
|
621
|
+
|
|
622
|
+
Raises :class:`PylockValidationError` if the input data is not
|
|
623
|
+
spec-compliant.
|
|
624
|
+
"""
|
|
625
|
+
return cls._from_dict(d)
|
|
626
|
+
|
|
627
|
+
def to_dict(self) -> Mapping[str, Any]:
|
|
628
|
+
"""Convert the Pylock instance to a TOML dictionary."""
|
|
629
|
+
return dataclasses.asdict(self, dict_factory=_toml_dict_factory)
|
|
630
|
+
|
|
631
|
+
def validate(self) -> None:
|
|
632
|
+
"""Validate the Pylock instance against the specification.
|
|
633
|
+
|
|
634
|
+
Raises :class:`PylockValidationError` otherwise."""
|
|
635
|
+
self.from_dict(self.to_dict())
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# for complete details.
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
-
from typing import
|
|
6
|
+
from typing import Iterator
|
|
7
7
|
|
|
8
8
|
from ._parser import parse_requirement as _parse_requirement
|
|
9
9
|
from ._tokenizer import ParserSyntaxError
|
|
@@ -57,7 +57,7 @@ class Requirement:
|
|
|
57
57
|
yield str(self.specifier)
|
|
58
58
|
|
|
59
59
|
if self.url:
|
|
60
|
-
yield f"@ {self.url}"
|
|
60
|
+
yield f" @ {self.url}"
|
|
61
61
|
if self.marker:
|
|
62
62
|
yield " "
|
|
63
63
|
|
|
@@ -68,17 +68,12 @@ class Requirement:
|
|
|
68
68
|
return "".join(self._iter_parts(self.name))
|
|
69
69
|
|
|
70
70
|
def __repr__(self) -> str:
|
|
71
|
-
return f"<
|
|
71
|
+
return f"<{self.__class__.__name__}('{self}')>"
|
|
72
72
|
|
|
73
73
|
def __hash__(self) -> int:
|
|
74
|
-
return hash(
|
|
75
|
-
(
|
|
76
|
-
self.__class__.__name__,
|
|
77
|
-
*self._iter_parts(canonicalize_name(self.name)),
|
|
78
|
-
)
|
|
79
|
-
)
|
|
74
|
+
return hash(tuple(self._iter_parts(canonicalize_name(self.name))))
|
|
80
75
|
|
|
81
|
-
def __eq__(self, other:
|
|
76
|
+
def __eq__(self, other: object) -> bool:
|
|
82
77
|
if not isinstance(other, Requirement):
|
|
83
78
|
return NotImplemented
|
|
84
79
|
|