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..1c6a13b 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..424a686 100644 --- a/tests/test_index.py +++ b/tests/test_index.py @@ -133,6 +133,24 @@ 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"