Why PragyaLint

Dead-code detection for Python isn't new. Here's specifically what PragyaLint does differently from the tools you may already have.

Reachability, not per-file heuristics

Tools like ruff --select F401 or pyflakes catch unused imports within a file, but they don't know whether the file itself is ever imported by anything. vulture goes further with a whole-project scan, but reports a confidence percentage derived from name-usage heuristics rather than an actual reachability proof.

PragyaLint builds a real module graph from your entry points and asks a structural question: can anything reach this module by following imports? If not, it's HIGH-confidence dead — not a percentage, a fact about your import graph.

Dynamic-dispatch aware by default

Interpreters, plugin systems, and command-dispatch code call functions by name at runtime — getattr(obj, name), a {"cmd": handler} table, exec/eval. None of that is a plain name reference, so most static analyzers either miss it (false negative — they report nothing, silently missing real usage) or, worse, confidently delete the "unused" function anyway.

PragyaLint specifically recognizes dispatch tables and getattr() calls as real usage, and when it detects patterns it can't resolve statically (a computed getattr name, exec, globals()), it downgrades remaining findings project-wide to LOW confidence and refuses to delete anything without --force. See Dynamic dispatch for the full mechanism.

Understands your test suite

Pytest discovers test_*.py and conftest.py files by walking the filesystem, not through any import statement in your source. A tool that only looks at the import graph will always see these as "unreachable" and propose deleting your entire test suite. PragyaLint recognizes pytest's discovery convention and treats these files as entry points automatically.

Zero dependencies

PragyaLint is built entirely on the Python standard-library ast module. There's no third-party parser to pin, update, or audit — install it and it works with whatever Python version is running it.

What PragyaLint doesn't do

To be fair about the trade-offs: