moat-kv 0.71.0__py3-none-any.whl → 0.71.7__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 (178) hide show
  1. moat/kv/__init__.py +6 -7
  2. moat/kv/_cfg.yaml +3 -2
  3. moat/kv/actor/__init__.py +2 -1
  4. moat/kv/actor/deletor.py +4 -1
  5. moat/kv/auth/__init__.py +12 -13
  6. moat/kv/auth/_test.py +4 -1
  7. moat/kv/auth/password.py +11 -7
  8. moat/kv/backend/mqtt.py +4 -5
  9. moat/kv/client.py +20 -39
  10. moat/kv/code.py +3 -3
  11. moat/kv/command/data.py +4 -3
  12. moat/kv/command/dump/__init__.py +36 -34
  13. moat/kv/command/internal.py +2 -3
  14. moat/kv/command/job.py +1 -2
  15. moat/kv/command/type.py +3 -6
  16. moat/kv/data.py +9 -8
  17. moat/kv/errors.py +16 -8
  18. moat/kv/mock/__init__.py +2 -12
  19. moat/kv/model.py +29 -33
  20. moat/kv/obj/__init__.py +3 -3
  21. moat/kv/obj/command.py +3 -3
  22. moat/kv/runner.py +4 -5
  23. moat/kv/server.py +106 -126
  24. moat/kv/types.py +10 -12
  25. {moat_kv-0.71.0.dist-info → moat_kv-0.71.7.dist-info}/METADATA +6 -2
  26. moat_kv-0.71.7.dist-info/RECORD +47 -0
  27. {moat_kv-0.71.0.dist-info → moat_kv-0.71.7.dist-info}/WHEEL +1 -1
  28. moat_kv-0.71.7.dist-info/licenses/LICENSE +3 -0
  29. moat_kv-0.71.7.dist-info/licenses/LICENSE.APACHE2 +202 -0
  30. moat_kv-0.71.7.dist-info/licenses/LICENSE.MIT +20 -0
  31. moat_kv-0.71.7.dist-info/top_level.txt +1 -0
  32. build/lib/docs/source/conf.py +0 -201
  33. build/lib/examples/pathify.py +0 -45
  34. build/lib/moat/kv/__init__.py +0 -19
  35. build/lib/moat/kv/_cfg.yaml +0 -93
  36. build/lib/moat/kv/_main.py +0 -91
  37. build/lib/moat/kv/actor/__init__.py +0 -98
  38. build/lib/moat/kv/actor/deletor.py +0 -139
  39. build/lib/moat/kv/auth/__init__.py +0 -444
  40. build/lib/moat/kv/auth/_test.py +0 -166
  41. build/lib/moat/kv/auth/password.py +0 -234
  42. build/lib/moat/kv/auth/root.py +0 -58
  43. build/lib/moat/kv/backend/__init__.py +0 -67
  44. build/lib/moat/kv/backend/mqtt.py +0 -71
  45. build/lib/moat/kv/client.py +0 -1025
  46. build/lib/moat/kv/code.py +0 -236
  47. build/lib/moat/kv/codec.py +0 -11
  48. build/lib/moat/kv/command/__init__.py +0 -1
  49. build/lib/moat/kv/command/acl.py +0 -180
  50. build/lib/moat/kv/command/auth.py +0 -261
  51. build/lib/moat/kv/command/code.py +0 -293
  52. build/lib/moat/kv/command/codec.py +0 -186
  53. build/lib/moat/kv/command/data.py +0 -265
  54. build/lib/moat/kv/command/dump/__init__.py +0 -143
  55. build/lib/moat/kv/command/error.py +0 -149
  56. build/lib/moat/kv/command/internal.py +0 -248
  57. build/lib/moat/kv/command/job.py +0 -433
  58. build/lib/moat/kv/command/log.py +0 -53
  59. build/lib/moat/kv/command/server.py +0 -114
  60. build/lib/moat/kv/command/type.py +0 -201
  61. build/lib/moat/kv/config.py +0 -46
  62. build/lib/moat/kv/data.py +0 -216
  63. build/lib/moat/kv/errors.py +0 -561
  64. build/lib/moat/kv/exceptions.py +0 -126
  65. build/lib/moat/kv/mock/__init__.py +0 -101
  66. build/lib/moat/kv/mock/mqtt.py +0 -159
  67. build/lib/moat/kv/mock/tracer.py +0 -63
  68. build/lib/moat/kv/model.py +0 -1069
  69. build/lib/moat/kv/obj/__init__.py +0 -646
  70. build/lib/moat/kv/obj/command.py +0 -241
  71. build/lib/moat/kv/runner.py +0 -1347
  72. build/lib/moat/kv/server.py +0 -2809
  73. build/lib/moat/kv/types.py +0 -513
  74. ci/rtd-requirements.txt +0 -4
  75. ci/test-requirements.txt +0 -7
  76. ci/travis.sh +0 -96
  77. debian/.gitignore +0 -7
  78. debian/changelog +0 -1435
  79. debian/control +0 -43
  80. debian/moat-kv/usr/lib/python3/dist-packages/docs/source/conf.py +0 -201
  81. debian/moat-kv/usr/lib/python3/dist-packages/examples/pathify.py +0 -45
  82. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/__init__.py +0 -19
  83. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/_cfg.yaml +0 -93
  84. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/_main.py +0 -91
  85. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/actor/__init__.py +0 -98
  86. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/actor/deletor.py +0 -139
  87. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/__init__.py +0 -444
  88. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/_test.py +0 -166
  89. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/password.py +0 -234
  90. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/root.py +0 -58
  91. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/__init__.py +0 -67
  92. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/mqtt.py +0 -71
  93. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/client.py +0 -1025
  94. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/code.py +0 -236
  95. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/codec.py +0 -11
  96. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/__init__.py +0 -1
  97. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/acl.py +0 -180
  98. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/auth.py +0 -261
  99. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/code.py +0 -293
  100. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/codec.py +0 -186
  101. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/data.py +0 -265
  102. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/dump/__init__.py +0 -143
  103. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/error.py +0 -149
  104. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/internal.py +0 -248
  105. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/job.py +0 -433
  106. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/log.py +0 -53
  107. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/server.py +0 -114
  108. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/type.py +0 -201
  109. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/config.py +0 -46
  110. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/data.py +0 -216
  111. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/errors.py +0 -561
  112. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/exceptions.py +0 -126
  113. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/__init__.py +0 -101
  114. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/mqtt.py +0 -159
  115. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/tracer.py +0 -63
  116. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/model.py +0 -1069
  117. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/obj/__init__.py +0 -646
  118. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/obj/command.py +0 -241
  119. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/runner.py +0 -1347
  120. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/server.py +0 -2809
  121. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/types.py +0 -513
  122. debian/moat-kv.postinst +0 -3
  123. debian/rules +0 -20
  124. debian/source/format +0 -1
  125. debian/watch +0 -4
  126. docs/Makefile +0 -20
  127. docs/make.bat +0 -36
  128. docs/source/TODO.rst +0 -61
  129. docs/source/_static/.gitkeep +0 -0
  130. docs/source/acls.rst +0 -80
  131. docs/source/auth.rst +0 -84
  132. docs/source/client_protocol.rst +0 -456
  133. docs/source/code.rst +0 -341
  134. docs/source/command_line.rst +0 -1187
  135. docs/source/common_protocol.rst +0 -47
  136. docs/source/conf.py +0 -201
  137. docs/source/debugging.rst +0 -70
  138. docs/source/extend.rst +0 -37
  139. docs/source/history.rst +0 -36
  140. docs/source/index.rst +0 -75
  141. docs/source/model.rst +0 -54
  142. docs/source/overview.rst +0 -83
  143. docs/source/related.rst +0 -89
  144. docs/source/server_protocol.rst +0 -450
  145. docs/source/startup.rst +0 -31
  146. docs/source/translator.rst +0 -244
  147. docs/source/tutorial.rst +0 -711
  148. docs/source/v3.rst +0 -168
  149. examples/code/transform.scale.yml +0 -21
  150. examples/code/transform.switch.yml +0 -82
  151. examples/code/transform.timeslot.yml +0 -63
  152. examples/pathify.py +0 -45
  153. moat/kv/codec.py +0 -11
  154. moat_kv-0.71.0.dist-info/RECORD +0 -188
  155. moat_kv-0.71.0.dist-info/top_level.txt +0 -9
  156. scripts/current +0 -15
  157. scripts/env +0 -8
  158. scripts/init +0 -39
  159. scripts/recover +0 -17
  160. scripts/rotate +0 -33
  161. scripts/run +0 -29
  162. scripts/run-all +0 -10
  163. scripts/run-any +0 -10
  164. scripts/run-single +0 -15
  165. scripts/success +0 -4
  166. systemd/moat-kv-recover.service +0 -21
  167. systemd/moat-kv-rotate.service +0 -20
  168. systemd/moat-kv-rotate.timer +0 -10
  169. systemd/moat-kv-run-all.service +0 -26
  170. systemd/moat-kv-run-all@.service +0 -25
  171. systemd/moat-kv-run-any.service +0 -26
  172. systemd/moat-kv-run-any@.service +0 -25
  173. systemd/moat-kv-run-single.service +0 -26
  174. systemd/moat-kv-run-single@.service +0 -25
  175. systemd/moat-kv.service +0 -27
  176. systemd/postinst +0 -7
  177. systemd/sysusers +0 -3
  178. {moat_kv-0.71.0.dist-info → moat_kv-0.71.7.dist-info}/licenses/LICENSE.txt +0 -0
moat/kv/server.py CHANGED
@@ -1,15 +1,16 @@
1
1
  # Local server
2
2
  from __future__ import annotations
3
3
 
4
- import io
5
4
  import os
6
5
  import signal
7
6
  import time
7
+ from collections.abc import Mapping
8
8
 
9
9
  import anyio
10
10
  from anyio.abc import SocketAttribute
11
11
  from asyncscope import scope
12
12
  from moat.util import DelayedRead, DelayedWrite, create_queue, ensure_cfg
13
+ from moat.lib.codec import get_codec
13
14
 
14
15
  try:
15
16
  from contextlib import asynccontextmanager
@@ -17,10 +18,9 @@ except ImportError:
17
18
  from async_generator import asynccontextmanager
18
19
 
19
20
  import logging
20
- from collections.abc import Mapping
21
21
  from functools import partial
22
22
  from pprint import pformat
23
- from typing import Any
23
+ from typing import Any, TYPE_CHECKING
24
24
 
25
25
  from asyncactor import (
26
26
  Actor,
@@ -55,7 +55,6 @@ from . import _version_tuple
55
55
  from . import client as moat_kv_client # needs to be mock-able
56
56
  from .actor.deletor import DeleteActor
57
57
  from .backend import get_backend
58
- from .codec import packer, stream_unpacker, unpacker
59
58
  from .exceptions import (
60
59
  ACLError,
61
60
  CancelledError,
@@ -68,6 +67,10 @@ from .exceptions import (
68
67
  )
69
68
  from .model import Node, NodeEvent, NodeSet, UpdateEvent, Watcher
70
69
  from .types import ACLFinder, ACLStepper, ConvNull, NullACL, RootEntry
70
+ import contextlib
71
+
72
+ if TYPE_CHECKING:
73
+ import io
71
74
 
72
75
  Stream = anyio.abc.ByteStream
73
76
 
@@ -75,9 +78,6 @@ ClosedResourceError = anyio.ClosedResourceError
75
78
 
76
79
  _client_nr = 0
77
80
 
78
- SERF_MAXLEN = 450
79
- SERF_LEN_DELTA = 15
80
-
81
81
 
82
82
  def max_n(a, b):
83
83
  if a is None:
@@ -228,10 +228,8 @@ class StreamCommand:
228
228
  await self.send(error=repr(exc))
229
229
  finally:
230
230
  with anyio.move_on_after(2, shield=True):
231
- try:
231
+ with contextlib.suppress(anyio.BrokenResourceError):
232
232
  await self.send(state="end")
233
- except anyio.BrokenResourceError:
234
- pass
235
233
 
236
234
  else:
237
235
  res = await self.run(**kw)
@@ -553,62 +551,64 @@ class SCmd_watch(StreamCommand):
553
551
  min_depth = msg.get("min_depth", 0)
554
552
  empty = msg.get("empty", False)
555
553
 
556
- async with Watcher(entry) as watcher:
557
- async with anyio.create_task_group() as tg:
558
- tock = client.server.tock
559
- shorter = PathShortener(entry.path)
560
- if msg.get("fetch", False):
561
-
562
- async def orig_state():
563
- kv = {"max_depth": max_depth, "min_depth": min_depth}
564
-
565
- async def worker(entry, acl):
566
- if entry.data is NotGiven and not empty:
567
- return
568
- if entry.tock < tock:
569
- res = entry.serialize(
570
- chop_path=client._chop_path,
571
- nchain=nchain,
572
- conv=conv,
573
- )
574
- shorter(res)
575
- if not acl.allows("r"):
576
- res.pop("value", None)
577
- await self.send(**res)
578
-
579
- if not acl.allows("e"):
580
- raise StopAsyncIteration
581
- if not acl.allows("x"):
582
- acl.block("r")
583
-
584
- await entry.walk(worker, acl=acl, **kv)
585
- await self.send(state="uptodate")
586
-
587
- tg.start_soon(orig_state)
588
-
589
- async for m in watcher:
590
- ml = len(m.entry.path) - len(msg.path)
591
- if ml < min_depth:
592
- continue
593
- if max_depth >= 0 and ml > max_depth:
594
- continue
595
- a = acl
596
- for p in getattr(m, "path", [])[shorter.depth :]:
597
- if not a.allows("e"):
598
- break
554
+ async with (
555
+ Watcher(entry) as watcher,
556
+ anyio.create_task_group() as tg,
557
+ ):
558
+ tock = client.server.tock
559
+ shorter = PathShortener(entry.path)
560
+ if msg.get("fetch", False):
561
+
562
+ async def orig_state():
563
+ kv = {"max_depth": max_depth, "min_depth": min_depth}
564
+
565
+ async def worker(entry, acl):
566
+ if entry.data is NotGiven and not empty:
567
+ return
568
+ if entry.tock < tock:
569
+ res = entry.serialize(
570
+ chop_path=client._chop_path,
571
+ nchain=nchain,
572
+ conv=conv,
573
+ )
574
+ shorter(res)
575
+ if not acl.allows("r"):
576
+ res.pop("value", None)
577
+ await self.send(**res)
578
+
579
+ if not acl.allows("e"):
580
+ raise StopAsyncIteration
599
581
  if not acl.allows("x"):
600
- a.block("r")
601
- a = a.step(p)
602
- else:
603
- res = m.entry.serialize(
604
- chop_path=client._chop_path,
605
- nchain=nchain,
606
- conv=conv,
607
- )
608
- shorter(res)
609
- if not a.allows("r"):
610
- res.pop("value", None)
611
- await self.send(**res)
582
+ acl.block("r")
583
+
584
+ await entry.walk(worker, acl=acl, **kv)
585
+ await self.send(state="uptodate")
586
+
587
+ tg.start_soon(orig_state)
588
+
589
+ async for m in watcher:
590
+ ml = len(m.entry.path) - len(msg.path)
591
+ if ml < min_depth:
592
+ continue
593
+ if max_depth >= 0 and ml > max_depth:
594
+ continue
595
+ a = acl
596
+ for p in getattr(m, "path", [])[shorter.depth :]:
597
+ if not a.allows("e"):
598
+ break
599
+ if not acl.allows("x"):
600
+ a.block("r")
601
+ a = a.step(p)
602
+ else:
603
+ res = m.entry.serialize(
604
+ chop_path=client._chop_path,
605
+ nchain=nchain,
606
+ conv=conv,
607
+ )
608
+ shorter(res)
609
+ if not a.allows("r"):
610
+ res.pop("value", None)
611
+ await self.send(**res)
612
612
 
613
613
 
614
614
  class SCmd_msg_monitor(StreamCommand):
@@ -621,6 +621,7 @@ class SCmd_msg_monitor(StreamCommand):
621
621
  multiline = True
622
622
 
623
623
  async def run(self):
624
+ codec = get_codec("std-msgpack")
624
625
  msg = self.msg
625
626
  raw = msg.get("raw", False)
626
627
  topic = msg.topic
@@ -642,7 +643,7 @@ class SCmd_msg_monitor(StreamCommand):
642
643
  res["raw"] = resp.payload
643
644
  else:
644
645
  try:
645
- res["data"] = unpacker(resp.payload)
646
+ res["data"] = codec.decode(resp.payload)
646
647
  except Exception as exc:
647
648
  res["raw"] = resp.payload
648
649
  res["error"] = repr(exc)
@@ -716,10 +717,10 @@ class ServerClient:
716
717
  if fn is None:
717
718
  fn = StreamCommand(self, msg)
718
719
  if needAuth and not getattr(fn, "noAuth", False):
719
- raise NoAuthError()
720
+ raise NoAuthError
720
721
  else:
721
722
  if needAuth and not getattr(fn, "noAuth", False):
722
- raise NoAuthError()
723
+ raise NoAuthError
723
724
  fn = partial(self._process, fn, msg)
724
725
  if evt is not None:
725
726
  evt.set()
@@ -1069,7 +1070,7 @@ class ServerClient:
1069
1070
  assert "data" not in msg
1070
1071
  data = msg.raw
1071
1072
  else:
1072
- data = packer(msg.data)
1073
+ data = self.codec.encode(msg.data)
1073
1074
  await self.server.backend.send(*topic, payload=data)
1074
1075
 
1075
1076
  async def cmd_delete_tree(self, msg):
@@ -1173,7 +1174,7 @@ class ServerClient:
1173
1174
  if "tock" not in msg:
1174
1175
  msg["tock"] = self.server.tock
1175
1176
  try:
1176
- await self.stream.send(packer(msg))
1177
+ await self.stream.send(self.codec.encode(msg))
1177
1178
  except ClosedResourceError:
1178
1179
  self.logger.info("ERO%d %r", self._client_nr, msg)
1179
1180
  self._send_lock = None
@@ -1189,7 +1190,7 @@ class ServerClient:
1189
1190
 
1190
1191
  async def run(self):
1191
1192
  """Main loop for this client connection."""
1192
- unpacker_ = stream_unpacker() # pylint: disable=redefined-outer-name
1193
+ self.codec = get_codec("std-msgpack") # pylint: disable=redefined-outer-name
1193
1194
 
1194
1195
  async with anyio.create_task_group() as tg:
1195
1196
  self.tg = tg
@@ -1225,7 +1226,7 @@ class ServerClient:
1225
1226
  await self.send(msg)
1226
1227
 
1227
1228
  while True:
1228
- for msg in unpacker_:
1229
+ for msg in self.codec:
1229
1230
  seq = None
1230
1231
  try:
1231
1232
  seq = msg.seq
@@ -1263,7 +1264,7 @@ class ServerClient:
1263
1264
  if len(buf) == 0: # Connection was closed.
1264
1265
  self.logger.debug("EOF %d", self._client_nr)
1265
1266
  break
1266
- unpacker_.feed(buf)
1267
+ self.codec.feed(buf)
1267
1268
 
1268
1269
  tg.cancel_scope.cancel()
1269
1270
 
@@ -1350,7 +1351,7 @@ class _RecoverControl:
1350
1351
 
1351
1352
  class Server:
1352
1353
  """
1353
- This is the MoaT-KV server. It manages connections to the Serf/MQTT server,
1354
+ This is the MoaT-KV server. It manages connections to the MQTT server,
1354
1355
  the MoaT-KV clients, and (optionally) logs all changes to a file.
1355
1356
 
1356
1357
  Args:
@@ -1379,7 +1380,8 @@ class Server:
1379
1380
  ports = None
1380
1381
  _tock = 0
1381
1382
 
1382
- def __init__(self, name: str, cfg: dict = None, init: Any = NotGiven):
1383
+ def __init__(self, name: str, cfg: dict | None = None, init: Any = NotGiven):
1384
+ self.codec = get_codec("std-msgpack")
1383
1385
  self.root = RootEntry(self, tock=self.tock)
1384
1386
  from moat.util import CFG
1385
1387
 
@@ -1408,16 +1410,16 @@ class Server:
1408
1410
  # connected clients
1409
1411
  self._clients = set()
1410
1412
 
1411
- # cache for partial messages
1412
- self._part_len = SERF_MAXLEN - SERF_LEN_DELTA - len(self.node.name)
1413
- self._part_seq = 0
1414
- self._part_cache = dict()
1415
-
1413
+ # running saver tasks
1416
1414
  self._savers = []
1417
1415
 
1418
1416
  # This is here, not in _run_del, because _del_actor needs to be accessible early
1419
1417
  self._del_actor = DeleteActor(self)
1420
1418
 
1419
+ # backwards compat
1420
+ self._part_cache = dict()
1421
+ self._part_unpacker = get_codec("std-msgpack").decode
1422
+
1421
1423
  @property
1422
1424
  def node_cache(self):
1423
1425
  """
@@ -1514,7 +1516,7 @@ class Server:
1514
1516
 
1515
1517
  async def _set_tock(self):
1516
1518
  if self._actor is not None and self._ready.is_set():
1517
- await self._actor.set_value((self._tock, self.node.tick))
1519
+ await self._actor.set_value([self._tock, self.node.tick])
1518
1520
 
1519
1521
  async def del_check(self, value):
1520
1522
  """
@@ -1568,8 +1570,7 @@ class Server:
1568
1570
  if "tick" not in msg:
1569
1571
  msg["tick"] = self.node.tick
1570
1572
  self.logger.debug("Send %s: %r", action, msg)
1571
- for m in self._pack_multiple(msg):
1572
- await self.backend.send(*self.cfg.server.root, action, payload=m)
1573
+ await self.backend.send(*self.cfg.server.root, action, payload=msg)
1573
1574
 
1574
1575
  async def watcher(self):
1575
1576
  """
@@ -1832,37 +1833,6 @@ class Server:
1832
1833
  self._del_actor.add_deleted(self._delete_also_nodes)
1833
1834
  self._delete_also_nodes = NodeSet()
1834
1835
 
1835
- def _pack_multiple(self, msg):
1836
- """"""
1837
- # protect against mistakenly encoded multi-part messages
1838
- # TODO use a msgpack extension instead
1839
- if isinstance(msg, Mapping):
1840
- i = 0
1841
- while (f"_p{i}") in msg:
1842
- i += 1
1843
- j = i
1844
- while i:
1845
- i -= 1
1846
- msg[f"_p{i + 1}"] = msg[f"_p{i}"]
1847
- if j:
1848
- msg["_p0"] = ""
1849
-
1850
- p = packer(msg)
1851
- pl = self._part_len
1852
- if len(p) > SERF_MAXLEN:
1853
- # Owch. We need to split this thing.
1854
- self._part_seq = seq = self._part_seq + 1
1855
- i = 0
1856
- while i >= 0:
1857
- i += 1
1858
- px, p = p[:pl], p[pl:]
1859
- if not p:
1860
- i = -i
1861
- px = {"_p0": (self.node.name, seq, i, px)}
1862
- yield packer(px)
1863
- return
1864
- yield p
1865
-
1866
1836
  def _unpack_multiple(self, msg):
1867
1837
  """
1868
1838
  Undo the effects of _pack_multiple.
@@ -1885,7 +1855,7 @@ class Server:
1885
1855
  return None
1886
1856
  p = b"".join(s)
1887
1857
  del self._part_cache[(nn, seq)]
1888
- msg = unpacker(p)
1858
+ msg = self._part_unpacker(p)
1889
1859
  msg["_p0"] = ""
1890
1860
 
1891
1861
  i = 0
@@ -1907,15 +1877,21 @@ class Server:
1907
1877
  """
1908
1878
  cmd = getattr(self, "user_" + action)
1909
1879
  try:
1910
- async with self.backend.monitor(*self.cfg.server.root, action) as stream:
1880
+ async with self.backend.monitor(
1881
+ *self.cfg.server.root,
1882
+ action,
1883
+ codec=self.codec,
1884
+ ) as stream:
1911
1885
  if delay is not None:
1912
1886
  await delay.wait()
1913
1887
 
1914
1888
  async for resp in stream:
1915
- msg = unpacker(resp.payload)
1889
+ msg = resp.payload
1890
+
1916
1891
  msg = self._unpack_multiple(msg)
1917
1892
  if not msg: # None, empty, whatever
1918
1893
  continue
1894
+
1919
1895
  self.logger.debug("Recv %s: %r", action, msg)
1920
1896
  try:
1921
1897
  with anyio.fail_after(15):
@@ -2406,8 +2382,8 @@ class Server:
2406
2382
 
2407
2383
  async def load(
2408
2384
  self,
2409
- path: str = None,
2410
- stream: io.IOBase = None,
2385
+ path: str | None = None,
2386
+ stream: io.IOBase | None = None,
2411
2387
  local: bool = False,
2412
2388
  authoritative: bool = False,
2413
2389
  ):
@@ -2425,8 +2401,10 @@ class Server:
2425
2401
  raise RuntimeError("This server already has data.")
2426
2402
  elif not local and self.node.tick is None:
2427
2403
  raise RuntimeError("This server is not yet operational.")
2428
- async with MsgReader(path=path, stream=stream) as rdr:
2404
+ async with MsgReader(path=path, stream=stream, codec="std-msgpack") as rdr:
2429
2405
  async for m in rdr:
2406
+ if m is None:
2407
+ continue
2430
2408
  if "value" in m:
2431
2409
  longer(m)
2432
2410
  if "tock" in m:
@@ -2472,15 +2450,15 @@ class Server:
2472
2450
  await writer(msg) # XXX legacy
2473
2451
  await self.root.walk(saver, full=full)
2474
2452
 
2475
- async def save(self, path: str = None, stream=None, full=True):
2453
+ async def save(self, path: str | None = None, stream=None, full=True):
2476
2454
  """Save the current state to ``path`` or ``stream``."""
2477
2455
  shorter = PathShortener([])
2478
- async with MsgWriter(path=path, stream=stream) as mw:
2456
+ async with MsgWriter(path=path, stream=stream, codec="std-msgpack") as mw:
2479
2457
  await self._save(mw, shorter, full=full)
2480
2458
 
2481
2459
  async def save_stream(
2482
2460
  self,
2483
- path: str = None,
2461
+ path: str | None = None,
2484
2462
  stream: anyio.abc.Stream = None,
2485
2463
  save_state: bool = False,
2486
2464
  done: ValueEvent = None,
@@ -2504,7 +2482,7 @@ class Server:
2504
2482
  """
2505
2483
  shorter = PathShortener([])
2506
2484
 
2507
- async with MsgWriter(path=path, stream=stream) as mw:
2485
+ async with MsgWriter(path=path, stream=stream, codec="std-msgpack") as mw:
2508
2486
  msg = await self.get_state(nodes=True, known=True, deleted=True)
2509
2487
  # await mw({"info": msg})
2510
2488
  await mw(msg) # XXX legacy
@@ -2557,7 +2535,7 @@ class Server:
2557
2535
 
2558
2536
  async def _saver(
2559
2537
  self,
2560
- path: str = None,
2538
+ path: str | None = None,
2561
2539
  stream=None,
2562
2540
  done: ValueEvent = None,
2563
2541
  save_state=False,
@@ -2582,7 +2560,9 @@ class Server:
2582
2560
  with anyio.CancelScope(shield=True):
2583
2561
  sd.set()
2584
2562
 
2585
- async def run_saver(self, path: str = None, stream=None, save_state=False, wait: bool = True):
2563
+ async def run_saver(
2564
+ self, path: str | None = None, stream=None, save_state=False, wait: bool = True
2565
+ ):
2586
2566
  """
2587
2567
  Start a task that continually saves to disk.
2588
2568
 
moat/kv/types.py CHANGED
@@ -110,7 +110,7 @@ class MatchEntry(MetaEntry):
110
110
  elif isinstance(value.type, str):
111
111
  value.type = P(value.type)
112
112
  elif not isinstance(value.type, (Path, list, tuple)):
113
- raise ValueError("Type of %r is not a list" % (value.type,))
113
+ raise ValueError(f"Type of {value.type!r} is not a list")
114
114
  try:
115
115
  self.metaroot["type"].follow(value.type, create=False)
116
116
  except KeyError:
@@ -277,18 +277,16 @@ class CodecEntry(Entry):
277
277
  if self._enc is not None:
278
278
  try:
279
279
  value = self._enc(value, entry=entry, data=self._data, **kv)
280
- except TypeError:
281
- if value is not None:
282
- raise
280
+ except (TypeError,ValueError):
281
+ pass
283
282
  return value
284
283
 
285
284
  def dec_value(self, value, entry=None, **kv):
286
285
  if self._dec is not None:
287
286
  try:
288
287
  value = self._dec(value, entry=entry, data=self._data, **kv)
289
- except TypeError:
290
- if value is not None:
291
- raise
288
+ except (TypeError,ValueError):
289
+ pass
292
290
  return value
293
291
 
294
292
  async def set(self, value):
@@ -306,8 +304,9 @@ class CodecEntry(Entry):
306
304
  f"failed decoder at {self.path} on {v!r} with {exc!r}",
307
305
  ) from exc
308
306
  else:
309
- if r != w:
310
- raise ValueError(f"Decoding at {self.path}: {v!r} got {r!r}, not {w!r}")
307
+ pass # float, list/tuple, and similar nonsense
308
+ # if r != w:
309
+ # raise ValueError(f"Decoding at {self.path}: {v!r} got {r!r}, not {w!r}")
311
310
 
312
311
  if value is not None and value.encode is not None:
313
312
  if not value["out"]:
@@ -321,8 +320,7 @@ class CodecEntry(Entry):
321
320
  f"failed encoder at {self.path} on {v!r} with {exc!r}",
322
321
  ) from exc
323
322
  else:
324
- if r != w:
325
- raise ValueError(f"Encoding at {self.path}: {v!r} got {r!r}, not {w!r}")
323
+ pass # float, list/tuple, and similar nonsense
326
324
 
327
325
  await super().set(value)
328
326
  self._enc = enc
@@ -364,7 +362,7 @@ class ConvEntry(MetaEntry):
364
362
  if isinstance(value.codec, str):
365
363
  value.codec = P(value.codec)
366
364
  elif not isinstance(value.codec, (Path, list, tuple)):
367
- raise ValueError("Codec %r is not a list" % (value.codec,))
365
+ raise ValueError(f"Codec {value.codec!r} is not a list")
368
366
  try:
369
367
  self.metaroot["codec"].follow(value.codec, create=False)
370
368
  except KeyError:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: moat-kv
3
- Version: 0.71.0
3
+ Version: 0.71.7
4
4
  Summary: A distributed no-master key-value store
5
5
  Author-email: Matthias Urlichs <matthias@urlichs.de>
6
6
  Project-URL: homepage, https://m-o-a-t.org
@@ -19,18 +19,22 @@ Classifier: Topic :: Home Automation
19
19
  Classifier: Topic :: System :: Distributed Computing
20
20
  Requires-Python: >=3.8
21
21
  Description-Content-Type: text/x-rst
22
+ License-File: LICENSE
23
+ License-File: LICENSE.APACHE2
24
+ License-File: LICENSE.MIT
22
25
  License-File: LICENSE.txt
23
26
  Requires-Dist: asyncclick>7.99
24
27
  Requires-Dist: trio>=0.22
25
28
  Requires-Dist: anyio>=4
26
29
  Requires-Dist: range_set>=0.2
27
30
  Requires-Dist: attrs>=22
28
- Requires-Dist: asyncactor>=0.24
31
+ Requires-Dist: asyncactor~=0.26.3
29
32
  Requires-Dist: asyncscope>=0.10.4
30
33
  Requires-Dist: jsonschema>=2.5
31
34
  Requires-Dist: ruyaml>=0.89
32
35
  Requires-Dist: PyNaCl>=1.3
33
36
  Requires-Dist: moat-lib-diffiehellman~=0.13.4
37
+ Requires-Dist: moat-link
34
38
  Requires-Dist: psutil
35
39
  Requires-Dist: simpleeval>=0.9.10
36
40
  Requires-Dist: moat-mqtt~=0.42.4
@@ -0,0 +1,47 @@
1
+ moat/kv/__init__.py,sha256=an570v034lNp7TNb23T501SM7WNmfeXl2ztGuodG_hg,444
2
+ moat/kv/_cfg.yaml,sha256=j_rLFRiK1xbj_nzlI1AbzLDhq2pXWrM4M_lr6BH-rSc,2174
3
+ moat/kv/_main.py,sha256=SgI4ef8AQDOC4N4pLRiyaiSiFIzN-J337WIcuC7DPOY,2234
4
+ moat/kv/client.py,sha256=qxhhGtfxbcR0MN2dcQzcTga9UBAr9-zRRrMscqeTpVE,34207
5
+ moat/kv/code.py,sha256=QtXHx09Hh9mxSA8m0z1KAFUEDqR0gpTII5g8MlJ8mbI,6427
6
+ moat/kv/config.py,sha256=cWJ1m8PRwzzapsin1XSDjRtYJCgNN6g7gA9q48luDV8,1093
7
+ moat/kv/data.py,sha256=85N2W_pDVRgBn7l9AGdDZHvuHrIPGlbxzgXi_3S0DSI,5924
8
+ moat/kv/errors.py,sha256=cjX0e184EC10HvQ7UOkWdWmc1A8jPr983IFR8UkaI8U,17089
9
+ moat/kv/exceptions.py,sha256=4gRsYa6kuBy1qFYiBUYElup_cvOe7QgPQtryHAYphzo,1849
10
+ moat/kv/model.py,sha256=A7J1Zblk5Wu5zKarD60LwxlLukT-iDtFbTPnQZWz0Kc,33284
11
+ moat/kv/runner.py,sha256=6vpChgCjiWHAjc2u0bbOOQ8cT5KB-f4uBnQb8MgH9wk,41826
12
+ moat/kv/server.py,sha256=fwQZNzyJAhqR01d__3hUmEoAD3kgmRfSG0KdESezqpQ,94108
13
+ moat/kv/types.py,sha256=aCi7_LXzqwruHbEEDJukOjnXNUC2HUaDbgblN8Q05Rc,13998
14
+ moat/kv/actor/__init__.py,sha256=i560uy9WLE4SmOXWcXKjFKNKZdRR51jOwWg7cSASVRo,2131
15
+ moat/kv/actor/deletor.py,sha256=F-pb0jHddhmqLp65G7vbZ_QIxXu5ZF0UBzHWKRgN45U,4537
16
+ moat/kv/auth/__init__.py,sha256=m1x3tyAIxhaF5jSVC-wEJkntBAG2uERvgN0wRTLnp2w,12572
17
+ moat/kv/auth/_test.py,sha256=k-cHh4m22XuVdXjBN3XAqL5zkH8nKDriEbxH25Tuj10,4404
18
+ moat/kv/auth/password.py,sha256=wwKrynFPKcJept3IPCWCDLSyTF298NdSBABgCn0htqY,6474
19
+ moat/kv/auth/root.py,sha256=lW-_hgQp3ZIzAAe4iBl0rdMU-pFuHIC6ObA3rdFXxkk,1303
20
+ moat/kv/backend/__init__.py,sha256=f5nIOWD2zml2YiaBNXtEOzC7SEGgwDFJMB8YzRISJ0A,1689
21
+ moat/kv/backend/mqtt.py,sha256=KSerkVN4MHhzjFprZMraTk56Hj0Hx1vev9xfYoKST2A,2009
22
+ moat/kv/command/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8
23
+ moat/kv/command/acl.py,sha256=wSdlzKBjWIFX3IxCdLcG3ZqjvtX8KPtqe38ibyU2VDo,4665
24
+ moat/kv/command/auth.py,sha256=vP22ZIBMjRdUxN4_ELKiDgIE5ZqdMxs1ky1WDVUx50M,7149
25
+ moat/kv/command/code.py,sha256=ycGBoKBB0Gse3Tt9h9WvgiIZVe3Yg-x92RRjfCCCXVQ,8066
26
+ moat/kv/command/codec.py,sha256=8GqOl-oxWFM2D1Fm9Iheot2AmS_SsOYfqfbbde3qvQY,6028
27
+ moat/kv/command/data.py,sha256=HcLMMcAnyMBH4iH8X0hZiBgmsnV1yppOlg26zB9R72A,8400
28
+ moat/kv/command/error.py,sha256=XqHmVJgBGjr6XOozi23wEjiTqPopD1CzkIeR1a3Z_vY,4350
29
+ moat/kv/command/internal.py,sha256=KnteLPA9SIYk1e2AaN9yWt9r52QvlJCpDEwYcH6hJVI,7404
30
+ moat/kv/command/job.py,sha256=QIiaK0c08CyB-SMjgoWLqa6N3cXQvds0i_w01Q6Jm5I,13590
31
+ moat/kv/command/log.py,sha256=ycdtMgiBw2DaCu0yu6tpWKPLlQ33ZZemXsfwZrCzRGg,1350
32
+ moat/kv/command/server.py,sha256=Hl_6FFq322c5KjJCKxH1nCrxo-h0VEwH4IOMbVeUBN4,3392
33
+ moat/kv/command/type.py,sha256=bQrbJzVo-KWFC69ywA4EsuE_Gp0IT_YpYrgtU6yaW1g,6214
34
+ moat/kv/command/dump/__init__.py,sha256=eF7hlFharOShTjoBxU_tyzPB5-HJUZ0XdZTWQfg2JMc,3951
35
+ moat/kv/mock/__init__.py,sha256=XWqHDWWWbXL61GgIFxxz0ciu2nW2Ba1uKm1J5ZTUuBw,2595
36
+ moat/kv/mock/mqtt.py,sha256=b9pzbwQBmTX2eSiSgNO7nh2-7l8-0TT8n2GxinQ-1l0,5213
37
+ moat/kv/mock/tracer.py,sha256=qLEIn9gdlYUypyRYD8O8SbM8ye8XR4xtBl3otP6uNLs,2058
38
+ moat/kv/obj/__init__.py,sha256=OaQ7PsvvhRhd_H7KUM15nGeApckkCyOv7Xf4llqkzi0,19792
39
+ moat/kv/obj/command.py,sha256=PzMQOEsXRKjKU3WvFtq1QaPJYBk6oWScKIrA-hAD5vg,7426
40
+ moat_kv-0.71.7.dist-info/licenses/LICENSE,sha256=ZSyHhIjRRWNh4Iw_hgf9e6WYkqFBA9Fczk_5PIW1zIs,185
41
+ moat_kv-0.71.7.dist-info/licenses/LICENSE.APACHE2,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
42
+ moat_kv-0.71.7.dist-info/licenses/LICENSE.MIT,sha256=Pm2uVV65J4f8gtHUg1Vnf0VMf2Wus40_nnK_mj2vA0s,1046
43
+ moat_kv-0.71.7.dist-info/licenses/LICENSE.txt,sha256=L5vKJLVOg5t0CEEPpW9-O_0vzbP0PEjEF06tLvnIDuk,541
44
+ moat_kv-0.71.7.dist-info/METADATA,sha256=lNjt0HBXSSzRYA2HR_Ibl3_Nfk5FmxkueQzChvs8u_k,3376
45
+ moat_kv-0.71.7.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
46
+ moat_kv-0.71.7.dist-info/top_level.txt,sha256=pcs9fl5w5AB5GVi4SvBqIVmFrkRwQkVw_dEvW0Q0cSA,5
47
+ moat_kv-0.71.7.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (77.0.3.post20250321)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,3 @@
1
+ This software is made available under the terms of *either* of the
2
+ licenses found in LICENSE.APACHE2 or LICENSE.MIT. Contributions to are
3
+ made under the terms of *both* these licenses.