moat-kv 0.70.22__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.
Files changed (52) hide show
  1. moat/kv/__init__.py +1 -0
  2. moat/kv/_cfg.yaml +97 -0
  3. moat/kv/_main.py +6 -9
  4. moat/kv/client.py +34 -50
  5. moat/kv/code.py +10 -3
  6. moat/kv/codec.py +1 -0
  7. moat/kv/config.py +2 -0
  8. moat/kv/data.py +8 -7
  9. moat/kv/errors.py +17 -9
  10. moat/kv/exceptions.py +1 -7
  11. moat/kv/model.py +16 -24
  12. moat/kv/runner.py +38 -35
  13. moat/kv/server.py +86 -90
  14. moat/kv/types.py +5 -8
  15. {moat_kv-0.70.22.dist-info → moat_kv-0.70.23.dist-info}/METADATA +15 -18
  16. moat_kv-0.70.23.dist-info/RECORD +19 -0
  17. {moat_kv-0.70.22.dist-info → moat_kv-0.70.23.dist-info}/WHEEL +1 -1
  18. moat_kv-0.70.23.dist-info/licenses/LICENSE.txt +14 -0
  19. moat/kv/_config.yaml +0 -98
  20. moat/kv/actor/__init__.py +0 -97
  21. moat/kv/actor/deletor.py +0 -137
  22. moat/kv/auth/__init__.py +0 -446
  23. moat/kv/auth/_test.py +0 -172
  24. moat/kv/auth/password.py +0 -232
  25. moat/kv/auth/root.py +0 -56
  26. moat/kv/backend/__init__.py +0 -66
  27. moat/kv/backend/mqtt.py +0 -74
  28. moat/kv/backend/serf.py +0 -44
  29. moat/kv/command/__init__.py +0 -1
  30. moat/kv/command/acl.py +0 -174
  31. moat/kv/command/auth.py +0 -258
  32. moat/kv/command/code.py +0 -306
  33. moat/kv/command/codec.py +0 -190
  34. moat/kv/command/data.py +0 -274
  35. moat/kv/command/dump/__init__.py +0 -141
  36. moat/kv/command/error.py +0 -156
  37. moat/kv/command/internal.py +0 -257
  38. moat/kv/command/job.py +0 -438
  39. moat/kv/command/log.py +0 -52
  40. moat/kv/command/server.py +0 -115
  41. moat/kv/command/type.py +0 -203
  42. moat/kv/mock/__init__.py +0 -97
  43. moat/kv/mock/mqtt.py +0 -164
  44. moat/kv/mock/serf.py +0 -253
  45. moat/kv/mock/tracer.py +0 -65
  46. moat/kv/obj/__init__.py +0 -636
  47. moat/kv/obj/command.py +0 -246
  48. moat_kv-0.70.22.dist-info/LICENSE +0 -3
  49. moat_kv-0.70.22.dist-info/LICENSE.APACHE2 +0 -202
  50. moat_kv-0.70.22.dist-info/LICENSE.MIT +0 -20
  51. moat_kv-0.70.22.dist-info/RECORD +0 -49
  52. {moat_kv-0.70.22.dist-info → moat_kv-0.70.23.dist-info}/top_level.txt +0 -0
moat/kv/command/auth.py DELETED
@@ -1,258 +0,0 @@
1
- # command line interface
2
-
3
- import asyncclick as click
4
- from moat.util import NotGiven, Path, split_arg, yprint
5
-
6
- from moat.kv.auth import gen_auth, loader
7
-
8
-
9
- @click.group(short_help="Manage authorization") # pylint: disable=undefined-variable
10
- @click.option("-m", "--method", default=None, help="Affect/use this auth method")
11
- @click.pass_obj
12
- async def cli(obj, method):
13
- """Manage authorization. Usage: … auth METHOD command…. Use '.' for 'all methods'."""
14
- a = await obj.client._request(action="auth_info")
15
- a = a.get("value", None)
16
- if a is not None:
17
- a = a["current"]
18
- obj.auth_current = a
19
- if method is None:
20
- obj.auth = a or (await one_auth(obj))
21
- else:
22
- if method == "-":
23
- method = None
24
- obj.auth = method
25
-
26
-
27
- async def enum_auth(obj):
28
- """List all configured auth types."""
29
- if obj.get("auth", None) is not None:
30
- yield obj.auth
31
- return
32
- # TODO create a method for this
33
- res = await obj.client._request(
34
- action="enum_internal",
35
- path=Path("auth"),
36
- iter=False,
37
- with_data=False,
38
- empty=True,
39
- nchain=0,
40
- )
41
- for r in res.result:
42
- print(r)
43
- yield r
44
- pass
45
-
46
-
47
- async def one_auth(obj):
48
- """Return the current auth method (from the command line or as used by the server)."""
49
- if obj.get("auth", None) is not None:
50
- return obj.auth
51
- auth = None
52
- async for a in enum_auth(obj):
53
- if auth is not None:
54
- raise click.UsageError("You need to set the auth method")
55
- auth = a
56
- if auth is None:
57
- raise click.UsageError("You need to set the auth method")
58
- return auth
59
-
60
-
61
- async def enum_typ(obj, kind="user", ident=None, nchain=0):
62
- """List all known auth entries of a kind."""
63
- async for auth in enum_auth(obj):
64
- if ident is not None:
65
- res = await obj.client._request(
66
- action="auth_list",
67
- typ=auth,
68
- kind=kind,
69
- ident=ident,
70
- iter=False,
71
- nchain=nchain,
72
- )
73
- yield res
74
- else:
75
- async with obj.client._stream(
76
- action="auth_list", typ=auth, kind=kind, nchain=nchain
77
- ) as res:
78
- async for r in res:
79
- yield r
80
-
81
-
82
- @cli.command("list")
83
- @click.pass_obj
84
- async def list_(obj):
85
- """List known auth methods"""
86
- async for auth in enum_auth(obj):
87
- print(auth, file=obj.stdout)
88
-
89
-
90
- @cli.command()
91
- @click.option("-s", "--switch", is_flag=True, help="Switch to a different auth method")
92
- @click.pass_obj
93
- async def init(obj, switch):
94
- """Setup authorization"""
95
- if obj.auth_current is not None and not switch:
96
- raise click.UsageError("Authentication is already set up")
97
-
98
- await obj.client._request(action="set_auth_typ", typ=obj.auth)
99
- if obj.debug >= 0:
100
- if obj.auth:
101
- print("Authorization switched to", obj.auth, file=obj.stdout)
102
- else:
103
- print("Authorization turned off.", file=obj.stdout)
104
-
105
-
106
- @cli.group()
107
- async def user():
108
- """Manage users."""
109
- pass
110
-
111
-
112
- @user.command("list")
113
- @click.option(
114
- "-v",
115
- "--verbose",
116
- is_flag=True,
117
- help="Print complete results. Default: just the names",
118
- )
119
- @click.pass_obj # pylint: disable=function-redefined
120
- async def list_user(obj, verbose):
121
- """List all users (raw data)."""
122
- async for r in enum_typ(obj, nchain=obj.meta):
123
- if obj.meta or verbose:
124
- if obj.debug < 2:
125
- del r["seq"]
126
- del r["tock"]
127
- yprint(r, stream=obj.stdout)
128
- else:
129
- print(r.ident, file=obj.stdout)
130
-
131
-
132
- @user.command()
133
- @click.argument("ident", nargs=1)
134
- @click.pass_obj
135
- async def get(obj, ident):
136
- """Retrieve a user (processed)."""
137
- lv = loader(await one_auth(obj), "user", make=True, server=False)
138
- if obj.DEBUG:
139
- lv._length = 16
140
-
141
- u = await lv.recv(obj.client, ident, _initial=False)
142
- yprint(u.export(), stream=obj.stdout)
143
-
144
-
145
- @user.command()
146
- @click.argument("args", nargs=-1)
147
- @click.pass_obj
148
- async def add(obj, args):
149
- """Add a user."""
150
- await add_mod_user(obj, args, None)
151
-
152
-
153
- @user.command()
154
- @click.option("-n", "--new", is_flag=True, help="New: ignore previous content")
155
- @click.argument("ident", nargs=1)
156
- @click.argument("type", nargs=1)
157
- @click.argument("key", nargs=1)
158
- @click.argument("args", nargs=-1)
159
- @click.pass_obj
160
- async def param(obj, new, ident, type, key, args): # pylint: disable=redefined-builtin
161
- """Set user parameters for auth, conversion, etc."""
162
- auth = await one_auth(obj)
163
- u = loader(auth, "user", make=True, server=False)
164
- if obj.DEBUG:
165
- u._length = 16
166
- # ou = await u.recv(obj.client, ident, _initial=False) # unused
167
- res = await obj.client._request(
168
- action="get_internal",
169
- path=("auth", auth, "user", ident, type),
170
- iter=False,
171
- nchain=3,
172
- )
173
-
174
- kw = res.get("value", NotGiven)
175
- if new or kw is NotGiven:
176
- kw = {}
177
- res.chain = None
178
- if key == "-":
179
- if args:
180
- raise click.UsageError("You can't set params when deleting")
181
- res = await obj.client._request(
182
- action="delete_internal",
183
- path=("auth", auth, "user", ident, type),
184
- iter=False,
185
- chain=res.chain,
186
- )
187
-
188
- else:
189
- kw["key"] = key
190
-
191
- for a in args:
192
- split_arg(a, kw)
193
-
194
- res = await obj.client._request(
195
- action="set_internal",
196
- path=("auth", auth, "user", ident, type),
197
- iter=False,
198
- chain=res.chain,
199
- value=kw,
200
- )
201
- if obj.meta:
202
- # res.ident = ident
203
- # res.type = type
204
- yprint(res, stream=obj.stdout)
205
- else:
206
- print(ident, type, key, file=obj.stdout)
207
-
208
-
209
- @user.command()
210
- @click.argument("ident", nargs=1)
211
- @click.argument("args", nargs=-1)
212
- @click.pass_obj
213
- async def mod(obj, ident, args):
214
- """Change a user."""
215
- await add_mod_user(obj, args, ident)
216
-
217
-
218
- async def add_mod_user(obj, args, modify):
219
- auth = await one_auth(obj)
220
- u = loader(auth, "user", make=True, server=False)
221
- if obj.DEBUG:
222
- u._length = 16
223
- if modify:
224
- ou = await u.recv(obj.client, modify, _initial=False)
225
- kw = ou.export()
226
- else:
227
- kw = {}
228
- for a in args:
229
- split_arg(a, kw)
230
-
231
- u = u.build(kw, _initial=False)
232
- if modify is None or u.ident != modify:
233
- u._chain = None # new user
234
- else:
235
- u._chain = ou._chain
236
- res = await u.send(obj.client)
237
- if obj.meta:
238
- res.ident = u.ident
239
- yprint(res, stream=obj.stdout)
240
- else:
241
- print(u.ident, file=obj.stdout)
242
-
243
-
244
- @user.command(name="auth")
245
- @click.option(
246
- "-a",
247
- "--auth",
248
- type=str,
249
- default="root",
250
- help="Auth params. =file or 'type param=value…' Default: root",
251
- )
252
- @click.pass_obj
253
- async def auth_(obj, auth):
254
- """Test user authorization."""
255
- user = gen_auth(auth) # pylint: disable=redefined-outer-name
256
- await user.auth(obj.client)
257
- if obj.debug > 0:
258
- print("OK.", file=obj.stdout)
moat/kv/command/code.py DELETED
@@ -1,306 +0,0 @@
1
- # command line interface
2
-
3
- import sys
4
-
5
- import asyncclick as click
6
- from moat.util import (
7
- NotGiven,
8
- P,
9
- Path,
10
- PathLongener,
11
- attr_args,
12
- process_args,
13
- yload,
14
- yprint,
15
- )
16
-
17
-
18
- @click.group(invoke_without_command=True) # pylint: disable=undefined-variable
19
- @click.argument("path", nargs=1, type=P)
20
- @click.pass_context
21
- async def cli(ctx, path):
22
- """Manage code stored in MoaT-KV."""
23
- obj = ctx.obj
24
- obj.path = obj.cfg["kv"]["codes"]["prefix"] + path
25
- obj.codepath = path
26
-
27
- if ctx.invoked_subcommand is None:
28
- pl = PathLongener(path)
29
- async for res in obj.client.get_tree(obj.path, long_path=False):
30
- pl(res)
31
- print(Path(*res.path), res.value.info, file=obj.stdout)
32
-
33
-
34
- @cli.command()
35
- @click.option(
36
- "-s", "--script", type=click.File(mode="w", lazy=True), help="Save the code here"
37
- )
38
- @click.pass_obj
39
- async def get(obj, script):
40
- """Read a code entry"""
41
- if not len(obj.codepath):
42
- raise click.UsageError("You need a non-empty path.")
43
-
44
- res = await obj.client._request(
45
- action="get_value", path=obj.path, iter=False, nchain=obj.meta
46
- )
47
- if "value" not in res:
48
- if obj.debug:
49
- print("No entry here.", file=sys.stderr)
50
- sys.exit(1)
51
- if not obj.meta:
52
- res = res.value
53
- if script:
54
- code = res.pop("code", None)
55
- if code is not None:
56
- print(code, file=script)
57
- yprint(res, stream=obj.stdout)
58
-
59
-
60
- @cli.command("set")
61
- @click.option(
62
- "-a/-A",
63
- "--async/--sync",
64
- "async_",
65
- is_flag=True,
66
- help="The code is async / sync (default: async)",
67
- default=True,
68
- )
69
- @click.option(
70
- "-t", "--thread", is_flag=True, help="The code should run in a worker thread"
71
- )
72
- @click.option("-s", "--script", type=click.File(mode="r"), help="File with the code")
73
- @click.option("-i", "--info", type=str, help="one-liner info about the code")
74
- @click.option(
75
- "-d", "--data", type=click.File(mode="r"), help="load the metadata (YAML)"
76
- )
77
- @attr_args
78
- @click.pass_obj
79
- async def set_(obj, thread, script, data, vars_, eval_, path_, async_, info):
80
- """Save Python code.
81
-
82
- The code may have inputs. You specify the inputs and their default
83
- values with '-v VAR VALUE' (string), '-p VAR PATH' (MoaT-KV path), or
84
- '-e VAR EXPR' (simple Python expression). Use '-e VAR -' to state that
85
- VAR shall not have a default value, and '-e VAR /' to delete VAR from
86
- the list of inputs entirely.
87
- """
88
- if thread:
89
- async_ = False
90
- elif not async_:
91
- async_ = None
92
-
93
- if not len(obj.codepath):
94
- raise click.UsageError("You need a non-empty path.")
95
- path = obj.path
96
-
97
- if data:
98
- msg = yload(data)
99
- else:
100
- msg = await obj.client.get(path, nchain=3)
101
- chain = NotGiven
102
- if "value" in msg:
103
- chain = msg.get("chain", NotGiven)
104
- msg = msg["value"]
105
- if async_ is not None or "is_async" not in msg:
106
- msg["is_async"] = async_
107
-
108
- if info is not None:
109
- msg["info"] = info
110
-
111
- if script:
112
- msg["code"] = script.read()
113
- elif "code" not in msg:
114
- raise click.UsageError("Missing script")
115
-
116
- if "vars" in msg:
117
- vs = set(msg["vars"])
118
- else:
119
- vs = set()
120
- vd = msg.setdefault("default", {})
121
-
122
- vd = process_args(vd, vars_, eval_, path_, vs=vs)
123
- msg["vars"] = list(vs)
124
- msg["default"] = vd
125
-
126
- kv = {}
127
- if chain is not NotGiven:
128
- kv["chain"] = chain
129
-
130
- res = await obj.client.set(obj.path, value=msg, nchain=obj.meta, **kv)
131
- if obj.meta:
132
- yprint(res, stream=obj.stdout)
133
-
134
-
135
- # disabled for now
136
- @cli.group("module", hidden=True)
137
- async def mod():
138
- """
139
- Change the code of a module stored in MoaT-KV
140
- """
141
-
142
-
143
- @mod.command("get")
144
- @click.option(
145
- "-s", "--script", type=click.File(mode="w", lazy=True), help="Save the code here"
146
- )
147
- @click.argument("path", nargs=1)
148
- @click.pass_obj # pylint: disable=function-redefined
149
- async def get_mod(obj, path, script):
150
- """Read a module entry"""
151
- path = P(path)
152
- if not len(path):
153
- raise click.UsageError("You need a non-empty path.")
154
- res = await obj.client._request(
155
- action="get_value",
156
- path=obj.cfg["kv"]["modules"]["prefix"] + path,
157
- iter=False,
158
- nchain=obj.meta,
159
- )
160
- if not obj.meta:
161
- res = res.value
162
-
163
- code = res.pop("code", None)
164
- if code is not None:
165
- code = code.rstrip("\n \t") + "\n"
166
- if script:
167
- print(code, file=script)
168
- else:
169
- res["code"] = code
170
-
171
- yprint(res, stream=obj.stdout)
172
-
173
-
174
- @mod.command("set")
175
- @click.option(
176
- "-s", "--script", type=click.File(mode="r"), help="File with the module's code"
177
- )
178
- @click.option(
179
- "-d", "--data", type=click.File(mode="r"), help="load the metadata (YAML)"
180
- )
181
- @click.argument("path", nargs=1) # pylint: disable=function-redefined
182
- @click.pass_obj
183
- async def set_mod(obj, path, script, data):
184
- """Save a Python module to MoaT-KV."""
185
- path = P(path)
186
- if not len(path):
187
- raise click.UsageError("You need a non-empty path.")
188
-
189
- if data:
190
- msg = yload(data)
191
- else:
192
- msg = {}
193
- chain = None
194
- if "value" in msg:
195
- chain = msg.get("chain", None)
196
- msg = msg["value"]
197
-
198
- if "code" not in msg:
199
- if script:
200
- raise click.UsageError("Duplicate script")
201
- else:
202
- if not script:
203
- raise click.UsageError("Missing script")
204
- msg["code"] = script.read()
205
-
206
- res = await obj.client.set(
207
- *obj.cfg["kv"]["modules"]["prefix"],
208
- *path,
209
- value=msg,
210
- iter=False,
211
- nchain=obj.meta,
212
- chain=chain,
213
- )
214
- if obj.meta:
215
- yprint(res, stream=obj.stdout)
216
-
217
-
218
- @cli.command("list")
219
- @click.option(
220
- "-d",
221
- "--as-dict",
222
- default=None,
223
- help="Structure as dictionary. The argument is the key to use "
224
- "for values. Default: return as list",
225
- )
226
- @click.option(
227
- "-m",
228
- "--maxdepth",
229
- type=int,
230
- default=None,
231
- help="Limit recursion depth. Default: whole tree",
232
- )
233
- @click.option(
234
- "-M",
235
- "--mindepth",
236
- type=int,
237
- default=None,
238
- help="Starting depth. Default: whole tree",
239
- )
240
- @click.option("-f", "--full", is_flag=True, help="print complete entries.")
241
- @click.option("-s", "--short", is_flag=True, help="print shortened entries.")
242
- @click.pass_obj
243
- async def list_(obj, as_dict, maxdepth, mindepth, full, short):
244
- """
245
- List code entries.
246
-
247
- Be aware that the whole subtree will be read before anything is
248
- printed if you use the `--as-dict` option.
249
- """
250
-
251
- if (full or as_dict) and short:
252
- raise click.UsageError("'-f'/'-d' and '-s' are incompatible.")
253
- kw = {}
254
- if maxdepth is not None:
255
- kw["max_depth"] = maxdepth
256
- if mindepth is not None:
257
- kw["min_depth"] = mindepth
258
- y = {}
259
- async for r in obj.client.get_tree(obj.path, nchain=obj.meta, **kw):
260
- r.pop("seq", None)
261
- path = r.pop("path")
262
- if not full:
263
- if "info" not in r.value:
264
- r.value.info = f"<{len(r.value.code.splitlines())} lines>"
265
- del r.value["code"]
266
-
267
- if short:
268
- print(path, "::", r.value.info)
269
- continue
270
-
271
- if as_dict is not None:
272
- yy = y
273
- for p in path:
274
- yy = yy.setdefault(p, {})
275
- try:
276
- yy[as_dict] = r if obj.meta else r.value
277
- except AttributeError:
278
- continue
279
- else:
280
- y = {}
281
- try:
282
- y[path] = r if obj.meta else r.value
283
- except AttributeError:
284
- continue
285
- yprint([y], stream=obj.stdout)
286
-
287
- if as_dict is not None:
288
- yprint(y, stream=obj.stdout)
289
- return
290
-
291
-
292
- @cli.command()
293
- @click.pass_obj
294
- async def delete(obj):
295
- """Remove a code entry"""
296
- res = await obj.client.get(obj.path, nchain=3)
297
- if "value" not in res:
298
- res.info = "Does not exist."
299
- else:
300
- res = await obj.client.delete(obj.path, chain=res.chain)
301
- res.info = "Deleted."
302
-
303
- if obj.meta:
304
- yprint(res, stream=obj.stdout)
305
- elif obj.debug:
306
- print(res.info, file=obj.stdout)