Skip to content

Commit bdb7288

Browse files
committed
-md markdown and -ws . options for comfort
1 parent c017a18 commit bdb7288

File tree

4 files changed

+24
-97
lines changed

4 files changed

+24
-97
lines changed

winpython/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,6 @@
2828
OTHER DEALINGS IN THE SOFTWARE.
2929
"""
3030

31-
__version__ = '16.3.20250530'
31+
__version__ = '16.4.20250603'
3232
__license__ = __doc__
3333
__project_url__ = 'http://winpython.github.io/'

winpython/packagemetadata.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from typing import Dict, List, Optional, Tuple
1515
from . import utils
1616

17-
from packaging.utils import canonicalize_name, parse_wheel_filename, parse_sdist_filename
1817
# --- Abstract metadata accessor ---
1918

2019
class PackageMetadata:
@@ -64,7 +63,7 @@ def extract_metadata_from_wheel(path: str) -> PackageMetadata:
6463
with zf.open(name) as f:
6564
# Parse metadata (simple parsing for Name, Version, Requires-Dist)
6665
return parse_metadata_file(f.read().decode())
67-
raise ValueError(f"No METADATA found in {path}")
66+
raise ValueError(f"No METADATA found in {path}")
6867

6968
def extract_metadata_from_sdist(path: str) -> PackageMetadata:
7069
import tarfile
@@ -78,6 +77,8 @@ def extract_metadata_from_sdist(path: str) -> PackageMetadata:
7877
def parse_metadata_file(txt: str) -> PackageMetadata:
7978
name = version = summary = description = ""
8079
requires = []
80+
description_lines = []
81+
in_description = False
8182
for line in txt.splitlines():
8283
if line.startswith('Name: '):
8384
name = line[6:].strip()
@@ -86,15 +87,9 @@ def parse_metadata_file(txt: str) -> PackageMetadata:
8687
elif line.startswith('Summary: '):
8788
summary = description = line[9:].strip()
8889
elif line.startswith('Requires-Dist: '):
89-
requires.append(line[14:].strip())
90+
requires.append(line[14:].strip())
9091
return PackageMetadata(name, version, requires, summary, description, {'Name': name, "Summary": summary, "Description": description})
9192

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-
9893
def main():
9994
if len(sys.argv) > 1:
10095
# Directory mode
@@ -103,7 +98,6 @@ def main():
10398
else:
10499
# Installed packages mode
105100
pkgs = get_installed_metadata()
106-
build_dependency_tree(pkgs)
107101

108102
if __name__ == "__main__":
109103
main()

winpython/wheelhouse.py

Lines changed: 3 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@
1212
import shutil
1313
import subprocess
1414
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
15+
from . import packagemetadata as pm
1816
from . import utils
1917

2018
from packaging.utils import canonicalize_name, parse_wheel_filename, parse_sdist_filename
@@ -192,83 +190,10 @@ def get_pylock_wheels(wheelhouse: Path, lockfile: Path, wheelorigin: Optional[Pa
192190
else:
193191
print(f"\n\n*** We can't install {filename} ! ***\n\n")
194192

195-
def extract_metadata_from_wheel(filepath: Path) -> Optional[Tuple[str, str, str]]:
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-
206-
with zipfile.ZipFile(filepath, 'r') as z:
207-
# Locate *.dist-info/METADATA file inside but not in a vendored directory (flit-core)
208-
for name in z.namelist():
209-
if name.endswith(r'.dist-info/METADATA') and name.split("/")[1] == "METADATA":
210-
with z.open(name) as meta_file:
211-
metadata = BytesParser(policy=default).parse(meta_file)
212-
meta_name = canonicalize_name(str(metadata.get('Name', 'unknown'))) # Avoid Head type
213-
meta_version = str(metadata.get('Version', 'unknown'))
214-
summary = utils.sum_up(str(metadata.get('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
221-
return None
222-
223-
def extract_metadata_from_sdist(filepath: Path) -> Optional[Tuple[str, str, str]]:
224-
"get metadata from a tar.gz or .zip package"
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-
235-
with open_func(filepath, 'r') as archive:
236-
namelist = archive.getnames() if isinstance(archive, tarfile.TarFile) else archive.namelist()
237-
for name in namelist:
238-
if name.endswith('PKG-INFO'):
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
254-
return None
255-
256193
def list_packages_with_metadata(directory: str) -> List[Tuple[str, str, str]]:
257194
"get metadata from a Wheelhouse directory"
258-
results = []
259-
for file in os.listdir(directory):
260-
path = Path(directory) / file
261-
try:
262-
if path.suffix == '.whl':
263-
meta = extract_metadata_from_wheel(path)
264-
elif path.suffix == '.zip' or path.name.endswith('.tar.gz'):
265-
meta = extract_metadata_from_sdist(path)
266-
else:
267-
continue
268-
if meta:
269-
results.append(meta)
270-
except OSError: #Exception as e: # need to see it
271-
print(f"Skipping {file}: {e}")
195+
packages = pm.get_directory_metadata(directory)
196+
results = [ (p.name, p.version, p.summary) for p in packages]
272197
return results
273198

274199
def main() -> None:

winpython/wppm.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -274,14 +274,15 @@ def main(test=False):
274274
# parser.add_argument( "--unregister_forall", action="store_true", help="un-Register distribution for all users")
275275
parser.add_argument("--fix", action="store_true", help="make WinPython fix")
276276
parser.add_argument("--movable", action="store_true", help="make WinPython movable")
277-
parser.add_argument("-ws", dest="wheelsource", default=None, type=str, help="location to search wheels: wppm pylock.toml -ws source_of_wheels")
278-
parser.add_argument("-wd", dest="wheeldrain" , default=None, type=str, help="location of found wheels: wppm pylock.toml -wd destination_of_wheels")
279-
parser.add_argument("-ls", "--list", action="store_true", help="list installed packages matching the given [optional] package expression: wppm -ls, wppm -ls pand")
280-
parser.add_argument("-lsa", dest="all", action="store_true",help=f"list details of package names matching given regular expression: wppm -lsa pandas -l1")
281-
parser.add_argument("-p",dest="pipdown",action="store_true",help="show Package dependencies of the given package[option]: wppm -p pandas[test]")
282-
parser.add_argument("-r", dest="pipup", action="store_true", help=f"show Reverse dependancies of the given package[option]: wppm -r pytest[test]")
283-
parser.add_argument("-l", "--levels", type=int, default=2, help="show 'LEVELS' levels of dependencies (with -p, -r), default is 2: wppm -p pandas -l1")
284-
parser.add_argument("-t", "--target", default=sys.prefix, help=f'path to target Python distribution (default: "{sys.prefix}")')
277+
parser.add_argument("-ws", dest="wheelsource", default=None, type=str, help="wheels location, '.' = WheelHouse): wppm pylock.toml -ws source_of_wheels, wppm -ls -ws .")
278+
parser.add_argument("-wd", dest="wheeldrain" , default=None, type=str, help="wheels destination: wppm pylock.toml -wd destination_of_wheels")
279+
parser.add_argument("-ls", "--list", action="store_true", help="list installed packages matching [optional] expression: wppm -ls, wppm -ls pand")
280+
parser.add_argument("-lsa", dest="all", action="store_true",help=f"list details of packages matching [optional] expression: wppm -lsa pandas -l1")
281+
parser.add_argument("-md", dest="markdown", action="store_true",help=f"markdown summary if the installation")
282+
parser.add_argument("-p",dest="pipdown",action="store_true",help="show Package dependencies of the given package[option], [.]=all: wppm -p pandas[.]")
283+
parser.add_argument("-r", dest="pipup", action="store_true", help=f"show Reverse wppmdependancies of the given package[option]: wppm -r pytest[test]")
284+
parser.add_argument("-l", dest="levels", type=int, default=2, help="show 'LEVELS' levels of dependencies (with -p, -r), default is 2: wppm -p pandas -l1")
285+
parser.add_argument("-t", dest="target", default=sys.prefix, help=f'path to target Python distribution (default: "{sys.prefix}")')
285286
parser.add_argument("-i", "--install", action="store_true", help="install a given package wheel or pylock file (use pip for more features)")
286287
parser.add_argument("-u", "--uninstall", action="store_true", help="uninstall package (use pip for more features)")
287288

@@ -290,6 +291,10 @@ def main(test=False):
290291
targetpython = None
291292
if args.target and args.target != sys.prefix:
292293
targetpython = args.target if args.target.lower().endswith('.exe') else str(Path(args.target) / 'python.exe')
294+
if args.wheelsource == ".": # play in default WheelHouse
295+
if utils.is_python_distribution(args.target):
296+
dist = Distribution(args.target)
297+
args.wheelsource = dist.wheelhouse / 'included.wheels'
293298
if args.install and args.uninstall:
294299
raise RuntimeError("Incompatible arguments: --install and --uninstall")
295300
if args.registerWinPython and args.unregisterWinPython:
@@ -360,6 +365,9 @@ def main(test=False):
360365
if args.movable:
361366
p = subprocess.Popen(["start", "cmd", "/k",dist.python_exe, "-c" , cmd_mov], shell = True, cwd=dist.target)
362367
sys.exit()
368+
if args.markdown:
369+
print(dist.generate_package_index_markdown())
370+
sys.exit()
363371
if not args.install and not args.uninstall and args.fname.endswith(".toml"):
364372
args.install = True # for Drag & Drop of .toml (and not wheel)
365373
if args.fname == "" or (not args.install and not args.uninstall):

0 commit comments

Comments
 (0)