17
17
from email .policy import default
18
18
from . import utils
19
19
20
- from packaging .utils import canonicalize_name
20
+ from packaging .utils import canonicalize_name , parse_wheel_filename , parse_sdist_filename
21
21
22
22
# Use tomllib if available (Python 3.11+), otherwise fall back to tomli
23
23
try :
@@ -193,32 +193,64 @@ def get_pylock_wheels(wheelhouse: Path, lockfile: Path, wheelorigin: Optional[Pa
193
193
print (f"\n \n *** We can't install { filename } ! ***\n \n " )
194
194
195
195
def extract_metadata_from_wheel (filepath : Path ) -> Optional [Tuple [str , str , str ]]:
196
- "get metadata from a wheel package"
196
+ "Extract package metadata from a .whl file and validate it matches the filename"
197
+ wheel_name = filepath .name
198
+ try :
199
+ name , version , build , tags = parse_wheel_filename (wheel_name )
200
+ filename_name = canonicalize_name (name )
201
+ filename_version = str (version )
202
+ except Exception as e :
203
+ print (f"❌ Could not parse filename: { wheel_name } " , e )
204
+ return None
205
+
197
206
with zipfile .ZipFile (filepath , 'r' ) as z :
198
207
# Locate *.dist-info/METADATA file inside but not in a vendored directory (flit-core)
199
208
for name in z .namelist ():
200
209
if name .endswith (r'.dist-info/METADATA' ) and name .split ("/" )[1 ] == "METADATA" :
201
210
with z .open (name ) as meta_file :
202
211
metadata = BytesParser (policy = default ).parse (meta_file )
203
- name = canonicalize_name (str (metadata .get ('Name' , 'unknown' ))) # Avoid Head type
204
- version = str (metadata .get ('Version' , 'unknown' ))
212
+ meta_name = canonicalize_name (str (metadata .get ('Name' , 'unknown' ))) # Avoid Head type
213
+ meta_version = str (metadata .get ('Version' , 'unknown' ))
205
214
summary = utils .sum_up (str (metadata .get ('Summary' , '' )))
206
- return name , version , summary
215
+ # Assert consistency
216
+ if meta_name != filename_name or meta_version != filename_version :
217
+ print (f"⚠️ Mismatch in { wheel_name } : filename says { filename_name } =={ filename_version } , "
218
+ f"but METADATA says { meta_name } =={ meta_version } " )
219
+ return None
220
+ return meta_name , meta_version , summary
207
221
return None
208
222
209
223
def extract_metadata_from_sdist (filepath : Path ) -> Optional [Tuple [str , str , str ]]:
210
224
"get metadata from a tar.gz or .zip package"
211
225
open_func = tarfile .open if filepath .suffixes [- 2 :] == ['.tar' , '.gz' ] else zipfile .ZipFile
226
+ sdist_name = filepath .name
227
+ try :
228
+ name , version = parse_sdist_filename (sdist_name )
229
+ filename_name = canonicalize_name (name )
230
+ filename_version = str (version )
231
+ except Exception as e :
232
+ print (f"❌ Could not parse filename: { sdist_name } " , e )
233
+ return None
234
+
212
235
with open_func (filepath , 'r' ) as archive :
213
236
namelist = archive .getnames () if isinstance (archive , tarfile .TarFile ) else archive .namelist ()
214
237
for name in namelist :
215
238
if name .endswith ('PKG-INFO' ):
216
- content = archive .extractfile (name ).read () if isinstance (archive , tarfile .TarFile ) else archive .open (name ).read ()
217
- metadata = message_from_bytes (content )
218
- name = canonicalize_name (str (metadata .get ('Name' , 'unknown' ))) # Avoid Head type
219
- version = str (metadata .get ('Version' , 'unknown' ))
220
- summary = utils .sum_up (str (metadata .get ('Summary' , '' )))
221
- return name , version , summary
239
+ if isinstance (archive , tarfile .TarFile ):
240
+ content = archive .extractfile (name )
241
+ else :
242
+ content = archive .open (name )
243
+ if content :
244
+ metadata = BytesParser (policy = default ).parse (content )
245
+ meta_name = canonicalize_name (str (metadata .get ('Name' , 'unknown' ))) # Avoid Head type
246
+ meta_version = str (metadata .get ('Version' , 'unknown' ))
247
+ summary = utils .sum_up (str (metadata .get ('Summary' , '' )))
248
+ # Assert consistency
249
+ if meta_name != filename_name or meta_version != filename_version :
250
+ print (f"⚠️ Mismatch in { sdist_name } : filename says { filename_name } =={ filename_version } , "
251
+ f"but METADATA says { meta_name } =={ meta_version } " )
252
+ return None
253
+ return meta_name , meta_version , summary
222
254
return None
223
255
224
256
def list_packages_with_metadata (directory : str ) -> List [Tuple [str , str , str ]]:
0 commit comments