moat-kv 0.70.20__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 +36 -52
  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 +39 -36
  13. moat/kv/server.py +86 -90
  14. moat/kv/types.py +5 -8
  15. {moat_kv-0.70.20.dist-info → moat_kv-0.70.23.dist-info}/METADATA +22 -25
  16. moat_kv-0.70.23.dist-info/RECORD +19 -0
  17. {moat_kv-0.70.20.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.20.dist-info/LICENSE +0 -3
  49. moat_kv-0.70.20.dist-info/LICENSE.APACHE2 +0 -202
  50. moat_kv-0.70.20.dist-info/LICENSE.MIT +0 -20
  51. moat_kv-0.70.20.dist-info/RECORD +0 -49
  52. {moat_kv-0.70.20.dist-info → moat_kv-0.70.23.dist-info}/top_level.txt +0 -0
moat/kv/__init__.py CHANGED
@@ -1,4 +1,5 @@
1
1
  # pylint: disable=W0703,C0103
2
+ from __future__ import annotations
2
3
 
3
4
  __path__ = __import__("pkgutil").extend_path(__path__, __name__)
4
5
 
moat/kv/_cfg.yaml ADDED
@@ -0,0 +1,97 @@
1
+ conn:
2
+ # client: controls how to talk to the MoaT-KV server
3
+ host: localhost
4
+ port: 27586
5
+ ssl: false
6
+ # ssl:
7
+ # cert: '/path/to/cert.pem',key='/path/to/cert.key'
8
+ init_timeout: 5
9
+ # time to wait for connection plus greeting
10
+ auth: null
11
+ # no auth used by default
12
+ name: null
13
+ # defaults to a seqnum
14
+ config:
15
+ prefix: !P :.moat.kv.config
16
+ errors:
17
+ prefix: !P :.moat.kv.error
18
+ codes:
19
+ prefix: !P :.moat.kv.code.proc
20
+ modules:
21
+ prefix: !P :.moat.kv.code.module
22
+ runner: # for moat.kv.runner.RunnerRoot
23
+ # storage for runnable commands
24
+ prefix: !P :.moat.kv.run"
25
+ # storage for runner states
26
+ state: !P :.moat.kv.state"
27
+
28
+ name: "run"
29
+ # Serf event name, suffixed by subpath
30
+
31
+ start_delay: 1
32
+ # time to wait between job starts. Not optional.
33
+
34
+ ping: -15
35
+ # set an I-am-running message every those-many seconds
36
+ # positive: set in moat.kv, negative: broadcast to :moat.kv.run tag
37
+
38
+ actor:
39
+ # Actor config, required for Runner
40
+ cycle: 20
41
+ nodes: -1
42
+ splits: 5
43
+ n_hosts: 3
44
+ version: 1
45
+ sub:
46
+ # tags for various runner modes
47
+ group: "any"
48
+ single: "at"
49
+ all: "all"
50
+ server:
51
+ # server-side configuration
52
+ buffer: 10
53
+ # per-stream buffer
54
+
55
+ backend: "mqtt"
56
+ # default
57
+ mqtt:
58
+ uri: "mqtt://localhost:1883"
59
+ serf:
60
+ host: "localhost"
61
+ port: 7373
62
+
63
+ # event message path/topic prefix
64
+ root: !P moat.kv
65
+
66
+ paranoia: False
67
+ # typecheck server-to-server updates?
68
+ #
69
+ # which addresses/ports to accept MoaT-KV connections on
70
+ bind: [{}]
71
+ bind_default:
72
+ # default values for all elements of "bind"
73
+ host: "localhost"
74
+ port: PORT
75
+ ssl: False
76
+ change:
77
+ length: 5
78
+ # chain length: use max nr of network sections +1
79
+ ping:
80
+ cycle: 10
81
+ gap: 2
82
+ # asyncserf.Actor config timing for server sync
83
+ # ping also controls minimum server startup time
84
+ delete:
85
+ # asyncserf.Actor config timing for deletion
86
+ cycle: 100
87
+ gap: 10
88
+ version: 1
89
+ paranoia: false
90
+ # typecheck server>server updates?
91
+
92
+ # how does a new server reach existing nodes, to download state?
93
+ domain: null
94
+ # domain in which to look up node names, if not in hostmap
95
+ hostmap: # map MoaT-KV server names to connect destinations
96
+ test1: ["localhost", 27586]
97
+ test2: ["does-not-exist.invalid", 27586]
moat/kv/_main.py CHANGED
@@ -4,11 +4,12 @@ Basic DistKV support
4
4
 
5
5
  """
6
6
 
7
+ from __future__ import annotations
8
+
7
9
  import logging
8
- from pathlib import Path
9
10
 
10
11
  import asyncclick as click
11
- from moat.util import attrdict, combine_dict, load_subgroup, yload
12
+ from moat.util import attrdict, combine_dict, load_subgroup, CFG, ensure_cfg
12
13
 
13
14
  from moat.kv.auth import gen_auth
14
15
  from moat.kv.client import client_scope
@@ -16,7 +17,7 @@ from moat.kv.client import client_scope
16
17
  logger = logging.getLogger(__name__)
17
18
 
18
19
 
19
- CFG = yload(Path(__file__).parent / "_config.yaml", attr=True)
20
+ ensure_cfg("moat.kv")
20
21
 
21
22
 
22
23
  class NullObj:
@@ -40,12 +41,8 @@ class NullObj:
40
41
  raise self._exc
41
42
 
42
43
 
43
- @load_subgroup(
44
- sub_pre="moat.kv.command", sub_post="cli", ext_pre="moat.kv", ext_post="_main.cli"
45
- )
46
- @click.option(
47
- "-h", "--host", default=None, help=f"Host to use. Default: {CFG.kv.conn.host}"
48
- )
44
+ @load_subgroup(sub_pre="moat.kv.command", sub_post="cli", ext_pre="moat.kv", ext_post="_main.cli")
45
+ @click.option("-h", "--host", default=None, help=f"Host to use. Default: {CFG.kv.conn.host}")
49
46
  @click.option(
50
47
  "-p",
51
48
  "--port",
moat/kv/client.py CHANGED
@@ -4,17 +4,18 @@ Client code.
4
4
  Main entry point: :func:`open_client`.
5
5
  """
6
6
 
7
+ from __future__ import annotations
8
+
7
9
  import logging
8
10
  import os
9
11
  import socket
10
12
  from contextlib import AsyncExitStack, asynccontextmanager
11
13
  from inspect import iscoroutine
12
- from pathlib import Path
13
- from typing import Tuple
14
14
 
15
15
  import anyio
16
16
  from asyncscope import Scope, main_scope, scope
17
17
  from moat.util import ( # pylint: disable=no-name-in-module
18
+ CFG,
18
19
  DelayedRead,
19
20
  DelayedWrite,
20
21
  NotGiven,
@@ -25,9 +26,10 @@ from moat.util import ( # pylint: disable=no-name-in-module
25
26
  byte2num,
26
27
  combine_dict,
27
28
  create_queue,
29
+ ensure_cfg,
28
30
  gen_ssl,
31
+ gen_ident,al_lower,
29
32
  num2byte,
30
- yload,
31
33
  )
32
34
 
33
35
  from .codec import packer, stream_unpacker
@@ -45,17 +47,6 @@ logger = logging.getLogger(__name__)
45
47
 
46
48
  ClosedResourceError = anyio.ClosedResourceError
47
49
 
48
- rs = os.environ.get("PYTHONHASHSEED", None)
49
- if rs is None:
50
- import random
51
- else: # pragma: no cover
52
- try:
53
- import trio._core._run as tcr
54
- except ImportError:
55
- import random
56
- else:
57
- random = tcr._r
58
-
59
50
  __all__ = ["NoData", "ManyData", "open_client", "client_scope", "StreamedRequest"]
60
51
 
61
52
 
@@ -117,9 +108,7 @@ async def client_scope(_name=None, **cfg):
117
108
  _name = f"_{_cid}"
118
109
  # uniqueness required for testing.
119
110
  # TODO replace with a dependency on the test server.
120
- return await scope.service(
121
- f"moat.kv.client.{_name}", _scoped_client, _name=_name, **cfg
122
- )
111
+ return await scope.service(f"moat.kv.client.{_name}", _scoped_client, _name=_name, **cfg)
123
112
 
124
113
 
125
114
  class StreamedRequest:
@@ -155,9 +144,7 @@ class StreamedRequest:
155
144
  self._path_long = lambda x: x
156
145
  if client.qlen > 0:
157
146
  self.dw = DelayedWrite(client.qlen)
158
- self.qr = DelayedRead(
159
- client.qlen, get_seq=self._get_seq, send_ack=self._send_ack
160
- )
147
+ self.qr = DelayedRead(client.qlen, get_seq=self._get_seq, send_ack=self._send_ack)
161
148
  else:
162
149
  self.qr = create_queue(client.config.server.buffer)
163
150
 
@@ -227,7 +214,7 @@ class StreamedRequest:
227
214
 
228
215
  async def get(self):
229
216
  """Receive a single reply"""
230
- pass # receive reply
217
+ # receive reply
231
218
  if self._reply_stream:
232
219
  raise RuntimeError("Unexpected multi stream msg")
233
220
  msg = await self.recv()
@@ -246,7 +233,7 @@ class StreamedRequest:
246
233
  async def __anext__(self):
247
234
  try:
248
235
  res = await self.qr.get()
249
- except (anyio.EndOfStream, anyio.ClosedResourceError):
236
+ except (anyio.EndOfStream, anyio.ClosedResourceError, EOFError):
250
237
  raise StopAsyncIteration
251
238
  except CancelledError:
252
239
  raise StopAsyncIteration # just terminate
@@ -274,7 +261,7 @@ class StreamedRequest:
274
261
  async def cancel(self):
275
262
  try:
276
263
  await self.qr.put_error(CancelledError())
277
- except (anyio.BrokenResourceError, anyio.ClosedResourceError):
264
+ except (anyio.BrokenResourceError, anyio.ClosedResourceError, EOFError):
278
265
  pass
279
266
  else:
280
267
  try:
@@ -411,7 +398,7 @@ class Client:
411
398
  qlen: int = 0
412
399
 
413
400
  def __init__(self, cfg: dict):
414
- CFG = yload(Path(__file__).parent / "_config.yaml")
401
+ ensure_cfg("moat.kv")
415
402
  self._cfg = combine_dict(cfg, CFG["kv"], cls=attrdict)
416
403
  self.config = ClientConfig(self)
417
404
 
@@ -419,7 +406,7 @@ class Client:
419
406
  self._handlers = {}
420
407
  self._send_lock = anyio.Lock()
421
408
  self._helpers = {}
422
- self._name = "".join(random.choices("abcdefghjkmnopqrstuvwxyz23456789", k=9))
409
+ self._name = gen_ident(9, alphabet=al_lower)
423
410
  self.logger = logging.getLogger(f"moat.kv.client.{self._name}")
424
411
 
425
412
  @property
@@ -482,11 +469,11 @@ class Client:
482
469
 
483
470
  k = await anyio.to_thread.run_sync(gen_key)
484
471
  res = await self._request(
485
- "diffie_hellman", pubkey=num2byte(k.public_key), length=length
472
+ "diffie_hellman",
473
+ pubkey=num2byte(k.public_key),
474
+ length=length,
486
475
  ) # length=k.key_length
487
- await anyio.to_thread.run_sync(
488
- k.generate_shared_secret, byte2num(res.pubkey)
489
- )
476
+ await anyio.to_thread.run_sync(k.generate_shared_secret, byte2num(res.pubkey))
490
477
  self._dh_key = num2byte(k.shared_secret)[0:32]
491
478
  return self._dh_key
492
479
 
@@ -681,16 +668,12 @@ class Client:
681
668
  if not sa or not sa[0]:
682
669
  # no auth required
683
670
  if auth:
684
- logger.info(
685
- "Tried to use auth=%s, but not required.", auth._auth_method
686
- )
671
+ logger.info("Tried to use auth=%s, but not required.", auth._auth_method)
687
672
  return
688
673
  if not auth:
689
674
  raise ClientAuthRequiredError("You need to log in using:", sa[0])
690
675
  if auth._auth_method != sa[0]:
691
- raise ClientAuthMethodError(
692
- f"You cannot use {auth._auth_method!r} auth", sa
693
- )
676
+ raise ClientAuthMethodError(f"You cannot use {auth._auth_method!r} auth", sa)
694
677
  if getattr(auth, "_DEBUG", False):
695
678
  auth._length = 16
696
679
  await auth.auth(self)
@@ -736,9 +719,7 @@ class Client:
736
719
  self._server_init = msg = await hello.get()
737
720
  self.logger.debug("Hello %s", msg)
738
721
  self.server_name = msg.node
739
- self.client_name = (
740
- cfg["name"] or socket.gethostname() or self.server_name
741
- )
722
+ self.client_name = cfg["name"] or socket.gethostname() or self.server_name
742
723
  if "qlen" in msg:
743
724
  self.qlen = min(msg.qlen, 99) # self.config.server.buffer
744
725
  await self._send(seq=0, qlen=self.qlen)
@@ -746,13 +727,11 @@ class Client:
746
727
 
747
728
  from .config import ConfigRoot
748
729
 
749
- self._config = await ConfigRoot.as_handler(
750
- self, require_client=False
751
- )
730
+ self._config = await ConfigRoot.as_handler(self, require_client=False)
752
731
 
753
732
  except TimeoutError:
754
733
  raise
755
- except socket.error as e:
734
+ except OSError as e:
756
735
  raise ServerConnectionError(host, port) from e
757
736
  else:
758
737
  yield self
@@ -830,7 +809,12 @@ class Client:
830
809
  kw["idem"] = idem
831
810
 
832
811
  return self._request(
833
- action="set_value", path=path, value=value, iter=False, nchain=nchain, **kw
812
+ action="set_value",
813
+ path=path,
814
+ value=value,
815
+ iter=False,
816
+ nchain=nchain,
817
+ **kw,
834
818
  )
835
819
 
836
820
  def delete(self, path, *, chain=NotGiven, prev=NotGiven, nchain=0, recursive=False):
@@ -878,9 +862,7 @@ class Client:
878
862
  raise RuntimeError("You need a path, not a string")
879
863
  if empty is None:
880
864
  empty = not with_data
881
- res = await self._request(
882
- action="enum", path=path, with_data=with_data, empty=empty, **kw
883
- )
865
+ res = await self._request(action="enum", path=path, with_data=with_data, empty=empty, **kw)
884
866
  try:
885
867
  return res.result
886
868
  except AttributeError:
@@ -910,7 +892,11 @@ class Client:
910
892
  if long_path:
911
893
  lp = PathLongener()
912
894
  async for r in await self._request(
913
- action="get_tree", path=path, iter=True, long_path=True, **kw
895
+ action="get_tree",
896
+ path=path,
897
+ iter=True,
898
+ long_path=True,
899
+ **kw,
914
900
  ):
915
901
  if long_path:
916
902
  lp(r)
@@ -963,9 +949,7 @@ class Client:
963
949
  """
964
950
  if isinstance(path, str):
965
951
  raise RuntimeError("You need a path, not a string")
966
- return self._stream(
967
- action="watch", path=path, iter=True, long_path=long_path, **kw
968
- )
952
+ return self._stream(action="watch", path=path, iter=True, long_path=long_path, **kw)
969
953
 
970
954
  def mirror(self, path, *, root_type=None, **kw):
971
955
  """An async context manager that affords an update-able mirror
@@ -997,7 +981,7 @@ class Client:
997
981
  root = root_type(self, path, **kw)
998
982
  return root.run()
999
983
 
1000
- def msg_monitor(self, topic: Tuple[str], raw: bool = False):
984
+ def msg_monitor(self, topic: tuple[str], raw: bool = False):
1001
985
  """
1002
986
  Return an async iterator of tunneled messages. This receives
1003
987
  all messages sent using :meth:`msg_send` with the same topic.
@@ -1023,7 +1007,7 @@ class Client:
1023
1007
  """
1024
1008
  return self._stream(action="msg_monitor", topic=topic, raw=raw)
1025
1009
 
1026
- def msg_send(self, topic: Tuple[str], data=None, raw: bytes = None):
1010
+ def msg_send(self, topic: tuple[str], data=None, raw: bytes = None):
1027
1011
  """
1028
1012
  Tunnel a user-tagged message. This sends the message
1029
1013
  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
+ from __future__ import annotations
8
9
 
9
10
  import logging
10
11
  import sys
@@ -36,7 +37,7 @@ class ModuleRoot(ClientRoot):
36
37
 
37
38
  CFG = "modules"
38
39
 
39
- err: "ErrorRoot" = None # noqa: F821
40
+ err: ErrorRoot = None # noqa: F821
40
41
 
41
42
  @classmethod
42
43
  def child_type(cls, name):
@@ -95,7 +96,10 @@ class ModuleEntry(ClientEntry):
95
96
  self._module = None
96
97
  logger.warning("Could not compile @%r", self.subpath)
97
98
  await self.root.err.record_error(
98
- "compile", self.subpath, exc=exc, message="compiler error"
99
+ "compile",
100
+ self.subpath,
101
+ exc=exc,
102
+ message="compiler error",
99
103
  )
100
104
  else:
101
105
  await self.root.err.record_working("compile", self.subpath)
@@ -207,7 +211,10 @@ class CodeEntry(ClientEntry):
207
211
  except Exception as exc:
208
212
  logger.warning("Could not compile @%s", self.subpath)
209
213
  await self.root.err.record_error(
210
- "compile", self.subpath, exc=exc, message="compiler error"
214
+ "compile",
215
+ self.subpath,
216
+ exc=exc,
217
+ message="compiler error",
211
218
  )
212
219
  self._code = None
213
220
  else:
moat/kv/codec.py CHANGED
@@ -4,6 +4,7 @@ plus an unpacker factory for streams.
4
4
  """
5
5
 
6
6
  # compatibility
7
+ from __future__ import annotations
7
8
 
8
9
  from moat.util import * # noqa: F403
9
10
 
moat/kv/config.py CHANGED
@@ -3,6 +3,8 @@ An online-updated config store
3
3
 
4
4
  """
5
5
 
6
+ from __future__ import annotations
7
+
6
8
  try:
7
9
  from contextlib import asynccontextmanager
8
10
  except ImportError: # pragma: no cover
moat/kv/data.py CHANGED
@@ -1,6 +1,8 @@
1
1
  """
2
2
  Data access
3
3
  """
4
+ from __future__ import annotations
5
+
4
6
  import datetime
5
7
  import os
6
8
  import sys
@@ -35,7 +37,8 @@ def add_dates(d):
35
37
  continue
36
38
  if start <= v <= stop:
37
39
  d[f"_{k}"] = datetime.datetime.fromtimestamp(v).isoformat(
38
- sep=" ", timespec="milliseconds"
40
+ sep=" ",
41
+ timespec="milliseconds",
39
42
  )
40
43
 
41
44
  _add(d)
@@ -81,9 +84,7 @@ async def data_get(
81
84
  kw.setdefault("nchain", obj.meta)
82
85
  y = {}
83
86
  if internal:
84
- res = await obj.client._request(
85
- action="get_tree_internal", path=path, iter=True, **kw
86
- )
87
+ res = await obj.client._request(action="get_tree_internal", path=path, iter=True, **kw)
87
88
  else:
88
89
  res = obj.client.get_tree(path, **kw)
89
90
  async for r in res:
@@ -182,16 +183,16 @@ def res_update(res, attr: Path, value=None, **kw): # pylint: disable=redefined-
182
183
  return val._update(attr, value=value, **kw)
183
184
 
184
185
 
185
- async def node_attr(obj, path, vars_, eval_, path_, res=None, chain=None):
186
+ async def node_attr(obj, path, res=None, chain=None, **kw):
186
187
  """
187
188
  Sub-attr setter.
188
189
 
189
190
  Args:
190
191
  obj: command object
191
192
  path: address of the node to change
192
- vars_, eval_, path_: the results of `attr_args`
193
193
  res: old node, if it has been read already
194
194
  chain: change chain of node, copied from res if clear
195
+ **kw: the results of `attr_args`
195
196
 
196
197
  Returns the result of setting the attribute.
197
198
  """
@@ -207,7 +208,7 @@ async def node_attr(obj, path, vars_, eval_, path_, res=None, chain=None):
207
208
  except AttributeError:
208
209
  chain = None
209
210
  val = NotGiven
210
- val = process_args(val, vars_, eval_, path_)
211
+ val = process_args(val, **kw)
211
212
  if val is NotGiven:
212
213
  res = await obj.client.delete(path, nchain=obj.meta, chain=chain)
213
214
  else:
moat/kv/errors.py CHANGED
@@ -79,6 +79,8 @@ of this record.
79
79
 
80
80
  """
81
81
 
82
+ from __future__ import annotations
83
+
82
84
  import logging
83
85
  import traceback
84
86
  from collections import defaultdict
@@ -212,7 +214,7 @@ class ErrorEntry(AttrClientEntry):
212
214
  try:
213
215
  m = message.format(exc=exc, **data)
214
216
  except Exception as exc: # pylint: disable=unused-argument # OH COME ON
215
- m = message + f" (FORMAT {exc !r})"
217
+ m = message + f" (FORMAT {exc!r})"
216
218
  else:
217
219
  m = message
218
220
  if m:
@@ -304,9 +306,7 @@ class ErrorEntry(AttrClientEntry):
304
306
  if value is NotGiven:
305
307
  if self.value is NotGiven:
306
308
  return
307
- keep = await self.root.get_error_record(
308
- self.subsystem, self.path, create=False
309
- )
309
+ keep = await self.root.get_error_record(self.subsystem, self.path, create=False)
310
310
  if keep is not None:
311
311
  self._real_entry = keep.real_entry
312
312
  await self.move_to_real()
@@ -449,7 +449,13 @@ class ErrorRoot(ClientRoot):
449
449
  raise RuntimeError(f"This cannot happen: {entry.node} {entry.tock}")
450
450
 
451
451
  async def record_working( # pylint: disable=dangerous-default-value
452
- self, subsystem, path, *, comment=None, data={}, force=False
452
+ self,
453
+ subsystem,
454
+ path,
455
+ *,
456
+ comment=None,
457
+ data={},
458
+ force=False,
453
459
  ):
454
460
  """This exception has been fixed.
455
461
 
@@ -522,7 +528,11 @@ class ErrorRoot(ClientRoot):
522
528
  return # owch, but can't be helped
523
529
 
524
530
  r = await rec.real_entry.add_exc(
525
- self.name, exc=exc, data=data, comment=comment, message=message
531
+ self.name,
532
+ exc=exc,
533
+ data=data,
534
+ comment=comment,
535
+ message=message,
526
536
  )
527
537
  return r
528
538
 
@@ -535,9 +545,7 @@ class ErrorRoot(ClientRoot):
535
545
  return
536
546
 
537
547
  try:
538
- del (self._done if entry.resolved else self._active)[entry.subsystem][
539
- entry.path
540
- ]
548
+ del (self._done if entry.resolved else self._active)[entry.subsystem][entry.path]
541
549
  except KeyError:
542
550
  pass
543
551
 
moat/kv/exceptions.py CHANGED
@@ -3,7 +3,7 @@ This module affords all MoaT-KV exceptions.
3
3
  """
4
4
 
5
5
  # pylint: disable=unnecessary-pass
6
-
6
+ from __future__ import annotations
7
7
 
8
8
  error_types = {}
9
9
 
@@ -39,8 +39,6 @@ class ClientError(MoaTKVError):
39
39
 
40
40
  etype: str = None
41
41
 
42
- pass
43
-
44
42
 
45
43
  @_typed
46
44
  class ClientChainError(ClientError):
@@ -48,8 +46,6 @@ class ClientChainError(ClientError):
48
46
 
49
47
  etype = "chain"
50
48
 
51
- pass
52
-
53
49
 
54
50
  @_typed
55
51
  class ClientConnectionError(ClientError):
@@ -57,8 +53,6 @@ class ClientConnectionError(ClientError):
57
53
 
58
54
  etype = "conn"
59
55
 
60
- pass
61
-
62
56
 
63
57
  class ServerClosedError(ServerError):
64
58
  """The server closed our connection."""