"""Diff dependency manifests to produce PackageDiff.""" from __future__ import annotations import json import re from scripts.manifest import PackageDiff, PackageEntry, PackageUpgrade _REQ_LINE_RE = re.compile( r"^\s*(?P[A-Za-z0-9_\-.]+)\s*==\s*(?P[^\s#]+)" ) def _parse_requirements(text: str) -> dict[str, str]: out: dict[str, str] = {} for line in text.splitlines(): line = line.split("#", 1)[0] m = _REQ_LINE_RE.match(line) if m: out[m.group("name").lower()] = m.group("version") return out def _diff_maps(before: dict[str, str], after: dict[str, str]) -> PackageDiff: added = [ PackageEntry(name=n, version=v) for n, v in sorted(after.items()) if n not in before ] removed = [ PackageEntry(name=n, version=v) for n, v in sorted(before.items()) if n not in after ] upgraded = [ PackageUpgrade(name=n, from_version=before[n], to_version=after[n]) for n in sorted(before.keys() & after.keys()) if before[n] != after[n] ] return PackageDiff(added=added, removed=removed, upgraded=upgraded) def diff_requirements_txt(before: str, after: str) -> PackageDiff: return _diff_maps(_parse_requirements(before), _parse_requirements(after)) def _parse_package_json(text: str) -> dict[str, str]: try: doc = json.loads(text) except json.JSONDecodeError: return {} out: dict[str, str] = {} for key in ("dependencies", "devDependencies"): section = doc.get(key) or {} if isinstance(section, dict): for name, version in section.items(): if isinstance(version, str): out[name.lower()] = version return out def diff_package_json(before: str, after: str) -> PackageDiff: return _diff_maps(_parse_package_json(before), _parse_package_json(after))