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
@@ -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)