From bd78d17be8e84c6f45134bd7691eeadadb756004 Mon Sep 17 00:00:00 2001 From: aiolibsbot Date: Sat, 16 May 2026 23:18:22 +0000 Subject: [PATCH 1/2] fix(index): always emit PEP-658/PEP-714 metadata attributes; doc them The anchor builder used `elif self.metadata_hash is not None`, so any wheel that declared `Requires-Python` (i.e. virtually every modern wheel) had its `data-dist-info-metadata` attribute suppressed even though the `.metadata` sidecar was being written to disk. Switch to a second `if` so both attributes can be emitted together, and add the PEP-714 alias `data-core-metadata` next to the legacy `data-dist-info-metadata`. README + docs now mention PEP-658/PEP-714 output as a first-class feature (closes #53). Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 10 +++++++ docs/usage.md | 58 ++++++++++++++++++++++++++++++++++--- src/index_503/wheel_file.py | 10 +++++-- tests/test_index.py | 20 +++++++++++++ 4 files changed, 92 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8fe1d6e..9482316 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,16 @@ Running this again will replace the original index and delete the old index in a A lock will be held in the parent directory to prevent concurrent executions. +## Features + +- **PEP 503** simple repository layout (per-project `index.html` pages). +- **PEP 658 / PEP 714** core metadata sidecars: each wheel gets a `.metadata` + file alongside it, and the anchor tag advertises it via both `data-core-metadata` + (PEP 714) and `data-dist-info-metadata` (PEP 658) so installers like pip can + fetch dependency metadata without downloading the wheel. +- Wheel `Requires-Python` is exposed via `data-requires-python`. +- Cache (`cache.json`) avoids re-reading unchanged wheels on subsequent runs. + ## Example For image builds diff --git a/docs/usage.md b/docs/usage.md index 08e834d..8d5e890 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -1,9 +1,59 @@ # Usage -To use this package, import it: +`index-503` generates a [PEP 503](https://peps.python.org/pep-0503/) "simple" +repository index from a directory of wheel files. -```python -import index_503 +## Command line + +Point `index-503` at a directory that contains `.whl` files: + +```bash +index-503 musllinux +``` + +This produces a sibling directory `musllinux-index/` containing: + +- A top-level `index.html` listing every project found. +- A per-project `/index.html` listing every wheel for that project. +- A `.metadata` sidecar for every wheel (see *Generated metadata* below). +- A `cache.json` used to skip unchanged wheels on subsequent runs. + +The wheels themselves are exposed via a symlink, so the original wheel +directory is never modified. A re-run swaps the index atomically and a +lock in the parent directory prevents concurrent executions. + +## Installing from the index + +For image builds (single index): + +```bash +pip install --only-binary=:all: \ + --index-url "https://wheels.example.org/musllinux-index/" \ + -r requirements.txt +``` + +For runtime installs that should fall back to PyPI: + +```bash +pip install --only-binary=:all: \ + --extra-index-url "https://wheels.example.org/musllinux-index/" \ + -r requirements.txt ``` -TODO: Document usage +`pip` 23.2 or newer is required — see +[pypa/pip#12038](https://github.com/pypa/pip/issues/12038). + +## Generated metadata + +`index-503` produces more than the bare PEP 503 layout — every wheel gets a +[PEP 658](https://peps.python.org/pep-0658/) / +[PEP 714](https://peps.python.org/pep-0714/) core-metadata sidecar: + +- The wheel's `METADATA` file is extracted to `.metadata`. +- The anchor tag for each wheel advertises that sidecar with both + `data-core-metadata="sha256=..."` (PEP 714, current) and + `data-dist-info-metadata="sha256=..."` (PEP 658, legacy alias). +- The wheel's `Requires-Python` value is exposed via `data-requires-python`. + +This lets resolvers like `pip` fetch dependency metadata without downloading +the full wheel, which makes resolution dramatically faster on slow links. diff --git a/src/index_503/wheel_file.py b/src/index_503/wheel_file.py index 4c1253e..aadb8df 100644 --- a/src/index_503/wheel_file.py +++ b/src/index_503/wheel_file.py @@ -99,8 +99,14 @@ def as_anchor(self, page: Airium, base_url: Union[str, URL] = "/") -> None: if self.requires_python is not None: kwargs["data-requires-python"] = escape(self.requires_python) - elif self.metadata_hash is not None: - kwargs["data-dist-info-metadata"] = f"{HASH_FORMAT}={self.metadata_hash}" + if self.metadata_hash is not None: + metadata_value = ( + "true" + if self.metadata_hash is True + else f"{HASH_FORMAT}={self.metadata_hash}" + ) + kwargs["data-core-metadata"] = metadata_value + kwargs["data-dist-info-metadata"] = metadata_value with page.a(**kwargs): page(posixpath.basename(self.filename)) diff --git a/tests/test_index.py b/tests/test_index.py index e6bdaef..0dec389 100644 --- a/tests/test_index.py +++ b/tests/test_index.py @@ -133,6 +133,26 @@ def test_make_index_end_to_end(tmp_path: Path) -> None: 'data-dist-info-metadata="sha256=a6e73c9cf4f9469c5b308830afbc000bb806df5d894598dd499737e94974c27c"' in co2signal_index_html ) + # PEP-714 alias for the same metadata hash. + assert ( + 'data-core-metadata="sha256=a6e73c9cf4f9469c5b308830afbc000bb806df5d894598dd499737e94974c27c"' + in co2signal_index_html + ) + + # Regression: a wheel that declares Requires-Python must still + # advertise its PEP-658 / PEP-714 metadata sidecar. + bleak_index_html = origin_path_index.joinpath( + "bleak", "index.html" + ).read_text() + assert 'data-requires-python=">=3.7,<4.0"' in bleak_index_html + assert ( + 'data-dist-info-metadata="sha256=b826a4a16ef36e8a2165b16cec9b46d2956930a66046e977a499a418388e33d1"' + in bleak_index_html + ) + assert ( + 'data-core-metadata="sha256=b826a4a16ef36e8a2165b16cec9b46d2956930a66046e977a499a418388e33d1"' + in bleak_index_html + ) bleak_metadata_path = origin_path_index.joinpath( "bleak-0.17.0-py3-none-any.whl.metadata" From 46d2a2b32e2bf1634891ec6d6a961a83085f38b8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 16 May 2026 23:20:17 +0000 Subject: [PATCH 2/2] chore(pre-commit.ci): auto fixes --- docs/usage.md | 2 +- tests/test_index.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 8d5e890..1c6a13b 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -15,7 +15,7 @@ This produces a sibling directory `musllinux-index/` containing: - A top-level `index.html` listing every project found. - A per-project `/index.html` listing every wheel for that project. -- A `.metadata` sidecar for every wheel (see *Generated metadata* below). +- A `.metadata` sidecar for every wheel (see _Generated metadata_ below). - A `cache.json` used to skip unchanged wheels on subsequent runs. The wheels themselves are exposed via a symlink, so the original wheel diff --git a/tests/test_index.py b/tests/test_index.py index 0dec389..424a686 100644 --- a/tests/test_index.py +++ b/tests/test_index.py @@ -141,9 +141,7 @@ def test_make_index_end_to_end(tmp_path: Path) -> None: # Regression: a wheel that declares Requires-Python must still # advertise its PEP-658 / PEP-714 metadata sidecar. - bleak_index_html = origin_path_index.joinpath( - "bleak", "index.html" - ).read_text() + bleak_index_html = origin_path_index.joinpath("bleak", "index.html").read_text() assert 'data-requires-python=">=3.7,<4.0"' in bleak_index_html assert ( 'data-dist-info-metadata="sha256=b826a4a16ef36e8a2165b16cec9b46d2956930a66046e977a499a418388e33d1"'