haiway 0.8.4__tar.gz → 0.9.0__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.
- {haiway-0.8.4/src/haiway.egg-info → haiway-0.9.0}/PKG-INFO +1 -1
- {haiway-0.8.4 → haiway-0.9.0}/pyproject.toml +1 -1
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/__init__.py +4 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/context/__init__.py +2 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/context/access.py +56 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/context/metrics.py +38 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/helpers/__init__.py +2 -1
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/helpers/asynchrony.py +4 -6
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/helpers/metrics.py +141 -18
- {haiway-0.8.4 → haiway-0.9.0/src/haiway.egg-info}/PKG-INFO +1 -1
- haiway-0.9.0/src/haiway.egg-info/top_level.txt +2 -0
- {haiway-0.8.4 → haiway-0.9.0}/tests/test_auto_retry.py +2 -2
- haiway-0.8.4/src/haiway.egg-info/top_level.txt +0 -1
- {haiway-0.8.4 → haiway-0.9.0}/LICENSE +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/README.md +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/setup.cfg +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/context/disposables.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/context/identifier.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/context/logging.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/context/state.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/context/tasks.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/context/types.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/helpers/caching.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/helpers/retries.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/helpers/throttling.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/helpers/timeouted.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/helpers/tracing.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/py.typed +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/state/__init__.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/state/attributes.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/state/path.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/state/requirement.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/state/structure.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/state/validation.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/types/__init__.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/types/frozen.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/types/missing.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/utils/__init__.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/utils/always.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/utils/env.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/utils/immutable.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/utils/logs.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/utils/mappings.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/utils/mimic.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/utils/noop.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/utils/queue.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway/utils/sequences.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway.egg-info/SOURCES.txt +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway.egg-info/dependency_links.txt +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/src/haiway.egg-info/requires.txt +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/tests/test_async_queue.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/tests/test_attribute_path.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/tests/test_cache.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/tests/test_context.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/tests/test_state.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/tests/test_streaming.py +0 -0
- {haiway-0.8.4 → haiway-0.9.0}/tests/test_timeout.py +0 -0
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
5
5
|
[project]
|
6
6
|
name = "haiway"
|
7
7
|
description = "Framework for dependency injection and state management within structured concurrency model."
|
8
|
-
version = "0.
|
8
|
+
version = "0.9.0"
|
9
9
|
readme = "README.md"
|
10
10
|
maintainers = [
|
11
11
|
{ name = "Kacper Kaliński", email = "kacper.kalinski@miquido.com" },
|
@@ -3,6 +3,7 @@ from haiway.context import (
|
|
3
3
|
Disposables,
|
4
4
|
MetricsContext,
|
5
5
|
MetricsHandler,
|
6
|
+
MetricsReading,
|
6
7
|
MetricsRecording,
|
7
8
|
MetricsScopeEntering,
|
8
9
|
MetricsScopeExiting,
|
@@ -13,6 +14,7 @@ from haiway.context import (
|
|
13
14
|
)
|
14
15
|
from haiway.helpers import (
|
15
16
|
ArgumentsTrace,
|
17
|
+
MetricsHolder,
|
16
18
|
MetricsLogger,
|
17
19
|
ResultTrace,
|
18
20
|
asynchronous,
|
@@ -61,7 +63,9 @@ __all__ = [
|
|
61
63
|
"Disposables",
|
62
64
|
"MetricsContext",
|
63
65
|
"MetricsHandler",
|
66
|
+
"MetricsHolder",
|
64
67
|
"MetricsLogger",
|
68
|
+
"MetricsReading",
|
65
69
|
"MetricsRecording",
|
66
70
|
"MetricsScopeEntering",
|
67
71
|
"MetricsScopeExiting",
|
@@ -4,6 +4,7 @@ from haiway.context.identifier import ScopeIdentifier
|
|
4
4
|
from haiway.context.metrics import (
|
5
5
|
MetricsContext,
|
6
6
|
MetricsHandler,
|
7
|
+
MetricsReading,
|
7
8
|
MetricsRecording,
|
8
9
|
MetricsScopeEntering,
|
9
10
|
MetricsScopeExiting,
|
@@ -15,6 +16,7 @@ __all__ = [
|
|
15
16
|
"Disposables",
|
16
17
|
"MetricsContext",
|
17
18
|
"MetricsHandler",
|
19
|
+
"MetricsReading",
|
18
20
|
"MetricsRecording",
|
19
21
|
"MetricsScopeEntering",
|
20
22
|
"MetricsScopeExiting",
|
@@ -423,6 +423,62 @@ class ctx:
|
|
423
423
|
|
424
424
|
MetricsContext.record(metric)
|
425
425
|
|
426
|
+
@overload
|
427
|
+
@staticmethod
|
428
|
+
async def read[Metric: State](
|
429
|
+
metric: type[Metric],
|
430
|
+
/,
|
431
|
+
*,
|
432
|
+
merged: bool = False,
|
433
|
+
) -> Metric | None: ...
|
434
|
+
|
435
|
+
@overload
|
436
|
+
@staticmethod
|
437
|
+
async def read[Metric: State](
|
438
|
+
metric: type[Metric],
|
439
|
+
/,
|
440
|
+
*,
|
441
|
+
merged: bool = False,
|
442
|
+
default: Metric,
|
443
|
+
) -> Metric: ...
|
444
|
+
|
445
|
+
@staticmethod
|
446
|
+
async def read[Metric: State](
|
447
|
+
metric: type[Metric],
|
448
|
+
/,
|
449
|
+
*,
|
450
|
+
merged: bool = False,
|
451
|
+
default: Metric | None = None,
|
452
|
+
) -> Metric | None:
|
453
|
+
"""
|
454
|
+
Read metric within current scope context.
|
455
|
+
|
456
|
+
Parameters
|
457
|
+
----------
|
458
|
+
metric: type[Metric]
|
459
|
+
type of metric to be read from current context.
|
460
|
+
|
461
|
+
merged: bool
|
462
|
+
control wheather to merge metrics from nested scopes (True)\
|
463
|
+
or access only the current scope value (False) without combining them
|
464
|
+
|
465
|
+
default: Metric | None
|
466
|
+
default value to return when metric was not recorded yet.
|
467
|
+
|
468
|
+
Returns
|
469
|
+
-------
|
470
|
+
Metric | None
|
471
|
+
"""
|
472
|
+
|
473
|
+
value: Metric | None = await MetricsContext.read(
|
474
|
+
metric,
|
475
|
+
merged=merged,
|
476
|
+
)
|
477
|
+
if value is None:
|
478
|
+
return default
|
479
|
+
|
480
|
+
return value
|
481
|
+
|
426
482
|
@staticmethod
|
427
483
|
def log_error(
|
428
484
|
message: str,
|
@@ -9,6 +9,7 @@ from haiway.state import State
|
|
9
9
|
__all__ = [
|
10
10
|
"MetricsContext",
|
11
11
|
"MetricsHandler",
|
12
|
+
"MetricsReading",
|
12
13
|
"MetricsRecording",
|
13
14
|
"MetricsScopeEntering",
|
14
15
|
"MetricsScopeExiting",
|
@@ -25,6 +26,18 @@ class MetricsRecording(Protocol):
|
|
25
26
|
) -> None: ...
|
26
27
|
|
27
28
|
|
29
|
+
@runtime_checkable
|
30
|
+
class MetricsReading(Protocol):
|
31
|
+
async def __call__[Metric: State](
|
32
|
+
self,
|
33
|
+
scope: ScopeIdentifier,
|
34
|
+
/,
|
35
|
+
*,
|
36
|
+
metric: type[Metric],
|
37
|
+
merged: bool,
|
38
|
+
) -> Metric | None: ...
|
39
|
+
|
40
|
+
|
28
41
|
@runtime_checkable
|
29
42
|
class MetricsScopeEntering(Protocol):
|
30
43
|
def __call__[Metric: State](
|
@@ -45,6 +58,7 @@ class MetricsScopeExiting(Protocol):
|
|
45
58
|
|
46
59
|
class MetricsHandler(State):
|
47
60
|
record: MetricsRecording
|
61
|
+
read: MetricsReading
|
48
62
|
enter_scope: MetricsScopeEntering
|
49
63
|
exit_scope: MetricsScopeExiting
|
50
64
|
|
@@ -100,6 +114,30 @@ class MetricsContext:
|
|
100
114
|
exception=exc,
|
101
115
|
)
|
102
116
|
|
117
|
+
@classmethod
|
118
|
+
async def read[Metric: State](
|
119
|
+
cls,
|
120
|
+
metric: type[Metric],
|
121
|
+
/,
|
122
|
+
merged: bool,
|
123
|
+
) -> Metric | None:
|
124
|
+
try: # catch exceptions - we don't wan't to blow up on metrics
|
125
|
+
metrics: Self = cls._context.get()
|
126
|
+
|
127
|
+
if metrics._metrics is not None:
|
128
|
+
return await metrics._metrics.read(
|
129
|
+
metrics._scope,
|
130
|
+
metric=metric,
|
131
|
+
merged=merged,
|
132
|
+
)
|
133
|
+
|
134
|
+
except Exception as exc:
|
135
|
+
LoggerContext.log_error(
|
136
|
+
"Failed to read metric: %s",
|
137
|
+
metric.__qualname__,
|
138
|
+
exception=exc,
|
139
|
+
)
|
140
|
+
|
103
141
|
def __init__(
|
104
142
|
self,
|
105
143
|
scope: ScopeIdentifier,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from haiway.helpers.asynchrony import asynchronous, wrap_async
|
2
2
|
from haiway.helpers.caching import cache
|
3
|
-
from haiway.helpers.metrics import MetricsLogger
|
3
|
+
from haiway.helpers.metrics import MetricsHolder, MetricsLogger
|
4
4
|
from haiway.helpers.retries import retry
|
5
5
|
from haiway.helpers.throttling import throttle
|
6
6
|
from haiway.helpers.timeouted import timeout
|
@@ -8,6 +8,7 @@ from haiway.helpers.tracing import ArgumentsTrace, ResultTrace, traced
|
|
8
8
|
|
9
9
|
__all__ = [
|
10
10
|
"ArgumentsTrace",
|
11
|
+
"MetricsHolder",
|
11
12
|
"MetricsLogger",
|
12
13
|
"ResultTrace",
|
13
14
|
"asynchronous",
|
@@ -30,12 +30,10 @@ def wrap_async[**Args, Result](
|
|
30
30
|
|
31
31
|
|
32
32
|
@overload
|
33
|
-
def asynchronous[**Args, Result]() ->
|
34
|
-
Callable[
|
35
|
-
|
36
|
-
|
37
|
-
]
|
38
|
-
): ...
|
33
|
+
def asynchronous[**Args, Result]() -> Callable[
|
34
|
+
[Callable[Args, Result]],
|
35
|
+
Callable[Args, Coroutine[None, None, Result]],
|
36
|
+
]: ...
|
39
37
|
|
40
38
|
|
41
39
|
@overload
|
@@ -1,14 +1,15 @@
|
|
1
1
|
from collections.abc import Sequence
|
2
2
|
from itertools import chain
|
3
3
|
from time import monotonic
|
4
|
-
from typing import Any, Self, cast, final
|
4
|
+
from typing import Any, Self, cast, final, overload
|
5
5
|
|
6
6
|
from haiway.context import MetricsHandler, ScopeIdentifier, ctx
|
7
7
|
from haiway.state import State
|
8
|
-
from haiway.types import MISSING
|
8
|
+
from haiway.types import MISSING
|
9
9
|
|
10
10
|
__all_ = [
|
11
11
|
"MetricsLogger",
|
12
|
+
"MetricsHolder",
|
12
13
|
]
|
13
14
|
|
14
15
|
|
@@ -32,25 +33,129 @@ class MetricsScopeStore:
|
|
32
33
|
def finished(self) -> float:
|
33
34
|
return self.exited is not None and all(nested.finished for nested in self.nested)
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
current: State | Missing = merged_metrics.get(
|
40
|
-
metric_type,
|
41
|
-
MISSING,
|
42
|
-
)
|
36
|
+
@overload
|
37
|
+
def merged[Metric: State](
|
38
|
+
self,
|
39
|
+
) -> Sequence[State]: ...
|
43
40
|
|
44
|
-
|
45
|
-
|
41
|
+
@overload
|
42
|
+
def merged[Metric: State](
|
43
|
+
self,
|
44
|
+
metric: type[Metric],
|
45
|
+
) -> Metric | None: ...
|
46
46
|
|
47
|
-
|
48
|
-
|
47
|
+
def merged[Metric: State](
|
48
|
+
self,
|
49
|
+
metric: type[Metric] | None = None,
|
50
|
+
) -> Sequence[State] | Metric | None:
|
51
|
+
if metric is None:
|
52
|
+
merged_metrics: dict[type[State], State] = dict(self.metrics)
|
53
|
+
for nested in chain.from_iterable(nested.merged() for nested in self.nested):
|
54
|
+
metric_type: type[State] = type(nested)
|
55
|
+
current: State | None = merged_metrics.get(metric_type)
|
49
56
|
|
50
|
-
|
51
|
-
|
57
|
+
if current is None:
|
58
|
+
merged_metrics[metric_type] = nested
|
59
|
+
continue # keep going
|
60
|
+
|
61
|
+
if hasattr(current, "__add__"):
|
62
|
+
merged_metrics[metric_type] = current.__add__(nested) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
|
63
|
+
assert isinstance(merged_metrics[metric_type], State) # nosec: B101
|
64
|
+
continue # keep going
|
65
|
+
|
66
|
+
break # we have multiple value without a way to merge
|
67
|
+
|
68
|
+
return tuple(merged_metrics.values())
|
69
|
+
|
70
|
+
else:
|
71
|
+
merged_metric: State | None = self.metrics.get(metric)
|
72
|
+
for nested in self.nested:
|
73
|
+
nested_metric: Metric | None = nested.merged(metric)
|
74
|
+
if nested_metric is None:
|
75
|
+
continue # skip missing
|
76
|
+
|
77
|
+
if merged_metric is None:
|
78
|
+
merged_metric = nested_metric
|
79
|
+
continue # keep going
|
80
|
+
|
81
|
+
if hasattr(merged_metric, "__add__"):
|
82
|
+
merged_metric = merged_metric.__add__(nested_metric) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue, reportUnknownVariableType]
|
83
|
+
assert isinstance(merged_metric, metric) # nosec: B101
|
84
|
+
continue # keep going
|
85
|
+
|
86
|
+
break # we have multiple value without a way to merge
|
87
|
+
|
88
|
+
return cast(Metric | None, merged_metric)
|
89
|
+
|
90
|
+
|
91
|
+
@final
|
92
|
+
class MetricsHolder:
|
93
|
+
@classmethod
|
94
|
+
def handler(cls) -> MetricsHandler:
|
95
|
+
store_handler: Self = cls()
|
96
|
+
return MetricsHandler(
|
97
|
+
record=store_handler.record,
|
98
|
+
read=store_handler.read,
|
99
|
+
enter_scope=store_handler.enter_scope,
|
100
|
+
exit_scope=store_handler.exit_scope,
|
101
|
+
)
|
102
|
+
|
103
|
+
def __init__(self) -> None:
|
104
|
+
self.scopes: dict[ScopeIdentifier, MetricsScopeStore] = {}
|
105
|
+
|
106
|
+
def record(
|
107
|
+
self,
|
108
|
+
scope: ScopeIdentifier,
|
109
|
+
/,
|
110
|
+
metric: State,
|
111
|
+
) -> None:
|
112
|
+
assert scope in self.scopes # nosec: B101
|
113
|
+
metric_type: type[State] = type(metric)
|
114
|
+
metrics: dict[type[State], State] = self.scopes[scope].metrics
|
115
|
+
if (current := metrics.get(metric_type)) and hasattr(current, "__add__"):
|
116
|
+
metrics[type(metric)] = current.__add__(metric) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
|
117
|
+
|
118
|
+
metrics[type(metric)] = metric
|
119
|
+
|
120
|
+
async def read[Metric: State](
|
121
|
+
self,
|
122
|
+
scope: ScopeIdentifier,
|
123
|
+
/,
|
124
|
+
*,
|
125
|
+
metric: type[Metric],
|
126
|
+
merged: bool,
|
127
|
+
) -> Metric | None:
|
128
|
+
if merged:
|
129
|
+
return self.scopes[scope].merged(metric)
|
52
130
|
|
53
|
-
|
131
|
+
else:
|
132
|
+
return cast(Metric | None, self.scopes[scope].metrics.get(metric))
|
133
|
+
|
134
|
+
def enter_scope[Metric: State](
|
135
|
+
self,
|
136
|
+
scope: ScopeIdentifier,
|
137
|
+
/,
|
138
|
+
) -> None:
|
139
|
+
assert scope not in self.scopes # nosec: B101
|
140
|
+
scope_metrics = MetricsScopeStore(scope)
|
141
|
+
self.scopes[scope] = scope_metrics
|
142
|
+
if not scope.is_root: # root scopes have no actual parent
|
143
|
+
for key in self.scopes.keys():
|
144
|
+
if key.scope_id == scope.parent_id:
|
145
|
+
self.scopes[key].nested.append(scope_metrics)
|
146
|
+
return
|
147
|
+
|
148
|
+
ctx.log_debug(
|
149
|
+
"Attempting to enter nested scope metrics without entering its parent first"
|
150
|
+
)
|
151
|
+
|
152
|
+
def exit_scope[Metric: State](
|
153
|
+
self,
|
154
|
+
scope: ScopeIdentifier,
|
155
|
+
/,
|
156
|
+
) -> None:
|
157
|
+
assert scope in self.scopes # nosec: B101
|
158
|
+
self.scopes[scope].exited = monotonic()
|
54
159
|
|
55
160
|
|
56
161
|
@final
|
@@ -67,6 +172,7 @@ class MetricsLogger:
|
|
67
172
|
)
|
68
173
|
return MetricsHandler(
|
69
174
|
record=logger_handler.record,
|
175
|
+
read=logger_handler.read,
|
70
176
|
enter_scope=logger_handler.enter_scope,
|
71
177
|
exit_scope=logger_handler.exit_scope,
|
72
178
|
)
|
@@ -100,6 +206,20 @@ class MetricsLogger:
|
|
100
206
|
):
|
101
207
|
ctx.log_debug(f"Recorded metric:\n⎡ {type(metric).__qualname__}:{log}\n⌊")
|
102
208
|
|
209
|
+
async def read[Metric: State](
|
210
|
+
self,
|
211
|
+
scope: ScopeIdentifier,
|
212
|
+
/,
|
213
|
+
*,
|
214
|
+
metric: type[Metric],
|
215
|
+
merged: bool,
|
216
|
+
) -> Metric | None:
|
217
|
+
if merged:
|
218
|
+
return self.scopes[scope].merged(metric)
|
219
|
+
|
220
|
+
else:
|
221
|
+
return cast(Metric | None, self.scopes[scope].metrics.get(metric))
|
222
|
+
|
103
223
|
def enter_scope[Metric: State](
|
104
224
|
self,
|
105
225
|
scope: ScopeIdentifier,
|
@@ -145,6 +265,9 @@ def _tree_log(
|
|
145
265
|
)
|
146
266
|
|
147
267
|
for metric in metrics.merged():
|
268
|
+
if type(metric) not in metrics.metrics:
|
269
|
+
continue # skip metrics not available in this scope
|
270
|
+
|
148
271
|
metric_log: str = ""
|
149
272
|
for key, value in vars(metric).items():
|
150
273
|
if value_log := _value_log(
|
@@ -160,7 +283,7 @@ def _tree_log(
|
|
160
283
|
if not metric_log:
|
161
284
|
continue # skip empty logs
|
162
285
|
|
163
|
-
log += f"\n⎡ •{type(metric).__qualname__}:{metric_log.replace(
|
286
|
+
log += f"\n⎡ •{type(metric).__qualname__}:{metric_log.replace('\n', '\n| ')}\n⌊"
|
164
287
|
|
165
288
|
for nested in metrics.nested:
|
166
289
|
nested_log: str = _tree_log(
|
@@ -61,7 +61,7 @@ async def test_logs_issue_with_errors():
|
|
61
61
|
assert executions == 2
|
62
62
|
assert logs.output == [
|
63
63
|
f"ERROR:root:Attempting to retry {compute.__name__}"
|
64
|
-
f" which failed due to an error: {FakeException(
|
64
|
+
f" which failed due to an error: {FakeException('fake')}"
|
65
65
|
]
|
66
66
|
|
67
67
|
|
@@ -260,7 +260,7 @@ async def test_async_logs_issue_with_errors():
|
|
260
260
|
await compute("expected")
|
261
261
|
assert executions == 2
|
262
262
|
assert logs.output[0].startswith(
|
263
|
-
f"ERROR:root:Attempting to retry {compute.__name__}
|
263
|
+
f"ERROR:root:Attempting to retry {compute.__name__} which failed due to an error"
|
264
264
|
)
|
265
265
|
|
266
266
|
|
@@ -1 +0,0 @@
|
|
1
|
-
haiway
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|