Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 29 additions & 18 deletions morgan/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import argparse
import configparser
import gzip
import hashlib
import json
import os
Expand Down Expand Up @@ -161,24 +162,7 @@ def _mirror( # noqa: C901, PLR0912
self._processed_pkgs.add(requirement) # Mark as processed
return None

data: dict | None = None

# get information about this package from the Simple API in JSON
# format as per PEP 691
request = urllib.request.Request( # noqa: S310
f"{self.index_url}{requirement.name}/",
headers={
"Accept": "application/vnd.pypi.simple.v1+json",
},
)

response_url = ""
with urllib.request.urlopen(request) as response: # noqa: S310
data = json.load(response)
response_url = str(response.url)
if not data:
msg = f"Failed loading metadata: {response}"
raise RuntimeError(msg)
data, response_url = download_req(self.index_url, requirement.name)

# check metadata version ~1.0
v_str = data["meta"]["api-version"]
Expand Down Expand Up @@ -851,5 +835,32 @@ def my_url(arg):
Mirrorer(args).copy_server()


def download_req(index_url: str, req_name: str) -> tuple[dict, str]:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def download_req(index_url: str, req_name: str) -> tuple[dict, str]:
def fetch_package_metadata(index_url: str, req_name: str) -> tuple[dict, str]:

Please use a more descriptive name, like fetch_package_metadata

# get information about this package from the Simple API in JSON

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this method is only used by Mirrorer, please make it a Mirrorer method, not a global function

# format as per PEP 691
url = index_url.rstrip("/")
request = urllib.request.Request( # noqa: S310
f"{url}/{req_name}/",
headers={
"Accept": "application/vnd.pypi.simple.v1+json",
"Accept-Encoding": "gzip",
},
)

response_url = ""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
response_url = ""

This line here is unnecessary

with urllib.request.urlopen(request) as response: # noqa: S310
# Check if response is gzip-encoded
if response.headers.get("Content-Encoding") == "gzip":

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comparison should be case-insensitive (GZIP is a valid encoding), and you could strip whitespaces in the process.

with gzip.GzipFile(fileobj=response) as gzip_response:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A broken server or a corrupted or truncated body can raise a gzip.BadGzipFile or EOFError. We need to have a better error handling in case

A suggestion:

class MetadataError(RuntimeError):
    """The index returned an unusable metadata response."""

# and then inside the function's `with urllib.request.urlopen(...)` block:
try:
    if response.headers.get("Content-Encoding") == "gzip":
        with gzip.GzipFile(fileobj=response) as gzip_response:
            data = json.load(gzip_response)
    else:
        data = json.load(response)
except (gzip.BadGzipFile, EOFError, zlib.error, json.JSONDecodeError) as err:
    msg = f"Failed decoding metadata from {response.url}: {err}"
    raise MetadataError(msg) from err

and then expanding except (urllib.error.HTTPError, MetadataError) as err: in mirror().

data = json.load(gzip_response)
else:
data = json.load(response)
response_url = str(response.url)
if data:
return data, response_url
msg = f"Failed loading metadata: {response}"
raise RuntimeError(msg)


if __name__ == "__main__":
main()