Find dead Python code

Vulture - Find dead code

CI:Test Codecov Badge

Vulture finds unused code in Python programs. This is useful for cleaning up and finding errors in large code bases. If you run Vulture on both your library and test suite you can find untested code.

Due to Python's dynamic nature, static code analyzers like Vulture are likely to miss some dead code. Also, code that is only called implicitly may be reported as unused. Nonetheless, Vulture can be a very helpful tool for higher code quality.

Features

  • fast: uses static code analysis
  • tested: tests itself and has complete test coverage
  • complements pyflakes and has the same output syntax
  • sorts unused classes and functions by size with --sort-by-size
  • supports Python >= 3.6

Installation

$ pip install vulture

Usage

$ vulture myscript.py  # or
$ python3 -m vulture myscript.py
$ vulture myscript.py mypackage/
$ vulture myscript.py --min-confidence 100  # Only report 100% dead code.

The provided arguments may be Python files or directories. For each directory Vulture analyzes all contained *.py files.

Vulture assigns each chunk of dead code a confidence value. A confidence value of 100% means that the code will never be executed. Values below 100% are only estimates for how likely it is that the code is unused.

After you have found and deleted dead code, run Vulture again, because it may discover more dead code.

Handling false positives

When Vulture incorrectly reports chunks of code as unused, you have several options for suppressing the false positives. If fixing your false positives could benefit other users as well, please file an issue report.

Whitelists

The recommended option is to add used code that is reported as unused to a Python module and add it to the list of scanned paths. To obtain such a whitelist automatically, pass --make-whitelist to Vulture:

$ vulture mydir --make-whitelist > whitelist.py
$ vulture mydir whitelist.py

Note that the resulting whitelist.py file will contain valid Python syntax, but for Python to be able to run it, you will usually have to make some modifications.

We collect whitelists for common Python modules and packages in vulture/whitelists/ (pull requests are welcome).

Ignoring files

If you want to ignore a whole file or directory, use the --exclude parameter (e.g., --exclude *settings.py,docs/).

Flake8 noqa comments

For compatibility with flake8, Vulture supports the F401 and F841 error codes for ignoring unused imports (# noqa: F401) and unused local variables (# noqa: F841). However, we recommend using whitelists instead of noqa comments, since noqa comments add visual noise to the code and make it harder to read.

Ignoring names

You can use --ignore-names foo*,ba[rz] to let Vulture ignore all names starting with foo and the names bar and baz. Additionally, the --ignore-decorators option can be used to ignore functions decorated with the given decorator. This is helpful for example in Flask projects, where you can use --ignore-decorators "@app.route" to ignore all functions with the @app.route decorator.

We recommend using whitelists instead of --ignore-names or --ignore-decorators whenever possible, since whitelists are automatically checked for syntactic correctness when passed to Vulture and often you can even pass them to your Python interpreter and let it check that all whitelisted code actually still exists in your project.

Marking unused variables

There are situations where you can't just remove unused variables, e.g., in tuple assignments or function signatures. Vulture will ignore these variables if they start with an underscore (e.g., _x, y = get_pos() or def my_method(self, widget, **_kwargs)).

Minimum confidence

You can use the --min-confidence flag to set the minimum confidence for code to be reported as unused. Use --min-confidence 100 to only report code that is guaranteed to be unused within the analyzed files.

Unreachable code

If Vulture complains about code like if False:, you can use a Boolean flag debug = False and write if debug: instead. This makes the code more readable and silences Vulture.

Forward references for type annotations

See #216. For example, instead of def foo(arg: "Sequence"): ..., we recommend using

from __future__ import annotations

def foo(arg: Sequence):
    ...

if you're using Python 3.7+.

Configuration

You can also store command line arguments in pyproject.toml under the tool.vulture section. Simply remove leading dashes and replace all remaining dashes with underscores.

Options given on the command line have precedence over options in pyproject.toml.

Example Config:

[tool.vulture]
exclude = ["file*.py", "dir/"]
ignore_decorators = ["@app.route", "@require_*"]
ignore_names = ["visit_*", "do_*"]
make_whitelist = true
min_confidence = 80
paths = ["myscript.py", "mydir"]
sort_by_size = true
verbose = true

Version control integration

You can use a pre-commit hook to run Vulture before each commit. For this, install pre-commit and add the following to the .pre-commit-config.yaml file in your repository:

repos:
  - repo: https://github.com/jendrikseipp/vulture
    rev: 'v2.3'  # or any later Vulture version
    hooks:
      - id: vulture

Then run pre-commit install. Finally, create a pyproject.toml file in your repository and specify all files that Vulture should check under [tool.vulture] --> paths (see above).

How does it work?

Vulture uses the ast module to build abstract syntax trees for all given files. While traversing all syntax trees it records the names of defined and used objects. Afterwards, it reports the objects which have been defined, but not used. This analysis ignores scopes and only takes object names into account.

Vulture also detects unreachable code by looking for code after return, break, continue and raise statements, and by searching for unsatisfiable if- and while-conditions.

Sort by size

When using the --sort-by-size option, Vulture sorts unused code by its number of lines. This helps developers prioritize where to look for dead code first.

Examples

Consider the following Python script (dead_code.py):

import os

class Greeter:
    def greet(self):
        print("Hi")

def hello_world():
    message = "Hello, world!"
    greeter = Greeter()
    greet_func = getattr(greeter, "greet")
    greet_func()

if __name__ == "__main__":
    hello_world()

Calling :

$ vulture dead_code.py

results in the following output:

dead_code.py:1: unused import 'os' (90% confidence)
dead_code.py:4: unused function 'greet' (60% confidence)
dead_code.py:8: unused variable 'message' (60% confidence)

Vulture correctly reports "os" and "message" as unused, but it fails to detect that "greet" is actually used. The recommended method to deal with false positives like this is to create a whitelist Python file.

Preparing whitelists

In a whitelist we simulate the usage of variables, attributes, etc. For the program above, a whitelist could look as follows:

# whitelist_dead_code.py
from dead_code import Greeter
Greeter.greet

Alternatively, you can pass --make-whitelist to Vulture and obtain an automatically generated whitelist.

Passing both the original program and the whitelist to Vulture

$ vulture dead_code.py whitelist_dead_code.py

makes Vulture ignore the greet method:

dead_code.py:1: unused import 'os' (90% confidence)
dead_code.py:8: unused variable 'message' (60% confidence)

Exit codes

Exit code Description
0 No dead code found
1 Dead code found
1 Invalid input (file missing, syntax error, wrong encoding)
2 Invalid command line arguments

Similar programs

  • pyflakes finds unused imports and unused local variables (in addition to many other programmatic errors).
  • coverage finds unused code more reliably than Vulture, but requires all branches of the code to actually be run.
  • uncalled finds dead code by using the abstract syntax tree (like Vulture), regular expressions, or both.
  • dead finds dead code by using the abstract syntax tree (like Vulture).

Participate

Please visit https://github.com/jendrikseipp/vulture to report any issues or to make pull requests.

Comments
  • Implement support for a pyproject.toml config file

    Implement support for a pyproject.toml config file

    Description

    This change will read config values from a pyproject.toml file first, and then update those values with the CLI arguments.

    Related Issue

    See #164

    Checklist:

    • [x] My code follows the code style of this project.
    • [ ] My change requires a change to the documentation in the README file.
    • [x] I have updated the documentation accordingly.
    • [x] I have added an entry in CHANGELOG.md.
    • [x] I have added tests to cover my changes.
    • [x] All new and existing tests passed.
  • use vulture to automatically remove dead code?

    use vulture to automatically remove dead code?

    Originally reported by: Jendrik Seipp (Bitbucket: jendrikseipp, GitHub: jendrikseipp)


    I'm adding this issue since the topic came up in a discussion around GSoC proposals for www.coala.io with @sils1297. The idea was to write a program that uses vullture to find and remove dead code.

    After thinking some more about this, I think that the benefits of such a program are very small. For a program that changes code to be useful, most of the changes it proposes have to make sense. This is the case, e.g., for tools like autopep8. However, vulture (due to Python's dynamic nature) produces too many false positives. Even if it detects dead code, the underlying issue is not that code should be removed, but that e.g. a variable name is misspelled and therefore seems to be unused. Vulture is useful for hinting at possible programming errors, but I think almost all of them will have to be double-checked by the programmer.


    • Bitbucket: https://bitbucket.org/jendrikseipp/vulture/issue/25
  • Switch to GitHub Actions

    Switch to GitHub Actions

    Description

    Switch to GitHub Actions

    @jendrikseipp, in order to report to coveralls, you'd need to add a secret COVERALLS_REPO_TOKEN. The repo-token can be found here: https://coveralls.io/github/jendrikseipp/vulture/settings

    Checklist:

    • [x] My code follows the code style of this project.
    • [ ] My change requires a change to the documentation in the README file.
    • [ ] I have updated the documentation accordingly.
    • [ ] I have added an entry in CHANGELOG.md.
    • [x] I have added tests to cover my changes.
    • [x] All new and existing tests passed.
  • Write script for generating whitelist (was: False Unused function - ignoring APIs)

    Write script for generating whitelist (was: False Unused function - ignoring APIs)

    I was using vulture for my flask based API and found that it marked every API function I had as a "unused function".

    Example:

    @app.before_request
    def before_req():
        pass
    
    @user_api.route('/api/users', methods=['GET'])
    def get_all():
        pass
    
    

    Is it possible to make it such that if a specific decorator is used (possibly a regex for this) we can ignore the result ?

    In general flask applications will have Blueprints with the name .*api\..* (example: profile_api, user_api, book_api, etc.) and flask applications will be .*app\..* (example: flask_app, test_app, app, etc.)

  • Ignore type checking imports

    Ignore type checking imports

    Description

    Add type checking imports from typing to whitelist.

    File named as typing_whitelist.py, as it seems the code looks for those files, and it matches the other file names.

    Added a try block around the importing, as sometimes typing is not always available.

    I got the types from https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html and https://docs.python.org/3/library/typing.html. Please advise if I've missed any or need any changes.

    Related Issue

    https://github.com/jendrikseipp/vulture/issues/152

    Checklist:

    • [ ] My code follows the code style of this project.
    • [ ] My change requires a change to the documentation in the README file.
    • [ ] I have updated the documentation accordingly.
    • [ ] I have added an entry in NEWS.rst.
    • [ ] I have added tests to cover my changes.
    • [ ] All new and existing tests passed.
  • Overriden methods of C/C++ extensions

    Overriden methods of C/C++ extensions

    Originally reported by: Florian Bruhin (Bitbucket: The-Compiler, GitHub: The-Compiler)


    When subclassing a method of a class defined in a C/C++ extension, such as this example using the PyQt GUI framework:

    from PyQt5.QtCore import QSize
    from PyQt5.QtWidgets import QLineEdit, QApplication
    
    
    class LineEdit(QLineEdit):
    
        def sizeHint(self):
            return QSize(128, 128)
    
    app = QApplication([])
    le = LineEdit()
    le.show()
    app.exec_()
    

    vulture doesn't detect that sizeHint is called via C++:

    foo.py:7: Unused function 'sizeHint'
    

    Maybe if a class inherits from a non-Python class, and an existing method is overridden, it should always be considered used?


    • Bitbucket: https://bitbucket.org/jendrikseipp/vulture/issue/8
  • Change the whitelist file extension in readme

    Change the whitelist file extension in readme

    The example vulture mydir --make-whitelist > whitelist.py in the readme typically creates an invalid Python file since it lacks imports. Such a file would create a problem for multiple other static analyzers that would find it to be grossly invalid Python code. It would be better if this example were updated to to use the extension .txt instead as below:

    $ vulture mydir --make-whitelist > vulture.txt
    $ vulture mydir vulture.txt
    

    The # Vulture whitelist: header can be added to the created file. In this way, the Python imports can continue to be missing in this whitelist file, and the other static analyzers won't have any issue with it.


    I'm actually quite displeased with the current end-user whitelist setup. It doesn't seem well thought through at all. If I use a .py whitelist file with the appropriate imports, then vulture complains that the imports are unused. If I use a .txt whitelist file, then it's not possible to define which file each exclusion is supposed to have been imported from, making it a non-specific exclusion. If for example, I specify "my_foo_pkg.my_sub_pkg.xyzz" in a .txt file, then "my_foo_pkg.my_sub_pkg" are not checked.

  • Use stdlib whitelist by default

    Use stdlib whitelist by default

    Whitelists are a great way to tackle false positives, but we have to explicitly mention the whitelist fie every time we execute vulture, so should vulture use whitelist by default?

  • Add coveralls to track code coverage

    Add coveralls to track code coverage

    Checklist:

    • [X] My code follows the code style of this project.
    • [ ] My change requires a change to the documentation in the README file.
    • [ ] I have updated the documentation accordingly.
    • [ ] I have added an entry in NEWS.rst.
    • [ ] I have added tests to cover my changes.
    • [X] All new and existing tests passed.
  • Ignore type checking imports

    Ignore type checking imports

    Code like this is common when type annotations in comments are used in Python:

    from typing import TYPE_CHECKING
    if TYPE_CHECKING:
    	from typing import Optional, Iterator
    	from abc import xyz
    

    These will usually show up as unused import as they are only referenced from comments.

    The code should probably be parsed with a TYPE_CHECKING=False in mind.

  • add a pre-commit hook config file

    add a pre-commit hook config file

    Description

    Adding this file to the repo makes vulture usable as a hook for pre-commit. As such, it can be easily integrated to a shared development workflow and to CI (in particular within pre-commit.ci)

    Related Issue

    none

    Checklist:

    • [x] I have updated the documentation in the README.md file or my changes don't require an update.
    • [x] I have added an entry in CHANGELOG.md.
    • [ ] I have added or adapted tests to cover my changes.
  • [feat] Support setup.cfg configuration file

    [feat] Support setup.cfg configuration file

    It would be great if vulture also supports a setup.cfg configuration file file, or at least has an argument taking a path to a configuration file.

    A lot of project uses setup.cfg and a lot of tools support it (pytest, flake8, pydocstyle, pycodestyle, ...)

  • Feature request: not throw

    Feature request: not throw "unused variable" for __exit__

    Are you willing to consider built-in whitelisting "unused variable" reports for context management's __exit__?

    Context management is a fundamental aspect of Python, with __exit__'s signature documented in Python's data model. Furthermore, __exit__'s signature is autocompleted by IDE's like PyCharm. I don't think one should have to explicitly go back and replace commonly unused argument names like exc_type with _.

    Considering this is overrideable built-in object functionality, I think having unused arguments should be allowed by default.

    What do you think?


    Disclaimer: this issue is a blatent duplicate of https://github.com/jendrikseipp/vulture/issues/39, but I didn't want to grave dig an issue from over 5 years ago.

  • Fix get_decorator_name with callable in between

    Fix get_decorator_name with callable in between

    Description

    get_decorator_name would fail with an AttributeError on decorators on the style of the prometheus_client where you actually have a call in between instead of just at the end of it. Ex. @hist.labels('label').time()

    This pr changes the method to loop so that we will catch all the ast.Call and can build the full name.

    Related Issue

    https://github.com/jendrikseipp/vulture/issues/283

    Checklist:

    • [x] I have updated the documentation in the README.md file or my changes don't require an update.
    • [x] I have added an entry in CHANGELOG.md.
    • [x] I have added or adapted tests to cover my changes.
    • [x] I have run tox -e fix-style to format my code and checked the result with tox -e style.
  • AttributeError when parsing a decorator

    AttributeError when parsing a decorator

    Hello,

    I just wanted to raise awareness about an issue that I mainly encountered when working with the python prometheus client.

    The get_decorator_name function seems to assume that after cycling over the ast.Attribute you can get the id decorator.id. When using a decorator from the prometheus_client for example:

    from prometheus_client import Histogram
    hist = Histogram('name', 'description', labelnames=["label1"])
    
    @hist.labels('place1').time()
    def myfunc():
        ...
    

    after the Attribute you will be finding an ast.Call object that won't have the id attribute and raise the AttributeError. From my understanding this is due to some extra decorating work that the library is doing, but possibly the vulture tool shouldn't fail with an error.

    For now this has been solved by ignoring the file with the --exclude flag

    The traceback:

    Traceback (most recent call last):
      File "/usr/local/bin/vulture", line 8, in <module>
        sys.exit(main())
      File "/usr/local/lib/python3.10/site-packages/vulture/core.py", line 689, in main
        vulture.scavenge(config["paths"], exclude=config["exclude"])
      File "/usr/local/lib/python3.10/site-packages/vulture/core.py", line 264, in scavenge
        self.scan(module_string, filename=module)
      File "/usr/local/lib/python3.10/site-packages/vulture/core.py", line 231, in scan
        self.visit(node)
      File "/usr/local/lib/python3.10/site-packages/vulture/core.py", line 645, in visit
        return self.generic_visit(node)
      File "/usr/local/lib/python3.10/site-packages/vulture/core.py", line 677, in generic_visit
        self.visit(item)
      File "/usr/local/lib/python3.10/site-packages/vulture/core.py", line 630, in visit
        visitor(node)
      File "/usr/local/lib/python3.10/site-packages/vulture/core.py", line 564, in visit_FunctionDef
        decorator_names = [
      File "/usr/local/lib/python3.10/site-packages/vulture/core.py", line 565, in <listcomp>
        utils.get_decorator_name(decorator)
      File "/usr/local/lib/python3.10/site-packages/vulture/utils.py", line 69, in get_decorator_name
        return "@" + ".".join(reversed(parts))
    AttributeError: 'Call' object has no attribute 'id'
    
  • vulture does not detect code usage by `case` clauses in pattern matching

    vulture does not detect code usage by `case` clauses in pattern matching

    Hi,

    I'm running into a issue with pattern matching and vulture. In my case the property of a class is only used during pattern matching. Vulture doesn't pick up on it and considers it dead code. I've whitelisted the properties for now, but it would be great if vulture could be improved to cover this new scenario.

    Pseudo code example:

    @dataclass
    class MyClass
        my_field: MyType
        
        @property
        my_test(self) -> bool:
            return my_field == my_constant
    
    def do_something(x: Object) -> None:
        match x:
            case MyClass(my_test=True):
                do_stuff(x)
            case MyClass(my_test=False):
                do_other_stuff(x)
    

    In this example, vulture seems to flag my_test as dead code.

Learning source code review, spot vulnerability, find some ways how to fix it.

Learn Source Code Review Learning source code review, spot vulnerability, find some ways how to fix it. WordPress Plugin Authenticated Stored XSS on C

Nov 29, 2022
Robocop is a tool that performs static code analysis of Robot Framework code.
Robocop is a tool that performs static code analysis of Robot Framework code.

Robocop Introduction Documentation Values Requirements Installation Usage Example Robotidy FAQ Watch our talk from RoboCon 2021 about Robocop and Robo

Nov 23, 2022
CodeAnalysis - Static Code Analysis: a code comprehensive analysis platform
CodeAnalysis - Static Code Analysis: a code comprehensive analysis platform

TCA, Tencent Cloud Code Analysis English | 简体中文 What is TCA Tencent Cloud Code A

Dec 5, 2022
Turn your Python and Javascript code into DOT flowcharts
Turn your Python and Javascript code into DOT flowcharts

Notes from 2017 This is an older project which I am no longer working on. It was built before ES6 existed and before Python 3 had much usage. While it

Nov 28, 2022
Code audit tool for python.

Pylama Code audit tool for Python and JavaScript. Pylama wraps these tools: pycodestyle (formerly pep8) © 2012-2013, Florent Xicluna; pydocstyle (form

Nov 30, 2022
The uncompromising Python code formatter
The uncompromising Python code formatter

The Uncompromising Code Formatter “Any color you like.” Black is the uncompromising Python code formatter. By using it, you agree to cede control over

Nov 27, 2022
A static type analyzer for Python code

pytype - ? ✔ Pytype checks and infers types for your Python code - without requiring type annotations. Pytype can: Lint plain Python code, flagging c

Nov 30, 2022
Print a directory tree structure in your Python code.

directory-structure Print a directory tree structure in your Python code. Download You can simply: pip install directory-structure Or you can also: Cl

Sep 29, 2022
Python package to parse and generate C/C++ code as context aware preprocessor.

Devana Devana is a python tool that make it easy to parsing, format, transform and generate C++ (or C) code. This tool uses libclang to parse the code

Apr 3, 2022
A very minimalistic python module that lets you track the time your code snippets take to run.

Clock Keeper A very minimalistic python module that lets you track the time your code snippets take to run. This package is available on PyPI! Run the

Jan 19, 2022
This is a Python program to get the source lines of code (SLOC) count for a given GitHub repository.

This is a Python program to get the source lines of code (SLOC) count for a given GitHub repository.

Mar 10, 2022
coala provides a unified command-line interface for linting and fixing all your code, regardless of the programming languages you use.

"Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live." ― John F. Woods coala provides a

Dec 5, 2022
Guesslang detects the programming language of a given source code
Guesslang detects the programming language of a given source code

Detect the programming language of a source code

Nov 29, 2022
An app to show the total number of lines of code written by an user.

Lines of code Have you ever wondered how many lines of code you wrote in github? This tool will calculate it for you! To calculate the total number of

Jan 26, 2022
Metrinome is an all-purpose tool for working with code complexity metrics.
Metrinome is an all-purpose tool for working with code complexity metrics.

Overview Metrinome is an all-purpose tool for working with code complexity metrics. It can be used as both a REPL and API, and includes: Converters to

Sep 27, 2022
A simple stopwatch for measuring code performance with static typing.

A simple stopwatch for measuring code performance. This is a fork from python-stopwatch, which adds static typing and a few other things.

Feb 18, 2022
pycallgraph is a Python module that creates call graphs for Python programs.
pycallgraph is a Python module that creates call graphs for Python programs.

Project Abandoned Many apologies. I've stopped maintaining this project due to personal time constraints. Blog post with more information. I'm happy t

Nov 29, 2022
Inspects Python source files and provides information about type and location of classes, methods etc

prospector About Prospector is a tool to analyse Python code and output information about errors, potential problems, convention violations and comple

Nov 26, 2022
The strictest and most opinionated python linter ever!
The strictest and most opinionated python linter ever!

wemake-python-styleguide Welcome to the strictest and most opinionated python linter ever. wemake-python-styleguide is actually a flake8 plugin with s

Dec 5, 2022