/
/
/
1#!/usr/bin/env python3
2"""Parse manifest.json files to extract dependency changes.
3
4This script compares old and new versions of manifest.json files
5to identify changes in the requirements field.
6"""
7
8# ruff: noqa: T201
9import json
10import re
11import sys
12
13
14def parse_requirements(manifest_content: str) -> list[str]:
15 """Extract requirements from manifest JSON content.
16
17 :param manifest_content: JSON string content of manifest file.
18 """
19 try:
20 data = json.loads(manifest_content)
21 return data.get("requirements", [])
22 except (json.JSONDecodeError, KeyError):
23 return []
24
25
26def main() -> int:
27 """Parse manifest dependency changes."""
28 if len(sys.argv) != 3:
29 print("Usage: parse_manifest_deps.py <old_manifest> <new_manifest>")
30 return 1
31
32 old_file = sys.argv[1]
33 new_file = sys.argv[2]
34
35 try:
36 with open(old_file) as f:
37 old_reqs = parse_requirements(f.read())
38 except FileNotFoundError:
39 old_reqs = []
40
41 try:
42 with open(new_file) as f:
43 new_reqs = parse_requirements(f.read())
44 except FileNotFoundError:
45 print("Error: New manifest file not found")
46 return 1
47
48 # Find added, removed, and unchanged requirements
49 old_set = set(old_reqs)
50 new_set = set(new_reqs)
51
52 added = new_set - old_set
53 removed = old_set - new_set
54 unchanged = old_set & new_set
55
56 if not added and not removed:
57 print("No dependency changes")
58 return 0
59
60 # Helper to extract package name and create PyPI link
61 def format_with_link(req: str, emoji: str) -> str:
62 """Format requirement with PyPI link."""
63 match = re.match(r"^([a-zA-Z0-9_-]+)", req)
64 if match:
65 package = match.group(1)
66 version = req[len(package) :].strip()
67 pypi_url = f"https://pypi.org/project/{package}/"
68 return f"- {emoji} [{package}]({pypi_url}) {version}"
69 return f"- {emoji} {req}"
70
71 # Output in markdown format with PyPI links
72 if added:
73 print("**Added:**")
74 for req in sorted(added):
75 print(format_with_link(req, "â
"))
76 print()
77
78 if removed:
79 print("**Removed:**")
80 for req in sorted(removed):
81 print(format_with_link(req, "â"))
82 print()
83
84 if unchanged and (added or removed):
85 print("<details>")
86 print("<summary>Unchanged dependencies</summary>")
87 print()
88 for req in sorted(unchanged):
89 print(format_with_link(req, ""))
90 print()
91 print("</details>")
92
93 return 0
94
95
96if __name__ == "__main__":
97 sys.exit(main())
98