repomatic.pypi module

PyPI API client for package metadata lookups.

Provides a shared HTTP client and domain-specific query functions used by repomatic.changelog (release dates, yanked status), repomatic.release.version_sync (release candidates) and repomatic.deps.dep_report (source repository discovery for release notes). Also the home of what counts as the public index at all (PYPI_INDEX_HOSTS), which the shippability gate reads.

repomatic.pypi.PYPI_INDEX_HOSTS = frozenset({'pypi.org', 'www.pypi.org'})

Hosts a package may be resolved from and still count as published.

Anything else is a private index, a staging index (TestPyPI lives on test.pypi.org, deliberately absent) or a proxy: a user running pip install or uvx against the default index reaches none of them, so a dependency pinned there is no more installable than one pinned to a git branch.

repomatic.pypi.is_pypi_url(url)[source]

Whether url points at the public Python Package Index.

Parameters:

url (str) – An index or registry URL.

Return type:

bool

Returns:

True when its host is one of PYPI_INDEX_HOSTS.

repomatic.pypi.PYPI_API_URL = 'https://pypi.org/pypi/{package}/json'

PyPI JSON API URL for fetching all release metadata for a package.

repomatic.pypi.PYPI_PACKAGE_URL = 'https://pypi.org/project/{package}/'

PyPI project homepage URL for a package (no version pinned).

repomatic.pypi.PYPI_PROJECT_URL = 'https://pypi.org/project/{package}/{version}/'

PyPI project page URL for a specific version.

repomatic.pypi.PYPI_PROVENANCE_URL = 'https://pypi.org/integrity/{package}/{version}/{filename}/provenance'

PyPI integrity API endpoint exposing PEP 740 attestation bundles for a file.

The response includes a publisher object per bundle that names the OIDC identity used to upload (kind, repository, workflow filename, environment). This is the only public surface where the OIDC job_workflow_ref claim is observable: project-level Trusted Publisher settings live behind the owner-only /manage/project/<name>/settings/publishing/ page.

repomatic.pypi.PYPI_TRUSTED_PUBLISHER_SETTINGS_URL = 'https://pypi.org/manage/project/{package}/settings/publishing/'

Owner-only page where Trusted Publisher entries are registered.

repomatic.pypi.PYPI_TRUSTED_PUBLISHER_WORKFLOW = 'release.yaml'

Workflow filename each downstream registers as the Trusted Publisher.

The caller-side publish-pypi job is appended to release.yaml in every downstream repo (reshaped from the canonical entry by repomatic.github.workflow_sync._render_publish_pypi_job), and the composite action it invokes inherits the calling job’s OIDC context. The OIDC job_workflow_ref claim therefore names this file: that is what the PyPI Trusted Publisher entry must match.

repomatic.pypi.pypi_trusted_publisher_settings_url(package, *, owner=None, repository=None, workflow_filename=None, environment=None)[source]

Build the PyPI Trusted Publisher settings page URL for a project.

Without keyword arguments, returns the bare settings URL. When any GitHub publisher field is provided, appends the query string PyPI’s settings page consumes to activate the GitHub tab and pre-populate the form: see the manage_project_oidc_publishers_prefill view in pypi/warehouse.

Parameters:
  • package (str) – PyPI project name.

  • owner (str | None) – GitHub owner (user or org) prefilled in the form.

  • repository (str | None) – GitHub repository name prefilled in the form.

  • workflow_filename (str | None) – Workflow filename prefilled in the form (e.g., PYPI_TRUSTED_PUBLISHER_WORKFLOW).

  • environment (str | None) – GitHub Actions environment name prefilled in the form.

Return type:

str

Returns:

The settings URL, optionally with a ?provider=github&… suffix.

repomatic.pypi.PYPI_LABEL = '🐍 PyPI'

Display label for PyPI releases in admonitions.

class repomatic.pypi.PyPIRelease(date: str, yanked: bool, package: str, yanked_reason: str = '')[source]

Bases: NamedTuple

Release metadata for a single version from PyPI.

Create new instance of PyPIRelease(date, yanked, package, yanked_reason)

date: str

Earliest upload date across all files in YYYY-MM-DD format.

yanked: bool

Whether all files for this version are yanked.

package: str

PyPI package name this release was fetched from.

Needed for projects that were renamed: older versions live under a former package name and their PyPI URLs must point to that name, not the current one.

yanked_reason: str

Why the release was yanked, empty when PyPI records no reason.

PyPI stores the reason per file and accepts a yank with none at all, so this carries the first non-empty one across the version’s files.

repomatic.pypi.get_release_dates(package, *, force_refresh=False)[source]

Get upload dates and yanked status for all versions from PyPI.

Fetches the package metadata in a single API call. For each version, selects the earliest upload time across all distribution files as the canonical release date. A version is considered yanked only if all of its files are yanked, and carries the first yank reason any of them records.

Parameters:
  • package (str) – The PyPI package name.

  • force_refresh (bool) – Ignore any cached response and re-fetch.

Return type:

dict[str, PyPIRelease]

Returns:

Dict mapping version strings to PyPIRelease tuples. Empty dict if the package is not found or the request fails.

repomatic.pypi.github_repo_root(url)[source]

Reduce any GitHub URL to its https://github.com/owner/repo root.

A project_urls entry often points inside a repository (/issues, /releases, /blob/main/CHANGELOG.md), which is fine for a human-facing link but not for callers that derive an owner/repo API slug from it: the releases API would be asked for repo/issues and answer 404.

Parameters:

url (str) – Any URL, GitHub or not.

Return type:

str | None

Returns:

The repository root, or None when url names no GitHub repository (a bare github.com, or an owner with no repo).

repomatic.pypi.get_source_url(package)[source]

Discover the GitHub repository URL for a PyPI package.

Queries the PyPI JSON API and scans project_urls for keys that typically point to a source repository on GitHub, then reduces the winner to its repository root so an API slug can be derived from it.

Parameters:

package (str) – The PyPI package name.

Return type:

str | None

Returns:

The GitHub repository URL, or None if not found.

class repomatic.pypi.TrustedPublisher(kind: str, repository: str, workflow: str, environment: str | None)[source]

Bases: NamedTuple

OIDC publisher metadata extracted from a PyPI provenance bundle.

Create new instance of TrustedPublisher(kind, repository, workflow, environment)

kind: str

Publisher kind, e.g., "GitHub" or "GitLab".

repository: str

Repository slug ("owner/name" for GitHub publishers).

workflow: str

Workflow filename within .github/workflows/ (e.g., "release.yaml").

environment: str | None

GitHub Actions environment name, when the publisher was scoped to one.

repomatic.pypi.get_latest_release_file(package)[source]

Return (version, filename) for the latest non-yanked release on PyPI.

Picks the version with the most recent earliest-upload time and returns a representative distribution file from that version. Wheels are preferred over sdists since wheels are guaranteed to exist for any package built with modern tooling.

Two releases uploaded on the same day are ordered by PEP 440, not by the version string: a raw string comparison sorts 1.9.0 above 1.10.0 and would return the older of the two as the latest. Versions PEP 440 cannot parse are skipped, since nothing can rank them.

Parameters:

package (str) – The PyPI package name.

Return type:

tuple[str, str] | None

Returns:

Tuple of (version, filename), or None if the package has no published releases or the request fails.

repomatic.pypi.get_trusted_publishers(package, version, filename)[source]

Fetch PEP 740 provenance for a file and extract publisher entries.

Calls PYPI_PROVENANCE_URL and parses the attestation_bundles array. Each bundle’s publisher object names the OIDC identity that uploaded the file.

Parameters:
  • package (str) – The PyPI package name.

  • version (str) – The release version (e.g., "1.2.3").

  • filename (str) – The distribution filename (e.g., "my_pkg-1.2.3-py3-none-any.whl").

Return type:

list[TrustedPublisher] | None

Returns:

List of TrustedPublisher entries (possibly empty when provenance exists but no bundles are present), or None when the endpoint returns 404 or any network/parse error occurs (signal that no provenance is available rather than that none was registered).

repomatic.pypi.get_changelog_url(package)[source]

Discover the changelog URL for a PyPI package.

Queries the PyPI JSON API and scans project_urls for keys that typically point to a changelog or release notes page. Keys are matched case-insensitively, for the reason spelled out on _SOURCE_URL_KEYS: PyPI preserves whatever spelling the project wrote, so Changelog, changelog and CHANGELOG all occur in the wild.

Parameters:

package (str) – The PyPI package name.

Return type:

str | None

Returns:

The changelog URL, or None if not found.