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
@@ -1,561 +0,0 @@
1
- """
2
- This module implements a way to store error messages in MoaT-KV,
3
- and of course to remove or disable them when the error is gone.
4
-
5
- Errors are implemented by storing relevant information at ``(*PREFIX,node,tock)``.
6
- The prefix defaults to ``("error",)``; subsystems may want to create their
7
- own list.
8
-
9
- Each error is stored as a record with these arguments:
10
-
11
- * path
12
-
13
- The path to the element that caused the error.
14
-
15
- * subsystem
16
-
17
- A subsystem.
18
-
19
- * severity
20
-
21
- 0…7 corresponding to fatal/error/warning/info/note/debug/trace.
22
-
23
- * resolved
24
-
25
- If set, contains the ``tock`` value when the problem was fixed.
26
-
27
- * created
28
-
29
- The timestamp (unixtime) when the problem first occurred.
30
-
31
- * count
32
-
33
- The number of occurrences.
34
-
35
- * first_seen
36
-
37
- The timestamp (unixtime) when the problem first occurred. Must not be
38
- modified; used for strict unique-ification.
39
-
40
- * last_seen
41
-
42
- The timestamp (unixtime) when the problem last occurred.
43
-
44
- (path,subsystem) must be unique.
45
-
46
-
47
- Single error trace records may be stored below the error message. Their
48
- name should be the node which noticed the problem, they have the following
49
- structure:
50
-
51
- * seen
52
-
53
- Timestamp
54
-
55
- * tock
56
-
57
- Tock value when the problem occurred
58
-
59
- * trace
60
-
61
- A multi-line textual error message
62
-
63
- * comment
64
-
65
- The ``repr`` of the error, or an explicit commenting text.
66
-
67
- * message
68
-
69
- Some textual explanation of the error.
70
-
71
- * data
72
-
73
- Any additional data required to reproduce the problem; e.g. if a
74
- stored procedure triggered an exception, the location of the actual code
75
- and the parameters used when invoking it.
76
-
77
- The message text must be fixed. It must be format-able using the "data"
78
- of this record.
79
-
80
- """
81
-
82
- from __future__ import annotations
83
-
84
- import logging
85
- import traceback
86
- from collections import defaultdict
87
- from time import time # wall clock, intentionally
88
- from weakref import WeakValueDictionary
89
-
90
- import anyio
91
- from moat.util import Cache, NotGiven, Path
92
-
93
- from .codec import packer
94
- from .exceptions import ServerError
95
- from .obj import AttrClientEntry, ClientEntry, ClientRoot
96
-
97
- logger = logging.getLogger(__name__)
98
-
99
-
100
- try:
101
- ClosedResourceError = anyio.exceptions.ClosedResourceError
102
- except AttributeError:
103
- ClosedResourceError = anyio.ClosedResourceError
104
-
105
-
106
- class ErrorSubEntry(AttrClientEntry):
107
- """
108
- Tracks the latest occurrence of an error, per node.
109
- """
110
-
111
- ATTRS = "seen tock trace str data comment message".split()
112
-
113
- def child_type(self, name): # pylint: disable=arguments-differ
114
- logger.warning("Unknown entry type at %r: %s", self._path, name)
115
- return ClientEntry
116
-
117
- def __repr__(self):
118
- return "‹%s %s %d›" % (
119
- self.__class__.__name__,
120
- self._path[-3:],
121
- getattr(self, "tock", -1),
122
- )
123
-
124
- async def set_value(self, value):
125
- await super().set_value(value)
126
- if value is NotGiven:
127
- p = self.parent
128
- if p is not None:
129
- await self.parent.check_move()
130
-
131
-
132
- class ErrorEntry(AttrClientEntry):
133
- """
134
- A specific error. While it's recorded per node+tock, for uniqueness,
135
- the error is really unique per subsystem and path.
136
- """
137
-
138
- ATTRS = "path subsystem severity resolved created count first_seen last_seen message".split()
139
- resolved = None # bool; if None, no details yet
140
- created = None
141
- count = 0
142
- subsystem = None
143
- first_seen = None
144
- last_seen = None
145
- path = ()
146
- _real_entry = None
147
-
148
- def __init__(self, *a, **kw):
149
- super().__init__(*a, **kw)
150
- self.node, self.tock = self.subpath[-2:]
151
-
152
- def __repr__(self):
153
- return "<%s: %s %s: %s>" % (
154
- self.__class__.__name__,
155
- "/".join(str(x) for x in self.subpath),
156
- self.subsystem,
157
- " ".join(str(x) for x in self.path),
158
- )
159
-
160
- @classmethod
161
- def child_type(cls, name):
162
- return ErrorSubEntry
163
-
164
- @property
165
- def real_entry(self):
166
- while self._real_entry is not None:
167
- self = self._real_entry # pylint: disable=self-cls-assignment
168
- return self
169
-
170
- async def check_move(self):
171
- dest = self.real_entry
172
- if dest is not self:
173
- await self.root.add_clean(self)
174
-
175
- async def resolve(self):
176
- """
177
- Record that this error has been resolved.
178
- """
179
- self.resolved = True
180
- await self.save()
181
-
182
- async def add_exc(self, node, exc, data, comment=None, message=None):
183
- """
184
- Store a detail record for this error.
185
-
186
- One per node, so we don't try to avoid collisions.
187
-
188
- Arguments:
189
- node (str): The node the error occurred in.
190
- exc (Exception): The actual exception
191
- data (dict): any relevant data to reproduce the problem.
192
- """
193
- res: ErrorSubEntry = self.get(node)
194
- if res is None:
195
- res = self.allocate(node)
196
- if True: # pylint: disable=using-constant-test # just for the pylint block
197
- # pylint: disable=attribute-defined-outside-init
198
- res.seen = time()
199
- res.tock = await self.root.client.get_tock()
200
- res.comment = comment or repr(exc)
201
-
202
- if exc is not None:
203
- t = traceback.format_exception(type(exc), exc, exc.__traceback__)
204
- if len(t) > 40:
205
- t = t[:10] + ["…\n"] + t[:30]
206
- res.trace = "".join(t) # pylint: disable=attribute-defined-outside-init
207
- if message is not None:
208
- res.message = message # pylint: disable=attribute-defined-outside-init
209
- if data is not None:
210
- res.data = data # pylint: disable=attribute-defined-outside-init
211
-
212
- if message:
213
- if data:
214
- try:
215
- m = message.format(exc=exc, **data)
216
- except Exception as exc: # pylint: disable=unused-argument # OH COME ON
217
- m = message + f" (FORMAT {exc!r})"
218
- else:
219
- m = message
220
- if m:
221
- if exc:
222
- m += f": {exc!r}"
223
- elif exc:
224
- m = repr(exc)
225
- elif comment:
226
- m = comment
227
- elif data:
228
- m = repr(data)
229
- logger.warning("Error %r %s: %s", self._path, node, m)
230
-
231
- try:
232
- r = await res.save()
233
- except TypeError:
234
- for k in res.ATTRS:
235
- v = getattr(res, k, None)
236
- try:
237
- packer(v)
238
- except TypeError:
239
- setattr(res, k, repr(v))
240
- r = await res.save()
241
- return r
242
-
243
- async def add_comment(self, node, comment, data):
244
- """
245
- Store this comment, typically used when something resumes working.
246
- One per node, so we don't need to avoid collisions.
247
- """
248
- res = dict(
249
- seen=time(),
250
- tock=await self.root.client.get_tock(),
251
- comment=comment,
252
- data=data,
253
- )
254
- logger.info("Comment %s: %s", node, comment)
255
- await self.root.client.set(self._path, chain=self.chain, value=res)
256
-
257
- async def delete(self): # pylint: disable=signature-differs,arguments-differ
258
- """
259
- Delete myself from storage.
260
-
261
- This doesn't do anything locally, the watcher will get it.
262
- """
263
- await self.root._pop(self)
264
- try:
265
- return await super().delete(chain=False)
266
- except ServerError as exc:
267
- if "is new" not in repr(exc):
268
- raise
269
-
270
- async def move_to_real(self):
271
- """
272
- Move this entry, or rather the errors in it, to another.
273
-
274
- This is used for collision resolution.
275
- """
276
-
277
- dest = self.real_entry
278
- # logger.warning("DEL 1 %r %r",self,dest)
279
- assert dest is not self, self
280
- kid = self.get(self.root.name)
281
- if kid is not None:
282
- dkid = dest.get(self.root.name)
283
- # logger.warning("DEL 2 %r %r %r %r",self,dest,kid,dkid)
284
- val = kid.get_value()
285
- if dkid is None:
286
- dkid = dest.allocate(self.root.name)
287
- await dkid.set_value(val)
288
- await dkid.save()
289
- elif getattr(dkid, "tock", 0) < getattr(kid, "tock", 0):
290
- await dkid.set_value(val)
291
- await dkid.save()
292
- # logger.warning("DEL 3 %r %r %r",self,dest,dkid)
293
- await kid.delete()
294
- if not len(self):
295
- # logger.warning("DEL 4 %r",self)
296
- await self.delete()
297
- # logger.warning("DEL 5 %r",self)
298
-
299
- async def set_value(self, value):
300
- """Overridden: set_value
301
-
302
- Stores a pointer to the error in the root and keeps the records unique
303
- """
304
-
305
- await self.root._pop(self)
306
- if value is NotGiven:
307
- if self.value is NotGiven:
308
- return
309
- keep = await self.root.get_error_record(self.subsystem, self.path, create=False)
310
- if keep is not None:
311
- self._real_entry = keep.real_entry
312
- await self.move_to_real()
313
- await super().set_value(value)
314
- return
315
-
316
- await super().set_value(value)
317
-
318
- drop, keep = await self.root._unique(self)
319
- # logger.debug("UNIQ %r %r %r %s/%s",self,drop,keep,
320
- # "x" if drop is None else drop.subpath[0],self.root.name)
321
- if drop is not None:
322
- # self.root._dropped(drop)
323
- drop._real_entry = keep
324
-
325
- if drop.subpath[0] == self.root.name:
326
- await drop.move_to_real()
327
- # TODO remember to do it anyway, after next tick and when we're it
328
- self.root._push(self.real_entry)
329
-
330
-
331
- class ErrorStep(ClientEntry):
332
- """
333
- Errors are stored at /tock/node; this represents the /tock part
334
- """
335
-
336
- @classmethod
337
- def child_type(cls, name):
338
- return ErrorEntry
339
-
340
-
341
- def _defaultdict_init(*a, **k):
342
- return defaultdict(_defaultdict_init, *a, **k)
343
-
344
-
345
- class ErrorRoot(ClientRoot):
346
- """
347
- This class represents the root of an error handling hierarchy. Ideally
348
- there should only be one, but you can use more if necessary.
349
-
350
- You typically don't create this class directly; instead, call
351
- :meth:`ClientRoot.as_handler`::
352
-
353
- errs = await ErrorRoot.as_handler(client, your_config.get("error-handler",{})
354
-
355
- Configuration:
356
-
357
- Arguments:
358
- prefix (list): Where to store the error data in MoaT-KV.
359
- The default is ``('.moat','kv','error')``.
360
- """
361
-
362
- CFG = "errors"
363
-
364
- def __init__(self, *a, name=None, **kw):
365
- super().__init__(*a, **kw)
366
- self.name = name or self.client.client_name
367
- self._loaded = anyio.Event()
368
- self._errors = defaultdict(dict) # node > tock > Entry
369
- self._active = defaultdict(dict) # subsystem > path > Entry
370
- self._done = defaultdict(WeakValueDictionary) # subsystem > path > Entry
371
- self._latest = Cache(100)
372
- self._to_clean = set()
373
-
374
- @classmethod
375
- def child_type(cls, name):
376
- return ErrorStep
377
-
378
- async def add_clean(self, entry):
379
- # self._to_clean.add(entry)
380
- pass
381
- # TODO run cleanup code that consolidates these errors when we get a TagMessage
382
- # TODO use a weakset
383
-
384
- def all_errors(self, subsystem=None):
385
- """
386
- Iterate over all active errors, either a single subsystem or all of
387
- them.
388
-
389
- Arguments:
390
- subsystem (str): The subsystem to filter for.
391
- """
392
-
393
- if subsystem is None:
394
- for s in list(self._active.values()):
395
- yield from iter(s.values())
396
- else:
397
- yield from iter(self._active[subsystem].values())
398
-
399
- async def get_error_record(self, subsystem, path, *, create=True):
400
- """Retrieve or generate an error record for a particular subsystem
401
- and path.
402
-
403
- If ``create`` is set, the record may be incomplete and
404
- must be filled and stored by the caller.
405
- """
406
-
407
- err = self._active[subsystem].get(path, None)
408
- if err is not None:
409
- return err
410
- err = self._done[subsystem].get(path, None)
411
- if err is not None:
412
- return err
413
- if not create:
414
- return None
415
- tock = await self.client.get_tock()
416
- return self.follow(Path(self.name, tock), create=True)
417
-
418
- async def _unique(self, entry):
419
- """
420
- Test whether this record is unique.
421
-
422
- Returns:
423
- ``None,None`` if there is no problem
424
- Otherwise, a tuple:
425
- - the record that should be deleted
426
- - the record that should be kept
427
-
428
- This is used for collision resolution and **must** be stable, i.e.
429
- not depend on which node it is running on or which entry arrives
430
- first.
431
- """
432
- other = await self.get_error_record(entry.subsystem, entry.path, create=False)
433
- if other is None or other is entry:
434
- return None, None
435
-
436
- def _n(x):
437
- return 99999999999999 if x is None else x
438
-
439
- if _n(entry.first_seen) < _n(other.first_seen):
440
- return other, entry
441
- elif _n(other.first_seen) < _n(entry.first_seen):
442
- return entry, other
443
-
444
- if entry.node < other.node:
445
- return other, entry
446
- elif other.node < entry.node:
447
- return entry, other
448
-
449
- raise RuntimeError(f"This cannot happen: {entry.node} {entry.tock}")
450
-
451
- async def record_working( # pylint: disable=dangerous-default-value
452
- self,
453
- subsystem,
454
- path,
455
- *,
456
- comment=None,
457
- data={},
458
- force=False,
459
- ):
460
- """This exception has been fixed.
461
-
462
- Arguments:
463
- subsystem (str): The subsystem with the error.
464
- *path: the path to the no-longer-offending entry.
465
- comment (str): text to enter
466
- data (dict): any relevant data
467
- force (bool): create an entry even if no error is open.
468
- """
469
- rec = await self.get_error_record(subsystem, path, create=force)
470
- if rec is None:
471
- return
472
- if not rec.resolved:
473
- rec.resolved = time()
474
- await rec.save()
475
- if comment or data:
476
- await rec.real_entry.add_comment(self.name, comment, data)
477
- return rec
478
-
479
- async def record_error( # pylint: disable=dangerous-default-value
480
- self,
481
- subsystem,
482
- path,
483
- *,
484
- exc=None,
485
- data={},
486
- severity=0,
487
- message=None,
488
- force: bool = False,
489
- comment: str = None,
490
- ):
491
- """An exception has occurred for this subtype and path.
492
-
493
- Arguments:
494
- subsystem (str): The subsystem with the error.
495
- *path: the path to the no-longer-offending entry.
496
- exc (Exception): The exception in question.
497
- data (dict): any relevant data
498
- severity (int): error gravity.
499
- force (bool): Flag whether a low-priority exception should
500
- override a high-prio one.
501
- message (str): some text to add to the error. It is formatted
502
- with the data when printed.
503
- """
504
- rec = await self.get_error_record(subsystem, path)
505
- if not force and hasattr(rec, "severity") and rec.severity < severity:
506
- return rec
507
-
508
- rec.severity = severity
509
- rec.subsystem = subsystem
510
- rec.path = path
511
- rec.resolved = False
512
- rec.count += 1
513
- rec.last_seen = time()
514
- if not hasattr(rec, "first_seen"):
515
- rec.first_seen = rec.last_seen
516
-
517
- try:
518
- await rec.save()
519
- except ClosedResourceError:
520
- logger.exception(
521
- "Could not save error %s %s: %s %r",
522
- subsystem,
523
- path,
524
- message,
525
- exc,
526
- exc_info=exc,
527
- )
528
- return # owch, but can't be helped
529
-
530
- r = await rec.real_entry.add_exc(
531
- self.name,
532
- exc=exc,
533
- data=data,
534
- comment=comment,
535
- message=message,
536
- )
537
- return r
538
-
539
- async def _pop(self, entry):
540
- """Override to deal with entry changes"""
541
- if entry.subsystem is None or entry.path is None:
542
- return
543
- rec = await self.get_error_record(entry.subsystem, entry.path, create=False)
544
- if rec is not entry:
545
- return
546
-
547
- try:
548
- del (self._done if entry.resolved else self._active)[entry.subsystem][entry.path]
549
- except KeyError:
550
- pass
551
-
552
- def _push(self, entry):
553
- if entry.subsystem is None or entry.path is None:
554
- return
555
-
556
- if entry.resolved:
557
- dest = self._done
558
- self._latest.keep(entry)
559
- else:
560
- dest = self._active
561
- dest[entry.subsystem][entry.path] = entry
@@ -1,126 +0,0 @@
1
- """
2
- This module affords all MoaT-KV exceptions.
3
- """
4
-
5
- # pylint: disable=unnecessary-pass
6
- from __future__ import annotations
7
-
8
- error_types = {}
9
-
10
-
11
- def _typed(cls):
12
- error_types[cls.etype] = cls
13
- return cls
14
-
15
-
16
- class MoaTKVError(RuntimeError):
17
- """Superclass of all MoaT-KV errors.
18
-
19
- Abstract class.
20
- """
21
-
22
- pass
23
-
24
-
25
- class ServerError(MoaTKVError):
26
- """Generic server error.
27
-
28
- This class includes errors forwarded to the client.
29
- """
30
-
31
- pass
32
-
33
-
34
- class ClientError(MoaTKVError):
35
- """Generic client error.
36
-
37
- Abstract class.
38
- """
39
-
40
- etype: str = None
41
-
42
-
43
- @_typed
44
- class ClientChainError(ClientError):
45
- """The chain you passed in didn't match the entry"""
46
-
47
- etype = "chain"
48
-
49
-
50
- @_typed
51
- class ClientConnectionError(ClientError):
52
- """Some connection error"""
53
-
54
- etype = "conn"
55
-
56
-
57
- class ServerClosedError(ServerError):
58
- """The server closed our connection."""
59
-
60
- pass
61
-
62
-
63
- class ServerConnectionError(ServerError):
64
- """Some connection error"""
65
-
66
- pass
67
-
68
-
69
- class ACLError(ServerError):
70
- """An ACL did not match"""
71
-
72
- pass
73
-
74
-
75
- class CancelledError(ClientError):
76
- """A client call was cancelled."""
77
-
78
- pass
79
-
80
-
81
- class ClientAuthError(ClientError):
82
- """Authorization failed.
83
-
84
- Abstract class.
85
- """
86
-
87
- pass
88
-
89
-
90
- class ClientAuthRequiredError(ClientAuthError):
91
- """Authorization required but missing."""
92
-
93
- pass
94
-
95
-
96
- class ClientAuthMethodError(ClientAuthError):
97
- """Wrong authorization method provided."""
98
-
99
- pass
100
-
101
-
102
- class MoaTKVauthError(ClientError):
103
- """Auth error.
104
-
105
- Abstract class.
106
- """
107
-
108
- pass
109
-
110
-
111
- class NoAuthError(MoaTKVauthError):
112
- """Server-side error: auth required"""
113
-
114
- pass
115
-
116
-
117
- class NoAuthModuleError(MoaTKVauthError):
118
- """Server-side error: auth module doesn't exist"""
119
-
120
- pass
121
-
122
-
123
- class AuthFailedError(MoaTKVauthError):
124
- """Server-side error: auth failed"""
125
-
126
- pass