Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit fbc603e

Browse filesBrowse files
authored
Merge pull request #1620 from stonebig/master
at last... a glitch-less wheelhouse report
2 parents f299e10 + de0423c commit fbc603e
Copy full SHA for fbc603e

File tree

Expand file treeCollapse file tree

4 files changed

+70
-38
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+70
-38
lines changed

‎winpython/piptree.py

Copy file name to clipboardExpand all lines: winpython/piptree.py
+2-10Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from pip._vendor.packaging.markers import Marker
1818
from importlib.metadata import Distribution, distributions
1919
from pathlib import Path
20+
from winpython import utils
2021

2122
logging.basicConfig(level=logging.INFO)
2223
logger = logging.getLogger(__name__)
@@ -25,15 +26,6 @@ class PipDataError(Exception):
2526
"""Custom exception for PipData related errors."""
2627
pass
2728

28-
def sum_up(text: str, max_length: int = 144, stop_at: str = ". ") -> str:
29-
"""Summarize text to fit within max_length, ending at last complete sentence."""
30-
summary = (text + os.linesep).splitlines()[0]
31-
if len(summary) <= max_length:
32-
return summary
33-
if stop_at and stop_at in summary[:max_length]:
34-
return summary[:summary.rfind(stop_at, 0, max_length)] + stop_at.rstrip()
35-
return summary[:max_length].rstrip()
36-
3729
class PipData:
3830
"""Manages package metadata and dependency relationships in a Python environment."""
3931

@@ -287,5 +279,5 @@ def pip_list(self, full: bool = False, max_length: int = 144) -> List[Tuple[str,
287279
"""
288280
pkgs = sorted(self.distro.items())
289281
if full:
290-
return [(p, d["version"], sum_up(d["summary"], max_length)) for p, d in pkgs]
282+
return [(p, d["version"], utils.sum_up(d["summary"], max_length)) for p, d in pkgs]
291283
return [(p, d["version"]) for p, d in pkgs]

‎winpython/utils.py

Copy file name to clipboardExpand all lines: winpython/utils.py
+8-24Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -71,30 +71,14 @@ def onerror(function, path, excinfo):
7171
else:
7272
raise
7373

74-
def getFileProperties(fname):
75-
"""Read all properties of the given file return them as a dictionary."""
76-
import win32api
77-
prop_names = ('ProductName', 'ProductVersion', 'FileDescription', 'FileVersion')
78-
props = {'FixedFileInfo': None, 'StringFileInfo': None, 'FileVersion': None}
79-
80-
try:
81-
fixed_info = win32api.GetFileVersionInfo(fname, '\\')
82-
props['FixedFileInfo'] = fixed_info
83-
props['FileVersion'] = "{}.{}.{}.{}".format(
84-
fixed_info['FileVersionMS'] // 65536,
85-
fixed_info['FileVersionMS'] % 65536,
86-
fixed_info['FileVersionLS'] // 65536,
87-
fixed_info['FileVersionLS'] % 65536
88-
)
89-
lang, codepage = win32api.GetFileVersionInfo(fname, '\\VarFileInfo\\Translation')[0]
90-
props['StringFileInfo'] = {
91-
prop_name: win32api.GetFileVersionInfo(fname, f'\\StringFileInfo\\{lang:04X}{codepage:04X}\\{prop_name}')
92-
for prop_name in prop_names
93-
}
94-
except:
95-
pass
96-
97-
return props
74+
def sum_up(text: str, max_length: int = 144, stop_at: str = ". ") -> str:
75+
"""Summarize text to fit within max_length, ending at last complete sentence."""
76+
summary = (text + os.linesep).splitlines()[0].strip()
77+
if len(summary) <= max_length:
78+
return summary
79+
if stop_at and stop_at in summary[:max_length]:
80+
return summary[:summary.rfind(stop_at, 0, max_length)] + stop_at.strip()
81+
return summary[:max_length].strip()
9882

9983
def get_special_folder_path(path_name):
10084
"""Return special folder path."""

‎winpython/wheelhouse.py

Copy file name to clipboardExpand all lines: winpython/wheelhouse.py
+57-1Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,22 @@
22
"""
33
WheelHouse.py - manage WinPython local WheelHouse.
44
"""
5-
5+
import os
6+
import re
7+
import tarfile
8+
import zipfile
69
import sys
710
from pathlib import Path
811
from collections import defaultdict
912
import shutil
1013
import subprocess
1114
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
1221

1322
# Use tomllib if available (Python 3.11+), otherwise fall back to tomli
1423
try:
@@ -183,6 +192,53 @@ def get_pylock_wheels(wheelhouse: Path, lockfile: Path, wheelorigin: Optional[Pa
183192
else:
184193
print(f"\n\n*** We can't install {filename} ! ***\n\n")
185194

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+
186242
def main() -> None:
187243
"""Main entry point for the script."""
188244
if len(sys.argv) != 2:

‎winpython/wppm.py

Copy file name to clipboardExpand all lines: winpython/wppm.py
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@
1616
from argparse import ArgumentParser, RawTextHelpFormatter
1717
from winpython import utils, piptree, associate
1818
from winpython import wheelhouse as wh
19+
from operator import itemgetter
1920
# Workaround for installing PyVISA on Windows from source:
2021
os.environ["HOME"] = os.environ["USERPROFILE"]
2122

2223
class Package:
2324
"""Standardize a Package from filename or pip list."""
2425
def __init__(self, fname: str, suggested_summary: str = None):
2526
self.fname = fname
26-
self.description = piptree.sum_up(suggested_summary) if suggested_summary else ""
27+
self.description = (utils.sum_up(suggested_summary) if suggested_summary else "").strip()
2728
self.name, self.version = fname, '?.?.?'
2829
if fname.lower().endswith((".zip", ".tar.gz", ".whl")):
2930
bname = Path(self.fname).name # e.g., "sqlite_bro-1.0.0..."
@@ -81,8 +82,7 @@ def get_wheelhouse_packages_markdown(self) -> str:
8182
if wheeldir.is_dir():
8283
package_lines = [
8384
f"[{name}](https://pypi.org/project/{name}) | {version} | {summary}"
84-
for name, version, summary in wh.list_packages_with_metadata(str(wheeldir))
85-
#for pkg in sorted(wh.list_packages_with_metadata(str(wheeldir)), key=lambda p: p.name.lower())
85+
for name, version, summary in sorted(wh.list_packages_with_metadata(str(wheeldir)), key=itemgetter(0 , 1)) # lambda p: p[0].lower())
8686
]
8787
return "\n".join(package_lines)
8888
return ""

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.