Files
AIPass/src/aipass/ai_mail/apps/handlers/email/inbox_lock.py
T
AIPassandClaude Opus 4.6 fc0de2a76f feat(system): STATUS board, flow DPLAN fixes, prompt architecture, dev.local.md (#32)
STATUS board: per-branch STATUS.local.md with central aggregation via
drone @prax status sync. Prax handler scans AIPASS_REGISTRY.json and
builds collapsible STATUS.md at repo root (14 operational, 1 in-progress).

Flow: migrated all 10 DPLAN handler files from Dev-Pass paths to
Path(__file__)-based resolution. Fixed FPLAN template detection
(is_template_content was deleting plans with real content).

Prompts: breadcrumbs philosophy in global prompt, dev.local.md awareness,
devpulse branch prompt revised (expertise table, working habits, git hygiene).

ai_mail: sender identity fix propagated, email.py handler refactoring.

Prax: status sync handler, dashboard command collision fix, central/reader.py.

dev.local.md seeded across all 15 branches + spawn template. Gitignore
updated (dev.local.md, STATUS.local.md, CLOSED_PLANS.local.json, backup_data).

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 13:36:05 -07:00

80 lines
2.2 KiB
Python

# =================== AIPass ====================
# Name: inbox_lock.py
# Description: Inbox File Lock Handler
# Version: 1.0.0
# Created: 2026-02-09
# Modified: 2026-02-09
# =============================================
"""
Inbox File Lock Handler
Provides exclusive file locking for inbox.json read-modify-write operations.
Uses fcntl.flock (POSIX advisory locks) to prevent concurrent write corruption.
Usage:
with inbox_lock(inbox_file):
data = json.load(open(inbox_file, encoding='utf-8'))
# ... modify data ...
json.dump(data, open(inbox_file, 'w', encoding='utf-8'))
"""
import sys
from pathlib import Path
from contextlib import contextmanager
# fcntl is POSIX-only (Linux/macOS). On Windows, use msvcrt for locking.
if sys.platform == "win32":
import msvcrt
else:
import fcntl
@contextmanager
def inbox_lock(inbox_file: Path):
"""
Context manager that acquires an exclusive lock on an inbox.json file.
Uses a separate .inbox.lock file adjacent to inbox.json to hold the
fcntl.flock advisory lock. This avoids issues with truncating the
locked file itself during writes.
Args:
inbox_file: Path to the inbox.json file to lock
Yields:
None - lock is held for the duration of the with block
Raises:
OSError: If lock cannot be acquired
"""
lock_file = inbox_file.parent / ".inbox.lock"
lock_fd = None
try:
# Create/open lock file
lock_fd = open(lock_file, 'w', encoding='utf-8')
# Acquire exclusive lock (blocking - waits for other processes)
if sys.platform == "win32":
msvcrt.locking(lock_fd.fileno(), msvcrt.LK_LOCK, 1)
else:
fcntl.flock(lock_fd.fileno(), fcntl.LOCK_EX)
yield
finally:
if lock_fd is not None:
try:
# Release lock
if sys.platform == "win32":
msvcrt.locking(lock_fd.fileno(), msvcrt.LK_UNLCK, 1)
else:
fcntl.flock(lock_fd.fileno(), fcntl.LOCK_UN)
lock_fd.close()
except Exception:
try:
lock_fd.close()
except Exception:
pass # Best-effort close