deadcode 2.2.2__tar.gz → 2.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {deadcode-2.2.2 → deadcode-2.3.1}/PKG-INFO +90 -31
- {deadcode-2.2.2 → deadcode-2.3.1}/README.md +77 -26
- {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/actions/find_python_filenames.py +6 -6
- deadcode-2.3.1/deadcode/actions/fix_or_show_unused_code.py +66 -0
- {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/actions/get_unused_names_error_message.py +6 -6
- {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/actions/merge_overlaping_file_parts.py +2 -7
- {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/actions/parse_abstract_syntax_tree.py +1 -1
- deadcode-2.3.1/deadcode/actions/parse_arguments.py +229 -0
- {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/actions/remove_file_parts_from_content.py +36 -19
- {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/cli.py +7 -6
- deadcode-2.3.1/deadcode/constants.py +49 -0
- {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/data_types.py +2 -1
- deadcode-2.3.1/deadcode/utils/add_colors_to_diff.py +10 -0
- deadcode-2.2.2/deadcode/tests/base.py → deadcode-2.3.1/deadcode/utils/base_test_case.py +18 -18
- deadcode-2.3.1/deadcode/utils/fix_indent.py +69 -0
- {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/utils/flatten_lists.py +2 -2
- deadcode-2.3.1/deadcode/utils/nested_scopes.py +83 -0
- deadcode-2.3.1/deadcode/utils/print_ast.py +5 -0
- {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/visitor/code_item.py +17 -27
- {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/visitor/dead_code_visitor.py +96 -197
- {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/visitor/ignore.py +29 -29
- {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/visitor/lines.py +3 -3
- deadcode-2.3.1/deadcode/visitor/noqa.py +68 -0
- {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/visitor/utils.py +1 -1
- deadcode-2.3.1/pyproject.toml +208 -0
- deadcode-2.2.2/call_arg_map_to_parameters_poc.py +0 -173
- deadcode-2.2.2/deadcode/actions/fix_unused_code.py +0 -43
- deadcode-2.2.2/deadcode/actions/parse_arguments.py +0 -211
- deadcode-2.2.2/deadcode/constants.py +0 -49
- deadcode-2.2.2/deadcode/tests/actions/test_find_python_filenames.py +0 -36
- deadcode-2.2.2/deadcode/tests/actions/test_merge_overlaping_file_parts.py +0 -62
- deadcode-2.2.2/deadcode/tests/cli_args/test_count.py +0 -0
- deadcode-2.2.2/deadcode/tests/cli_args/test_exclude.py +0 -24
- deadcode-2.2.2/deadcode/tests/cli_args/test_fix.py +0 -528
- deadcode-2.2.2/deadcode/tests/cli_args/test_ignore_bodies_if_inherits_from.py +0 -46
- deadcode-2.2.2/deadcode/tests/cli_args/test_ignore_definitions.py +0 -106
- deadcode-2.2.2/deadcode/tests/cli_args/test_ignore_definitions_if_inherits_from.py +0 -125
- deadcode-2.2.2/deadcode/tests/cli_args/test_ignore_names.py +0 -79
- deadcode-2.2.2/deadcode/tests/cli_args/test_ignore_names_in_files.py +0 -0
- deadcode-2.2.2/deadcode/tests/cli_args/test_no_color.py +0 -0
- deadcode-2.2.2/deadcode/tests/cli_args/test_parsing.py +0 -66
- deadcode-2.2.2/deadcode/tests/cli_args/test_quiet.py +0 -0
- deadcode-2.2.2/deadcode/tests/cli_args/test_verbose.py +0 -0
- deadcode-2.2.2/deadcode/tests/code_item/test_code_item.py +0 -16
- deadcode-2.2.2/deadcode/tests/files/classes.py +0 -15
- deadcode-2.2.2/deadcode/tests/files/functions.py +0 -21
- deadcode-2.2.2/deadcode/tests/files/subdir/another_subdir/__init__.py +0 -1
- deadcode-2.2.2/deadcode/tests/files/subdir/one_more_file.py +0 -2
- deadcode-2.2.2/deadcode/tests/files/variables.py +0 -8
- deadcode-2.2.2/deadcode/tests/fix/test_assign.py +0 -92
- deadcode-2.2.2/deadcode/tests/fix/test_class_def.py +0 -116
- deadcode-2.2.2/deadcode/tests/fix/test_empty_files.py +0 -36
- deadcode-2.2.2/deadcode/tests/fix/test_method_def.py +0 -66
- deadcode-2.2.2/deadcode/tests/fix/test_scope.py +0 -243
- deadcode-2.2.2/deadcode/tests/fix/test_unreachable_code.py +0 -100
- deadcode-2.2.2/deadcode/tests/nested_scope/test_nested_scope.py +0 -85
- deadcode-2.2.2/deadcode/tests/noqa/test_noqa_comments.py +0 -329
- deadcode-2.2.2/deadcode/tests/test_accurate_usage_detection.py +0 -49
- deadcode-2.2.2/deadcode/tests/test_deadcode.py +0 -68
- deadcode-2.2.2/deadcode/tests/test_imports.py +0 -101
- deadcode-2.2.2/deadcode/tests/test_line_number_detection.py +0 -97
- deadcode-2.2.2/deadcode/tests/test_output.py +0 -137
- deadcode-2.2.2/deadcode/tests/test_variable_parsing_from_ast_dump.py +0 -12
- deadcode-2.2.2/deadcode/tests/utils/test_fix_indent.py +0 -10
- deadcode-2.2.2/deadcode/utils/fix_indent.py +0 -36
- deadcode-2.2.2/deadcode/utils/nested_scopes.py +0 -47
- deadcode-2.2.2/deadcode/utils/print_ast.py +0 -5
- deadcode-2.2.2/deadcode/visitor/noqa.py +0 -55
- deadcode-2.2.2/pyproject.toml +0 -91
- {deadcode-2.2.2 → deadcode-2.3.1}/.gitignore +0 -0
- {deadcode-2.2.2 → deadcode-2.3.1}/LICENSE +0 -0
- {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/__init__.py +0 -0
- {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/actions/__init__.py +0 -0
- {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/actions/find_unused_names.py +0 -0
- {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/utils/__init__.py +0 -0
- {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/visitor/__init__.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: deadcode
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.1
|
|
4
4
|
Summary: Find and remove dead code.
|
|
5
|
-
Project-URL:
|
|
6
|
-
Project-URL:
|
|
5
|
+
Project-URL: homepage, https://github.com/albertas/deadcode
|
|
6
|
+
Project-URL: documentation, https://deadcode.readthedocs.io/
|
|
7
7
|
Author-email: Albertas Gimbutas <albertasgim@gmail.com>
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Classifier: Development Status :: 3 - Alpha
|
|
@@ -15,14 +15,28 @@ Classifier: Programming Language :: Python :: 3.8
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.9
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
19
|
Requires-Python: >=3.8
|
|
19
|
-
Requires-Dist: tomli
|
|
20
|
+
Requires-Dist: tomli>=2.0.1
|
|
21
|
+
Provides-Extra: test
|
|
22
|
+
Requires-Dist: hatch; extra == 'test'
|
|
23
|
+
Requires-Dist: mypy; extra == 'test'
|
|
24
|
+
Requires-Dist: pip-audit; extra == 'test'
|
|
25
|
+
Requires-Dist: pytest; extra == 'test'
|
|
26
|
+
Requires-Dist: pytest-cov; extra == 'test'
|
|
27
|
+
Requires-Dist: ruff; extra == 'test'
|
|
20
28
|
Description-Content-Type: text/markdown
|
|
21
29
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
30
|
+
[](https://deadcode.readthedocs.io/en/stable/)
|
|
31
|
+
|
|
32
|
+
<h2 align="center">Find and Fix Unused Python Code</h2>
|
|
33
|
+
|
|
34
|
+
<p align="center">
|
|
35
|
+
<a href="https://github.com/albertas/deadcode/blob/main/LICENSE"><img alt="License: AGPLv3" src="https://raw.githubusercontent.com/albertas/deadcode/main/docs/_static/AGPLv3-license.svg"></a>
|
|
36
|
+
<a href="https://pypi.org/project/deadcode/"><img alt="PyPI" src="https://img.shields.io/pypi/v/deadcode"></a>
|
|
37
|
+
<a href="https://pepy.tech/project/deadcode"><img alt="Downloads" src="https://static.pepy.tech/badge/deadcode"></a>
|
|
38
|
+
</p>
|
|
39
|
+
|
|
26
40
|
|
|
27
41
|
## Installation
|
|
28
42
|
```shell
|
|
@@ -30,11 +44,27 @@ pip install deadcode
|
|
|
30
44
|
```
|
|
31
45
|
|
|
32
46
|
## Usage
|
|
47
|
+
To see unused code findings:
|
|
33
48
|
```shell
|
|
34
49
|
deadcode .
|
|
35
50
|
```
|
|
36
51
|
|
|
37
|
-
|
|
52
|
+
To see suggested fixes for all files:
|
|
53
|
+
```shell
|
|
54
|
+
deadcode . --fix --dry
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
To see suggested fixes only for `foo.py` file:
|
|
58
|
+
```shell
|
|
59
|
+
deadcode . --fix --dry foo.py
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
To fix:
|
|
63
|
+
```shell
|
|
64
|
+
deadcode . --fix
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Tune out some of the false positives, e.g.:
|
|
38
68
|
```
|
|
39
69
|
deadcode . --exclude=venv,tests --ignore-names=BaseTestCase,*Mixin --ignore-names-in-files=migrations
|
|
40
70
|
```
|
|
@@ -47,11 +77,12 @@ ignore-names = ["BaseTestCase", "*Mixin"]
|
|
|
47
77
|
ignore-names-in-files = ["migrations"]
|
|
48
78
|
```
|
|
49
79
|
|
|
50
|
-
|
|
80
|
+
## Command line options
|
|
51
81
|
|
|
52
82
|
| Option | Type | Meaning |
|
|
53
83
|
|-------------------------------------------|------|----------------------------------------------------------------------|
|
|
54
84
|
|`--fix` | - | Automatically remove detected unused code expressions from the code base. |
|
|
85
|
+
|`--dry` | - or list | Show changes which would be made in files. Shows changes for provided filenames or shows all changes if no filename is specified. |
|
|
55
86
|
|`--exclude` | list | Filenames (or path expressions), which will be completely skipped without being analysed. |
|
|
56
87
|
|`--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`. |
|
|
57
88
|
|`--ignore-names-in-files` | list | Ignores unused names in files, which filenames match provided path expressions. |
|
|
@@ -77,27 +108,26 @@ definition - whole class or function definition expression including its name an
|
|
|
77
108
|
## Rules
|
|
78
109
|
| Code | Name | Message |
|
|
79
110
|
|--------|--------------------|----------------|
|
|
80
|
-
|
|
|
81
|
-
|
|
|
82
|
-
|
|
|
83
|
-
|
|
|
84
|
-
|
|
|
85
|
-
|
|
|
86
|
-
|
|
|
87
|
-
|
|
|
88
|
-
|
|
|
89
|
-
|
|
|
90
|
-
|
|
|
91
|
-
|
|
|
92
|
-
|
|
93
|
-
`*` - are not yet implemented rules.
|
|
111
|
+
| DC01 | unused-variable | Variable `{name}` is never used
|
|
112
|
+
| DC02 | unused-function | Function `{name}` is never used
|
|
113
|
+
| DC03 | unused-class | Class `{name}` is never used
|
|
114
|
+
| DC04 | unused-method | Method `{name}` is never used
|
|
115
|
+
| DC05 | unused-attribute | Attribute `{name}` is never used
|
|
116
|
+
| DC06 | unused-name | Name `{name}` is never used
|
|
117
|
+
| DC07 | unused-import | Import `{name}` is never used
|
|
118
|
+
| DC08 | unused-property | Property `{name}` is never used
|
|
119
|
+
| DC09 | unreachable-if-block | Unreachable conditional statement block
|
|
120
|
+
| DC11 | empty-file | Empty Python file
|
|
121
|
+
| DC12 | commented-out-code | Commented out code
|
|
122
|
+
| DC13 | unreachable-code | Code after terminal statement, e.g. `return`, `raise`, `continue`, `break`
|
|
123
|
+
| 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)
|
|
94
124
|
|
|
95
125
|
## Ignoring checks with noqa comments
|
|
96
126
|
Inline `# noqa` comments can be used to ignore `deadcode` checks.
|
|
97
|
-
E.g. unused `Foo` class wont be detected/fixed because `# noqa:
|
|
127
|
+
E.g. unused `Foo` class wont be detected/fixed because `# noqa: DC03` comment is used:
|
|
98
128
|
|
|
99
129
|
```python
|
|
100
|
-
class Foo: # noqa:
|
|
130
|
+
class Foo: # noqa: DC03
|
|
101
131
|
pass
|
|
102
132
|
```
|
|
103
133
|
|
|
@@ -127,10 +157,10 @@ code base is implemented in.
|
|
|
127
157
|
|
|
128
158
|
## Feature requests
|
|
129
159
|
- [x] Replace `.*` with only `*` in regexp matching.
|
|
130
|
-
- [x] Add unused class method detection
|
|
160
|
+
- [x] Add unused class method detection DC04 check.
|
|
131
161
|
- [x] Add `--fix` option to automatically remove detected dead code occourencies
|
|
132
162
|
- [x] Add a check for empty python files.
|
|
133
|
-
- [x] Split error codes into
|
|
163
|
+
- [x] Split error codes into DC01, DC02, DC03 for variables, functions, class.
|
|
134
164
|
- [x] Should have different codes for ignoring name and ignoring whole definition (reserved DCxx0 - ignore name, DCxx1 - ignore definition).
|
|
135
165
|
- [ ] Allow to disable each check separately using:
|
|
136
166
|
- [ ] inline comment.
|
|
@@ -144,8 +174,37 @@ code base is implemented in.
|
|
|
144
174
|
- [ ] --ignore-definitions-if-decorated-with
|
|
145
175
|
- [ ] --ignore-names-if-inherits-from
|
|
146
176
|
- [ ] --ignore-names-if-decorated-with
|
|
177
|
+
- [ ] --ignore-bodies-of
|
|
178
|
+
- [ ] --ignore-bodies-if-decorated-with
|
|
179
|
+
- [ ] --ignore-bodies-if-inherits-from
|
|
180
|
+
- [ ] --ignore-definitions
|
|
181
|
+
- [ ] --ignore-definitions-if-inherits-from
|
|
182
|
+
- [ ] --ignore-definitions-if-decorated-with
|
|
183
|
+
- 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?
|
|
184
|
+
- What expression would allow this type of precission?
|
|
147
185
|
- [ ] Distinguish between definitions with same name, but different files.
|
|
148
186
|
- [ ] Repeated application of `deadcode` till the output stops changing.
|
|
149
|
-
- [ ] Unreachable code detection and fixing.
|
|
150
|
-
- [
|
|
187
|
+
- [ ] Unreachable code detection and fixing: this should only be scoped for if statements and only limited to primitive variables.
|
|
188
|
+
- [x] `--fix --dry [filenames]` - only show whats about to change in the listed filenames.
|
|
151
189
|
- [ ] Benchmarking performance with larger projects (time, CPU and memory consumption) in order to optimize.
|
|
190
|
+
- [ ] `--fix` could accept a list of filenames as well (only those files would be changed, but the summary could would be full).
|
|
191
|
+
(This might be confusing, because filenames, which have to be considered are provided without any flag, --fix is expected to not accept arguments)
|
|
192
|
+
- [ ] pre-commit-hook.
|
|
193
|
+
- [ ] language server.
|
|
194
|
+
- [x] Use only two digits for error codes instead of 3. Two is plenty and it simplifies usage a bit
|
|
195
|
+
- [ ] DC10: remove code after terminal statements like `raise`, `return`, `break`, `continue` and comes in the same scope.
|
|
196
|
+
- [ ] Add `ignore` and `per-file-ignores` command line and pyproject.toml options, which allows to skip some rules.
|
|
197
|
+
- [ ] Make sure that all rules are being skipped by `noqa` comment and all rules react to `noqa: rule_id` comments.
|
|
198
|
+
- [ ] Include package names into code item scope (dot-separated path), e.g. "package1.package2.module.class.method.variable".
|
|
199
|
+
- [ ] All options should be able to accept dot-separated path or a generic name, e.g. "marshmallow.Schema" vs "Schema",
|
|
200
|
+
documentation should cleary demonstrate the behaviour/example that "Schema" means "*.Schema".
|
|
201
|
+
- [ ] Redefinition of an existing name makes previous name unreachable, unless it is assigned somehow.
|
|
202
|
+
- [ ] Check if file is still valid/parsable after automatic fixing, if not: halt the change and report error.
|
|
203
|
+
|
|
204
|
+
## Release notes
|
|
205
|
+
- v2.3.1:
|
|
206
|
+
- Started analysing files in bytes instead of trying to convert them into UTF-8 encoded strings.
|
|
207
|
+
- Improved automatic removal of unused imports.
|
|
208
|
+
- v2.3.0:
|
|
209
|
+
- Add `--dry` option.
|
|
210
|
+
- Update error codes to use DCXX format instead of DCXXX.
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
[](https://deadcode.readthedocs.io/en/stable/)
|
|
2
|
+
|
|
3
|
+
<h2 align="center">Find and Fix Unused Python Code</h2>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://github.com/albertas/deadcode/blob/main/LICENSE"><img alt="License: AGPLv3" src="https://raw.githubusercontent.com/albertas/deadcode/main/docs/_static/AGPLv3-license.svg"></a>
|
|
7
|
+
<a href="https://pypi.org/project/deadcode/"><img alt="PyPI" src="https://img.shields.io/pypi/v/deadcode"></a>
|
|
8
|
+
<a href="https://pepy.tech/project/deadcode"><img alt="Downloads" src="https://static.pepy.tech/badge/deadcode"></a>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
5
11
|
|
|
6
12
|
## Installation
|
|
7
13
|
```shell
|
|
@@ -9,11 +15,27 @@ pip install deadcode
|
|
|
9
15
|
```
|
|
10
16
|
|
|
11
17
|
## Usage
|
|
18
|
+
To see unused code findings:
|
|
12
19
|
```shell
|
|
13
20
|
deadcode .
|
|
14
21
|
```
|
|
15
22
|
|
|
16
|
-
|
|
23
|
+
To see suggested fixes for all files:
|
|
24
|
+
```shell
|
|
25
|
+
deadcode . --fix --dry
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
To see suggested fixes only for `foo.py` file:
|
|
29
|
+
```shell
|
|
30
|
+
deadcode . --fix --dry foo.py
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
To fix:
|
|
34
|
+
```shell
|
|
35
|
+
deadcode . --fix
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Tune out some of the false positives, e.g.:
|
|
17
39
|
```
|
|
18
40
|
deadcode . --exclude=venv,tests --ignore-names=BaseTestCase,*Mixin --ignore-names-in-files=migrations
|
|
19
41
|
```
|
|
@@ -26,11 +48,12 @@ ignore-names = ["BaseTestCase", "*Mixin"]
|
|
|
26
48
|
ignore-names-in-files = ["migrations"]
|
|
27
49
|
```
|
|
28
50
|
|
|
29
|
-
|
|
51
|
+
## Command line options
|
|
30
52
|
|
|
31
53
|
| Option | Type | Meaning |
|
|
32
54
|
|-------------------------------------------|------|----------------------------------------------------------------------|
|
|
33
55
|
|`--fix` | - | Automatically remove detected unused code expressions from the code base. |
|
|
56
|
+
|`--dry` | - or list | Show changes which would be made in files. Shows changes for provided filenames or shows all changes if no filename is specified. |
|
|
34
57
|
|`--exclude` | list | Filenames (or path expressions), which will be completely skipped without being analysed. |
|
|
35
58
|
|`--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`. |
|
|
36
59
|
|`--ignore-names-in-files` | list | Ignores unused names in files, which filenames match provided path expressions. |
|
|
@@ -56,27 +79,26 @@ definition - whole class or function definition expression including its name an
|
|
|
56
79
|
## Rules
|
|
57
80
|
| Code | Name | Message |
|
|
58
81
|
|--------|--------------------|----------------|
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
72
|
-
`*` - are not yet implemented rules.
|
|
82
|
+
| DC01 | unused-variable | Variable `{name}` is never used
|
|
83
|
+
| DC02 | unused-function | Function `{name}` is never used
|
|
84
|
+
| DC03 | unused-class | Class `{name}` is never used
|
|
85
|
+
| DC04 | unused-method | Method `{name}` is never used
|
|
86
|
+
| DC05 | unused-attribute | Attribute `{name}` is never used
|
|
87
|
+
| DC06 | unused-name | Name `{name}` is never used
|
|
88
|
+
| DC07 | unused-import | Import `{name}` is never used
|
|
89
|
+
| DC08 | unused-property | Property `{name}` is never used
|
|
90
|
+
| DC09 | unreachable-if-block | Unreachable conditional statement block
|
|
91
|
+
| DC11 | empty-file | Empty Python file
|
|
92
|
+
| DC12 | commented-out-code | Commented out code
|
|
93
|
+
| DC13 | unreachable-code | Code after terminal statement, e.g. `return`, `raise`, `continue`, `break`
|
|
94
|
+
| 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)
|
|
73
95
|
|
|
74
96
|
## Ignoring checks with noqa comments
|
|
75
97
|
Inline `# noqa` comments can be used to ignore `deadcode` checks.
|
|
76
|
-
E.g. unused `Foo` class wont be detected/fixed because `# noqa:
|
|
98
|
+
E.g. unused `Foo` class wont be detected/fixed because `# noqa: DC03` comment is used:
|
|
77
99
|
|
|
78
100
|
```python
|
|
79
|
-
class Foo: # noqa:
|
|
101
|
+
class Foo: # noqa: DC03
|
|
80
102
|
pass
|
|
81
103
|
```
|
|
82
104
|
|
|
@@ -106,10 +128,10 @@ code base is implemented in.
|
|
|
106
128
|
|
|
107
129
|
## Feature requests
|
|
108
130
|
- [x] Replace `.*` with only `*` in regexp matching.
|
|
109
|
-
- [x] Add unused class method detection
|
|
131
|
+
- [x] Add unused class method detection DC04 check.
|
|
110
132
|
- [x] Add `--fix` option to automatically remove detected dead code occourencies
|
|
111
133
|
- [x] Add a check for empty python files.
|
|
112
|
-
- [x] Split error codes into
|
|
134
|
+
- [x] Split error codes into DC01, DC02, DC03 for variables, functions, class.
|
|
113
135
|
- [x] Should have different codes for ignoring name and ignoring whole definition (reserved DCxx0 - ignore name, DCxx1 - ignore definition).
|
|
114
136
|
- [ ] Allow to disable each check separately using:
|
|
115
137
|
- [ ] inline comment.
|
|
@@ -123,8 +145,37 @@ code base is implemented in.
|
|
|
123
145
|
- [ ] --ignore-definitions-if-decorated-with
|
|
124
146
|
- [ ] --ignore-names-if-inherits-from
|
|
125
147
|
- [ ] --ignore-names-if-decorated-with
|
|
148
|
+
- [ ] --ignore-bodies-of
|
|
149
|
+
- [ ] --ignore-bodies-if-decorated-with
|
|
150
|
+
- [ ] --ignore-bodies-if-inherits-from
|
|
151
|
+
- [ ] --ignore-definitions
|
|
152
|
+
- [ ] --ignore-definitions-if-inherits-from
|
|
153
|
+
- [ ] --ignore-definitions-if-decorated-with
|
|
154
|
+
- 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?
|
|
155
|
+
- What expression would allow this type of precission?
|
|
126
156
|
- [ ] Distinguish between definitions with same name, but different files.
|
|
127
157
|
- [ ] Repeated application of `deadcode` till the output stops changing.
|
|
128
|
-
- [ ] Unreachable code detection and fixing.
|
|
129
|
-
- [
|
|
158
|
+
- [ ] Unreachable code detection and fixing: this should only be scoped for if statements and only limited to primitive variables.
|
|
159
|
+
- [x] `--fix --dry [filenames]` - only show whats about to change in the listed filenames.
|
|
130
160
|
- [ ] Benchmarking performance with larger projects (time, CPU and memory consumption) in order to optimize.
|
|
161
|
+
- [ ] `--fix` could accept a list of filenames as well (only those files would be changed, but the summary could would be full).
|
|
162
|
+
(This might be confusing, because filenames, which have to be considered are provided without any flag, --fix is expected to not accept arguments)
|
|
163
|
+
- [ ] pre-commit-hook.
|
|
164
|
+
- [ ] language server.
|
|
165
|
+
- [x] Use only two digits for error codes instead of 3. Two is plenty and it simplifies usage a bit
|
|
166
|
+
- [ ] DC10: remove code after terminal statements like `raise`, `return`, `break`, `continue` and comes in the same scope.
|
|
167
|
+
- [ ] Add `ignore` and `per-file-ignores` command line and pyproject.toml options, which allows to skip some rules.
|
|
168
|
+
- [ ] Make sure that all rules are being skipped by `noqa` comment and all rules react to `noqa: rule_id` comments.
|
|
169
|
+
- [ ] Include package names into code item scope (dot-separated path), e.g. "package1.package2.module.class.method.variable".
|
|
170
|
+
- [ ] All options should be able to accept dot-separated path or a generic name, e.g. "marshmallow.Schema" vs "Schema",
|
|
171
|
+
documentation should cleary demonstrate the behaviour/example that "Schema" means "*.Schema".
|
|
172
|
+
- [ ] Redefinition of an existing name makes previous name unreachable, unless it is assigned somehow.
|
|
173
|
+
- [ ] Check if file is still valid/parsable after automatic fixing, if not: halt the change and report error.
|
|
174
|
+
|
|
175
|
+
## Release notes
|
|
176
|
+
- v2.3.1:
|
|
177
|
+
- Started analysing files in bytes instead of trying to convert them into UTF-8 encoded strings.
|
|
178
|
+
- Improved automatic removal of unused imports.
|
|
179
|
+
- v2.3.0:
|
|
180
|
+
- Add `--dry` option.
|
|
181
|
+
- Update error codes to use DCXX format instead of DCXXX.
|
|
@@ -16,21 +16,21 @@ def find_python_filenames(args: Args) -> List[str]:
|
|
|
16
16
|
|
|
17
17
|
if _match(path, args.exclude):
|
|
18
18
|
if args.verbose:
|
|
19
|
-
logger.info(f
|
|
19
|
+
logger.info(f'Ignoring: {path}')
|
|
20
20
|
continue
|
|
21
21
|
|
|
22
|
-
if path.is_file() and path.suffix ==
|
|
22
|
+
if path.is_file() and path.suffix == '.py':
|
|
23
23
|
filenames.append(str(path))
|
|
24
24
|
elif path.is_dir():
|
|
25
|
-
paths += list([str(p) for p in path.glob(
|
|
25
|
+
paths += list([str(p) for p in path.glob('*')])
|
|
26
26
|
elif not path.exists():
|
|
27
27
|
# TODO: unify error logging and reporting
|
|
28
28
|
# Maybe a cli flag could be added to stop on error
|
|
29
|
-
logger.error(f
|
|
29
|
+
logger.error(f'Error: {path} could not be found.')
|
|
30
30
|
|
|
31
31
|
if args.verbose:
|
|
32
|
-
sep =
|
|
33
|
-
logger.info(f
|
|
32
|
+
sep = '\n - '
|
|
33
|
+
logger.info(f'Files to be checked for dead code: {sep.join(filenames)}')
|
|
34
34
|
return filenames
|
|
35
35
|
|
|
36
36
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
from difflib import diff_bytes, unified_diff
|
|
3
|
+
from typing import Iterable
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
from deadcode.actions.merge_overlaping_file_parts import merge_overlaping_file_parts
|
|
7
|
+
from deadcode.actions.remove_file_parts_from_content import remove_file_parts_from_content
|
|
8
|
+
from deadcode.data_types import Args
|
|
9
|
+
from deadcode.visitor.code_item import CodeItem
|
|
10
|
+
from deadcode.utils.flatten_lists import flatten_list
|
|
11
|
+
from deadcode.utils.add_colors_to_diff import add_colors_to_diff
|
|
12
|
+
from deadcode.visitor.ignore import _match
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def fix_or_show_unused_code(unused_items: Iterable[CodeItem], args: Args) -> str:
|
|
16
|
+
# Reading and writing should be patched in this file.
|
|
17
|
+
|
|
18
|
+
# Group unused_names by filenames
|
|
19
|
+
# For each filename find file parts, which have to be fixed.
|
|
20
|
+
# And fix those file parts.
|
|
21
|
+
#
|
|
22
|
+
# TODO: trailing commas, trailing white spaces and empty lines
|
|
23
|
+
|
|
24
|
+
filename_to_unused_items = defaultdict(list)
|
|
25
|
+
for unused_item in unused_items:
|
|
26
|
+
filename_to_unused_items[str(unused_item.filename)].append(unused_item)
|
|
27
|
+
|
|
28
|
+
result = []
|
|
29
|
+
|
|
30
|
+
for filename, file_unused_items in filename_to_unused_items.items():
|
|
31
|
+
file_parts = flatten_list([item.code_parts for item in file_unused_items])
|
|
32
|
+
|
|
33
|
+
unused_file_parts = merge_overlaping_file_parts(file_parts)
|
|
34
|
+
|
|
35
|
+
with open(filename, 'rb') as f:
|
|
36
|
+
file_content_lines = f.readlines()
|
|
37
|
+
|
|
38
|
+
updated_file_content_lines = remove_file_parts_from_content(file_content_lines, unused_file_parts)
|
|
39
|
+
updated_file_content = b''.join(updated_file_content_lines)
|
|
40
|
+
if updated_file_content.strip():
|
|
41
|
+
if args.dry and ('__all_files__' in args.dry or _match(filename, args.dry)):
|
|
42
|
+
with open(filename, 'rb') as f:
|
|
43
|
+
filename_bytes = filename.encode()
|
|
44
|
+
diff = diff_bytes(unified_diff, f.readlines(), updated_file_content_lines,
|
|
45
|
+
fromfile=filename_bytes, tofile=filename_bytes)
|
|
46
|
+
# TODO: consider printing result instantly to save memory
|
|
47
|
+
result_chunk = b''.join(diff)
|
|
48
|
+
if args.no_color:
|
|
49
|
+
result.append(result_chunk)
|
|
50
|
+
else:
|
|
51
|
+
result.append(add_colors_to_diff(result_chunk))
|
|
52
|
+
|
|
53
|
+
elif args.fix:
|
|
54
|
+
with open(filename, 'wb') as f:
|
|
55
|
+
# TODO: is there a method writelines?
|
|
56
|
+
f.write(updated_file_content)
|
|
57
|
+
else:
|
|
58
|
+
os.remove(filename)
|
|
59
|
+
|
|
60
|
+
if result:
|
|
61
|
+
return b'\n'.join(result).decode()
|
|
62
|
+
return ''
|
|
63
|
+
|
|
64
|
+
# TODO: update this one: solution is to use read and write operations.
|
|
65
|
+
#
|
|
66
|
+
# Solution is to open file for reading and then for writing.
|
|
@@ -11,25 +11,25 @@ def get_unused_names_error_message(unused_names: Iterable[CodeItem], args: Args)
|
|
|
11
11
|
return None
|
|
12
12
|
|
|
13
13
|
if args.quiet:
|
|
14
|
-
return
|
|
14
|
+
return ''
|
|
15
15
|
|
|
16
16
|
if args.count:
|
|
17
|
-
return f
|
|
17
|
+
return f'{len(unused_names)}'
|
|
18
18
|
|
|
19
19
|
messages = []
|
|
20
20
|
for item in unused_names:
|
|
21
|
-
message = f
|
|
21
|
+
message = f'{item.filename_with_position} \033[91m{item.error_code}\033[0m '
|
|
22
22
|
message += item.message or (
|
|
23
23
|
f"{item.type_.replace('_', ' ').capitalize()} " f"`\033[1m{item.name}\033[0m` " f"is never used"
|
|
24
24
|
)
|
|
25
25
|
if args.no_color:
|
|
26
|
-
message = message.replace(
|
|
26
|
+
message = message.replace('\033[91m', '').replace('\033[1m', '').replace('\033[0m', '')
|
|
27
27
|
messages.append(message)
|
|
28
28
|
|
|
29
29
|
if args.fix:
|
|
30
30
|
message = f"\nRemoved \033[1m{len(unused_names)}\033[0m unused code item{'s' if len(unused_names) > 1 else ''}!"
|
|
31
31
|
if args.no_color:
|
|
32
|
-
message = message.replace(
|
|
32
|
+
message = message.replace('\x1b[1m', '').replace('\x1b[0m', '')
|
|
33
33
|
messages.append(message)
|
|
34
34
|
|
|
35
|
-
return
|
|
35
|
+
return '\n'.join(messages)
|
|
@@ -10,9 +10,7 @@ def does_include(bigger_part: Part, smaller_part: Part) -> bool:
|
|
|
10
10
|
|
|
11
11
|
ends_faster = (line_end_b < line_end_s) or ((line_end_b == line_end_b) and (col_end_b <= col_end_s))
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
return True
|
|
15
|
-
return False
|
|
13
|
+
return bool(starts_later and ends_faster)
|
|
16
14
|
|
|
17
15
|
|
|
18
16
|
def sort_parts(bigger_part: Part, smaller_part: Part) -> Tuple[Part, Part]:
|
|
@@ -34,10 +32,7 @@ def does_overlap(bigger_part: Part, smaller_part: Part) -> bool:
|
|
|
34
32
|
line_start_s, line_end_s, col_start_s, col_end_s = smaller_part
|
|
35
33
|
|
|
36
34
|
# ar pirmo pabaiga perlipa antro pradžia
|
|
37
|
-
|
|
38
|
-
return True
|
|
39
|
-
|
|
40
|
-
return False
|
|
35
|
+
return bool((line_end_b > line_start_s) or ((line_end_b == line_start_s) and (col_end_b > col_start_s)))
|
|
41
36
|
|
|
42
37
|
|
|
43
38
|
def merge_parts(p1: Part, p2: Part) -> Optional[Part]:
|
|
@@ -9,5 +9,5 @@ def parse_abstract_syntax_tree(file_content: FileContent, args: Args, filename:
|
|
|
9
9
|
return ast.parse(file_content, filename=filename, type_comments=True)
|
|
10
10
|
except: # noqa: E722
|
|
11
11
|
if not args.count and not args.quiet:
|
|
12
|
-
print(f
|
|
12
|
+
print(f'Error: Failed to parse {filename} file, ignoring it.')
|
|
13
13
|
return ast.Module()
|