nab-python 0.0.1__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.
- nab_python/__init__.py +1 -0
- nab_python/_build/__init__.py +1 -0
- nab_python/_build/env.py +364 -0
- nab_python/_build/errors.py +17 -0
- nab_python/_build/runner.py +254 -0
- nab_python/_lockfile/__init__.py +1 -0
- nab_python/_lockfile/builder.py +339 -0
- nab_python/_lockfile/disjointness.py +207 -0
- nab_python/_lockfile/pylock.py +323 -0
- nab_python/_lockfile/requirements.py +121 -0
- nab_python/_packaging_provider.py +98 -0
- nab_python/_provider/__init__.py +1 -0
- nab_python/_provider/build_remote.py +95 -0
- nab_python/_provider/extras.py +231 -0
- nab_python/_provider/listing.py +442 -0
- nab_python/_provider/lookahead.py +156 -0
- nab_python/_provider/metadata_resolver.py +450 -0
- nab_python/_provider/priority.py +174 -0
- nab_python/_provider/sources.py +215 -0
- nab_python/_testing/__init__.py +1 -0
- nab_python/_testing/coordinator_fake.py +240 -0
- nab_python/_vcs_admission.py +209 -0
- nab_python/_vendor/__init__.py +6 -0
- nab_python/_vendor/packaging/LICENSE +3 -0
- nab_python/_vendor/packaging/LICENSE.APACHE +177 -0
- nab_python/_vendor/packaging/LICENSE.BSD +23 -0
- nab_python/_vendor/packaging/PROVENANCE.md +73 -0
- nab_python/_vendor/packaging/__init__.py +15 -0
- nab_python/_vendor/packaging/_elffile.py +108 -0
- nab_python/_vendor/packaging/_manylinux.py +265 -0
- nab_python/_vendor/packaging/_musllinux.py +88 -0
- nab_python/_vendor/packaging/_parser.py +394 -0
- nab_python/_vendor/packaging/_structures.py +33 -0
- nab_python/_vendor/packaging/_tokenizer.py +196 -0
- nab_python/_vendor/packaging/dependency_groups.py +302 -0
- nab_python/_vendor/packaging/direct_url.py +325 -0
- nab_python/_vendor/packaging/errors.py +94 -0
- nab_python/_vendor/packaging/licenses/__init__.py +186 -0
- nab_python/_vendor/packaging/licenses/_spdx.py +799 -0
- nab_python/_vendor/packaging/markers.py +506 -0
- nab_python/_vendor/packaging/metadata.py +964 -0
- nab_python/_vendor/packaging/py.typed +0 -0
- nab_python/_vendor/packaging/pylock.py +910 -0
- nab_python/_vendor/packaging/ranges.py +1803 -0
- nab_python/_vendor/packaging/requirements.py +132 -0
- nab_python/_vendor/packaging/specifiers.py +1141 -0
- nab_python/_vendor/packaging/tags.py +929 -0
- nab_python/_vendor/packaging/utils.py +296 -0
- nab_python/_vendor/packaging/version.py +1230 -0
- nab_python/build_backend.py +184 -0
- nab_python/config.py +805 -0
- nab_python/download.py +170 -0
- nab_python/fetch.py +827 -0
- nab_python/lockfile.py +238 -0
- nab_python/metadata.py +145 -0
- nab_python/provider.py +1235 -0
- nab_python/py.typed +0 -0
- nab_python/requirements_file.py +180 -0
- nab_python/resolve.py +497 -0
- nab_python/universal/__init__.py +1 -0
- nab_python/universal/matrix.py +235 -0
- nab_python/universal/provider.py +214 -0
- nab_python/universal/reresolve.py +310 -0
- nab_python/universal/resolve.py +508 -0
- nab_python/universal/validate.py +439 -0
- nab_python/universal/wheel_selection.py +327 -0
- nab_python/workspace.py +214 -0
- nab_python-0.0.1.dist-info/METADATA +49 -0
- nab_python-0.0.1.dist-info/RECORD +71 -0
- nab_python-0.0.1.dist-info/WHEEL +4 -0
- nab_python-0.0.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"""Re-resolve a tuple using its specific wheel's metadata.
|
|
2
|
+
|
|
3
|
+
When :mod:`validate` reports a ``divergent`` finding, the lock's pins
|
|
4
|
+
were chosen against the resolver's baseline metadata, not the
|
|
5
|
+
metadata of the wheel the tuple would actually install. This module
|
|
6
|
+
provides an opt-in second pass that re-resolves the affected tuple
|
|
7
|
+
with the wheel-specific metadata so the lock surfaces any new or
|
|
8
|
+
removed pins driven by the divergence.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from collections import defaultdict
|
|
14
|
+
from contextlib import contextmanager
|
|
15
|
+
from dataclasses import dataclass, field
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
17
|
+
|
|
18
|
+
from .._vendor.packaging.utils import canonicalize_name
|
|
19
|
+
from ..provider import (
|
|
20
|
+
BuildPolicy,
|
|
21
|
+
DistPolicy,
|
|
22
|
+
LocalSource,
|
|
23
|
+
VcsConfig,
|
|
24
|
+
VcsSource,
|
|
25
|
+
)
|
|
26
|
+
from .matrix import Matrix as _Matrix
|
|
27
|
+
from .resolve import resolve_with_coordinator
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
from collections.abc import Iterator, Mapping
|
|
31
|
+
from datetime import datetime
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
|
|
34
|
+
from ..config import NabProjectConfig
|
|
35
|
+
from ..fetch import FetchCoordinator
|
|
36
|
+
from .matrix import MatrixTuple
|
|
37
|
+
from .resolve import UniversalResult
|
|
38
|
+
from .validate import PinValidation, ValidationReport
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
__all__ = [
|
|
42
|
+
"ReresolveDiff",
|
|
43
|
+
"reresolve_divergent_tuples",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class ReresolveDiff:
|
|
49
|
+
"""Difference between the original and the wheel-aware re-resolve."""
|
|
50
|
+
|
|
51
|
+
tuple_label: str
|
|
52
|
+
added: dict[str, str] = field(default_factory=dict)
|
|
53
|
+
removed: dict[str, str] = field(default_factory=dict)
|
|
54
|
+
version_changed: dict[str, tuple[str, str]] = field(default_factory=dict)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def reresolve_divergent_tuples( # noqa: PLR0913 - mirrors the original resolve
|
|
58
|
+
coordinator: FetchCoordinator,
|
|
59
|
+
requirements: list[str],
|
|
60
|
+
original: UniversalResult,
|
|
61
|
+
report: ValidationReport,
|
|
62
|
+
*,
|
|
63
|
+
constraints: list[str] | None = None,
|
|
64
|
+
uploaded_prior_to: datetime | None = None,
|
|
65
|
+
uploaded_prior_to_overrides: Mapping[str, datetime | None] | None = None,
|
|
66
|
+
dist_policy: DistPolicy = DistPolicy.WHEEL_OR_SDIST,
|
|
67
|
+
dist_policy_overrides: Mapping[str, DistPolicy] | None = None,
|
|
68
|
+
build_policy: BuildPolicy = BuildPolicy.BUILD_LOCAL,
|
|
69
|
+
build_policy_overrides: Mapping[str, BuildPolicy] | None = None,
|
|
70
|
+
vcs_config: VcsConfig | None = None,
|
|
71
|
+
local_sources: list[LocalSource] | None = None,
|
|
72
|
+
vcs_sources: list[VcsSource] | None = None,
|
|
73
|
+
vcs_cache_dir: Path | None = None,
|
|
74
|
+
build_config: NabProjectConfig | None = None,
|
|
75
|
+
resolution_strategy: str = "highest",
|
|
76
|
+
) -> dict[str, ReresolveDiff]:
|
|
77
|
+
"""Re-resolve every tuple with at least one divergent pin.
|
|
78
|
+
|
|
79
|
+
Returns a mapping from tuple_label to its diff against the
|
|
80
|
+
original pins. Tuples with no divergent findings are skipped.
|
|
81
|
+
|
|
82
|
+
The resolve is run with the *same* configuration the original
|
|
83
|
+
matrix used (``uploaded_prior_to``, sdist/build policies,
|
|
84
|
+
resolution strategy). Mismatched options would compare apples
|
|
85
|
+
to oranges; passing them through keeps the diff meaningful.
|
|
86
|
+
"""
|
|
87
|
+
new_pins_per_tuple = _reresolve_one_step(
|
|
88
|
+
coordinator,
|
|
89
|
+
requirements,
|
|
90
|
+
original,
|
|
91
|
+
report,
|
|
92
|
+
constraints=constraints,
|
|
93
|
+
uploaded_prior_to=uploaded_prior_to,
|
|
94
|
+
uploaded_prior_to_overrides=uploaded_prior_to_overrides,
|
|
95
|
+
dist_policy=dist_policy,
|
|
96
|
+
dist_policy_overrides=dist_policy_overrides,
|
|
97
|
+
build_policy=build_policy,
|
|
98
|
+
build_policy_overrides=build_policy_overrides,
|
|
99
|
+
vcs_config=vcs_config,
|
|
100
|
+
local_sources=local_sources,
|
|
101
|
+
vcs_sources=vcs_sources,
|
|
102
|
+
vcs_cache_dir=vcs_cache_dir,
|
|
103
|
+
build_config=build_config,
|
|
104
|
+
resolution_strategy=resolution_strategy,
|
|
105
|
+
)
|
|
106
|
+
diffs: dict[str, ReresolveDiff] = {}
|
|
107
|
+
for tr in original.tuple_results:
|
|
108
|
+
if not tr.success:
|
|
109
|
+
continue
|
|
110
|
+
if tr.tuple_.label not in new_pins_per_tuple:
|
|
111
|
+
continue
|
|
112
|
+
original_pins = {k: str(v) for k, v in tr.pins.items()}
|
|
113
|
+
diffs[tr.tuple_.label] = _diff_pins(
|
|
114
|
+
tr.tuple_.label,
|
|
115
|
+
new_pins=new_pins_per_tuple[tr.tuple_.label],
|
|
116
|
+
original_pins=original_pins,
|
|
117
|
+
)
|
|
118
|
+
return diffs
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _reresolve_one_step( # noqa: PLR0913
|
|
122
|
+
coordinator: FetchCoordinator,
|
|
123
|
+
requirements: list[str],
|
|
124
|
+
current: UniversalResult,
|
|
125
|
+
report: ValidationReport,
|
|
126
|
+
*,
|
|
127
|
+
constraints: list[str] | None,
|
|
128
|
+
uploaded_prior_to: datetime | None,
|
|
129
|
+
uploaded_prior_to_overrides: Mapping[str, datetime | None] | None = None,
|
|
130
|
+
dist_policy: DistPolicy,
|
|
131
|
+
dist_policy_overrides: Mapping[str, DistPolicy] | None = None,
|
|
132
|
+
build_policy: BuildPolicy,
|
|
133
|
+
build_policy_overrides: Mapping[str, BuildPolicy] | None = None,
|
|
134
|
+
vcs_config: VcsConfig | None = None,
|
|
135
|
+
local_sources: list[LocalSource] | None = None,
|
|
136
|
+
vcs_sources: list[VcsSource] | None = None,
|
|
137
|
+
vcs_cache_dir: Path | None = None,
|
|
138
|
+
build_config: NabProjectConfig | None = None,
|
|
139
|
+
resolution_strategy: str,
|
|
140
|
+
) -> dict[str, dict[str, str]]:
|
|
141
|
+
"""Run one re-resolve sweep; return ``{tuple_label: {package: version}}``.
|
|
142
|
+
|
|
143
|
+
Tuples without a divergent finding are absent from the output. A
|
|
144
|
+
tuple whose re-resolve fails (returns no pins) IS present with an
|
|
145
|
+
empty value; callers represent that as "all pins removed", not
|
|
146
|
+
"no work attempted".
|
|
147
|
+
"""
|
|
148
|
+
by_tuple = _findings_by_tuple_label(report)
|
|
149
|
+
new_pins_per_tuple: dict[str, dict[str, str]] = {}
|
|
150
|
+
for tr in current.tuple_results:
|
|
151
|
+
if not tr.success:
|
|
152
|
+
continue
|
|
153
|
+
findings = by_tuple.get(tr.tuple_.label, [])
|
|
154
|
+
divergent = [f for f in findings if f.status == "divergent"]
|
|
155
|
+
if not divergent:
|
|
156
|
+
continue
|
|
157
|
+
wheel_metadata = _collect_wheel_metadata_overrides(coordinator, divergent)
|
|
158
|
+
if not wheel_metadata: # pragma: no cover - divergent always has metadata
|
|
159
|
+
continue
|
|
160
|
+
new_pins = _resolve_one_tuple_with_overrides(
|
|
161
|
+
coordinator,
|
|
162
|
+
tr.tuple_,
|
|
163
|
+
requirements,
|
|
164
|
+
wheel_metadata,
|
|
165
|
+
constraints=constraints,
|
|
166
|
+
uploaded_prior_to=uploaded_prior_to,
|
|
167
|
+
uploaded_prior_to_overrides=uploaded_prior_to_overrides,
|
|
168
|
+
dist_policy=dist_policy,
|
|
169
|
+
dist_policy_overrides=dist_policy_overrides,
|
|
170
|
+
build_policy=build_policy,
|
|
171
|
+
build_policy_overrides=build_policy_overrides,
|
|
172
|
+
vcs_config=vcs_config,
|
|
173
|
+
local_sources=local_sources,
|
|
174
|
+
vcs_sources=vcs_sources,
|
|
175
|
+
vcs_cache_dir=vcs_cache_dir,
|
|
176
|
+
build_config=build_config,
|
|
177
|
+
resolution_strategy=resolution_strategy,
|
|
178
|
+
)
|
|
179
|
+
new_pins_per_tuple[tr.tuple_.label] = new_pins
|
|
180
|
+
return new_pins_per_tuple
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _resolve_one_tuple_with_overrides( # noqa: PLR0913
|
|
184
|
+
coordinator: FetchCoordinator,
|
|
185
|
+
tup: MatrixTuple,
|
|
186
|
+
requirements: list[str],
|
|
187
|
+
wheel_metadata: dict[tuple[str, str], str],
|
|
188
|
+
*,
|
|
189
|
+
constraints: list[str] | None,
|
|
190
|
+
uploaded_prior_to: datetime | None,
|
|
191
|
+
uploaded_prior_to_overrides: Mapping[str, datetime | None] | None = None,
|
|
192
|
+
dist_policy: DistPolicy,
|
|
193
|
+
dist_policy_overrides: Mapping[str, DistPolicy] | None = None,
|
|
194
|
+
build_policy: BuildPolicy,
|
|
195
|
+
build_policy_overrides: Mapping[str, BuildPolicy] | None = None,
|
|
196
|
+
vcs_config: VcsConfig | None = None,
|
|
197
|
+
local_sources: list[LocalSource] | None = None,
|
|
198
|
+
vcs_sources: list[VcsSource] | None = None,
|
|
199
|
+
vcs_cache_dir: Path | None = None,
|
|
200
|
+
build_config: NabProjectConfig | None = None,
|
|
201
|
+
resolution_strategy: str,
|
|
202
|
+
) -> dict[str, str]:
|
|
203
|
+
"""Re-resolve ``tup`` with the given metadata overrides; return pins."""
|
|
204
|
+
one_tuple_matrix = _Matrix(
|
|
205
|
+
python=f"=={tup.python_version}",
|
|
206
|
+
platforms=(tup.platform_spec,),
|
|
207
|
+
)
|
|
208
|
+
with _override_metadata(coordinator, wheel_metadata):
|
|
209
|
+
result = resolve_with_coordinator(
|
|
210
|
+
coordinator,
|
|
211
|
+
one_tuple_matrix,
|
|
212
|
+
requirements,
|
|
213
|
+
constraints=constraints,
|
|
214
|
+
uploaded_prior_to=uploaded_prior_to,
|
|
215
|
+
uploaded_prior_to_overrides=uploaded_prior_to_overrides,
|
|
216
|
+
dist_policy=dist_policy,
|
|
217
|
+
dist_policy_overrides=dist_policy_overrides,
|
|
218
|
+
build_policy=build_policy,
|
|
219
|
+
build_policy_overrides=build_policy_overrides,
|
|
220
|
+
vcs_config=vcs_config,
|
|
221
|
+
local_sources=local_sources,
|
|
222
|
+
vcs_sources=vcs_sources,
|
|
223
|
+
vcs_cache_dir=vcs_cache_dir,
|
|
224
|
+
build_config=build_config,
|
|
225
|
+
resolution_strategy=resolution_strategy,
|
|
226
|
+
)
|
|
227
|
+
if not result.success:
|
|
228
|
+
return {}
|
|
229
|
+
return {k: str(v) for k, v in result.tuple_results[0].pins.items()}
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _findings_by_tuple_label(
|
|
233
|
+
report: ValidationReport,
|
|
234
|
+
) -> dict[str, list[PinValidation]]:
|
|
235
|
+
"""Group validation findings by tuple label."""
|
|
236
|
+
out: defaultdict[str, list[PinValidation]] = defaultdict(list)
|
|
237
|
+
for f in report.findings:
|
|
238
|
+
out[f.tuple_label].append(f)
|
|
239
|
+
return out
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _collect_wheel_metadata_overrides(
|
|
243
|
+
coordinator: FetchCoordinator,
|
|
244
|
+
findings: list[PinValidation],
|
|
245
|
+
) -> dict[tuple[str, str], str]:
|
|
246
|
+
"""For each divergent finding, fetch the chosen wheel's metadata text."""
|
|
247
|
+
out: dict[tuple[str, str], str] = {}
|
|
248
|
+
for f in findings:
|
|
249
|
+
if f.chosen_wheel is None: # pragma: no cover
|
|
250
|
+
continue
|
|
251
|
+
normalized = canonicalize_name(f.package)
|
|
252
|
+
text = coordinator.index.get_metadata(
|
|
253
|
+
normalized, f"{f.version}#{f.chosen_wheel}"
|
|
254
|
+
)
|
|
255
|
+
if text is not None:
|
|
256
|
+
out[(normalized, f.version)] = text
|
|
257
|
+
return out
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _diff_pins(
|
|
261
|
+
tuple_label: str,
|
|
262
|
+
*,
|
|
263
|
+
new_pins: dict[str, str],
|
|
264
|
+
original_pins: dict[str, str] | None = None,
|
|
265
|
+
) -> ReresolveDiff:
|
|
266
|
+
"""Compute (added, removed, version_changed) between two pin sets."""
|
|
267
|
+
diff = ReresolveDiff(tuple_label=tuple_label)
|
|
268
|
+
if original_pins is None:
|
|
269
|
+
diff.added = dict(new_pins)
|
|
270
|
+
return diff
|
|
271
|
+
for name, new_ver in new_pins.items():
|
|
272
|
+
if name not in original_pins:
|
|
273
|
+
diff.added[name] = new_ver
|
|
274
|
+
elif original_pins[name] != new_ver:
|
|
275
|
+
diff.version_changed[name] = (original_pins[name], new_ver)
|
|
276
|
+
for name, old_ver in original_pins.items():
|
|
277
|
+
if name not in new_pins:
|
|
278
|
+
diff.removed[name] = old_ver
|
|
279
|
+
return diff
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
@contextmanager
|
|
283
|
+
def _override_metadata(
|
|
284
|
+
coordinator: FetchCoordinator,
|
|
285
|
+
overrides: dict[tuple[str, str], str],
|
|
286
|
+
) -> Iterator[None]:
|
|
287
|
+
"""Snapshot, override, and restore baseline metadata for one re-resolve.
|
|
288
|
+
|
|
289
|
+
Stores ``overrides`` at the ``(name, version)`` key the provider
|
|
290
|
+
reads, then restores the previous values on exit. Also evicts
|
|
291
|
+
the matching entries from the coordinator's parsed-metadata
|
|
292
|
+
cache so a subsequent ``get_parsed_metadata`` re-parses the
|
|
293
|
+
overridden raw text rather than serving the prior tuple's view.
|
|
294
|
+
"""
|
|
295
|
+
text_snapshot: dict[tuple[str, str], str | None] = {}
|
|
296
|
+
parsed_snapshot: dict[tuple[str, str], object | None] = {}
|
|
297
|
+
for key in overrides:
|
|
298
|
+
text_snapshot[key] = coordinator.index.get_metadata(*key)
|
|
299
|
+
parsed_snapshot[key] = coordinator.index.pop_parsed_metadata(*key)
|
|
300
|
+
try:
|
|
301
|
+
for key, text in overrides.items():
|
|
302
|
+
coordinator.index.store_metadata(*key, text)
|
|
303
|
+
yield
|
|
304
|
+
finally:
|
|
305
|
+
for key, prior in text_snapshot.items():
|
|
306
|
+
coordinator.index.store_metadata(*key, prior)
|
|
307
|
+
for key, parsed in parsed_snapshot.items():
|
|
308
|
+
coordinator.index.pop_parsed_metadata(*key)
|
|
309
|
+
if parsed is not None:
|
|
310
|
+
coordinator.index.store_parsed_metadata(*key, parsed)
|