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