about summary refs log tree commit diff
path: root/nixpkgs/pkgs/development/interpreters/python/catch_conflicts/catch_conflicts.py
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/pkgs/development/interpreters/python/catch_conflicts/catch_conflicts.py')
-rw-r--r--nixpkgs/pkgs/development/interpreters/python/catch_conflicts/catch_conflicts.py73
1 files changed, 61 insertions, 12 deletions
diff --git a/nixpkgs/pkgs/development/interpreters/python/catch_conflicts/catch_conflicts.py b/nixpkgs/pkgs/development/interpreters/python/catch_conflicts/catch_conflicts.py
index d5c99e64751c..d4219192790b 100644
--- a/nixpkgs/pkgs/development/interpreters/python/catch_conflicts/catch_conflicts.py
+++ b/nixpkgs/pkgs/development/interpreters/python/catch_conflicts/catch_conflicts.py
@@ -2,28 +2,77 @@ from importlib.metadata import PathDistribution
 from pathlib import Path
 import collections
 import sys
+import os
+from typing import Dict, List, Tuple
+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'
 
 
-do_abort = False
-packages = collections.defaultdict(list)
+def get_name(dist: PathDistribution) -> str:
+    return dist.metadata['name'].lower().replace('-', '_')
 
 
-for path in sys.path:
-    for dist_info in Path(path).glob("*.dist-info"):
-        dist = PathDistribution(dist_info)
+# pretty print a package
+def describe_package(dist: PathDistribution) -> str:
+    return f"{get_name(dist)} {dist.version} ({dist._path})"
 
-        packages[dist._normalized_name].append(
-            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))
 
-for name, duplicates in packages.items():
-    if len(duplicates) > 1:
+
+# 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 = PathDistribution(dist_info)
+            add_entry(get_name(dist), dist.version, store_path, parents)
+
+    # recursively add dependencies
+    if propagated_build_inputs.exists():
+        with open(propagated_build_inputs, "r") as f:
+            build_inputs: List[str] = f.read().strip().split(" ")
+            for build_input in build_inputs:
+                find_packages(Path(build_input), site_packages_path, parents + [build_input])
+
+
+find_packages(out_path, site_packages_path, [f"this derivation: {out_path}"])
+
+# 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(