Skip to content

Commit dbefe0f

Browse files
mlugo-apxclaude
andcommitted
Add comprehensive type hints to all Python functions
Changes: - Add typing imports: Dict, Any, Optional, Callable, Tuple, List - Add type hints to all 13 functions/methods: * parse_rsync_stats(stdout: Optional[str]) -> Dict[str, Any] * retry_on_failure decorator with proper Callable typing * load_config() -> Dict[str, str] * All GCodeHandler methods with FileSystemEvent types * main() -> None - Zero functional changes, only type annotations added - Improves IDE autocomplete and code maintainability - Follows Python 3.6+ typing conventions This makes the codebase more contributor-friendly and catches type errors earlier in development. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 9dd50a5 commit dbefe0f

File tree

1 file changed

+18
-17
lines changed

1 file changed

+18
-17
lines changed

monitor_and_sync.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515
import shutil
1616
from pathlib import Path
1717
from functools import wraps
18+
from typing import Dict, Any, Optional, Callable, Tuple, List
1819
from watchdog.observers import Observer
19-
from watchdog.events import FileSystemEventHandler
20+
from watchdog.events import FileSystemEventHandler, FileSystemEvent
2021

2122
# Determine script directory
2223
SCRIPT_DIR = Path(__file__).parent.resolve()
@@ -38,9 +39,9 @@
3839
RETRY_BACKOFF_MULTIPLIER = 2 # Exponential backoff multiplier
3940

4041

41-
def parse_rsync_stats(stdout):
42+
def parse_rsync_stats(stdout: Optional[str]) -> Dict[str, Any]:
4243
"""Parse rsync --stats output into a dictionary of values."""
43-
stats = {}
44+
stats: Dict[str, Any] = {}
4445

4546
if not stdout:
4647
return stats
@@ -72,8 +73,8 @@ def parse_rsync_stats(stdout):
7273

7374
return stats
7475

75-
def retry_on_failure(max_attempts=RETRY_MAX_ATTEMPTS, initial_delay=RETRY_INITIAL_DELAY,
76-
backoff_multiplier=RETRY_BACKOFF_MULTIPLIER):
76+
def retry_on_failure(max_attempts: int = RETRY_MAX_ATTEMPTS, initial_delay: float = RETRY_INITIAL_DELAY,
77+
backoff_multiplier: float = RETRY_BACKOFF_MULTIPLIER) -> Callable:
7778
"""
7879
Decorator to retry function on transient failures with exponential backoff.
7980
@@ -87,9 +88,9 @@ def retry_on_failure(max_attempts=RETRY_MAX_ATTEMPTS, initial_delay=RETRY_INITIA
8788
or subprocess.TimeoutExpired. On success, returns a tuple of
8889
(result, attempts_used).
8990
"""
90-
def decorator(func):
91+
def decorator(func: Callable) -> Callable:
9192
@wraps(func)
92-
def wrapper(*args, **kwargs):
93+
def wrapper(*args: Any, **kwargs: Any) -> Tuple[Any, int]:
9394
attempt = 1
9495
delay = initial_delay
9596

@@ -113,7 +114,7 @@ def wrapper(*args, **kwargs):
113114
return wrapper
114115
return decorator
115116

116-
def load_config():
117+
def load_config() -> Dict[str, str]:
117118
"""Load configuration from config.local"""
118119
config_file = SCRIPT_DIR / 'config.local'
119120

@@ -270,26 +271,26 @@ def load_config():
270271
class GCodeHandler(FileSystemEventHandler):
271272
"""Handler for .gcode file events"""
272273

273-
def __init__(self):
274+
def __init__(self) -> None:
274275
self.syncing = set() # Track files currently being synced
275276
self.syncing_lock = threading.Lock() # Prevent race conditions
276277

277-
def on_created(self, event):
278+
def on_created(self, event: FileSystemEvent) -> None:
278279
"""Called when a file is created"""
279280
if not event.is_directory and event.src_path.endswith('.gcode'):
280281
self.sync_file(event.src_path)
281282

282-
def on_moved(self, event):
283+
def on_moved(self, event: FileSystemEvent) -> None:
283284
"""Called when a file is moved into the directory"""
284285
if not event.is_directory and event.dest_path.endswith('.gcode'):
285286
self.sync_file(event.dest_path)
286287

287-
def on_modified(self, event):
288+
def on_modified(self, event: FileSystemEvent) -> None:
288289
"""Called when a file is modified (handles saves from some editors)"""
289290
if not event.is_directory and event.src_path.endswith('.gcode'):
290291
self.sync_file(event.src_path)
291292

292-
def sync_file(self, file_path):
293+
def sync_file(self, file_path: str) -> None:
293294
"""Sync a file to the remote server"""
294295
# Thread-safe check and add
295296
with self.syncing_lock:
@@ -440,7 +441,7 @@ def sync_file(self, file_path):
440441
self.syncing.discard(file_path)
441442

442443
@retry_on_failure()
443-
def _execute_rsync_with_retry(self, rsync_cmd, timeout_seconds):
444+
def _execute_rsync_with_retry(self, rsync_cmd: List[str], timeout_seconds: int) -> subprocess.CompletedProcess:
444445
"""Execute rsync command with retry logic for transient failures.
445446
446447
Args:
@@ -464,7 +465,7 @@ def _execute_rsync_with_retry(self, rsync_cmd, timeout_seconds):
464465
)
465466

466467
@retry_on_failure(max_attempts=3, initial_delay=2, backoff_multiplier=2)
467-
def _execute_usb_refresh_with_retry(self):
468+
def _execute_usb_refresh_with_retry(self) -> bool:
468469
"""Execute USB refresh with subprocess (called by retry decorator).
469470
470471
Returns:
@@ -497,7 +498,7 @@ def _execute_usb_refresh_with_retry(self):
497498
logging.debug(f"Refresh output: {result.stdout.strip()}")
498499
return True
499500

500-
def refresh_usb_gadget(self):
501+
def refresh_usb_gadget(self) -> bool:
501502
"""Trigger USB gadget refresh on the Pi with retry logic.
502503
503504
Returns:
@@ -528,7 +529,7 @@ def refresh_usb_gadget(self):
528529
return False
529530

530531

531-
def main():
532+
def main() -> None:
532533
"""Main function"""
533534
# Check if watchdog is installed
534535
try:

0 commit comments

Comments
 (0)