lapsed 2.7.0__tar.gz → 2.7.2__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 (33) hide show
  1. {lapsed-2.7.0 → lapsed-2.7.2}/.gitignore +0 -3
  2. {lapsed-2.7.0 → lapsed-2.7.2}/PKG-INFO +2 -8
  3. {lapsed-2.7.0 → lapsed-2.7.2}/README.md +1 -7
  4. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/actions/find_python_filenames.py +5 -0
  5. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/actions/parse_tach_config.py +41 -3
  6. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/visitor/dead_code_visitor.py +1 -1
  7. {lapsed-2.7.0 → lapsed-2.7.2}/pyproject.toml +1 -1
  8. {lapsed-2.7.0 → lapsed-2.7.2}/LICENSE +0 -0
  9. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/__init__.py +0 -0
  10. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/actions/__init__.py +0 -0
  11. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/actions/find_unused_names.py +0 -0
  12. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/actions/fix_or_show_unused_code.py +0 -0
  13. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/actions/get_unused_names_error_message.py +0 -0
  14. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/actions/merge_overlaping_file_parts.py +0 -0
  15. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/actions/parse_abstract_syntax_tree.py +0 -0
  16. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/actions/parse_arguments.py +0 -0
  17. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/actions/remove_file_parts_from_content.py +0 -0
  18. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/cli.py +0 -0
  19. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/constants.py +0 -0
  20. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/data_types.py +0 -0
  21. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/utils/__init__.py +0 -0
  22. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/utils/add_colors_to_diff.py +0 -0
  23. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/utils/base_test_case.py +0 -0
  24. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/utils/fix_indent.py +0 -0
  25. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/utils/flatten_lists.py +0 -0
  26. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/utils/nested_scopes.py +0 -0
  27. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/utils/print_ast.py +0 -0
  28. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/visitor/__init__.py +0 -0
  29. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/visitor/code_item.py +0 -0
  30. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/visitor/ignore.py +0 -0
  31. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/visitor/lines.py +0 -0
  32. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/visitor/noqa.py +0 -0
  33. {lapsed-2.7.0 → lapsed-2.7.2}/deadcode/visitor/utils.py +0 -0
@@ -163,6 +163,3 @@ cython_debug/
163
163
  #.idea/
164
164
 
165
165
  notes
166
-
167
- # Local scratch notes
168
- *.local.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lapsed
3
- Version: 2.7.0
3
+ Version: 2.7.2
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.7.0
97
+ rev: 2.6.0
98
98
  hooks:
99
99
  - id: deadcode
100
100
  ```
@@ -227,12 +227,6 @@ code base is implemented in.
227
227
  - [ ] Investigate ways of extracting and backporting Python3.10+ `ast` implementation to lower Python versions.
228
228
 
229
229
  ## Release notes
230
- - v2.7.0:
231
- - Treat `pytest_*` functions defined in `conftest.py` (e.g. `pytest_configure`,
232
- `pytest_collection_modifyitems`, `pytest_addoption`) as pytest hooks, which pytest
233
- calls automatically by name, and never report them as unused.
234
- - Treat methods decorated with `@typing.override` (or `@typing_extensions.override`)
235
- as used, since they are called via the base class they override, not by their own name.
236
230
  - v2.6.0:
237
231
  - Add `--ignore-class-attributes` option to not report unused attributes assigned directly
238
232
  in a class body (e.g. `THING = "blah"` inside `class Foo:`), since such attributes may
@@ -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.7.0
69
+ rev: 2.6.0
70
70
  hooks:
71
71
  - id: deadcode
72
72
  ```
@@ -199,12 +199,6 @@ code base is implemented in.
199
199
  - [ ] Investigate ways of extracting and backporting Python3.10+ `ast` implementation to lower Python versions.
200
200
 
201
201
  ## Release notes
202
- - v2.7.0:
203
- - Treat `pytest_*` functions defined in `conftest.py` (e.g. `pytest_configure`,
204
- `pytest_collection_modifyitems`, `pytest_addoption`) as pytest hooks, which pytest
205
- calls automatically by name, and never report them as unused.
206
- - Treat methods decorated with `@typing.override` (or `@typing_extensions.override`)
207
- as used, since they are called via the base class they override, not by their own name.
208
202
  - v2.6.0:
209
203
  - Add `--ignore-class-attributes` option to not report unused attributes assigned directly
210
204
  in a class body (e.g. `THING = "blah"` inside `class Foo:`), since such attributes may
@@ -22,6 +22,11 @@ def find_python_filenames(args: Args) -> List[str]:
22
22
  logger.info(f'Ignoring: {path}')
23
23
  continue
24
24
 
25
+ if tach_index.is_outside_source_roots(path):
26
+ if args.verbose:
27
+ logger.info(f'Ignoring (outside tach source roots): {path}')
28
+ continue
29
+
25
30
  if path.is_file() and path.suffix == '.py' and tach_index.is_unchecked(path):
26
31
  if args.verbose:
27
32
  logger.info(f'Ignoring (unchecked tach module): {path}')
@@ -28,6 +28,7 @@ class TachModule:
28
28
  @dataclass
29
29
  class TachConfig:
30
30
  source_roots: List[Path]
31
+ project_root: Optional[Path] = None
31
32
  modules: List[TachModule] = field(default_factory=list)
32
33
  interfaces: List[TachInterface] = field(default_factory=list)
33
34
 
@@ -38,6 +39,31 @@ class TachIndex:
38
39
  def __init__(self, configs: List[TachConfig]) -> None:
39
40
  self._configs = configs
40
41
 
42
+ def is_outside_source_roots(self, path: Path) -> bool:
43
+ """True if `path` belongs to a tach project (is its project root or nested under it)
44
+
45
+ but falls outside every one of that project's `source_roots`, and outside them for
46
+ every other tach project it might also belong to. Paths unrelated to any tach project
47
+ (e.g. an explicitly provided directory that lives elsewhere entirely) are unaffected.
48
+ """
49
+ path = path.resolve()
50
+
51
+ related_to_any_config = False
52
+ for config in self._configs:
53
+ if config.project_root is None:
54
+ continue
55
+ relation = _relation_to_project_root(path, config.project_root)
56
+ if relation == 'unrelated':
57
+ continue
58
+ related_to_any_config = True
59
+ if relation == 'ancestor':
60
+ # `path` is still above the project root, so it must be walked into to reach it.
61
+ return False
62
+ if any(_within_or_towards_source_root(path, source_root) for source_root in config.source_roots):
63
+ return False
64
+
65
+ return related_to_any_config
66
+
41
67
  def is_unchecked(self, file: Path) -> bool:
42
68
  for config in self._configs:
43
69
  module_path = _dotted_module_path(file, config.source_roots)
@@ -54,7 +80,7 @@ class TachIndex:
54
80
  if module_path is None:
55
81
  continue
56
82
  for interface in config.interfaces:
57
- adopts_interface = interface.from_patterns is None or _match_any_regex(
83
+ adopts_interface = interface.from_patterns is None or _match_any_dotted_glob(
58
84
  interface.from_patterns, module_path
59
85
  )
60
86
  if adopts_interface and _match_any_regex(interface.expose, name):
@@ -77,11 +103,11 @@ def _parse_tach_toml(path: Path) -> TachConfig:
77
103
  with open(path, 'rb') as f:
78
104
  data = tomllib.load(f)
79
105
 
80
- project_root = path.parent
106
+ project_root = path.parent.resolve()
81
107
  raw_source_roots = data.get('source_roots') or ['.']
82
108
  source_roots = [(project_root / root).resolve() for root in raw_source_roots]
83
109
 
84
- config = TachConfig(source_roots=source_roots)
110
+ config = TachConfig(source_roots=source_roots, project_root=project_root)
85
111
  config.modules.extend(_extract_modules(data))
86
112
  config.interfaces.extend(_extract_interfaces(data))
87
113
 
@@ -180,6 +206,18 @@ def _dotted_module_path(file: Path, source_roots: List[Path]) -> Optional[str]:
180
206
  return '.'.join(parts) if parts else None
181
207
 
182
208
 
209
+ def _relation_to_project_root(path: Path, project_root: Path) -> str:
210
+ if path == project_root or project_root in path.parents:
211
+ return 'inside'
212
+ if path in project_root.parents:
213
+ return 'ancestor'
214
+ return 'unrelated'
215
+
216
+
217
+ def _within_or_towards_source_root(path: Path, source_root: Path) -> bool:
218
+ return path == source_root or source_root in path.parents or path in source_root.parents
219
+
220
+
183
221
  def _dotted_glob_to_regex(pattern: str) -> re.Pattern[str]:
184
222
  regex_segments = [
185
223
  '.*' if segment == '**' else re.escape(segment).replace(r'\*', '[^.]*') for segment in pattern.split('.')
@@ -287,7 +287,7 @@ class DeadCodeVisitor(ast.NodeVisitor):
287
287
 
288
288
  self.scopes.add(code_item)
289
289
 
290
- if ignored(first_lineno, type_=type_):
290
+ if ignored(last_node.lineno, type_=type_):
291
291
  self._log(f'Ignoring {type_} "{name}"')
292
292
  else:
293
293
  collection.append(code_item)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lapsed"
3
- version = "2.7.0"
3
+ version = "2.7.2"
4
4
  authors = [
5
5
  {name = "Albertas Gimbutas", email = "albertasgim@gmail.com"},
6
6
  ]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes