Why I built it
Python lets you import almost anything from almost anywhere — until the day
two modules need each other at load time and the whole app dies with an
ImportError on a half-initialized module. The frustrating part is that most
import cycles are actually fine: Python tolerates cycles that only pass
module objects around. The dangerous ones are those where load-time code
reaches into another module's namespace before it exists.
TraceGraph is built around that distinction. It parses every file with
Python's own ast module, resolves each import to a real module in the
project, and tags each edge with when the dependency is realized (at import
time, inside a function, or only for type checkers) and what it binds (the
module object, or symbols out of its namespace). A cycle is only flagged when
it runs through at least one load-time symbol edge — the pattern that can
actually blow up.
Test impact analysis
The same graph, walked in reverse, answers a second question: if I change this file, which tests do I need to run? TraceGraph selects every test module with a dependency path to the changed file.
Claims like that are easy to make and hard to trust, so the selection is
validated empirically. A harness runs a target project's real test suite under
coverage.py with one dynamic context per test, giving ground truth for which
tests actually executed each module. Against Flask (491 tests) and Requests
(635 tests), TraceGraph's static prediction achieved recall 1.00 — it
never missed a test that mattered — while cutting the suite by 8–34%
depending on how the project's fixtures are wired.