moat-kv 0.70.20__py3-none-any.whl → 0.70.23__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.
- moat/kv/__init__.py +1 -0
- moat/kv/_cfg.yaml +97 -0
- moat/kv/_main.py +6 -9
- moat/kv/client.py +36 -52
- moat/kv/code.py +10 -3
- moat/kv/codec.py +1 -0
- moat/kv/config.py +2 -0
- moat/kv/data.py +8 -7
- moat/kv/errors.py +17 -9
- moat/kv/exceptions.py +1 -7
- moat/kv/model.py +16 -24
- moat/kv/runner.py +39 -36
- moat/kv/server.py +86 -90
- moat/kv/types.py +5 -8
- {moat_kv-0.70.20.dist-info → moat_kv-0.70.23.dist-info}/METADATA +22 -25
- moat_kv-0.70.23.dist-info/RECORD +19 -0
- {moat_kv-0.70.20.dist-info → moat_kv-0.70.23.dist-info}/WHEEL +1 -1
- moat_kv-0.70.23.dist-info/licenses/LICENSE.txt +14 -0
- moat/kv/_config.yaml +0 -98
- moat/kv/actor/__init__.py +0 -97
- moat/kv/actor/deletor.py +0 -137
- moat/kv/auth/__init__.py +0 -446
- moat/kv/auth/_test.py +0 -172
- moat/kv/auth/password.py +0 -232
- moat/kv/auth/root.py +0 -56
- moat/kv/backend/__init__.py +0 -66
- moat/kv/backend/mqtt.py +0 -74
- moat/kv/backend/serf.py +0 -44
- moat/kv/command/__init__.py +0 -1
- moat/kv/command/acl.py +0 -174
- moat/kv/command/auth.py +0 -258
- moat/kv/command/code.py +0 -306
- moat/kv/command/codec.py +0 -190
- moat/kv/command/data.py +0 -274
- moat/kv/command/dump/__init__.py +0 -141
- moat/kv/command/error.py +0 -156
- moat/kv/command/internal.py +0 -257
- moat/kv/command/job.py +0 -438
- moat/kv/command/log.py +0 -52
- moat/kv/command/server.py +0 -115
- moat/kv/command/type.py +0 -203
- moat/kv/mock/__init__.py +0 -97
- moat/kv/mock/mqtt.py +0 -164
- moat/kv/mock/serf.py +0 -253
- moat/kv/mock/tracer.py +0 -65
- moat/kv/obj/__init__.py +0 -636
- moat/kv/obj/command.py +0 -246
- moat_kv-0.70.20.dist-info/LICENSE +0 -3
- moat_kv-0.70.20.dist-info/LICENSE.APACHE2 +0 -202
- moat_kv-0.70.20.dist-info/LICENSE.MIT +0 -20
- moat_kv-0.70.20.dist-info/RECORD +0 -49
- {moat_kv-0.70.20.dist-info → moat_kv-0.70.23.dist-info}/top_level.txt +0 -0
moat/kv/model.py
CHANGED
@@ -9,7 +9,7 @@ from __future__ import annotations
|
|
9
9
|
import weakref
|
10
10
|
from collections import defaultdict
|
11
11
|
from logging import getLogger
|
12
|
-
from typing import Any
|
12
|
+
from typing import Any
|
13
13
|
|
14
14
|
from moat.util import NotGiven, Path, attrdict, create_queue
|
15
15
|
from range_set import RangeSet
|
@@ -39,9 +39,7 @@ class Node:
|
|
39
39
|
tick: int = None
|
40
40
|
_present: RangeSet = None # I have these as valid data. Superset of ``._deleted``.
|
41
41
|
_deleted: RangeSet = None # I have these as no-longer-valid data
|
42
|
-
_reported: RangeSet =
|
43
|
-
None # somebody else reported these missing data for this node
|
44
|
-
)
|
42
|
+
_reported: RangeSet = None # somebody else reported these missing data for this node
|
45
43
|
_superseded: RangeSet = None # I know these once existed, but no more.
|
46
44
|
entries: dict = None
|
47
45
|
tock: int = 0 # tock when node was last observed
|
@@ -371,7 +369,7 @@ class NodeEvent:
|
|
371
369
|
|
372
370
|
"""
|
373
371
|
|
374
|
-
def __init__(self, node: Node, tick: int = None, prev:
|
372
|
+
def __init__(self, node: Node, tick: int = None, prev: NodeEvent = None):
|
375
373
|
self.node = node
|
376
374
|
if tick is None:
|
377
375
|
tick = node.tick
|
@@ -524,13 +522,15 @@ class NodeEvent:
|
|
524
522
|
self.prev = cls.deserialize(msg["prev"], cache=cache)
|
525
523
|
return self
|
526
524
|
|
527
|
-
def attach(self, prev:
|
525
|
+
def attach(self, prev: NodeEvent = None, server=None):
|
528
526
|
"""Copy this node, if necessary, and attach a filtered `prev` chain to it"""
|
529
527
|
if prev is not None:
|
530
528
|
prev = prev.filter(self.node, server=server)
|
531
529
|
if self.prev is not None or prev is not None:
|
532
530
|
self = NodeEvent( # pylint: disable=self-cls-assignment
|
533
|
-
node=self.node,
|
531
|
+
node=self.node,
|
532
|
+
tick=self.tick,
|
533
|
+
prev=prev,
|
534
534
|
)
|
535
535
|
return self
|
536
536
|
|
@@ -538,9 +538,7 @@ class NodeEvent:
|
|
538
538
|
class UpdateEvent:
|
539
539
|
"""Represents an event which updates something."""
|
540
540
|
|
541
|
-
def __init__(
|
542
|
-
self, event: NodeEvent, entry: "Entry", new_value, old_value=NotGiven, tock=None
|
543
|
-
):
|
541
|
+
def __init__(self, event: NodeEvent, entry: Entry, new_value, old_value=NotGiven, tock=None):
|
544
542
|
self.event = event
|
545
543
|
self.entry = entry
|
546
544
|
self.new_value = new_value
|
@@ -611,10 +609,10 @@ class UpdateEvent:
|
|
611
609
|
class Entry:
|
612
610
|
"""This class represents one key/value pair"""
|
613
611
|
|
614
|
-
_parent:
|
612
|
+
_parent: Entry = None
|
615
613
|
name: str = None
|
616
|
-
_path:
|
617
|
-
_root:
|
614
|
+
_path: list[str] = None
|
615
|
+
_root: Entry = None
|
618
616
|
chain: NodeEvent = None
|
619
617
|
SUBTYPE = None
|
620
618
|
SUBTYPES = {}
|
@@ -622,7 +620,7 @@ class Entry:
|
|
622
620
|
|
623
621
|
monitors = None
|
624
622
|
|
625
|
-
def __init__(self, name: str, parent:
|
623
|
+
def __init__(self, name: str, parent: Entry, tock=None):
|
626
624
|
self.name = name
|
627
625
|
self._sub = {}
|
628
626
|
self.monitors = set()
|
@@ -632,7 +630,7 @@ class Entry:
|
|
632
630
|
parent._add_subnode(self)
|
633
631
|
self._parent = weakref.ref(parent)
|
634
632
|
|
635
|
-
def _add_subnode(self, child:
|
633
|
+
def _add_subnode(self, child: Entry):
|
636
634
|
self._sub[child.name] = child
|
637
635
|
|
638
636
|
def __hash__(self):
|
@@ -904,9 +902,7 @@ class Entry:
|
|
904
902
|
if not loading:
|
905
903
|
logger.warning("*** inconsistency ***")
|
906
904
|
logger.warning("Node: %s", self.path)
|
907
|
-
logger.warning(
|
908
|
-
"Current: %s :%s: %r", self.chain, self.tock, self._data
|
909
|
-
)
|
905
|
+
logger.warning("Current: %s :%s: %r", self.chain, self.tock, self._data)
|
910
906
|
logger.warning("New: %s :%s: %r", evt.event, evt.tock, evt_val)
|
911
907
|
if evt.tock < self.tock:
|
912
908
|
if not loading:
|
@@ -934,9 +930,7 @@ class Entry:
|
|
934
930
|
n.seen(t, self)
|
935
931
|
await self.updated(evt)
|
936
932
|
|
937
|
-
async def walk(
|
938
|
-
self, proc, acl=None, max_depth=-1, min_depth=0, _depth=0, full=False
|
939
|
-
):
|
933
|
+
async def walk(self, proc, acl=None, max_depth=-1, min_depth=0, _depth=0, full=False):
|
940
934
|
"""
|
941
935
|
Call coroutine ``proc`` on this node and all its children).
|
942
936
|
|
@@ -960,9 +954,7 @@ class Entry:
|
|
960
954
|
if k is None and not full:
|
961
955
|
continue
|
962
956
|
a = acl.step(k) if acl is not None else None
|
963
|
-
await v.walk(
|
964
|
-
proc, acl=a, max_depth=max_depth, min_depth=min_depth, _depth=_depth
|
965
|
-
)
|
957
|
+
await v.walk(proc, acl=a, max_depth=max_depth, min_depth=min_depth, _depth=_depth)
|
966
958
|
|
967
959
|
def serialize(self, chop_path=0, nchain=2, conv=None):
|
968
960
|
"""Serialize this entry for msgpack.
|
moat/kv/runner.py
CHANGED
@@ -3,6 +3,7 @@ This module's job is to run code, resp. to keep it running.
|
|
3
3
|
|
4
4
|
|
5
5
|
"""
|
6
|
+
from __future__ import annotations
|
6
7
|
|
7
8
|
import time
|
8
9
|
from collections.abc import Mapping
|
@@ -120,7 +121,6 @@ for _c in (
|
|
120
121
|
CompleteState,
|
121
122
|
ActorState,
|
122
123
|
BrokenState,
|
123
|
-
NotGiven,
|
124
124
|
TimerMsg,
|
125
125
|
ReadyMsg,
|
126
126
|
ChangeMsg,
|
@@ -130,6 +130,8 @@ for _c in (
|
|
130
130
|
):
|
131
131
|
_CLASSES[_c.__name__] = _c
|
132
132
|
|
133
|
+
_CLASSES["NotGiven"] = NotGiven # ellipsis
|
134
|
+
|
133
135
|
|
134
136
|
class CallAdmin:
|
135
137
|
"""
|
@@ -169,9 +171,9 @@ class CallAdmin:
|
|
169
171
|
"""Called by the runner to actually execute the code."""
|
170
172
|
self._logger.debug("Start %s with %s", self._runner._path, self._runner.code)
|
171
173
|
async with (
|
172
|
-
|
173
|
-
|
174
|
-
|
174
|
+
anyio.create_task_group() as tg,
|
175
|
+
AsyncExitStack() as stack,
|
176
|
+
):
|
175
177
|
self._stack = stack
|
176
178
|
self._taskgroup = tg
|
177
179
|
self._runner.scope = sc = tg.cancel_scope
|
@@ -249,7 +251,7 @@ class CallAdmin:
|
|
249
251
|
path = self._path
|
250
252
|
r = await self._err.record_error("run", path, **kw)
|
251
253
|
await self._err.root.wait_chain(r.chain)
|
252
|
-
raise ErrorRecorded
|
254
|
+
raise ErrorRecorded
|
253
255
|
|
254
256
|
async def open_context(self, ctx):
|
255
257
|
return await self._stack.enter_async_context(ctx)
|
@@ -294,9 +296,7 @@ class CallAdmin:
|
|
294
296
|
async with slf.client.watch(path, **kw) as watcher:
|
295
297
|
yield watcher
|
296
298
|
else:
|
297
|
-
raise RuntimeError(
|
298
|
-
f"What should I do with a path marked {path.mark !r}?"
|
299
|
-
)
|
299
|
+
raise RuntimeError(f"What should I do with a path marked {path.mark!r}?")
|
300
300
|
|
301
301
|
with anyio.CancelScope() as sc:
|
302
302
|
slf.scope = sc
|
@@ -318,9 +318,7 @@ class CallAdmin:
|
|
318
318
|
elif msg.get("state", "") == "uptodate":
|
319
319
|
slf.admin._n_watch_seen += 1
|
320
320
|
if slf.admin._n_watch_seen == slf.admin._n_watch:
|
321
|
-
await slf.runner.send_event(
|
322
|
-
ReadyMsg(slf.admin._n_watch_seen)
|
323
|
-
)
|
321
|
+
await slf.runner.send_event(ReadyMsg(slf.admin._n_watch_seen))
|
324
322
|
|
325
323
|
def cancel(slf):
|
326
324
|
if slf.scope is None:
|
@@ -361,7 +359,7 @@ class CallAdmin:
|
|
361
359
|
if path.mark == "r":
|
362
360
|
return await self.send(path, value)
|
363
361
|
elif path.mark:
|
364
|
-
raise RuntimeError(f"What should I do with a path marked {path.mark
|
362
|
+
raise RuntimeError(f"What should I do with a path marked {path.mark!r}")
|
365
363
|
|
366
364
|
if isinstance(path, (tuple, list)):
|
367
365
|
path = Path.build(path)
|
@@ -556,7 +554,9 @@ class RunnerEntry(AttrClientEntry):
|
|
556
554
|
raise RuntimeError(f"already running on {state.node}")
|
557
555
|
code = self.root.code.follow(self.code, create=False)
|
558
556
|
data = combine_dict(
|
559
|
-
self.data or {},
|
557
|
+
self.data or {},
|
558
|
+
{} if code.value is NotGiven else code.value.get("default", {}),
|
559
|
+
deep=True,
|
560
560
|
)
|
561
561
|
|
562
562
|
if code.is_async:
|
@@ -574,9 +574,7 @@ class RunnerEntry(AttrClientEntry):
|
|
574
574
|
|
575
575
|
await state.save(wait=True)
|
576
576
|
if state.node != state.root.name:
|
577
|
-
raise RuntimeError(
|
578
|
-
"Rudely taken away from us.", state.node, state.root.name
|
579
|
-
)
|
577
|
+
raise RuntimeError("Rudely taken away from us.", state.node, state.root.name)
|
580
578
|
|
581
579
|
data["_self"] = calls = CallAdmin(self, state, data)
|
582
580
|
res = await calls._run(code, data)
|
@@ -600,7 +598,11 @@ class RunnerEntry(AttrClientEntry):
|
|
600
598
|
c, self._comment = self._comment, None
|
601
599
|
with anyio.move_on_after(5, shield=True):
|
602
600
|
r = await self.root.err.record_error(
|
603
|
-
"run",
|
601
|
+
"run",
|
602
|
+
self._path,
|
603
|
+
exc=exc,
|
604
|
+
data=self.data,
|
605
|
+
comment=c,
|
604
606
|
)
|
605
607
|
if r is not None:
|
606
608
|
await self.root.err.wait_chain(r.chain)
|
@@ -803,7 +805,9 @@ class StateEntry(AttrClientEntry):
|
|
803
805
|
self.node = None
|
804
806
|
self.backoff = min(20, self.backoff + 1)
|
805
807
|
await self.root.runner.err.record_error(
|
806
|
-
"run",
|
808
|
+
"run",
|
809
|
+
self.runner._path,
|
810
|
+
message="Runner restarted",
|
807
811
|
)
|
808
812
|
await self.save()
|
809
813
|
|
@@ -848,7 +852,7 @@ class StateEntry(AttrClientEntry):
|
|
848
852
|
return
|
849
853
|
elif self.node is None or n == self.root.runner.name:
|
850
854
|
# Owch. Our job got taken away from us.
|
851
|
-
run._comment = f"Cancel: Node set to {self.node
|
855
|
+
run._comment = f"Cancel: Node set to {self.node!r}"
|
852
856
|
run.scope.cancel()
|
853
857
|
elif n is not None:
|
854
858
|
logger.warning(
|
@@ -918,7 +922,8 @@ class StateRoot(MirrorRoot):
|
|
918
922
|
await self.update(val)
|
919
923
|
else:
|
920
924
|
await self.client.msg_send(
|
921
|
-
"run",
|
925
|
+
"run",
|
926
|
+
{"group": self.runner.group, "time": t, "node": self.name},
|
922
927
|
)
|
923
928
|
|
924
929
|
|
@@ -961,9 +966,7 @@ class _BaseRunnerRoot(ClientRoot):
|
|
961
966
|
cfg_ = client._cfg["runner"]
|
962
967
|
else:
|
963
968
|
cfg_ = combine_dict(cfg, client._cfg["runner"])
|
964
|
-
return await super().as_handler(
|
965
|
-
client, subpath=subpath, _subpath=subpath, cfg=cfg_, **kw
|
966
|
-
)
|
969
|
+
return await super().as_handler(client, subpath=subpath, _subpath=subpath, cfg=cfg_, **kw)
|
967
970
|
|
968
971
|
async def run_starting(self):
|
969
972
|
from .code import CodeRoot
|
@@ -984,7 +987,10 @@ class _BaseRunnerRoot(ClientRoot):
|
|
984
987
|
|
985
988
|
async def _state_runner(self):
|
986
989
|
self.state = await StateRoot.as_handler(
|
987
|
-
self.client,
|
990
|
+
self.client,
|
991
|
+
cfg=self._cfg,
|
992
|
+
subpath=self._x_subpath,
|
993
|
+
key="state",
|
988
994
|
)
|
989
995
|
|
990
996
|
@property
|
@@ -1014,7 +1020,7 @@ class _BaseRunnerRoot(ClientRoot):
|
|
1014
1020
|
|
1015
1021
|
Its job is to control which tasks are started.
|
1016
1022
|
"""
|
1017
|
-
raise RuntimeError("You want to override me."
|
1023
|
+
raise RuntimeError("You want to override me.")
|
1018
1024
|
|
1019
1025
|
async def trigger_rescan(self):
|
1020
1026
|
"""Tell the _run_actor task to rescan our job list prematurely"""
|
@@ -1096,9 +1102,7 @@ class AnyRunnerRoot(_BaseRunnerRoot):
|
|
1096
1102
|
def __init__(self, *a, **kw):
|
1097
1103
|
super().__init__(*a, **kw)
|
1098
1104
|
self.group = (
|
1099
|
-
P(self.client.config.server["root"]) + P(self._cfg["name"])
|
1100
|
-
| "any"
|
1101
|
-
| self._path[-1]
|
1105
|
+
P(self.client.config.server["root"]) + P(self._cfg["name"]) | "any" | self._path[-1]
|
1102
1106
|
)
|
1103
1107
|
|
1104
1108
|
def get_node(self, name):
|
@@ -1116,7 +1120,10 @@ class AnyRunnerRoot(_BaseRunnerRoot):
|
|
1116
1120
|
Monitor the Actor state, run a :meth:`_run_now` subtask whenever we're 'it'.
|
1117
1121
|
"""
|
1118
1122
|
async with ClientActor(
|
1119
|
-
self.client,
|
1123
|
+
self.client,
|
1124
|
+
self.name,
|
1125
|
+
topic=self.group,
|
1126
|
+
cfg=self._cfg["actor"],
|
1120
1127
|
) as act:
|
1121
1128
|
self._act = act
|
1122
1129
|
|
@@ -1176,7 +1183,7 @@ class AnyRunnerRoot(_BaseRunnerRoot):
|
|
1176
1183
|
self._stale_times.append(cur)
|
1177
1184
|
if self._stale_times[0] > cur - self.max_age:
|
1178
1185
|
return
|
1179
|
-
if len(self._stale_times) <= 2*self._cfg["actor"]["n_hosts"]+1:
|
1186
|
+
if len(self._stale_times) <= 2 * self._cfg["actor"]["n_hosts"] + 1:
|
1180
1187
|
return
|
1181
1188
|
cut = self._stale_times.pop(0)
|
1182
1189
|
|
@@ -1278,9 +1285,7 @@ class SingleRunnerRoot(_BaseRunnerRoot):
|
|
1278
1285
|
async with anyio.create_task_group() as tg:
|
1279
1286
|
age_q = create_queue(1)
|
1280
1287
|
|
1281
|
-
async with ClientActor(
|
1282
|
-
self.client, self.name, topic=self.group, cfg=self._cfg
|
1283
|
-
) as act:
|
1288
|
+
async with ClientActor(self.client, self.name, topic=self.group, cfg=self._cfg) as act:
|
1284
1289
|
self._act = act
|
1285
1290
|
tg.start_soon(self._age_notifier, age_q)
|
1286
1291
|
await self.spawn(self._run_now)
|
@@ -1330,9 +1335,7 @@ class AllRunnerRoot(SingleRunnerRoot):
|
|
1330
1335
|
def __init__(self, *a, **kw):
|
1331
1336
|
super().__init__(*a, **kw)
|
1332
1337
|
self.group = (
|
1333
|
-
P(self.client.config.server["root"]) + P(self._cfg["name"])
|
1334
|
-
| "all"
|
1335
|
-
| self._path[-1]
|
1338
|
+
P(self.client.config.server["root"]) + P(self._cfg["name"]) | "all" | self._path[-1]
|
1336
1339
|
)
|
1337
1340
|
|
1338
1341
|
async def _state_runner(self):
|