1+ #!/usr/bin/env python3
2+ """
3+ packagemetadata.py - get metadata from designated place
4+ """
5+ import os
6+ import re
7+ import tarfile
8+ import zipfile
9+ import sys
10+ from pathlib import Path
11+ from collections import defaultdict
12+ import shutil
13+ import subprocess
14+ from typing import Dict , List , Optional , Tuple
15+ from . import utils
16+
17+ from packaging .utils import canonicalize_name , parse_wheel_filename , parse_sdist_filename
18+ # --- Abstract metadata accessor ---
19+
20+ class PackageMetadata :
21+ """A minimal abstraction for package metadata."""
22+ def __init__ (self , name , version , requires , summary , description , metadata ):
23+ self .name = name
24+ self .version = version
25+ self .requires = requires # List[str] of dependencies
26+ self .summary = summary
27+ self .description = description
28+ self .metadata = metadata
29+
30+ def get_installed_metadata (path = None ) -> List [PackageMetadata ]:
31+ # Use importlib.metadata or pkg_resources
32+ import importlib .metadata
33+ pkgs = []
34+ distro = importlib .metadata .distributions (path = path ) if path else importlib .metadata .distributions ()
35+ for dist in distro :
36+ name = dist .metadata ['Name' ]
37+ version = dist .version
38+ summary = dist .metadata .get ("Summary" , "" ),
39+ description = dist .metadata .get ("Description" , "" ),
40+ requires = dist .requires or []
41+ metadata = dist .metadata
42+ pkgs .append (PackageMetadata (name , version , requires , summary , description , metadata ))
43+ return pkgs
44+
45+ def get_directory_metadata (directory : str ) -> List [PackageMetadata ]:
46+ # For each .whl/.tar.gz file in directory, extract metadata
47+ pkgs = []
48+ for fname in os .listdir (directory ):
49+ if fname .endswith ('.whl' ):
50+ # Extract METADATA from wheel
51+ meta = extract_metadata_from_wheel (os .path .join (directory , fname ))
52+ pkgs .append (meta )
53+ elif fname .endswith ('.tar.gz' ):
54+ # Extract PKG-INFO from sdist
55+ meta = extract_metadata_from_sdist (os .path .join (directory , fname ))
56+ pkgs .append (meta )
57+ return pkgs
58+
59+ def extract_metadata_from_wheel (path : str ) -> PackageMetadata :
60+ import zipfile
61+ with zipfile .ZipFile (path ) as zf :
62+ for name in zf .namelist ():
63+ if name .endswith (r'.dist-info/METADATA' ) and name .split ("/" )[1 ] == "METADATA" :
64+ with zf .open (name ) as f :
65+ # Parse metadata (simple parsing for Name, Version, Requires-Dist)
66+ return parse_metadata_file (f .read ().decode ())
67+ raise ValueError (f"No METADATA found in { path } " )
68+
69+ def extract_metadata_from_sdist (path : str ) -> PackageMetadata :
70+ import tarfile
71+ with tarfile .open (path , "r:gz" ) as tf :
72+ for member in tf .getmembers ():
73+ if member .name .endswith ('PKG-INFO' ):
74+ f = tf .extractfile (member )
75+ return parse_metadata_file (f .read ().decode ())
76+ raise ValueError (f"No PKG-INFO found in { path } " )
77+
78+ def parse_metadata_file (txt : str ) -> PackageMetadata :
79+ name = version = summary = description = ""
80+ requires = []
81+ for line in txt .splitlines ():
82+ if line .startswith ('Name: ' ):
83+ name = line [6 :].strip ()
84+ elif line .startswith ('Version: ' ):
85+ version = line [9 :].strip ()
86+ elif line .startswith ('Summary: ' ):
87+ summary = description = line [9 :].strip ()
88+ elif line .startswith ('Requires-Dist: ' ):
89+ requires .append (line [14 :].strip ())
90+ return PackageMetadata (name , version , requires , summary , description , {'Name' : name , "Summary" : summary , "Description" : description })
91+
92+ # --- Main dependency tree logic ---
93+
94+ def build_dependency_tree (pkgs : List [PackageMetadata ]):
95+ # Existing logic, but using our PackageMetadata objects
96+ pass
97+
98+ def main ():
99+ if len (sys .argv ) > 1 :
100+ # Directory mode
101+ directory = sys .argv [1 ]
102+ pkgs = get_directory_metadata (directory )
103+ else :
104+ # Installed packages mode
105+ pkgs = get_installed_metadata ()
106+ build_dependency_tree (pkgs )
107+
108+ if __name__ == "__main__" :
109+ main ()
0 commit comments