Find dead Python code

Overview

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.

Issues
  • 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.
    opened by exhuma 26
  • 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
    proposal minor 
    opened by jendrikseipp 24
  • 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.
    opened by RJ722 21
  • 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.)

    opened by AbdealiJK 21
  • 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.
    opened by kx-chen 19
  • 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
    proposal minor 
    opened by jendrikseipp 19
  • 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.

    opened by impredicative 18
  • 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?

    opened by RJ722 18
  • 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.
    opened by RJ722 17
  • 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.

    GCI 
    opened by Dobatymo 15
  • 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.
    opened by neutrinoceros 14
  • Mark imports in __all__ as used

    Mark imports in __all__ as used

    At the moment, symbols that are referenced in string form in the __all__ variable assignment are not marked as used.

    Example

    # Currently, 'Bar' would be marked as unused.
    from foo import Bar
    
    __all__ = ["Bar"]
    
    

    Implementation

    Add visit_Assign to cover assignments of the following form:

    # Default case
    __all__ = ["A"]
    
    # Tuples are also tolerated
    __all__ = ("B", )
    

    Limitations

    The implementation cannot cover dynamic definitions e.g.:

    a = ["A"]
    __all__ = a
    

    Related Issue

    This PR is addressing the issue https://github.com/jendrikseipp/vulture/issues/172.

    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.
    opened by kreathon 1
  • Mark deleted variables as used

    Mark deleted variables as used

    Hello guys, for overridden functions and unused arguments, can the vulture follow the same rules as pylint and other tools.

    It is described here: https://stackoverflow.com/a/14836005

    And this is specified as the suggested solution by PyLint: http://pylint-messages.wikidot.com/messages:w0613

    Currently I am trying to use del but vulture is still reporting the variable as unused

    Originally posted by @sshishov in https://github.com/jendrikseipp/vulture/issues/235#issuecomment-1105162434

    opened by jendrikseipp 1
  • 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.

    opened by exoriente 3
  • Replace use of whitelist with allowlist/clearlist and blacklist with denylist/blocklist

    Replace use of whitelist with allowlist/clearlist and blacklist with denylist/blocklist

    In a similar fashion to this issue: https://github.com/rails/rails/issues/33677, it would be nice if vulture could replace use of whitelist with allowlist/clearlist and blacklist with denylist/blocklist for better terminology. If not replace, then perhaps add support to detect allowlist and blocklist.

    opened by hvongsachang 0
  • False positive example should be updated

    False positive example should be updated

    Follow the instruction in README.md with vulture >= 2.1 results:

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

    It does not detect

    dead_code.py:4: unused function 'greet' (60% confidence)
    

    as expected since https://github.com/jendrikseipp/vulture/pull/219 eliminates the false positive.

    I think it would be good to update the example to something that can still be reproduced.

    opened by yoichi 1
  • Should report on unreachable code after try-return-except-raise

    Should report on unreachable code after try-return-except-raise

    For a following file:

    def test() -> int:
        try:
            return 2
        except Exception as e:
            raise e
        return 1
    test()
    

    vulture dead_return.py doesn't report anything, while it should report code on line 6, as this one is unreachable.

    vulture 2.3, python 3.9

    opened by kretes 1
Releases(v2.4)
  • v2.4(May 19, 2022)

    • Print absolute filepaths as relative again (as in version 2.1 and before) if they are below the current directory (The-Compiler, #246).
    • Run tests and add PyPI trove for Python 3.10 (chayim, #266).
    • Allow using the del keyword to mark unused variables (sshishov, #279).
    Source code(tar.gz)
    Source code(zip)
  • v2.3(Jan 16, 2021)

  • v2.2(Jan 15, 2021)

    • Only parse format strings when being used with locals() (jingw, #225).
    • Don't override paths in pyproject.toml with empty CLI paths (bcbnz, #228).
    • Run continuous integration tests for Python 3.9 (ju-sh, #232).
    • Use pathlib internally (ju-sh, #226).
    Source code(tar.gz)
    Source code(zip)
  • v2.1(Aug 19, 2020)

    • Treat getattr/hasattr(obj, "constant_string", ...) as a reference to obj.constant_string (jingw, #219).
    • Fix false positives when assigning to x.some_name but reading via some_name, at the cost of potential false negatives (jingw, #221).
    • Allow reading options from pyproject.toml (Michel Albert, #164, #215).
    Source code(tar.gz)
    Source code(zip)
  • v2.0(Aug 11, 2020)

    • Parse # type: ... comments if on Python 3.8+ (jingw, #220).
    • Bump minimum Python version to 3.6 (Jendrik Seipp, #218). The last Vulture release that supports Python 2.7 and Python 3.5 is version 1.6.
    • Consider all files under test or tests directories test files (Jendrik Seipp).
    • Ignore logging.Logger.propagate attribute (Jendrik Seipp).
    Source code(tar.gz)
    Source code(zip)
  • v1.6(Jul 28, 2020)

  • v1.5(May 24, 2020)

    • Support flake8 "noqa" error codes F401 (unused import) and F841 (unused local variable) (RJ722, #195).
    • Detect unreachable code in conditional expressions (Agathiyan Bragadeesh, #178).
    Source code(tar.gz)
    Source code(zip)
  • v1.4(Mar 30, 2020)

    • Ignore unused import statements in __init__.py (RJ722, #192).
    • Report first decorator's line number for unused decorated objects on Python 3.8+ (RJ722, #200).
    • Check code with black and pyupgrade.
    Source code(tar.gz)
    Source code(zip)
  • v1.3(Feb 3, 2020)

Owner
Jendrik Seipp
Jendrik Seipp
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

Shan 11 Sep 1, 2021
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

marketsquare 119 Jun 26, 2022
CodeAnalysis - Static Code Analysis: a code comprehensive analysis platform

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

Tencent 1.2k Jul 1, 2022
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

Scott Rogowski 2.5k Jun 27, 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

Kirill Klenov 898 Jun 25, 2022
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

Python Software Foundation 28.4k Jul 5, 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

Google 3.8k Jun 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

Gabriel Stork 41 Jun 13, 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

null 5 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

Rajdeep Biswas 1 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.

Nipuna Weerasekara 2 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

coala development group 3.3k Jul 3, 2022
Guesslang detects the programming language of a given source code

Detect the programming language of a source code

Y. SOMDA 554 Jun 30, 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

B.Jothin kumar 10 Jan 26, 2022
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

null 23 Jun 9, 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.

Rafael 2 Feb 18, 2022
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

gak 1.7k Jun 21, 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

Python Code Quality Authority 1.7k Jun 22, 2022
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

wemake.services 1.9k Jun 27, 2022