asyncactor 0.25.2__tar.gz → 0.26.0__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.
Files changed (50) hide show
  1. {asyncactor-0.25.2 → asyncactor-0.26.0}/PKG-INFO +3 -2
  2. {asyncactor-0.25.2 → asyncactor-0.26.0}/asyncactor/actor.py +11 -8
  3. {asyncactor-0.25.2 → asyncactor-0.26.0}/asyncactor/backend/moat_link.py +4 -4
  4. {asyncactor-0.25.2 → asyncactor-0.26.0}/asyncactor/events.py +5 -6
  5. {asyncactor-0.25.2 → asyncactor-0.26.0}/asyncactor/messages.py +4 -4
  6. {asyncactor-0.25.2 → asyncactor-0.26.0}/asyncactor/nodelist.py +1 -1
  7. {asyncactor-0.25.2 → asyncactor-0.26.0}/asyncactor.egg-info/PKG-INFO +3 -2
  8. {asyncactor-0.25.2 → asyncactor-0.26.0}/docs/source/actor.rst +17 -3
  9. {asyncactor-0.25.2 → asyncactor-0.26.0}/.coveragerc +0 -0
  10. {asyncactor-0.25.2 → asyncactor-0.26.0}/.gitignore +0 -0
  11. {asyncactor-0.25.2 → asyncactor-0.26.0}/.pylintrc +0 -0
  12. {asyncactor-0.25.2 → asyncactor-0.26.0}/.readthedocs.yml +0 -0
  13. {asyncactor-0.25.2 → asyncactor-0.26.0}/.style.yapf +0 -0
  14. {asyncactor-0.25.2 → asyncactor-0.26.0}/.travis.yml +0 -0
  15. {asyncactor-0.25.2 → asyncactor-0.26.0}/CHANGELOG.rst +0 -0
  16. {asyncactor-0.25.2 → asyncactor-0.26.0}/LICENSE +0 -0
  17. {asyncactor-0.25.2 → asyncactor-0.26.0}/Makefile +0 -0
  18. {asyncactor-0.25.2 → asyncactor-0.26.0}/README.rst +0 -0
  19. {asyncactor-0.25.2 → asyncactor-0.26.0}/asyncactor/__init__.py +0 -0
  20. {asyncactor-0.25.2 → asyncactor-0.26.0}/asyncactor/abc.py +0 -0
  21. {asyncactor-0.25.2 → asyncactor-0.26.0}/asyncactor/backend/__init__.py +0 -0
  22. {asyncactor-0.25.2 → asyncactor-0.26.0}/asyncactor/backend/moat_kv.py +0 -0
  23. {asyncactor-0.25.2 → asyncactor-0.26.0}/asyncactor/backend/mqtt.py +0 -0
  24. {asyncactor-0.25.2 → asyncactor-0.26.0}/asyncactor/backend/mqttproto.py +0 -0
  25. {asyncactor-0.25.2 → asyncactor-0.26.0}/asyncactor/backend/serf.py +0 -0
  26. {asyncactor-0.25.2 → asyncactor-0.26.0}/asyncactor/exceptions.py +0 -0
  27. {asyncactor-0.25.2 → asyncactor-0.26.0}/asyncactor.egg-info/SOURCES.txt +0 -0
  28. {asyncactor-0.25.2 → asyncactor-0.26.0}/asyncactor.egg-info/dependency_links.txt +0 -0
  29. {asyncactor-0.25.2 → asyncactor-0.26.0}/asyncactor.egg-info/requires.txt +0 -0
  30. {asyncactor-0.25.2 → asyncactor-0.26.0}/asyncactor.egg-info/top_level.txt +0 -0
  31. {asyncactor-0.25.2 → asyncactor-0.26.0}/ci/rtd-requirements.txt +0 -0
  32. {asyncactor-0.25.2 → asyncactor-0.26.0}/ci/test-requirements.txt +0 -0
  33. {asyncactor-0.25.2 → asyncactor-0.26.0}/ci/travis.sh +0 -0
  34. {asyncactor-0.25.2 → asyncactor-0.26.0}/docs/Makefile +0 -0
  35. {asyncactor-0.25.2 → asyncactor-0.26.0}/docs/make.bat +0 -0
  36. {asyncactor-0.25.2 → asyncactor-0.26.0}/docs/source/_static/.git_empty +0 -0
  37. {asyncactor-0.25.2 → asyncactor-0.26.0}/docs/source/conf.py +0 -0
  38. {asyncactor-0.25.2 → asyncactor-0.26.0}/docs/source/history.rst +0 -0
  39. {asyncactor-0.25.2 → asyncactor-0.26.0}/docs/source/index.rst +0 -0
  40. {asyncactor-0.25.2 → asyncactor-0.26.0}/docs/source/rationale.rst +0 -0
  41. {asyncactor-0.25.2 → asyncactor-0.26.0}/mktag +0 -0
  42. {asyncactor-0.25.2 → asyncactor-0.26.0}/pyproject.toml +0 -0
  43. {asyncactor-0.25.2 → asyncactor-0.26.0}/setup.cfg +0 -0
  44. {asyncactor-0.25.2 → asyncactor-0.26.0}/tests/__init__.py +0 -0
  45. {asyncactor-0.25.2 → asyncactor-0.26.0}/tests/mock_serf.py +0 -0
  46. {asyncactor-0.25.2 → asyncactor-0.26.0}/tests/test_actor.py +0 -0
  47. {asyncactor-0.25.2 → asyncactor-0.26.0}/tests/test_mosquitto.py +0 -0
  48. {asyncactor-0.25.2 → asyncactor-0.26.0}/tests/test_mqttproto.py +0 -0
  49. {asyncactor-0.25.2 → asyncactor-0.26.0}/tests/test_serf.py +0 -0
  50. {asyncactor-0.25.2 → asyncactor-0.26.0}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: asyncactor
3
- Version: 0.25.2
3
+ Version: 0.26.0
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 = False
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 Hello only once
318
+ if self._self_seen.is_set():
319
+ # We may see our own Init only once.
320
320
  raise ActorCollisionError
321
- self._self_seen = True
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
- await anyio.sleep((self.random / 2 + 1.5) * self._gap + self._cycle)
331
- if not self._self_seen:
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
- async with anyio.open_cancel_scope() as xx:
717
+ async 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 = None):
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:[self.tag] @{self.conn!r}>"
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.tag, maximum_qos=QoS.AT_LEAST_ONCE, codec=self.codec) as mon:
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[TNode](AuthPingEvent):
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[TNode](UntagEvent):
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
- if msg and "version" not in msg:
190
- self.version = 1
189
+ self.__attrs_init__()
191
190
  for k in "version cycle gap nodes splits n_hosts".split():
192
- with suppress(KeyError):
193
- setattr(self, k, msg[k])
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[TNode](Message):
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[TNode](_NodeMessage):
141
+ class PingMessage(_NodeMessage): # [TNode]
142
142
  """
143
143
  Your regular actor announcement.
144
144
  """
@@ -155,12 +155,12 @@ class PingMessage[TNode](_NodeMessage):
155
155
 
156
156
  @_reg
157
157
  @define
158
- class HistoryMessage[TNode](Message):
158
+ class HistoryMessage(Message): # [TNode]
159
159
  """
160
160
  Your regular actor announcement.
161
161
  """
162
162
 
163
- value = None
163
+ value:Any = None
164
164
  history: NodeList[TNode] = ()
165
165
 
166
166
  _type: ClassVar[str] = "hist"
@@ -9,7 +9,7 @@ __all__ = [
9
9
  ]
10
10
 
11
11
 
12
- class NodeList[TName](list):
12
+ class NodeList(list): # [TName]
13
13
  """
14
14
  This is an augmented :class: `list`, used to store unique node names,
15
15
  up to some maximum (if used).
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: asyncactor
3
- Version: 0.25.2
3
+ Version: 0.26.0
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, a participating actor broadcasts a
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