nab-python 0.0.1__tar.gz → 0.0.2__tar.gz

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.
Files changed (71) hide show
  1. {nab_python-0.0.1 → nab_python-0.0.2}/PKG-INFO +3 -3
  2. {nab_python-0.0.1 → nab_python-0.0.2}/pyproject.toml +3 -3
  3. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_build/env.py +7 -3
  4. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_lockfile/builder.py +29 -11
  5. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_provider/sources.py +7 -0
  6. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vcs_admission.py +9 -33
  7. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/download.py +29 -22
  8. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/fetch.py +1 -0
  9. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/provider.py +9 -0
  10. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/universal/resolve.py +10 -2
  11. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/universal/wheel_selection.py +4 -3
  12. {nab_python-0.0.1 → nab_python-0.0.2}/LICENSE +0 -0
  13. {nab_python-0.0.1 → nab_python-0.0.2}/README.md +0 -0
  14. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/__init__.py +0 -0
  15. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_build/__init__.py +0 -0
  16. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_build/errors.py +0 -0
  17. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_build/runner.py +0 -0
  18. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_lockfile/__init__.py +0 -0
  19. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_lockfile/disjointness.py +0 -0
  20. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_lockfile/pylock.py +0 -0
  21. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_lockfile/requirements.py +0 -0
  22. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_packaging_provider.py +0 -0
  23. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_provider/__init__.py +0 -0
  24. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_provider/build_remote.py +0 -0
  25. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_provider/extras.py +0 -0
  26. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_provider/listing.py +0 -0
  27. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_provider/lookahead.py +0 -0
  28. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_provider/metadata_resolver.py +0 -0
  29. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_provider/priority.py +0 -0
  30. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_testing/__init__.py +0 -0
  31. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_testing/coordinator_fake.py +0 -0
  32. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/__init__.py +0 -0
  33. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/LICENSE +0 -0
  34. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/LICENSE.APACHE +0 -0
  35. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/LICENSE.BSD +0 -0
  36. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/PROVENANCE.md +0 -0
  37. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/__init__.py +0 -0
  38. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/_elffile.py +0 -0
  39. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/_manylinux.py +0 -0
  40. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/_musllinux.py +0 -0
  41. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/_parser.py +0 -0
  42. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/_structures.py +0 -0
  43. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/_tokenizer.py +0 -0
  44. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/dependency_groups.py +0 -0
  45. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/direct_url.py +0 -0
  46. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/errors.py +0 -0
  47. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/licenses/__init__.py +0 -0
  48. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/licenses/_spdx.py +0 -0
  49. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/markers.py +0 -0
  50. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/metadata.py +0 -0
  51. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/py.typed +0 -0
  52. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/pylock.py +0 -0
  53. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/ranges.py +0 -0
  54. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/requirements.py +0 -0
  55. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/specifiers.py +0 -0
  56. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/tags.py +0 -0
  57. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/utils.py +0 -0
  58. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/_vendor/packaging/version.py +0 -0
  59. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/build_backend.py +0 -0
  60. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/config.py +0 -0
  61. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/lockfile.py +0 -0
  62. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/metadata.py +0 -0
  63. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/py.typed +0 -0
  64. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/requirements_file.py +0 -0
  65. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/resolve.py +0 -0
  66. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/universal/__init__.py +0 -0
  67. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/universal/matrix.py +0 -0
  68. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/universal/provider.py +0 -0
  69. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/universal/reresolve.py +0 -0
  70. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/universal/validate.py +0 -0
  71. {nab_python-0.0.1 → nab_python-0.0.2}/src/nab_python/workspace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nab-python
3
- Version: 0.0.1
3
+ Version: 0.0.2
4
4
  Summary: Index-backed provider, lockfile emitter, and downloader for nab
5
5
  Project-URL: Homepage, https://github.com/notatallshaw/nab
6
6
  Project-URL: Documentation, https://nab.readthedocs.io/
@@ -20,8 +20,8 @@ Classifier: Typing :: Typed
20
20
  Requires-Python: >=3.10
21
21
  Requires-Dist: build>=1.2
22
22
  Requires-Dist: installer>=0.7
23
- Requires-Dist: nab-index==0.0.1
24
- Requires-Dist: nab-resolver==0.0.1
23
+ Requires-Dist: nab-index==0.0.2
24
+ Requires-Dist: nab-resolver==0.0.2
25
25
  Requires-Dist: pyproject-hooks>=1.2
26
26
  Requires-Dist: tomli-w>=1.2
27
27
  Requires-Dist: tomli>=2.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nab-python"
3
- version = "0.0.1"
3
+ version = "0.0.2"
4
4
  description = "Index-backed provider, lockfile emitter, and downloader for nab"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -19,8 +19,8 @@ classifiers = [
19
19
  "Typing :: Typed",
20
20
  ]
21
21
  dependencies = [
22
- "nab-resolver==0.0.1",
23
- "nab-index==0.0.1",
22
+ "nab-resolver==0.0.2",
23
+ "nab-index==0.0.2",
24
24
  "tomli>=2.0",
25
25
  "tomli_w>=1.2",
26
26
  "build>=1.2",
@@ -117,7 +117,7 @@ class NabBuildEnv:
117
117
  self._requires = list(requires)
118
118
  self._config = config
119
119
  self._python_version = python_version
120
- self._transport = transport_factory()
120
+ self._transport_factory = transport_factory
121
121
 
122
122
  self._tmpdir: tempfile.TemporaryDirectory[str] | None = None
123
123
  self._venv_path: Path | None = None
@@ -273,9 +273,13 @@ class NabBuildEnv:
273
273
  uploaded_prior_to=self._config.uploaded_prior_to,
274
274
  uploaded_prior_to_overrides=self._config.uploaded_prior_to_overrides,
275
275
  )
276
+ # download_lock closes its transport, and ``install`` may call
277
+ # this again for ``get_requires_for_build_wheel`` follow-ups;
278
+ # build a fresh transport each time.
279
+ transport = self._transport_factory()
276
280
  result = resolve_pyproject(
277
281
  synthetic,
278
- self._transport,
282
+ transport,
279
283
  config=inner_config,
280
284
  python_version=self._python_version,
281
285
  )
@@ -298,7 +302,7 @@ class NabBuildEnv:
298
302
  )
299
303
  raise BuildEnvError(msg)
300
304
 
301
- download_result = download_lock(result.lock_input, self._transport, wheel_dir)
305
+ download_result = download_lock(result.lock_input, transport, wheel_dir)
302
306
  # Both wheels and sdists are downloaded; only wheels feed
303
307
  # ``installer.install``. The sdists are inert clutter under
304
308
  # the temp dir, cleaned up with the env.
@@ -81,6 +81,10 @@ class LockInputProvider(Protocol):
81
81
  """Return the configured VcsSource for ``canonical_name`` or None."""
82
82
  ...
83
83
 
84
+ def vcs_pin_for(self, canonical_name: str, /) -> str | None:
85
+ """Return the resolved 40-char SHA captured during materialisation."""
86
+ ...
87
+
84
88
  def dist_files_for(
85
89
  self, canonical_name: str, version: Version, /
86
90
  ) -> list[WheelFile | SdistFile]:
@@ -176,7 +180,12 @@ def build_lock_input_from_provider(
176
180
  continue
177
181
  vcs_source = provider.vcs_source_for(canonical)
178
182
  if vcs_source is not None:
179
- lock_pins[canonical] = _vcs_pin_from_source(canonical, version, vcs_source)
183
+ lock_pins[canonical] = _vcs_pin_from_source(
184
+ canonical,
185
+ version,
186
+ vcs_source,
187
+ resolved_sha=provider.vcs_pin_for(canonical),
188
+ )
180
189
  continue
181
190
  lock_pins[canonical] = _index_pin_from_listing(
182
191
  provider, canonical, version, indexes
@@ -314,23 +323,32 @@ def _common_requires_python(files: Iterable[WheelFile | SdistFile]) -> str | Non
314
323
  return None
315
324
 
316
325
 
317
- def _vcs_pin_from_source(canonical: str, version: Version, source: VcsSource) -> VcsPin:
326
+ def _vcs_pin_from_source(
327
+ canonical: str,
328
+ version: Version,
329
+ source: VcsSource,
330
+ *,
331
+ resolved_sha: str | None,
332
+ ) -> VcsPin:
318
333
  """Build a :class:`VcsPin` from a :class:`VcsSource`.
319
334
 
320
- The commit id is the ``@<ref>`` portion of the source URL when
321
- present, parsed via :class:`nab_index.vcs.VcsRequest`. Unparseable
322
- URLs fall through to an empty commit id; the source URL itself is
323
- recorded verbatim.
335
+ Prefer ``resolved_sha`` (the post-clone SHA recorded on the
336
+ provider) over the URL's ``@<ref>``: annotated tags and floating
337
+ refs only resolve to a commit after the clone runs. Fall back to
338
+ the URL ref when the source was never materialised.
324
339
  """
325
340
  from nab_index.vcs import VcsCloneError, VcsRequest
326
341
 
327
342
  from ..lockfile import VcsPin
328
343
 
329
- try:
330
- parsed = VcsRequest.parse(source.url)
331
- commit_id = parsed.ref
332
- except VcsCloneError:
333
- commit_id = ""
344
+ if resolved_sha is not None:
345
+ commit_id = resolved_sha
346
+ else:
347
+ try:
348
+ parsed = VcsRequest.parse(source.url)
349
+ commit_id = parsed.ref
350
+ except VcsCloneError:
351
+ commit_id = ""
334
352
  return VcsPin(
335
353
  name=canonical,
336
354
  version=str(version),
@@ -151,8 +151,13 @@ def index_vcs_sources(
151
151
  :func:`extract_source_metadata`). ``VcsPolicy.BLOCK`` still refuses
152
152
  any declaration up-front because that is an independent decision
153
153
  about whether VCS fetching is permitted at all.
154
+
155
+ Each URL is passed through :func:`admit_vcs_url` so the scheme,
156
+ repo, and pin allowlists apply to ``[[tool.nab.vcs-sources]]``
157
+ just like project-root direct-URL requirements.
154
158
  """
155
159
  # Late import: ``pypi`` imports this module at module load.
160
+ from .._vcs_admission import admit_vcs_url
156
161
  from ..provider import VcsPolicy
157
162
 
158
163
  if not sources:
@@ -168,6 +173,7 @@ def index_vcs_sources(
168
173
 
169
174
  out: dict[str, VcsSource] = {}
170
175
  for src in sources:
176
+ admit_vcs_url(src.url, provider.vcs_config)
171
177
  canonical = canonicalize_name(src.name)
172
178
  if canonical in out or canonical in provider.local_sources:
173
179
  msg = f"duplicate source declared for {src.name!r}"
@@ -204,6 +210,7 @@ def materialize_vcs_source(
204
210
  except VcsCloneError as exc:
205
211
  msg = f"vcs source {source.name!r}: {exc}"
206
212
  raise UnsupportedSdistError(msg) from exc
213
+ provider.vcs_pins[canonicalize_name(source.name)] = clone.commit_sha
207
214
  path = clone.path / clone.subdirectory if clone.subdirectory else clone.path
208
215
  metadata = extract_source_metadata(
209
216
  provider,
@@ -77,49 +77,26 @@ _VCS_SCHEMES: frozenset[str] = frozenset(
77
77
  "git+http",
78
78
  "git+file",
79
79
  "git+git",
80
- "git",
81
- "hg+https",
82
- "hg+ssh",
83
- "hg+http",
84
- "hg+file",
85
- "hg+static-http",
86
- "bzr+https",
87
- "bzr+ssh",
88
- "bzr+sftp",
89
- "bzr+ftp",
90
- "bzr+lp",
91
- "bzr+file",
92
- "svn",
93
- "svn+https",
94
- "svn+ssh",
95
- "svn+http",
96
- "svn+file",
97
80
  }
98
81
  )
99
82
 
100
- _VCS_INSECURE_SCHEMES: frozenset[str] = frozenset(
101
- {"git", "git+git", "git+http", "hg+http", "bzr+http", "svn", "svn+http"}
102
- )
83
+ _VCS_INSECURE_SCHEMES: frozenset[str] = frozenset({"git+git", "git+http"})
103
84
 
104
85
 
105
86
  def split_vcs_scheme(url: str) -> tuple[str | None, str]:
106
87
  """Strip a recognized VCS scheme prefix.
107
88
 
108
89
  ``"git+https://example.com/r.git@v1"`` -> ``("git+https", "https://example.com/r.git@v1")``.
109
- ``"svn://example.com/r"`` -> ``("svn", "svn://example.com/r")``.
110
90
  ``"https://example.com/file.whl"`` -> ``(None, "https://example.com/file.whl")``.
111
91
 
112
92
  Returns ``(None, url)`` for non-VCS URLs (e.g. plain ``https://``
113
93
  archives or ``file://`` paths) so the caller can refuse them
114
- separately. Pip-compatible scheme list; not standardized by any PEP.
94
+ separately. Only ``git+`` schemes are recognised; ``hg+``/``bzr+``/``svn+``
95
+ are intentionally absent so they are refused as non-VCS.
115
96
  """
116
97
  for vcs_scheme in _VCS_SCHEMES:
117
- if not url.startswith(f"{vcs_scheme}://"):
118
- continue
119
- inner_scheme, plus, _ = vcs_scheme.partition("+")
120
- if not plus:
121
- return (vcs_scheme, url)
122
- return (vcs_scheme, url[len(inner_scheme) + 1 :])
98
+ if url.startswith(f"{vcs_scheme}://"):
99
+ return (vcs_scheme, url[len("git+") :])
123
100
  return (None, url)
124
101
 
125
102
 
@@ -128,9 +105,7 @@ def has_full_commit_sha(url: str) -> bool:
128
105
 
129
106
  Looks for ``@<sha>`` after the scheme://host portion; ignores any
130
107
  ``#`` fragment. Tolerates ``user@host`` syntax by taking the last
131
- ``@`` in the path/ref portion. Mercurial uses 40-char SHA1 too, so
132
- the same regex covers git+hg. Bzr/svn revisions are not hashes;
133
- ``vcs_require_pin = False`` is the way to allow those.
108
+ ``@`` in the path/ref portion.
134
109
  """
135
110
  fragmentless = url.split("#", 1)[0]
136
111
  after_authority = fragmentless.split("://", 1)[-1]
@@ -152,8 +127,9 @@ def admit_vcs_url(url: str, config: VcsConfig) -> str:
152
127
  msg = (
153
128
  "refusing direct-URL requirement (not a recognized VCS scheme)\n"
154
129
  f" {url}\n"
155
- " note: nab supports git+/hg+/bzr+/svn+ schemes only;"
156
- " plain http(s)/file archive URLs are not supported."
130
+ " note: nab supports git+https / git+ssh / git+http /"
131
+ " git+file / git+git only; hg/bzr/svn and plain"
132
+ " http(s)/file archive URLs are not supported."
157
133
  )
158
134
  raise UnsupportedVcsError(msg)
159
135
 
@@ -136,29 +136,36 @@ async def _run_downloads(
136
136
  client = AsyncSimpleClient(transport)
137
137
  written: list[Path] = []
138
138
  skipped: list[Path] = []
139
- try:
140
139
 
141
- async def _one(entry: DownloadEntry) -> None:
142
- async with sem:
143
- target = output_dir / entry.filename
144
- if _already_present(target, entry.hash_algo, entry.digest):
145
- skipped.append(target)
146
- logger.info("skip %s (%s matches)", entry.filename, entry.hash_algo)
147
- return
148
- data = await client.download(entry.url)
149
- actual = hashlib.new(entry.hash_algo, data).hexdigest()
150
- if actual != entry.digest:
151
- msg = (
152
- f"{entry.package}=={entry.version}: {entry.hash_algo}"
153
- f" mismatch for {entry.filename}\n"
154
- f" expected: {entry.digest}\n actual: {actual}"
155
- )
156
- raise DownloadError(msg)
157
- target.write_bytes(data)
158
- written.append(target)
159
- logger.info("wrote %s", target)
160
-
161
- await asyncio.gather(*(_one(a) for a in artefacts))
140
+ async def _one(entry: DownloadEntry) -> None:
141
+ async with sem:
142
+ target = output_dir / entry.filename
143
+ if _already_present(target, entry.hash_algo, entry.digest):
144
+ skipped.append(target)
145
+ logger.info("skip %s (%s matches)", entry.filename, entry.hash_algo)
146
+ return
147
+ data = await client.download(entry.url)
148
+ actual = hashlib.new(entry.hash_algo, data).hexdigest()
149
+ if actual != entry.digest:
150
+ msg = (
151
+ f"{entry.package}=={entry.version}: {entry.hash_algo}"
152
+ f" mismatch for {entry.filename}\n"
153
+ f" expected: {entry.digest}\n actual: {actual}"
154
+ )
155
+ raise DownloadError(msg)
156
+ target.write_bytes(data)
157
+ written.append(target)
158
+ logger.info("wrote %s", target)
159
+
160
+ tasks = [asyncio.create_task(_one(a)) for a in artefacts]
161
+ try:
162
+ await asyncio.gather(*tasks)
163
+ except BaseException:
164
+ for t in tasks:
165
+ if not t.done():
166
+ t.cancel()
167
+ await asyncio.gather(*tasks, return_exceptions=True)
168
+ raise
162
169
  finally:
163
170
  await client.aclose()
164
171
  return DownloadResult(written=tuple(written), skipped=tuple(skipped))
@@ -740,6 +740,7 @@ class FetchCoordinator:
740
740
  # transitive dep 404s or fails any other way.
741
741
  if req.kind is FetchKind.LISTING:
742
742
  self.index.store_listing(req.package, [])
743
+ self._record_serving_index(client, req.package)
743
744
  else:
744
745
  assert req.version is not None
745
746
  if req.kind is FetchKind.METADATA:
@@ -425,6 +425,7 @@ class Provider:
425
425
  self.vcs_config = vcs_config or VcsConfig()
426
426
  self.local_sources = _sources.index_local_sources(self, local_sources or [])
427
427
  self.vcs_cache_dir = vcs_cache_dir
428
+ self.vcs_pins: dict[str, str] = {}
428
429
  self.vcs_sources = _sources.index_vcs_sources(self, vcs_sources or [])
429
430
 
430
431
  # default_environment() returns a TypedDict whose ``.items()`` view
@@ -1223,6 +1224,14 @@ class Provider:
1223
1224
  """Return the VCS source registered under ``canonical_name`` or None."""
1224
1225
  return self.vcs_sources.get(canonicalize_name(canonical_name))
1225
1226
 
1227
+ def vcs_pin_for(self, canonical_name: str) -> str | None:
1228
+ """Return the post-clone commit SHA for ``canonical_name``, or None.
1229
+
1230
+ Written by :func:`~nab_python._provider.sources.materialize_vcs_source`
1231
+ after the shallow clone resolves the ref to a 40-char SHA.
1232
+ """
1233
+ return self.vcs_pins.get(canonicalize_name(canonical_name))
1234
+
1226
1235
  def dist_files_for(self, canonical_name: str, version: Version) -> list[DistFile]:
1227
1236
  """Return every distribution file the resolver saw at ``version``.
1228
1237
 
@@ -495,8 +495,16 @@ def _resolve_one_tuple( # noqa: PLR0913
495
495
  lock_input = build_lock_input_from_provider(
496
496
  provider, pins, indexes=coordinator.indexes
497
497
  )
498
- except MissingHashError:
499
- lock_input = None
498
+ except MissingHashError as exc:
499
+ return TupleResult(
500
+ tuple_=t,
501
+ success=False,
502
+ pins=pins,
503
+ error=f"MissingHashError: {exc}"[:_ERROR_MESSAGE_LIMIT],
504
+ wall_time=elapsed,
505
+ rounds=resolver.stats.rounds,
506
+ decisions=resolver.stats.decisions,
507
+ )
500
508
  return TupleResult(
501
509
  tuple_=t,
502
510
  success=True,
@@ -131,11 +131,12 @@ def _linux_platform_tags(
131
131
  out = [f"manylinux_{major}_{m}_{arch}" for m in range(minor, -1, -1)]
132
132
  # Legacy aliases (PEPs 513/571/599). These map to specific
133
133
  # glibc versions: manylinux1=2.5, manylinux2010=2.12,
134
- # manylinux2014=2.17. We include them when they're <= floor.
134
+ # manylinux2014=2.17. Listed highest-glibc first so the output
135
+ # stays in install-preference order alongside the PEP 600 forms.
135
136
  legacy_aliases = [
136
- ("manylinux1", (2, 5)),
137
- ("manylinux2010", (2, 12)),
138
137
  ("manylinux2014", (2, 17)),
138
+ ("manylinux2010", (2, 12)),
139
+ ("manylinux1", (2, 5)),
139
140
  ]
140
141
  out.extend(
141
142
  f"{name}_{arch}" for name, lver in legacy_aliases if lver <= manylinux_floor
File without changes
File without changes