pip install deadcodeTo see unused code findings:
deadcode .To see suggested fixes for all files:
deadcode . --fix --dryTo see suggested fixes only for foo.py file:
deadcode . --fix --dry --only foo.pyTo fix:
deadcode . --fixTune out some of the false positives, e.g.:
deadcode . --exclude=venv,tests --ignore-names=BaseTestCase,*Mixin --ignore-names-in-files=migrations
The same options can be provided in pyproject.toml settings file:
[tool.deadcode]
exclude = ["venv", "tests"]
ignore-names = ["BaseTestCase", "*Mixin"]
ignore-names-in-files = ["migrations"]
FastAPI registers handlers through decorators and populates Pydantic model fields at runtime, so both look unused to a static analyser. Configure deadcode like this:
[tool.deadcode]
ignore-names-if-decorated-with = ["@app.*", "@router.*"]
ignore-bodies-if-inherits-from = ["BaseModel"]Read the FastAPI guide for common use cases, the complete example application, the dependency injection strategy, and the expected output with no wrong detections.
Create a .pre-commit-config.yaml file in the root of your project directory, if it doesn't exist, and add the following to the file:
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/albertas/deadcode
rev: 2.5.0
hooks:
- id: deadcode
The rev is the release tag, so it matches the version published on PyPI. To
run the same hook while developing deadcode itself, use make pre-commit.
| Option | Type | Meaning |
|---|---|---|
--fix |
- | Automatically remove detected unused code expressions from the code base. |
--dry |
- | Show changes which would be made in files. |
--only |
list | Filenames (or path expressions), that will be reflected in the output (and modified if needed). |
--exclude |
list | Filenames (or path expressions), which will be completely skipped without being analysed. |
--ignore-names |
list | Removes provided list of names from the output. Regexp expressions to match multiple names can also be provided, e.g. *Mixin will match all classes ending with Mixin. |
--ignore-names-in-files |
list | Ignores unused names in files, which filenames match provided path expressions. |
--ignore-names-if-inherits-from |
list | Ignores names of classes, which inherit from provided class names. |
--ignore-names-if-decorated-with |
list | Ignores names of an expression, which is decorated with one of the provided decorator names. |
--ignore-bodies-of |
list | Ignores body of an expression if its name matches any of the provided names. |
--ignore-bodies-if-decorated-with |
list | Ignores body of an expression if its decorated with one of the provided decorator names. |
--ignore-bodies-if-inherits-from |
list | Ignores body of a class if it inherits from any of the provided class names. The class name may be a plain name, a dotted path such as graphene.ObjectType, or a glob such as *.ObjectType; a dotted base also matches its final component (ObjectType). |
--ignore-definitions |
list | Ignores definition (including name and body) if a name of an expression matches any of the provided ones. |
--ignore-definitions-if-inherits-from |
list | Ignores definition (including name and body) of a class if it inherits from any of the provided class names. The class name may be a plain name, a dotted path such as graphene.ObjectType, or a glob such as *.ObjectType; a dotted base also matches its final component (ObjectType). |
--ignore-definitions-if-decorated-with |
list | Ignores definition (including name and body) of an expression, which is decorated with any of the provided decorator names. |
--no-color |
- | Removes colors from the output. |
--count |
- | Provides the count of the detected unused names instead of printing them all out. |
--quiet |
- | Does not output anything. The command exits with code 1 if unused names are found. |
-V, --version |
- | Shows deadcode version. |
-v, --verbose |
- | Shows verbose logging output. |
0— no dead code found.1— dead code found (in any output mode).2— invalid command line usage.- Unexpected errors print a Python traceback and exit
1.
The status is independent of --quiet, --count, --only, --dry, and --fix.
A --fix run exits 1 on the run that found items and 0 on a subsequent run
over the cleaned tree.
name- variable, function or class name.body- code block which follows after:in function or class definition.definition- whole class or function definition expression including its name and body.
| Code | Name | Message |
|---|---|---|
| DC01 | unused-variable | Variable {name} is never used |
| DC02 | unused-function | Function {name} is never used |
| DC03 | unused-class | Class {name} is never used |
| DC04 | unused-method | Method {name} is never used |
| DC05 | unused-attribute | Attribute {name} is never used |
| DC06 | unused-name | Name {name} is never used |
| DC07 | unused-import | Import {name} is never used |
| DC08 | unused-property | Property {name} is never used |
| DC09 | unreachable-if-block | Unreachable conditional statement block |
| DC11 | empty-file | Empty Python file |
| DC12 | commented-out-code | Commented out code |
| DC13 | unreachable-code | Code after terminal statement, e.g. return, raise, continue, break |
| DC | ignore-expression | Do not show any findings for an expression, which starts on current line (this code can only be used in # noqa: DC comments) |
Inline # noqa comments can be used to ignore deadcode checks.
E.g. unused Foo class wont be detected/fixed because # noqa: DC03 comment is used:
class Foo: # noqa: DC03
passFor a decorated function, method, property, or class, place the comment on the physical line of the def, async def, or class keyword:
@cache
def foo(): # noqa: DC02
passA comment on the first decorator line is still accepted.
Create a feature branch from main, open a pull request targeting main, and
wait for review. Do not commit feature changes directly to main; release
commits are made by the maintainer on main.
make check- runs unit tests and other checks using virtual environment.
ruff and
flake8 - don't have rules for unused global
code detection, only for local ones F823, F841, F842. deadcode package
tries to add new DCXXX checks for detecting variables/functions/classes/files
which are not used in a whole code base.
deadcode - is supposed to be used inline with other static code checkers like ruff.
There is an alternative vulture package.
In case there are several definitions using the same name - they all wont be reported if at least one usage of that name is being detected.
Files with syntax errors will be ignored, because deadcode uses ast to
build abstract syntax tree for name usage detection.
It is assumed that deadcode will be run using the same or higher Python version as the
code base is implemented in.
Wildcard imports are resolved by module basename, without package paths, so modules with the same basename are treated as one.
__all__ is honoured only when it is a statically readable module-level list or tuple of strings; modules with unknown exports
(dynamic __all__, unreadable or unparsable files, or modules outside the analysed file set) are not restricted. Without __all__,
the public module-level names are exported.
- Replace
.*with only*in regexp matching. - Add unused class method detection DC04 check.
- Add
--fixoption to automatically remove detected dead code occourencies - Add a check for empty python files.
- Split error codes into DC01, DC02, DC03 for variables, functions, class.
- Should have different codes for ignoring name and ignoring whole definition (reserved DCxx0 - ignore name, DCxx1 - ignore definition).
- Allow to disable each check separately using:
- inline comment.
- pyproject.toml file
- Add a check for code in comments.
- Add target python version option, if specified it will be used for code base check.
- Add a
--depthparameter to ignore nested code.. (To only check global scope use 0). - Add options:
- --ignore-definitions
- --ignore-definitions-if-inherits-from
- --ignore-definitions-if-decorated-with
- --ignore-names-if-inherits-from
- --ignore-names-if-decorated-with
- --ignore-bodies-of
- --ignore-bodies-if-decorated-with
- --ignore-bodies-if-inherits-from
- --ignore-definitions
- --ignore-definitions-if-inherits-from
- --ignore-definitions-if-decorated-with
- Question: would it be possible to ignore only certain types of checks for a body, e.g. only variable attributes of TypedDict and still check usage of methods and properties?
- What expression would allow this type of precission?
- Distinguish between definitions with same name, but different files.
- Repeated application of
deadcodetill the output stops changing. - Unreachable code detection and fixing: this should only be scoped for if statements and only limited to primitive variables.
- Benchmarking performance with larger projects (time, CPU and memory consumption) in order to optimize.
-
--fixcould accept a list of filenames as well (only those files would be changed, but the summary could would be full). (This might be confusing, because filenames, which have to be considered are provided without any flag, --fix is expected to not accept arguments) - pre-commit-hook.
- language server.
- DC10: remove code after terminal statements like
raise,return,break,continueand comes in the same scope. - Add
ignoreandper-file-ignorescommand line and pyproject.toml options, which allows to skip some rules. - Make sure that all rules are being skipped by
noqacomment and all rules react tonoqa: rule_idcomments. - Include package names into code item scope (dot-separated path), e.g. "package1.package2.module.class.method.variable".
- All options should be able to accept dot-separated path or a generic name, e.g. "marshmallow.Schema" vs "Schema", documentation should cleary demonstrate the behaviour/example that "Schema" means "*.Schema".
- Redefinition of an existing name makes previous name unreachable, unless it is assigned somehow.
- Check if file is still valid/parsable after automatic fixing, if not: halt the change and report error.
- Investigate ways of extracting and backporting Python3.10+
astimplementation to lower Python versions.
- v2.5.1:
- Features:
- Add support for Python 3.13, 3.14 and 3.15.
- Documentation is now published on Read the Docs at https://deadcode.readthedocs.io/.
- Add
-Vas a short alias for--version. - Document the pre-commit hook setup and usage on a separate documentation page, including why a hook run differs from
deadcode ..
- Bugfixes:
- Fix
--fixleaving invalid or partially removed imports when unused names are deleted from parenthesized or multi-line import statements. - Do not crash when the terminal cannot encode the success message.
- Exit with code 1 when unused code is found, so CI jobs and pre-commit hooks can fail.
- Fix
deadcode --fixremoving theffrom thefromkeyword in multi-line parenthesized imports (issue #5) - Remove fully unused import statements, including multi-line parenthesized ones, without leaving broken syntax behind
- Remove only the unused names from partially used import statements while keeping the used names and their order
- Honor
# noqacomments on the definition line of decorated functions, methods and classes. - Wildcard imports now honour a module
__all__list or tuple, so excluded names are reported as unused. - Wildcard imports stay unrestricted when
__all__is dynamic or unknown, and explicit imports keep working. - Without
__all__, wildcard imports expose public module-level names, and the README documents the limits. - Honour
--ignore-names-if-decorated-withand render decorator names as dotted identifiers such as@app.get(see the FastAPI section). - Decorator names are reported as dotted identifiers such as
@app.getinstead of memory addresses --ignore-names-if-decorated-withnow suppresses decorated functions, methods, properties, and classes- The FastAPI guide documents the recommended configuration, common use cases, dependency injection, and a complete example application with only real findings
- Test suites cover common FastAPI features and every dependency injection form without reporting them as dead code
- Print verbose log messages as readable ASCII instead of a
bytesrepr when the output stream cannot encode a character. - The FastAPI guide now explains dependency injection and shows feature usage examples
- --ignore-bodies-if-inherits-from and --ignore-definitions-if-inherits-from now recognize dotted base classes such as graphene.ObjectType and match their final component.
- The option help text and README document the accepted plain, dotted and glob base-name forms.
- Fix
- Features:
- v2.5.0 (2026-11-07):
- Bugfixes:
- Fix a crash when a decorator has no
idattribute.
- Fix a crash when a decorator has no
- Bugfixes:
- v2.4.1 (2024-08-09):
- Features:
- Add
--versionoption to showdeadcodeversion.
- Add
- Bugfixes:
- Use stdout for
deadcodeoutput.
- Use stdout for
- Features:
- v2.4.0 (2024-07-12):
- Features:
- Add
--onlyoption that accepts filenames only which will be reflected in the output and modified. This option can be used with--fixand--fix --dryoptions as well as for simple unused code detection without fixing.
- Add
- Features:
- v2.3.2 (2024-07-10):
- Features:
- Add
pre-commithook support. - Drop support for Python 3.8 and 3.9 versions, since their
astimplementation is lacking features.
- Add
- Features:
- v2.3.1 (2024-07-03):
- Bugfixes:
- Started analysing files in bytes instead of trying to convert them into UTF-8 encoded strings.
- Improved automatic removal of unused imports.
- Bugfixes:
- v2.3.0 (2024-04-07):
- Features:
- Add
--dryoption. - Update error codes to use DCXX format instead of DCXXX.
- Add
- Features:
