Add check that _lock is a lock in requires_lock

This commit is contained in:
Julian Stirling 2025-08-05 23:12:40 +01:00
parent 9be9097246
commit cbd2f9e0e3
2 changed files with 25 additions and 1 deletions

View file

@ -23,6 +23,16 @@ COMMIT_REGEX = re.compile(r"[0-9a-f]{40}")
REF_REGEX = re.compile(r"^ref:\s(.*)$")
def _is_lock_like(obj):
"""Check if an object is a lock.
Cannot use ``isinstance(obj, threading.RLock)``, may be possible to use
``isinstance(obj, threading._RLock)``. But this uses a private method and may
break. Instead making the check that both "acquire" and "release" exist.
"""
return hasattr(obj, "acquire") and hasattr(obj, "release")
def requires_lock(method: Callable[P, T]) -> Callable[P, T]:
"""Decorate a class method so that it requires the class lock to run.
@ -31,8 +41,12 @@ def requires_lock(method: Callable[P, T]) -> Callable[P, T]:
@wraps(method)
def wrapper(self, *args: P.args, **kwargs: P.kwargs) -> T:
# Confirm the object the method is attached to has a self._lock property
if not hasattr(self, "_lock"):
raise AttributeError(f"{self.__class__.__name__} has no '_lock' attribute")
# And that it is a reentrant lock.
if not _is_lock_like(self._lock):
raise TypeError("requires_lock requires self._lock to be a lock")
with self._lock:
return method(self, *args, **kwargs)