orchestrata_ultimate.refactor.codemod.analysis.dead_code
Reference › orchestrata_ultimate › refactor › codemod › analysis › dead_code
DeadCodeDetector finds modules that no entry point reaches, at module granularity only. analyze(target) returns a DeadCodeReport (see _types). _entry_roots treats as roots every module under scripts/ or tests/, every test_*.py file and the files named __main__.py, __init__.py or conftest.py, since these run or load without being imported. _unreachable_modules then follows the graph's dependencies from the roots and reports the modules left over. The report's granularity is module and its list of unreachable symbols stays empty, because the graph has no reachability query for symbols. Read-only; imported by the analysis facade.
At a glance
| Source | src/orchestrata_ultimate/refactor/codemod/analysis/dead_code.py |
| Lines of code | 104 |
| Classes | 1 |
| Functions | 0 |
| Imports (internal) | orchestrata_ultimate.core.base.traces, orchestrata_ultimate.refactor.codemod._cig, orchestrata_ultimate.refactor.codemod._types, orchestrata_ultimate.refactor.codemod.analysis.protocol |
| Imported by | orchestrata_ultimate.refactor.codemod.analysis |
| Tests that import it | none |
Class diagram
Dependency diagram
API
Classes
DeadCodeDetector
Decorators: traces(spec='110', story='US-110-006', reqs=['REQ-110-031', 'REQ-110-032']) · Line 34
Report modules unreachable from any entry root.
| Method | Signature | Summary |
|---|---|---|
__init__ |
__init__(cig: CodeIntelligenceGraph) -> None |
|
analyze |
analyze(target: str | None = None) -> DeadCodeReport |
Unreachable modules, or only target when it is unreachable. |
_entry_roots |
_entry_roots() -> frozenset[int] |
Modules that run or load without being imported. |
_unreachable_modules |
_unreachable_modules(roots: frozenset[int]) -> frozenset[int] |
Module nodes not reachable from roots over module imports. |
Source
"""DeadCodeDetector — modules no entry point reaches (module granularity only).
GAP-1: the CIG has no reachability query and no public edge-type accessor, so
symbol-level dead code (CALLS/INHERITS/IMPORTS/OVERRIDES reachability from
entry roots) is not computable here: ``DeadCodeReport.granularity`` is
``'module'`` and ``unreachable_symbols`` is empty. Module reachability uses
the public ``CodeIntelligenceGraph.dependencies`` over Module nodes (the
IMPORTS edges between modules).
GAP-2: CALLS edges resolve to the first node inserted under a callee name, so
any CALLS-based answer would over-approximate "alive"; module reachability
does not use CALLS and is unaffected.
"""
from __future__ import annotations
from collections import deque
from pathlib import PurePosixPath
from typing import TYPE_CHECKING
from orchestrata_ultimate.core.base.traces import traces
from orchestrata_ultimate.refactor.codemod._types import DeadCodeReport
from orchestrata_ultimate.refactor.codemod.analysis.protocol import resolve_node_id
if TYPE_CHECKING:
from orchestrata_ultimate.refactor.codemod._cig import CodeIntelligenceGraph
__all__ = ["DeadCodeDetector"]
_ENTRY_NAMES = ("__main__.py", "__init__.py", "conftest.py")
@traces(spec="110", story="US-110-006", reqs=["REQ-110-031", "REQ-110-032"])
class DeadCodeDetector:
"""Report modules unreachable from any entry root."""
name = "dead_code"
@traces(spec="110", story="US-110-006", reqs=["REQ-110-031", "REQ-110-032"])
def __init__(self, cig: CodeIntelligenceGraph) -> None:
self._cig = cig
@traces(spec="110", story="US-110-006", reqs=["REQ-110-031", "REQ-110-032"])
def analyze(self, target: str | None = None) -> DeadCodeReport:
"""Unreachable modules, or only ``target`` when it is unreachable.
Raises:
ValueError: ``target`` names no module in the graph.
"""
roots = self._entry_roots()
unreachable = self._unreachable_modules(roots)
if target is not None:
node_id = resolve_node_id(self._cig, target)
if node_id is None:
raise ValueError(f"module '{target}' not found in CIG")
unreachable = unreachable & {node_id}
return DeadCodeReport(
roots=roots,
unreachable_modules=frozenset(unreachable),
unreachable_symbols=frozenset(),
granularity="module",
)
@traces(spec="110", story="US-110-006", reqs=["REQ-110-031"])
def _entry_roots(self) -> frozenset[int]:
"""Modules that run or load without being imported.
Roots: anything under ``scripts/`` or ``tests/``, ``test_*.py``
files, and ``__main__.py`` / ``__init__.py`` / ``conftest.py``.
"""
roots: set[int] = set()
for nid, node in self._cig.nodes.items():
if node.node_type != "Module":
continue
pure = PurePosixPath(node.path)
if (
pure.name in _ENTRY_NAMES
or pure.name.startswith("test_")
or "tests" in pure.parts
or "scripts" in pure.parts
):
roots.add(nid)
return frozenset(roots)
@traces(spec="110", story="US-110-006", reqs=["REQ-110-031"])
def _unreachable_modules(self, roots: frozenset[int]) -> frozenset[int]:
"""Module nodes not reachable from ``roots`` over module imports.
With no roots there is nothing to reach from, so nothing is reported.
"""
if not roots:
return frozenset()
nodes = self._cig.nodes
seen: set[int] = set(roots)
queue = deque(roots)
while queue:
current = queue.popleft()
for dep in self._cig.dependencies(current):
node = nodes.get(dep)
if node is not None and node.node_type == "Module" and dep not in seen:
seen.add(dep)
queue.append(dep)
modules = {nid for nid, node in nodes.items() if node.node_type == "Module"}
return frozenset(modules - seen)