Skip to main content

Incremental PyPI Simple API caching mirror

Project description

PyTail

CI Release PyPI Python

pytail is a minimal incremental PyPI caching mirror.

It is no longer a devpi clone. The new server only does four things:

  • expose a valid Python Simple Repository API root at /simple/
  • lazily fetch and cache /simple/<project>/ from an upstream index
  • rewrite file links to a devpi-style root/pypi/+f/... path
  • serve cached project pages and cached files on later requests

Plan

  1. Implement the smallest Simple API surface that pip and uv actually need: root index, project index, normalized names, file links, and content negotiation for HTML vs JSON responses.
  2. Keep the cache incremental: do not mirror the full upstream project list, only remember projects that have already been requested.
  3. Use conditional project refresh: cache upstream project pages, preserve ETag, and refresh a project after a TTL with If-None-Match.
  4. Keep package files immutable: once a file URL is requested, cache it under +files/ and serve it from a stable devpi-style file route on later requests.
  5. Store the index in SQLite: project pages, file metadata, and known-project state live in SQLite, while package bytes live on disk.
  6. Handle concurrent requests safely inside one process: per-project and per-file locks avoid duplicate upstream fetches and duplicate downloads.
  7. Explicitly drop old devpi goals: no users, no ACLs, no upload API, no index inheritance, no replication, no snapshot format, no mirror whitelist, no web UI.

Spec Coverage

The implementation is intentionally narrow:

  • PEP 503 HTML Simple API for /simple/ and /simple/<project>/
  • PEP 691 JSON Simple API responses when Accept asks for application/vnd.pypi.simple.v1+json
  • preservation of requires-python, yanked, gpg-sig, dist-info-metadata, and core-metadata markers when they are present on the upstream project page
  • lazy local root index containing only already-cached projects

The implementation does not currently fetch or synthesize a full global upstream project list. That is deliberate: the root index is only a local catalogue of known projects, while project pages are fetched on demand.

Why This Is Enough

Package resolution for pip and uv depends primarily on per-project Simple API pages. A full pre-fetched mirror root is not required for normal dependency resolution as long as:

  • /simple/<normalized-project>/ is available and correct
  • file links are reachable
  • project metadata such as hashes and requires-python are preserved

Run

cargo run -- \
  --bind 127.0.0.1:3141 \
  --upstream-base-url https://pypi.org \
  --torch-url https://download.pytorch.org/whl/ \
  --cache-dir .cache/pytail

Then point tools at it:

uv pip install --index-url http://127.0.0.1:3141/simple/ requests
pip install --index-url http://127.0.0.1:3141/simple/ requests

PyTorch wheel indexes are also exposed under /pytorch-wheels/, so a command that uses:

pip install torch --index-url https://download.pytorch.org/whl/cu126

can use the local caching endpoint instead:

pip install torch --index-url http://127.0.0.1:3141/pytorch-wheels/cu126

The local path is mapped directly under the configured PyTorch wheels upstream:

/pytorch-wheels/torch/        -> <torch-url>/torch/
/pytorch-wheels/cu126/torch/  -> <torch-url>/cu126/torch/

For packages that need both PyPI and PyTorch wheels, keep the indexes separate:

pip install torch \
  --index-url http://127.0.0.1:3141/pytorch-wheels/cu126 \
  --extra-index-url http://127.0.0.1:3141/simple/

Configuration

  • --bind: listen address, default 127.0.0.1:3141
  • --upstream-base-url: upstream index origin, default https://pypi.org
  • --torch-url: PyTorch wheels upstream root, default https://download.pytorch.org/whl/
  • --cache-dir: local cache directory, default .cache/pytail
  • --project-cache-ttl-secs: refresh age for cached project pages, default 900
  • --request-timeout-secs: upstream HTTP timeout, default 15
  • --stats-interval-secs: cache hit-rate log interval, default 60; set to 0 to disable periodic stats
  • --verbose: enable debug logging for pytail

Package

Build a Python wheel with maturin:

maturin build --release

Publish to PyPI:

maturin publish --release

The wheel installs the pytail command as a native Rust binary.

Cache Layout

<cache-dir>/
  index.sqlite3
  +files/
    root/
      pypi/
        +f/
          f0a/
            f3fc39e7459b0/
              gradio_client-1.0.2-py3-none-any.whl
          _url/
            <url-digest>/
              <filename>
  • SQLite stores project cache entries, parsed file rows, upstream ETag, and known project names
  • +files/root/pypi/+f/<first-3-sha256>/<next-13-sha256>/<filename> stores hashed files in the same shape as devpi's filesystem layout
  • +files/root/pypi/+f/_url/<url-digest>/<filename> is used only when an upstream file link does not provide a usable sha256 hash
  • PyTorch wheel endpoints reuse the same blob store and download pipeline; only their project cache keys and client-facing file URLs are namespaced

Non-Goals

  • full PyPI mirroring
  • authentication or private indexes
  • package upload
  • merge of local and upstream package sources
  • replica mode or background synchronization

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pytail-0.1.4.tar.gz (77.3 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pytail-0.1.4-py3-none-win_amd64.whl (3.4 MB view details)

Uploaded Python 3Windows x86-64

pytail-0.1.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.8 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

pytail-0.1.4-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl (6.9 MB view details)

Uploaded Python 3macOS 10.12+ universal2 (ARM64, x86-64)macOS 10.12+ x86-64macOS 11.0+ ARM64

File details

Details for the file pytail-0.1.4.tar.gz.

File metadata

  • Download URL: pytail-0.1.4.tar.gz
  • Upload date:
  • Size: 77.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pytail-0.1.4.tar.gz
Algorithm Hash digest
SHA256 4b83a68b59c0bbe98aac04706f6d9d2fe1486471b5724613ef91fbf8625d8290
MD5 6c1a8330a821d195dd9be439619eb395
BLAKE2b-256 7a68dde5b52a9ee5386232166a56ef5be3627078467400ec1a2f07b6535fcb8b

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytail-0.1.4.tar.gz:

Publisher: release.yml on AndPuQing/PyTail

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pytail-0.1.4-py3-none-win_amd64.whl.

File metadata

  • Download URL: pytail-0.1.4-py3-none-win_amd64.whl
  • Upload date:
  • Size: 3.4 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pytail-0.1.4-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 2ddab579ff65c7d1a2c83c36f3692e13ed5f631fc4303ff53138dc13533fbae5
MD5 0b0f38b0d5b5741c1ba26bc13f1e391c
BLAKE2b-256 0cf489d6a31432931577902b1e63bf8e61f284219f7a5e8d4ba435adc9a0ae50

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytail-0.1.4-py3-none-win_amd64.whl:

Publisher: release.yml on AndPuQing/PyTail

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pytail-0.1.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pytail-0.1.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 d033f1dc10ca46d2123481bcb7c3c1589967a775805cbc4856fbdb97d0f4f265
MD5 1bc1ad9ff37cd23177fb8ad7e9da5ecd
BLAKE2b-256 31e770222460b6221782abfb20b259c440cb9ce80b7391d67e573e7a3bc8eba2

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytail-0.1.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on AndPuQing/PyTail

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pytail-0.1.4-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl.

File metadata

File hashes

Hashes for pytail-0.1.4-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 1f7e8427dc2bb956043c8c009d8f47e11fa1f5a3fedac09081be430cc0be30ca
MD5 ad4ec77289a1f476f6b9e14ed375225d
BLAKE2b-256 f7b0fc080d680ebe1790c2201ef92c86b3b1cc1f0794336c5938b6167a16a9c9

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytail-0.1.4-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl:

Publisher: release.yml on AndPuQing/PyTail

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page