| 338 | |
| 339 | |
| 340 | class FileLockMeta(ABCMeta): |
| 341 | _instances: WeakValueDictionary[str, BaseFileLock] |
| 342 | _instances_lock: RLock |
| 343 | _instances_under_construction: set[str] |
| 344 | |
| 345 | def __call__( # ruff:ignore[too-many-arguments] # forwards the public constructor's documented parameters |
| 346 | cls: type[_T], |
| 347 | lock_file: str | os.PathLike[str], |
| 348 | timeout: float = -1, |
| 349 | mode: int = _UNSET_FILE_MODE, |
| 350 | thread_local: bool = True, # ruff:ignore[boolean-type-hint-positional-argument, boolean-default-value-positional-argument] # public API: positional bool kept for backwards compatibility |
| 351 | *, |
| 352 | blocking: bool = True, |
| 353 | is_singleton: bool = False, |
| 354 | poll_interval: float = 0.05, |
| 355 | lifetime: float | None = None, |
| 356 | context_error_policy: ContextErrorPolicy = "chain", |
| 357 | close_error_policy: CloseErrorPolicy = "default", |
| 358 | fallback_to_soft: bool = True, |
| 359 | preserve_lock_file: bool = False, |
| 360 | on_acquired: Callable[[int], None] | None = None, |
| 361 | **kwargs: _ExtraValue, |
| 362 | ) -> _T: |
| 363 | _ensure_current_process() |
| 364 | lifetime = _resolve_lifetime(lifetime, cls, stacklevel=cls._constructor_lifetime_warning_stacklevel) |
| 365 | # Validate before building the instance: a raise inside __init__ would leave a half-constructed object whose |
| 366 | # __del__ then trips over the missing context. |
| 367 | context_error_policy = _resolve_context_error_policy(context_error_policy) |
| 368 | close_error_policy = _resolve_close_error_policy(close_error_policy) |
| 369 | preserve_lock_file = _resolve_preserve_lock_file( |
| 370 | preserve=preserve_lock_file, supported=cls._preserve_lock_file_supported, cls_name=cls.__name__ |
| 371 | ) |
| 372 | on_acquired = _resolve_on_acquired(on_acquired, supported=cls._on_acquired_supported, cls_name=cls.__name__) |
| 373 | params: dict[str, _LockInitValue | _ExtraValue] = { |
| 374 | "timeout": timeout, |
| 375 | "mode": mode, |
| 376 | "thread_local": thread_local, |
| 377 | "blocking": blocking, |
| 378 | "is_singleton": is_singleton, |
| 379 | "poll_interval": poll_interval, |
| 380 | "lifetime": lifetime, |
| 381 | "context_error_policy": context_error_policy, |
| 382 | "close_error_policy": close_error_policy, |
| 383 | "fallback_to_soft": fallback_to_soft, |
| 384 | "preserve_lock_file": preserve_lock_file, |
| 385 | "on_acquired": on_acquired, |
| 386 | **kwargs, |
| 387 | } |
| 388 | if not is_singleton: |
| 389 | return cls._create_instance(lock_file, params) |
| 390 | |
| 391 | # Look up, build and store under one lock. Without it two threads racing the first construction for a |
| 392 | # path both miss the cache and each build their own instance, so callers relying on is_singleton for |
| 393 | # reentrant locking across instances end up with two "singletons" and acquire()'s deadlock check then |
| 394 | # rejects a legitimate reentrant acquire; the unguarded writes to the WeakValueDictionary are a data |
| 395 | # race besides. ReadWriteLock and SoftReadWriteLock already guard their singleton caches this way. |
| 396 | # Key the cache on the canonical form so equivalent spellings of one path share a singleton, and it matches the |
| 397 | # deadlock-registry key acquire() uses. |
nothing calls this directly
no outgoing calls
no test coverage detected