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