(lock_dir: str, filename: str, timeout: float = 120.0)
| 744 | |
| 745 | @contextmanager |
| 746 | def file_lock(lock_dir: str, filename: str, timeout: float = 120.0): |
| 747 | os.makedirs(lock_dir, exist_ok=True) |
| 748 | lock_file_name = filename.replace(os.sep, '_') + '.lock' |
| 749 | lock_file_path = os.path.join(lock_dir, lock_file_name) |
| 750 | |
| 751 | # Acquire lock with timeout |
| 752 | start_time = time.time() |
| 753 | |
| 754 | while True: |
| 755 | try: |
| 756 | lock_fd = os.open(lock_file_path, |
| 757 | os.O_CREAT | os.O_EXCL | os.O_WRONLY) |
| 758 | os.write(lock_fd, f'{os.getpid()}'.encode()) |
| 759 | break |
| 760 | except FileExistsError: |
| 761 | if time.time() - start_time >= timeout: |
| 762 | raise TimeoutError( |
| 763 | f'Failed to acquire lock for {filename} after {timeout} seconds' |
| 764 | ) |
| 765 | time.sleep(0.1) # Wait 100ms before retry |
| 766 | |
| 767 | try: |
| 768 | yield |
| 769 | finally: |
| 770 | # Release lock |
| 771 | if lock_fd is not None: |
| 772 | os.close(lock_fd) |
| 773 | try: |
| 774 | os.remove(lock_file_path) |
| 775 | except OSError: |
| 776 | pass |
| 777 | |
| 778 | |
| 779 | def render_markdown_todo(md_path: str, |
no test coverage detected