moat-kv 0.70.14__py3-none-any.whl → 0.70.18__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 +4 -1
- moat/kv/_main.py +12 -4
- moat/kv/actor/deletor.py +4 -1
- moat/kv/auth/__init__.py +16 -10
- moat/kv/auth/_test.py +12 -4
- moat/kv/auth/password.py +1 -3
- moat/kv/client.py +44 -15
- moat/kv/code.py +3 -1
- moat/kv/command/acl.py +17 -4
- moat/kv/command/auth.py +14 -3
- moat/kv/command/code.py +41 -10
- moat/kv/command/codec.py +33 -14
- moat/kv/command/data.py +51 -16
- moat/kv/command/dump/__init__.py +10 -1
- moat/kv/command/error.py +12 -4
- moat/kv/command/internal.py +25 -7
- moat/kv/command/job.py +31 -9
- moat/kv/command/server.py +13 -3
- moat/kv/command/type.py +24 -8
- moat/kv/data.py +3 -1
- moat/kv/errors.py +23 -5
- moat/kv/mock/__init__.py +5 -2
- moat/kv/mock/mqtt.py +9 -3
- moat/kv/mock/serf.py +6 -2
- moat/kv/mock/tracer.py +3 -1
- moat/kv/model.py +21 -7
- moat/kv/obj/__init__.py +26 -5
- moat/kv/obj/command.py +10 -6
- moat/kv/runner.py +41 -12
- moat/kv/server.py +113 -32
- moat/kv/types.py +6 -2
- {moat_kv-0.70.14.dist-info → moat_kv-0.70.18.dist-info}/METADATA +5 -5
- moat_kv-0.70.18.dist-info/RECORD +49 -0
- moat_kv-0.70.14.dist-info/RECORD +0 -49
- {moat_kv-0.70.14.dist-info → moat_kv-0.70.18.dist-info}/LICENSE +0 -0
- {moat_kv-0.70.14.dist-info → moat_kv-0.70.18.dist-info}/LICENSE.APACHE2 +0 -0
- {moat_kv-0.70.14.dist-info → moat_kv-0.70.18.dist-info}/LICENSE.MIT +0 -0
- {moat_kv-0.70.14.dist-info → moat_kv-0.70.18.dist-info}/WHEEL +0 -0
- {moat_kv-0.70.14.dist-info → moat_kv-0.70.18.dist-info}/top_level.txt +0 -0
moat/kv/command/data.py
CHANGED
@@ -10,9 +10,7 @@ from moat.kv.client import StreamedRequest
|
|
10
10
|
from moat.kv.data import add_dates, data_get, node_attr
|
11
11
|
|
12
12
|
|
13
|
-
@click.group(
|
14
|
-
short_help="Manage data.", invoke_without_command=True
|
15
|
-
) # pylint: disable=undefined-variable
|
13
|
+
@click.group(short_help="Manage data.", invoke_without_command=True) # pylint: disable=undefined-variable
|
16
14
|
@click.argument("path", type=P, nargs=1)
|
17
15
|
@click.pass_context
|
18
16
|
async def cli(ctx, path):
|
@@ -34,14 +32,24 @@ async def cli(ctx, path):
|
|
34
32
|
"for values. Default: return as list",
|
35
33
|
)
|
36
34
|
@click.option(
|
37
|
-
"-m",
|
35
|
+
"-m",
|
36
|
+
"--maxdepth",
|
37
|
+
type=int,
|
38
|
+
default=None,
|
39
|
+
help="Limit recursion depth. Default: whole tree",
|
38
40
|
)
|
39
41
|
@click.option(
|
40
|
-
"-M",
|
42
|
+
"-M",
|
43
|
+
"--mindepth",
|
44
|
+
type=int,
|
45
|
+
default=None,
|
46
|
+
help="Starting depth. Default: whole tree",
|
41
47
|
)
|
42
48
|
@click.option("-r", "--recursive", is_flag=True, help="Read a complete subtree")
|
43
49
|
@click.option("-e", "--empty", is_flag=True, help="Include empty nodes")
|
44
|
-
@click.option(
|
50
|
+
@click.option(
|
51
|
+
"-R", "--raw", is_flag=True, help="Print string values without quotes etc."
|
52
|
+
)
|
45
53
|
@click.option("-D", "--add-date", is_flag=True, help="Add *_date entries")
|
46
54
|
@click.pass_obj
|
47
55
|
async def get(obj, **k):
|
@@ -72,7 +80,11 @@ async def get(obj, **k):
|
|
72
80
|
help="Limit recursion depth. Default: 1 (single layer).",
|
73
81
|
)
|
74
82
|
@click.option(
|
75
|
-
"-M",
|
83
|
+
"-M",
|
84
|
+
"--mindepth",
|
85
|
+
type=int,
|
86
|
+
default=1,
|
87
|
+
help="Starting depth. Default: 1 (single layer).",
|
76
88
|
)
|
77
89
|
@click.pass_obj
|
78
90
|
async def list_(obj, **k):
|
@@ -135,12 +147,18 @@ class nstr:
|
|
135
147
|
|
136
148
|
@cli.command(short_help="Delete an entry / subtree")
|
137
149
|
@click.option(
|
138
|
-
"-p",
|
150
|
+
"-p",
|
151
|
+
"--prev",
|
152
|
+
type=nstr,
|
153
|
+
default=NotGiven,
|
154
|
+
help="Previous value. Deprecated; use 'last'",
|
139
155
|
)
|
140
156
|
@click.option("-l", "--last", nargs=2, help="Previous change entry (node serial)")
|
141
157
|
@click.option("-r", "--recursive", is_flag=True, help="Delete a complete subtree")
|
142
158
|
@click.option("--internal", is_flag=True, help="Affect the internal tree. DANGER.")
|
143
|
-
@click.option(
|
159
|
+
@click.option(
|
160
|
+
"-e", "--eval", "eval_", is_flag=True, help="The previous value shall be evaluated."
|
161
|
+
)
|
144
162
|
@click.pass_obj
|
145
163
|
async def delete(obj, prev, last, recursive, eval_, internal):
|
146
164
|
"""
|
@@ -160,7 +178,9 @@ async def delete(obj, prev, last, recursive, eval_, internal):
|
|
160
178
|
raise click.UsageError("You need to add a value that can be evaluated")
|
161
179
|
if recursive:
|
162
180
|
if prev is not NotGiven or last:
|
163
|
-
raise click.UsageError(
|
181
|
+
raise click.UsageError(
|
182
|
+
"You can't use a prev value when deleting recursively."
|
183
|
+
)
|
164
184
|
if internal:
|
165
185
|
raise click.UsageError("'internal' and 'recursive' are mutually exclusive")
|
166
186
|
else:
|
@@ -171,7 +191,9 @@ async def delete(obj, prev, last, recursive, eval_, internal):
|
|
171
191
|
if last:
|
172
192
|
args["chain"] = {"node": last[0], "tick": int(last[1])}
|
173
193
|
|
174
|
-
res = await obj.client.delete(
|
194
|
+
res = await obj.client.delete(
|
195
|
+
path=obj.path, nchain=obj.meta, recursive=recursive, **args
|
196
|
+
)
|
175
197
|
if isinstance(res, StreamedRequest):
|
176
198
|
pl = PathLongener(obj.path)
|
177
199
|
async for r in res:
|
@@ -186,40 +208,53 @@ async def delete(obj, prev, last, recursive, eval_, internal):
|
|
186
208
|
@cli.command()
|
187
209
|
@click.option("-s", "--state", is_flag=True, help="Also get the current state.")
|
188
210
|
@click.option("-o", "--only", is_flag=True, help="Value only, nothing fancy.")
|
211
|
+
@click.option("-p", "--path-only", is_flag=True, help="Value only, nothing fancy.")
|
189
212
|
@click.option("-D", "--add-date", is_flag=True, help="Add *_date entries")
|
190
213
|
@click.option("-i", "--ignore", multiple=True, type=P, help="Skip this (sub)tree")
|
191
214
|
@click.pass_obj
|
192
|
-
async def monitor(obj, state, only, add_date, ignore):
|
215
|
+
async def monitor(obj, state, only, path_only, add_date, ignore):
|
193
216
|
"""Monitor a MoaT-KV subtree"""
|
194
217
|
|
195
218
|
flushing = not state
|
196
219
|
seen = False
|
197
220
|
|
198
221
|
async with obj.client.watch(
|
199
|
-
obj.path,
|
222
|
+
obj.path,
|
223
|
+
nchain=obj.meta,
|
224
|
+
fetch=state, max_depth=0 if only else -1,
|
225
|
+
long_path=False,
|
200
226
|
) as res:
|
227
|
+
pl = PathLongener(() if path_only else obj.path)
|
201
228
|
async for r in res:
|
202
229
|
if add_date and "value" in r:
|
203
230
|
add_dates(r.value)
|
204
231
|
if any(p == r.path[: len(p)] for p in ignore):
|
205
232
|
continue
|
233
|
+
pl(r)
|
206
234
|
if r.get("state", "") == "uptodate":
|
207
235
|
if only and not seen:
|
208
236
|
# value doesn't exist
|
209
237
|
return
|
210
238
|
flushing = True
|
239
|
+
if only or path_only:
|
240
|
+
continue
|
211
241
|
else:
|
212
242
|
del r["seq"]
|
213
|
-
if only:
|
243
|
+
if only or path_only:
|
214
244
|
try:
|
215
|
-
|
245
|
+
if path_only:
|
246
|
+
print(f"{r.path} {r.value}", file=obj.stdout)
|
247
|
+
else:
|
248
|
+
print(r.value, file=obj.stdout)
|
216
249
|
continue
|
217
250
|
except AttributeError:
|
218
251
|
# value has been deleted
|
219
252
|
continue
|
220
253
|
if flushing:
|
221
254
|
r["time"] = time.time()
|
222
|
-
r["_time"] = datetime.datetime.now().isoformat(
|
255
|
+
r["_time"] = datetime.datetime.now().isoformat(
|
256
|
+
sep=" ", timespec="milliseconds"
|
257
|
+
)
|
223
258
|
yprint(r, stream=obj.stdout)
|
224
259
|
print("---", file=obj.stdout)
|
225
260
|
if flushing:
|
moat/kv/command/dump/__init__.py
CHANGED
@@ -6,7 +6,16 @@ from collections.abc import Mapping
|
|
6
6
|
|
7
7
|
import asyncclick as click
|
8
8
|
from moat.mqtt.codecs import MsgPackCodec
|
9
|
-
from moat.util import
|
9
|
+
from moat.util import (
|
10
|
+
MsgReader,
|
11
|
+
MsgWriter,
|
12
|
+
P,
|
13
|
+
Path,
|
14
|
+
PathLongener,
|
15
|
+
load_subgroup,
|
16
|
+
yload,
|
17
|
+
yprint,
|
18
|
+
)
|
10
19
|
|
11
20
|
from moat.kv.codec import unpacker
|
12
21
|
|
moat/kv/command/error.py
CHANGED
@@ -37,11 +37,17 @@ async def resolve(obj, path, subsys):
|
|
37
37
|
|
38
38
|
@cli.command()
|
39
39
|
@click.option("-n", "--node", help="add details from this node")
|
40
|
-
@click.option(
|
40
|
+
@click.option(
|
41
|
+
"-s", "--subsystem", "subsys", help="only show errors from this subsystem"
|
42
|
+
)
|
41
43
|
@click.option("-r", "--resolved", is_flag=True, help="only resolved errors")
|
42
|
-
@click.option(
|
44
|
+
@click.option(
|
45
|
+
"-v", "--verbose", count=True, help="add per-node details (-vv for traces)"
|
46
|
+
)
|
43
47
|
@click.option("-a", "--all-errors", is_flag=True, help="add details from all nodes")
|
44
|
-
@click.option(
|
48
|
+
@click.option(
|
49
|
+
"-d", "--as-dict", default=None, help="Dump a list of all open (or resolved) error."
|
50
|
+
)
|
45
51
|
@click.option("-p", "--path", default=":", help="only show errors below this subpath")
|
46
52
|
@click.pass_obj
|
47
53
|
async def dump(obj, as_dict, path, node, all_errors, verbose, resolved, subsys):
|
@@ -140,7 +146,9 @@ async def dump(obj, as_dict, path, node, all_errors, verbose, resolved, subsys):
|
|
140
146
|
path = ()
|
141
147
|
|
142
148
|
if res is None:
|
143
|
-
res = obj.client.get_tree(
|
149
|
+
res = obj.client.get_tree(
|
150
|
+
path_, min_depth=d, max_depth=d, nchain=3 if obj.meta else 0
|
151
|
+
)
|
144
152
|
async for r in res:
|
145
153
|
await one(r)
|
146
154
|
|
moat/kv/command/internal.py
CHANGED
@@ -30,7 +30,9 @@ async def cli():
|
|
30
30
|
@click.option("-s", "--superseded", is_flag=True, help="Get superseded-data status.")
|
31
31
|
@click.option("-D", "--debug", is_flag=True, help="Get internal verbosity.")
|
32
32
|
@click.option("--debugger", is_flag=True, help="Start a remote debugger. DO NOT USE.")
|
33
|
-
@click.option(
|
33
|
+
@click.option(
|
34
|
+
"-k", "--known", hidden=True, is_flag=True, help="Get superseded-data status."
|
35
|
+
)
|
34
36
|
@click.option("-a", "--all", is_flag=True, help="All available data.")
|
35
37
|
@click.pass_obj
|
36
38
|
async def state(obj, **flags):
|
@@ -54,9 +56,15 @@ async def state(obj, **flags):
|
|
54
56
|
|
55
57
|
|
56
58
|
@cli.command()
|
57
|
-
@click.option("-d", "--deleted", is_flag=True, help="Mark as deleted. Default: superseded")
|
58
59
|
@click.option(
|
59
|
-
"-
|
60
|
+
"-d", "--deleted", is_flag=True, help="Mark as deleted. Default: superseded"
|
61
|
+
)
|
62
|
+
@click.option(
|
63
|
+
"-n",
|
64
|
+
"--node",
|
65
|
+
"source",
|
66
|
+
default="?",
|
67
|
+
help="The node this message is faked as being from.",
|
60
68
|
)
|
61
69
|
@click.option("-b", "--broadcast", is_flag=True, help="Send to all servers")
|
62
70
|
@click.argument("node", nargs=1)
|
@@ -139,7 +147,11 @@ async def deleter(obj, delete, nodes):
|
|
139
147
|
|
140
148
|
val = {"nodes": list(val)}
|
141
149
|
res = await obj.client._request(
|
142
|
-
action="set_internal",
|
150
|
+
action="set_internal",
|
151
|
+
path=("actor", "del"),
|
152
|
+
iter=False,
|
153
|
+
chain=res.chain,
|
154
|
+
value=val,
|
143
155
|
)
|
144
156
|
res.value = val
|
145
157
|
yprint(res, stream=obj.stdout)
|
@@ -158,7 +170,9 @@ async def dump(obj, path):
|
|
158
170
|
path = P(path)
|
159
171
|
y = {}
|
160
172
|
pl = PathLongener()
|
161
|
-
async for r in await obj.client._request(
|
173
|
+
async for r in await obj.client._request(
|
174
|
+
"get_tree_internal", path=path, iter=True, nchain=0
|
175
|
+
):
|
162
176
|
pl(r)
|
163
177
|
path = r["path"]
|
164
178
|
yy = y
|
@@ -213,13 +227,17 @@ async def enum(obj, node, num, current, copy):
|
|
213
227
|
else:
|
214
228
|
for k in res.result:
|
215
229
|
if copy or obj.debug > 1:
|
216
|
-
res = await obj.client._request(
|
230
|
+
res = await obj.client._request(
|
231
|
+
"get_value", node=node, tick=k, nchain=3
|
232
|
+
)
|
217
233
|
if obj.debug > 1:
|
218
234
|
print(k, res.path)
|
219
235
|
else:
|
220
236
|
print(k)
|
221
237
|
if copy and res.chain.node == node:
|
222
|
-
res = await obj.client.set(
|
238
|
+
res = await obj.client.set(
|
239
|
+
res.path, value=res.value, chain=res.chain
|
240
|
+
)
|
223
241
|
else:
|
224
242
|
print(k)
|
225
243
|
|
moat/kv/command/job.py
CHANGED
@@ -14,7 +14,9 @@ from moat.kv.runner import AllRunnerRoot, AnyRunnerRoot, SingleRunnerRoot
|
|
14
14
|
|
15
15
|
|
16
16
|
@click.group() # pylint: disable=undefined-variable
|
17
|
-
@click.option(
|
17
|
+
@click.option(
|
18
|
+
"-n", "--node", help="node to run this code on. Empty: any one node, '-': all nodes"
|
19
|
+
)
|
18
20
|
@click.option("-g", "--group", help="group to run this code on. Empty: default")
|
19
21
|
@click.pass_context
|
20
22
|
async def cli(ctx, node, group):
|
@@ -54,7 +56,9 @@ async def cli(ctx, node, group):
|
|
54
56
|
obj.statepath = cfg["state"] + obj.subpath
|
55
57
|
|
56
58
|
|
57
|
-
@cli.group(
|
59
|
+
@cli.group(
|
60
|
+
"at", short_help="path of the job to operate on", invoke_without_command=True
|
61
|
+
)
|
58
62
|
@click.argument("path", nargs=1, type=P)
|
59
63
|
@click.pass_context
|
60
64
|
async def at_cli(ctx, path):
|
@@ -113,7 +117,11 @@ async def path__(obj):
|
|
113
117
|
|
114
118
|
@cli.command("run")
|
115
119
|
@click.option(
|
116
|
-
"-n",
|
120
|
+
"-n",
|
121
|
+
"--nodes",
|
122
|
+
type=int,
|
123
|
+
default=0,
|
124
|
+
help="Size of the group (not for single-node runners)",
|
117
125
|
)
|
118
126
|
@click.pass_obj
|
119
127
|
async def run(obj, nodes):
|
@@ -227,7 +235,9 @@ async def list_(obj, state, state_only, table, as_dict):
|
|
227
235
|
obj,
|
228
236
|
obj.path + path,
|
229
237
|
as_dict=as_dict,
|
230
|
-
item_mangle=partial(
|
238
|
+
item_mangle=partial(
|
239
|
+
_state_fix, obj, state, state_only, None if as_dict else path
|
240
|
+
),
|
231
241
|
)
|
232
242
|
|
233
243
|
|
@@ -306,12 +316,18 @@ async def delete(obj, force):
|
|
306
316
|
val.target = None
|
307
317
|
if val.target is not None:
|
308
318
|
val.target = None
|
309
|
-
res = await obj.client.set(
|
319
|
+
res = await obj.client.set(
|
320
|
+
obj.path + path, value=val, nchain=3, chain=res.chain
|
321
|
+
)
|
310
322
|
if not force:
|
311
323
|
res.info = "'target' was set: cleared but not deleted."
|
312
324
|
if force or val.target is None:
|
313
325
|
sres = await obj.client.get(obj.statepath + path, nchain=3)
|
314
|
-
if
|
326
|
+
if (
|
327
|
+
not force
|
328
|
+
and "value" in sres
|
329
|
+
and sres.value.stopped < sres.value.started
|
330
|
+
):
|
315
331
|
res.info = "Still running, not deleted."
|
316
332
|
else:
|
317
333
|
sres = await obj.client.delete(obj.statepath + path, chain=sres.chain)
|
@@ -334,11 +350,15 @@ async def delete(obj, force):
|
|
334
350
|
@click.option("-r", "--repeat", type=int, help="Seconds the code should re-run after")
|
335
351
|
@click.option("-k", "--ok", type=float, help="Code is OK if it ran this many seconds")
|
336
352
|
@click.option("-b", "--backoff", type=float, help="Back-off factor. Default: 1.4")
|
337
|
-
@click.option(
|
353
|
+
@click.option(
|
354
|
+
"-d", "--delay", type=int, help="Seconds the code should retry after (w/ backoff)"
|
355
|
+
)
|
338
356
|
@click.option("-i", "--info", help="Short human-readable information")
|
339
357
|
@attr_args
|
340
358
|
@click.pass_obj
|
341
|
-
async def set_(
|
359
|
+
async def set_(
|
360
|
+
obj, code, tm, info, ok, repeat, delay, backoff, copy, vars_, eval_, path_
|
361
|
+
):
|
342
362
|
"""Add or modify a runner.
|
343
363
|
|
344
364
|
Code typically requires some input parameters.
|
@@ -357,7 +377,9 @@ async def set_(obj, code, tm, info, ok, repeat, delay, backoff, copy, vars_, eva
|
|
357
377
|
copy = P(copy)
|
358
378
|
path = obj.path + P(path)
|
359
379
|
|
360
|
-
res = await obj.client._request(
|
380
|
+
res = await obj.client._request(
|
381
|
+
action="get_value", path=copy or path, iter=False, nchain=3
|
382
|
+
)
|
361
383
|
if "value" not in res:
|
362
384
|
if copy:
|
363
385
|
raise click.UsageError("--copy: use the complete path to an existing entry")
|
moat/kv/command/server.py
CHANGED
@@ -37,7 +37,12 @@ from moat.kv.server import Server
|
|
37
37
|
hidden=True,
|
38
38
|
)
|
39
39
|
@click.option(
|
40
|
-
"-e",
|
40
|
+
"-e",
|
41
|
+
"--eval",
|
42
|
+
"eval_",
|
43
|
+
is_flag=True,
|
44
|
+
help="The 'init' value shall be evaluated.",
|
45
|
+
hidden=True,
|
41
46
|
)
|
42
47
|
@click.option(
|
43
48
|
"-a",
|
@@ -47,7 +52,10 @@ from moat.kv.server import Server
|
|
47
52
|
help="Data in this file is complete: mark anything missing as known even if not.",
|
48
53
|
)
|
49
54
|
@click.option(
|
50
|
-
"-f",
|
55
|
+
"-f",
|
56
|
+
"--force",
|
57
|
+
is_flag=True,
|
58
|
+
help="Force 'successful' startup even if data are missing.",
|
51
59
|
)
|
52
60
|
@click.argument("name", nargs=1)
|
53
61
|
@click.argument("nodes", nargs=-1)
|
@@ -91,7 +99,9 @@ async def cli(obj, name, load, save, init, incremental, eval_, auth, force, node
|
|
91
99
|
from moat.util import as_service
|
92
100
|
|
93
101
|
if load and nodes:
|
94
|
-
raise click.UsageError(
|
102
|
+
raise click.UsageError(
|
103
|
+
"Either read from a file or fetch from a node. Not both."
|
104
|
+
)
|
95
105
|
if auth and force:
|
96
106
|
raise click.UsageError("Using both '-a' and '-f' is redundant. Choose one.")
|
97
107
|
|
moat/kv/command/type.py
CHANGED
@@ -13,9 +13,15 @@ async def cli():
|
|
13
13
|
|
14
14
|
|
15
15
|
@cli.command()
|
16
|
-
@click.option(
|
17
|
-
|
18
|
-
|
16
|
+
@click.option(
|
17
|
+
"-s", "--script", type=click.File(mode="w", lazy=True), help="Save the script here"
|
18
|
+
)
|
19
|
+
@click.option(
|
20
|
+
"-S", "--schema", type=click.File(mode="w", lazy=True), help="Save the schema here"
|
21
|
+
)
|
22
|
+
@click.option(
|
23
|
+
"-y", "--yaml", "yaml_", is_flag=True, help="Write schema as YAML. Default: JSON."
|
24
|
+
)
|
19
25
|
@click.argument("path", type=P, nargs=1)
|
20
26
|
@click.pass_obj
|
21
27
|
async def get(obj, path, script, schema, yaml_):
|
@@ -45,10 +51,18 @@ async def get(obj, path, script, schema, yaml_):
|
|
45
51
|
@cli.command("set")
|
46
52
|
@click.option("-g", "--good", multiple=True, help="Example for passing values")
|
47
53
|
@click.option("-b", "--bad", multiple=True, help="Example for failing values")
|
48
|
-
@click.option(
|
49
|
-
|
50
|
-
|
51
|
-
@click.option(
|
54
|
+
@click.option(
|
55
|
+
"-d", "--data", type=click.File(mode="r"), help="Load metadata from this YAML file."
|
56
|
+
)
|
57
|
+
@click.option(
|
58
|
+
"-s", "--script", type=click.File(mode="r"), help="File with the checking script"
|
59
|
+
)
|
60
|
+
@click.option(
|
61
|
+
"-S", "--schema", type=click.File(mode="r"), help="File with the JSON schema"
|
62
|
+
)
|
63
|
+
@click.option(
|
64
|
+
"-y", "--yaml", "yaml_", is_flag=True, help="load the schema as YAML. Default: JSON"
|
65
|
+
)
|
52
66
|
@click.argument("path", type=P, nargs=1)
|
53
67
|
@click.pass_obj
|
54
68
|
async def set_(obj, path, good, bad, script, schema, yaml_, data):
|
@@ -140,7 +154,9 @@ async def match(obj, path, type_, delete, raw): # pylint: disable=redefined-bui
|
|
140
154
|
raise click.UsageError("You can only print the raw path when reading a match.")
|
141
155
|
|
142
156
|
if delete:
|
143
|
-
res = await obj.client._request(
|
157
|
+
res = await obj.client._request(
|
158
|
+
action="delete_internal", path=Path("type") + path
|
159
|
+
)
|
144
160
|
if obj.meta:
|
145
161
|
yprint(res, stream=obj.stdout)
|
146
162
|
return
|
moat/kv/data.py
CHANGED
@@ -81,7 +81,9 @@ async def data_get(
|
|
81
81
|
kw.setdefault("nchain", obj.meta)
|
82
82
|
y = {}
|
83
83
|
if internal:
|
84
|
-
res = await obj.client._request(
|
84
|
+
res = await obj.client._request(
|
85
|
+
action="get_tree_internal", path=path, iter=True, **kw
|
86
|
+
)
|
85
87
|
else:
|
86
88
|
res = obj.client.get_tree(path, **kw)
|
87
89
|
async for r in res:
|
moat/kv/errors.py
CHANGED
@@ -113,7 +113,11 @@ class ErrorSubEntry(AttrClientEntry):
|
|
113
113
|
return ClientEntry
|
114
114
|
|
115
115
|
def __repr__(self):
|
116
|
-
return "‹%s %s %d›" % (
|
116
|
+
return "‹%s %s %d›" % (
|
117
|
+
self.__class__.__name__,
|
118
|
+
self._path[-3:],
|
119
|
+
getattr(self, "tock", -1),
|
120
|
+
)
|
117
121
|
|
118
122
|
async def set_value(self, value):
|
119
123
|
await super().set_value(value)
|
@@ -239,7 +243,12 @@ class ErrorEntry(AttrClientEntry):
|
|
239
243
|
Store this comment, typically used when something resumes working.
|
240
244
|
One per node, so we don't need to avoid collisions.
|
241
245
|
"""
|
242
|
-
res = dict(
|
246
|
+
res = dict(
|
247
|
+
seen=time(),
|
248
|
+
tock=await self.root.client.get_tock(),
|
249
|
+
comment=comment,
|
250
|
+
data=data,
|
251
|
+
)
|
243
252
|
logger.info("Comment %s: %s", node, comment)
|
244
253
|
await self.root.client.set(self._path, chain=self.chain, value=res)
|
245
254
|
|
@@ -295,7 +304,9 @@ class ErrorEntry(AttrClientEntry):
|
|
295
304
|
if value is NotGiven:
|
296
305
|
if self.value is NotGiven:
|
297
306
|
return
|
298
|
-
keep = await self.root.get_error_record(
|
307
|
+
keep = await self.root.get_error_record(
|
308
|
+
self.subsystem, self.path, create=False
|
309
|
+
)
|
299
310
|
if keep is not None:
|
300
311
|
self._real_entry = keep.real_entry
|
301
312
|
await self.move_to_real()
|
@@ -501,7 +512,12 @@ class ErrorRoot(ClientRoot):
|
|
501
512
|
await rec.save()
|
502
513
|
except ClosedResourceError:
|
503
514
|
logger.exception(
|
504
|
-
"Could not save error %s %s: %s %r",
|
515
|
+
"Could not save error %s %s: %s %r",
|
516
|
+
subsystem,
|
517
|
+
path,
|
518
|
+
message,
|
519
|
+
exc,
|
520
|
+
exc_info=exc,
|
505
521
|
)
|
506
522
|
return # owch, but can't be helped
|
507
523
|
|
@@ -519,7 +535,9 @@ class ErrorRoot(ClientRoot):
|
|
519
535
|
return
|
520
536
|
|
521
537
|
try:
|
522
|
-
del (self._done if entry.resolved else self._active)[entry.subsystem][
|
538
|
+
del (self._done if entry.resolved else self._active)[entry.subsystem][
|
539
|
+
entry.path
|
540
|
+
]
|
523
541
|
except KeyError:
|
524
542
|
pass
|
525
543
|
|
moat/kv/mock/__init__.py
CHANGED
@@ -60,7 +60,8 @@ class S:
|
|
60
60
|
continue
|
61
61
|
try:
|
62
62
|
cfg = combine_dict(
|
63
|
-
dict(conn=dict(host=host, port=port, ssl=self.client_ctx, **kv)),
|
63
|
+
dict(conn=dict(host=host, port=port, ssl=self.client_ctx, **kv)),
|
64
|
+
CFG["kv"],
|
64
65
|
)
|
65
66
|
|
66
67
|
async def scc(s, **cfg):
|
@@ -91,4 +92,6 @@ class S:
|
|
91
92
|
if isinstance(args, str):
|
92
93
|
args = args.split(" ")
|
93
94
|
async with scope.using_scope():
|
94
|
-
return await run(
|
95
|
+
return await run(
|
96
|
+
"-VV", "kv", "-h", h, "-p", p, *args, do_stdout=do_stdout
|
97
|
+
)
|
moat/kv/mock/mqtt.py
CHANGED
@@ -129,10 +129,14 @@ async def stdtest(n=1, run=True, ssl=False, tocks=20, **kw):
|
|
129
129
|
args_def.pop("init", None)
|
130
130
|
s = Server(name, **args)
|
131
131
|
ex.enter_context(
|
132
|
-
mock.patch.object(
|
132
|
+
mock.patch.object(
|
133
|
+
s, "_set_tock", new=partial(mock_set_tock, s, s._set_tock)
|
134
|
+
)
|
133
135
|
)
|
134
136
|
ex.enter_context(
|
135
|
-
mock.patch.object(
|
137
|
+
mock.patch.object(
|
138
|
+
s, "_get_host_port", new=partial(mock_get_host_port, st)
|
139
|
+
)
|
136
140
|
)
|
137
141
|
st.s.append(s)
|
138
142
|
|
@@ -143,7 +147,9 @@ async def stdtest(n=1, run=True, ssl=False, tocks=20, **kw):
|
|
143
147
|
await scp.spawn_service(with_broker, st.s[i], ready_evt=evt)
|
144
148
|
evts.append(evt)
|
145
149
|
else:
|
146
|
-
setattr(
|
150
|
+
setattr(
|
151
|
+
st, f"run_{i}", partial(scp.spawn_service, with_broker, st.s[i])
|
152
|
+
)
|
147
153
|
|
148
154
|
for e in evts:
|
149
155
|
await e.wait()
|
moat/kv/mock/serf.py
CHANGED
@@ -121,10 +121,14 @@ async def stdtest(n=1, run=True, ssl=False, tocks=20, **kw):
|
|
121
121
|
)
|
122
122
|
s = Server(name, **args)
|
123
123
|
ex.enter_context(
|
124
|
-
mock.patch.object(
|
124
|
+
mock.patch.object(
|
125
|
+
s, "_set_tock", new=partial(mock_set_tock, s, s._set_tock)
|
126
|
+
)
|
125
127
|
)
|
126
128
|
ex.enter_context(
|
127
|
-
mock.patch.object(
|
129
|
+
mock.patch.object(
|
130
|
+
s, "_get_host_port", new=partial(mock_get_host_port, st)
|
131
|
+
)
|
128
132
|
)
|
129
133
|
st.s.append(s)
|
130
134
|
|
moat/kv/mock/tracer.py
CHANGED
@@ -34,7 +34,9 @@ class Tracer(trio.abc.Instrument):
|
|
34
34
|
pass
|
35
35
|
|
36
36
|
def before_task_step(self, task):
|
37
|
-
if isinstance(task._next_send, Error) and isinstance(
|
37
|
+
if isinstance(task._next_send, Error) and isinstance(
|
38
|
+
task._next_send.error, Exception
|
39
|
+
):
|
38
40
|
self._print_with_task("*** step resume ERROR", task, task._next_send.error)
|
39
41
|
self.etasks.add(task)
|
40
42
|
elif moat.kill: # pylint: disable=c-extension-no-member # OH COME ON
|