lapsed 2.4.2__tar.gz → 2.6.0__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.
- {lapsed-2.4.2 → lapsed-2.6.0}/PKG-INFO +12 -2
- {lapsed-2.4.2 → lapsed-2.6.0}/README.md +11 -1
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/actions/parse_arguments.py +23 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/data_types.py +2 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/visitor/dead_code_visitor.py +18 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/visitor/ignore.py +6 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/pyproject.toml +1 -1
- {lapsed-2.4.2 → lapsed-2.6.0}/.gitignore +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/LICENSE +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/__init__.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/actions/__init__.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/actions/find_python_filenames.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/actions/find_unused_names.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/actions/fix_or_show_unused_code.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/actions/get_unused_names_error_message.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/actions/merge_overlaping_file_parts.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/actions/parse_abstract_syntax_tree.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/actions/parse_tach_config.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/actions/remove_file_parts_from_content.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/cli.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/constants.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/utils/__init__.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/utils/add_colors_to_diff.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/utils/base_test_case.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/utils/fix_indent.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/utils/flatten_lists.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/utils/nested_scopes.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/utils/print_ast.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/visitor/__init__.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/visitor/code_item.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/visitor/lines.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/visitor/noqa.py +0 -0
- {lapsed-2.4.2 → lapsed-2.6.0}/deadcode/visitor/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lapsed
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.0
|
|
4
4
|
Summary: Find and remove dead code.
|
|
5
5
|
Project-URL: homepage, https://github.com/albertas/deadcode
|
|
6
6
|
Project-URL: documentation, https://deadcode.readthedocs.io/
|
|
@@ -94,7 +94,7 @@ Create a `.pre-commit-config.yaml` file in the root of your project directory, i
|
|
|
94
94
|
# See https://pre-commit.com/hooks.html for more hooks
|
|
95
95
|
repos:
|
|
96
96
|
- repo: https://github.com/albertas/deadcode
|
|
97
|
-
rev: 2.
|
|
97
|
+
rev: 2.6.0
|
|
98
98
|
hooks:
|
|
99
99
|
- id: deadcode
|
|
100
100
|
```
|
|
@@ -117,6 +117,8 @@ repos:
|
|
|
117
117
|
|`--ignore-definitions` | list | Ignores definition (including name and body) if a name of an expression matches any of the provided ones. |
|
|
118
118
|
|`--ignore-definitions-if-inherits-from` | list | Ignores definition (including name and body) of a class if it inherits from any of the provided class names. |
|
|
119
119
|
|`--ignore-definitions-if-decorated-with` | list | Ignores definition (including name and body) of an expression, which is decorated with any of the provided decorator names. |
|
|
120
|
+
|`--ignore-non-self-attributes` | - | Does not report unused attributes assigned on objects other than `self`, e.g. `foo.bar = 1`. Such assignments may have side effects or be consumed by code outside of the analysed files, so they cannot safely be assumed dead. |
|
|
121
|
+
|`--ignore-class-attributes` | - | Does not report unused attributes assigned directly in a class body, e.g. `THING = "blah"` inside a `class Foo:` block. Such attributes may control behaviour of inherited/overridden methods, external frameworks, or metaclasses, so they cannot safely be assumed dead. |
|
|
120
122
|
|`--tach-config` | list | Paths to `tach.toml` files (see [Tach](https://docs.gauge.sh)). Names exposed via `[[interfaces]]` are treated as public API and never reported as unused; files in `unchecked = true` modules are skipped entirely. |
|
|
121
123
|
|`--no-color` | - | Removes colors from the output. |
|
|
122
124
|
|`--count` | - | Provides the count of the detected unused names instead of printing them all out. |
|
|
@@ -225,6 +227,14 @@ code base is implemented in.
|
|
|
225
227
|
- [ ] Investigate ways of extracting and backporting Python3.10+ `ast` implementation to lower Python versions.
|
|
226
228
|
|
|
227
229
|
## Release notes
|
|
230
|
+
- v2.6.0:
|
|
231
|
+
- Add `--ignore-class-attributes` option to not report unused attributes assigned directly
|
|
232
|
+
in a class body (e.g. `THING = "blah"` inside `class Foo:`), since such attributes may
|
|
233
|
+
control behaviour of inherited/overridden methods, external frameworks, or metaclasses.
|
|
234
|
+
- v2.5.0:
|
|
235
|
+
- Add `--ignore-non-self-attributes` option to not report unused attributes assigned on
|
|
236
|
+
objects other than `self` (e.g. `foo.bar = 1`), since such assignments may have side
|
|
237
|
+
effects or be consumed by code outside of the analysed files.
|
|
228
238
|
- v2.4.2:
|
|
229
239
|
- Add `--tach-config` option to read `tach.toml` file(s) and treat names exposed via `[[interfaces]]`
|
|
230
240
|
as public API, and skip `unchecked = true` modules entirely.
|
|
@@ -66,7 +66,7 @@ Create a `.pre-commit-config.yaml` file in the root of your project directory, i
|
|
|
66
66
|
# See https://pre-commit.com/hooks.html for more hooks
|
|
67
67
|
repos:
|
|
68
68
|
- repo: https://github.com/albertas/deadcode
|
|
69
|
-
rev: 2.
|
|
69
|
+
rev: 2.6.0
|
|
70
70
|
hooks:
|
|
71
71
|
- id: deadcode
|
|
72
72
|
```
|
|
@@ -89,6 +89,8 @@ repos:
|
|
|
89
89
|
|`--ignore-definitions` | list | Ignores definition (including name and body) if a name of an expression matches any of the provided ones. |
|
|
90
90
|
|`--ignore-definitions-if-inherits-from` | list | Ignores definition (including name and body) of a class if it inherits from any of the provided class names. |
|
|
91
91
|
|`--ignore-definitions-if-decorated-with` | list | Ignores definition (including name and body) of an expression, which is decorated with any of the provided decorator names. |
|
|
92
|
+
|`--ignore-non-self-attributes` | - | Does not report unused attributes assigned on objects other than `self`, e.g. `foo.bar = 1`. Such assignments may have side effects or be consumed by code outside of the analysed files, so they cannot safely be assumed dead. |
|
|
93
|
+
|`--ignore-class-attributes` | - | Does not report unused attributes assigned directly in a class body, e.g. `THING = "blah"` inside a `class Foo:` block. Such attributes may control behaviour of inherited/overridden methods, external frameworks, or metaclasses, so they cannot safely be assumed dead. |
|
|
92
94
|
|`--tach-config` | list | Paths to `tach.toml` files (see [Tach](https://docs.gauge.sh)). Names exposed via `[[interfaces]]` are treated as public API and never reported as unused; files in `unchecked = true` modules are skipped entirely. |
|
|
93
95
|
|`--no-color` | - | Removes colors from the output. |
|
|
94
96
|
|`--count` | - | Provides the count of the detected unused names instead of printing them all out. |
|
|
@@ -197,6 +199,14 @@ code base is implemented in.
|
|
|
197
199
|
- [ ] Investigate ways of extracting and backporting Python3.10+ `ast` implementation to lower Python versions.
|
|
198
200
|
|
|
199
201
|
## Release notes
|
|
202
|
+
- v2.6.0:
|
|
203
|
+
- Add `--ignore-class-attributes` option to not report unused attributes assigned directly
|
|
204
|
+
in a class body (e.g. `THING = "blah"` inside `class Foo:`), since such attributes may
|
|
205
|
+
control behaviour of inherited/overridden methods, external frameworks, or metaclasses.
|
|
206
|
+
- v2.5.0:
|
|
207
|
+
- Add `--ignore-non-self-attributes` option to not report unused attributes assigned on
|
|
208
|
+
objects other than `self` (e.g. `foo.bar = 1`), since such assignments may have side
|
|
209
|
+
effects or be consumed by code outside of the analysed files.
|
|
200
210
|
- v2.4.2:
|
|
201
211
|
- Add `--tach-config` option to read `tach.toml` file(s) and treat names exposed via `[[interfaces]]`
|
|
202
212
|
as public API, and skip `unchecked = true` modules entirely.
|
|
@@ -167,6 +167,29 @@ def parse_arguments(args: Optional[List[str]]) -> Args:
|
|
|
167
167
|
type=str,
|
|
168
168
|
)
|
|
169
169
|
|
|
170
|
+
parser.add_argument(
|
|
171
|
+
'--ignore-non-self-attributes',
|
|
172
|
+
help=(
|
|
173
|
+
'Does not report unused attributes assigned on objects other than `self`, e.g. `foo.bar = 1` '
|
|
174
|
+
'where `foo` is not the first argument of the enclosing method. Such assignments may have '
|
|
175
|
+
'side effects or be consumed by code outside of the analysed files, so they cannot safely '
|
|
176
|
+
'be assumed dead.'
|
|
177
|
+
),
|
|
178
|
+
action='store_true',
|
|
179
|
+
default=False,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
parser.add_argument(
|
|
183
|
+
'--ignore-class-attributes',
|
|
184
|
+
help=(
|
|
185
|
+
'Does not report unused attributes assigned directly in a class body, e.g. `THING = "blah"` '
|
|
186
|
+
'inside a `class Foo:` block. Such attributes may control behaviour of inherited or overridden '
|
|
187
|
+
'methods, external frameworks, or metaclasses, so they cannot safely be assumed dead.'
|
|
188
|
+
),
|
|
189
|
+
action='store_true',
|
|
190
|
+
default=False,
|
|
191
|
+
)
|
|
192
|
+
|
|
170
193
|
parser.add_argument(
|
|
171
194
|
'--tach-config',
|
|
172
195
|
help=(
|
|
@@ -31,6 +31,8 @@ class Args:
|
|
|
31
31
|
ignore_names_if_decorated_with: Iterable[Pathname] = ()
|
|
32
32
|
ignore_names_if_inherits_from: Iterable[Pathname] = ()
|
|
33
33
|
ignore_names_in_files: Iterable[Pathname] = ()
|
|
34
|
+
ignore_non_self_attributes: bool = False
|
|
35
|
+
ignore_class_attributes: bool = False
|
|
34
36
|
tach_config: Iterable[Pathname] = ()
|
|
35
37
|
no_color: bool = False
|
|
36
38
|
quiet: bool = False
|
|
@@ -35,6 +35,7 @@ from deadcode.visitor.ignore import (
|
|
|
35
35
|
_ignore_method,
|
|
36
36
|
_ignore_pytest_fixture,
|
|
37
37
|
_ignore_variable,
|
|
38
|
+
_is_self_attribute,
|
|
38
39
|
)
|
|
39
40
|
|
|
40
41
|
logger = getLogger()
|
|
@@ -72,6 +73,11 @@ class DeadCodeVisitor(ast.NodeVisitor):
|
|
|
72
73
|
# Note: scope is a stack containing current module name, class names, function names
|
|
73
74
|
self.scope_parts: List[str] = []
|
|
74
75
|
|
|
76
|
+
# Parallel stack to scope_parts (module name excluded), tracking whether each
|
|
77
|
+
# pushed scope is a 'class' or a 'function', so that class-level attributes
|
|
78
|
+
# (assigned directly in a class body) can be told apart from local variables.
|
|
79
|
+
self.scope_kinds: List[str] = []
|
|
80
|
+
|
|
75
81
|
# This flag is used to stop registering code definitions in a code item
|
|
76
82
|
# during recursive its parsing
|
|
77
83
|
self.should_ignore_new_definitions = False
|
|
@@ -100,6 +106,7 @@ class DeadCodeVisitor(ast.NodeVisitor):
|
|
|
100
106
|
filename = os.path.basename(file_path)
|
|
101
107
|
module_name = os.path.splitext(filename)[0]
|
|
102
108
|
self.scope_parts = [module_name]
|
|
109
|
+
self.scope_kinds = []
|
|
103
110
|
|
|
104
111
|
file_content = f.read()
|
|
105
112
|
if file_content.strip() or (filename.startswith('__') and filename.endswith('__.py')):
|
|
@@ -284,7 +291,13 @@ class DeadCodeVisitor(ast.NodeVisitor):
|
|
|
284
291
|
else:
|
|
285
292
|
collection.append(code_item)
|
|
286
293
|
|
|
294
|
+
def _is_directly_in_class_body(self) -> bool:
|
|
295
|
+
return bool(self.scope_kinds) and self.scope_kinds[-1] == 'class'
|
|
296
|
+
|
|
287
297
|
def _define_variable(self, name: str, node: ast.AST) -> None:
|
|
298
|
+
if self.args.ignore_class_attributes and self._is_directly_in_class_body():
|
|
299
|
+
return
|
|
300
|
+
|
|
288
301
|
self._define(
|
|
289
302
|
self.defined_vars,
|
|
290
303
|
name,
|
|
@@ -301,6 +314,8 @@ class DeadCodeVisitor(ast.NodeVisitor):
|
|
|
301
314
|
|
|
302
315
|
def visit_Attribute(self, node: ast.Attribute) -> None:
|
|
303
316
|
if isinstance(node.ctx, ast.Store):
|
|
317
|
+
if self.args.ignore_non_self_attributes and not _is_self_attribute(node):
|
|
318
|
+
return
|
|
304
319
|
self._define(self.defined_attrs, node.attr, node)
|
|
305
320
|
elif isinstance(node.ctx, ast.Load):
|
|
306
321
|
self.add_used_name(node.attr)
|
|
@@ -502,8 +517,10 @@ class DeadCodeVisitor(ast.NodeVisitor):
|
|
|
502
517
|
was_scope_increased = True
|
|
503
518
|
if isinstance(node, ast.ClassDef):
|
|
504
519
|
self.scope_parts.append(node.name)
|
|
520
|
+
self.scope_kinds.append('class')
|
|
505
521
|
elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
506
522
|
self.scope_parts.append(node.name)
|
|
523
|
+
self.scope_kinds.append('function')
|
|
507
524
|
else:
|
|
508
525
|
was_scope_increased = False
|
|
509
526
|
|
|
@@ -541,6 +558,7 @@ class DeadCodeVisitor(ast.NodeVisitor):
|
|
|
541
558
|
# TODO: use decorator for this code chunk
|
|
542
559
|
if was_scope_increased:
|
|
543
560
|
self.scope_parts.pop()
|
|
561
|
+
self.scope_kinds.pop()
|
|
544
562
|
|
|
545
563
|
def _handle_ast_list(self, ast_list: List[ast.AST]) -> None:
|
|
546
564
|
"""
|
|
@@ -123,6 +123,12 @@ def _ignore_method(filename: Path, method_name: str) -> bool:
|
|
|
123
123
|
)
|
|
124
124
|
|
|
125
125
|
|
|
126
|
+
def _is_self_attribute(node: ast.Attribute) -> bool:
|
|
127
|
+
"""Whether an attribute assignment target is `self.attr` (as opposed to
|
|
128
|
+
e.g. `foo.attr`, where `foo` is some other object)."""
|
|
129
|
+
return isinstance(node.value, ast.Name) and node.value.id == 'self'
|
|
130
|
+
|
|
131
|
+
|
|
126
132
|
def _ignore_variable(filename: Path, varname: str) -> bool:
|
|
127
133
|
"""
|
|
128
134
|
Ignore _ (Python idiom), _x (pylint convention) and
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|