Get dependency graph from ruff analyze graph. Args: repo_root: Root of the repository direction: Either "dependencies" or "dependents" Returns: Dictionary mapping file paths to list of related file paths
(
repo_root: Path, direction: str = "dependents"
)
| 99 | |
| 100 | |
| 101 | def get_dependency_graph( |
| 102 | repo_root: Path, direction: str = "dependents" |
| 103 | ) -> dict[str, list[str]]: |
| 104 | """ |
| 105 | Get dependency graph from ruff analyze graph. |
| 106 | |
| 107 | Args: |
| 108 | repo_root: Root of the repository |
| 109 | direction: Either "dependencies" or "dependents" |
| 110 | |
| 111 | Returns: |
| 112 | Dictionary mapping file paths to list of related file paths |
| 113 | """ |
| 114 | try: |
| 115 | result = subprocess.run( |
| 116 | [ |
| 117 | "uvx", |
| 118 | # uv notes `analyze graph` is experimental, |
| 119 | # so we fix the ruff version for now |
| 120 | # Follow project's UV_EXCLUDE_NEWER setting to avoid resolution issues |
| 121 | "ruff@0.15.18", |
| 122 | "analyze", |
| 123 | "graph", |
| 124 | "--detect-string-imports", |
| 125 | "--direction", |
| 126 | direction, |
| 127 | ".", |
| 128 | ], |
| 129 | cwd=repo_root, |
| 130 | capture_output=True, |
| 131 | text=True, |
| 132 | check=True, |
| 133 | ) |
| 134 | |
| 135 | # Parse JSON output |
| 136 | graph: dict[str, list[str]] = json.loads(result.stdout) |
| 137 | |
| 138 | # Convert to absolute paths |
| 139 | absolute_graph = {} |
| 140 | for file_path, dependencies in graph.items(): |
| 141 | abs_path = str(repo_root / file_path) |
| 142 | abs_deps = [str(repo_root / dep) for dep in dependencies] |
| 143 | absolute_graph[abs_path] = abs_deps |
| 144 | |
| 145 | return absolute_graph |
| 146 | except subprocess.CalledProcessError as e: |
| 147 | pytest.exit(f"Failed to run ruff analyze graph: {e}", returncode=1) |
| 148 | except json.JSONDecodeError as e: |
| 149 | pytest.exit(f"Failed to parse ruff output: {e}", returncode=1) |
| 150 | |
| 151 | raise UnreachableError("Failed to run ruff analyze graph") |
| 152 | |
| 153 | |
| 154 | def find_affected_files( |
no test coverage detected
searching dependent graphs…