Common questions, caveats, and limitations.
Yes. It detects importlib.import_module() calls and treats them
as references. However, if you build module names from strings at runtime,
PragyaLint can't know the target. In those cases use an
entry point (add the file to entries in your
config) to mark it as reachable.
Possibly. Code loaded via reflection, decorators that register things, or
frameworks that scan modules can look "unused" to a static analyzer. This is
why exports and locals are medium (or
low) confidence and are fixed only with an
explicit --confidence opt-in. Analysis-only findings never
modify your code.
__all__?
Yes. Names listed in __all__ are treated as exports and are not
removed unless you pass --force.
utils.py reported as unused?
If no reachable module imports it, it's genuinely unreachable from your entry
points. But it might be imported by a module that PragyaLint doesn't consider
an entry point, by generated code, or via a dynamic name. Add it to
entries, or exclude it, if it's intentional.
The fixer is explicit and confidence-gated.
It removes only unreachable files and unused imports by default. Always run
--dry-run first and run your test suite after fixing. For
projects with reflection or plugins, be conservative with
exports fixes.
pragyalint --fail-on high
This exits with a non-zero code when any high-confidence finding exists.
Combine with --json or --sarif for richer CI
integration.
None at runtime. PragyaLint uses only the Python standard library
(ast, pathlib, etc.). Great for locked-down
environments.
Python 3.11 and newer, including 3.14.
PragyaLint brings OptiPrune's dead-code analysis and confidence-gated fixer model to the Python ecosystem. Where OptiPrune prunes TypeScript and JavaScript, PragyaLint removes dead Python modules, imports, and exports.
Yes. PragyaLint is free software under the GPL-3.0-or-later license.