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
moat/kv/__init__.py CHANGED
@@ -1,19 +1,18 @@
1
1
  # pylint: disable=W0703,C0103
2
2
  from __future__ import annotations
3
+ import contextlib
3
4
 
4
5
  __path__ = __import__("pkgutil").extend_path(__path__, __name__)
5
6
 
6
7
  try:
7
- import warning
8
- import pkg_resources # part of setuptools
9
-
10
- with warnings.filterwarnings("ignore"):
11
- _version = pkg_resources.require("moat.kv")[0].version
12
- del pkg_resources
13
- del warnings
8
+ from importlib.metadata import version
14
9
 
10
+ _version = version("moat.kv")
15
11
  _version_tuple = tuple(int(x) for x in _version.split("."))
16
12
 
17
13
  except Exception: # pragma: no cover
18
14
  _version = "0.0.1"
19
15
  _version_tuple = (0, 0, 1)
16
+
17
+ with contextlib.suppress(NameError):
18
+ del version
moat/kv/_cfg.yaml CHANGED
@@ -21,12 +21,11 @@ modules:
21
21
  prefix: !P :.moat.kv.code.module
22
22
  runner: # for moat.kv.runner.RunnerRoot
23
23
  # storage for runnable commands
24
- prefix: !P :.moat.kv.run"
24
+ prefix: !P :.moat.kv.run
25
25
  # storage for runner states
26
- state: !P :.moat.kv.state"
26
+ state: !P :.moat.kv.state
27
27
 
28
28
  name: "run"
29
- # Serf event name, suffixed by subpath
30
29
 
31
30
  start_delay: 1
32
31
  # time to wait between job starts. Not optional.
@@ -56,9 +55,7 @@ server:
56
55
  # default
57
56
  mqtt:
58
57
  uri: "mqtt://localhost:1883"
59
- serf:
60
- host: "localhost"
61
- port: 7373
58
+ codec: std-msgpack
62
59
 
63
60
  # event message path/topic prefix
64
61
  root: !P moat.kv
@@ -79,10 +76,10 @@ server:
79
76
  ping:
80
77
  cycle: 10
81
78
  gap: 2
82
- # asyncserf.Actor config timing for server sync
79
+ # asyncactor config timing for server sync
83
80
  # ping also controls minimum server startup time
84
81
  delete:
85
- # asyncserf.Actor config timing for deletion
82
+ # asyncactor config timing for deletion
86
83
  cycle: 100
87
84
  gap: 10
88
85
  version: 1
moat/kv/actor/__init__.py CHANGED
@@ -2,6 +2,7 @@
2
2
  This module implements a :class:`asyncactor.Actor` which works on top of
3
3
  a MoaT-KV client.
4
4
  """
5
+
5
6
  from __future__ import annotations
6
7
 
7
8
  from asyncactor import Actor
@@ -71,7 +72,7 @@ class ActorState:
71
72
  self.msg = msg
72
73
 
73
74
  def __repr__(self):
74
- return "<%s:%r>" % (self.__class__.__name__, self.msg)
75
+ return f"<{self.__class__.__name__}:{self.msg!r}>"
75
76
 
76
77
 
77
78
  class BrokenState(ActorState):
moat/kv/actor/deletor.py CHANGED
@@ -8,9 +8,12 @@ from __future__ import annotations
8
8
  import weakref
9
9
  from collections import deque
10
10
 
11
- import anyio
12
11
  from asyncactor import Actor, PingEvent, TagEvent
13
12
  from asyncactor.backend import get_transport
13
+ from typing import TYPE_CHECKING
14
+
15
+ if TYPE_CHECKING:
16
+ import anyio
14
17
 
15
18
  TAGS = 4
16
19
 
moat/kv/auth/__init__.py CHANGED
@@ -61,17 +61,20 @@ The server process is:
61
61
 
62
62
  from __future__ import annotations
63
63
 
64
- import io
65
64
  from importlib import import_module
66
65
 
67
66
  import jsonschema
68
67
  from moat.util import NotGiven, Path, attrdict, split_arg, yload
69
68
 
70
- from ..client import Client, NoData
71
- from ..exceptions import NoAuthModuleError
72
- from ..model import Entry
73
- from ..server import ServerClient, StreamCommand
74
- from ..types import ACLFinder, NullACL
69
+ from moat.kv.client import Client, NoData
70
+ from moat.kv.exceptions import NoAuthModuleError
71
+ from moat.kv.types import ACLFinder, NullACL
72
+ import contextlib
73
+ from typing import TYPE_CHECKING
74
+
75
+ if TYPE_CHECKING:
76
+ from moat.kv.server import ServerClient, StreamCommand
77
+ from moat.kv.model import Entry
75
78
 
76
79
  # Empty schema
77
80
  null_schema = {"type": "object", "additionalProperties": False}
@@ -211,7 +214,7 @@ class BaseClientAuth(_AuthLoaded):
211
214
  """
212
215
  Authorizes this record with the server.
213
216
  """
214
- try:
217
+ with contextlib.suppress(NoData):
215
218
  await client._request(
216
219
  action="auth",
217
220
  typ=self._auth_method,
@@ -220,8 +223,6 @@ class BaseClientAuth(_AuthLoaded):
220
223
  ident=self.ident,
221
224
  data=self.auth_data(),
222
225
  )
223
- except NoData:
224
- pass
225
226
 
226
227
  def auth_data(self):
227
228
  """
@@ -293,7 +294,7 @@ class BaseClientAuthMaker(_AuthLoaded):
293
294
 
294
295
  async def send(self, client: Client, _kind="user"):
295
296
  """Send this user to the server."""
296
- try:
297
+ with contextlib.suppress(NoData):
297
298
  await client._request(
298
299
  "auth_set",
299
300
  iter=False,
@@ -303,8 +304,6 @@ class BaseClientAuthMaker(_AuthLoaded):
303
304
  chain=self._chain,
304
305
  data=self.send_data(),
305
306
  )
306
- except NoData:
307
- pass
308
307
 
309
308
  def send_data(self):
310
309
  return {}
@@ -340,7 +339,7 @@ class BaseServerAuth(_AuthLoaded):
340
339
  jsonschema.validate(instance=data.get("data", {}), schema=type(self).schema)
341
340
 
342
341
  def aux_conv(self, data: Entry, root: Entry):
343
- from ..types import ConvNull
342
+ from moat.kv.types import ConvNull
344
343
 
345
344
  try:
346
345
  data = data["conv"].data["key"]
moat/kv/auth/_test.py CHANGED
@@ -11,7 +11,6 @@ import logging
11
11
 
12
12
  log = logging.getLogger(__name__)
13
13
 
14
- from ..client import Client
15
14
  from . import (
16
15
  BaseClientAuth,
17
16
  BaseClientAuthMaker,
@@ -20,6 +19,10 @@ from . import (
20
19
  null_client_login,
21
20
  null_server_login,
22
21
  )
22
+ from typing import TYPE_CHECKING
23
+
24
+ if TYPE_CHECKING:
25
+ from moat.kv.client import Client
23
26
 
24
27
 
25
28
  def load(typ: str, *, make: bool = False, server: bool):
moat/kv/auth/password.py CHANGED
@@ -9,10 +9,8 @@ from __future__ import annotations
9
9
 
10
10
  import nacl.secret
11
11
 
12
- from ..client import Client, NoData
13
- from ..exceptions import AuthFailedError
14
- from ..model import Entry
15
- from ..server import StreamCommand
12
+ from moat.kv.client import Client, NoData
13
+ from moat.kv.exceptions import AuthFailedError
16
14
  from . import (
17
15
  BaseClientAuth,
18
16
  BaseClientAuthMaker,
@@ -21,6 +19,12 @@ from . import (
21
19
  null_client_login,
22
20
  null_server_login,
23
21
  )
22
+ import contextlib
23
+ from typing import TYPE_CHECKING
24
+
25
+ if TYPE_CHECKING:
26
+ from moat.kv.server import StreamCommand
27
+ from moat.kv.model import Entry
24
28
 
25
29
 
26
30
  def load(typ: str, *, make: bool = False, server: bool):
@@ -57,6 +61,8 @@ async def pack_pwd(client, password, length):
57
61
  async def unpack_pwd(client, password):
58
62
  """Server side: extract password"""
59
63
  box = nacl.secret.SecretBox(client.dh_key)
64
+ if isinstance(password, memoryview):
65
+ password = bytes(password)
60
66
  pwd = box.decrypt(password)
61
67
  return pwd
62
68
  # TODO check with Argon2
@@ -164,10 +170,8 @@ class ClientUserMaker(BaseClientAuthMaker):
164
170
  # There's no reason to send the password hash back
165
171
  self = cls(_initial=_initial)
166
172
  self._name = m.name
167
- try:
173
+ with contextlib.suppress(AttributeError):
168
174
  self._chain = m.chain
169
- except AttributeError:
170
- pass
171
175
  return self
172
176
 
173
177
  async def send(self, client: Client, _kind="user", **msg): # pylint: disable=unused-argument,arguments-differ
moat/kv/backend/mqtt.py CHANGED
@@ -4,16 +4,12 @@ from contextlib import asynccontextmanager
4
4
 
5
5
  import anyio
6
6
  from moat.mqtt.client import MQTTClient
7
- from moat.mqtt.codecs import NoopCodec
8
7
  from moat.util import NotGiven
9
8
 
10
9
  from . import Backend
11
10
 
12
11
  logger = logging.getLogger(__name__)
13
12
 
14
- # Simply setting connect=asyncserf.serf_client interferes with mocking
15
- # when testing.
16
-
17
13
 
18
14
  class MqttMessage:
19
15
  def __init__(self, topic, payload):
@@ -39,11 +35,11 @@ class MqttBackend(Backend):
39
35
  await C.disconnect()
40
36
 
41
37
  @asynccontextmanager
42
- async def monitor(self, *topic):
38
+ async def monitor(self, *topic, codec=NotGiven):
43
39
  topic = "/".join(str(x) for x in topic)
44
40
  logger.info("Monitor %s start", topic)
45
41
  try:
46
- async with self.client.subscription(topic) as sub:
42
+ async with self.client.subscription(topic, codec=codec) as sub:
47
43
 
48
44
  async def sub_get(sub):
49
45
  async for msg in sub:
@@ -58,12 +54,12 @@ class MqttBackend(Backend):
58
54
  else:
59
55
  logger.info("Monitor %s end", topic)
60
56
 
61
- def send(self, *topic, payload): # pylint: disable=invalid-overridden-method
57
+ def send(self, *topic, payload, **kw): # pylint: disable=invalid-overridden-method
62
58
  """
63
59
  Send this payload to this topic.
64
60
  """
65
61
  # client.publish is also async, pass-thru
66
- return self.client.publish("/".join(str(x) for x in topic), message=payload)
62
+ return self.client.publish("/".join(str(x) for x in topic), message=payload, **kw)
67
63
 
68
64
 
69
65
  @asynccontextmanager
moat/kv/client.py CHANGED
@@ -7,13 +7,13 @@ Main entry point: :func:`open_client`.
7
7
  from __future__ import annotations
8
8
 
9
9
  import logging
10
- import os
11
10
  import socket
12
- from contextlib import AsyncExitStack, asynccontextmanager
11
+ from contextlib import AsyncExitStack, asynccontextmanager, suppress
13
12
  from inspect import iscoroutine
14
13
 
15
14
  import anyio
16
15
  from asyncscope import Scope, main_scope, scope
16
+ from moat.lib.codec import get_codec
17
17
  from moat.util import ( # pylint: disable=no-name-in-module
18
18
  CFG,
19
19
  DelayedRead,
@@ -28,11 +28,11 @@ from moat.util import ( # pylint: disable=no-name-in-module
28
28
  create_queue,
29
29
  ensure_cfg,
30
30
  gen_ssl,
31
- gen_ident,al_lower,
31
+ gen_ident,
32
+ al_lower,
32
33
  num2byte,
33
34
  )
34
35
 
35
- from .codec import packer, stream_unpacker
36
36
  from .exceptions import (
37
37
  CancelledError,
38
38
  ClientAuthMethodError,
@@ -50,13 +50,6 @@ ClosedResourceError = anyio.ClosedResourceError
50
50
  __all__ = ["NoData", "ManyData", "open_client", "client_scope", "StreamedRequest"]
51
51
 
52
52
 
53
- class AsyncValueEvent(ValueEvent):
54
- def cancel(self):
55
- if self.scope is not None:
56
- self.scope.cancel()
57
- super().set_error(CancelledError())
58
-
59
-
60
53
  class NoData(ValueError):
61
54
  """No reply arrived"""
62
55
 
@@ -264,10 +257,8 @@ class StreamedRequest:
264
257
  except (anyio.BrokenResourceError, anyio.ClosedResourceError, EOFError):
265
258
  pass
266
259
  else:
267
- try:
260
+ with suppress(ServerClosedError):
268
261
  await self.aclose()
269
- except ServerClosedError:
270
- pass
271
262
 
272
263
  async def wait_started(self):
273
264
  await self._started.wait()
@@ -281,10 +272,8 @@ class StreamedRequest:
281
272
  if self._open:
282
273
  msg = dict(action="stop", task=self.seq)
283
274
  # self._client.logger.debug("SendC %s", msg)
284
- try:
275
+ with suppress(ServerClosedError, anyio.BrokenResourceError):
285
276
  await self._client._request(**msg, _async=True)
286
- except (ServerClosedError, anyio.BrokenResourceError):
287
- pass
288
277
  # ignore the reply
289
278
  finally:
290
279
  self.qr.close_sender()
@@ -301,7 +290,7 @@ class _SingleReply:
301
290
  def __init__(self, conn, seq, params):
302
291
  self._conn = conn
303
292
  self.seq = seq
304
- self.q = AsyncValueEvent()
293
+ self.q = ValueEvent()
305
294
  self._params = params
306
295
 
307
296
  async def set(self, msg):
@@ -401,6 +390,7 @@ class Client:
401
390
  ensure_cfg("moat.kv")
402
391
  self._cfg = combine_dict(cfg, CFG["kv"], cls=attrdict)
403
392
  self.config = ClientConfig(self)
393
+ self.codec = get_codec("std-msgpack")
404
394
 
405
395
  self._seq = 0
406
396
  self._handlers = {}
@@ -484,26 +474,24 @@ class Client:
484
474
  raise ServerClosedError("Disconnected")
485
475
 
486
476
  try:
487
- p = packer(params)
477
+ p = self.codec.encode(params)
488
478
  except TypeError as e:
489
479
  raise ValueError(f"Unable to pack: {params!r}") from e
490
480
  await sock.send(p)
491
481
 
492
482
  async def _reader(self, *, evt=None):
493
483
  """Main loop for reading"""
494
- unpacker = stream_unpacker()
495
-
496
484
  with anyio.CancelScope():
497
485
  # XXX store the scope so that the redaer may get cancelled?
498
486
  if evt is not None:
499
487
  await evt.set()
500
488
  try:
501
489
  while True:
502
- for msg in unpacker:
490
+ for msg in self.codec:
503
491
  # self.logger.debug("Recv %s", msg)
504
492
  try:
505
493
  await self._handle_msg(msg)
506
- except ClosedResourceError as exc:
494
+ except (ClosedResourceError,EOFError) as exc:
507
495
  logger.warning("Reader closed in handler", exc_info=exc)
508
496
  return
509
497
 
@@ -518,7 +506,7 @@ class Client:
518
506
  return # closed by us
519
507
  if len(buf) == 0: # Connection was closed.
520
508
  raise ServerClosedError("Connection closed by peer")
521
- unpacker.feed(buf)
509
+ self.codec.feed(buf)
522
510
 
523
511
  except BaseException as exc:
524
512
  logger.warning("Reader died: %r", exc, exc_info=exc)
@@ -554,7 +542,7 @@ class Client:
554
542
  Any other keywords are forwarded to the server.
555
543
  """
556
544
  if self._handlers is None:
557
- raise ClosedResourceError()
545
+ raise ClosedResourceError
558
546
  if seq is None:
559
547
  act = "action"
560
548
  self._seq += 1
@@ -612,7 +600,7 @@ class Client:
612
600
 
613
601
  async with client._stream("update", path=P("private.storage"),
614
602
  stream=True) as req:
615
- with MsgReader("/tmp/msgs.pack") as f:
603
+ with MsgReader("/tmp/msgs.pack",codec="std-cbor") as f:
616
604
  for msg in f:
617
605
  await req.send(msg)
618
606
  # … or …
@@ -647,17 +635,12 @@ class Client:
647
635
  yield res
648
636
  except BaseException as exc:
649
637
  if stream:
650
- try:
638
+ with suppress(anyio.ClosedResourceError):
651
639
  await res.send(error=repr(exc))
652
- except anyio.ClosedResourceError:
653
- pass
654
640
  raise
655
641
  finally:
656
- with anyio.fail_after(2, shield=True):
657
- try:
658
- await res.aclose()
659
- except anyio.ClosedResourceError:
660
- pass
642
+ with anyio.fail_after(2, shield=True), suppress(anyio.ClosedResourceError):
643
+ await res.aclose()
661
644
 
662
645
  async def _run_auth(self, auth=None):
663
646
  """
@@ -684,7 +667,7 @@ class Client:
684
667
  This async context manager handles the actual TCP connection to
685
668
  the MoaT-KV server.
686
669
  """
687
- hello = AsyncValueEvent()
670
+ hello = ValueEvent()
688
671
  self._handlers[0] = hello
689
672
 
690
673
  cfg = self._cfg["conn"]
@@ -737,10 +720,8 @@ class Client:
737
720
  yield self
738
721
  finally:
739
722
  # Clean up our hacked config
740
- try:
723
+ with suppress(AttributeError):
741
724
  del self._config
742
- except AttributeError:
743
- pass
744
725
  self.config = ClientConfig(self)
745
726
  finally:
746
727
  self._socket = None
@@ -1007,7 +988,7 @@ class Client:
1007
988
  """
1008
989
  return self._stream(action="msg_monitor", topic=topic, raw=raw)
1009
990
 
1010
- def msg_send(self, topic: tuple[str], data=None, raw: bytes = None):
991
+ def msg_send(self, topic: tuple[str], data=None, raw: bytes | None = None):
1011
992
  """
1012
993
  Tunnel a user-tagged message. This sends the message
1013
994
  to all active callers of :meth:`msg_monitor` which use the same topic.
moat/kv/code.py CHANGED
@@ -5,6 +5,7 @@ so that it can be called easily.
5
5
  "Code" consists of either Python modules or single procedures.
6
6
 
7
7
  """
8
+
8
9
  from __future__ import annotations
9
10
 
10
11
  import logging
@@ -15,6 +16,7 @@ import anyio
15
16
  from moat.util import NotGiven, P, make_module, make_proc
16
17
 
17
18
  from .obj import ClientEntry, ClientRoot
19
+ import contextlib
18
20
 
19
21
  logger = logging.getLogger(__name__)
20
22
 
@@ -81,10 +83,8 @@ class ModuleEntry(ClientEntry):
81
83
  await super().set_value(value)
82
84
  if value is NotGiven:
83
85
  self._module = None
84
- try:
86
+ with contextlib.suppress(KeyError):
85
87
  del sys.modules[self.name]
86
- except KeyError:
87
- pass
88
88
  return
89
89
 
90
90
  try:
moat/kv/command/data.py CHANGED
@@ -6,6 +6,7 @@ import time
6
6
 
7
7
  import asyncclick as click
8
8
  from moat.util import MsgReader, NotGiven, P, PathLongener, attr_args, yprint
9
+ from moat.util.times import ts2iso
9
10
 
10
11
  from moat.kv.client import StreamedRequest
11
12
  from moat.kv.data import add_dates, data_get, node_attr
@@ -244,8 +245,8 @@ async def monitor(obj, state, only, path_only, add_date, ignore):
244
245
  # value has been deleted
245
246
  continue
246
247
  if flushing:
247
- r["time"] = time.time()
248
- r["_time"] = datetime.datetime.now().isoformat(sep=" ", timespec="milliseconds")
248
+ r["time"] = ts = time.time()
249
+ r["_time"] = ts2iso(ts)
249
250
  yprint(r, stream=obj.stdout)
250
251
  print("---", file=obj.stdout)
251
252
  if flushing:
@@ -258,7 +259,7 @@ async def monitor(obj, state, only, path_only, add_date, ignore):
258
259
  @click.pass_obj
259
260
  async def update(obj, infile):
260
261
  """Send a list of updates to a MoaT-KV subtree"""
261
- async with MsgReader(path=infile) as reader:
262
+ async with MsgReader(path=infile, codec="std-msgpack") as reader:
262
263
  async for msg in reader:
263
264
  if not hasattr(msg, "path"):
264
265
  continue
@@ -6,19 +6,17 @@ import sys
6
6
  from collections.abc import Mapping
7
7
 
8
8
  import asyncclick as click
9
- from moat.mqtt.codecs import MsgPackCodec
10
9
  from moat.util import (
11
- MsgReader,
12
10
  MsgWriter,
13
11
  P,
14
12
  Path,
15
- PathLongener,
16
13
  load_subgroup,
17
14
  yload,
18
15
  yprint,
19
16
  )
20
17
 
21
- from moat.kv.codec import unpacker
18
+ from moat.util.mqtt import unpacker
19
+ from moat.util.msgpack import StdMsgpack
22
20
 
23
21
 
24
22
  @load_subgroup(short_help="Local data mangling", sub_pre="dump")
@@ -42,7 +40,7 @@ async def init(node, file):
42
40
  Using this command, followed by "moat kv server -l <outfile> <node>", is
43
41
  equivalent to running "moat kv server -i 'Initial data' <node>.
44
42
  """
45
- async with MsgWriter(path=file) as f:
43
+ async with MsgWriter(path=file, codec="std-msgpack") as f:
46
44
  await f(
47
45
  dict(
48
46
  chain=dict(node=node, tick=1, prev=None),
@@ -93,29 +91,31 @@ async def msg_(obj, path):
93
91
  be = obj.cfg.server.backend
94
92
  kw = obj.cfg.server[be]
95
93
 
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)
94
+ async with (
95
+ get_backend(be)(**kw) as conn,
96
+ conn.monitor(*path) as stream,
97
+ ):
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
119
 
120
120
 
121
121
  @cli.command("post")
@@ -137,7 +137,7 @@ async def post_(obj, path):
137
137
  be = obj.cfg.server.backend
138
138
  kw = obj.cfg.server[be]
139
139
 
140
- async with get_backend(be)(codec=MsgPackCodec, **kw) as conn:
140
+ async with get_backend(be)(codec=StdMsgpack(), **kw) as conn:
141
141
  for d in yload(sys.stdin, multi=True):
142
142
  topic = d.pop("_topic", path)
143
143
  await conn.send(*topic, payload=d)
@@ -6,6 +6,7 @@ from collections.abc import Mapping
6
6
  import asyncclick as click
7
7
  from moat.util import P, PathLongener, yprint
8
8
  from range_set import RangeSet
9
+ import contextlib
9
10
 
10
11
 
11
12
  @click.group(short_help="Control internal state.") # pylint: disable=undefined-variable
@@ -173,10 +174,8 @@ async def dump(obj, path):
173
174
  yy = y
174
175
  for p in path:
175
176
  yy = yy.setdefault(p, {})
176
- try:
177
+ with contextlib.suppress(KeyError):
177
178
  yy["_"] = r["value"]
178
- except KeyError:
179
- pass
180
179
  yprint(y, stream=obj.stdout)
181
180
 
182
181
 
moat/kv/command/job.py CHANGED
@@ -223,8 +223,7 @@ async def list_(obj, state, state_only, table, as_dict):
223
223
  st = "-stopped-"
224
224
  else:
225
225
  st = " | ".join(
226
- "%s %s"
227
- % (
226
+ "{} {}".format(
228
227
  Path.build(e.subpath)
229
228
  if e._path[-2] == ee._path[-1]
230
229
  else Path.build(ee.subpath),
moat/kv/command/type.py CHANGED
@@ -5,6 +5,7 @@ import json
5
5
 
6
6
  import asyncclick as click
7
7
  from moat.util import NotGiven, P, Path, PathLongener, yload, yprint
8
+ import contextlib
8
9
 
9
10
 
10
11
  @click.group() # pylint: disable=undefined-variable
@@ -134,10 +135,8 @@ async def match(obj, path, type_, delete, raw): # pylint: disable=redefined-bui
134
135
  yy = y
135
136
  for p in path:
136
137
  yy = yy.setdefault(p, {})
137
- try:
138
+ with contextlib.suppress(KeyError):
138
139
  yy["_"] = r["value"]
139
- except KeyError:
140
- pass
141
140
  yprint(y, stream=obj.stdout)
142
141
  return
143
142
 
@@ -194,8 +193,6 @@ async def list(obj, path): # pylint: disable=redefined-builtin
194
193
  yy = y
195
194
  for p in path:
196
195
  yy = yy.setdefault(p, {})
197
- try:
196
+ with contextlib.suppress(KeyError):
198
197
  yy["_"] = r["value"]
199
- except KeyError:
200
- pass
201
198
  yprint(y, stream=obj.stdout)