aline-ai 0.1.10__py3-none-any.whl → 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
realign/file_lock.py ADDED
@@ -0,0 +1,120 @@
1
+ """File-based locking mechanism for cross-process synchronization."""
2
+
3
+ import fcntl
4
+ import os
5
+ import time
6
+ from pathlib import Path
7
+ from typing import Optional
8
+ from contextlib import contextmanager
9
+
10
+
11
+ class FileLock:
12
+ """Simple file-based lock using fcntl (Unix/macOS only)."""
13
+
14
+ def __init__(self, lock_file: Path, timeout: float = 10.0):
15
+ """
16
+ Initialize a file lock.
17
+
18
+ Args:
19
+ lock_file: Path to the lock file
20
+ timeout: Maximum time to wait for lock acquisition (seconds)
21
+ """
22
+ self.lock_file = lock_file
23
+ self.timeout = timeout
24
+ self.fd: Optional[int] = None
25
+
26
+ def acquire(self, blocking: bool = True) -> bool:
27
+ """
28
+ Acquire the lock.
29
+
30
+ Args:
31
+ blocking: If True, wait for lock; if False, return immediately
32
+
33
+ Returns:
34
+ True if lock was acquired, False otherwise
35
+ """
36
+ # Create lock file directory if needed
37
+ self.lock_file.parent.mkdir(parents=True, exist_ok=True)
38
+
39
+ # Open lock file
40
+ self.fd = os.open(str(self.lock_file), os.O_CREAT | os.O_RDWR)
41
+
42
+ if blocking:
43
+ # Try to acquire with timeout
44
+ start_time = time.time()
45
+ while True:
46
+ try:
47
+ fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
48
+ return True
49
+ except BlockingIOError:
50
+ if time.time() - start_time > self.timeout:
51
+ os.close(self.fd)
52
+ self.fd = None
53
+ return False
54
+ time.sleep(0.1)
55
+ else:
56
+ # Non-blocking attempt
57
+ try:
58
+ fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
59
+ return True
60
+ except BlockingIOError:
61
+ os.close(self.fd)
62
+ self.fd = None
63
+ return False
64
+
65
+ def release(self):
66
+ """Release the lock."""
67
+ if self.fd is not None:
68
+ try:
69
+ fcntl.flock(self.fd, fcntl.LOCK_UN)
70
+ os.close(self.fd)
71
+ except Exception:
72
+ pass
73
+ finally:
74
+ self.fd = None
75
+
76
+ def __enter__(self):
77
+ """Context manager entry."""
78
+ if not self.acquire():
79
+ raise TimeoutError(f"Could not acquire lock on {self.lock_file} within {self.timeout}s")
80
+ return self
81
+
82
+ def __exit__(self, exc_type, exc_val, exc_tb):
83
+ """Context manager exit."""
84
+ self.release()
85
+ return False
86
+
87
+ def __del__(self):
88
+ """Cleanup on deletion."""
89
+ self.release()
90
+
91
+
92
+ @contextmanager
93
+ def commit_lock(repo_path: Path, timeout: float = 10.0):
94
+ """
95
+ Context manager for acquiring a commit lock.
96
+
97
+ Prevents multiple watchers from committing simultaneously.
98
+
99
+ Usage:
100
+ with commit_lock(repo_path):
101
+ # Perform git commit
102
+ subprocess.run(["git", "commit", ...])
103
+
104
+ Args:
105
+ repo_path: Path to the repository
106
+ timeout: Maximum time to wait for lock (seconds)
107
+
108
+ Yields:
109
+ True if lock was acquired
110
+ """
111
+ lock_file = repo_path / ".realign" / ".commit.lock"
112
+ lock = FileLock(lock_file, timeout=timeout)
113
+
114
+ try:
115
+ if lock.acquire():
116
+ yield True
117
+ else:
118
+ yield False
119
+ finally:
120
+ lock.release()