asyncactor 0.25.3__tar.gz → 0.26.1__tar.gz
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.
- {asyncactor-0.25.3 → asyncactor-0.26.1}/PKG-INFO +3 -2
- {asyncactor-0.25.3 → asyncactor-0.26.1}/asyncactor/actor.py +11 -8
- {asyncactor-0.25.3 → asyncactor-0.26.1}/asyncactor/backend/moat_link.py +4 -4
- {asyncactor-0.25.3 → asyncactor-0.26.1}/asyncactor/events.py +5 -6
- {asyncactor-0.25.3 → asyncactor-0.26.1}/asyncactor/messages.py +3 -3
- {asyncactor-0.25.3 → asyncactor-0.26.1}/asyncactor/nodelist.py +1 -1
- {asyncactor-0.25.3 → asyncactor-0.26.1}/asyncactor.egg-info/PKG-INFO +3 -2
- {asyncactor-0.25.3 → asyncactor-0.26.1}/docs/source/actor.rst +17 -3
- {asyncactor-0.25.3 → asyncactor-0.26.1}/.coveragerc +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/.gitignore +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/.pylintrc +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/.readthedocs.yml +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/.style.yapf +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/.travis.yml +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/CHANGELOG.rst +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/LICENSE +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/Makefile +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/README.rst +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/asyncactor/__init__.py +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/asyncactor/abc.py +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/asyncactor/backend/__init__.py +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/asyncactor/backend/moat_kv.py +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/asyncactor/backend/mqtt.py +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/asyncactor/backend/mqttproto.py +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/asyncactor/backend/serf.py +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/asyncactor/exceptions.py +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/asyncactor.egg-info/SOURCES.txt +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/asyncactor.egg-info/dependency_links.txt +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/asyncactor.egg-info/requires.txt +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/asyncactor.egg-info/top_level.txt +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/ci/rtd-requirements.txt +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/ci/test-requirements.txt +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/ci/travis.sh +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/docs/Makefile +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/docs/make.bat +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/docs/source/_static/.git_empty +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/docs/source/conf.py +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/docs/source/history.rst +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/docs/source/index.rst +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/docs/source/rationale.rst +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/mktag +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/pyproject.toml +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/setup.cfg +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/tests/__init__.py +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/tests/mock_serf.py +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/tests/test_actor.py +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/tests/test_mosquitto.py +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/tests/test_mqttproto.py +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/tests/test_serf.py +0 -0
- {asyncactor-0.25.3 → asyncactor-0.26.1}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: asyncactor
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.26.1
|
|
4
4
|
Summary: Async decentralized actor
|
|
5
5
|
Author-email: Matthias Urlichs <matthias@urlichs.de>
|
|
6
6
|
Project-URL: homepage, https://github.com/smurfix/asyncactor
|
|
@@ -32,6 +32,7 @@ Requires-Dist: pytest-cov>=2.3; extra == "dev"
|
|
|
32
32
|
Requires-Dist: pytest-trio; extra == "dev"
|
|
33
33
|
Requires-Dist: pytest>=2.5.2; extra == "dev"
|
|
34
34
|
Requires-Dist: trio>=0.23; extra == "dev"
|
|
35
|
+
Dynamic: license-file
|
|
35
36
|
|
|
36
37
|
asyncactor
|
|
37
38
|
==========
|
|
@@ -127,7 +127,7 @@ class Actor(CtxObj):
|
|
|
127
127
|
self._version = SetupMessage(node=self._name, **(cfg or {}))
|
|
128
128
|
self._version_job = None
|
|
129
129
|
self._off_nodes = self._version.nodes
|
|
130
|
-
self._self_seen =
|
|
130
|
+
self._self_seen = anyio.Event()
|
|
131
131
|
|
|
132
132
|
self._evt_q = create_queue(1)
|
|
133
133
|
self._rdr_q = create_queue(99)
|
|
@@ -315,10 +315,14 @@ class Actor(CtxObj):
|
|
|
315
315
|
await self.post_event(RawMsgEvent(msg))
|
|
316
316
|
msg = Message.read(msg)
|
|
317
317
|
if type(msg) is InitMessage and msg.node == self._name:
|
|
318
|
-
if self._self_seen:
|
|
319
|
-
# We may see our own
|
|
318
|
+
if self._self_seen.is_set():
|
|
319
|
+
# We may see our own Init only once.
|
|
320
320
|
raise ActorCollisionError
|
|
321
|
-
|
|
321
|
+
|
|
322
|
+
# This may not hold if your bronze-age MQTT server doesn't
|
|
323
|
+
# do subscription IDs. Sorry, but that's not supported.
|
|
324
|
+
|
|
325
|
+
self._self_seen.set()
|
|
322
326
|
|
|
323
327
|
await self._rdr_q.put(msg)
|
|
324
328
|
|
|
@@ -327,9 +331,8 @@ class Actor(CtxObj):
|
|
|
327
331
|
await self._send_msg(InitMessage(node=self._name))
|
|
328
332
|
await self._send_msg(self._version)
|
|
329
333
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
raise ActorTimeoutError
|
|
334
|
+
with anyio.fail_after((self.random / 2 + 1.5) * self._gap + self._cycle):
|
|
335
|
+
await self._self_seen.wait()
|
|
333
336
|
await self._send_ping()
|
|
334
337
|
|
|
335
338
|
t_dest = 0
|
|
@@ -711,7 +714,7 @@ class Actor(CtxObj):
|
|
|
711
714
|
"""
|
|
712
715
|
# For pos=0 this is a no-op and times out immediately
|
|
713
716
|
try:
|
|
714
|
-
|
|
717
|
+
with anyio.CancelScope() as xx:
|
|
715
718
|
if self._version_job is not None:
|
|
716
719
|
self._version_job.cancel()
|
|
717
720
|
self._version_job = xx
|
|
@@ -8,7 +8,7 @@ from asyncactor.abc import MonitorStream, Transport
|
|
|
8
8
|
|
|
9
9
|
from mqttproto import QoS
|
|
10
10
|
|
|
11
|
-
from moat.util import CtxObj, Path
|
|
11
|
+
from moat.util import CtxObj, Path, NotGiven
|
|
12
12
|
|
|
13
13
|
from typing import TYPE_CHECKING
|
|
14
14
|
|
|
@@ -26,7 +26,7 @@ class MQTTPTransport(Transport):
|
|
|
26
26
|
MQTTProto Transport for AsyncActor.
|
|
27
27
|
"""
|
|
28
28
|
|
|
29
|
-
def __init__(self, conn: Link, topic: Path, codec: Codec | str =
|
|
29
|
+
def __init__(self, conn: Link, topic: Path, codec: Codec | str = NotGiven):
|
|
30
30
|
self.conn = conn
|
|
31
31
|
self.topic = topic
|
|
32
32
|
self.codec = codec
|
|
@@ -48,7 +48,7 @@ class MQTTPTransport(Transport):
|
|
|
48
48
|
return self.conn.send(self.topic, payload, qos=QoS.AT_LEAST_ONCE, codec=self.codec)
|
|
49
49
|
|
|
50
50
|
def __repr__(self):
|
|
51
|
-
return f"<MQTT:
|
|
51
|
+
return f"<MQTT:{self.topic} @{self.conn!r}>"
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
class MQTTMonitor(MonitorStream, CtxObj):
|
|
@@ -56,7 +56,7 @@ class MQTTMonitor(MonitorStream, CtxObj):
|
|
|
56
56
|
|
|
57
57
|
async def _ctx(self) -> AsyncIterator[Self]:
|
|
58
58
|
c = self.transport
|
|
59
|
-
async with c.conn.monitor(c.
|
|
59
|
+
async with c.conn.monitor(c.topic, qos=QoS.AT_LEAST_ONCE, codec=c.codec) as mon:
|
|
60
60
|
self._it = aiter(mon)
|
|
61
61
|
try:
|
|
62
62
|
yield self
|
|
@@ -44,7 +44,7 @@ class AuthPingEvent(NodeEvent):
|
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
@define
|
|
47
|
-
class TagEvent
|
|
47
|
+
class TagEvent(AuthPingEvent): # [TNode]
|
|
48
48
|
"""
|
|
49
49
|
This event says that for the moment, you're "it".
|
|
50
50
|
|
|
@@ -69,7 +69,7 @@ class UntagEvent(NodeEvent):
|
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
@define
|
|
72
|
-
class DetagEvent
|
|
72
|
+
class DetagEvent(UntagEvent): # [TNode]
|
|
73
73
|
"""
|
|
74
74
|
A ping from another node has arrived while you're "it".
|
|
75
75
|
Unfortunately, it is "better" than ours.
|
|
@@ -186,8 +186,7 @@ class SetupEvent(NodeEvent):
|
|
|
186
186
|
force_in: bool = False
|
|
187
187
|
|
|
188
188
|
def __init__(self, msg):
|
|
189
|
-
|
|
190
|
-
self.version = 1
|
|
189
|
+
self.__attrs_init__()
|
|
191
190
|
for k in "version cycle gap nodes splits n_hosts".split():
|
|
192
|
-
with suppress(
|
|
193
|
-
setattr(self, k, msg
|
|
191
|
+
with suppress(AttributeError):
|
|
192
|
+
setattr(self, k, getattr(msg,k))
|
|
@@ -74,7 +74,7 @@ class Message:
|
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
@define
|
|
77
|
-
class _NodeMessage
|
|
77
|
+
class _NodeMessage(Message): # [TNode]
|
|
78
78
|
node: TNode = None
|
|
79
79
|
|
|
80
80
|
def __attrs_post_init__(self):
|
|
@@ -138,7 +138,7 @@ class InitMessage(_NodeMessage):
|
|
|
138
138
|
|
|
139
139
|
@_reg
|
|
140
140
|
@define
|
|
141
|
-
class PingMessage
|
|
141
|
+
class PingMessage(_NodeMessage): # [TNode]
|
|
142
142
|
"""
|
|
143
143
|
Your regular actor announcement.
|
|
144
144
|
"""
|
|
@@ -155,7 +155,7 @@ class PingMessage[TNode](_NodeMessage):
|
|
|
155
155
|
|
|
156
156
|
@_reg
|
|
157
157
|
@define
|
|
158
|
-
class HistoryMessage
|
|
158
|
+
class HistoryMessage(Message): # [TNode]
|
|
159
159
|
"""
|
|
160
160
|
Your regular actor announcement.
|
|
161
161
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: asyncactor
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.26.1
|
|
4
4
|
Summary: Async decentralized actor
|
|
5
5
|
Author-email: Matthias Urlichs <matthias@urlichs.de>
|
|
6
6
|
Project-URL: homepage, https://github.com/smurfix/asyncactor
|
|
@@ -32,6 +32,7 @@ Requires-Dist: pytest-cov>=2.3; extra == "dev"
|
|
|
32
32
|
Requires-Dist: pytest-trio; extra == "dev"
|
|
33
33
|
Requires-Dist: pytest>=2.5.2; extra == "dev"
|
|
34
34
|
Requires-Dist: trio>=0.23; extra == "dev"
|
|
35
|
+
Dynamic: license-file
|
|
35
36
|
|
|
36
37
|
asyncactor
|
|
37
38
|
==========
|
|
@@ -19,8 +19,8 @@ AsyncActor is based on timeslots. At the start of each slot, one
|
|
|
19
19
|
participant is selected as the leader (or, like we said when we were
|
|
20
20
|
children: *you're "it"*).
|
|
21
21
|
|
|
22
|
-
Some time after the end of the slot,
|
|
23
|
-
``ping`` message. The first actor that does this starts the next slot.
|
|
22
|
+
Some time after the end of the slot, another participating actor broadcasts
|
|
23
|
+
a ``ping`` message. The first actor that does this starts the next slot.
|
|
24
24
|
Collisions are avoided by appropriately modelling the "some time after"
|
|
25
25
|
part; if that fails and messages cross each other, collisions are resolved
|
|
26
26
|
deterministically.
|
|
@@ -38,7 +38,7 @@ value.
|
|
|
38
38
|
The resolution method may be changed, if necessary, by overriding
|
|
39
39
|
:meth:`Actor.has_priority`. Note that this method **must** be deterministic,
|
|
40
40
|
i.e. every recipient of a set of messages must select the same node as
|
|
41
|
-
the new leader.
|
|
41
|
+
the new leader, irrespective of the order these messages arrive in.
|
|
42
42
|
|
|
43
43
|
Depending on the parameters, the default implementation randomly selects a
|
|
44
44
|
number of participating actors and round-robins the "it" role between them.
|
|
@@ -59,6 +59,20 @@ participate in the protocol. You can use this to selectively enable or
|
|
|
59
59
|
disable actors if you want e.g. to only let the highest-priority actors
|
|
60
60
|
be *it*.
|
|
61
61
|
|
|
62
|
+
Message Transport
|
|
63
|
+
+++++++++++++++++
|
|
64
|
+
|
|
65
|
+
AsyncActor messages can be transported by any method that doesn't lose
|
|
66
|
+
messages (except in case of a network split). MQTT with CBOR or MsgPack
|
|
67
|
+
encoding are good examples.
|
|
68
|
+
|
|
69
|
+
Messages *must not* be duplicated by the transport. In practice this means
|
|
70
|
+
that your MQTT server and library either must support subscription IDs, or
|
|
71
|
+
you need to be very careful not to create overlapping subscriptions. We
|
|
72
|
+
strongly recommend the former strategy because subscription IDs also avoid
|
|
73
|
+
redundant topic comparisons by the client.
|
|
74
|
+
|
|
75
|
+
|
|
62
76
|
API
|
|
63
77
|
===
|
|
64
78
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|