|
| 1 | +"""flock() compatibility seam. |
| 2 | +
|
| 3 | +POSIX re-exports ``fcntl.flock`` and its constants verbatim. Windows maps the |
| 4 | +same surface onto an ``msvcrt`` 1-byte region lock: every holder locks the |
| 5 | +first byte, which is all flock's whole-file-mutex semantics need. Windows has |
| 6 | +no shared locks — ``LOCK_SH`` degrades to exclusive, and ``SHARED_IS_EXCLUSIVE`` |
| 7 | +tells lock-mode transitions (shared→exclusive and back) to relabel their |
| 8 | +bookkeeping instead of re-locking a region this process already owns (a |
| 9 | +same-process re-lock raises on Windows). A non-blocking failure is re-raised |
| 10 | +with ``EAGAIN`` so POSIX-shaped retry loops work unchanged. |
| 11 | +""" |
| 12 | +from __future__ import annotations |
| 13 | + |
| 14 | +try: |
| 15 | + from fcntl import LOCK_EX, LOCK_NB, LOCK_SH, LOCK_UN, flock # noqa: F401 |
| 16 | + |
| 17 | + SHARED_IS_EXCLUSIVE = False |
| 18 | +except ImportError: # Windows |
| 19 | + import errno as _errno |
| 20 | + import msvcrt as _msvcrt |
| 21 | + import os as _os |
| 22 | + |
| 23 | + LOCK_SH = 0x1 |
| 24 | + LOCK_EX = 0x2 |
| 25 | + LOCK_NB = 0x4 |
| 26 | + LOCK_UN = 0x8 |
| 27 | + SHARED_IS_EXCLUSIVE = True |
| 28 | + |
| 29 | + def flock(fd: int, operation: int) -> None: |
| 30 | + _os.lseek(fd, 0, _os.SEEK_SET) |
| 31 | + if operation & LOCK_UN: |
| 32 | + try: |
| 33 | + _msvcrt.locking(fd, _msvcrt.LK_UNLCK, 1) |
| 34 | + except OSError: |
| 35 | + # flock(LOCK_UN) on an unheld lock is a no-op; mirror that. |
| 36 | + pass |
| 37 | + return |
| 38 | + mode = _msvcrt.LK_NBLCK if operation & LOCK_NB else _msvcrt.LK_LOCK |
| 39 | + try: |
| 40 | + _msvcrt.locking(fd, mode, 1) |
| 41 | + except OSError as exc: |
| 42 | + raise OSError(_errno.EAGAIN, "lock held (msvcrt)") from exc |
0 commit comments