Skip to content

Commit

Permalink
Add PEP691 support
Browse files Browse the repository at this point in the history
  • Loading branch information
chriskuehl committed May 16, 2024
1 parent 7a36c26 commit 5b3ab8e
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 15 deletions.
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,21 @@ You can set these environment variables to configure the server:
* `PYPI_BROWSER_PYPI_URL`: URL for the PyPI server to use (defaults to
`https://pypi.org`)

If your registry supports the pypi.org-compatible JSON API (e.g.
`{registry}/pypi/{package}/json`), specify your base registry URL without
appending `/simple` (e.g. `https://my-registry`).
For best results (i.e. most available metadata), choose the first option from
this list which is supported by your PyPI registry:

If your registry only supports the traditional HTML "simple" index, specify
the registry URL with `/simple` at the end (e.g.
`https://my-registry/simple`).
* If your registry supports [PEP691](pep691) JSON "simple" indexes, specify
your base registry URL with `/simple` appended (e.g.
`https://my-registry/simple`).

* If your registry supports the legacy pypi.org-compatible JSON API (e.g.
`{registry}/pypi/{package}/json`), specify your base registry URL without
a suffix (e.g. `https://my-registry`).

* Otherwise, if your registry only supports the traditional HTML "simple"
index, specify the registry URL with `/simple` at the end (e.g.
`https://my-registry/simple`).

Note that the [PEP691][pep691] JSON-based "simple" API is not yet supported.

* `PYPI_BROWSER_PACKAGE_CACHE_PATH`: Filesystem path to use for caching
downloaded files. This will grow forever (the app does not clean it up) so
Expand Down
27 changes: 19 additions & 8 deletions pypi_browser/pypi.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,29 +37,40 @@ def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None

@dataclasses.dataclass(frozen=True)
class SimpleRepository(PythonRepository):
"""Old-style "simple" PyPI registry serving HTML files."""
# TODO: Also handle PEP691 JSON simple repositories.
""""Simple" PyPI registry serving either JSON or HTML files."""
pypi_url: str

async def files_for_package(self, package_name: str) -> dict[str, str]:
async with httpx.AsyncClient() as client:
resp = await client.get(
f'{self.pypi_url}/{package_name}',
follow_redirects=True,
headers={
'Accept': ', '.join((
'application/vnd.pypi.simple.v1+json',
'application/vnd.pypi.simple.v1+html;q=0.2',
'text/html;q=0.01',
)),
},
)
if resp.status_code == 404:
raise PackageDoesNotExist(package_name)
parser = HTMLAnchorParser()
parser.feed(resp.text)

def clean_url(url: str) -> str:
parsed = urllib.parse.urlparse(urllib.parse.urljoin(str(resp.url), url))
return parsed._replace(fragment='').geturl()

return {
(urllib.parse.urlparse(url).path).split('/')[-1]: clean_url(url)
for url in parser.anchors
}
if resp.headers.get('Content-Type') == 'application/vnd.pypi.simple.v1+json':
result = resp.json()
return {file_['filename']: clean_url(file_['url']) for file_ in result['files']}
else:
parser = HTMLAnchorParser()
parser.feed(resp.text)

return {
(urllib.parse.urlparse(url).path).split('/')[-1]: clean_url(url)
for url in parser.anchors
}


@dataclasses.dataclass(frozen=True)
Expand Down

0 comments on commit 5b3ab8e

Please sign in to comment.