asottile / dead Goto Github PK
View Code? Open in Web Editor NEWdead simple python dead code detection
License: MIT License
dead simple python dead code detection
License: MIT License
Is there a way to run the dead pre-commit-hook only on files to be committed? Otherwise it runs on all files (e.g. not staged for commit)
A related feature that could be useful would be to detect "dead arguments," or function arguments not used by the function body.
Here is an example of a dead argument found in pip: pypa/pip#7015
# foo.py
def foo(): ...
# bar.py
from foo import foo # Imported but unused
$ dead
# Dead silence, even if you commit foo.py and bar.py
From what I've looked this seems like intended behavior - why? is there a false positive corner case I'm missing?
https://github.com/asottile/dead/blob/master/dead.py#L50
And if it is intended I'd like a configuration value to change it
As always, thanks for the tools! ❤️
I use dead
on a small number of files in a larger codebase, and have it integrated via pre-commit
. We're only using dead
where we consider it particularly helpful.
Right now, this means that we have a config in pre-commit like so:
- repo: https://github.com/asottile/dead
rev: v1.4.0
hooks:
- id: dead
args:
- "--files"
- |
(?x)(
src/project/component1/foo.py|
src/project/component1/bar/baz.py|
src/project/component2/spam.py
)
This feels awkward as the list grows. Plus, it reports things inaccurately if they're used in a module which I don't want covered by dead
.
I'm trying to work out the right approach. It would be really cool to have dead
read the full codebase as it does by default, but only report failures in the target files.
e.g.
args:
# `--check-files` or `--report-files` or `--only` or some other name is fine
- "--report-files"
- "src/project/component1/foo.py"
- "src/project/component1/bar/baz.py"
- "src/project/component2/spam.py"
I'm more than happy to try to contribute a patch if this would be acceptable. I don't want to bloat things. I think just a filter at the very end, when output and the exit code are being handled, to see if the passed --report-files
matches files before treating them as failures.
Does this sound interesting as a feature?
Hi,
Just hit this twice in a week. I'm using dead
to clean up a large codebase and realized that as soon as you delete a file, dead
starts complaining that it can't find that same file in subsequent run
Traceback (most recent call last):
File "/home/***/.pyenv/versions/3.6.5/envs/cmp/bin/dead", line 8, in <module>
sys.exit(main())
File "/home/***/.pyenv/versions/3.6.5/envs/cmp/lib/python3.6/site-packages/dead.py", line 302, in main
for filename, is_test in _filenames(files_re, exclude_re, tests_re):
File "/home/***/.pyenv/versions/3.6.5/envs/cmp/lib/python3.6/site-packages/dead.py", line 213, in _filenames
'python' not in tags_from_path(filename)
File "/home/***/.pyenv/versions/3.6.5/envs/cmp/lib/python3.6/site-packages/identify/identify.py", line 38, in tags_from_path
raise ValueError('{} does not exist.'.format(path))
ValueError: myapp/lib/helpers.py does not exist.
I quickly check and the problem is that it relies on git ls-files
, which lists also the deleted files.
The workaround is to commit the change, then re-execute dead
but I thought it is worth fixing this.
I havn't seen an option in git ls-files
to lists everything except the deleted file but I see that I can list the deleted files with git ls-files --deleted
so I guess we can use that to subtract the deleted files.
Let me know your thoughts and if that seems like a good approach, I'd be happy to implement it.
Config:
- repo: https://github.com/asottile/dead
rev: ''
hooks:
- id: dead
Output:
detect dead code.........................................................Failed
hookid: dead
usage: dead [-h] [--files FILES] [--exclude EXCLUDE] [--tests TESTS]
dead: error: unrecognized arguments: tests/helpers/pkg/circles/simple_class.py src/_dependencies/checks/operation.py tests/helpers/examples/operation.py src/dependencies/contrib/django.py src/_dependencies/operation.py tests/helpers/pkg/injected.py tests/helpers/pkg/submodule.py src/_dependencies/markers.py tests/helpers/examples/order/commands.py tests/helpers/examples/users/__init__.py tests/test_contrib_django.py tests/helpers/pkg/circles/__init__.py src/dependencies/contrib/flask.py tests/helpers/django_project/__init__.py src/_dependencies/checks/__init__.py tests/helpers/helpers.py tests/test_contrib_flask.py tests/test_contrib_rest_framework.py tests/helpers/flask_project/__init__.py src/_dependencies/injector.py tests/helpers/examples/order/tasks.py src/_dependencies/attributes.py src/_dependencies/func.py src/_dependencies/checks/value.py src/dependencies/contrib/__init__.py tests/test_injector.py src/_dependencies/contrib/django.py tests/test_package.py
usage: dead [-h] [--files FILES] [--exclude EXCLUDE] [--tests TESTS]
dead: error: unrecognized arguments: tests/helpers/examples/cart/serializers.py src/dependencies/contrib/celery.py src/_dependencies/spec.py tests/helpers/pkg/circles/complex_operation.py tests/helpers/examples/cart/commands.py tests/helpers/examples/stubs.py tests/helpers/examples/overview.py tests/helpers/pkg/circles/long_value.py tests/helpers/examples/cart/views.py tests/helpers/django_project/api/throttle.py tests/helpers/django_project/api/exceptions.py tests/helpers/django_project/api/version.py tests/helpers/pkg/self_pointer.py tests/helpers/django_project/api/__init__.py src/_dependencies/checks/func.py tests/helpers/pkg/circles/simple_value.py tests/test_contrib_rest_framework_generic_view_set.py src/_dependencies/checks/this.py tests/helpers/flask_project/app.py tests/helpers/examples/cart/filtersets.py tests/helpers/django_project/commands.py tests/test_operation.py tests/helpers/examples/faq.py tests/test_checks_loops.py tests/helpers/flask_project/commands.py tests/helpers/pkg/__init__.py tests/helpers/django_project/api/negotiation.py src/_dependencies/contrib/rest_framework.py
usage: dead [-h] [--files FILES] [--exclude EXCLUDE] [--tests TESTS]
dead: error: unrecognized arguments: tests/helpers/mddoctest.py tests/helpers/django_project/urls.py tests/helpers/django_project/api/urls.py tests/helpers/django_project/api/metadata.py tests/test_value.py src/_dependencies/raw.py tests/helpers/examples/utils.py tests/helpers/django_project/api/auth.py src/_dependencies/classes.py tests/helpers/django_project/api/commands.py tests/helpers/django_project/api/filtersets.py src/_dependencies/__init__.py tests/helpers/flask_project/views.py tests/helpers/django_project/settings.py src/_dependencies/value.py src/dependencies/contrib/rest_framework.py src/dependencies/contrib/pytest.py tests/helpers/django_project/forms.py src/_dependencies/contrib/celery.py tests/helpers/django_project/api/serializers.py tests/helpers/django_project/apps.py src/_dependencies/contrib/flask.py tests/helpers/pkg/circles/long_operation.py tests/helpers/examples/cart/forms.py tests/helpers/examples/order/__init__.py tests/helpers/examples/users/utils.py tests/test_nested.py src/_dependencies/this.py
usage: dead [-h] [--files FILES] [--exclude EXCLUDE] [--tests TESTS]
dead: error: unrecognized arguments: tests/helpers/examples/__init__.py src/_dependencies/exceptions.py tests/helpers/pkg/circles/complex_value.py src/_dependencies/checks/injector.py src/_dependencies/checks/loops.py src/dependencies/__init__.py tests/test_contrib_pytest.py src/_dependencies/contrib/pytest.py tests/helpers/django_project/api/views.py tests/helpers/django_project/views.py tests/test_configuration.py tests/helpers/pkg/circles/long_class.py src/dependencies/exceptions.py tests/test_this.py tests/helpers/pkg/circles/complex_class.py src/_dependencies/contrib/__init__.py src/_dependencies/nested.py tests/helpers/examples/cart/models.py src/_dependencies/package.py tests/helpers/examples/cart/__init__.py tests/test_contrib_celery.py tests/test_checks_circles.py tests/helpers/pkg/circles/simple_operation.py src/_dependencies/replace.py src/_dependencies/checks/circles.py tests/test_contrib_rest_framework_view_set.py
Certain decorators like Flask.route
point to usage from some external code or framework.
I'd like the ability to create a per-project whitelist (maybe with some common defaults) of such decorators to reduce the number of false positives
So it might be rather difficult to do, and i get the "i wrote it on an airplane" - but i like the idea of the code. However it doesn't really work with Django (i got a million deadcode things like my settings constants etc)
We have a lot of dead code that this tool catches beautifully. Thank you!
We also have a lot of not-actually-dead code, or "yes technically that's dead but there's a good reason why it's there".
Unfortunately, I don't have the stomach for adding # dead: disable
to all the places where this is the case. It would be a lot of changes that would make the code harder to read.
Would you be open to supporting as an input a "ignore_filelines" file? I'm thinking newline separated, in exactly the format you already use (fully/qualified/filename.py:<linum>
), that would essentially pre-populate visitor.disabled
(probably right after construction: https://github.com/asottile/dead/blob/main/dead.py#L310)?
This would let me keep our code separate from our linting. I think it would pretty much 'just' work, but I haven't fully grokked your code.
Let me know - if so, I'd like to make a PR and get this working!
Are there any cases dead
can detect dead code, which for example codecov can't?
The global option exclude
does not work for the dead
hook.
exclude: "..."
repos:
- repo: ...
To workaround this I exclude for this hook additionally:
- repo: https://github.com/asottile/dead
rev: v1.4.0
hooks:
- id: dead
args: ["--exclude", "..."]
can probably take inspiration from PyCQA/pyflakes#400
here's a small example
X = str
def s(): # type: () -> X
return 'hi'
It appears that dead
can't handle code that uses MyPy error codes in type: ignore
directives.
To reproduce:
def foo() -> int:
return None # type: ignore[no-any-return]
Run dead
with no arguments:
dargueta@tux ~/blah (master)$ dead
Traceback (most recent call last):
File "/Users/dargueta/.pyenv/versions/3.8.6/bin/dead", line 8, in <module>
sys.exit(main())
File "/Users/dargueta/.pyenv/versions/3.8.6/lib/python3.8/site-packages/dead.py", line 314, in main
visitor.visit_comment(lineno, s)
File "/Users/dargueta/.pyenv/versions/3.8.6/lib/python3.8/site-packages/dead.py", line 195, in visit_comment
ast_obj = ast.parse(part, f'<{self.filename}:{lineno}: comment>')
File "/Users/dargueta/.pyenv/versions/3.8.6/lib/python3.8/ast.py", line 47, in parse
return compile(source, filename, mode, flags,
File "<blah.py:2: comment>", line 1
ignore[no-any-return]
^
SyntaxError: invalid syntax
The problem is in the TYPE_IGNORE_RE
regex. The visit_comment() function ignores these type: ignore
comments, but the regex doesn't match type: ignore[xyz]
, only type: ignore
. I recommend changing the regex to:
TYPE_COMMENT_RE.pattern + r'ignore\s*(\[[\w-]+\]\s*)?(#|$)'
# ^^^^^^^^^^^^^^^^
# insert this
Environment:
dead
version: 1.3.0For example running dead
on itself yields
visit_ImportFrom is never read, defined in {'dead.py'}
visit_ClassDef is never read, defined in {'dead.py'}
visit_AsyncFunctionDef is never read, defined in {'dead.py'}
visit_Assign is never read, defined in {'dead.py'}
visit_Name is never read, defined in {'dead.py'}
visit_Attribute is never read, defined in {'dead.py'}
visit_Str is never read, defined in {'dead.py'}
Even though they are all read by NodeVisitor.visit
https://github.com/python/cpython/blob/master/Lib/ast.py#L250-253
Hi,
I use a framework that uses callbacks with some parameters I do not need in some cases. dead
always shows them as unused (and they are). Would it be possible to have a solution like pylint has to avoid dead
reporting them?
From pylint docs:
# fix for positional arguments: prepend _
def example1(_foo):
return 1
# fix for keyword args: catch into _
def example2(used_kw_arg=1, **_):
return used_kw_arg + 1
# fix for a single keyword arg: delete it
def example2(used_kw_arg=1, another_kw_arg=2):
del another_kw_arg
return used_kw_arg + 1
I tried the first and last solution with dead
and it still reports the parameters.
Best regards
dead is ~technically a linter of sorts -- I could see someone wanting to run it via pre-commit 🤷♂️
instructions are here -- here's an example
they are notably missing in this code
Hello, first of all, nice projects. I'm having some problems trying to run this with pre-commit. Basically, it's failing when reaching code with the new python syntax for generics.
Traceback (most recent call last):
File "/home/canalejas/.cache/pre-commit/repo05xoyxeg/py_env-python3/bin/dead", line 8, in <module>
sys.exit(main())
^^^^^^
File "/home/canalejas/.cache/pre-commit/repo05xoyxeg/py_env-python3/lib/python3.11/site-packages/dead.py", line 319, in main
tree = _ast(filename)
^^^^^^^^^^^^^^
File "/home/canalejas/.cache/pre-commit/repo05xoyxeg/py_env-python3/lib/python3.11/site-packages/dead.py", line 236, in _ast
return ast.parse(f.read(), filename=filename)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/ast.py", line 50, in parse
return compile(source, filename, mode, flags,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "apps/actions/management/helpers/input.py", line 56
def _input_competition[T: Trophy | Flag](_model: type[T], name: str) -> tuple[T | None, int | None]:
^
SyntaxError: expected '('
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.