Skip to content

Commit 1ce6588

Browse files
committed
tmp
1 parent 583f3fe commit 1ce6588

File tree

4 files changed

+78
-36
lines changed

4 files changed

+78
-36
lines changed

pulp_python/app/pypi/views.py

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -277,17 +277,17 @@ def list(self, request, path):
277277
return redirect(urljoin(self.base_content_url, f"{path}/simple/"))
278278
names = content.order_by("name").values_list("name", flat=True).distinct().iterator()
279279
media_type = request.accepted_renderer.media_type
280+
headers = {"X-PyPI-Last-Serial": str(PYPI_SERIAL_CONSTANT)}
280281

281282
if media_type == PYPI_SIMPLE_V1_JSON:
282283
index_data = write_simple_index_json(names)
283-
headers = {"X-PyPI-Last-Serial": str(PYPI_SERIAL_CONSTANT)}
284284
return Response(index_data, headers=headers)
285285
else:
286286
index_data = write_simple_index(names, streamed=True)
287-
kwargs = {"content_type": media_type}
287+
kwargs = {"content_type": media_type, "headers": headers}
288288
return StreamingHttpResponse(index_data, **kwargs)
289289

290-
def pull_through_package_simple(self, package, path, remote):
290+
def pull_through_package_simple(self, package, path, remote, media_type):
291291
"""Gets the package's simple page from remote."""
292292

293293
def parse_package(release_package):
@@ -299,7 +299,8 @@ def parse_package(release_package):
299299
"filename": release_package.filename,
300300
"url": d_url,
301301
"sha256": release_package.digests.get("sha256", ""),
302-
# todo: more fields?
302+
"requires_python": release_package.requires_python,
303+
"metadata_sha256": (release_package.metadata_digests or {}).get("sha256", ""),
303304
}
304305

305306
rfilter = get_remote_package_filter(remote)
@@ -324,27 +325,33 @@ def parse_package(release_package):
324325
packages = [
325326
parse_package(p) for p in page.packages if rfilter.filter_release(package, p.version)
326327
]
327-
return HttpResponse(write_simple_detail(package, packages))
328+
headers = {"X-PyPI-Last-Serial": str(PYPI_SERIAL_CONSTANT)}
329+
330+
if media_type == PYPI_SIMPLE_V1_JSON:
331+
detail_data = write_simple_detail_json(package, packages)
332+
return Response(detail_data, headers=headers)
333+
else:
334+
detail_data = write_simple_detail(package, packages)
335+
kwargs = {"content_type": media_type, "headers": headers}
336+
return HttpResponse(detail_data, **kwargs)
328337

329338
@extend_schema(operation_id="pypi_simple_package_read", summary="Get package simple page")
330339
def retrieve(self, request, path, package):
331-
"""Retrieves the simple api html page for a package."""
340+
"""Retrieves the simple api html/json page for a package."""
341+
media_type = request.accepted_renderer.media_type
342+
332343
repo_ver, content = self.get_rvc()
333344
# Should I redirect if the normalized name is different?
334345
normalized = canonicalize_name(package)
335346
if self.distribution.remote:
336-
return self.pull_through_package_simple(normalized, path, self.distribution.remote)
347+
return self.pull_through_package_simple(
348+
normalized, path, self.distribution.remote, media_type
349+
)
337350
if self.should_redirect(repo_version=repo_ver):
338351
return redirect(urljoin(self.base_content_url, f"{path}/simple/{normalized}/"))
339352
packages = (
340353
content.filter(name__normalize=normalized)
341-
.values_list(
342-
"filename",
343-
"sha256",
344-
"name",
345-
"metadata_sha256",
346-
"requires_python",
347-
)
354+
.values_list("filename", "sha256", "name", "metadata_sha256", "requires_python")
348355
.iterator()
349356
)
350357
try:
@@ -365,15 +372,15 @@ def retrieve(self, request, path, package):
365372
for f, s, _, ms, rp in packages
366373
)
367374
media_type = request.accepted_renderer.media_type
375+
headers = {"X-PyPI-Last-Serial": str(PYPI_SERIAL_CONSTANT)}
368376

369377
if media_type == PYPI_SIMPLE_V1_JSON:
370378
detail_data = write_simple_detail_json(name, releases)
371-
headers = {"X-PyPI-Last-Serial": str(PYPI_SERIAL_CONSTANT)}
372379
return Response(detail_data, headers=headers)
373380
else:
374381
detail_data = write_simple_detail(name, releases, streamed=True)
375-
kwargs = {"content_type": media_type}
376-
return StreamingHttpResponse(detail_data, kwargs)
382+
kwargs = {"content_type": media_type, "headers": headers}
383+
return StreamingHttpResponse(detail_data, **kwargs)
377384

378385
@extend_schema(
379386
request=PackageUploadSerializer,

pulp_python/app/utils.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
<html>
2323
<head>
2424
<title>Simple Index</title>
25-
<meta name="pypi:repository-version" content="{SIMPLE_API_VERSION}">
25+
<meta name="pypi:repository-version" content="{{ SIMPLE_API_VERSION }}">
2626
</head>
2727
<body>
2828
{% for name, canonical_name in projects %}
@@ -36,7 +36,7 @@
3636
<html>
3737
<head>
3838
<title>Links for {{ project_name }}</title>
39-
<meta name="api-version" value="2" />
39+
<meta name="pypi:repository-version" content="{{ SIMPLE_API_VERSION }}">
4040
</head>
4141
<body>
4242
<h1>Links for {{ project_name }}</h1>
@@ -409,14 +409,21 @@ def find_artifact():
409409
def write_simple_index(project_names, streamed=False):
410410
"""Writes the simple index."""
411411
simple = Template(simple_index_template)
412-
context = {"projects": ((x, canonicalize_name(x)) for x in project_names)}
412+
context = {
413+
"SIMPLE_API_VERSION": SIMPLE_API_VERSION,
414+
"projects": ((x, canonicalize_name(x)) for x in project_names),
415+
}
413416
return simple.stream(**context) if streamed else simple.render(**context)
414417

415418

416419
def write_simple_detail(project_name, project_packages, streamed=False):
417420
"""Writes the simple detail page of a package."""
418421
detail = Template(simple_detail_template)
419-
context = {"project_name": project_name, "project_packages": project_packages}
422+
context = {
423+
"SIMPLE_API_VERSION": SIMPLE_API_VERSION,
424+
"project_name": project_name,
425+
"project_packages": project_packages,
426+
}
420427
return detail.stream(**context) if streamed else detail.render(**context)
421428

422429

pulp_python/tests/functional/api/test_domains.py

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -265,18 +265,28 @@ def test_domain_pypi_apis(
265265
assert response["projects"] == response["releases"] == response["files"] == 1
266266

267267
# Test download
268-
subprocess.run(
269-
(
270-
"pip",
271-
"install",
272-
"--no-deps",
273-
"--no-build-isolation",
274-
"--trusted-host",
275-
urlsplit(distro.base_url).hostname,
276-
"-i",
277-
distro.base_url + "simple/",
278-
"shelf-reader",
279-
),
280-
capture_output=True,
281-
check=True,
282-
)
268+
try:
269+
subprocess.run(
270+
(
271+
"pip",
272+
"install",
273+
"--no-deps",
274+
"--no-build-isolation",
275+
"--trusted-host",
276+
urlsplit(distro.base_url).hostname,
277+
"-i",
278+
distro.base_url + "simple/",
279+
"shelf-reader",
280+
),
281+
capture_output=True,
282+
check=True,
283+
)
284+
except subprocess.CalledProcessError as e:
285+
print(f"Pip install failed with return code {e.returncode}")
286+
print(f"STDOUT: {e.stdout.decode() if e.stdout else 'None'}")
287+
print(f"STDERR: {e.stderr.decode() if e.stderr else 'None'}")
288+
raise
289+
except Exception as e:
290+
print(f"Unexpected error during pip install: {e}")
291+
print(f"Exception type: {type(e)}")
292+
raise

pulp_python/tests/functional/api/test_full_mirror.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,24 @@ def test_pull_through_simple(python_remote_factory, python_distribution_factory,
5858
assert PYTHON_XS_FIXTURE_CHECKSUMS[package.filename] == package.digests["sha256"]
5959

6060

61+
@pytest.mark.parallel
62+
@pytest.mark.parametrize("media_type", ["application/vnd.pypi.simple.v1+json", "text/html"])
63+
def test_pull_through_simple_media_types(
64+
media_type, python_remote_factory, python_distribution_factory
65+
):
66+
"""Tests pull-through with different media types (JSON and HTML)."""
67+
remote = python_remote_factory(url=PYPI_URL, includes=["shelf-reader"])
68+
distro = python_distribution_factory(remote=remote.pulp_href)
69+
70+
url = f"{distro.base_url}simple/shelf-reader/"
71+
headers = {"Accept": media_type}
72+
response = requests.get(url, headers=headers)
73+
74+
assert response.status_code == 200
75+
assert media_type in response.headers["Content-Type"]
76+
assert "X-PyPI-Last-Serial" in response.headers
77+
78+
6179
@pytest.mark.parallel
6280
def test_pull_through_filter(python_remote_factory, python_distribution_factory):
6381
"""Tests that pull-through respects the includes/excludes filter on the remote."""

0 commit comments

Comments
 (0)