1111from zipfile import ZipFile
1212
1313import anytree
14- import pkginfo
1514import toml
1615import tabulate
1716import wimpy
@@ -158,18 +157,14 @@ def children(self):
158157
159158 @property
160159 def homepage (self ):
161- for project_url in self .metadata .get ("project_urls " , []):
160+ for project_url in self .metadata .get ("project_url " , []):
162161 if project_url .lower ().startswith ("homepage, " ):
163162 _ , url = project_url .split (", " , 1 )
164163 return url
165164 try :
166165 return self .metadata ["home_page" ]
167166 except KeyError :
168- for k in "python.details" , "python.project" :
169- try :
170- return self .metadata ["extensions" ][k ]["project_urls" ]["Home" ]
171- except KeyError :
172- pass
167+ pass
173168 self .log .info ("unknown homepage" )
174169
175170 @property
@@ -185,7 +180,7 @@ def license(self):
185180 # for a list of valid classifiers:
186181 # requests.get('https://pypi.python.org/pypi', params={':action': 'list_classifiers'}).text.splitlines()
187182 self .log .debug ("metadata license is not set, checking trove classifiers" )
188- for classifier in self .metadata .get ("classifiers " , []):
183+ for classifier in self .metadata .get ("classifier " , []):
189184 if classifier .startswith ("License :: " ):
190185 crap , result = classifier .rsplit (" :: " , 1 )
191186 break
@@ -243,10 +238,10 @@ def version_latest_in_spec(self):
243238
244239 @property
245240 def extras_available (self ):
246- extras = {x for x in self .metadata .get ("provides_extras " , []) if x }
241+ extras = {x for x in self .metadata .get ("provides_extra " , []) if x }
247242 for req_str in self .metadata .get ("requires_dist" , []):
248243 req = requirements .Requirement (req_str )
249- extras |= set (re .findall (r' extra == " (.*?)"' , str (req .marker )))
244+ extras |= set (re .findall (r""" extra == ['"] (.*?)['"]""" , str (req .marker )))
250245 return sorted (extras )
251246
252247 @property
@@ -256,7 +251,7 @@ def project_name(self):
256251 @property
257252 def console_scripts (self ):
258253 eps = [ep for ep in self .entry_points or [] if ep .group == "console_scripts" ]
259- return ", " . join ( ["{} = {}" .format (ep .name , ep .value ) for ep in eps ])
254+ return ["{} = {}" .format (ep .name , ep .value ) for ep in eps ]
260255
261256 @property
262257 def pinned (self ):
@@ -435,52 +430,78 @@ def _discover_import_names(whl_file):
435430 try :
436431 [top_level_fname ] = [x for x in namelist if x .endswith ("top_level.txt" )]
437432 except ValueError :
438- log .debug ("top_level absent, trying metadata" )
439- metadata = _extract_metadata (whl_file )
440- try :
441- public_names = metadata ["python.exports" ]["modules" ] or []
442- except KeyError :
443- # this dist was packaged by a dinosaur, exports is not in metadata.
444- # we gotta do it the hard way ...
445- public_names = []
446- for name in namelist :
447- if ".dist-info/" not in name and ".egg-info/" not in name :
448- parts = name .split ("/" )
449- if len (parts ) == 2 and parts [1 ] == "__init__.py" :
450- # found a top-level package
451- public_names .append (parts [0 ])
452- elif len (parts ) == 1 :
453- # TODO: find or make an exhaustive list of file extensions importable
454- name , ext = os .path .splitext (parts [0 ])
455- if ext == ".py" or ext == ".so" :
456- # found a top level module
457- public_names .append (name )
433+ log .debug ("top_level.txt absent, iterating contents" )
434+ # we gotta do it the hard way ...
435+ public_names = []
436+ for name in namelist :
437+ if ".dist-info/" not in name and ".egg-info/" not in name :
438+ parts = name .split ("/" )
439+ if len (parts ) == 2 and parts [1 ] == "__init__.py" :
440+ # found a top-level package
441+ public_names .append (parts [0 ])
442+ elif len (parts ) == 1 :
443+ # TODO: find or make an exhaustive list of file extensions importable
444+ name , ext = os .path .splitext (parts [0 ])
445+ if ext == ".py" or ext == ".so" :
446+ # found a top level module
447+ public_names .append (name )
458448 else :
459449 all_names = zf .read (top_level_fname ).decode ("utf-8" ).strip ().splitlines ()
460450 public_names = [n for n in all_names if not n .startswith ("_" )]
461451 result = [n .replace ("/" , "." ) for n in public_names ]
462452 return result
463453
464454
465- def _discover_entry_points (whl_file ):
466- log = logger .bind (whl_file = whl_file )
467- log .debug ("finding entry points" )
455+ def _path_dist (whl_file ):
468456 parts = os .path .basename (whl_file ).split ("-" )
469457 metadata_path = "-" .join (parts [:2 ]) + ".dist-info/"
470458 zf_path = zipfile_path (whl_file , metadata_path )
471- path_dist = PathDistribution (zf_path )
459+ return PathDistribution (zf_path )
460+
461+
462+ def _discover_entry_points (whl_file ):
463+ log = logger .bind (whl_file = whl_file )
464+ log .debug ("finding entry points" )
465+ path_dist = _path_dist (whl_file )
472466 return path_dist .entry_points
473467
474468
475469def _extract_metadata (whl_file ):
476470 log = logger .bind (whl_file = whl_file )
477- log .debug ("searching metadata" , whl_file = whl_file )
478- info = pkginfo .get_metadata (whl_file )
479- if info is None :
480- raise JohnnyError ("failed to get metadata" )
481- data = {k .lower (): v for k ,v in vars (info ).items ()}
482- data .pop ("filename" , None )
483- return data
471+ log .debug ("finding metadata" , whl_file = whl_file )
472+ path_dist = _path_dist (whl_file )
473+ message = path_dist .metadata
474+ try :
475+ result = message .json
476+ except AttributeError :
477+ # older python
478+ multiple_use_keys = {
479+ "Classifier" ,
480+ "Platform" ,
481+ "Requires-External" ,
482+ "Obsoletes-Dist" ,
483+ "Supported-Platform" ,
484+ "Provides-Dist" ,
485+ "Requires-Dist" ,
486+ "Project-URL" ,
487+ "Provides-Extra" ,
488+ "Dynamic" ,
489+ }
490+ result = {}
491+ # https://peps.python.org/pep-0566/#json-compatible-metadata
492+ for orig_key in message .keys ():
493+ k = orig_key .lower ().replace ("-" , "_" )
494+ if k in result :
495+ continue
496+ if orig_key in multiple_use_keys :
497+ result [k ] = message .get_all (orig_key )
498+ else :
499+ result [k ] = message [orig_key ]
500+ if "description" not in result :
501+ payload = message .get_payload ()
502+ if payload :
503+ result ["description" ] = payload
504+ return result
484505
485506
486507def has_error (dist ):
0 commit comments