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.
Files changed (76) hide show
  1. {deadcode-2.2.2 → deadcode-2.3.1}/PKG-INFO +90 -31
  2. {deadcode-2.2.2 → deadcode-2.3.1}/README.md +77 -26
  3. {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/actions/find_python_filenames.py +6 -6
  4. deadcode-2.3.1/deadcode/actions/fix_or_show_unused_code.py +66 -0
  5. {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/actions/get_unused_names_error_message.py +6 -6
  6. {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/actions/merge_overlaping_file_parts.py +2 -7
  7. {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/actions/parse_abstract_syntax_tree.py +1 -1
  8. deadcode-2.3.1/deadcode/actions/parse_arguments.py +229 -0
  9. {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/actions/remove_file_parts_from_content.py +36 -19
  10. {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/cli.py +7 -6
  11. deadcode-2.3.1/deadcode/constants.py +49 -0
  12. {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/data_types.py +2 -1
  13. deadcode-2.3.1/deadcode/utils/add_colors_to_diff.py +10 -0
  14. deadcode-2.2.2/deadcode/tests/base.py → deadcode-2.3.1/deadcode/utils/base_test_case.py +18 -18
  15. deadcode-2.3.1/deadcode/utils/fix_indent.py +69 -0
  16. {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/utils/flatten_lists.py +2 -2
  17. deadcode-2.3.1/deadcode/utils/nested_scopes.py +83 -0
  18. deadcode-2.3.1/deadcode/utils/print_ast.py +5 -0
  19. {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/visitor/code_item.py +17 -27
  20. {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/visitor/dead_code_visitor.py +96 -197
  21. {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/visitor/ignore.py +29 -29
  22. {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/visitor/lines.py +3 -3
  23. deadcode-2.3.1/deadcode/visitor/noqa.py +68 -0
  24. {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/visitor/utils.py +1 -1
  25. deadcode-2.3.1/pyproject.toml +208 -0
  26. deadcode-2.2.2/call_arg_map_to_parameters_poc.py +0 -173
  27. deadcode-2.2.2/deadcode/actions/fix_unused_code.py +0 -43
  28. deadcode-2.2.2/deadcode/actions/parse_arguments.py +0 -211
  29. deadcode-2.2.2/deadcode/constants.py +0 -49
  30. deadcode-2.2.2/deadcode/tests/actions/test_find_python_filenames.py +0 -36
  31. deadcode-2.2.2/deadcode/tests/actions/test_merge_overlaping_file_parts.py +0 -62
  32. deadcode-2.2.2/deadcode/tests/cli_args/test_count.py +0 -0
  33. deadcode-2.2.2/deadcode/tests/cli_args/test_exclude.py +0 -24
  34. deadcode-2.2.2/deadcode/tests/cli_args/test_fix.py +0 -528
  35. deadcode-2.2.2/deadcode/tests/cli_args/test_ignore_bodies_if_inherits_from.py +0 -46
  36. deadcode-2.2.2/deadcode/tests/cli_args/test_ignore_definitions.py +0 -106
  37. deadcode-2.2.2/deadcode/tests/cli_args/test_ignore_definitions_if_inherits_from.py +0 -125
  38. deadcode-2.2.2/deadcode/tests/cli_args/test_ignore_names.py +0 -79
  39. deadcode-2.2.2/deadcode/tests/cli_args/test_ignore_names_in_files.py +0 -0
  40. deadcode-2.2.2/deadcode/tests/cli_args/test_no_color.py +0 -0
  41. deadcode-2.2.2/deadcode/tests/cli_args/test_parsing.py +0 -66
  42. deadcode-2.2.2/deadcode/tests/cli_args/test_quiet.py +0 -0
  43. deadcode-2.2.2/deadcode/tests/cli_args/test_verbose.py +0 -0
  44. deadcode-2.2.2/deadcode/tests/code_item/test_code_item.py +0 -16
  45. deadcode-2.2.2/deadcode/tests/files/classes.py +0 -15
  46. deadcode-2.2.2/deadcode/tests/files/functions.py +0 -21
  47. deadcode-2.2.2/deadcode/tests/files/subdir/another_subdir/__init__.py +0 -1
  48. deadcode-2.2.2/deadcode/tests/files/subdir/one_more_file.py +0 -2
  49. deadcode-2.2.2/deadcode/tests/files/variables.py +0 -8
  50. deadcode-2.2.2/deadcode/tests/fix/test_assign.py +0 -92
  51. deadcode-2.2.2/deadcode/tests/fix/test_class_def.py +0 -116
  52. deadcode-2.2.2/deadcode/tests/fix/test_empty_files.py +0 -36
  53. deadcode-2.2.2/deadcode/tests/fix/test_method_def.py +0 -66
  54. deadcode-2.2.2/deadcode/tests/fix/test_scope.py +0 -243
  55. deadcode-2.2.2/deadcode/tests/fix/test_unreachable_code.py +0 -100
  56. deadcode-2.2.2/deadcode/tests/nested_scope/test_nested_scope.py +0 -85
  57. deadcode-2.2.2/deadcode/tests/noqa/test_noqa_comments.py +0 -329
  58. deadcode-2.2.2/deadcode/tests/test_accurate_usage_detection.py +0 -49
  59. deadcode-2.2.2/deadcode/tests/test_deadcode.py +0 -68
  60. deadcode-2.2.2/deadcode/tests/test_imports.py +0 -101
  61. deadcode-2.2.2/deadcode/tests/test_line_number_detection.py +0 -97
  62. deadcode-2.2.2/deadcode/tests/test_output.py +0 -137
  63. deadcode-2.2.2/deadcode/tests/test_variable_parsing_from_ast_dump.py +0 -12
  64. deadcode-2.2.2/deadcode/tests/utils/test_fix_indent.py +0 -10
  65. deadcode-2.2.2/deadcode/utils/fix_indent.py +0 -36
  66. deadcode-2.2.2/deadcode/utils/nested_scopes.py +0 -47
  67. deadcode-2.2.2/deadcode/utils/print_ast.py +0 -5
  68. deadcode-2.2.2/deadcode/visitor/noqa.py +0 -55
  69. deadcode-2.2.2/pyproject.toml +0 -91
  70. {deadcode-2.2.2 → deadcode-2.3.1}/.gitignore +0 -0
  71. {deadcode-2.2.2 → deadcode-2.3.1}/LICENSE +0 -0
  72. {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/__init__.py +0 -0
  73. {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/actions/__init__.py +0 -0
  74. {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/actions/find_unused_names.py +0 -0
  75. {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/utils/__init__.py +0 -0
  76. {deadcode-2.2.2 → deadcode-2.3.1}/deadcode/visitor/__init__.py +0 -0
@@ -1,9 +1,9 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: deadcode
3
- Version: 2.2.2
3
+ Version: 2.3.1
4
4
  Summary: Find and remove dead code.
5
- Project-URL: Homepage, https://github.com/albertas/deadcode
6
- Project-URL: Documentation, https://deadcode.readthedocs.io/
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==2.0.1
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
- # deadcode
23
- `deadcode` allows to detect and fix unused Python code. It implements `DCXXX`
24
- static code linting rules for detecting unused code such as
25
- variables, functions and classes.
30
+ [![Deadcode Logo](https://raw.githubusercontent.com/albertas/deadcode/main/docs/_static/deadcode-logo-readme.png)](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
- Or with command line options:
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
- ### Command line options
80
+ ## Command line options
51
81
 
52
82
  | Option&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 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
- | DC001 | unused-variable | Variable `{name}` is never used
81
- | DC002 | unused-function | Function `{name}` is never used
82
- | DC003 | unused-class | Class `{name}` is never used
83
- | DC004 | unused-method | Method `{name}` is never used
84
- | DC005 | unused-attribute | Attribute `{name}` is never used
85
- | DC006 | unused-name | Name `{name}` is never used
86
- | DC007 | unused-import | Import `{name}` is never used
87
- | DC008 | unused-property | Property `{name}` is never used
88
- | DC009* | unreachable-code | Unreachable `else` block
89
- | DC011 | empty-file | Empty file
90
- | DC012* | commented-out-code | Commented out code
91
- | DC013* | ignore-expression | *This error code can ony be used in `# noqa: DC013` comments (no errors will be reported for expression which begins in current line)*
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: DC003` comment is used:
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: DC003
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 DC004 check.
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 DC001, DC002, DC003 for variables, functions, class.
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
- - [ ] `--fix --dry [filenames]` - only show whats about to change in the listed filenames.
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
- # deadcode
2
- `deadcode` allows to detect and fix unused Python code. It implements `DCXXX`
3
- static code linting rules for detecting unused code such as
4
- variables, functions and classes.
1
+ [![Deadcode Logo](https://raw.githubusercontent.com/albertas/deadcode/main/docs/_static/deadcode-logo-readme.png)](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
- Or with command line options:
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
- ### Command line options
51
+ ## Command line options
30
52
 
31
53
  | Option&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 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
- | DC001 | unused-variable | Variable `{name}` is never used
60
- | DC002 | unused-function | Function `{name}` is never used
61
- | DC003 | unused-class | Class `{name}` is never used
62
- | DC004 | unused-method | Method `{name}` is never used
63
- | DC005 | unused-attribute | Attribute `{name}` is never used
64
- | DC006 | unused-name | Name `{name}` is never used
65
- | DC007 | unused-import | Import `{name}` is never used
66
- | DC008 | unused-property | Property `{name}` is never used
67
- | DC009* | unreachable-code | Unreachable `else` block
68
- | DC011 | empty-file | Empty file
69
- | DC012* | commented-out-code | Commented out code
70
- | DC013* | ignore-expression | *This error code can ony be used in `# noqa: DC013` comments (no errors will be reported for expression which begins in current line)*
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: DC003` comment is used:
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: DC003
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 DC004 check.
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 DC001, DC002, DC003 for variables, functions, class.
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
- - [ ] `--fix --dry [filenames]` - only show whats about to change in the listed filenames.
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"Ignoring: {path}")
19
+ logger.info(f'Ignoring: {path}')
20
20
  continue
21
21
 
22
- if path.is_file() and path.suffix == ".py":
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"Error: {path} could not be found.")
29
+ logger.error(f'Error: {path} could not be found.')
30
30
 
31
31
  if args.verbose:
32
- sep = "\n - "
33
- logger.info(f"Files to be checked for dead code: {sep.join(filenames)}")
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"{len(unused_names)}"
17
+ return f'{len(unused_names)}'
18
18
 
19
19
  messages = []
20
20
  for item in unused_names:
21
- message = f"{item.filename_with_position} \033[91m{item.error_code}\033[0m "
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("\033[91m", "").replace("\033[1m", "").replace("\033[0m", "")
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("\x1b[1m", "").replace("\x1b[0m", "")
32
+ message = message.replace('\x1b[1m', '').replace('\x1b[0m', '')
33
33
  messages.append(message)
34
34
 
35
- return "\n".join(messages)
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
- if starts_later and ends_faster:
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
- if (line_end_b > line_start_s) or ((line_end_b == line_start_s) and (col_end_b > col_start_s)):
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"Error: Failed to parse {filename} file, ignoring it.")
12
+ print(f'Error: Failed to parse {filename} file, ignoring it.')
13
13
  return ast.Module()