Skip to content

Conversation

iBz-04
Copy link

@iBz-04 iBz-04 commented Oct 7, 2025

  • Added download routes for entire distribution packages (.whl, .tar.gz, etc.)
  • Added download routes for individual files within packages
  • Display download button on package view pages
Screenshot 2025-10-07 133717

Fixes #166.

Copy link
Member

@di di left a comment

Choose a reason for hiding this comment

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

Thanks for the PR!

Comment on lines +4 to +9
try:
import gunicorn.http.errors
GUNICORN_AVAILABLE = True
except ImportError:
GUNICORN_AVAILABLE = False

Copy link
Member

Choose a reason for hiding this comment

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

Why would Gunicorn be unavailable here?

Copy link
Author

Choose a reason for hiding this comment

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

Gunicorn might be unavailable in development environments where the application is run using Flask's built-in development server instead of Gunicorn

Copy link
Member

Choose a reason for hiding this comment

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

Got it. Since our recommendation for running for local development with make serve & Docker Compose, I don't think this will be an issue.

Comment on lines 332 to 378
@app.route(
"/project/<project_name>/<version>/packages/<first>/<second>/<rest>/<distname>/<path:filepath>/download"
)
def download_file(project_name, version, first, second, rest, distname, filepath):
"""Download individual file from a distribution."""
if project_name != canonicalize_name(project_name):
return redirect(
url_for(
"download_file",
project_name=canonicalize_name(project_name),
version=version,
first=first,
second=second,
rest=rest,
distname=distname,
filepath=filepath,
),
301,
)

try:
dist = _get_dist(first, second, rest, distname)
except InspectorError:
return abort(400)

if not dist:
return abort(404)

try:
contents = dist.contents(filepath)
except FileNotFoundError:
return abort(404)
except InspectorError:
return abort(400)

# Extract just the filename from the path
filename = filepath.split("/")[-1]

# Return the file with proper headers to trigger download
return Response(
contents,
mimetype="application/octet-stream",
headers={
"Content-Disposition": f"attachment; filename={filename}",
"Content-Length": str(len(contents)),
},
)
Copy link
Member

Choose a reason for hiding this comment

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

This is pretty similar to the file view above, maybe they should share a common function instead?

Copy link
Author

Choose a reason for hiding this comment

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

You are right, thanks for drawing my attention i'll take care of that

Comment on lines 286 to 329
@app.route(
"/project/<project_name>/<version>/packages/<first>/<second>/<rest>/<distname>/download"
)
def download_distribution(project_name, version, first, second, rest, distname):
"""Download entire distribution package."""
if project_name != canonicalize_name(project_name):
return redirect(
url_for(
"download_distribution",
project_name=canonicalize_name(project_name),
version=version,
first=first,
second=second,
rest=rest,
distname=distname,
),
301,
)

try:
dist = _get_dist(first, second, rest, distname)
except InspectorError:
return abort(400)

if not dist:
return abort(404)

# Get the raw distribution file from PyPI
url = f"https://files.pythonhosted.org/packages/{first}/{second}/{rest}/{distname}"
try:
resp = requests_session().get(url, stream=True)
resp.raise_for_status()
except Exception:
return abort(404)

# Return the file with proper headers to trigger download
return Response(
resp.content,
mimetype="application/octet-stream",
headers={
"Content-Disposition": f"attachment; filename={distname}",
"Content-Length": str(len(resp.content)),
},
)
Copy link
Member

Choose a reason for hiding this comment

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

I think we can just provide a direct link to the URL where the distribution is hosted on PyPI here, rather than passing it through the application.

Copy link
Author

Choose a reason for hiding this comment

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

alright i'll take care of that too!!

@iBz-04
Copy link
Author

iBz-04 commented Oct 7, 2025

@di can you check the latest changes, thanks

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.

Add a "Download this package" / "Download this file" link

2 participants