moat-kv 0.70.14__tar.gz → 0.70.18__tar.gz

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 (140) hide show
  1. moat-kv-0.70.18/.gitmodules +21 -0
  2. {moat-kv-0.70.14 → moat-kv-0.70.18}/PKG-INFO +1 -1
  3. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/source/TODO.rst +4 -7
  4. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/__init__.py +4 -1
  5. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/_main.py +12 -4
  6. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/actor/deletor.py +4 -1
  7. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/auth/__init__.py +16 -10
  8. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/auth/_test.py +12 -4
  9. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/auth/password.py +1 -3
  10. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/client.py +44 -15
  11. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/code.py +3 -1
  12. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/command/acl.py +17 -4
  13. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/command/auth.py +14 -3
  14. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/command/code.py +41 -10
  15. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/command/codec.py +33 -14
  16. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/command/data.py +51 -16
  17. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/command/dump/__init__.py +10 -1
  18. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/command/error.py +12 -4
  19. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/command/internal.py +25 -7
  20. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/command/job.py +31 -9
  21. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/command/server.py +13 -3
  22. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/command/type.py +24 -8
  23. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/data.py +3 -1
  24. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/errors.py +23 -5
  25. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/mock/__init__.py +5 -2
  26. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/mock/mqtt.py +9 -3
  27. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/mock/serf.py +6 -2
  28. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/mock/tracer.py +3 -1
  29. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/model.py +21 -7
  30. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/obj/__init__.py +26 -5
  31. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/obj/command.py +10 -6
  32. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/runner.py +41 -12
  33. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/server.py +113 -32
  34. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/types.py +6 -2
  35. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat_kv.egg-info/PKG-INFO +1 -1
  36. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat_kv.egg-info/requires.txt +4 -4
  37. {moat-kv-0.70.14 → moat-kv-0.70.18}/pyproject.toml +31 -4
  38. {moat-kv-0.70.14 → moat-kv-0.70.18}/scripts/init +3 -1
  39. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/__init__.py +2 -2
  40. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/test_basic.py +7 -3
  41. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/test_basic_serf.py +7 -3
  42. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/test_feature_config.py +3 -1
  43. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/test_feature_convert.py +4 -2
  44. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/test_feature_error.py +4 -1
  45. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/test_feature_ssl.py +4 -2
  46. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/test_feature_typecheck.py +7 -3
  47. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/test_load_save.py +29 -7
  48. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/test_multi.py +4 -1
  49. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/test_recover.py +9 -3
  50. moat-kv-0.70.14/.gitmodules +0 -9
  51. {moat-kv-0.70.14 → moat-kv-0.70.18}/.appveyor.yml +0 -0
  52. {moat-kv-0.70.14 → moat-kv-0.70.18}/.coveragerc +0 -0
  53. {moat-kv-0.70.14 → moat-kv-0.70.18}/.gitignore +0 -0
  54. {moat-kv-0.70.14 → moat-kv-0.70.18}/.pylintrc +0 -0
  55. {moat-kv-0.70.14 → moat-kv-0.70.18}/.readthedocs.yml +0 -0
  56. {moat-kv-0.70.14 → moat-kv-0.70.18}/.travis.yml +0 -0
  57. {moat-kv-0.70.14 → moat-kv-0.70.18}/LICENSE +0 -0
  58. {moat-kv-0.70.14 → moat-kv-0.70.18}/LICENSE.APACHE2 +0 -0
  59. {moat-kv-0.70.14 → moat-kv-0.70.18}/LICENSE.MIT +0 -0
  60. {moat-kv-0.70.14 → moat-kv-0.70.18}/MANIFEST.in +0 -0
  61. {moat-kv-0.70.14 → moat-kv-0.70.18}/Makefile +0 -0
  62. {moat-kv-0.70.14 → moat-kv-0.70.18}/README.rst +0 -0
  63. {moat-kv-0.70.14 → moat-kv-0.70.18}/ci/rtd-requirements.txt +0 -0
  64. {moat-kv-0.70.14 → moat-kv-0.70.18}/ci/test-requirements.txt +0 -0
  65. {moat-kv-0.70.14 → moat-kv-0.70.18}/ci/travis.sh +0 -0
  66. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/Makefile +0 -0
  67. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/make.bat +0 -0
  68. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/source/_static/.gitkeep +0 -0
  69. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/source/acls.rst +0 -0
  70. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/source/auth.rst +0 -0
  71. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/source/client_protocol.rst +0 -0
  72. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/source/code.rst +0 -0
  73. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/source/command_line.rst +0 -0
  74. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/source/common_protocol.rst +0 -0
  75. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/source/conf.py +0 -0
  76. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/source/debugging.rst +0 -0
  77. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/source/extend.rst +0 -0
  78. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/source/history.rst +0 -0
  79. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/source/index.rst +0 -0
  80. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/source/model.rst +0 -0
  81. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/source/overview.rst +0 -0
  82. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/source/related.rst +0 -0
  83. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/source/server_protocol.rst +0 -0
  84. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/source/startup.rst +0 -0
  85. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/source/translator.rst +0 -0
  86. {moat-kv-0.70.14 → moat-kv-0.70.18}/docs/source/tutorial.rst +0 -0
  87. {moat-kv-0.70.14 → moat-kv-0.70.18}/examples/code/transform.scale.yml +0 -0
  88. {moat-kv-0.70.14 → moat-kv-0.70.18}/examples/code/transform.switch.yml +0 -0
  89. {moat-kv-0.70.14 → moat-kv-0.70.18}/examples/code/transform.timeslot.yml +0 -0
  90. {moat-kv-0.70.14 → moat-kv-0.70.18}/examples/pathify.py +0 -0
  91. {moat-kv-0.70.14 → moat-kv-0.70.18}/mktag +0 -0
  92. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/__init__.py +0 -0
  93. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/_config.yaml +0 -0
  94. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/actor/__init__.py +0 -0
  95. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/auth/root.py +0 -0
  96. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/backend/__init__.py +0 -0
  97. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/backend/mqtt.py +0 -0
  98. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/backend/serf.py +0 -0
  99. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/codec.py +0 -0
  100. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/command/__init__.py +0 -0
  101. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/command/log.py +0 -0
  102. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/config.py +0 -0
  103. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat/kv/exceptions.py +0 -0
  104. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat_kv.egg-info/SOURCES.txt +0 -0
  105. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat_kv.egg-info/dependency_links.txt +0 -0
  106. {moat-kv-0.70.14 → moat-kv-0.70.18}/moat_kv.egg-info/top_level.txt +0 -0
  107. {moat-kv-0.70.14 → moat-kv-0.70.18}/scripts/current +0 -0
  108. {moat-kv-0.70.14 → moat-kv-0.70.18}/scripts/env +0 -0
  109. {moat-kv-0.70.14 → moat-kv-0.70.18}/scripts/recover +0 -0
  110. {moat-kv-0.70.14 → moat-kv-0.70.18}/scripts/rotate +0 -0
  111. {moat-kv-0.70.14 → moat-kv-0.70.18}/scripts/run +0 -0
  112. {moat-kv-0.70.14 → moat-kv-0.70.18}/scripts/run-all +0 -0
  113. {moat-kv-0.70.14 → moat-kv-0.70.18}/scripts/run-any +0 -0
  114. {moat-kv-0.70.14 → moat-kv-0.70.18}/scripts/run-single +0 -0
  115. {moat-kv-0.70.14 → moat-kv-0.70.18}/scripts/success +0 -0
  116. {moat-kv-0.70.14 → moat-kv-0.70.18}/setup.cfg +0 -0
  117. {moat-kv-0.70.14 → moat-kv-0.70.18}/systemd/moat-kv-recover.service +0 -0
  118. {moat-kv-0.70.14 → moat-kv-0.70.18}/systemd/moat-kv-rotate.service +0 -0
  119. {moat-kv-0.70.14 → moat-kv-0.70.18}/systemd/moat-kv-rotate.timer +0 -0
  120. {moat-kv-0.70.14 → moat-kv-0.70.18}/systemd/moat-kv-run-all.service +0 -0
  121. {moat-kv-0.70.14 → moat-kv-0.70.18}/systemd/moat-kv-run-all@.service +0 -0
  122. {moat-kv-0.70.14 → moat-kv-0.70.18}/systemd/moat-kv-run-any.service +0 -0
  123. {moat-kv-0.70.14 → moat-kv-0.70.18}/systemd/moat-kv-run-any@.service +0 -0
  124. {moat-kv-0.70.14 → moat-kv-0.70.18}/systemd/moat-kv-run-single.service +0 -0
  125. {moat-kv-0.70.14 → moat-kv-0.70.18}/systemd/moat-kv-run-single@.service +0 -0
  126. {moat-kv-0.70.14 → moat-kv-0.70.18}/systemd/moat-kv.service +0 -0
  127. {moat-kv-0.70.14 → moat-kv-0.70.18}/systemd/postinst +0 -0
  128. {moat-kv-0.70.14 → moat-kv-0.70.18}/systemd/sysusers +0 -0
  129. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/conftest.py +0 -0
  130. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/logging.cfg +0 -0
  131. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/test_feature_acl.py +1 -1
  132. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/test_feature_allrunner.py +0 -0
  133. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/test_feature_auth.py +1 -1
  134. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/test_feature_code.py +0 -0
  135. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/test_feature_dh.py +0 -0
  136. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/test_feature_mirror.py +0 -0
  137. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/test_feature_runner.py +0 -0
  138. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/test_feature_singlerunner.py +0 -0
  139. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/test_kill.py +1 -1
  140. {moat-kv-0.70.14 → moat-kv-0.70.18}/tests/test_passthru.py +0 -0
@@ -0,0 +1,21 @@
1
+ [submodule "ext/inv"]
2
+ path = ext/inv
3
+ url = git://git.smurf.noris.de/moat-kv-inv.git
4
+ [submodule "ext/ow"]
5
+ path = ext/ow
6
+ url = git://git.smurf.noris.de/moat-kv-ow.git
7
+ [submodule "ext/ha"]
8
+ path = ext/ha
9
+ url = git://git.smurf.noris.de/moat-kv-ha.git
10
+ [submodule "ext/moat-knx"]
11
+ path = ext/knx
12
+ url = git://git.smurf.noris.de/moat-kv-knx.git
13
+ [submodule "ext/gpio"]
14
+ path = ext/gpio
15
+ url = git://git.smurf.noris.de/moat-kv-gpio.git
16
+ [submodule "moat-kv-akumuli"]
17
+ path = ext/akumuli
18
+ url = git://git.smurf.noris.de/moat-kv-akumuli.git
19
+ [submodule "ext/wago"]
20
+ path = ext/wago
21
+ url = git://git.smurf.noris.de/moat-kv-wago.git
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: moat-kv
3
- Version: 0.70.14
3
+ Version: 0.70.18
4
4
  Summary: A distributed no-master key-value store
5
5
  Author-email: Matthias Urlichs <matthias@urlichs.de>
6
6
  Project-URL: homepage, https://m-o-a-t.org
@@ -1,6 +1,10 @@
1
1
  Open issues
2
2
  ===========
3
3
 
4
+ * Exchange a version code on startup
5
+
6
+ * CBOR
7
+
4
8
  * Ping: ignore messages with decreasing tock (per node)
5
9
 
6
10
  * chroot operation: add and test proper sub-roots, including auth and
@@ -22,14 +26,9 @@ Open issues
22
26
 
23
27
  * Rather than mangling split messages, use a MsgPack extension type.
24
28
 
25
- * Rate limiting. Currently the server can flood the client with data and no
26
- other call can get a word in edgewise.
27
-
28
29
  * AnyRunner: Do proper load balancing; the leader should be able to tell
29
30
  some other node to run a job if it's busy.
30
31
 
31
- * Add an EveryRunner (with per-node status of course).
32
-
33
32
  * Keep an error index on the server? Something more general?
34
33
 
35
34
  * Restart code that's been changed (without waiting for restart/retry).
@@ -39,8 +38,6 @@ Open issues
39
38
 
40
39
  * Implement a shared secret to sign server-to-server messages.
41
40
 
42
- * Re-implement Serf (or something like it) in Python, it's large and adds latency
43
-
44
41
  * Runner: switch to monotonic time (except for target time!)
45
42
 
46
43
  * Error consolidation: if a conflict doesn't get resolved on its own, do it
@@ -3,10 +3,13 @@
3
3
  __path__ = __import__("pkgutil").extend_path(__path__, __name__)
4
4
 
5
5
  try:
6
+ import warning
6
7
  import pkg_resources # part of setuptools
7
8
 
8
- _version = pkg_resources.require("moat.kv")[0].version
9
+ with warnings.filterwarnings("ignore"):
10
+ _version = pkg_resources.require("moat.kv")[0].version
9
11
  del pkg_resources
12
+ del warnings
10
13
 
11
14
  _version_tuple = tuple(int(x) for x in _version.split("."))
12
15
 
@@ -35,15 +35,23 @@ class NullObj:
35
35
  raise self._exc
36
36
 
37
37
  def __getattr__(self, k):
38
- if k[0] == "_" and k not in ("_request","_cfg"):
38
+ if k[0] == "_" and k not in ("_request", "_cfg"):
39
39
  return object.__getattribute__(self, k)
40
40
  raise self._exc
41
41
 
42
42
 
43
- @load_subgroup(sub_pre="moat.kv.command", sub_post="cli", ext_pre="moat.kv", ext_post="_main.cli")
44
- @click.option("-h", "--host", default=None, help=f"Host to use. Default: {CFG.kv.conn.host}")
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
+ )
45
49
  @click.option(
46
- "-p", "--port", type=int, default=None, help=f"Port to use. Default: {CFG.kv.conn.port}"
50
+ "-p",
51
+ "--port",
52
+ type=int,
53
+ default=None,
54
+ help=f"Port to use. Default: {CFG.kv.conn.port}",
47
55
  )
48
56
  @click.option(
49
57
  "-a",
@@ -117,7 +117,10 @@ class DeleteActor:
117
117
  continue
118
118
  self.n_pings += 1
119
119
  if self.n_pings > self.n_nodes:
120
- mx, self.max_seen = (self.max_seen, max(self.max_seen, val[1]))
120
+ mx, self.max_seen = (
121
+ self.max_seen,
122
+ max(self.max_seen, val[1]),
123
+ )
121
124
  if val[0] > mx > 0:
122
125
  await self.server.resync_deleted(evt.msg.history)
123
126
  continue
@@ -279,7 +279,11 @@ class BaseClientAuthMaker(_AuthLoaded):
279
279
  """
280
280
  # pragma: no cover
281
281
  res = await client._request(
282
- "auth_get", typ=cls._auth_method, kind=_kind, ident=ident, nchain=0 if _initial else 2
282
+ "auth_get",
283
+ typ=cls._auth_method,
284
+ kind=_kind,
285
+ ident=ident,
286
+ nchain=0 if _initial else 2,
283
287
  )
284
288
  self = cls(_initial=_initial)
285
289
  self._chain = res.chain
@@ -338,7 +342,9 @@ class BaseServerAuth(_AuthLoaded):
338
342
 
339
343
  try:
340
344
  data = data["conv"].data["key"]
341
- res, _ = root.follow_acl(Path(None, "conv", data), create=False, nulls_ok=True)
345
+ res, _ = root.follow_acl(
346
+ Path(None, "conv", data), create=False, nulls_ok=True
347
+ )
342
348
  return res
343
349
  except (KeyError, AttributeError):
344
350
  return ConvNull
@@ -348,7 +354,9 @@ class BaseServerAuth(_AuthLoaded):
348
354
  data = data["acl"].data["key"]
349
355
  if data == "*":
350
356
  return NullACL
351
- acl, _ = root.follow_acl(Path(None, "acl", data), create=False, nulls_ok=True)
357
+ acl, _ = root.follow_acl(
358
+ Path(None, "acl", data), create=False, nulls_ok=True
359
+ )
352
360
  return ACLFinder(acl)
353
361
  except (KeyError, AttributeError):
354
362
  return NullACL
@@ -362,17 +370,13 @@ class BaseServerAuth(_AuthLoaded):
362
370
  """
363
371
  return {}
364
372
 
365
- async def check_read(
366
- self, *path, client: ServerClient, data=None
367
- ): # pylint: disable=unused-argument
373
+ async def check_read(self, *path, client: ServerClient, data=None): # pylint: disable=unused-argument
368
374
  """Check that this user may read the element at this location.
369
375
  This method may modify the data.
370
376
  """
371
377
  return data
372
378
 
373
- async def check_write(
374
- self, *path, client: ServerClient, data=None
375
- ): # pylint: disable=unused-argument
379
+ async def check_write(self, *path, client: ServerClient, data=None): # pylint: disable=unused-argument
376
380
  """Check that this user may write the element at this location.
377
381
  This method may modify the data.
378
382
  """
@@ -415,7 +419,9 @@ class BaseServerAuthMaker(_AuthLoaded):
415
419
 
416
420
  @classmethod
417
421
  async def recv(
418
- cls, cmd: StreamCommand, data: attrdict # pylint: disable=unused-argument
422
+ cls,
423
+ cmd: StreamCommand,
424
+ data: attrdict, # pylint: disable=unused-argument
419
425
  ) -> "BaseServerAuthMaker":
420
426
  """Create/update a new user by reading the record from the client"""
421
427
  dt = data.get("data", None) or {}
@@ -62,7 +62,9 @@ class ServerUserMaker(BaseServerAuthMaker):
62
62
  await cmd.send(step="SendWant")
63
63
  msg = await cmd.recv()
64
64
  assert msg.step == "WantName"
65
- await cmd.send(step="SendName", name=self.name, chain=self._chain.serialize(nchain=3))
65
+ await cmd.send(
66
+ step="SendName", name=self.name, chain=self._chain.serialize(nchain=3)
67
+ )
66
68
  msg = await cmd.recv()
67
69
 
68
70
  # Annoying methods to read+save the user name from/to KV
@@ -82,13 +84,17 @@ class ClientUserMaker(BaseClientAuthMaker):
82
84
  gen_schema = dict(
83
85
  type="object",
84
86
  additionalProperties=False,
85
- properties=dict(name=dict(type="string", minLength=1, pattern="^[a-zA-Z][a-zA-Z0-9_]*$")),
87
+ properties=dict(
88
+ name=dict(type="string", minLength=1, pattern="^[a-zA-Z][a-zA-Z0-9_]*$")
89
+ ),
86
90
  required=["name"],
87
91
  )
88
92
  mod_schema = dict(
89
93
  type="object",
90
94
  additionalProperties=False,
91
- properties=dict(name=dict(type="string", minLength=1, pattern="^[a-zA-Z][a-zA-Z0-9_]*$")),
95
+ properties=dict(
96
+ name=dict(type="string", minLength=1, pattern="^[a-zA-Z][a-zA-Z0-9_]*$")
97
+ ),
92
98
  # required=[],
93
99
  )
94
100
  name = None
@@ -148,7 +154,9 @@ class ClientUser(BaseClientAuth):
148
154
  schema = dict(
149
155
  type="object",
150
156
  additionalProperties=False,
151
- properties=dict(name=dict(type="string", minLength=1, pattern="^[a-zA-Z][a-zA-Z0-9_]*$")),
157
+ properties=dict(
158
+ name=dict(type="string", minLength=1, pattern="^[a-zA-Z][a-zA-Z0-9_]*$")
159
+ ),
152
160
  required=["name"],
153
161
  )
154
162
  _name = None
@@ -168,9 +168,7 @@ class ClientUserMaker(BaseClientAuthMaker):
168
168
  pass
169
169
  return self
170
170
 
171
- async def send(
172
- self, client: Client, _kind="user", **msg
173
- ): # pylint: disable=unused-argument,arguments-differ
171
+ async def send(self, client: Client, _kind="user", **msg): # pylint: disable=unused-argument,arguments-differ
174
172
  """Send a record representing this user to the server."""
175
173
  if self._pass is not None:
176
174
  msg["password"] = await pack_pwd(client, self._pass, self._length)
@@ -110,14 +110,16 @@ async def client_scope(_name=None, **cfg):
110
110
  """
111
111
 
112
112
  if _name is None:
113
- _name = cfg.get("conn",{}).get("name", "conn")
113
+ _name = cfg.get("conn", {}).get("name", "conn")
114
114
  if _name is None:
115
115
  global _cid
116
116
  _cid += 1
117
117
  _name = f"_{_cid}"
118
118
  # uniqueness required for testing.
119
119
  # TODO replace with a dependency on the test server.
120
- return await scope.service(f"moat.kv.client.{_name}", _scoped_client, _name=_name, **cfg)
120
+ return await scope.service(
121
+ f"moat.kv.client.{_name}", _scoped_client, _name=_name, **cfg
122
+ )
121
123
 
122
124
 
123
125
  class StreamedRequest:
@@ -153,7 +155,9 @@ class StreamedRequest:
153
155
  self._path_long = lambda x: x
154
156
  if client.qlen > 0:
155
157
  self.dw = DelayedWrite(client.qlen)
156
- self.qr = DelayedRead(client.qlen, get_seq=self._get_seq, send_ack=self._send_ack)
158
+ self.qr = DelayedRead(
159
+ client.qlen, get_seq=self._get_seq, send_ack=self._send_ack
160
+ )
157
161
  else:
158
162
  self.qr = create_queue(client.config.server.buffer)
159
163
 
@@ -480,7 +484,9 @@ class Client:
480
484
  res = await self._request(
481
485
  "diffie_hellman", pubkey=num2byte(k.public_key), length=length
482
486
  ) # length=k.key_length
483
- await anyio.to_thread.run_sync(k.generate_shared_secret, byte2num(res.pubkey))
487
+ await anyio.to_thread.run_sync(
488
+ k.generate_shared_secret, byte2num(res.pubkey)
489
+ )
484
490
  self._dh_key = num2byte(k.shared_secret)[0:32]
485
491
  return self._dh_key
486
492
 
@@ -529,6 +535,7 @@ class Client:
529
535
 
530
536
  except BaseException as exc:
531
537
  logger.warning("Reader died: %r", exc, exc_info=exc)
538
+ raise
532
539
  finally:
533
540
  with anyio.fail_after(2, shield=True):
534
541
  hdl, self._handlers = self._handlers, None
@@ -540,9 +547,7 @@ class Client:
540
547
  except ClosedResourceError:
541
548
  pass
542
549
 
543
- async def _request(
544
- self, action, iter=None, seq=None, _async=False, **params
545
- ): # pylint: disable=redefined-builtin # iter
550
+ async def _request(self, action, iter=None, seq=None, _async=False, **params): # pylint: disable=redefined-builtin # iter
546
551
  """Send a request. Wait for a reply.
547
552
 
548
553
  Args:
@@ -676,12 +681,16 @@ class Client:
676
681
  if not sa or not sa[0]:
677
682
  # no auth required
678
683
  if auth:
679
- logger.info("Tried to use auth=%s, but not required.", auth._auth_method)
684
+ logger.info(
685
+ "Tried to use auth=%s, but not required.", auth._auth_method
686
+ )
680
687
  return
681
688
  if not auth:
682
689
  raise ClientAuthRequiredError("You need to log in using:", sa[0])
683
690
  if auth._auth_method != sa[0]:
684
- raise ClientAuthMethodError(f"You cannot use {auth._auth_method!r} auth", sa)
691
+ raise ClientAuthMethodError(
692
+ f"You cannot use {auth._auth_method!r} auth", sa
693
+ )
685
694
  if getattr(auth, "_DEBUG", False):
686
695
  auth._length = 16
687
696
  await auth.auth(self)
@@ -727,7 +736,9 @@ class Client:
727
736
  self._server_init = msg = await hello.get()
728
737
  self.logger.debug("Hello %s", msg)
729
738
  self.server_name = msg.node
730
- self.client_name = cfg["name"] or socket.gethostname() or self.server_name
739
+ self.client_name = (
740
+ cfg["name"] or socket.gethostname() or self.server_name
741
+ )
731
742
  if "qlen" in msg:
732
743
  self.qlen = min(msg.qlen, 99) # self.config.server.buffer
733
744
  await self._send(seq=0, qlen=self.qlen)
@@ -735,7 +746,9 @@ class Client:
735
746
 
736
747
  from .config import ConfigRoot
737
748
 
738
- self._config = await ConfigRoot.as_handler(self, require_client=False)
749
+ self._config = await ConfigRoot.as_handler(
750
+ self, require_client=False
751
+ )
739
752
 
740
753
  except TimeoutError:
741
754
  raise
@@ -779,7 +792,16 @@ class Client:
779
792
  raise RuntimeError("You need a path, not a string")
780
793
  return self._request(action="get_value", path=path, iter=False, nchain=nchain)
781
794
 
782
- def set(self, path, value=NotGiven, *, chain=NotGiven, prev=NotGiven, nchain=0, idem=None):
795
+ def set(
796
+ self,
797
+ path,
798
+ value=NotGiven,
799
+ *,
800
+ chain=NotGiven,
801
+ prev=NotGiven,
802
+ nchain=0,
803
+ idem=None,
804
+ ):
783
805
  """
784
806
  Set or update a value.
785
807
 
@@ -856,8 +878,13 @@ class Client:
856
878
  raise RuntimeError("You need a path, not a string")
857
879
  if empty is None:
858
880
  empty = not with_data
859
- res = await self._request(action="enum", path=path, with_data=with_data, empty=empty, **kw)
860
- return res.result
881
+ res = await self._request(
882
+ action="enum", path=path, with_data=with_data, empty=empty, **kw
883
+ )
884
+ try:
885
+ return res.result
886
+ except AttributeError:
887
+ raise res.q.value.error from None # XXX fix this
861
888
 
862
889
  async def get_tree(self, path, *, long_path=True, **kw):
863
890
  """
@@ -936,7 +963,9 @@ class Client:
936
963
  """
937
964
  if isinstance(path, str):
938
965
  raise RuntimeError("You need a path, not a string")
939
- return self._stream(action="watch", path=path, iter=True, long_path=long_path, **kw)
966
+ return self._stream(
967
+ action="watch", path=path, iter=True, long_path=long_path, **kw
968
+ )
940
969
 
941
970
  def mirror(self, path, *, root_type=None, **kw):
942
971
  """An async context manager that affords an update-able mirror
@@ -159,7 +159,9 @@ class CodeRoot(ClientRoot):
159
159
  make_proc(code, variables, path, use_async=is_async)
160
160
 
161
161
  r = await self.client.set(
162
- self._path + path, value=dict(code=code, is_async=is_async, vars=variables), nchain=2
162
+ self._path + path,
163
+ value=dict(code=code, is_async=is_async, vars=variables),
164
+ nchain=2,
163
165
  )
164
166
  await self.wait_chain(r.chain)
165
167
 
@@ -94,10 +94,15 @@ async def set_(obj, acl, name, path):
94
94
  acl = set(acl)
95
95
 
96
96
  if acl - ACL:
97
- raise click.UsageError(f"You're trying to set an unknown ACL flag: {acl - ACL !r}")
97
+ raise click.UsageError(
98
+ f"You're trying to set an unknown ACL flag: {acl - ACL !r}"
99
+ )
98
100
 
99
101
  res = await obj.client._request(
100
- action="get_internal", path=("acl", name) + path, iter=False, nchain=3 if obj.meta else 1
102
+ action="get_internal",
103
+ path=("acl", name) + path,
104
+ iter=False,
105
+ nchain=3 if obj.meta else 1,
101
106
  )
102
107
  ov = set(res.get("value", ""))
103
108
  if ov - ACL:
@@ -105,7 +110,10 @@ async def set_(obj, acl, name, path):
105
110
 
106
111
  if mode == "-" and not acl:
107
112
  res = await obj.client._request(
108
- action="delete_internal", path=("acl", name) + path, iter=False, chain=res.chain
113
+ action="delete_internal",
114
+ path=("acl", name) + path,
115
+ iter=False,
116
+ chain=res.chain,
109
117
  )
110
118
  v = "-"
111
119
 
@@ -125,7 +133,12 @@ async def set_(obj, acl, name, path):
125
133
  )
126
134
 
127
135
  if obj.meta:
128
- res = {"old": "".join(ov), "new": "".join(v), "chain": res.chain, "tock": res.tock}
136
+ res = {
137
+ "old": "".join(ov),
138
+ "new": "".join(v),
139
+ "chain": res.chain,
140
+ "tock": res.tock,
141
+ }
129
142
  yprint(res, stream=obj.stdout)
130
143
  else:
131
144
  res = {"old": "".join(ov), "new": "".join(v)}
@@ -63,7 +63,12 @@ async def enum_typ(obj, kind="user", ident=None, nchain=0):
63
63
  async for auth in enum_auth(obj):
64
64
  if ident is not None:
65
65
  res = await obj.client._request(
66
- action="auth_list", typ=auth, kind=kind, ident=ident, iter=False, nchain=nchain
66
+ action="auth_list",
67
+ typ=auth,
68
+ kind=kind,
69
+ ident=ident,
70
+ iter=False,
71
+ nchain=nchain,
67
72
  )
68
73
  yield res
69
74
  else:
@@ -106,7 +111,10 @@ async def user():
106
111
 
107
112
  @user.command("list")
108
113
  @click.option(
109
- "-v", "--verbose", is_flag=True, help="Print complete results. Default: just the names"
114
+ "-v",
115
+ "--verbose",
116
+ is_flag=True,
117
+ help="Print complete results. Default: just the names",
110
118
  )
111
119
  @click.pass_obj # pylint: disable=function-redefined
112
120
  async def list_user(obj, verbose):
@@ -157,7 +165,10 @@ async def param(obj, new, ident, type, key, args): # pylint: disable=redefined-
157
165
  u._length = 16
158
166
  # ou = await u.recv(obj.client, ident, _initial=False) # unused
159
167
  res = await obj.client._request(
160
- action="get_internal", path=("auth", auth, "user", ident, type), iter=False, nchain=3
168
+ action="get_internal",
169
+ path=("auth", auth, "user", ident, type),
170
+ iter=False,
171
+ nchain=3,
161
172
  )
162
173
 
163
174
  kw = res.get("value", NotGiven)
@@ -3,7 +3,16 @@
3
3
  import sys
4
4
 
5
5
  import asyncclick as click
6
- from moat.util import NotGiven, P, Path, PathLongener, attr_args, process_args, yload, yprint
6
+ from moat.util import (
7
+ NotGiven,
8
+ P,
9
+ Path,
10
+ PathLongener,
11
+ attr_args,
12
+ process_args,
13
+ yload,
14
+ yprint,
15
+ )
7
16
 
8
17
 
9
18
  @click.group(invoke_without_command=True) # pylint: disable=undefined-variable
@@ -23,14 +32,18 @@ async def cli(ctx, path):
23
32
 
24
33
 
25
34
  @cli.command()
26
- @click.option("-s", "--script", type=click.File(mode="w", lazy=True), help="Save the code here")
35
+ @click.option(
36
+ "-s", "--script", type=click.File(mode="w", lazy=True), help="Save the code here"
37
+ )
27
38
  @click.pass_obj
28
39
  async def get(obj, script):
29
40
  """Read a code entry"""
30
41
  if not len(obj.codepath):
31
42
  raise click.UsageError("You need a non-empty path.")
32
43
 
33
- res = await obj.client._request(action="get_value", path=obj.path, iter=False, nchain=obj.meta)
44
+ res = await obj.client._request(
45
+ action="get_value", path=obj.path, iter=False, nchain=obj.meta
46
+ )
34
47
  if "value" not in res:
35
48
  if obj.debug:
36
49
  print("No entry here.", file=sys.stderr)
@@ -53,10 +66,14 @@ async def get(obj, script):
53
66
  help="The code is async / sync (default: async)",
54
67
  default=True,
55
68
  )
56
- @click.option("-t", "--thread", is_flag=True, help="The code should run in a worker thread")
69
+ @click.option(
70
+ "-t", "--thread", is_flag=True, help="The code should run in a worker thread"
71
+ )
57
72
  @click.option("-s", "--script", type=click.File(mode="r"), help="File with the code")
58
73
  @click.option("-i", "--info", type=str, help="one-liner info about the code")
59
- @click.option("-d", "--data", type=click.File(mode="r"), help="load the metadata (YAML)")
74
+ @click.option(
75
+ "-d", "--data", type=click.File(mode="r"), help="load the metadata (YAML)"
76
+ )
60
77
  @attr_args
61
78
  @click.pass_obj
62
79
  async def set_(obj, thread, script, data, vars_, eval_, path_, async_, info):
@@ -124,7 +141,9 @@ async def mod():
124
141
 
125
142
 
126
143
  @mod.command("get")
127
- @click.option("-s", "--script", type=click.File(mode="w", lazy=True), help="Save the code here")
144
+ @click.option(
145
+ "-s", "--script", type=click.File(mode="w", lazy=True), help="Save the code here"
146
+ )
128
147
  @click.argument("path", nargs=1)
129
148
  @click.pass_obj # pylint: disable=function-redefined
130
149
  async def get_mod(obj, path, script):
@@ -153,8 +172,12 @@ async def get_mod(obj, path, script):
153
172
 
154
173
 
155
174
  @mod.command("set")
156
- @click.option("-s", "--script", type=click.File(mode="r"), help="File with the module's code")
157
- @click.option("-d", "--data", type=click.File(mode="r"), help="load the metadata (YAML)")
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
+ )
158
181
  @click.argument("path", nargs=1) # pylint: disable=function-redefined
159
182
  @click.pass_obj
160
183
  async def set_mod(obj, path, script, data):
@@ -201,10 +224,18 @@ async def set_mod(obj, path, script, data):
201
224
  "for values. Default: return as list",
202
225
  )
203
226
  @click.option(
204
- "-m", "--maxdepth", type=int, default=None, help="Limit recursion depth. Default: whole tree"
227
+ "-m",
228
+ "--maxdepth",
229
+ type=int,
230
+ default=None,
231
+ help="Limit recursion depth. Default: whole tree",
205
232
  )
206
233
  @click.option(
207
- "-M", "--mindepth", type=int, default=None, help="Starting depth. Default: whole tree"
234
+ "-M",
235
+ "--mindepth",
236
+ type=int,
237
+ default=None,
238
+ help="Starting depth. Default: whole tree",
208
239
  )
209
240
  @click.option("-f", "--full", is_flag=True, help="print complete entries.")
210
241
  @click.option("-s", "--short", is_flag=True, help="print shortened entries.")