Cross-process read-write lock backed by SQLite. Allows concurrent shared readers or a single exclusive writer. The lock is reentrant within the same mode (multiple ``acquire_read`` calls nest, as do multiple ``acquire_write`` calls from the same thread), but upgrading from read to
| 245 | |
| 246 | |
| 247 | class ReadWriteLock(metaclass=_ReadWriteLockMeta): |
| 248 | """ |
| 249 | Cross-process read-write lock backed by SQLite. |
| 250 | |
| 251 | Allows concurrent shared readers or a single exclusive writer. The lock is reentrant within the same mode (multiple |
| 252 | ``acquire_read`` calls nest, as do multiple ``acquire_write`` calls from the same thread), but upgrading from read |
| 253 | to write or downgrading from write to read raises :class:`RuntimeError`. Write locks are pinned to the thread that |
| 254 | acquired them. |
| 255 | |
| 256 | By default, ``is_singleton=True``: calling ``ReadWriteLock(path)`` with the same resolved path returns the same |
| 257 | instance. The path is handed to :func:`sqlite3.connect` as given, so a ``.db`` extension is a convention rather |
| 258 | than a requirement; the filesystem must be one the active SQLite VFS supports. |
| 259 | |
| 260 | :param lock_file: path to the SQLite database file used as the lock |
| 261 | :param timeout: maximum wait time in seconds; ``-1`` means block indefinitely |
| 262 | :param blocking: if ``False``, raise :class:`~filelock.Timeout` immediately when the lock is unavailable |
| 263 | :param is_singleton: if ``True``, reuse existing instances for the same resolved path |
| 264 | |
| 265 | .. versionadded:: 3.21.0 |
| 266 | |
| 267 | """ |
| 268 | |
| 269 | _instances: WeakValueDictionary[pathlib.Path, ReadWriteLock] = WeakValueDictionary() |
| 270 | _instances_lock = threading.RLock() |
| 271 | _instances_pid = _GETPID() |
| 272 | _instances_under_construction: ClassVar[set[pathlib.Path]] = set() |
| 273 | |
| 274 | def __init_subclass__(cls) -> None: |
| 275 | super().__init_subclass__() |
| 276 | cls._instances = WeakValueDictionary() |
| 277 | cls._instances_lock = threading.RLock() |
| 278 | cls._instances_pid = _GETPID() |
| 279 | cls._instances_under_construction = set() |
| 280 | _register_fork_class(cls) |
| 281 | |
| 282 | @classmethod |
| 283 | def get_lock( |
| 284 | cls, lock_file: str | os.PathLike[str], timeout: float = -1, *, blocking: bool = True |
| 285 | ) -> ReadWriteLock: |
| 286 | """ |
| 287 | Return the singleton :class:`ReadWriteLock` for *lock_file*. |
| 288 | |
| 289 | :param lock_file: path to the SQLite database file used as the lock |
| 290 | :param timeout: maximum wait time in seconds; ``-1`` means block indefinitely |
| 291 | :param blocking: if ``False``, raise :class:`~filelock.Timeout` immediately when the lock is unavailable |
| 292 | |
| 293 | :returns: the singleton lock instance |
| 294 | |
| 295 | :raises ValueError: if an instance already exists for this path with different *timeout* or *blocking* values |
| 296 | |
| 297 | """ |
| 298 | return cls(lock_file, timeout, blocking=blocking) |
| 299 | |
| 300 | def __init__( |
| 301 | self, |
| 302 | lock_file: str | os.PathLike[str], |
| 303 | timeout: float = -1, |
| 304 | *, |
no outgoing calls