1+ import hashlib
12import pkginfo
23import re
34import shutil
45import tempfile
6+ import zipfile
57import json
68from collections import defaultdict
79from django .conf import settings
@@ -130,7 +132,7 @@ def parse_project_metadata(project):
130132 # Release metadata
131133 "packagetype" : project .get ("packagetype" ) or "" ,
132134 "python_version" : project .get ("python_version" ) or "" ,
133- "metadata_sha256" : "" , # TODO
135+ "metadata_sha256" : project . get ( "metadata_sha256" ) or "" ,
134136 }
135137
136138
@@ -158,9 +160,9 @@ def parse_metadata(project, version, distribution):
158160 package ["url" ] = distribution .get ("url" ) or ""
159161 package ["sha256" ] = distribution .get ("digests" , {}).get ("sha256" ) or ""
160162 package ["python_version" ] = distribution .get ("python_version" ) or package .get ("python_version" )
161- package ["requires_python" ] = distribution .get ("requires_python" ) or package .get (
162- "requires_python"
163- ) # noqa: E501
163+ package ["requires_python" ] = distribution .get ("requires_python" ) or "" # package.get(
164+ # "requires_python"
165+ # ) # noqa: E501
164166 package ["metadata_sha256" ] = distribution .get ("data-dist-info-metadata" , {}).get (
165167 "sha256"
166168 ) or package .get ("metadata_sha256" )
@@ -181,6 +183,7 @@ def get_project_metadata_from_file(filename):
181183 packagetype = DIST_EXTENSIONS [extensions [pkg_type_index ]]
182184
183185 metadata = DIST_TYPES [packagetype ](filename )
186+ metadata .metadata_sha256 = compute_metadata_sha256 (filename )
184187 metadata .packagetype = packagetype
185188 if packagetype == "sdist" :
186189 metadata .python_version = "source"
@@ -193,6 +196,29 @@ def get_project_metadata_from_file(filename):
193196 return metadata
194197
195198
199+ def compute_metadata_sha256 (filename : str ) -> str :
200+ """
201+ Compute SHA256 hash of the metadata file from a Python package.
202+
203+ Returns SHA256 hash or empty string if metadata cannot be extracted.
204+ """
205+ if not filename .endswith (".whl" ):
206+ return ""
207+ try :
208+ with tempfile .NamedTemporaryFile () as temp_file :
209+ with open (filename , "rb" ) as source :
210+ shutil .copyfileobj (source , temp_file )
211+ temp_file .flush ()
212+ with zipfile .ZipFile (temp_file .name , "r" ) as f :
213+ for file_path in f .namelist ():
214+ if file_path .endswith (".dist-info/METADATA" ):
215+ metadata_content = f .read (file_path )
216+ return hashlib .sha256 (metadata_content ).hexdigest ()
217+ except Exception :
218+ pass
219+ return ""
220+
221+
196222def artifact_to_python_content_data (filename , artifact , domain = None ):
197223 """
198224 Takes the artifact/filename and returns the metadata needed to create a PythonPackageContent.
@@ -448,7 +474,7 @@ def write_simple_detail_json(project_name, project_packages):
448474 "filename" : package ["filename" ],
449475 "url" : package ["url" ],
450476 "hashes" : {"sha256" : package ["sha256" ]},
451- "requires_python " : package ["requires_python" ] or None ,
477+ "requires-python " : package ["requires_python" ] or None ,
452478 # data-dist-info-metadata is deprecated alias for core-metadata
453479 "data-dist-info-metadata" : (
454480 {"sha256" : package ["metadata_sha256" ]} if package ["metadata_sha256" ] else False
0 commit comments