Skip to content

Mirror PyPI packages - #591

Merged
andrew merged 1 commit into
git-pkgs:mainfrom
simonchrz:feat/mirror-pypi
Oct 25, 2026
Merged

andrew merged 1 commit into
git-pkgs:mainfrom
simonchrz:feat/mirror-pypi

Conversation

@simonchrz

Copy link
Copy Markdown
Contributor

Part of #414: PyPI.

proxy mirror, mirror API jobs and schedules failed for every pypi PURL with unsupported ecosystem: pypi, since the resolver has no registry for it.

A PyPI version has several files, so the mirror doesn't go through the resolver here. It lists the version's files from upstream.pypi's JSON API, through the same client and lister #548 added for versions, and fetches each one with GetOrFetchArtifactFromURLWithIntegrity, so it is checked against the listed sha256. The file_name qualifier from the PURL spec limits it to one file.

Each file is cached under the name and version the download handler reads from its filename (handler.PyPIFilenameIdentity, which parseFilename now wraps), not under the PURL's. They can differ: a wheel spelled example_pkg and an sdist spelled example.pkg end up under different keys, and the handler looks them up by filename.

Tests, following the review on #414:

  • TestMirrorPyPIFetchesEveryFile mirrors a version with a wheel and an sdist whose names are spelled differently, then shuts the upstream down and fetches both through the PyPI handler from the cache.
  • TestMirrorPyPIFileNameQualifier mirrors only the named file, and fails with the name when it isn't listed.
  • TestMirrorPyPIRefusesDigestMismatch refuses a file whose sha256 doesn't match.

Against pypi.org, proxy mirror pkg:pypi/six@1.16.0 fetches the wheel and the sdist, and pkg:pypi/zope.interface@7.2?file_name=zope.interface-7.2-cp312-cp312-macosx_11_0_arm64.whl fetches only that wheel.

Files are fetched from the URLs the JSON API lists. With the default upstreams those are on files.pythonhosted.org, which upstream.pypi_download points at too.

proxy mirror and mirror jobs failed for pypi with "unsupported
ecosystem", since the resolver they use has no registry for it. A PyPI
version has several files, so the mirror now lists them from
upstream.pypi's JSON API, through the same client as version listing,
and fetches each one, verified against its sha256. The file_name
qualifier limits it to one file.

Files are cached under the name and version the download handler reads
from the filename, which can differ from the PURL (zope.interface's
wheels are zope_interface), so pip is served them from the cache.
@andrew
andrew merged commit 255ba72 into git-pkgs:main Oct 25, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants