Coverage for src/drive_file_existence.py: 100%
48 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"""File existence checking utilities.
3This module provides file and package existence checking functionality,
4separating existence validation logic from sync operations per SRP.
5"""
7__author__ = "Mandar Patil (mandarons@pm.me)"
9import os
10from pathlib import Path
11from shutil import rmtree
12from typing import Any
14from src import configure_icloudpy_logging, get_logger
16# Configure icloudpy logging immediately after import
17configure_icloudpy_logging()
19LOGGER = get_logger()
22def file_exists(item: Any, local_file: str) -> bool:
23 """Check if a file exists locally and is up-to-date.
25 Args:
26 item: iCloud file item with date_modified and size attributes
27 local_file: Path to the local file
29 Returns:
30 True if file exists and is up-to-date, False otherwise
31 """
32 if not (item and local_file and os.path.isfile(local_file)):
33 LOGGER.debug(f"File {local_file} does not exist locally.")
34 return False
36 local_file_modified_time = int(os.path.getmtime(local_file))
37 remote_file_modified_time = int(item.date_modified.timestamp())
38 local_file_size = os.path.getsize(local_file)
39 remote_file_size = item.size
41 if local_file_modified_time == remote_file_modified_time and (
42 local_file_size == remote_file_size
43 or (local_file_size == 0 and remote_file_size is None)
44 or (local_file_size is None and remote_file_size == 0)
45 ):
46 LOGGER.debug(f"No changes detected. Skipping the file {local_file} ...")
47 return True
49 LOGGER.debug(
50 f"Changes detected: local_modified_time is {local_file_modified_time}, "
51 + f"remote_modified_time is {remote_file_modified_time}, "
52 + f"local_file_size is {local_file_size} and remote_file_size is {remote_file_size}.",
53 )
54 return False
57def package_exists(item: Any, local_package_path: str) -> bool:
58 """Check if a package exists locally and is up-to-date.
60 Args:
61 item: iCloud package item with date_modified and size attributes
62 local_package_path: Path to the local package directory
64 Returns:
65 True if package exists and is up-to-date, False otherwise
66 """
67 if not (item and local_package_path and os.path.isdir(local_package_path)):
68 LOGGER.debug(f"Package {local_package_path} does not exist locally.")
69 return False
71 local_package_modified_time = int(os.path.getmtime(local_package_path))
72 remote_package_modified_time = int(item.date_modified.timestamp())
73 local_package_size = sum(f.stat().st_size for f in Path(local_package_path).glob("**/*") if f.is_file())
74 remote_package_size = item.size
76 if local_package_modified_time == remote_package_modified_time and local_package_size == remote_package_size:
77 LOGGER.debug(f"No changes detected. Skipping the package {local_package_path} ...")
78 return True
80 LOGGER.info(
81 f"Changes detected: local_modified_time is {local_package_modified_time}, "
82 + f"remote_modified_time is {remote_package_modified_time}, "
83 + f"local_package_size is {local_package_size} and remote_package_size is {remote_package_size}.",
84 )
85 rmtree(local_package_path)
86 return False
89def is_package(item: Any) -> bool:
90 """Determine if an iCloud item is a package that needs special handling.
92 Args:
93 item: iCloud item to check
95 Returns:
96 True if item is a package, False otherwise
97 """
98 file_is_a_package = False
99 try:
100 with item.open(stream=True) as response:
101 file_is_a_package = response.url and "/packageDownload?" in response.url
102 except Exception as e:
103 # Enhanced error logging with file context
104 # This catches all exceptions including iCloudPy errors like ObjectNotFoundException
105 error_msg = str(e)
106 item_name = getattr(item, "name", "Unknown file")
107 if "ObjectNotFoundException" in error_msg or "NOT_FOUND" in error_msg:
108 LOGGER.error(f"File not found in iCloud Drive while checking package type - {item_name}: {error_msg}")
109 else:
110 LOGGER.error(f"Failed to check package type for {item_name}: {error_msg}")
111 # Return False if we can't determine package type due to error
112 file_is_a_package = False
113 return file_is_a_package