flowevent 1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,116 @@
1
+ """Subscription ownership for synchronous and asynchronous event buses."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import threading
7
+ from collections.abc import Awaitable, Callable
8
+
9
+
10
+ class Subscription:
11
+ def __init__(self, subscription_id: int, cancel: Callable[[], None]) -> None:
12
+ self._id = subscription_id
13
+ self._cancel = cancel
14
+ self._active = True
15
+ self._lock = threading.Lock()
16
+
17
+ @property
18
+ def id(self) -> int:
19
+ return self._id
20
+
21
+ @property
22
+ def active(self) -> bool:
23
+ with self._lock:
24
+ return self._active
25
+
26
+ def _deactivate(self) -> bool:
27
+ with self._lock:
28
+ if not self._active:
29
+ return False
30
+ self._active = False
31
+ return True
32
+
33
+ def cancel(self) -> None:
34
+ self._consume()
35
+
36
+ def _consume(self) -> bool:
37
+ if not self._deactivate():
38
+ return False
39
+ try:
40
+ self._cancel()
41
+ except BaseException:
42
+ with self._lock:
43
+ self._active = True
44
+ raise
45
+ return True
46
+
47
+ def __enter__(self) -> "Subscription":
48
+ return self
49
+
50
+ def __exit__(self, *_args: object) -> None:
51
+ self.cancel()
52
+
53
+
54
+ class AsyncSubscription:
55
+ def __init__(
56
+ self,
57
+ subscription_id: int,
58
+ cancel: Callable[[], Awaitable[None]],
59
+ ) -> None:
60
+ self._id = subscription_id
61
+ self._cancel = cancel
62
+ self._active = True
63
+ self._cancel_completion: asyncio.Future[BaseException | None] | None = None
64
+ self._cancel_owner: asyncio.Task | None = None
65
+
66
+ @property
67
+ def id(self) -> int:
68
+ return self._id
69
+
70
+ @property
71
+ def active(self) -> bool:
72
+ return self._active
73
+
74
+ def _deactivate(self) -> bool:
75
+ if not self._active:
76
+ return False
77
+ self._active = False
78
+ return True
79
+
80
+ async def cancel(self) -> None:
81
+ await self._consume()
82
+
83
+ async def _consume(self) -> bool:
84
+ completion = self._cancel_completion
85
+ if not self._active:
86
+ if completion is None:
87
+ return False
88
+ if self._cancel_owner is asyncio.current_task():
89
+ return False
90
+ error = await asyncio.shield(completion)
91
+ if error is not None:
92
+ raise error
93
+ return False
94
+
95
+ self._active = False
96
+ completion = asyncio.get_running_loop().create_future()
97
+ self._cancel_completion = completion
98
+ self._cancel_owner = asyncio.current_task()
99
+ try:
100
+ await self._cancel()
101
+ except BaseException as error:
102
+ self._active = True
103
+ self._cancel_completion = None
104
+ self._cancel_owner = None
105
+ completion.set_result(error)
106
+ raise
107
+ self._cancel_completion = None
108
+ self._cancel_owner = None
109
+ completion.set_result(None)
110
+ return True
111
+
112
+ async def __aenter__(self) -> "AsyncSubscription":
113
+ return self
114
+
115
+ async def __aexit__(self, *_args: object) -> None:
116
+ await self.cancel()