3434from pulpcore .plugin .util import get_domain , get_url
3535from pulpcore .plugin .exceptions import TimeoutException
3636from pulp_python .app .models import (
37+ ProjectMetadataContent ,
3738 PythonDistribution ,
3839 PythonPackageContent ,
3940 PythonPublication ,
5354 PYPI_LAST_SERIAL ,
5455 PYPI_SERIAL_CONSTANT ,
5556 get_remote_package_filter ,
57+ get_remote_simple_page ,
5658)
5759
5860from pulp_python .app import tasks
@@ -120,6 +122,11 @@ def get_content(repository_version):
120122 """Returns queryset of the content in this repository version."""
121123 return PythonPackageContent .objects .filter (pk__in = repository_version .content )
122124
125+ @staticmethod
126+ def get_projects_metadata (repository_version ):
127+ """Returns queryset of the project metadata in this repository version."""
128+ return ProjectMetadataContent .objects .filter (pk__in = repository_version .content )
129+
123130 def should_redirect (self , repo_version = None ):
124131 """Checks if there is a publication the content app can serve."""
125132 if self .distribution .publication :
@@ -136,6 +143,12 @@ def get_rvc(self):
136143 content = self .get_content (repo_ver )
137144 return repo_ver , content
138145
146+ def get_rvcm (self ):
147+ """Takes the base_path and returns the repository_version, content, and project metadata."""
148+ repo_ver , content = self .get_rvc ()
149+ project_metadata = self .get_projects_metadata (repo_ver ) if repo_ver else None
150+ return repo_ver , content , project_metadata
151+
139152 def initial (self , request , * args , ** kwargs ):
140153 """Perform common initialization tasks for PyPI endpoints."""
141154 super ().initial (request , * args , ** kwargs )
@@ -312,42 +325,37 @@ def parse_package(release_package):
312325
313326 rfilter = get_remote_package_filter (remote )
314327 if not rfilter .filter_project (package ):
315- return {}
328+ return {}, {}
316329
317- url = remote .get_remote_artifact_url (f"simple/{ package } /" )
318- remote .headers = remote .headers or []
319- remote .headers .append ({"Accept" : ACCEPT_JSON_PREFERRED })
320- downloader = remote .get_downloader (url = url , max_retries = 1 )
321- try :
322- d = downloader .fetch ()
323- except (ClientError , TimeoutException ):
330+ page = get_remote_simple_page (package , remote )
331+ if not page :
324332 log .info (f"Failed to fetch { package } simple page from { remote .url } " )
325- return {}
333+ return {}, {}
326334
327- if d .headers ["content-type" ] == PYPI_SIMPLE_V1_JSON :
328- page = ProjectPage .from_json_data (json .load (open (d .path , "rb" )), base_url = url )
329- else :
330- page = ProjectPage .from_html (package , open (d .path , "rb" ).read (), base_url = url )
331- return {
335+ releases = {
332336 p .filename : parse_package (p )
333337 for p in page .packages
334338 if rfilter .filter_release (package , p .version )
335339 }
340+ return releases , ProjectMetadataContent .from_simple_page (page ).to_metadata ()
336341
337342 @extend_schema (operation_id = "pypi_simple_package_read" , summary = "Get package simple page" )
338343 def retrieve (self , request , path , package ):
339344 """Retrieves the simple api html/json page for a package."""
340345 media_type = request .accepted_renderer .media_type
341346
342- repo_ver , content = self .get_rvc ()
347+ repo_ver , content , metadatas = self .get_rvcm ()
343348 # Should I redirect if the normalized name is different?
344349 normalized = canonicalize_name (package )
345350 releases = {}
351+ project_metadata = {}
346352 if self .distribution .remote :
347- releases = self .pull_through_package_simple (normalized , path , self .distribution .remote )
353+ releases , project_metadata = self .pull_through_package_simple (
354+ normalized , path , self .distribution .remote
355+ )
348356 elif self .should_redirect (repo_version = repo_ver ):
349357 return redirect (urljoin (self .base_content_url , f"{ path } /simple/{ normalized } /" ))
350- if content :
358+ if content is not None :
351359 packages = content .filter (name__normalize = normalized ).values (
352360 "filename" ,
353361 "sha256" ,
@@ -366,17 +374,25 @@ def retrieve(self, request, path, package):
366374 for p in packages
367375 }
368376 releases .update (local_releases )
369- if not releases :
377+ if metadatas is not None :
378+ local_project_metadata = (
379+ metadatas .filter (project_name = normalized )
380+ .values ("tracks" , "alternate_locations" )
381+ .first ()
382+ )
383+ if local_project_metadata :
384+ project_metadata .update (local_project_metadata )
385+ if not (releases or project_metadata ):
370386 return HttpResponseNotFound (f"{ normalized } does not exist." )
371387
372388 media_type = request .accepted_renderer .media_type
373389 headers = {"X-PyPI-Last-Serial" : str (PYPI_SERIAL_CONSTANT )}
374390
375391 if media_type == PYPI_SIMPLE_V1_JSON :
376- detail_data = write_simple_detail_json (normalized , releases .values ())
392+ detail_data = write_simple_detail_json (normalized , releases .values (), project_metadata )
377393 return Response (detail_data , headers = headers )
378394 else :
379- detail_data = write_simple_detail (normalized , releases .values ())
395+ detail_data = write_simple_detail (normalized , releases .values (), project_metadata )
380396 kwargs = {"content_type" : media_type , "headers" : headers }
381397 return HttpResponse (detail_data , ** kwargs )
382398
0 commit comments