about summary refs log tree commit diff
path: root/pkgs/development/interpreters/python/catch_conflicts/catch_conflicts.py
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/development/interpreters/python/catch_conflicts/catch_conflicts.py')
-rw-r--r--pkgs/development/interpreters/python/catch_conflicts/catch_conflicts.py70
1 files changed, 51 insertions, 19 deletions
diff --git a/pkgs/development/interpreters/python/catch_conflicts/catch_conflicts.py b/pkgs/development/interpreters/python/catch_conflicts/catch_conflicts.py
index 9339c62a6399e..319b92bee3bcf 100644
--- a/pkgs/development/interpreters/python/catch_conflicts/catch_conflicts.py
+++ b/pkgs/development/interpreters/python/catch_conflicts/catch_conflicts.py
@@ -5,39 +5,71 @@ import sys
 import os
 
 
-do_abort = False
-packages = collections.defaultdict(list)
-out_path = Path(os.getenv("out"))
-version = sys.version_info
-site_packages_path = f'lib/python{version[0]}.{version[1]}/site-packages'
+do_abort: bool = False
+packages: dict[str, dict[str, list[dict[str, list[str]]]]] = collections.defaultdict(list)
+out_path: Path = Path(os.getenv("out"))
+version: tuple[int, int] = sys.version_info
+site_packages_path: str = f'lib/python{version[0]}.{version[1]}/site-packages'
 
 
-def find_packages(store_path, site_packages_path):
-    site_packages = (store_path / site_packages_path)
-    propagated_build_inputs = (store_path / "nix-support/propagated-build-inputs")
+# pretty print a package
+def describe_package(dist: PathDistribution) -> str:
+    return f"{dist._normalized_name} {dist.version} ({dist._path})"
+
+
+# pretty print a list of parents (dependency chain)
+def describe_parents(parents: list[str]) -> str:
+    if not parents:
+        return ""
+    return \
+        f"    dependency chain:\n      " \
+        + str(f"\n      ...depending on: ".join(parents))
+
+
+# inserts an entry into 'packages'
+def add_entry(name: str, version: str, store_path: str, parents: list[str]) -> None:
+    if name not in packages:
+        packages[name] = {}
+    if store_path not in packages[name]:
+        packages[name][store_path] = []
+    packages[name][store_path].append(dict(
+        version=version,
+        parents=parents,
+    ))
+
+
+# transitively discover python dependencies and store them in 'packages'
+def find_packages(store_path: Path, site_packages_path: str, parents: list[str]) -> None:
+    site_packages: Path = (store_path / site_packages_path)
+    propagated_build_inputs: Path = (store_path / "nix-support/propagated-build-inputs")
+
+    # add the current package to the list
     if site_packages.exists():
         for dist_info in site_packages.glob("*.dist-info"):
-            dist = PathDistribution(dist_info)
-            packages[dist._normalized_name].append(
-                f"{dist._normalized_name} {dist.version} ({dist._path})"
-            )
+            dist: PathDistribution = PathDistribution(dist_info)
+            add_entry(dist._normalized_name, dist.version, store_path, parents)
 
+    # recursively add dependencies
     if propagated_build_inputs.exists():
         with open(propagated_build_inputs, "r") as f:
-            build_inputs = f.read().strip().split(" ")
+            build_inputs: list[str] = f.read().strip().split(" ")
             for build_input in build_inputs:
-                find_packages(Path(build_input), site_packages_path)
+                find_packages(Path(build_input), site_packages_path, parents + [build_input])
 
 
-find_packages(out_path, site_packages_path)
+find_packages(out_path, site_packages_path, [f"this derivation: {out_path}"])
 
-for name, duplicates in packages.items():
-    if len(duplicates) > 1:
+# print all duplicates
+for name, store_paths in packages.items():
+    if len(store_paths) > 1:
         do_abort = True
         print("Found duplicated packages in closure for dependency '{}': ".format(name))
-        for duplicate in duplicates:
-            print(f"\t{duplicate}")
+        for store_path, candidates in store_paths.items():
+            for candidate in candidates:
+                print(f"  {name} {candidate['version']} ({store_path})")
+                print(describe_parents(candidate['parents']))
 
+# fail if duplicates were found
 if do_abort:
     print("")
     print(