Skip to content

Commit 914f68c

Browse files
committed
docs: add types to recipe.lock
1 parent 410b890 commit 914f68c

File tree

4 files changed

+100
-48
lines changed

4 files changed

+100
-48
lines changed

kazoo/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -922,7 +922,7 @@ def create(
922922
sequence=False,
923923
makepath=False,
924924
include_data=False,
925-
):
925+
) -> str:
926926
"""Create a node with the given value as its data. Optionally
927927
set an ACL on the node.
928928

kazoo/protocol/states.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
"""Kazoo State and Event objects"""
22
from collections import namedtuple
3+
from enum import Enum
34

45

5-
class KazooState(object):
6+
class KazooState(str, Enum):
67
"""High level connection state values
78
89
States inspired by Netflix Curator.

kazoo/recipe/lock.py

Lines changed: 95 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@
1414
and/or the lease has been lost.
1515
1616
"""
17+
from __future__ import annotations
18+
1719
import re
1820
import sys
21+
from typing import TYPE_CHECKING, cast, ContextManager
22+
import uuid
1923

2024
try:
2125
from time import monotonic as now
2226
except ImportError:
2327
from time import time as now
24-
import uuid
2528

2629
import six
2730

@@ -38,24 +41,37 @@
3841
RetryFailedError,
3942
)
4043

44+
if TYPE_CHECKING:
45+
from kazoo.client import KazooClient
46+
from kazoo.protocol.states import WatchedEvent
47+
from typing import (
48+
Any,
49+
List,
50+
Optional,
51+
Sequence,
52+
Type,
53+
Union,
54+
)
55+
from typing_extensions import Literal
56+
4157

4258
class _Watch(object):
43-
def __init__(self, duration=None):
59+
def __init__(self, duration: Optional[float] = None):
4460
self.duration = duration
45-
self.started_at = None
61+
self.started_at: Optional[float] = None
4662

47-
def start(self):
63+
def start(self) -> None:
4864
self.started_at = now()
4965

50-
def leftover(self):
66+
def leftover(self) -> Optional[float]:
5167
if self.duration is None:
5268
return None
5369
else:
54-
elapsed = now() - self.started_at
70+
elapsed = now() - cast(float, self.started_at)
5571
return max(0, self.duration - elapsed)
5672

5773

58-
class Lock(object):
74+
class Lock(ContextManager[None]):
5975
"""Kazoo Lock
6076
6177
Example usage with a :class:`~kazoo.client.KazooClient` instance:
@@ -84,7 +100,13 @@ class Lock(object):
84100
# sequence number. Involved in read/write locks.
85101
_EXCLUDE_NAMES = ["__lock__"]
86102

87-
def __init__(self, client, path, identifier=None, extra_lock_patterns=()):
103+
def __init__(
104+
self,
105+
client: KazooClient,
106+
path: str,
107+
identifier: Optional[str] = None,
108+
extra_lock_patterns: Sequence[str] = (),
109+
):
88110
"""Create a Kazoo lock.
89111
90112
:param client: A :class:`~kazoo.client.KazooClient` instance.
@@ -116,7 +138,7 @@ def __init__(self, client, path, identifier=None, extra_lock_patterns=()):
116138
# some data is written to the node. this can be queried via
117139
# contenders() to see who is contending for the lock
118140
self.data = str(identifier or "").encode("utf-8")
119-
self.node = None
141+
self.node: Optional[str] = None
120142

121143
self.wake_event = client.handler.event_object()
122144

@@ -132,20 +154,25 @@ def __init__(self, client, path, identifier=None, extra_lock_patterns=()):
132154
self.assured_path = False
133155
self.cancelled = False
134156
self._retry = KazooRetry(
135-
max_tries=None, sleep_func=client.handler.sleep_func
157+
max_tries=-1, sleep_func=client.handler.sleep_func
136158
)
137159
self._acquire_method_lock = client.handler.lock_object()
138160

139-
def _ensure_path(self):
161+
def _ensure_path(self) -> None:
140162
self.client.ensure_path(self.path)
141163
self.assured_path = True
142164

143-
def cancel(self):
165+
def cancel(self) -> None:
144166
"""Cancel a pending lock acquire."""
145167
self.cancelled = True
146168
self.wake_event.set()
147169

148-
def acquire(self, blocking=True, timeout=None, ephemeral=True):
170+
def acquire(
171+
self,
172+
blocking: bool = True,
173+
timeout: Optional[float] = None,
174+
ephemeral: bool = True,
175+
) -> bool:
149176
"""
150177
Acquire the lock. By defaults blocks and waits forever.
151178
@@ -212,11 +239,13 @@ def acquire(self, blocking=True, timeout=None, ephemeral=True):
212239
finally:
213240
self._acquire_method_lock.release()
214241

215-
def _watch_session(self, state):
242+
def _watch_session(self, state: KazooState) -> bool:
216243
self.wake_event.set()
217244
return True
218245

219-
def _inner_acquire(self, blocking, timeout, ephemeral=True):
246+
def _inner_acquire(
247+
self, blocking: bool, timeout: Optional[float], ephemeral: bool = True
248+
) -> bool:
220249

221250
# wait until it's our chance to get it..
222251
if self.is_acquired:
@@ -274,10 +303,10 @@ def _inner_acquire(self, blocking, timeout, ephemeral=True):
274303
finally:
275304
self.client.remove_listener(self._watch_session)
276305

277-
def _watch_predecessor(self, event):
306+
def _watch_predecessor(self, event: WatchedEvent) -> None:
278307
self.wake_event.set()
279308

280-
def _get_predecessor(self, node):
309+
def _get_predecessor(self, node: str) -> Optional[str]:
281310
"""returns `node`'s predecessor or None
282311
283312
Note: This handle the case where the current lock is not a contender
@@ -286,7 +315,7 @@ def _get_predecessor(self, node):
286315
"""
287316
node_sequence = node[len(self.prefix) :]
288317
children = self.client.get_children(self.path)
289-
found_self = False
318+
found_self: Union[Literal[False], re.Match[bytes]] = False
290319
# Filter out the contenders using the computed regex
291320
contender_matches = []
292321
for child in children:
@@ -301,7 +330,7 @@ def _get_predecessor(self, node):
301330
if child == node:
302331
# Remember the node's match object so we can short circuit
303332
# below.
304-
found_self = match
333+
found_self = cast(re.Match[bytes], match)
305334

306335
if found_self is False: # pragma: nocover
307336
# somehow we aren't in the childrens -- probably we are
@@ -317,42 +346,42 @@ def _get_predecessor(self, node):
317346
sorted_matches = sorted(contender_matches, key=lambda m: m.groups())
318347
return sorted_matches[-1].string
319348

320-
def _find_node(self):
349+
def _find_node(self) -> Optional[str]:
321350
children = self.client.get_children(self.path)
322351
for child in children:
323352
if child.startswith(self.prefix):
324353
return child
325354
return None
326355

327-
def _delete_node(self, node):
356+
def _delete_node(self, node: str) -> None:
328357
self.client.delete(self.path + "/" + node)
329358

330-
def _best_effort_cleanup(self):
359+
def _best_effort_cleanup(self) -> None:
331360
try:
332361
node = self.node or self._find_node()
333362
if node:
334363
self._delete_node(node)
335364
except KazooException: # pragma: nocover
336365
pass
337366

338-
def release(self):
367+
def release(self) -> bool:
339368
"""Release the lock immediately."""
340369
return self.client.retry(self._inner_release)
341370

342-
def _inner_release(self):
371+
def _inner_release(self) -> bool:
343372
if not self.is_acquired:
344373
return False
345374

346375
try:
347-
self._delete_node(self.node)
376+
self._delete_node(cast(str, self.node))
348377
except NoNodeError: # pragma: nocover
349378
pass
350379

351380
self.is_acquired = False
352381
self.node = None
353382
return True
354383

355-
def contenders(self):
384+
def contenders(self) -> List[str]:
356385
"""Return an ordered list of the current contenders for the
357386
lock.
358387
@@ -388,7 +417,7 @@ def contenders(self):
388417
for match in sorted(contender_matches, key=lambda m: m.groups())
389418
]
390419
# Retrieve all the contender nodes data (preserving order).
391-
contenders = []
420+
contenders: List[str] = []
392421
for node in contender_nodes:
393422
try:
394423
data, stat = self.client.get(self.path + "/" + node)
@@ -399,10 +428,15 @@ def contenders(self):
399428

400429
return contenders
401430

402-
def __enter__(self):
431+
def __enter__(self) -> None:
403432
self.acquire()
404433

405-
def __exit__(self, exc_type, exc_value, traceback):
434+
def __exit__(
435+
self,
436+
exc_type: Optional[Type[BaseException]],
437+
exc_value: Optional[BaseException],
438+
traceback: Any,
439+
) -> None:
406440
self.release()
407441

408442

@@ -466,7 +500,7 @@ class ReadLock(Lock):
466500
_EXCLUDE_NAMES = ["__lock__"]
467501

468502

469-
class Semaphore(object):
503+
class Semaphore(ContextManager[None]):
470504
"""A Zookeeper-based Semaphore
471505
472506
This synchronization primitive operates in the same manner as the
@@ -501,7 +535,13 @@ class Semaphore(object):
501535
502536
"""
503537

504-
def __init__(self, client, path, identifier=None, max_leases=1):
538+
def __init__(
539+
self,
540+
client: KazooClient,
541+
path: str,
542+
identifier: Optional[str] = None,
543+
max_leases: int = 1,
544+
):
505545
"""Create a Kazoo Lock
506546
507547
:param client: A :class:`~kazoo.client.KazooClient` instance.
@@ -537,7 +577,7 @@ def __init__(self, client, path, identifier=None, max_leases=1):
537577
self.cancelled = False
538578
self._session_expired = False
539579

540-
def _ensure_path(self):
580+
def _ensure_path(self) -> None:
541581
result = self.client.ensure_path(self.path)
542582
self.assured_path = True
543583
if result is True:
@@ -558,12 +598,14 @@ def _ensure_path(self):
558598
else:
559599
self.client.set(self.path, str(self.max_leases).encode("utf-8"))
560600

561-
def cancel(self):
601+
def cancel(self) -> None:
562602
"""Cancel a pending semaphore acquire."""
563603
self.cancelled = True
564604
self.wake_event.set()
565605

566-
def acquire(self, blocking=True, timeout=None):
606+
def acquire(
607+
self, blocking: bool = True, timeout: Optional[float] = None
608+
) -> bool:
567609
"""Acquire the semaphore. By defaults blocks and waits forever.
568610
569611
:param blocking: Block until semaphore is obtained or
@@ -601,7 +643,9 @@ def acquire(self, blocking=True, timeout=None):
601643

602644
return self.is_acquired
603645

604-
def _inner_acquire(self, blocking, timeout=None):
646+
def _inner_acquire(
647+
self, blocking: bool, timeout: Optional[float] = None
648+
) -> bool:
605649
"""Inner loop that runs from the top anytime a command hits a
606650
retryable Zookeeper exception."""
607651
self._session_expired = False
@@ -642,10 +686,10 @@ def _inner_acquire(self, blocking, timeout=None):
642686
finally:
643687
lock.release()
644688

645-
def _watch_lease_change(self, event):
689+
def _watch_lease_change(self, event: WatchedEvent) -> None:
646690
self.wake_event.set()
647691

648-
def _get_lease(self, data=None):
692+
def _get_lease(self) -> bool:
649693
# Make sure the session is still valid
650694
if self._session_expired:
651695
raise ForceRetryError("Retry on session loss at top")
@@ -674,25 +718,26 @@ def _get_lease(self, data=None):
674718
# Return current state
675719
return self.is_acquired
676720

677-
def _watch_session(self, state):
721+
def _watch_session(self, state: KazooState) -> Optional[Literal[True]]:
678722
if state == KazooState.LOST:
679723
self._session_expired = True
680724
self.wake_event.set()
681725

682726
# Return true to de-register
683727
return True
728+
return None
684729

685-
def _best_effort_cleanup(self):
730+
def _best_effort_cleanup(self) -> None:
686731
try:
687732
self.client.delete(self.create_path)
688733
except KazooException: # pragma: nocover
689734
pass
690735

691-
def release(self):
736+
def release(self) -> bool:
692737
"""Release the lease immediately."""
693738
return self.client.retry(self._inner_release)
694739

695-
def _inner_release(self):
740+
def _inner_release(self) -> bool:
696741
if not self.is_acquired:
697742
return False
698743
try:
@@ -702,7 +747,7 @@ def _inner_release(self):
702747
self.is_acquired = False
703748
return True
704749

705-
def lease_holders(self):
750+
def lease_holders(self) -> List[str]:
706751
"""Return an unordered list of the current lease holders.
707752
708753
.. note::
@@ -716,7 +761,7 @@ def lease_holders(self):
716761

717762
children = self.client.get_children(self.path)
718763

719-
lease_holders = []
764+
lease_holders: List[str] = []
720765
for child in children:
721766
try:
722767
data, stat = self.client.get(self.path + "/" + child)
@@ -725,8 +770,13 @@ def lease_holders(self):
725770
pass
726771
return lease_holders
727772

728-
def __enter__(self):
773+
def __enter__(self) -> None:
729774
self.acquire()
730775

731-
def __exit__(self, exc_type, exc_value, traceback):
776+
def __exit__(
777+
self,
778+
exc_type: Optional[Type[BaseException]],
779+
exc_value: Optional[BaseException],
780+
traceback: Any,
781+
) -> None:
732782
self.release()

0 commit comments

Comments
 (0)