Coverage for src/drive_filtering.py: 100%

46 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2025-10-16 04:41 +0000

1"""Drive filtering utilities. 

2 

3This module provides filtering functionality for iCloud Drive sync operations, 

4separating file and folder filtering logic from the main sync logic per SRP. 

5""" 

6 

7__author__ = "Mandar Patil (mandarons@pm.me)" 

8 

9import os 

10import re 

11from pathlib import Path, PurePath 

12 

13from src import configure_icloudpy_logging, get_logger 

14 

15# Configure icloudpy logging immediately after import 

16configure_icloudpy_logging() 

17 

18LOGGER = get_logger() 

19 

20 

21def wanted_file(filters: list[str] | None, ignore: list[str] | None, file_path: str) -> bool: 

22 """Check if a file should be synced based on filters and ignore patterns. 

23 

24 Args: 

25 filters: List of file extension patterns to include (None means include all) 

26 ignore: List of ignore patterns to exclude 

27 file_path: Path to the file to check 

28 

29 Returns: 

30 True if file should be synced, False otherwise 

31 """ 

32 if not file_path: 

33 return False 

34 

35 if ignore and _is_ignored_path(ignore, file_path): 

36 LOGGER.debug(f"Skipping the unwanted file {file_path}") 

37 return False 

38 

39 if not filters or len(filters) == 0: 

40 return True 

41 

42 for file_extension in filters: 

43 if re.search(f"{file_extension}$", file_path, re.IGNORECASE): 

44 return True 

45 

46 LOGGER.debug(f"Skipping the unwanted file {file_path}") 

47 return False 

48 

49 

50def wanted_folder( 

51 filters: list[str] | None, 

52 ignore: list[str] | None, 

53 root: str, 

54 folder_path: str, 

55) -> bool: 

56 """Check if a folder should be synced based on filters and ignore patterns. 

57 

58 Args: 

59 filters: List of folder paths to include (None means include all) 

60 ignore: List of ignore patterns to exclude 

61 root: Root directory path for relative path calculations 

62 folder_path: Path to the folder to check 

63 

64 Returns: 

65 True if folder should be synced, False otherwise 

66 """ 

67 if ignore and _is_ignored_path(ignore, folder_path): 

68 return False 

69 

70 if not filters or not folder_path or not root or len(filters) == 0: 

71 # Nothing to filter, return True 

72 return True 

73 

74 # Something to filter 

75 folder_path = Path(folder_path) 

76 for folder in filters: 

77 child_path = Path(os.path.join(os.path.abspath(root), str(folder).removeprefix("/").removesuffix("/"))) 

78 if folder_path in child_path.parents or child_path in folder_path.parents or folder_path == child_path: 

79 return True 

80 return False 

81 

82 

83def wanted_parent_folder( 

84 filters: list[str] | None, 

85 ignore: list[str] | None, 

86 root: str, 

87 folder_path: str, 

88) -> bool: 

89 """Check if a parent folder should be processed based on filters. 

90 

91 Args: 

92 filters: List of folder paths to include (None means include all) 

93 ignore: List of ignore patterns to exclude 

94 root: Root directory path for relative path calculations 

95 folder_path: Path to the parent folder to check 

96 

97 Returns: 

98 True if parent folder should be processed, False otherwise 

99 """ 

100 if not filters or not folder_path or not root or len(filters) == 0: 

101 return True 

102 

103 folder_path = Path(folder_path) 

104 for folder in filters: 

105 child_path = Path(os.path.join(os.path.abspath(root), folder.removeprefix("/").removesuffix("/"))) 

106 if child_path in folder_path.parents or folder_path == child_path: 

107 return True 

108 return False 

109 

110 

111def _is_ignored_path(ignore_list: list[str], path: str) -> bool: 

112 """Check if a path matches any ignore pattern. 

113 

114 Args: 

115 ignore_list: List of ignore patterns 

116 path: Path to check against patterns 

117 

118 Returns: 

119 True if path should be ignored, False otherwise 

120 """ 

121 for ignore in ignore_list: 

122 if PurePath(path).match(ignore + "*" if ignore.endswith("/") else ignore): 

123 return True 

124 return False 

125 

126 

127# Legacy alias for backward compatibility 

128ignored_path = _is_ignored_path