Async wrapper around :class:`ReadWriteLock` for use in ``asyncio`` applications. Because Python's :mod:`sqlite3` module has no async API, all blocking SQLite operations are dispatched to a thread pool via ``loop.run_in_executor()``. Reentrancy, upgrade/downgrade rules, and singleton be
| 36 | |
| 37 | |
| 38 | class AsyncReadWriteLock: |
| 39 | """ |
| 40 | Async wrapper around :class:`ReadWriteLock` for use in ``asyncio`` applications. |
| 41 | |
| 42 | Because Python's :mod:`sqlite3` module has no async API, all blocking SQLite operations are dispatched to a thread |
| 43 | pool via ``loop.run_in_executor()``. Reentrancy, upgrade/downgrade rules, and singleton behavior are delegated |
| 44 | to the underlying :class:`ReadWriteLock`. |
| 45 | |
| 46 | :param lock_file: path to the SQLite database file used as the lock |
| 47 | :param timeout: maximum wait time in seconds; ``-1`` means block indefinitely |
| 48 | :param blocking: if ``False``, raise :class:`~filelock.Timeout` immediately when the lock is unavailable |
| 49 | :param is_singleton: if ``True``, reuse existing :class:`ReadWriteLock` instances for the same resolved path |
| 50 | :param loop: event loop for ``run_in_executor``; ``None`` uses the running loop |
| 51 | :param executor: executor for ``run_in_executor``. When ``None`` a dedicated single-thread executor is created |
| 52 | and owned by this lock, ensuring every operation runs on the same thread (required for SQLite affinity); it |
| 53 | is shut down by :meth:`close`. A caller-supplied executor is used as-is and never shut down here, so when no |
| 54 | executor is passed remember to call :meth:`close` to release the owned one. |
| 55 | |
| 56 | .. versionadded:: 3.21.0 |
| 57 | |
| 58 | """ |
| 59 | |
| 60 | def __init__( # noqa: PLR0913 |
| 61 | self, |
| 62 | lock_file: str | os.PathLike[str], |
| 63 | timeout: float = -1, |
| 64 | *, |
| 65 | blocking: bool = True, |
| 66 | is_singleton: bool = True, |
| 67 | loop: asyncio.AbstractEventLoop | None = None, |
| 68 | executor: futures.Executor | None = None, |
| 69 | ) -> None: |
| 70 | self._lock = ReadWriteLock(lock_file, timeout, blocking=blocking, is_singleton=is_singleton) |
| 71 | self._loop = loop |
| 72 | self._owns_executor = executor is None |
| 73 | self._executor = executor or ThreadPoolExecutor(max_workers=1) |
| 74 | |
| 75 | @property |
| 76 | def lock_file(self) -> str: |
| 77 | """The path to the lock file.""" |
| 78 | return self._lock.lock_file |
| 79 | |
| 80 | @property |
| 81 | def timeout(self) -> float: |
| 82 | """The default timeout.""" |
| 83 | return self._lock.timeout |
| 84 | |
| 85 | @property |
| 86 | def blocking(self) -> bool: |
| 87 | """Whether blocking is enabled by default.""" |
| 88 | return self._lock.blocking |
| 89 | |
| 90 | @property |
| 91 | def loop(self) -> asyncio.AbstractEventLoop | None: |
| 92 | """The event loop (or ``None`` for the running loop).""" |
| 93 | return self._loop |
| 94 | |
| 95 | @property |
no outgoing calls
searching dependent graphs…