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
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-16 04:41 +0000
1"""Drive filtering utilities.
3This module provides filtering functionality for iCloud Drive sync operations,
4separating file and folder filtering logic from the main sync logic per SRP.
5"""
7__author__ = "Mandar Patil (mandarons@pm.me)"
9import os
10import re
11from pathlib import Path, PurePath
13from src import configure_icloudpy_logging, get_logger
15# Configure icloudpy logging immediately after import
16configure_icloudpy_logging()
18LOGGER = get_logger()
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.
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
29 Returns:
30 True if file should be synced, False otherwise
31 """
32 if not file_path:
33 return False
35 if ignore and _is_ignored_path(ignore, file_path):
36 LOGGER.debug(f"Skipping the unwanted file {file_path}")
37 return False
39 if not filters or len(filters) == 0:
40 return True
42 for file_extension in filters:
43 if re.search(f"{file_extension}$", file_path, re.IGNORECASE):
44 return True
46 LOGGER.debug(f"Skipping the unwanted file {file_path}")
47 return False
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.
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
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
70 if not filters or not folder_path or not root or len(filters) == 0:
71 # Nothing to filter, return True
72 return True
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
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.
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
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
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
111def _is_ignored_path(ignore_list: list[str], path: str) -> bool:
112 """Check if a path matches any ignore pattern.
114 Args:
115 ignore_list: List of ignore patterns
116 path: Path to check against patterns
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
127# Legacy alias for backward compatibility
128ignored_path = _is_ignored_path