33import logging
44from collections .abc import Awaitable , Callable
55from datetime import timedelta
6- from typing import Any
6+ from typing import Any , ParamSpec , TypeVar
77
88import arrow
99
1212
1313_logger = logging .getLogger (__file__ )
1414
15+ P = ParamSpec ("P" )
16+ R = TypeVar ("R" )
17+
1518
1619def exclusive (
17- redis : RedisClientSDK , * , lock_key : str , lock_value : bytes | str | None = None
18- ):
20+ redis : RedisClientSDK | Callable [..., RedisClientSDK ],
21+ * ,
22+ lock_key : str | Callable [..., str ],
23+ lock_value : bytes | str | None = None ,
24+ ) -> Callable [[Callable [P , Awaitable [R ]]], Callable [P , Awaitable [R ]]]:
1925 """
2026 Define a method to run exclusively across
2127 processes by leveraging a Redis Lock.
@@ -24,12 +30,30 @@ def exclusive(
2430 redis: the redis client SDK
2531 lock_key: a string as the name of the lock (good practice: app_name:lock_name)
2632 lock_value: some additional data that can be retrieved by another client
33+
34+ Raises:
35+ - ValueError if used incorrectly
36+ - CouldNotAcquireLockError if the lock could not be acquired
2737 """
2838
29- def decorator (func ):
39+ if not lock_key :
40+ msg = "lock_key cannot be empty string!"
41+ raise ValueError (msg )
42+
43+ def decorator (func : Callable [P , Awaitable [R ]]) -> Callable [P , Awaitable [R ]]:
3044 @functools .wraps (func )
31- async def wrapper (* args , ** kwargs ):
32- async with redis .lock_context (lock_key = lock_key , lock_value = lock_value ):
45+ async def wrapper (* args : P .args , ** kwargs : P .kwargs ) -> R :
46+ redis_lock_key = (
47+ lock_key (* args , ** kwargs ) if callable (lock_key ) else lock_key
48+ )
49+ assert isinstance (redis_lock_key , str ) # nosec
50+
51+ redis_client = redis (* args , ** kwargs ) if callable (redis ) else redis
52+ assert isinstance (redis_client , RedisClientSDK ) # nosec
53+
54+ async with redis_client .lock_context (
55+ lock_key = redis_lock_key , lock_value = lock_value
56+ ):
3357 return await func (* args , ** kwargs )
3458
3559 return wrapper
0 commit comments