Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<wheel>.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
Expand Down
58 changes: 54 additions & 4 deletions docs/usage.md
Original file line number Diff line number Diff line change
@@ -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 `<name>/index.html` listing every wheel for that project.
- A `<wheel>.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 `<wheel-filename>.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.
10 changes: 8 additions & 2 deletions src/index_503/wheel_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
18 changes: 18 additions & 0 deletions tests/test_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -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="&gt;=3.7,&lt;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"
Expand Down