haiway 0.17.0__py3-none-any.whl → 0.18.1__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.
- haiway/__init__.py +24 -18
- haiway/context/__init__.py +23 -13
- haiway/context/access.py +127 -91
- haiway/context/disposables.py +2 -2
- haiway/context/identifier.py +4 -5
- haiway/context/observability.py +526 -0
- haiway/context/state.py +2 -2
- haiway/context/tasks.py +1 -3
- haiway/context/types.py +2 -2
- haiway/helpers/__init__.py +5 -7
- haiway/helpers/asynchrony.py +2 -2
- haiway/helpers/caching.py +2 -2
- haiway/helpers/observability.py +244 -0
- haiway/helpers/retries.py +1 -3
- haiway/helpers/throttling.py +1 -3
- haiway/helpers/timeouted.py +1 -3
- haiway/helpers/tracing.py +21 -35
- haiway/opentelemetry/__init__.py +3 -0
- haiway/opentelemetry/observability.py +452 -0
- haiway/state/__init__.py +2 -2
- haiway/state/attributes.py +2 -2
- haiway/state/path.py +1 -3
- haiway/state/requirement.py +1 -3
- haiway/state/structure.py +161 -30
- haiway/state/validation.py +2 -2
- haiway/types/__init__.py +2 -2
- haiway/types/default.py +2 -2
- haiway/types/frozen.py +1 -3
- haiway/types/missing.py +2 -2
- haiway/utils/__init__.py +2 -2
- haiway/utils/always.py +2 -2
- haiway/utils/collections.py +2 -2
- haiway/utils/env.py +2 -2
- haiway/utils/freezing.py +1 -3
- haiway/utils/logs.py +1 -3
- haiway/utils/mimic.py +1 -3
- haiway/utils/noop.py +2 -2
- haiway/utils/queue.py +1 -3
- haiway/utils/stream.py +1 -3
- {haiway-0.17.0.dist-info → haiway-0.18.1.dist-info}/METADATA +9 -5
- haiway-0.18.1.dist-info/RECORD +44 -0
- haiway/context/logging.py +0 -242
- haiway/context/metrics.py +0 -176
- haiway/helpers/metrics.py +0 -465
- haiway-0.17.0.dist-info/RECORD +0 -43
- {haiway-0.17.0.dist-info → haiway-0.18.1.dist-info}/WHEEL +0 -0
- {haiway-0.17.0.dist-info → haiway-0.18.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,526 @@
|
|
1
|
+
from collections.abc import Sequence
|
2
|
+
from contextvars import ContextVar, Token
|
3
|
+
from enum import IntEnum
|
4
|
+
from logging import DEBUG as DEBUG_LOGGING
|
5
|
+
from logging import ERROR as ERROR_LOGGING
|
6
|
+
from logging import INFO as INFO_LOGGING
|
7
|
+
from logging import WARNING as WARNING_LOGGING
|
8
|
+
from logging import Logger, getLogger
|
9
|
+
from types import TracebackType
|
10
|
+
from typing import Any, Final, Protocol, Self, final, runtime_checkable
|
11
|
+
|
12
|
+
from haiway.context.identifier import ScopeIdentifier
|
13
|
+
|
14
|
+
# from haiway.context.logging import LoggerContext
|
15
|
+
from haiway.state import State
|
16
|
+
|
17
|
+
__all__ = (
|
18
|
+
"DEBUG",
|
19
|
+
"ERROR",
|
20
|
+
"INFO",
|
21
|
+
"WARNING",
|
22
|
+
"Observability",
|
23
|
+
"ObservabilityAttribute",
|
24
|
+
"ObservabilityAttributesRecording",
|
25
|
+
"ObservabilityContext",
|
26
|
+
"ObservabilityEventRecording",
|
27
|
+
"ObservabilityLevel",
|
28
|
+
"ObservabilityLogRecording",
|
29
|
+
"ObservabilityMetricRecording",
|
30
|
+
"ObservabilityScopeEntering",
|
31
|
+
"ObservabilityScopeExiting",
|
32
|
+
)
|
33
|
+
|
34
|
+
|
35
|
+
class ObservabilityLevel(IntEnum):
|
36
|
+
# values from logging package
|
37
|
+
ERROR = ERROR_LOGGING
|
38
|
+
WARNING = WARNING_LOGGING
|
39
|
+
INFO = INFO_LOGGING
|
40
|
+
DEBUG = DEBUG_LOGGING
|
41
|
+
|
42
|
+
|
43
|
+
ERROR: Final[int] = ObservabilityLevel.ERROR
|
44
|
+
WARNING: Final[int] = ObservabilityLevel.WARNING
|
45
|
+
INFO: Final[int] = ObservabilityLevel.INFO
|
46
|
+
DEBUG: Final[int] = ObservabilityLevel.DEBUG
|
47
|
+
|
48
|
+
type ObservabilityAttribute = (
|
49
|
+
Sequence[str] | Sequence[float] | Sequence[int] | Sequence[bool] | str | float | int | bool
|
50
|
+
)
|
51
|
+
|
52
|
+
|
53
|
+
@runtime_checkable
|
54
|
+
class ObservabilityLogRecording(Protocol):
|
55
|
+
def __call__(
|
56
|
+
self,
|
57
|
+
scope: ScopeIdentifier,
|
58
|
+
/,
|
59
|
+
level: ObservabilityLevel,
|
60
|
+
message: str,
|
61
|
+
*args: Any,
|
62
|
+
exception: BaseException | None,
|
63
|
+
**extra: Any,
|
64
|
+
) -> None: ...
|
65
|
+
|
66
|
+
|
67
|
+
@runtime_checkable
|
68
|
+
class ObservabilityEventRecording(Protocol):
|
69
|
+
def __call__(
|
70
|
+
self,
|
71
|
+
scope: ScopeIdentifier,
|
72
|
+
/,
|
73
|
+
*,
|
74
|
+
level: ObservabilityLevel,
|
75
|
+
event: State,
|
76
|
+
**extra: Any,
|
77
|
+
) -> None: ...
|
78
|
+
|
79
|
+
|
80
|
+
@runtime_checkable
|
81
|
+
class ObservabilityMetricRecording(Protocol):
|
82
|
+
def __call__(
|
83
|
+
self,
|
84
|
+
scope: ScopeIdentifier,
|
85
|
+
/,
|
86
|
+
*,
|
87
|
+
metric: str,
|
88
|
+
value: float | int,
|
89
|
+
unit: str | None,
|
90
|
+
**extra: Any,
|
91
|
+
) -> None: ...
|
92
|
+
|
93
|
+
|
94
|
+
@runtime_checkable
|
95
|
+
class ObservabilityAttributesRecording(Protocol):
|
96
|
+
def __call__(
|
97
|
+
self,
|
98
|
+
scope: ScopeIdentifier,
|
99
|
+
/,
|
100
|
+
**attributes: ObservabilityAttribute,
|
101
|
+
) -> None: ...
|
102
|
+
|
103
|
+
|
104
|
+
@runtime_checkable
|
105
|
+
class ObservabilityScopeEntering(Protocol):
|
106
|
+
def __call__[Metric: State](
|
107
|
+
self,
|
108
|
+
scope: ScopeIdentifier,
|
109
|
+
/,
|
110
|
+
) -> None: ...
|
111
|
+
|
112
|
+
|
113
|
+
@runtime_checkable
|
114
|
+
class ObservabilityScopeExiting(Protocol):
|
115
|
+
def __call__[Metric: State](
|
116
|
+
self,
|
117
|
+
scope: ScopeIdentifier,
|
118
|
+
/,
|
119
|
+
*,
|
120
|
+
exception: BaseException | None,
|
121
|
+
) -> None: ...
|
122
|
+
|
123
|
+
|
124
|
+
class Observability: # avoiding State inheritance to prevent propagation as scope state
|
125
|
+
__slots__ = (
|
126
|
+
"attributes_recording",
|
127
|
+
"event_recording",
|
128
|
+
"log_recording",
|
129
|
+
"metric_recording",
|
130
|
+
"scope_entering",
|
131
|
+
"scope_exiting",
|
132
|
+
)
|
133
|
+
|
134
|
+
def __init__(
|
135
|
+
self,
|
136
|
+
log_recording: ObservabilityLogRecording,
|
137
|
+
metric_recording: ObservabilityMetricRecording,
|
138
|
+
event_recording: ObservabilityEventRecording,
|
139
|
+
attributes_recording: ObservabilityAttributesRecording,
|
140
|
+
scope_entering: ObservabilityScopeEntering,
|
141
|
+
scope_exiting: ObservabilityScopeExiting,
|
142
|
+
) -> None:
|
143
|
+
self.log_recording: ObservabilityLogRecording
|
144
|
+
object.__setattr__(
|
145
|
+
self,
|
146
|
+
"log_recording",
|
147
|
+
log_recording,
|
148
|
+
)
|
149
|
+
self.metric_recording: ObservabilityMetricRecording
|
150
|
+
object.__setattr__(
|
151
|
+
self,
|
152
|
+
"metric_recording",
|
153
|
+
metric_recording,
|
154
|
+
)
|
155
|
+
self.event_recording: ObservabilityEventRecording
|
156
|
+
object.__setattr__(
|
157
|
+
self,
|
158
|
+
"event_recording",
|
159
|
+
event_recording,
|
160
|
+
)
|
161
|
+
self.attributes_recording: ObservabilityAttributesRecording
|
162
|
+
object.__setattr__(
|
163
|
+
self,
|
164
|
+
"attributes_recording",
|
165
|
+
attributes_recording,
|
166
|
+
)
|
167
|
+
|
168
|
+
self.scope_entering: ObservabilityScopeEntering
|
169
|
+
object.__setattr__(
|
170
|
+
self,
|
171
|
+
"scope_entering",
|
172
|
+
scope_entering,
|
173
|
+
)
|
174
|
+
self.scope_exiting: ObservabilityScopeExiting
|
175
|
+
object.__setattr__(
|
176
|
+
self,
|
177
|
+
"scope_exiting",
|
178
|
+
scope_exiting,
|
179
|
+
)
|
180
|
+
|
181
|
+
def __setattr__(
|
182
|
+
self,
|
183
|
+
name: str,
|
184
|
+
value: Any,
|
185
|
+
) -> Any:
|
186
|
+
raise AttributeError(
|
187
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
188
|
+
f" attribute - '{name}' cannot be modified"
|
189
|
+
)
|
190
|
+
|
191
|
+
def __delattr__(
|
192
|
+
self,
|
193
|
+
name: str,
|
194
|
+
) -> None:
|
195
|
+
raise AttributeError(
|
196
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
197
|
+
f" attribute - '{name}' cannot be deleted"
|
198
|
+
)
|
199
|
+
|
200
|
+
|
201
|
+
def _logger_observability(
|
202
|
+
logger: Logger,
|
203
|
+
/,
|
204
|
+
) -> Observability:
|
205
|
+
def log_recording(
|
206
|
+
scope: ScopeIdentifier,
|
207
|
+
/,
|
208
|
+
level: ObservabilityLevel,
|
209
|
+
message: str,
|
210
|
+
*args: Any,
|
211
|
+
exception: BaseException | None,
|
212
|
+
**extra: Any,
|
213
|
+
) -> None:
|
214
|
+
logger.log(
|
215
|
+
level,
|
216
|
+
f"{scope.unique_name} {message}",
|
217
|
+
*args,
|
218
|
+
exc_info=exception,
|
219
|
+
)
|
220
|
+
|
221
|
+
def event_recording(
|
222
|
+
scope: ScopeIdentifier,
|
223
|
+
/,
|
224
|
+
*,
|
225
|
+
level: ObservabilityLevel,
|
226
|
+
event: State,
|
227
|
+
**extra: Any,
|
228
|
+
) -> None:
|
229
|
+
logger.log(
|
230
|
+
level,
|
231
|
+
f"{scope.unique_name} Recorded event:\n{event.to_str(pretty=True)}",
|
232
|
+
)
|
233
|
+
|
234
|
+
def metric_recording(
|
235
|
+
scope: ScopeIdentifier,
|
236
|
+
/,
|
237
|
+
*,
|
238
|
+
metric: str,
|
239
|
+
value: float | int,
|
240
|
+
unit: str | None,
|
241
|
+
**extra: Any,
|
242
|
+
) -> None:
|
243
|
+
logger.log(
|
244
|
+
INFO,
|
245
|
+
f"{scope.unique_name} Recorded metric: {metric}={value}{unit or ''}",
|
246
|
+
)
|
247
|
+
|
248
|
+
def attributes_recording(
|
249
|
+
scope: ScopeIdentifier,
|
250
|
+
/,
|
251
|
+
**attributes: ObservabilityAttribute,
|
252
|
+
) -> None:
|
253
|
+
if not attributes:
|
254
|
+
return
|
255
|
+
|
256
|
+
logger.log(
|
257
|
+
INFO,
|
258
|
+
f"{scope.unique_name} Recorded attributes:"
|
259
|
+
f"\n{'\n'.join([f'{k}: {v}' for k, v in attributes.items()])}",
|
260
|
+
)
|
261
|
+
|
262
|
+
def scope_entering[Metric: State](
|
263
|
+
scope: ScopeIdentifier,
|
264
|
+
/,
|
265
|
+
) -> None:
|
266
|
+
logger.log(
|
267
|
+
DEBUG,
|
268
|
+
f"{scope.unique_name} Entering scope: {scope.label}",
|
269
|
+
)
|
270
|
+
|
271
|
+
def scope_exiting[Metric: State](
|
272
|
+
scope: ScopeIdentifier,
|
273
|
+
/,
|
274
|
+
*,
|
275
|
+
exception: BaseException | None,
|
276
|
+
) -> None:
|
277
|
+
logger.log(
|
278
|
+
DEBUG,
|
279
|
+
f"{scope.unique_name} Exiting scope: {scope.label}",
|
280
|
+
exc_info=exception,
|
281
|
+
)
|
282
|
+
|
283
|
+
return Observability(
|
284
|
+
log_recording=log_recording,
|
285
|
+
event_recording=event_recording,
|
286
|
+
metric_recording=metric_recording,
|
287
|
+
attributes_recording=attributes_recording,
|
288
|
+
scope_entering=scope_entering,
|
289
|
+
scope_exiting=scope_exiting,
|
290
|
+
)
|
291
|
+
|
292
|
+
|
293
|
+
@final
|
294
|
+
class ObservabilityContext:
|
295
|
+
_context = ContextVar[Self]("ObservabilityContext")
|
296
|
+
|
297
|
+
@classmethod
|
298
|
+
def scope(
|
299
|
+
cls,
|
300
|
+
scope: ScopeIdentifier,
|
301
|
+
/,
|
302
|
+
*,
|
303
|
+
observability: Observability | Logger | None,
|
304
|
+
) -> Self:
|
305
|
+
current: Self
|
306
|
+
try: # check for current scope
|
307
|
+
current = cls._context.get()
|
308
|
+
|
309
|
+
except LookupError:
|
310
|
+
resolved_observability: Observability
|
311
|
+
match observability:
|
312
|
+
case Observability() as observability:
|
313
|
+
resolved_observability = observability
|
314
|
+
|
315
|
+
case None:
|
316
|
+
resolved_observability = _logger_observability(getLogger(scope.label))
|
317
|
+
|
318
|
+
case Logger() as logger:
|
319
|
+
resolved_observability = _logger_observability(logger)
|
320
|
+
|
321
|
+
# create root scope when missing
|
322
|
+
return cls(
|
323
|
+
scope=scope,
|
324
|
+
observability=resolved_observability,
|
325
|
+
)
|
326
|
+
|
327
|
+
# create nested scope otherwise
|
328
|
+
resolved_observability: Observability
|
329
|
+
match observability:
|
330
|
+
case None:
|
331
|
+
resolved_observability = current.observability
|
332
|
+
|
333
|
+
case Logger() as logger:
|
334
|
+
resolved_observability = _logger_observability(logger)
|
335
|
+
|
336
|
+
case observability:
|
337
|
+
resolved_observability = observability
|
338
|
+
|
339
|
+
return cls(
|
340
|
+
scope=scope,
|
341
|
+
observability=resolved_observability,
|
342
|
+
)
|
343
|
+
|
344
|
+
@classmethod
|
345
|
+
def record_log(
|
346
|
+
cls,
|
347
|
+
level: ObservabilityLevel,
|
348
|
+
message: str,
|
349
|
+
/,
|
350
|
+
*args: Any,
|
351
|
+
exception: BaseException | None,
|
352
|
+
**extra: Any,
|
353
|
+
) -> None:
|
354
|
+
try: # catch exceptions - we don't wan't to blow up on observability
|
355
|
+
context: Self = cls._context.get()
|
356
|
+
|
357
|
+
if context.observability is not None:
|
358
|
+
context.observability.log_recording(
|
359
|
+
context._scope,
|
360
|
+
level,
|
361
|
+
message,
|
362
|
+
*args,
|
363
|
+
exception=exception,
|
364
|
+
**extra,
|
365
|
+
)
|
366
|
+
|
367
|
+
except LookupError:
|
368
|
+
getLogger().log(
|
369
|
+
level,
|
370
|
+
message,
|
371
|
+
*args,
|
372
|
+
exc_info=exception,
|
373
|
+
)
|
374
|
+
|
375
|
+
@classmethod
|
376
|
+
def record_event(
|
377
|
+
cls,
|
378
|
+
event: State,
|
379
|
+
/,
|
380
|
+
*,
|
381
|
+
level: ObservabilityLevel,
|
382
|
+
**extra: Any,
|
383
|
+
) -> None:
|
384
|
+
try: # catch exceptions - we don't wan't to blow up on observability
|
385
|
+
context: Self = cls._context.get()
|
386
|
+
|
387
|
+
if context.observability is not None:
|
388
|
+
context.observability.event_recording(
|
389
|
+
context._scope,
|
390
|
+
level=level,
|
391
|
+
event=event,
|
392
|
+
**extra,
|
393
|
+
)
|
394
|
+
|
395
|
+
except Exception as exc:
|
396
|
+
cls.record_log(
|
397
|
+
ERROR,
|
398
|
+
f"Failed to record event: {type(event).__qualname__}",
|
399
|
+
exception=exc,
|
400
|
+
)
|
401
|
+
|
402
|
+
@classmethod
|
403
|
+
def record_metric(
|
404
|
+
cls,
|
405
|
+
metric: str,
|
406
|
+
/,
|
407
|
+
*,
|
408
|
+
value: float | int,
|
409
|
+
unit: str | None,
|
410
|
+
**extra: Any,
|
411
|
+
) -> None:
|
412
|
+
try: # catch exceptions - we don't wan't to blow up on observability
|
413
|
+
context: Self = cls._context.get()
|
414
|
+
|
415
|
+
if context.observability is not None:
|
416
|
+
context.observability.metric_recording(
|
417
|
+
context._scope,
|
418
|
+
metric=metric,
|
419
|
+
value=value,
|
420
|
+
unit=unit,
|
421
|
+
**extra,
|
422
|
+
)
|
423
|
+
|
424
|
+
except Exception as exc:
|
425
|
+
cls.record_log(
|
426
|
+
ERROR,
|
427
|
+
f"Failed to record metric: {metric}",
|
428
|
+
exception=exc,
|
429
|
+
)
|
430
|
+
|
431
|
+
@classmethod
|
432
|
+
def record_attributes(
|
433
|
+
cls,
|
434
|
+
**attributes: ObservabilityAttribute,
|
435
|
+
) -> None:
|
436
|
+
try: # catch exceptions - we don't wan't to blow up on observability
|
437
|
+
context: Self = cls._context.get()
|
438
|
+
|
439
|
+
if context.observability is not None:
|
440
|
+
context.observability.attributes_recording(
|
441
|
+
context._scope,
|
442
|
+
**attributes,
|
443
|
+
)
|
444
|
+
|
445
|
+
except Exception as exc:
|
446
|
+
cls.record_log(
|
447
|
+
ERROR,
|
448
|
+
f"Failed to record attributes: {attributes}",
|
449
|
+
exception=exc,
|
450
|
+
)
|
451
|
+
|
452
|
+
__slots__ = (
|
453
|
+
"_scope",
|
454
|
+
"_token",
|
455
|
+
"observability",
|
456
|
+
)
|
457
|
+
|
458
|
+
def __init__(
|
459
|
+
self,
|
460
|
+
scope: ScopeIdentifier,
|
461
|
+
observability: Observability | None,
|
462
|
+
) -> None:
|
463
|
+
self._scope: ScopeIdentifier
|
464
|
+
object.__setattr__(
|
465
|
+
self,
|
466
|
+
"_scope",
|
467
|
+
scope,
|
468
|
+
)
|
469
|
+
self.observability: Observability
|
470
|
+
object.__setattr__(
|
471
|
+
self,
|
472
|
+
"observability",
|
473
|
+
observability,
|
474
|
+
)
|
475
|
+
self._token: Token[ObservabilityContext] | None
|
476
|
+
object.__setattr__(
|
477
|
+
self,
|
478
|
+
"_token",
|
479
|
+
None,
|
480
|
+
)
|
481
|
+
|
482
|
+
def __setattr__(
|
483
|
+
self,
|
484
|
+
name: str,
|
485
|
+
value: Any,
|
486
|
+
) -> Any:
|
487
|
+
raise AttributeError(
|
488
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
489
|
+
f" attribute - '{name}' cannot be modified"
|
490
|
+
)
|
491
|
+
|
492
|
+
def __delattr__(
|
493
|
+
self,
|
494
|
+
name: str,
|
495
|
+
) -> None:
|
496
|
+
raise AttributeError(
|
497
|
+
f"Can't modify immutable {self.__class__.__qualname__},"
|
498
|
+
f" attribute - '{name}' cannot be deleted"
|
499
|
+
)
|
500
|
+
|
501
|
+
def __enter__(self) -> None:
|
502
|
+
assert self._token is None, "Context reentrance is not allowed" # nosec: B101
|
503
|
+
object.__setattr__(
|
504
|
+
self,
|
505
|
+
"_token",
|
506
|
+
ObservabilityContext._context.set(self),
|
507
|
+
)
|
508
|
+
self.observability.scope_entering(self._scope)
|
509
|
+
|
510
|
+
def __exit__(
|
511
|
+
self,
|
512
|
+
exc_type: type[BaseException] | None,
|
513
|
+
exc_val: BaseException | None,
|
514
|
+
exc_tb: TracebackType | None,
|
515
|
+
) -> None:
|
516
|
+
assert self._token is not None, "Unbalanced context enter/exit" # nosec: B101
|
517
|
+
ObservabilityContext._context.reset(self._token)
|
518
|
+
object.__setattr__(
|
519
|
+
self,
|
520
|
+
"_token",
|
521
|
+
None,
|
522
|
+
)
|
523
|
+
self.observability.scope_exiting(
|
524
|
+
self._scope,
|
525
|
+
exception=exc_val,
|
526
|
+
)
|
haiway/context/state.py
CHANGED
haiway/context/tasks.py
CHANGED
haiway/context/types.py
CHANGED
haiway/helpers/__init__.py
CHANGED
@@ -1,18 +1,16 @@
|
|
1
1
|
from haiway.helpers.asynchrony import asynchronous, wrap_async
|
2
2
|
from haiway.helpers.caching import CacheMakeKey, CacheRead, CacheWrite, cache
|
3
|
-
from haiway.helpers.
|
3
|
+
from haiway.helpers.observability import LoggerObservability
|
4
4
|
from haiway.helpers.retries import retry
|
5
5
|
from haiway.helpers.throttling import throttle
|
6
6
|
from haiway.helpers.timeouted import timeout
|
7
|
-
from haiway.helpers.tracing import
|
7
|
+
from haiway.helpers.tracing import ResultTrace, traced
|
8
8
|
|
9
|
-
__all__ =
|
10
|
-
"ArgumentsTrace",
|
9
|
+
__all__ = (
|
11
10
|
"CacheMakeKey",
|
12
11
|
"CacheRead",
|
13
12
|
"CacheWrite",
|
14
|
-
"
|
15
|
-
"MetricsLogger",
|
13
|
+
"LoggerObservability",
|
16
14
|
"ResultTrace",
|
17
15
|
"asynchronous",
|
18
16
|
"cache",
|
@@ -21,4 +19,4 @@ __all__ = [
|
|
21
19
|
"timeout",
|
22
20
|
"traced",
|
23
21
|
"wrap_async",
|
24
|
-
|
22
|
+
)
|
haiway/helpers/asynchrony.py
CHANGED
haiway/helpers/caching.py
CHANGED
@@ -8,12 +8,12 @@ from typing import Any, NamedTuple, Protocol, cast, overload
|
|
8
8
|
from haiway.context.access import ctx
|
9
9
|
from haiway.utils.mimic import mimic_function
|
10
10
|
|
11
|
-
__all__ =
|
11
|
+
__all__ = (
|
12
12
|
"CacheMakeKey",
|
13
13
|
"CacheRead",
|
14
14
|
"CacheWrite",
|
15
15
|
"cache",
|
16
|
-
|
16
|
+
)
|
17
17
|
|
18
18
|
|
19
19
|
class CacheMakeKey[**Args, Key](Protocol):
|