moat-kv 0.70.24__py3-none-any.whl → 0.71.6__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 (127) hide show
  1. moat/kv/__init__.py +6 -7
  2. moat/kv/_cfg.yaml +5 -8
  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 -8
  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 +29 -29
  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 +28 -32
  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 +8 -6
  25. {moat_kv-0.70.24.dist-info → moat_kv-0.71.6.dist-info}/METADATA +7 -6
  26. moat_kv-0.71.6.dist-info/RECORD +47 -0
  27. {moat_kv-0.70.24.dist-info → moat_kv-0.71.6.dist-info}/WHEEL +1 -1
  28. moat_kv-0.71.6.dist-info/licenses/LICENSE +3 -0
  29. moat_kv-0.71.6.dist-info/licenses/LICENSE.APACHE2 +202 -0
  30. moat_kv-0.71.6.dist-info/licenses/LICENSE.MIT +20 -0
  31. moat_kv-0.71.6.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 -97
  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 -74
  45. build/lib/moat/kv/backend/serf.py +0 -45
  46. build/lib/moat/kv/client.py +0 -1025
  47. build/lib/moat/kv/code.py +0 -236
  48. build/lib/moat/kv/codec.py +0 -11
  49. build/lib/moat/kv/command/__init__.py +0 -1
  50. build/lib/moat/kv/command/acl.py +0 -180
  51. build/lib/moat/kv/command/auth.py +0 -261
  52. build/lib/moat/kv/command/code.py +0 -293
  53. build/lib/moat/kv/command/codec.py +0 -186
  54. build/lib/moat/kv/command/data.py +0 -265
  55. build/lib/moat/kv/command/dump/__init__.py +0 -143
  56. build/lib/moat/kv/command/error.py +0 -149
  57. build/lib/moat/kv/command/internal.py +0 -248
  58. build/lib/moat/kv/command/job.py +0 -433
  59. build/lib/moat/kv/command/log.py +0 -53
  60. build/lib/moat/kv/command/server.py +0 -114
  61. build/lib/moat/kv/command/type.py +0 -201
  62. build/lib/moat/kv/config.py +0 -46
  63. build/lib/moat/kv/data.py +0 -216
  64. build/lib/moat/kv/errors.py +0 -561
  65. build/lib/moat/kv/exceptions.py +0 -126
  66. build/lib/moat/kv/mock/__init__.py +0 -101
  67. build/lib/moat/kv/mock/mqtt.py +0 -159
  68. build/lib/moat/kv/mock/serf.py +0 -250
  69. build/lib/moat/kv/mock/tracer.py +0 -63
  70. build/lib/moat/kv/model.py +0 -1069
  71. build/lib/moat/kv/obj/__init__.py +0 -646
  72. build/lib/moat/kv/obj/command.py +0 -241
  73. build/lib/moat/kv/runner.py +0 -1347
  74. build/lib/moat/kv/server.py +0 -2809
  75. build/lib/moat/kv/types.py +0 -513
  76. debian/moat-kv/usr/lib/python3/dist-packages/docs/source/conf.py +0 -201
  77. debian/moat-kv/usr/lib/python3/dist-packages/examples/pathify.py +0 -45
  78. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/__init__.py +0 -19
  79. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/_cfg.yaml +0 -97
  80. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/_main.py +0 -91
  81. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/actor/__init__.py +0 -98
  82. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/actor/deletor.py +0 -139
  83. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/__init__.py +0 -444
  84. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/_test.py +0 -166
  85. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/password.py +0 -234
  86. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/root.py +0 -58
  87. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/__init__.py +0 -67
  88. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/mqtt.py +0 -74
  89. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/serf.py +0 -45
  90. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/client.py +0 -1025
  91. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/code.py +0 -236
  92. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/codec.py +0 -11
  93. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/__init__.py +0 -1
  94. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/acl.py +0 -180
  95. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/auth.py +0 -261
  96. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/code.py +0 -293
  97. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/codec.py +0 -186
  98. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/data.py +0 -265
  99. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/dump/__init__.py +0 -143
  100. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/error.py +0 -149
  101. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/internal.py +0 -248
  102. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/job.py +0 -433
  103. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/log.py +0 -53
  104. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/server.py +0 -114
  105. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/type.py +0 -201
  106. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/config.py +0 -46
  107. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/data.py +0 -216
  108. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/errors.py +0 -561
  109. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/exceptions.py +0 -126
  110. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/__init__.py +0 -101
  111. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/mqtt.py +0 -159
  112. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/serf.py +0 -250
  113. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/tracer.py +0 -63
  114. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/model.py +0 -1069
  115. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/obj/__init__.py +0 -646
  116. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/obj/command.py +0 -241
  117. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/runner.py +0 -1347
  118. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/server.py +0 -2809
  119. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/types.py +0 -513
  120. docs/source/conf.py +0 -201
  121. examples/pathify.py +0 -45
  122. moat/kv/backend/serf.py +0 -45
  123. moat/kv/codec.py +0 -11
  124. moat/kv/mock/serf.py +0 -250
  125. moat_kv-0.70.24.dist-info/RECORD +0 -137
  126. moat_kv-0.70.24.dist-info/top_level.txt +0 -9
  127. {moat_kv-0.70.24.dist-info → moat_kv-0.71.6.dist-info}/licenses/LICENSE.txt +0 -0
@@ -1,265 +0,0 @@
1
- # command line interface
2
- from __future__ import annotations
3
-
4
- import datetime
5
- import time
6
-
7
- import asyncclick as click
8
- from moat.util import MsgReader, NotGiven, P, PathLongener, attr_args, yprint
9
-
10
- from moat.kv.client import StreamedRequest
11
- from moat.kv.data import add_dates, data_get, node_attr
12
-
13
-
14
- @click.group(short_help="Manage data.", invoke_without_command=True) # pylint: disable=undefined-variable
15
- @click.argument("path", type=P, nargs=1)
16
- @click.pass_context
17
- async def cli(ctx, path):
18
- """
19
- This subcommand accesses the actual user data stored in your MoaT-KV tree.
20
- """
21
- if ctx.invoked_subcommand is None:
22
- await data_get(ctx.obj, path, recursive=False)
23
- else:
24
- ctx.obj.path = path
25
-
26
-
27
- @cli.command()
28
- @click.option(
29
- "-d",
30
- "--as-dict",
31
- default=None,
32
- help="Structure as dictionary. The argument is the key to use "
33
- "for values. Default: return as list",
34
- )
35
- @click.option(
36
- "-m",
37
- "--maxdepth",
38
- type=int,
39
- default=None,
40
- help="Limit recursion depth. Default: whole tree",
41
- )
42
- @click.option(
43
- "-M",
44
- "--mindepth",
45
- type=int,
46
- default=None,
47
- help="Starting depth. Default: whole tree",
48
- )
49
- @click.option("-r", "--recursive", is_flag=True, help="Read a complete subtree")
50
- @click.option("-e", "--empty", is_flag=True, help="Include empty nodes")
51
- @click.option("-R", "--raw", is_flag=True, help="Print string values without quotes etc.")
52
- @click.option("-D", "--add-date", is_flag=True, help="Add *_date entries")
53
- @click.pass_obj
54
- async def get(obj, **k):
55
- """
56
- Read a MoaT-KV value.
57
-
58
- If you read a sub-tree recursively, be aware that the whole subtree
59
- will be read before anything is printed. Use the "watch --state" subcommand
60
- for incremental output.
61
- """
62
-
63
- await data_get(obj, obj.path, **k)
64
-
65
-
66
- @cli.command("list")
67
- @click.option(
68
- "-d",
69
- "--as-dict",
70
- default=None,
71
- help="Structure as dictionary. The argument is the key to use "
72
- "for values. Default: return as list",
73
- )
74
- @click.option(
75
- "-m",
76
- "--maxdepth",
77
- type=int,
78
- default=1,
79
- help="Limit recursion depth. Default: 1 (single layer).",
80
- )
81
- @click.option(
82
- "-M",
83
- "--mindepth",
84
- type=int,
85
- default=1,
86
- help="Starting depth. Default: 1 (single layer).",
87
- )
88
- @click.pass_obj
89
- async def list_(obj, **k):
90
- """
91
- List MoaT-KV values.
92
-
93
- This is like "get" but with "--mindepth=1 --maxdepth=1 --recursive --empty"
94
-
95
- If you read a sub-tree recursively, be aware that the whole subtree
96
- will be read before anything is printed. Use the "watch --state" subcommand
97
- for incremental output.
98
- """
99
-
100
- k["recursive"] = True
101
- k["raw"] = True
102
- k["empty"] = True
103
- await data_get(obj, obj.path, **k)
104
-
105
-
106
- @cli.command("set", short_help="Add or update an entry")
107
- @attr_args
108
- @click.option("-l", "--last", nargs=2, help="Previous change entry (node serial)")
109
- @click.option("-n", "--new", is_flag=True, help="This is a new entry.")
110
- @click.pass_obj
111
- async def set_(obj, last, new, **kw):
112
- """
113
- Store a value at some MoaT-KV position.
114
-
115
- If you update a value you can use "--last" to ensure that no other
116
- change arrived between reading and writing the entry. (This is not
117
- foolproof but reduces the window to a fraction of a second.)
118
-
119
- When adding a new entry use "--new" to ensure that you don't
120
- accidentally overwrite something.
121
-
122
- MoaT-KV entries typically are mappings. Use a colon as the path if you
123
- want to replace the top level.
124
- """
125
- if new:
126
- if last:
127
- raise click.UsageError("'new' and 'last' are mutually exclusive")
128
- kw["chain"] = None
129
- else:
130
- if last:
131
- kw["chain"] = {"node": last[0], "tick": int(last[1])}
132
-
133
- res = await node_attr(obj, obj.path, **kw)
134
-
135
- if obj.meta:
136
- yprint(res, stream=obj.stdout)
137
-
138
-
139
- class nstr:
140
- def __new__(cls, val):
141
- if val is NotGiven:
142
- return val
143
- return str(val)
144
-
145
-
146
- @cli.command(short_help="Delete an entry / subtree")
147
- @click.option(
148
- "-p",
149
- "--prev",
150
- type=nstr,
151
- default=NotGiven,
152
- help="Previous value. Deprecated; use 'last'",
153
- )
154
- @click.option("-l", "--last", nargs=2, help="Previous change entry (node serial)")
155
- @click.option("-r", "--recursive", is_flag=True, help="Delete a complete subtree")
156
- @click.option("--internal", is_flag=True, help="Affect the internal tree. DANGER.")
157
- @click.option("-e", "--eval", "eval_", is_flag=True, help="The previous value shall be evaluated.")
158
- @click.pass_obj
159
- async def delete(obj, prev, last, recursive, eval_, internal):
160
- """
161
- Delete an entry, or a whole subtree.
162
-
163
- You really should use "--last" (preferred) or "--prev" (if you must) to
164
- ensure that no other change arrived (but note that this doesn't work
165
- when deleting a subtree).
166
-
167
- Non-recursively deleting an entry with children works and does *not*
168
- affect the child entries.
169
-
170
- The root entry cannot be deleted.
171
- """
172
- args = {}
173
- if eval_ and prev is NotGiven:
174
- raise click.UsageError("You need to add a value that can be evaluated")
175
- if recursive:
176
- if prev is not NotGiven or last:
177
- raise click.UsageError("You can't use a prev value when deleting recursively.")
178
- if internal:
179
- raise click.UsageError("'internal' and 'recursive' are mutually exclusive")
180
- else:
181
- if prev is not NotGiven:
182
- if eval_:
183
- prev = eval(prev) # pylint: disable=eval-used
184
- args["prev"] = prev
185
- if last:
186
- args["chain"] = {"node": last[0], "tick": int(last[1])}
187
-
188
- res = await obj.client.delete(path=obj.path, nchain=obj.meta, recursive=recursive, **args)
189
- if isinstance(res, StreamedRequest):
190
- pl = PathLongener(obj.path)
191
- async for r in res:
192
- pl(r)
193
- if obj.meta:
194
- yprint(r, stream=obj.stdout)
195
- else:
196
- if obj.meta:
197
- yprint(res, stream=obj.stdout)
198
-
199
-
200
- @cli.command()
201
- @click.option("-s", "--state", is_flag=True, help="Also get the current state.")
202
- @click.option("-o", "--only", is_flag=True, help="Value only, nothing fancy.")
203
- @click.option("-p", "--path-only", is_flag=True, help="Value only, nothing fancy.")
204
- @click.option("-D", "--add-date", is_flag=True, help="Add *_date entries")
205
- @click.option("-i", "--ignore", multiple=True, type=P, help="Skip this (sub)tree")
206
- @click.pass_obj
207
- async def monitor(obj, state, only, path_only, add_date, ignore):
208
- """Monitor a MoaT-KV subtree"""
209
-
210
- flushing = not state
211
- seen = False
212
-
213
- async with obj.client.watch(
214
- obj.path,
215
- nchain=obj.meta,
216
- fetch=state,
217
- max_depth=0 if only else -1,
218
- long_path=False,
219
- ) as res:
220
- pl = PathLongener(() if path_only else obj.path)
221
- async for r in res:
222
- if add_date and "value" in r:
223
- add_dates(r.value)
224
- if any(p == r.path[: len(p)] for p in ignore):
225
- continue
226
- pl(r)
227
- if r.get("state", "") == "uptodate":
228
- if only and not seen:
229
- # value doesn't exist
230
- return
231
- flushing = True
232
- if only or path_only:
233
- continue
234
- else:
235
- del r["seq"]
236
- if only or path_only:
237
- try:
238
- if path_only:
239
- print(f"{r.path} {r.value}", file=obj.stdout)
240
- else:
241
- print(r.value, file=obj.stdout)
242
- continue
243
- except AttributeError:
244
- # value has been deleted
245
- continue
246
- if flushing:
247
- r["time"] = time.time()
248
- r["_time"] = datetime.datetime.now().isoformat(sep=" ", timespec="milliseconds")
249
- yprint(r, stream=obj.stdout)
250
- print("---", file=obj.stdout)
251
- if flushing:
252
- obj.stdout.flush()
253
- seen = True
254
-
255
-
256
- @cli.command()
257
- @click.option("-i", "--infile", type=click.Path(), help="File to read (msgpack).")
258
- @click.pass_obj
259
- async def update(obj, infile):
260
- """Send a list of updates to a MoaT-KV subtree"""
261
- async with MsgReader(path=infile) as reader:
262
- async for msg in reader:
263
- if not hasattr(msg, "path"):
264
- continue
265
- await obj.client.set(obj.path + msg.path, value=msg.value)
@@ -1,143 +0,0 @@
1
- # command line interface
2
- from __future__ import annotations
3
-
4
- import datetime
5
- import sys
6
- from collections.abc import Mapping
7
-
8
- import asyncclick as click
9
- from moat.mqtt.codecs import MsgPackCodec
10
- from moat.util import (
11
- MsgReader,
12
- MsgWriter,
13
- P,
14
- Path,
15
- PathLongener,
16
- load_subgroup,
17
- yload,
18
- yprint,
19
- )
20
-
21
- from moat.kv.codec import unpacker
22
-
23
-
24
- @load_subgroup(short_help="Local data mangling", sub_pre="dump")
25
- async def cli():
26
- """
27
- Low-level tools that don't depend on a running MoaT-KV server.
28
- """
29
- pass
30
-
31
-
32
- @cli.command()
33
- @click.argument("node", nargs=1)
34
- @click.argument("file", type=click.Path(), nargs=1)
35
- async def init(node, file):
36
- """Write an initial preload file.
37
-
38
- Usage: moat kv dump init <node> <outfile>
39
-
40
- Writes an initial MoaT-KV file that behaves as if it was generated by <node>.
41
-
42
- Using this command, followed by "moat kv server -l <outfile> <node>", is
43
- equivalent to running "moat kv server -i 'Initial data' <node>.
44
- """
45
- async with MsgWriter(path=file) as f:
46
- await f(
47
- dict(
48
- chain=dict(node=node, tick=1, prev=None),
49
- depth=0,
50
- path=[],
51
- tock=1,
52
- value="Initial data",
53
- ),
54
- )
55
-
56
-
57
- @cli.command("msg")
58
- @click.argument("path", nargs=1)
59
- @click.pass_obj
60
- async def msg_(obj, path):
61
- """
62
- Monitor the server-to-sever message stream.
63
-
64
- Use ':' for the main server's "update" stream.
65
- Use '+NAME' to monitor a different stream instead.
66
- Use '+' to monitor all streams.
67
- Otherwise use the given name as-is; Mosquitto wildcard rules apply.
68
-
69
- \b
70
- Common streams (prefix with '+'):
71
- * ping all servers
72
- * update data changes
73
- * del nodes responsible for cleaning up deleted records
74
- """
75
- from moat.kv.backend import get_backend
76
-
77
- class _Unpack:
78
- def __init__(self):
79
- self._part_cache = dict()
80
-
81
- import moat.kv.server
82
-
83
- _Unpack._unpack_multiple = moat.kv.server.Server._unpack_multiple
84
- _unpacker = _Unpack()._unpack_multiple
85
-
86
- path = P(path)
87
- if len(path) == 0:
88
- path = P(obj.cfg.server["root"]) | "update"
89
- elif len(path) == 1 and path[0].startswith("+"): # pylint: disable=no-member # owch
90
- p = path[0][1:]
91
- path = P(obj.cfg.server["root"])
92
- path |= p or "#"
93
- be = obj.cfg.server.backend
94
- kw = obj.cfg.server[be]
95
-
96
- async with get_backend(be)(**kw) as conn:
97
- async with conn.monitor(*path) as stream:
98
- async for msg in stream:
99
- v = vars(msg)
100
- if isinstance(v.get("payload"), (bytearray, bytes)):
101
- t = msg.topic
102
- v = unpacker(v["payload"])
103
- v = _unpacker(v)
104
- if v is None:
105
- continue
106
- if not isinstance(v, Mapping):
107
- v = {"_data": v}
108
- v["_topic"] = Path.build(t)
109
- else:
110
- v["_type"] = type(msg).__name__
111
-
112
- v["_timestamp"] = datetime.datetime.now().isoformat(
113
- sep=" ",
114
- timespec="milliseconds",
115
- )
116
-
117
- yprint(v, stream=obj.stdout)
118
- print("---", file=obj.stdout)
119
-
120
-
121
- @cli.command("post")
122
- @click.argument("path", nargs=1)
123
- @click.pass_obj
124
- async def post_(obj, path):
125
- """
126
- Send a msgpack-encoded message (or several) to a specific MQTT topic.
127
-
128
- Messages are read from YAML.
129
- Common streams:
130
- * ping: sync: all servers (default)
131
- * update: data changes
132
- * del: sync: nodes responsible for cleaning up deleted records
133
- """
134
- from moat.kv.backend import get_backend
135
-
136
- path = P(path)
137
- be = obj.cfg.server.backend
138
- kw = obj.cfg.server[be]
139
-
140
- async with get_backend(be)(codec=MsgPackCodec, **kw) as conn:
141
- for d in yload(sys.stdin, multi=True):
142
- topic = d.pop("_topic", path)
143
- await conn.send(*topic, payload=d)
@@ -1,149 +0,0 @@
1
- # command line interface
2
- from __future__ import annotations
3
-
4
- import sys
5
-
6
- import asyncclick as click
7
- from moat.util import P, Path, yprint
8
-
9
- from moat.kv.data import add_dates
10
- from moat.kv.errors import ErrorRoot
11
-
12
-
13
- @click.group() # pylint: disable=undefined-variable
14
- @click.pass_obj
15
- async def cli(obj):
16
- """Manage error records in MoaT-KV."""
17
- obj.err = await ErrorRoot.as_handler(obj.client)
18
-
19
-
20
- @cli.command()
21
- @click.option("-s", "--subsys", help="Subsystem to access")
22
- @click.argument("path", nargs=1)
23
- @click.pass_obj
24
- async def resolve(obj, path, subsys):
25
- """
26
- Mark an error as resolved.
27
- """
28
- path = P(path)
29
- if subsys:
30
- e = obj.err.get_error_record(subsys, path)
31
- else:
32
- e = obj.err.follow(path)
33
- if e.resolved:
34
- print("Already resolved.", file=sys.stderr)
35
- return
36
- await e.resolve()
37
-
38
-
39
- @cli.command()
40
- @click.option("-n", "--node", help="add details from this node")
41
- @click.option("-s", "--subsystem", "subsys", help="only show errors from this subsystem")
42
- @click.option("-r", "--resolved", is_flag=True, help="only resolved errors")
43
- @click.option("-v", "--verbose", count=True, help="add per-node details (-vv for traces)")
44
- @click.option("-a", "--all-errors", is_flag=True, help="add details from all nodes")
45
- @click.option("-d", "--as-dict", default=None, help="Dump a list of all open (or resolved) error.")
46
- @click.option("-p", "--path", default=":", help="only show errors below this subpath")
47
- @click.pass_obj
48
- async def dump(obj, as_dict, path, node, all_errors, verbose, resolved, subsys):
49
- """Dump error entries."""
50
- path = P(path)
51
- path_ = obj.cfg["errors"].prefix
52
- d = 2
53
-
54
- async def one(r):
55
- nonlocal y
56
- val = r.value
57
- if subsys and val.get("subsystem", "") != subsys:
58
- return
59
- if path and val.get("path", ())[: len(path)] != path:
60
- return
61
-
62
- if not all_errors and bool(val.get("resolved", False)) != resolved:
63
- return
64
-
65
- add_dates(val)
66
- try:
67
- rp = val.path
68
- if as_dict:
69
- del val.path
70
- elif not isinstance(rp, Path):
71
- rp = Path.build(rp)
72
- except AttributeError:
73
- rp = Path("incomplete") + r.path
74
- if not as_dict:
75
- val.path = rp
76
- if rp[: len(path)] != path:
77
- return
78
- rp = rp[len(path) :]
79
- if node is None:
80
- val.syspath = r.path
81
- else:
82
- val.syspath = Path(node) + r.path
83
-
84
- rn = {}
85
-
86
- def disp(rr):
87
- if node and rr.path[-1] != node:
88
- return
89
- val = rr.value
90
- add_dates(val)
91
- if verbose < 2:
92
- rr.value.pop("trace", None)
93
- rn[rr.path[-1]] = rr if obj.meta else rr.value
94
-
95
- if verbose:
96
- rs = await obj.client._request(
97
- action="get_tree",
98
- min_depth=1,
99
- max_depth=1,
100
- path=path_ + r.path[-2:],
101
- iter=True,
102
- nchain=3 if obj.meta else 0,
103
- )
104
- async for rr in rs:
105
- disp(rr)
106
-
107
- elif node:
108
- rs = await obj.client.get(path_ + r.path[-2:] | node)
109
- if "value" in rs:
110
- disp(rs)
111
-
112
- if rn:
113
- val["nodes"] = rn
114
-
115
- if as_dict is not None:
116
- yy = y
117
- if subsys is None:
118
- yy = yy.setdefault(val.get("subsystem", "unknown"), {})
119
- for p in rp:
120
- yy = yy.setdefault(p, {})
121
- yy[as_dict] = r if obj.meta else val
122
- else:
123
- yy = r if obj.meta else r.value
124
-
125
- yprint([yy], stream=obj.stdout)
126
- print("---", file=obj.stdout)
127
-
128
- y = {}
129
- res = None
130
-
131
- if node is None and len(path) == 2 and isinstance(path[-1], int): # single error?
132
- r = await obj.client.get(path_ + path, nchain=3 if obj.meta else 0)
133
- # Mangle a few variables so that the output is still OK
134
- r.path = path
135
- node = None
136
-
137
- async def ait(r):
138
- yield r
139
-
140
- res = ait(r)
141
- path = ()
142
-
143
- if res is None:
144
- res = obj.client.get_tree(path_, min_depth=d, max_depth=d, nchain=3 if obj.meta else 0)
145
- async for r in res:
146
- await one(r)
147
-
148
- if as_dict is not None:
149
- yprint(y, stream=obj.stdout)