Coverage for lobster/common/file_collector.py: 87%
40 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-08-27 12:15 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2026-08-27 12:15 +0000
1from pathlib import Path
2from re import Pattern
3from typing import Iterable, List, Optional
5from lobster.common.errors import PathError
8class FileCollector:
9 def __init__(
10 self,
11 extensions: Iterable[str],
12 directory_exclude_patterns: Optional[Iterable[Pattern]],
13 ) -> None:
14 if extensions is None: 14 ↛ 15line 14 didn't jump to line 15 because the condition on line 14 was never true
15 raise ValueError("'extensions' must not be None")
16 if directory_exclude_patterns is None:
17 directory_exclude_patterns = []
18 self._extensions = extensions
19 self._files = []
20 self._directory_exclude_patterns = directory_exclude_patterns
21 for ext in self._extensions:
22 if not ext.startswith("."): 22 ↛ 23line 22 didn't jump to line 23 because the condition on line 22 was never true
23 raise ValueError(f"Extension '{ext}' must start with a dot (.)")
25 @property
26 def files(self) -> List[str]:
27 return self._files
29 def add_file(self, file: str, throw_on_mismatch: bool) -> None:
30 if self._is_file_of_interest(file):
31 self._files.append(file)
32 elif throw_on_mismatch:
33 raise PathError(
34 f"File {file} does not have a valid extension. "
35 f"Expected one of {', '.join(self._extensions)}."
36 )
38 def _is_file_of_interest(self, file: str) -> bool:
39 if not self._extensions: 39 ↛ 40line 39 didn't jump to line 40 because the condition on line 39 was never true
40 return True
41 name = Path(file).name.lower()
42 return any(name.endswith(ext.lower()) for ext in self._extensions)
44 def _is_dir_of_interest(self, dir_name: str) -> bool:
45 return not any(pattern.match(dir_name)
46 for pattern in self._directory_exclude_patterns)
48 def add_dir_recursively(self, dir_path: str) -> None:
49 """Recursively adds files from a directory, filtering by extensions."""
50 def walk_directory(path: Path):
51 for item in Path(path).iterdir():
52 if item.is_file():
53 self.add_file(str(item.as_posix()), throw_on_mismatch=False)
54 elif item.is_dir(): 54 ↛ 51line 54 didn't jump to line 51 because the condition on line 54 was always true
55 if self._is_dir_of_interest(item.name): 55 ↛ 51line 55 didn't jump to line 51 because the condition on line 55 was always true
56 walk_directory(item)
58 walk_directory(Path(dir_path))