moat-kv 0.70.22__py3-none-any.whl → 0.70.24__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 (142) hide show
  1. build/lib/docs/source/conf.py +201 -0
  2. build/lib/examples/pathify.py +45 -0
  3. build/lib/moat/kv/__init__.py +19 -0
  4. build/lib/moat/kv/_cfg.yaml +97 -0
  5. build/lib/moat/kv/_main.py +91 -0
  6. build/lib/moat/kv/actor/__init__.py +98 -0
  7. build/lib/moat/kv/actor/deletor.py +139 -0
  8. build/lib/moat/kv/auth/__init__.py +444 -0
  9. build/lib/moat/kv/auth/_test.py +166 -0
  10. build/lib/moat/kv/auth/password.py +234 -0
  11. build/lib/moat/kv/auth/root.py +58 -0
  12. build/lib/moat/kv/backend/__init__.py +67 -0
  13. build/lib/moat/kv/backend/mqtt.py +74 -0
  14. build/lib/moat/kv/backend/serf.py +45 -0
  15. build/lib/moat/kv/client.py +1025 -0
  16. build/lib/moat/kv/code.py +236 -0
  17. build/lib/moat/kv/codec.py +11 -0
  18. build/lib/moat/kv/command/__init__.py +1 -0
  19. build/lib/moat/kv/command/acl.py +180 -0
  20. build/lib/moat/kv/command/auth.py +261 -0
  21. build/lib/moat/kv/command/code.py +293 -0
  22. build/lib/moat/kv/command/codec.py +186 -0
  23. build/lib/moat/kv/command/data.py +265 -0
  24. build/lib/moat/kv/command/dump/__init__.py +143 -0
  25. build/lib/moat/kv/command/error.py +149 -0
  26. build/lib/moat/kv/command/internal.py +248 -0
  27. build/lib/moat/kv/command/job.py +433 -0
  28. build/lib/moat/kv/command/log.py +53 -0
  29. build/lib/moat/kv/command/server.py +114 -0
  30. build/lib/moat/kv/command/type.py +201 -0
  31. build/lib/moat/kv/config.py +46 -0
  32. build/lib/moat/kv/data.py +216 -0
  33. build/lib/moat/kv/errors.py +561 -0
  34. build/lib/moat/kv/exceptions.py +126 -0
  35. build/lib/moat/kv/mock/__init__.py +101 -0
  36. build/lib/moat/kv/mock/mqtt.py +159 -0
  37. build/lib/moat/kv/mock/serf.py +250 -0
  38. build/lib/moat/kv/mock/tracer.py +63 -0
  39. build/lib/moat/kv/model.py +1069 -0
  40. build/lib/moat/kv/obj/__init__.py +646 -0
  41. build/lib/moat/kv/obj/command.py +241 -0
  42. build/lib/moat/kv/runner.py +1347 -0
  43. build/lib/moat/kv/server.py +2809 -0
  44. build/lib/moat/kv/types.py +513 -0
  45. debian/moat-kv/usr/lib/python3/dist-packages/docs/source/conf.py +201 -0
  46. debian/moat-kv/usr/lib/python3/dist-packages/examples/pathify.py +45 -0
  47. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/__init__.py +19 -0
  48. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/_cfg.yaml +97 -0
  49. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/_main.py +91 -0
  50. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/actor/__init__.py +98 -0
  51. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/actor/deletor.py +139 -0
  52. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/__init__.py +444 -0
  53. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/_test.py +166 -0
  54. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/password.py +234 -0
  55. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/root.py +58 -0
  56. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/__init__.py +67 -0
  57. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/mqtt.py +74 -0
  58. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/serf.py +45 -0
  59. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/client.py +1025 -0
  60. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/code.py +236 -0
  61. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/codec.py +11 -0
  62. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/__init__.py +1 -0
  63. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/acl.py +180 -0
  64. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/auth.py +261 -0
  65. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/code.py +293 -0
  66. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/codec.py +186 -0
  67. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/data.py +265 -0
  68. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/dump/__init__.py +143 -0
  69. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/error.py +149 -0
  70. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/internal.py +248 -0
  71. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/job.py +433 -0
  72. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/log.py +53 -0
  73. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/server.py +114 -0
  74. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/type.py +201 -0
  75. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/config.py +46 -0
  76. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/data.py +216 -0
  77. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/errors.py +561 -0
  78. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/exceptions.py +126 -0
  79. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/__init__.py +101 -0
  80. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/mqtt.py +159 -0
  81. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/serf.py +250 -0
  82. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/tracer.py +63 -0
  83. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/model.py +1069 -0
  84. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/obj/__init__.py +646 -0
  85. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/obj/command.py +241 -0
  86. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/runner.py +1347 -0
  87. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/server.py +2809 -0
  88. debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/types.py +513 -0
  89. docs/source/conf.py +201 -0
  90. examples/pathify.py +45 -0
  91. moat/kv/__init__.py +1 -0
  92. moat/kv/_cfg.yaml +97 -0
  93. moat/kv/_main.py +6 -9
  94. moat/kv/actor/__init__.py +2 -1
  95. moat/kv/actor/deletor.py +3 -1
  96. moat/kv/auth/__init__.py +8 -10
  97. moat/kv/auth/_test.py +6 -12
  98. moat/kv/auth/password.py +2 -0
  99. moat/kv/auth/root.py +2 -0
  100. moat/kv/backend/__init__.py +1 -0
  101. moat/kv/backend/mqtt.py +3 -3
  102. moat/kv/backend/serf.py +1 -0
  103. moat/kv/client.py +34 -50
  104. moat/kv/code.py +10 -3
  105. moat/kv/codec.py +1 -0
  106. moat/kv/command/acl.py +12 -6
  107. moat/kv/command/auth.py +5 -2
  108. moat/kv/command/code.py +10 -23
  109. moat/kv/command/codec.py +10 -14
  110. moat/kv/command/data.py +12 -21
  111. moat/kv/command/dump/__init__.py +4 -2
  112. moat/kv/command/error.py +5 -12
  113. moat/kv/command/internal.py +6 -15
  114. moat/kv/command/job.py +26 -31
  115. moat/kv/command/log.py +1 -0
  116. moat/kv/command/server.py +2 -3
  117. moat/kv/command/type.py +26 -28
  118. moat/kv/config.py +2 -0
  119. moat/kv/data.py +8 -7
  120. moat/kv/errors.py +17 -9
  121. moat/kv/exceptions.py +1 -7
  122. moat/kv/mock/__init__.py +9 -5
  123. moat/kv/mock/mqtt.py +7 -12
  124. moat/kv/mock/serf.py +6 -9
  125. moat/kv/mock/tracer.py +2 -4
  126. moat/kv/model.py +16 -24
  127. moat/kv/obj/__init__.py +30 -20
  128. moat/kv/obj/command.py +7 -12
  129. moat/kv/runner.py +38 -35
  130. moat/kv/server.py +86 -90
  131. moat/kv/types.py +5 -8
  132. {moat_kv-0.70.22.dist-info → moat_kv-0.70.24.dist-info}/METADATA +15 -18
  133. moat_kv-0.70.24.dist-info/RECORD +137 -0
  134. {moat_kv-0.70.22.dist-info → moat_kv-0.70.24.dist-info}/WHEEL +1 -1
  135. moat_kv-0.70.24.dist-info/licenses/LICENSE.txt +14 -0
  136. moat_kv-0.70.24.dist-info/top_level.txt +9 -0
  137. moat/kv/_config.yaml +0 -98
  138. moat_kv-0.70.22.dist-info/LICENSE +0 -3
  139. moat_kv-0.70.22.dist-info/LICENSE.APACHE2 +0 -202
  140. moat_kv-0.70.22.dist-info/LICENSE.MIT +0 -20
  141. moat_kv-0.70.22.dist-info/RECORD +0 -49
  142. moat_kv-0.70.22.dist-info/top_level.txt +0 -1
examples/pathify.py ADDED
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/python3
2
+
3
+ # Batch-convert data. In this case I had some entries which were stored as
4
+ # a list, but using Path made much more sense (esp when you need to
5
+ # view/edit the yaml export).
6
+
7
+ import anyio
8
+ from moat.kv.client import open_client
9
+ from moat.util import P, yload, Path
10
+ import asyncclick as click
11
+
12
+
13
+ def conv(m, s: str) -> bool:
14
+ try:
15
+ d = m.value[s]
16
+ except KeyError:
17
+ return 0
18
+ if isinstance(d, Path):
19
+ return 0
20
+ if not isinstance(d, Sequence):
21
+ return 0
22
+ d = Path.build(d)
23
+ m.value[s] = d
24
+ return 1
25
+
26
+
27
+ @click.command()
28
+ @click.argument("path", type=P)
29
+ @click.argument("keys", type=str, nargs=-1)
30
+ async def main(path, keys):
31
+ if not keys:
32
+ keys = "src dest dst state".split()
33
+ with open("/etc/moat.kv.cfg") as cff:
34
+ cfg = yload(cff)
35
+ async with open_client(**cfg) as client:
36
+ async for m in client.get_tree(path, nchain=2):
37
+ n = 0
38
+ for k in keys:
39
+ n += conv(m, k)
40
+ if n:
41
+ await client.set(ORIG + m.path, value=m.value, chain=m.chain)
42
+
43
+
44
+ if __name__ == "__main__":
45
+ main()
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/actor/__init__.py CHANGED
@@ -2,8 +2,9 @@
2
2
  This module implements a :class:`asyncactor.Actor` which works on top of
3
3
  a MoaT-KV client.
4
4
  """
5
+ from __future__ import annotations
5
6
 
6
- from asyncactor import Actor # noqa
7
+ from asyncactor import Actor
7
8
  from asyncactor.abc import MonitorStream, Transport
8
9
 
9
10
  __all__ = [
moat/kv/actor/deletor.py CHANGED
@@ -3,6 +3,8 @@ This module implements additional code for the server-side DeleteActor,
3
3
  which is used to clean up the list of deleted nodes.
4
4
  """
5
5
 
6
+ from __future__ import annotations
7
+
6
8
  import weakref
7
9
  from collections import deque
8
10
 
@@ -41,7 +43,7 @@ class DeleteActor:
41
43
  self.tags = self.tags[-TAGS:]
42
44
  await self.actor.set_value((self.tags[0], self.tags[-1]))
43
45
 
44
- def add_deleted(self, nodes: "NodeSet"): # noqa: F821
46
+ def add_deleted(self, nodes: NodeSet): # noqa: F821
45
47
  """
46
48
  These nodes are deleted. Remember them for some time.
47
49
  """
moat/kv/auth/__init__.py CHANGED
@@ -59,6 +59,8 @@ The server process is:
59
59
 
60
60
  """
61
61
 
62
+ from __future__ import annotations
63
+
62
64
  import io
63
65
  from importlib import import_module
64
66
 
@@ -87,7 +89,7 @@ add_schema = {
87
89
  "additionalProperties": False,
88
90
  "properties": {"key": {type: "string", "minLength": 1}},
89
91
  },
90
- }
92
+ },
91
93
  }
92
94
 
93
95
 
@@ -111,7 +113,7 @@ def gen_auth(s: str):
111
113
 
112
114
  m, *p = s.split()
113
115
  if len(p) == 0 and m[0] == "=":
114
- with io.open(m[1:], "r", encoding="utf-8") as f:
116
+ with open(m[1:], encoding="utf-8") as f:
115
117
  kw = yload(f)
116
118
  m = kw.pop("type")
117
119
  else:
@@ -147,7 +149,7 @@ async def null_server_login(stream):
147
149
  return stream
148
150
 
149
151
 
150
- async def null_client_login(stream, user: "BaseClientAuth"): # pylint: disable=unused-argument
152
+ async def null_client_login(stream, user: BaseClientAuth): # pylint: disable=unused-argument
151
153
  return stream
152
154
 
153
155
 
@@ -342,9 +344,7 @@ class BaseServerAuth(_AuthLoaded):
342
344
 
343
345
  try:
344
346
  data = data["conv"].data["key"]
345
- res, _ = root.follow_acl(
346
- Path(None, "conv", data), create=False, nulls_ok=True
347
- )
347
+ res, _ = root.follow_acl(Path(None, "conv", data), create=False, nulls_ok=True)
348
348
  return res
349
349
  except (KeyError, AttributeError):
350
350
  return ConvNull
@@ -354,9 +354,7 @@ class BaseServerAuth(_AuthLoaded):
354
354
  data = data["acl"].data["key"]
355
355
  if data == "*":
356
356
  return NullACL
357
- acl, _ = root.follow_acl(
358
- Path(None, "acl", data), create=False, nulls_ok=True
359
- )
357
+ acl, _ = root.follow_acl(Path(None, "acl", data), create=False, nulls_ok=True)
360
358
  return ACLFinder(acl)
361
359
  except (KeyError, AttributeError):
362
360
  return NullACL
@@ -422,7 +420,7 @@ class BaseServerAuthMaker(_AuthLoaded):
422
420
  cls,
423
421
  cmd: StreamCommand,
424
422
  data: attrdict, # pylint: disable=unused-argument
425
- ) -> "BaseServerAuthMaker":
423
+ ) -> BaseServerAuthMaker:
426
424
  """Create/update a new user by reading the record from the client"""
427
425
  dt = data.get("data", None) or {}
428
426
  jsonschema.validate(instance=dt, schema=cls.schema)
moat/kv/auth/_test.py CHANGED
@@ -5,6 +5,8 @@ Test auth method.
5
5
  Does not limit anything, allows everything.
6
6
  """
7
7
 
8
+ from __future__ import annotations
9
+
8
10
  import logging
9
11
 
10
12
  log = logging.getLogger(__name__)
@@ -62,9 +64,7 @@ class ServerUserMaker(BaseServerAuthMaker):
62
64
  await cmd.send(step="SendWant")
63
65
  msg = await cmd.recv()
64
66
  assert msg.step == "WantName"
65
- await cmd.send(
66
- step="SendName", name=self.name, chain=self._chain.serialize(nchain=3)
67
- )
67
+ await cmd.send(step="SendName", name=self.name, chain=self._chain.serialize(nchain=3))
68
68
  msg = await cmd.recv()
69
69
 
70
70
  # Annoying methods to read+save the user name from/to KV
@@ -84,17 +84,13 @@ class ClientUserMaker(BaseClientAuthMaker):
84
84
  gen_schema = dict(
85
85
  type="object",
86
86
  additionalProperties=False,
87
- properties=dict(
88
- name=dict(type="string", minLength=1, pattern="^[a-zA-Z][a-zA-Z0-9_]*$")
89
- ),
87
+ properties=dict(name=dict(type="string", minLength=1, pattern="^[a-zA-Z][a-zA-Z0-9_]*$")),
90
88
  required=["name"],
91
89
  )
92
90
  mod_schema = dict(
93
91
  type="object",
94
92
  additionalProperties=False,
95
- properties=dict(
96
- name=dict(type="string", minLength=1, pattern="^[a-zA-Z][a-zA-Z0-9_]*$")
97
- ),
93
+ properties=dict(name=dict(type="string", minLength=1, pattern="^[a-zA-Z][a-zA-Z0-9_]*$")),
98
94
  # required=[],
99
95
  )
100
96
  name = None
@@ -154,9 +150,7 @@ class ClientUser(BaseClientAuth):
154
150
  schema = dict(
155
151
  type="object",
156
152
  additionalProperties=False,
157
- properties=dict(
158
- name=dict(type="string", minLength=1, pattern="^[a-zA-Z][a-zA-Z0-9_]*$")
159
- ),
153
+ properties=dict(name=dict(type="string", minLength=1, pattern="^[a-zA-Z][a-zA-Z0-9_]*$")),
160
154
  required=["name"],
161
155
  )
162
156
  _name = None
moat/kv/auth/password.py CHANGED
@@ -5,6 +5,8 @@ Password-based auth method.
5
5
  Does not limit anything, allows everything.
6
6
  """
7
7
 
8
+ from __future__ import annotations
9
+
8
10
  import nacl.secret
9
11
 
10
12
  from ..client import Client, NoData
moat/kv/auth/root.py CHANGED
@@ -5,6 +5,8 @@ Null auth method.
5
5
  Does not limit anything, allows everything.
6
6
  """
7
7
 
8
+ from __future__ import annotations
9
+
8
10
  from . import (
9
11
  BaseClientAuth,
10
12
  BaseClientAuthMaker,
@@ -1,3 +1,4 @@
1
+ from __future__ import annotations
1
2
  from abc import ABCMeta, abstractmethod
2
3
  from contextlib import asynccontextmanager
3
4
 
moat/kv/backend/mqtt.py CHANGED
@@ -1,9 +1,11 @@
1
+ from __future__ import annotations
1
2
  import logging
2
3
  from contextlib import asynccontextmanager
3
4
 
4
5
  import anyio
5
6
  from moat.mqtt.client import MQTTClient
6
7
  from moat.mqtt.codecs import NoopCodec
8
+ from moat.util import NotGiven
7
9
 
8
10
  from . import Backend
9
11
 
@@ -24,9 +26,7 @@ class MqttBackend(Backend):
24
26
 
25
27
  @asynccontextmanager
26
28
  async def connect(self, *a, **kw):
27
- codec = kw.pop("codec", None)
28
- if codec is None:
29
- codec = NoopCodec()
29
+ codec = kw.pop("codec", NotGiven)
30
30
  C = MQTTClient(self._tg, codec=codec)
31
31
  try:
32
32
  await C.connect(*a, **kw)
moat/kv/backend/serf.py CHANGED
@@ -1,3 +1,4 @@
1
+ from __future__ import annotations
1
2
  from contextlib import asynccontextmanager
2
3
 
3
4
  import anyio
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()
@@ -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