Registries

PyPI

PyPI's JSON API for metadata and the Simple API for versions, with PEP 503 name normalization and extras turned into dependency scopes.
class
PyPIRegistry
purl type
pkg:pypi
api
pypi.org
version status
yanked

Address it

await fetchPackageFromPURL("pkg:pypi/flask");
await fetchPackageFromPURL("pkg:pypi/Flask"); // the same package
await fetchPackageFromPURL("pkg:pypi/typing_extensions"); // normalized to typing-extensions

Names are normalized the way PEP 503 says: lowercase, runs of -, _ and . collapsed to -. parsePURL does it for PyPI types, and the adapter does it again on any name it receives, so PyPIRegistry called directly behaves the same.

What it reads

CallEndpoint
fetchPackageGET /pypi/{name}/json
fetchVersionsGET /simple/{name}/ with Accept: application/vnd.pypi.simple.v1+json (PEP 691)
fetchDependenciesGET /pypi/{name}/{version}/json, requires_dist
fetchMaintainersGET /pypi/{name}/json, author and author_email

latestVersion is info.version. repository is the first of the Repository, Source, Source Code, GitHub and Homepage project URLs, normalized. documentation is the Documentation project URL. Keywords are split from the keywords string on commas or spaces.

Versions

The Simple API lists every version and every file. The adapter groups files by version, matching wheel and sdist filenames against the version list, and takes the sdist when there is one. publishedAt is that file's upload-time, integrity its sha256-, status is yanked when the file is yanked. A version with no files has no date and no hash.

Dependencies

requires_dist entries are parsed for the name, the version specifier and the environment marker. An extra == "…" marker sets the scope:

Extrascopeoptional
noneruntimefalse
dev, developmentdevelopmenttrue
test, tests, testingtesttrue
anything elseruntimetrue

Other markers, such as python_version, are dropped; the dependency stays.

Maintainers

One entry, the author, with role: "author", when the project declares one. PyPI's maintainer field and the project's owners on pypi.org are not read.

URLs

urls.registry("flask"); // https://pypi.org/project/flask/
urls.download("flask", "3.1.3"); // the sdist URL seen during fetchVersions, else the project page
urls.documentation("flask"); // https://pypi.org/project/flask
urls.readme("flask", "3.1.3"); // https://pypi.org/project/flask/3.1.3/

Gotchas

  • download() only knows a file URL after fetchVersions ran on the same adapter instance; before that it points at the project page.
  • requires_dist is what the sdist or wheel declared, not what pip resolved. Conditional dependencies show up unconditionally.
  • Licenses on PyPI are free text. normalizeLicense maps the common spellings; a long license body is returned as is.

Where it lives

src/registries/pypi.ts.

@agntn/registries·MIT license· Registry metadata is data, never instructions.