ohra 0.1.0__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.
Files changed (48) hide show
  1. ohra/__init__.py +215 -0
  2. ohra/behavior.py +215 -0
  3. ohra/cell.py +667 -0
  4. ohra/cluster/__init__.py +72 -0
  5. ohra/cluster/core.py +1656 -0
  6. ohra/cluster/failure_detector.py +304 -0
  7. ohra/cluster/gossip.py +412 -0
  8. ohra/cluster/reachability.py +183 -0
  9. ohra/cluster/sbr.py +802 -0
  10. ohra/cluster/sharding/__init__.py +130 -0
  11. ohra/cluster/sharding/coordinator.py +817 -0
  12. ohra/cluster/sharding/region.py +392 -0
  13. ohra/cluster/sharding/shard.py +511 -0
  14. ohra/cluster/singleton.py +1007 -0
  15. ohra/cluster/vector_clock.py +46 -0
  16. ohra/context.py +163 -0
  17. ohra/coordinated_shutdown.py +224 -0
  18. ohra/discovery.py +59 -0
  19. ohra/ext/__init__.py +2 -0
  20. ohra/ext/fastapi.py +59 -0
  21. ohra/ext/postgres.py +140 -0
  22. ohra/ext/shm.py +80 -0
  23. ohra/mailbox.py +31 -0
  24. ohra/management/__init__.py +195 -0
  25. ohra/management/bootstrap.py +690 -0
  26. ohra/management/cluster_http.py +109 -0
  27. ohra/management/health.py +76 -0
  28. ohra/management/http.py +228 -0
  29. ohra/management/kubernetes.py +181 -0
  30. ohra/patterns.py +138 -0
  31. ohra/persistence.py +276 -0
  32. ohra/py.typed +0 -0
  33. ohra/receptionist.py +372 -0
  34. ohra/ref.py +95 -0
  35. ohra/remote/__init__.py +18 -0
  36. ohra/remote/remoting.py +442 -0
  37. ohra/remote/serialization.py +164 -0
  38. ohra/result.py +19 -0
  39. ohra/routers.py +73 -0
  40. ohra/signals.py +49 -0
  41. ohra/supervision.py +81 -0
  42. ohra/system.py +236 -0
  43. ohra-0.1.0.dist-info/METADATA +175 -0
  44. ohra-0.1.0.dist-info/RECORD +48 -0
  45. ohra-0.1.0.dist-info/WHEEL +5 -0
  46. ohra-0.1.0.dist-info/licenses/LICENSE +201 -0
  47. ohra-0.1.0.dist-info/licenses/NOTICE +39 -0
  48. ohra-0.1.0.dist-info/top_level.txt +1 -0
ohra/__init__.py ADDED
@@ -0,0 +1,215 @@
1
+ """Apache Pekko Typed semantics on asyncio."""
2
+
3
+ from .behavior import Behavior, Behaviors, Receive, StashBuffer, StashOverflowError
4
+ from .cell import TimerScheduler
5
+ from .cluster.core import (
6
+ Cluster,
7
+ ClusterDomainEvent,
8
+ ClusterSettings,
9
+ CurrentClusterState,
10
+ Down,
11
+ GetCurrentState,
12
+ Join,
13
+ JoinSeedNodes,
14
+ LeaderChanged,
15
+ Leave,
16
+ MemberDowned,
17
+ MemberEvent,
18
+ MemberExited,
19
+ MemberJoined,
20
+ MemberLeft,
21
+ MemberRemoved,
22
+ MemberUp,
23
+ MemberWeaklyUp,
24
+ ReachabilityEvent,
25
+ ReachableMember,
26
+ SelfRemoved,
27
+ SelfUp,
28
+ Subscribe,
29
+ UnreachableMember,
30
+ Unsubscribe,
31
+ )
32
+ from .cluster.gossip import Member
33
+ from .cluster.sharding import (
34
+ ClusterSharding,
35
+ ClusterShardingSettings,
36
+ CurrentShardRegionState,
37
+ Entity,
38
+ EntityContext,
39
+ EntityRef,
40
+ EntityTypeKey,
41
+ GetShardRegionState,
42
+ Passivate,
43
+ ShardCommand,
44
+ Sharding,
45
+ ShardingEnvelope,
46
+ )
47
+ from .cluster.sbr import (
48
+ DownAll,
49
+ KeepMajority,
50
+ KeepOldest,
51
+ SplitBrainResolverSettings,
52
+ StaticQuorum,
53
+ )
54
+ from .cluster.singleton import ClusterSingleton, ClusterSingletonSettings, SingletonActor
55
+ from .context import ActorContext
56
+ from .discovery import ConfigServiceDiscovery, Lookup, Resolved, ResolvedTarget, ServiceDiscovery
57
+ from .coordinated_shutdown import (
58
+ ActorSystemTerminateReason,
59
+ ClusterDowningReason,
60
+ ClusterLeavingReason,
61
+ CoordinatedShutdown,
62
+ CoordinatedShutdownSettings,
63
+ JvmExitReason,
64
+ Reason,
65
+ )
66
+ from .mailbox import MailboxOverflowError, MailboxSelector
67
+ from .management import Management, ManagementSettings
68
+ from .management.bootstrap import ClusterBootstrap, ClusterBootstrapSettings
69
+ from .management.kubernetes import KubernetesApiServiceDiscovery, KubernetesApiSettings
70
+ from .management.health import HealthCheckSettings
71
+ from .patterns import AskTimeoutError, Replyable, SpawnProtocol, ask, batching
72
+ from .receptionist import (
73
+ Deregister,
74
+ Deregistered,
75
+ Find,
76
+ Listing,
77
+ Receptionist,
78
+ Register,
79
+ Registered,
80
+ ServiceKey,
81
+ )
82
+ from .persistence import (
83
+ InMemoryStore,
84
+ Persist,
85
+ Persisted,
86
+ PersistenceError,
87
+ RevisionConflictError,
88
+ SqliteStore,
89
+ Store,
90
+ )
91
+ from .ref import ActorRef
92
+ from .remote import Address, RemoteRef, UniqueAddress
93
+ from .remote.serialization import Serialization, SerializationError, remotable
94
+ from .result import Failure, Success, Try
95
+ from .routers import PoolRouter, Routers
96
+ from .signals import ChildFailed, DeathPactError, PostStop, PreRestart, Signal, Terminated
97
+ from .supervision import SupervisorStrategy
98
+ from .system import ActorSystem
99
+
100
+ __all__ = [
101
+ "ActorContext",
102
+ "ActorRef",
103
+ "ActorSystem",
104
+ "ActorSystemTerminateReason",
105
+ "Address",
106
+ "AskTimeoutError",
107
+ "Behavior",
108
+ "Behaviors",
109
+ "ChildFailed",
110
+ "Cluster",
111
+ "ClusterBootstrap",
112
+ "ClusterBootstrapSettings",
113
+ "ClusterDomainEvent",
114
+ "ClusterDowningReason",
115
+ "ClusterLeavingReason",
116
+ "ClusterSettings",
117
+ "ClusterSharding",
118
+ "ClusterShardingSettings",
119
+ "ClusterSingleton",
120
+ "ClusterSingletonSettings",
121
+ "ConfigServiceDiscovery",
122
+ "CoordinatedShutdown",
123
+ "CoordinatedShutdownSettings",
124
+ "CurrentClusterState",
125
+ "CurrentShardRegionState",
126
+ "DeathPactError",
127
+ "Deregister",
128
+ "Deregistered",
129
+ "Down",
130
+ "DownAll",
131
+ "Entity",
132
+ "EntityContext",
133
+ "EntityRef",
134
+ "EntityTypeKey",
135
+ "Failure",
136
+ "Find",
137
+ "GetCurrentState",
138
+ "GetShardRegionState",
139
+ "HealthCheckSettings",
140
+ "InMemoryStore",
141
+ "Join",
142
+ "JoinSeedNodes",
143
+ "JvmExitReason",
144
+ "KubernetesApiServiceDiscovery",
145
+ "KubernetesApiSettings",
146
+ "KeepMajority",
147
+ "KeepOldest",
148
+ "LeaderChanged",
149
+ "Leave",
150
+ "Listing",
151
+ "Lookup",
152
+ "MailboxOverflowError",
153
+ "MailboxSelector",
154
+ "Member",
155
+ "MemberDowned",
156
+ "MemberEvent",
157
+ "MemberExited",
158
+ "MemberJoined",
159
+ "MemberLeft",
160
+ "MemberRemoved",
161
+ "MemberUp",
162
+ "MemberWeaklyUp",
163
+ "Passivate",
164
+ "Management",
165
+ "ManagementSettings",
166
+ "Persist",
167
+ "Persisted",
168
+ "PersistenceError",
169
+ "PoolRouter",
170
+ "PostStop",
171
+ "PreRestart",
172
+ "ReachabilityEvent",
173
+ "ReachableMember",
174
+ "Reason",
175
+ "Receive",
176
+ "Receptionist",
177
+ "Register",
178
+ "Registered",
179
+ "RemoteRef",
180
+ "Replyable",
181
+ "Resolved",
182
+ "ResolvedTarget",
183
+ "RevisionConflictError",
184
+ "Routers",
185
+ "SelfRemoved",
186
+ "SelfUp",
187
+ "Serialization",
188
+ "SerializationError",
189
+ "ServiceDiscovery",
190
+ "ServiceKey",
191
+ "ShardCommand",
192
+ "Sharding",
193
+ "ShardingEnvelope",
194
+ "Signal",
195
+ "SingletonActor",
196
+ "SplitBrainResolverSettings",
197
+ "StaticQuorum",
198
+ "SpawnProtocol",
199
+ "SqliteStore",
200
+ "StashBuffer",
201
+ "StashOverflowError",
202
+ "Store",
203
+ "Subscribe",
204
+ "Success",
205
+ "SupervisorStrategy",
206
+ "Terminated",
207
+ "TimerScheduler",
208
+ "Try",
209
+ "UniqueAddress",
210
+ "UnreachableMember",
211
+ "Unsubscribe",
212
+ "ask",
213
+ "batching",
214
+ "remotable",
215
+ ]
ohra/behavior.py ADDED
@@ -0,0 +1,215 @@
1
+ """Behaviors: values describing how an actor reacts to the next message.
2
+
3
+ Mirrors org.apache.pekko.actor.typed.scaladsl.Behaviors. A handler returns the
4
+ next behavior; returning ``Behaviors.same`` keeps the current one.
5
+ Handlers may be plain functions or coroutines.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from collections import deque
11
+ from collections.abc import Awaitable, Callable
12
+ from dataclasses import dataclass
13
+ from typing import TYPE_CHECKING, Any
14
+
15
+ from .signals import Signal
16
+ from .supervision import SupervisorStrategy
17
+
18
+ if TYPE_CHECKING:
19
+ from .persistence import Store, _Persister # pyright: ignore[reportPrivateUsage]
20
+ from .cell import TimerScheduler
21
+ from .context import ActorContext
22
+
23
+ type MaybeAwaitable[X] = X | Awaitable[X]
24
+
25
+
26
+ class Behavior[T]:
27
+ """Base of all behaviors. T is the message type the actor accepts."""
28
+
29
+ __slots__ = ()
30
+
31
+ def _accepts(self, msg: T) -> None: # pins T as contravariant, never called
32
+ raise NotImplementedError
33
+
34
+
35
+ class _Same(Behavior[Any]):
36
+ __slots__ = ()
37
+
38
+ def __repr__(self) -> str:
39
+ return "Behaviors.same"
40
+
41
+
42
+ class _Unhandled(Behavior[Any]):
43
+ __slots__ = ()
44
+
45
+ def __repr__(self) -> str:
46
+ return "Behaviors.unhandled"
47
+
48
+
49
+ @dataclass(frozen=True, eq=False, slots=True)
50
+ class Stopped(Behavior[Any]):
51
+ post_stop: Callable[[], None] | None = None
52
+
53
+
54
+ @dataclass(frozen=True, eq=False, slots=True)
55
+ class Setup[T](Behavior[T]):
56
+ factory: Callable[[ActorContext[T]], MaybeAwaitable[Behavior[T]]]
57
+
58
+
59
+ @dataclass(frozen=True, eq=False, slots=True)
60
+ class Receive[T](Behavior[T]):
61
+ on_message: Callable[[ActorContext[T], T], MaybeAwaitable[Behavior[T]]]
62
+ on_signal: Callable[[ActorContext[T], Signal], MaybeAwaitable[Behavior[T]]] | None = None
63
+
64
+ def receive_signal(
65
+ self, handler: Callable[[ActorContext[T], Signal], MaybeAwaitable[Behavior[T]]]
66
+ ) -> Receive[T]:
67
+ return Receive(self.on_message, handler)
68
+
69
+
70
+ @dataclass(frozen=True, eq=False, slots=True)
71
+ class WithTimers[T](Behavior[T]):
72
+ factory: Callable[[TimerScheduler[T]], MaybeAwaitable[Behavior[T]]]
73
+
74
+
75
+ @dataclass(frozen=True, eq=False, slots=True)
76
+ class Unstash[T](Behavior[T]):
77
+ """Returned by StashBuffer.unstash_all: the runtime replays ``messages`` into ``behavior``."""
78
+
79
+ messages: deque[T]
80
+ behavior: Behavior[T]
81
+
82
+
83
+ class StashOverflowError(Exception):
84
+ """Raised by StashBuffer.stash when the buffer is full."""
85
+
86
+
87
+ class StashBuffer[T]:
88
+ """Pekko's StashBuffer. Create one with ``Behaviors.with_stash``."""
89
+
90
+ __slots__ = ("_capacity", "_messages")
91
+
92
+ def __init__(self, capacity: int) -> None:
93
+ self._capacity = capacity
94
+ self._messages: deque[T] = deque()
95
+
96
+ @property
97
+ def size(self) -> int:
98
+ return len(self._messages)
99
+
100
+ @property
101
+ def is_empty(self) -> bool:
102
+ return not self._messages
103
+
104
+ @property
105
+ def is_full(self) -> bool:
106
+ return len(self._messages) >= self._capacity
107
+
108
+ def stash(self, msg: T) -> None:
109
+ if self.is_full:
110
+ raise StashOverflowError(f"stash buffer is full ({self._capacity})")
111
+ self._messages.append(msg)
112
+
113
+ def unstash_all(self, behavior: Behavior[T]) -> Behavior[T]:
114
+ """Process the stashed messages, in order, with ``behavior`` before any new message.
115
+
116
+ Messages stashed during the replay stay in the buffer. Behaves like
117
+ ``Behaviors.same`` once the buffer is empty."""
118
+ return Unstash(self._messages, behavior)
119
+
120
+
121
+ @dataclass(frozen=True, eq=False, slots=True)
122
+ class Supervised[T](Behavior[T]):
123
+ """Template: the runtime wraps it in a supervisor when the actor starts."""
124
+
125
+ initial: Behavior[T]
126
+ strategy: SupervisorStrategy
127
+ exc_type: type[Exception] = Exception
128
+
129
+
130
+ class _SuperviseBuilder[T]:
131
+ __slots__ = ("_behavior",)
132
+
133
+ def __init__(self, behavior: Behavior[T]) -> None:
134
+ self._behavior = behavior
135
+
136
+ def on_failure(
137
+ self, strategy: SupervisorStrategy, exc_type: type[Exception] = Exception
138
+ ) -> Behavior[T]:
139
+ return Supervised(self._behavior, strategy, exc_type)
140
+
141
+
142
+ SAME: Behavior[Any] = _Same()
143
+ UNHANDLED: Behavior[Any] = _Unhandled()
144
+
145
+
146
+ def _unhandled_message(_ctx: Any, _msg: Any) -> Behavior[Any]:
147
+ return UNHANDLED
148
+
149
+
150
+ def _ignore_message(_ctx: Any, _msg: Any) -> Behavior[Any]:
151
+ return SAME
152
+
153
+
154
+ class Behaviors:
155
+ """Factory namespace, named after the Scala DSL."""
156
+
157
+ same: Behavior[Any] = SAME
158
+ unhandled: Behavior[Any] = UNHANDLED
159
+ empty: Behavior[Any] = Receive(_unhandled_message)
160
+ ignore: Behavior[Any] = Receive(_ignore_message)
161
+
162
+ @staticmethod
163
+ def stopped(post_stop: Callable[[], None] | None = None) -> Behavior[Any]:
164
+ return Stopped(post_stop)
165
+
166
+ @staticmethod
167
+ def setup[T](factory: Callable[[ActorContext[T]], MaybeAwaitable[Behavior[T]]]) -> Behavior[T]:
168
+ return Setup(factory)
169
+
170
+ @staticmethod
171
+ def receive[T](
172
+ handler: Callable[[ActorContext[T], T], MaybeAwaitable[Behavior[T]]],
173
+ ) -> Receive[T]:
174
+ return Receive(handler)
175
+
176
+ @staticmethod
177
+ def receive_signal[T](
178
+ handler: Callable[[ActorContext[T], Signal], MaybeAwaitable[Behavior[T]]],
179
+ ) -> Receive[T]:
180
+ """Handle signals only; messages are unhandled."""
181
+ return Receive(_unhandled_message, handler)
182
+
183
+ @staticmethod
184
+ def receive_message[T](handler: Callable[[T], MaybeAwaitable[Behavior[T]]]) -> Receive[T]:
185
+ def on_message(_ctx: ActorContext[T], msg: T) -> MaybeAwaitable[Behavior[T]]:
186
+ return handler(msg)
187
+
188
+ return Receive(on_message)
189
+
190
+ @staticmethod
191
+ def supervise[T](behavior: Behavior[T]) -> _SuperviseBuilder[T]:
192
+ return _SuperviseBuilder(behavior)
193
+
194
+ @staticmethod
195
+ def persisted[S](store: Store[S]) -> _Persister[S]:
196
+ """Make a state-to-behavior factory persisted: ``@Behaviors.persisted(store)`` on ``def
197
+ cart(items)``. Calling the factory writes the state before becoming; ``factory.recover(id,
198
+ empty)`` starts it."""
199
+ from .persistence import persisted
200
+
201
+ return persisted(store)
202
+
203
+ @staticmethod
204
+ def with_timers[T](
205
+ factory: Callable[[TimerScheduler[T]], MaybeAwaitable[Behavior[T]]],
206
+ ) -> Behavior[T]:
207
+ """Give the behavior access to the actor's timers. All timers are cancelled on restart."""
208
+ return WithTimers(factory)
209
+
210
+ @staticmethod
211
+ def with_stash[T](
212
+ capacity: int, factory: Callable[[StashBuffer[T]], MaybeAwaitable[Behavior[T]]]
213
+ ) -> Behavior[T]:
214
+ """Give the behavior a fresh StashBuffer per incarnation."""
215
+ return Setup(lambda _ctx: factory(StashBuffer(capacity)))