Skip to content

Commit 0e64d57

Browse files
committed
Fix pull-through metadata serving
fixes: #1083
1 parent 42d0913 commit 0e64d57

File tree

4 files changed

+57
-2
lines changed

4 files changed

+57
-2
lines changed

CHANGES/1083.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed pull-through PEP 658 metadata not being served correctly for certain tools.

docs/user/guides/publish.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ pulp python distribution update --name foo --remote bar
105105
Functionality may not work or may be incomplete. Also, backwards compatibility when upgrading
106106
is not guaranteed.
107107

108+
!!! warning
109+
Chaining pull-through indices, having a pull-through point to another pull-through, does not
110+
work.
111+
108112
## Use the newly created distribution
109113

110114
The metadata and packages can now be retrieved from the distribution:

pulp_python/app/models.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,11 +320,25 @@ def get_remote_artifact_url(self, relative_path=None, request=None):
320320
"""Get url for remote_artifact"""
321321
if request and (url := request.query.get("redirect")):
322322
# This is a special case for pull-through caching
323+
# To handle PEP 658, it states that if the package has metadata available then it
324+
# should be found at the download URL + ".metadata". Thus if the request url ends with
325+
# ".metadata" then we need to add ".metadata" to the redirect url if not present.
326+
if relative_path:
327+
if relative_path.endswith(".metadata") and not url.endswith(".metadata"):
328+
url += ".metadata"
329+
# Handle special case for bug in pip (TODO file issue in pip) where it appends
330+
# ".metadata" to the redirect url instead of the request url
331+
if url.endswith(".metadata") and not relative_path.endswith(".metadata"):
332+
setattr(self, "_real_relative_path", url.rsplit("/", 1)[1])
323333
return url
324334
return super().get_remote_artifact_url(relative_path, request=request)
325335

326336
def get_remote_artifact_content_type(self, relative_path=None):
327-
"""Return PythonPackageContent."""
337+
"""Return PythonPackageContent, except for metadata artifacts."""
338+
if hasattr(self, "_real_relative_path"):
339+
relative_path = getattr(self, "_real_relative_path")
340+
if relative_path and relative_path.endswith(".whl.metadata"):
341+
return None
328342
return PythonPackageContent
329343

330344
class Meta:

pulp_python/tests/functional/api/test_full_mirror.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111

1212
from pypi_simple import ProjectPage
1313
from packaging.version import parse
14-
from urllib.parse import urljoin, urlsplit
14+
from urllib.parse import urljoin, urlsplit, urlunsplit
1515
from random import sample
16+
from hashlib import sha256
1617

1718

1819
def test_pull_through_install(
@@ -182,3 +183,38 @@ def test_pull_through_local_only(
182183
r = requests.get(url)
183184
assert r.status_code == 404
184185
assert r.text == "pulp-python does not exist."
186+
187+
188+
@pytest.mark.parallel
189+
def test_pull_through_metadata(python_remote_factory, python_distribution_factory):
190+
"""
191+
Tests that metadata is correctly served when using pull-through.
192+
193+
So when requesting the metadata url according to PEP 658 you should just need to add .metadata
194+
to the end of the url path. Since pull-through includes a redirect query parameter we need to
195+
test adding .metadata to the end of the url path vs adding it to the end of redirect query.
196+
"""
197+
remote = python_remote_factory(includes=["pytz"])
198+
distro = python_distribution_factory(remote=remote.pulp_href)
199+
200+
url = f"{distro.base_url}simple/pytz/"
201+
project_page = ProjectPage.from_response(requests.get(url), "pytz")
202+
filename1 = "pytz-2023.2-py2.py3-none-any.whl"
203+
filename2 = "pytz-2023.3-py2.py3-none-any.whl"
204+
package1 = next(p for p in project_page.packages if p.filename == filename1)
205+
package2 = next(p for p in project_page.packages if p.filename == filename2)
206+
assert package1.has_metadata
207+
assert package2.has_metadata
208+
209+
# The correct way to get the metadata url: add to path (uv does this)
210+
parts1 = urlsplit(package1.url)
211+
url1 = urlunsplit((parts1[0], parts1[1], parts1[2] + ".metadata", parts1[3], parts1[4]))
212+
r = requests.get(url1)
213+
assert r.status_code == 200
214+
assert sha256(r.content).hexdigest() == package1.metadata_digests["sha256"]
215+
216+
# The incorrect way to get the metadata url: add to end of string (pip does this)
217+
url2 = package2.url + ".metadata"
218+
r = requests.get(url2)
219+
assert r.status_code == 200
220+
assert sha256(r.content).hexdigest() == package2.metadata_digests["sha256"]

0 commit comments

Comments
 (0)