deadcode 0.1.0__tar.gz → 1.1.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 (30) hide show
  1. {deadcode-0.1.0 → deadcode-1.1.1}/PKG-INFO +25 -14
  2. deadcode-1.1.1/README.md +64 -0
  3. {deadcode-0.1.0 → deadcode-1.1.1}/deadcode/actions/find_unused_names.py +14 -2
  4. {deadcode-0.1.0 → deadcode-1.1.1}/deadcode/actions/get_unused_names_error_message.py +6 -0
  5. {deadcode-0.1.0 → deadcode-1.1.1}/deadcode/actions/parse_abstract_syntax_trees.py +4 -3
  6. {deadcode-0.1.0 → deadcode-1.1.1}/deadcode/actions/parse_arguments.py +12 -0
  7. {deadcode-0.1.0 → deadcode-1.1.1}/deadcode/cli.py +3 -2
  8. {deadcode-0.1.0 → deadcode-1.1.1}/deadcode/data_types.py +2 -0
  9. deadcode-1.1.1/deadcode/tests/base.py +9 -0
  10. deadcode-1.1.1/deadcode/tests/test_command_line_argument_parsing.py +62 -0
  11. deadcode-1.1.1/deadcode/tests/test_count_output.py +49 -0
  12. {deadcode-0.1.0 → deadcode-1.1.1}/deadcode/tests/test_deadcode.py +4 -71
  13. deadcode-1.1.1/deadcode/tests/test_ignore_names_by_pattern.py +52 -0
  14. {deadcode-0.1.0 → deadcode-1.1.1}/pyproject.toml +2 -2
  15. deadcode-0.1.0/README.md +0 -53
  16. {deadcode-0.1.0 → deadcode-1.1.1}/.gitignore +0 -0
  17. {deadcode-0.1.0 → deadcode-1.1.1}/LICENSE +0 -0
  18. {deadcode-0.1.0 → deadcode-1.1.1}/deadcode/__init__.py +0 -0
  19. {deadcode-0.1.0 → deadcode-1.1.1}/deadcode/actions/__init__.py +0 -0
  20. {deadcode-0.1.0 → deadcode-1.1.1}/deadcode/actions/find_python_filenames.py +0 -0
  21. {deadcode-0.1.0 → deadcode-1.1.1}/deadcode/actions/parse_global_names.py +0 -0
  22. {deadcode-0.1.0 → deadcode-1.1.1}/deadcode/actions/read_files.py +0 -0
  23. {deadcode-0.1.0 → deadcode-1.1.1}/deadcode/tests/files/classes.py +0 -0
  24. {deadcode-0.1.0 → deadcode-1.1.1}/deadcode/tests/files/functions.py +0 -0
  25. {deadcode-0.1.0 → deadcode-1.1.1}/deadcode/tests/files/subdir/another_subdir/__init__.py +0 -0
  26. {deadcode-0.1.0 → deadcode-1.1.1}/deadcode/tests/files/subdir/one_more_file.py +0 -0
  27. {deadcode-0.1.0 → deadcode-1.1.1}/deadcode/tests/files/variables.py +0 -0
  28. {deadcode-0.1.0 → deadcode-1.1.1}/deadcode/utils/__init__.py +0 -0
  29. {deadcode-0.1.0 → deadcode-1.1.1}/deadcode/utils/flatten_lists.py +0 -0
  30. {deadcode-0.1.0 → deadcode-1.1.1}/deadcode/utils/path_matching.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: deadcode
3
- Version: 0.1.0
3
+ Version: 1.1.1
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/
@@ -22,7 +22,7 @@ Description-Content-Type: text/markdown
22
22
  `deadcode` package implements `DC100` - `unused-global-name` check for detecting
23
23
  variable/function/class names which are never used in a whole code base.
24
24
  Only globally defined names are being checked.
25
- Unused local names can be detected by other tools like `ruff`.
25
+ Unused local names can be detected by other tools like [ruff](https://pypi.org/project/ruff/).
26
26
 
27
27
  ## Installation
28
28
  ```shell
@@ -36,7 +36,7 @@ deadcode .
36
36
 
37
37
  Or with command line options:
38
38
  ```
39
- deadcode . --exclude=venv,tests --ignore-names=BaseTestCase --ignore-names-in-files=migrations
39
+ deadcode . --exclude=venv,tests --ignore-names=BaseTestCase,.*Mixin --ignore-names-in-files=migrations
40
40
  ```
41
41
 
42
42
  The same options can be provided in `pyproject.toml` settings file:
@@ -48,26 +48,37 @@ ignore-names-in-files = ["migrations"]
48
48
  ```
49
49
 
50
50
  Command line options:
51
- |       Option      | Type | Meaning |
52
- |---------------------------|-------|----------|
53
- |`--exclude` | list | Path expressions to completely skips files from being analysed. |
54
- |`--ignore-names` | list | Removes provided list of names from the error output. |
55
- |`--ignore-names-in-files` | list | Unused names from files matching provided path expressions. |
56
- |`--no-color` | None | Removes colors from the output. |
51
+
52
+ | Option | Type | Meaning |
53
+ |-----------------------------------|------|----------|
54
+ |<pre>--exclude</pre> | list | Path expressions to completely skip files from being analysed. |
55
+ |<pre>--ignore-names</pre> | list | Removes provided list of names from the error output. Regexp expressions to match multiple names can also be provided, e.g. `.*Mixin` will match all classes ending with `Mixin`. |
56
+ |<pre>--ignore-names-in-files</pre> | list | Unused names from files matching provided path expressions. |
57
+ |<pre>--no-color</pre> | None | Removes colors from the output. |
58
+ |<pre>--count</pre> | None | Provides the count of the detected unused names instead of printing them all out. |
59
+ |<pre>--quiet</pre> | None | Does not output anything. Makefile still fails with exit code 1 if unused names are found. |
60
+
57
61
 
58
62
  ## Contributing
59
63
  - `make check` - runs unit tests and other checks using virtual environment.
60
64
 
61
65
  ## Rationale
62
- `ruff` and `flake8` - don't have rules for unused global code detection, only for local ones `F823`, `F841`, `F842`.
63
- `deadcode` package tries to add a new `DC100` check for detecting variables/functions/classes which are not used in a whole code base.
66
+ [ruff](https://pypi.org/project/ruff/) and
67
+ [flake8](https://pypi.org/project/flake8/) - don't have rules for unused global
68
+ code detection, only for local ones `F823`, `F841`, `F842`. `deadcode` package
69
+ tries to add a new `DC100` check for detecting variables/functions/classes
70
+ which are not used in a whole code base.
64
71
 
65
- There is an alternative `vulture` package, which provides many false positives. `deadcode` - tries to find less, but findings are with higher confidence.
72
+ There is an alternative [vulture](https://pypi.org/project/vulture/) package,
73
+ which provides many false positives.
74
+ `deadcode` - tries to find less, but findings are with higher confidence.
66
75
  `deadcode` - is supposed to be used inline with other static code checkers like `ruff`.
67
76
 
68
77
  ## Known limitations
69
78
  If the same unused name is repeated in several files - it wont be detected.
70
79
 
71
- Files with syntax errors will be ignored, because `deadcode` uses `ast` to build abstract syntax tree for name usage detection.
80
+ Files with syntax errors will be ignored, because `deadcode` uses `ast` to
81
+ build abstract syntax tree for name usage detection.
72
82
 
73
- It is assumed that `deadcode` will be run using the same Python version as the checked code base is implemented in.
83
+ It is assumed that `deadcode` will be run using the same Python version as the
84
+ checked code base is implemented in.
@@ -0,0 +1,64 @@
1
+ # deadcode
2
+ `deadcode` package implements `DC100` - `unused-global-name` check for detecting
3
+ variable/function/class names which are never used in a whole code base.
4
+ Only globally defined names are being checked.
5
+ Unused local names can be detected by other tools like [ruff](https://pypi.org/project/ruff/).
6
+
7
+ ## Installation
8
+ ```shell
9
+ pip install deadcode
10
+ ```
11
+
12
+ ## Usage
13
+ ```shell
14
+ deadcode .
15
+ ```
16
+
17
+ Or with command line options:
18
+ ```
19
+ deadcode . --exclude=venv,tests --ignore-names=BaseTestCase,.*Mixin --ignore-names-in-files=migrations
20
+ ```
21
+
22
+ The same options can be provided in `pyproject.toml` settings file:
23
+ ```
24
+ [tool.deadcode]
25
+ exclude = ["venv", "tests"]
26
+ ignore-names = ["BaseTestCase"]
27
+ ignore-names-in-files = ["migrations"]
28
+ ```
29
+
30
+ Command line options:
31
+
32
+ | Option | Type | Meaning |
33
+ |-----------------------------------|------|----------|
34
+ |<pre>--exclude</pre> | list | Path expressions to completely skip files from being analysed. |
35
+ |<pre>--ignore-names</pre> | list | Removes provided list of names from the error output. Regexp expressions to match multiple names can also be provided, e.g. `.*Mixin` will match all classes ending with `Mixin`. |
36
+ |<pre>--ignore-names-in-files</pre> | list | Unused names from files matching provided path expressions. |
37
+ |<pre>--no-color</pre> | None | Removes colors from the output. |
38
+ |<pre>--count</pre> | None | Provides the count of the detected unused names instead of printing them all out. |
39
+ |<pre>--quiet</pre> | None | Does not output anything. Makefile still fails with exit code 1 if unused names are found. |
40
+
41
+
42
+ ## Contributing
43
+ - `make check` - runs unit tests and other checks using virtual environment.
44
+
45
+ ## Rationale
46
+ [ruff](https://pypi.org/project/ruff/) and
47
+ [flake8](https://pypi.org/project/flake8/) - don't have rules for unused global
48
+ code detection, only for local ones `F823`, `F841`, `F842`. `deadcode` package
49
+ tries to add a new `DC100` check for detecting variables/functions/classes
50
+ which are not used in a whole code base.
51
+
52
+ There is an alternative [vulture](https://pypi.org/project/vulture/) package,
53
+ which provides many false positives.
54
+ `deadcode` - tries to find less, but findings are with higher confidence.
55
+ `deadcode` - is supposed to be used inline with other static code checkers like `ruff`.
56
+
57
+ ## Known limitations
58
+ If the same unused name is repeated in several files - it wont be detected.
59
+
60
+ Files with syntax errors will be ignored, because `deadcode` uses `ast` to
61
+ build abstract syntax tree for name usage detection.
62
+
63
+ It is assumed that `deadcode` will be run using the same Python version as the
64
+ checked code base is implemented in.
@@ -11,16 +11,28 @@ def find_unused_names(
11
11
  args: Args,
12
12
  ) -> Dict[VariableName, Filename]:
13
13
  name_occourencies: Dict[str, int] = defaultdict(int)
14
+
14
15
  for name in global_names.keys():
15
16
  pattern = re.compile(
16
- rf"FunctionDef\(name=\'{name}\'|ClassDef\(name=\'{name}\'|Name\(id=\'{name}\'|alias\(name=\'{name}\'"
17
+ "|".join(
18
+ [
19
+ rf"FunctionDef\(name=\'{name}\'",
20
+ rf"ClassDef\(name=\'{name}\'",
21
+ rf"Name\(id=\'{name}\'",
22
+ rf"alias\(name=\'{name}\'",
23
+ ]
24
+ )
17
25
  )
18
26
  for abstract_syntax_tree in files.values():
19
27
  name_occourencies[name] += len(re.findall(pattern, abstract_syntax_tree))
20
28
 
29
+ # Filter out used and ignored names
30
+ ignore_names_pattern = "|".join(args.ignore_names) or "^$"
31
+ ignore_names_re = re.compile(ignore_names_pattern)
32
+
21
33
  unused_variables = {
22
34
  name: filename
23
35
  for name, filename in global_names.items()
24
- if name_occourencies[name] == 1 and name not in args.ignore_names
36
+ if name_occourencies[name] == 1 and not re.findall(ignore_names_re, name)
25
37
  }
26
38
  return unused_variables
@@ -8,6 +8,12 @@ def get_unused_names_error_message(unused_names: Dict[VariableName, Filename], a
8
8
  if not unused_names:
9
9
  return None
10
10
 
11
+ if args.quiet:
12
+ return ""
13
+
14
+ if args.count:
15
+ return f"{len(unused_names)}"
16
+
11
17
  if args.no_color:
12
18
  return "\n".join([f"{filename} DC100 Global {name} is never used" for name, filename in unused_names.items()])
13
19
 
@@ -1,16 +1,17 @@
1
1
  import ast
2
2
  from typing import Dict
3
3
 
4
- from deadcode.data_types import FileContent, Filename, AbstractSyntaxTree
4
+ from deadcode.data_types import Args, FileContent, Filename, AbstractSyntaxTree
5
5
 
6
6
 
7
- def parse_abstract_syntax_trees(files: Dict[Filename, FileContent]) -> Dict[Filename, AbstractSyntaxTree]:
7
+ def parse_abstract_syntax_trees(files: Dict[Filename, FileContent], args: Args) -> Dict[Filename, AbstractSyntaxTree]:
8
8
  """Did my code coverage just increased? Answer is no: a doc string was ignored :tada:"""
9
9
  abstract_syntax_trees_for_files = {}
10
10
  for filename, file_content in files.items():
11
11
  try:
12
12
  abstract_syntax_trees_for_files[filename] = ast.dump(ast.parse(file_content)) # Note: indent=1 for debug
13
13
  except: # noqa: E722
14
- print(f"Error: Failed to parse {filename} file, ignoring it.")
14
+ if not args.count and not args.quiet:
15
+ print(f"Error: Failed to parse {filename} file, ignoring it.")
15
16
  abstract_syntax_trees_for_files[filename] = ""
16
17
  return abstract_syntax_trees_for_files
@@ -57,6 +57,18 @@ def parse_arguments(args: Optional[List[str]]) -> Args:
57
57
  action="store_true",
58
58
  default=False,
59
59
  )
60
+ parser.add_argument(
61
+ "--quiet",
62
+ help="Does not output anything. Makefile still fails with exit code 1 if unused names are found.",
63
+ action="store_true",
64
+ default=False,
65
+ )
66
+ parser.add_argument(
67
+ "--count",
68
+ help="Provides the count of the detected unused names instead of printing them all out.",
69
+ action="store_true",
70
+ default=False,
71
+ )
60
72
 
61
73
  parsed_args = parser.parse_args(args).__dict__
62
74
 
@@ -21,13 +21,14 @@ def main(
21
21
  files = read_files(python_filenames)
22
22
 
23
23
  global_names = parse_global_names(files, args=args)
24
- abstract_syntax_trees_of_files = parse_abstract_syntax_trees(files)
24
+ abstract_syntax_trees_of_files = parse_abstract_syntax_trees(files, args=args)
25
25
  unused_names = find_unused_names(abstract_syntax_trees_of_files, global_names, args=args)
26
26
 
27
27
  if error_message := get_unused_names_error_message(unused_names, args=args):
28
28
  return error_message
29
29
 
30
- print("\033[1mWell done!\033[0m ✨ 🚀 ✨")
30
+ if not args.count and not args.quiet:
31
+ print("\033[1mWell done!\033[0m ✨ 🚀 ✨")
31
32
  return None
32
33
 
33
34
 
@@ -17,3 +17,5 @@ class Args:
17
17
  ignore_names: List[Pathname]
18
18
  ignore_names_in_files: List[Pathname]
19
19
  no_color: bool
20
+ quiet: bool
21
+ count: bool
@@ -0,0 +1,9 @@
1
+ from unittest import TestCase
2
+ from unittest.mock import MagicMock, patch
3
+
4
+
5
+ class BaseTestCase(TestCase):
6
+ def patch(self, path: str) -> MagicMock:
7
+ patcher = patch(path)
8
+ self.addCleanup(patcher.stop)
9
+ return patcher.start()
@@ -0,0 +1,62 @@
1
+ from deadcode.actions.parse_arguments import parse_arguments
2
+ from deadcode.tests.base import BaseTestCase
3
+
4
+
5
+ class CommandLineArgParsingTests(BaseTestCase):
6
+ def setUp(self):
7
+ self.tomlib_load_mock = self.patch("deadcode.actions.parse_arguments.tomllib.load")
8
+ self.tomlib_load_mock.return_value = {
9
+ "tool": {
10
+ "deadcode": {
11
+ "exclude": [],
12
+ "ignore_names": [],
13
+ "ignore_names_in_files": [],
14
+ }
15
+ }
16
+ }
17
+
18
+ def test_calling_with_one_paths_argument(self):
19
+ args = parse_arguments(["."])
20
+ self.assertEqual(args.paths, ["."])
21
+ self.assertEqual(args.exclude, [])
22
+ self.assertEqual(args.ignore_names, [])
23
+ self.assertEqual(args.ignore_names_in_files, [])
24
+
25
+ def test_calling_with_several_paths_argument(self):
26
+ args = parse_arguments([".", "tests"])
27
+ self.assertEqual(args.paths, [".", "tests"])
28
+ self.assertEqual(args.exclude, [])
29
+ self.assertEqual(args.ignore_names, [])
30
+ self.assertEqual(args.ignore_names_in_files, [])
31
+
32
+ def test_calling_with_exclude(self):
33
+ args = parse_arguments([".", "--exclude=tests,venv"])
34
+ self.assertEqual(args.paths, ["."])
35
+ self.assertEqual(args.exclude, ["tests", "venv"])
36
+ self.assertEqual(args.ignore_names, [])
37
+ self.assertEqual(args.ignore_names_in_files, [])
38
+
39
+ def test_calling_with_several_exclude_options(self):
40
+ args = parse_arguments([".", "--exclude=tests,venv", "--exclude=migrations"])
41
+ self.assertEqual(args.paths, ["."])
42
+ self.assertEqual(args.exclude, ["tests", "venv", "migrations"])
43
+ self.assertEqual(args.ignore_names, [])
44
+ self.assertEqual(args.ignore_names_in_files, [])
45
+
46
+ def test_calling_with_no_color_option(self):
47
+ args = parse_arguments([".", "--no-color"])
48
+ self.assertEqual(args.paths, ["."])
49
+ self.assertEqual(args.no_color, True)
50
+
51
+ def test_ignore_names_and_ignore_files_command_line_argument_parsing(self):
52
+ args = parse_arguments(
53
+ [
54
+ ".",
55
+ "--ignore-names-in-files=tests,venv",
56
+ "--ignore-names=BaseTestCase,lambda_handler",
57
+ ]
58
+ )
59
+ self.assertEqual(args.paths, ["."])
60
+ self.assertEqual(args.exclude, [])
61
+ self.assertEqual(args.ignore_names, ["BaseTestCase", "lambda_handler"])
62
+ self.assertEqual(args.ignore_names_in_files, ["tests", "venv"])
@@ -0,0 +1,49 @@
1
+ from deadcode.cli import main
2
+ from deadcode.tests.base import BaseTestCase
3
+
4
+
5
+ class IgnoreNamesByPatternTests(BaseTestCase):
6
+ def test_count_several_classes(self):
7
+ # Having
8
+ self.read_files_mock = self.patch("deadcode.cli.read_files")
9
+ self.read_files_mock.return_value = {
10
+ "ignore_names_by_pattern.py": """
11
+ class MyModel:
12
+ pass
13
+
14
+ class MyUserModel:
15
+ pass
16
+
17
+ class Unused:
18
+ pass
19
+
20
+ class ThisClassShouldBeIgnored:
21
+ pass
22
+ """
23
+ }
24
+
25
+ # When
26
+ unused_name_count = main(["ignore_names_by_pattern.py", "--no-color", "--count"])
27
+
28
+ # Then
29
+ self.assertEqual(unused_name_count, "4")
30
+
31
+ def test_count_variables_function_and_class(self):
32
+ # Having
33
+ self.read_files_mock = self.patch("deadcode.cli.read_files")
34
+ self.read_files_mock.return_value = {
35
+ "ignore_names_by_pattern.py": """
36
+ first_variable = 0
37
+ def this_is_a_function():
38
+ pass
39
+ class MyModel:
40
+ def __init__(self):
41
+ pass
42
+ """
43
+ }
44
+
45
+ # When
46
+ unused_name_count = main(["ignore_names_by_pattern.py", "--no-color", "--count"])
47
+
48
+ # Then
49
+ self.assertEqual(unused_name_count, "3")
@@ -1,18 +1,8 @@
1
- from unittest import TestCase
2
- from unittest.mock import MagicMock, patch
3
-
4
1
  from deadcode.cli import main
5
- from deadcode.actions.parse_arguments import parse_arguments
6
-
7
-
8
- class BaseTestCase(TestCase):
9
- def patch(self, path: str) -> MagicMock:
10
- patcher = patch(path)
11
- self.addCleanup(patcher.stop)
12
- return patcher.start()
2
+ from deadcode.tests.base import BaseTestCase
13
3
 
14
4
 
15
- class FindDeadCodeTests(BaseTestCase):
5
+ class DeadCodeIntegrationTests(BaseTestCase):
16
6
  def test_unused_variable_name_found(self):
17
7
  unused_names = main(["tests/files/variables.py", "--no-color"])
18
8
 
@@ -137,62 +127,5 @@ print(THIS_ONE_IS_USED)"""
137
127
  ),
138
128
  )
139
129
 
140
-
141
- class CommandLineArgParsingTests(BaseTestCase):
142
- def setUp(self):
143
- self.tomlib_load_mock = self.patch("deadcode.actions.parse_arguments.tomllib.load")
144
- self.tomlib_load_mock.return_value = {
145
- "tool": {
146
- "deadcode": {
147
- "exclude": [],
148
- "ignore_names": [],
149
- "ignore_names_in_files": [],
150
- }
151
- }
152
- }
153
-
154
- def test_calling_with_one_paths_argument(self):
155
- args = parse_arguments(["."])
156
- self.assertEqual(args.paths, ["."])
157
- self.assertEqual(args.exclude, [])
158
- self.assertEqual(args.ignore_names, [])
159
- self.assertEqual(args.ignore_names_in_files, [])
160
-
161
- def test_calling_with_several_paths_argument(self):
162
- args = parse_arguments([".", "tests"])
163
- self.assertEqual(args.paths, [".", "tests"])
164
- self.assertEqual(args.exclude, [])
165
- self.assertEqual(args.ignore_names, [])
166
- self.assertEqual(args.ignore_names_in_files, [])
167
-
168
- def test_calling_with_exclude(self):
169
- args = parse_arguments([".", "--exclude=tests,venv"])
170
- self.assertEqual(args.paths, ["."])
171
- self.assertEqual(args.exclude, ["tests", "venv"])
172
- self.assertEqual(args.ignore_names, [])
173
- self.assertEqual(args.ignore_names_in_files, [])
174
-
175
- def test_calling_with_several_exclude_options(self):
176
- args = parse_arguments([".", "--exclude=tests,venv", "--exclude=migrations"])
177
- self.assertEqual(args.paths, ["."])
178
- self.assertEqual(args.exclude, ["tests", "venv", "migrations"])
179
- self.assertEqual(args.ignore_names, [])
180
- self.assertEqual(args.ignore_names_in_files, [])
181
-
182
- def test_calling_with_no_color_option(self):
183
- args = parse_arguments([".", "--no-color"])
184
- self.assertEqual(args.paths, ["."])
185
- self.assertEqual(args.no_color, True)
186
-
187
- def test_ignore_names_and_ignore_files_command_line_argument_parsing(self):
188
- args = parse_arguments(
189
- [
190
- ".",
191
- "--ignore-names-in-files=tests,venv",
192
- "--ignore-names=BaseTestCase,lambda_handler",
193
- ]
194
- )
195
- self.assertEqual(args.paths, ["."])
196
- self.assertEqual(args.exclude, [])
197
- self.assertEqual(args.ignore_names, ["BaseTestCase", "lambda_handler"])
198
- self.assertEqual(args.ignore_names_in_files, ["tests", "venv"])
130
+ def test_variable_is_marked_as_used_even_if_its_imported_as_a_different_name(self):
131
+ pass
@@ -0,0 +1,52 @@
1
+ from deadcode.cli import main
2
+ from deadcode.tests.base import BaseTestCase
3
+
4
+
5
+ class IgnoreNamesByPatternTests(BaseTestCase):
6
+ def test_ignore_names_matched_by_word_and_group_regexp_patterns(self):
7
+ self.read_files_mock = self.patch("deadcode.cli.read_files")
8
+ self.read_files_mock.return_value = {
9
+ "ignore_names_by_pattern.py": """
10
+ class MyModel:
11
+ pass
12
+
13
+ class MyUserModel:
14
+ pass
15
+
16
+ class Unused:
17
+ pass
18
+
19
+ class ThisClassShouldBeIgnored:
20
+ pass
21
+ """
22
+ }
23
+ unused_names = main(["ignore_names_by_pattern.py", "--no-color", "--ignore-names=\\w*Model,.*[Ii]{1}gnore.*"])
24
+
25
+ self.assertEqual(
26
+ unused_names,
27
+ ("ignore_names_by_pattern.py:8:6: DC100 Global Unused is never used"),
28
+ )
29
+
30
+ def test_ignore_names_matched_by_dot_regexp_pattern(self):
31
+ self.read_files_mock = self.patch("deadcode.cli.read_files")
32
+ self.read_files_mock.return_value = {
33
+ "ignore_names_by_pattern.py": """
34
+ class MyModel:
35
+ pass
36
+
37
+ class MyUserModel:
38
+ pass
39
+
40
+ class Unused:
41
+ pass
42
+
43
+ class ThisClassShouldBeIgnored:
44
+ pass
45
+ """
46
+ }
47
+ unused_names = main(["ignore_names_by_pattern.py", "--no-color", "--ignore-names=.*Model,.*Ignore.*"])
48
+
49
+ self.assertEqual(
50
+ unused_names,
51
+ ("ignore_names_by_pattern.py:8:6: DC100 Global Unused is never used"),
52
+ )
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "deadcode"
3
- version = "0.1.0"
3
+ version = "1.1.1"
4
4
  authors = [
5
5
  {name = "Albertas Gimbutas", email = "albertasgim@gmail.com"},
6
6
  ]
@@ -69,4 +69,4 @@ target_version = ["py38"]
69
69
 
70
70
 
71
71
  [tool.pytest.ini_options]
72
- addopts = "--cov=. --cov-fail-under=90.0"
72
+ addopts = "--cov=. --cov-fail-under=92.0"
deadcode-0.1.0/README.md DELETED
@@ -1,53 +0,0 @@
1
- # deadcode
2
- `deadcode` package implements `DC100` - `unused-global-name` check for detecting
3
- variable/function/class names which are never used in a whole code base.
4
- Only globally defined names are being checked.
5
- Unused local names can be detected by other tools like `ruff`.
6
-
7
- ## Installation
8
- ```shell
9
- pip install deadcode
10
- ```
11
-
12
- ## Usage
13
- ```shell
14
- deadcode .
15
- ```
16
-
17
- Or with command line options:
18
- ```
19
- deadcode . --exclude=venv,tests --ignore-names=BaseTestCase --ignore-names-in-files=migrations
20
- ```
21
-
22
- The same options can be provided in `pyproject.toml` settings file:
23
- ```
24
- [tool.deadcode]
25
- exclude = ["venv", "tests"]
26
- ignore-names = ["BaseTestCase"]
27
- ignore-names-in-files = ["migrations"]
28
- ```
29
-
30
- Command line options:
31
- | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Option&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;| Type | Meaning |
32
- |---------------------------|-------|----------|
33
- |`--exclude` | list | Path expressions to completely skips files from being analysed. |
34
- |`--ignore-names` | list | Removes provided list of names from the error output. |
35
- |`--ignore-names-in-files` | list | Unused names from files matching provided path expressions. |
36
- |`--no-color` | None | Removes colors from the output. |
37
-
38
- ## Contributing
39
- - `make check` - runs unit tests and other checks using virtual environment.
40
-
41
- ## Rationale
42
- `ruff` and `flake8` - don't have rules for unused global code detection, only for local ones `F823`, `F841`, `F842`.
43
- `deadcode` package tries to add a new `DC100` check for detecting variables/functions/classes which are not used in a whole code base.
44
-
45
- There is an alternative `vulture` package, which provides many false positives. `deadcode` - tries to find less, but findings are with higher confidence.
46
- `deadcode` - is supposed to be used inline with other static code checkers like `ruff`.
47
-
48
- ## Known limitations
49
- If the same unused name is repeated in several files - it wont be detected.
50
-
51
- Files with syntax errors will be ignored, because `deadcode` uses `ast` to build abstract syntax tree for name usage detection.
52
-
53
- It is assumed that `deadcode` will be run using the same Python version as the checked code base is implemented in.
File without changes
File without changes
File without changes