|
2 | 2 | """
|
3 | 3 | WheelHouse.py - manage WinPython local WheelHouse.
|
4 | 4 | """
|
5 |
| - |
| 5 | +import os |
| 6 | +import re |
| 7 | +import tarfile |
| 8 | +import zipfile |
6 | 9 | import sys
|
7 | 10 | from pathlib import Path
|
8 | 11 | from collections import defaultdict
|
9 | 12 | import shutil
|
10 | 13 | import subprocess
|
11 | 14 | from typing import Dict, List, Optional, Tuple
|
| 15 | +from email import message_from_bytes |
| 16 | +from email.parser import BytesParser |
| 17 | +from email.policy import default |
| 18 | +from . import utils |
| 19 | + |
| 20 | +from packaging.utils import canonicalize_name |
12 | 21 |
|
13 | 22 | # Use tomllib if available (Python 3.11+), otherwise fall back to tomli
|
14 | 23 | try:
|
@@ -183,6 +192,53 @@ def get_pylock_wheels(wheelhouse: Path, lockfile: Path, wheelorigin: Optional[Pa
|
183 | 192 | else:
|
184 | 193 | print(f"\n\n*** We can't install {filename} ! ***\n\n")
|
185 | 194 |
|
| 195 | +def extract_metadata_from_wheel(filepath: Path) -> Optional[Tuple[str, str, str]]: |
| 196 | + "get metadata from a wheel package" |
| 197 | + with zipfile.ZipFile(filepath, 'r') as z: |
| 198 | + # Locate *.dist-info/METADATA file inside but not in a vendored directory (flit-core) |
| 199 | + for name in z.namelist(): |
| 200 | + if name.endswith(r'.dist-info/METADATA') and name.split("/")[1] == "METADATA": |
| 201 | + with z.open(name) as meta_file: |
| 202 | + 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')) |
| 205 | + summary = utils.sum_up(str(metadata.get('Summary', ''))) |
| 206 | + return name, version, summary |
| 207 | + return None |
| 208 | + |
| 209 | +def extract_metadata_from_sdist(filepath: Path) -> Optional[Tuple[str, str, str]]: |
| 210 | + "get metadata from a tar.gz or .zip package" |
| 211 | + open_func = tarfile.open if filepath.suffixes[-2:] == ['.tar', '.gz'] else zipfile.ZipFile |
| 212 | + with open_func(filepath, 'r') as archive: |
| 213 | + namelist = archive.getnames() if isinstance(archive, tarfile.TarFile) else archive.namelist() |
| 214 | + for name in namelist: |
| 215 | + 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 |
| 222 | + return None |
| 223 | + |
| 224 | +def list_packages_with_metadata(directory: str) -> List[Tuple[str, str, str]]: |
| 225 | + "get metadata from a Wheelhouse directory" |
| 226 | + results = [] |
| 227 | + for file in os.listdir(directory): |
| 228 | + path = Path(directory) / file |
| 229 | + try: |
| 230 | + if path.suffix == '.whl': |
| 231 | + meta = extract_metadata_from_wheel(path) |
| 232 | + elif path.suffix == '.zip' or path.name.endswith('.tar.gz'): |
| 233 | + meta = extract_metadata_from_sdist(path) |
| 234 | + else: |
| 235 | + continue |
| 236 | + if meta: |
| 237 | + results.append(meta) |
| 238 | + except OSError: #Exception as e: # need to see it |
| 239 | + print(f"Skipping {file}: {e}") |
| 240 | + return results |
| 241 | + |
186 | 242 | def main() -> None:
|
187 | 243 | """Main entry point for the script."""
|
188 | 244 | if len(sys.argv) != 2:
|
|
0 commit comments