Navegador
Analysis API Reference
from navegador.analysis import (
ImpactAnalyzer,
FlowTracer,
DeadCodeDetector,
CycleDetector,
TestMapper,
)
from navegador.graph import GraphStore
ImpactAnalyzer
Traces downstream dependents of a function, class, or file by following CALLS, INHERITS, and IMPORTS edges.
class ImpactAnalyzer:
def __init__(self, store: GraphStore) -> None: ...
analyze
def analyze(
self,
name: str,
*,
node_type: str = "",
file: str = "",
depth: int = 0,
include_tests: bool = False,
include_knowledge: bool = False,
) -> ImpactResult
Compute the impact set for a given node.
Parameters:
| Name | Type | Default | Description |
|---|---|---|---|
name |
str |
— | Name of the function, class, or file to analyze |
node_type |
str |
"" |
Node type hint: "Function", "Class", "File" |
file |
str |
"" |
Optional file path to disambiguate |
depth |
int |
0 |
Maximum hops to follow (0 = unlimited) |
include_tests |
bool |
False |
Include test files in the impact set |
include_knowledge |
bool |
False |
Include linked knowledge nodes (rules, concepts, decisions) |
Returns: ImpactResult
ImpactResult
@dataclass
class ImpactResult:
root: ContextNode
direct_dependents: list[ContextNode]
transitive_dependents: list[ContextNode]
affected_files: list[str]
knowledge_nodes: list[ContextNode] # empty unless include_knowledge=True
depth_reached: int
| Field | Type | Description |
|---|---|---|
root |
ContextNode |
The analyzed node |
direct_dependents |
list[ContextNode] |
Nodes one hop away |
transitive_dependents |
list[ContextNode] |
All nodes reachable beyond one hop |
affected_files |
list[str] |
Unique file paths in the full dependent set |
knowledge_nodes |
list[ContextNode] |
Linked concepts, rules, decisions |
depth_reached |
int |
Actual maximum depth traversed |
Example:
store = GraphStore.sqlite(".navegador/navegador.db")
analyzer = ImpactAnalyzer(store)
result = analyzer.analyze("validate_token", depth=3, include_knowledge=True)
print(f"{len(result.direct_dependents)} direct dependents")
print(f"{len(result.transitive_dependents)} transitive dependents")
print(f"Affects {len(result.affected_files)} files")
for rule in [n for n in result.knowledge_nodes if n.label == "Rule"]:
print(f" Governed by: {rule.name} ({rule.properties.get('severity')})")
FlowTracer
Finds call paths between two functions.
class FlowTracer:
def __init__(self, store: GraphStore) -> None: ...
trace
def trace(
self,
from_name: str,
to_name: str,
*,
from_file: str = "",
to_file: str = "",
max_paths: int = 3,
max_depth: int = 10,
) -> list[FlowPath]
Find call chains from from_name to to_name.
Parameters:
| Name | Type | Default | Description |
|---|---|---|---|
from_name |
str |
— | Starting function name |
to_name |
str |
— | Target function name |
from_file |
str |
"" |
File path to disambiguate start |
to_file |
str |
"" |
File path to disambiguate target |
max_paths |
int |
3 |
Maximum number of paths to return |
max_depth |
int |
10 |
Maximum call chain length |
Returns: list[FlowPath]
FlowPath
@dataclass
class FlowPath:
nodes: list[str] # function names in order
node_details: list[ContextNode]
length: int
Example:
tracer = FlowTracer(store)
paths = tracer.trace("create_order", "process_payment", max_paths=5)
for i, path in enumerate(paths, 1):
print(f"Path {i}: {' -> '.join(path.nodes)}")
DeadCodeDetector
Identifies functions and classes that are never called, never imported, and not entry points.
class DeadCodeDetector:
def __init__(self, store: GraphStore) -> None: ...
find
def find(
self,
path: str | Path,
*,
exclude_tests: bool = False,
min_confidence: int = 80,
) -> list[DeadCodeCandidate]
Find potentially dead code within a path.
Parameters:
| Name | Type | Default | Description |
|---|---|---|---|
path |
str \| Path |
— | Directory or file to analyze |
exclude_tests |
bool |
False |
Skip test files |
min_confidence |
int |
80 |
Minimum confidence score to include (0–100) |
Returns: list[DeadCodeCandidate]
DeadCodeCandidate
@dataclass
class DeadCodeCandidate:
node: ContextNode
confidence: int # 0–100
reasons: list[str] # e.g. ["no callers", "no imports", "no decorator entry point"]
| Field | Type | Description |
|---|---|---|
node |
ContextNode |
The potentially dead node |
confidence |
int |
Confidence that this is truly unreachable (higher = more confident) |
reasons |
list[str] |
Reasons for the classification |
Example:
detector = DeadCodeDetector(store)
candidates = detector.find("./src", exclude_tests=True, min_confidence=90)
for c in candidates:
print(f"[{c.confidence}%] {c.node.label}: {c.node.name} {c.node.properties['file']}")
print(f" Reasons: {', '.join(c.reasons)}")
CycleDetector
Finds circular dependency chains in call and import graphs.
class CycleDetector:
def __init__(self, store: GraphStore) -> None: ...
find_import_cycles
def find_import_cycles(
self,
path: str | Path,
*,
min_length: int = 2,
) -> list[Cycle]
Find circular import chains within a path.
find_call_cycles
def find_call_cycles(
self,
path: str | Path,
*,
min_length: int = 2,
) -> list[Cycle]
Find circular call chains within a path.
find_all
def find_all(
self,
path: str | Path,
*,
min_length: int = 2,
) -> CycleReport
Find both import and call cycles.
Parameters (all methods):
| Name | Type | Default | Description |
|---|---|---|---|
path |
str \| Path |
— | Directory or file to analyze |
min_length |
int |
2 |
Minimum cycle length to report |
Returns: list[Cycle] or CycleReport
Cycle
@dataclass
class Cycle:
path: list[str] # node names (files or functions) forming the cycle
cycle_type: str # "import" or "call"
length: int
CycleReport
@dataclass
class CycleReport:
import_cycles: list[Cycle]
call_cycles: list[Cycle]
total: int
Example:
detector = CycleDetector(store)
report = detector.find_all("./src")
print(f"{report.total} cycles found")
for cycle in report.import_cycles:
print(f" Import cycle: {' -> '.join(cycle.path)}")
TestMapper
Maps test functions to the production code they exercise via call graph analysis.
class TestMapper:
def __init__(self, store: GraphStore) -> None: ...
map
def map(
self,
src_path: str | Path,
test_path: str | Path,
*,
target: str = "",
) -> TestCoverageMap
Build a mapping of production functions to their covering tests.
Parameters:
| Name | Type | Default | Description |
|---|---|---|---|
src_path |
str \| Path |
— | Production code directory |
test_path |
str \| Path |
— | Test directory |
target |
str |
"" |
If set, only map coverage for this specific function |
Returns: TestCoverageMap
uncovered
def uncovered(
self,
src_path: str | Path,
test_path: str | Path,
) -> list[ContextNode]
Return production functions and classes with no covering tests.
TestCoverageMap
@dataclass
class TestCoverageMap:
coverage: dict[str, list[ContextNode]] # function name -> list of test nodes
uncovered: list[ContextNode]
coverage_percent: float
| Field | Type | Description |
|---|---|---|
coverage |
dict[str, list[ContextNode]] |
Maps each production function to its test nodes |
uncovered |
list[ContextNode] |
Production functions with no tests |
coverage_percent |
float |
Percentage of functions with at least one test |
Example:
mapper = TestMapper(store)
coverage_map = mapper.map("./src", "./tests")
print(f"Coverage: {coverage_map.coverage_percent:.1f}%")
print(f"Uncovered: {len(coverage_map.uncovered)} functions")
for fn_name, tests in coverage_map.coverage.items():
print(f" {fn_name}: {len(tests)} tests")
for test in tests:
print(f" {test.properties['file']}::{test.name}")