Coverage for lobster/common/file_collector.py: 89%

37 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2026-02-10 16:49 +0000

1from pathlib import Path 

2from re import Pattern 

3from typing import Iterable, List, Optional 

4 

5from lobster.common.errors import PathError 

6 

7 

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 (.)") 

24 

25 @property 

26 def files(self) -> List[str]: 

27 return self._files 

28 

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 ) 

37 

38 def _is_file_of_interest(self, file: str) -> bool: 

39 return (not self._extensions) or \ 

40 (Path(file).suffix.lower() in self._extensions) 

41 

42 def _is_dir_of_interest(self, dir_name: str) -> bool: 

43 return not any(pattern.match(dir_name) 

44 for pattern in self._directory_exclude_patterns) 

45 

46 def add_dir_recursively(self, dir_path: str) -> None: 

47 """Recursively adds files from a directory, filtering by extensions.""" 

48 def walk_directory(path: Path): 

49 for item in Path(path).iterdir(): 

50 if item.is_file(): 

51 self.add_file(str(item.as_posix()), throw_on_mismatch=False) 

52 elif item.is_dir(): 52 ↛ 49line 52 didn't jump to line 49 because the condition on line 52 was always true

53 if self._is_dir_of_interest(item.name): 53 ↛ 49line 53 didn't jump to line 49 because the condition on line 53 was always true

54 walk_directory(item) 

55 

56 walk_directory(Path(dir_path))