haiway 0.8.0__tar.gz → 0.8.1__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.0/src/haiway.egg-info → haiway-0.8.1}/PKG-INFO +1 -1
- {haiway-0.8.0 → haiway-0.8.1}/pyproject.toml +1 -1
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/context/access.py +5 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/helpers/metrics.py +59 -45
- {haiway-0.8.0 → haiway-0.8.1/src/haiway.egg-info}/PKG-INFO +1 -1
- {haiway-0.8.0 → haiway-0.8.1}/tests/test_context.py +0 -1
- {haiway-0.8.0 → haiway-0.8.1}/LICENSE +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/README.md +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/setup.cfg +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/__init__.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/context/__init__.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/context/disposables.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/context/identifier.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/context/logging.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/context/metrics.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/context/state.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/context/tasks.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/context/types.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/helpers/__init__.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/helpers/asynchrony.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/helpers/caching.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/helpers/retries.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/helpers/throttling.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/helpers/timeouted.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/helpers/tracing.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/py.typed +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/state/__init__.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/state/attributes.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/state/path.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/state/requirement.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/state/structure.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/state/validation.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/types/__init__.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/types/frozen.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/types/missing.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/utils/__init__.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/utils/always.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/utils/env.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/utils/immutable.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/utils/logs.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/utils/mimic.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/utils/noop.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway/utils/queue.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway.egg-info/SOURCES.txt +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway.egg-info/dependency_links.txt +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway.egg-info/requires.txt +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/src/haiway.egg-info/top_level.txt +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/tests/test_async_queue.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/tests/test_attribute_path.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/tests/test_auto_retry.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/tests/test_cache.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/tests/test_state.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/tests/test_streaming.py +0 -0
- {haiway-0.8.0 → haiway-0.8.1}/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.
|
8
|
+
version = "0.8.1"
|
9
9
|
readme = "README.md"
|
10
10
|
maintainers = [
|
11
11
|
{ name = "Kacper Kaliński", email = "kacper.kalinski@miquido.com" },
|
@@ -46,6 +46,11 @@ class ScopeContext:
|
|
46
46
|
self._state: tuple[State, ...] = state
|
47
47
|
self._disposables: Disposables | None = disposables
|
48
48
|
# pre-building metrics context to ensure nested context registering
|
49
|
+
if __debug__:
|
50
|
+
if self._identifier.is_root and metrics is None:
|
51
|
+
from haiway.helpers import MetricsLogger
|
52
|
+
|
53
|
+
metrics = MetricsLogger.handler()
|
49
54
|
self._metrics_context: MetricsContext = MetricsContext.scope(
|
50
55
|
self._identifier,
|
51
56
|
metrics=metrics,
|
@@ -1,16 +1,19 @@
|
|
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, Final, Self, cast, final
|
5
5
|
|
6
6
|
from haiway.context import MetricsHandler, ScopeIdentifier, ctx
|
7
7
|
from haiway.state import State
|
8
8
|
from haiway.types import MISSING, Missing
|
9
|
+
from haiway.utils import getenv_bool
|
9
10
|
|
10
11
|
__all_ = [
|
11
12
|
"MetricsLogger",
|
12
13
|
]
|
13
14
|
|
15
|
+
DEBUG_LOGGING: Final[bool] = getenv_bool("DEBUG_LOGGING", __debug__)
|
16
|
+
|
14
17
|
|
15
18
|
class MetricsScopeStore:
|
16
19
|
def __init__(
|
@@ -59,11 +62,11 @@ class MetricsLogger:
|
|
59
62
|
def handler(
|
60
63
|
cls,
|
61
64
|
items_limit: int | None = None,
|
62
|
-
|
65
|
+
redact_content: bool = False,
|
63
66
|
) -> MetricsHandler:
|
64
67
|
logger_handler: Self = cls(
|
65
68
|
items_limit=items_limit,
|
66
|
-
|
69
|
+
redact_content=redact_content,
|
67
70
|
)
|
68
71
|
return MetricsHandler(
|
69
72
|
record=logger_handler.record,
|
@@ -73,11 +76,11 @@ class MetricsLogger:
|
|
73
76
|
|
74
77
|
def __init__(
|
75
78
|
self,
|
76
|
-
items_limit: int | None
|
77
|
-
|
79
|
+
items_limit: int | None,
|
80
|
+
redact_content: bool,
|
78
81
|
) -> None:
|
79
82
|
self.items_limit: int | None = items_limit
|
80
|
-
self.
|
83
|
+
self.redact_content: bool = redact_content
|
81
84
|
self.scopes: dict[ScopeIdentifier, MetricsScopeStore] = {}
|
82
85
|
|
83
86
|
def record(
|
@@ -93,13 +96,13 @@ class MetricsLogger:
|
|
93
96
|
metrics[type(metric)] = current.__add__(metric) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
|
94
97
|
|
95
98
|
metrics[type(metric)] = metric
|
96
|
-
if
|
99
|
+
if DEBUG_LOGGING:
|
97
100
|
if log := _state_log(
|
98
101
|
metric,
|
99
102
|
list_items_limit=self.items_limit,
|
100
|
-
|
103
|
+
redact_content=self.redact_content,
|
101
104
|
):
|
102
|
-
ctx.log_info(f"Recorded:\n
|
105
|
+
ctx.log_info(f"Recorded metric:\n⎡ {type(metric).__qualname__}:{log}\n⌊")
|
103
106
|
|
104
107
|
def enter_scope[Metric: State](
|
105
108
|
self,
|
@@ -107,7 +110,17 @@ class MetricsLogger:
|
|
107
110
|
/,
|
108
111
|
) -> None:
|
109
112
|
assert scope not in self.scopes # nosec: B101
|
110
|
-
|
113
|
+
scope_metrics = MetricsScopeStore(scope)
|
114
|
+
self.scopes[scope] = scope_metrics
|
115
|
+
if not scope.is_root: # root scopes have no actual parent
|
116
|
+
for key in self.scopes.keys():
|
117
|
+
if key.scope_id == scope.parent_id:
|
118
|
+
self.scopes[key].nested.append(scope_metrics)
|
119
|
+
return
|
120
|
+
|
121
|
+
ctx.log_error(
|
122
|
+
"Attempting to enter nested scope metrics without entering its parent first"
|
123
|
+
)
|
111
124
|
|
112
125
|
def exit_scope[Metric: State](
|
113
126
|
self,
|
@@ -117,22 +130,24 @@ class MetricsLogger:
|
|
117
130
|
assert scope in self.scopes # nosec: B101
|
118
131
|
self.scopes[scope].exited = monotonic()
|
119
132
|
|
120
|
-
if
|
133
|
+
if DEBUG_LOGGING:
|
121
134
|
if scope.is_root and self.scopes[scope].finished:
|
122
135
|
if log := _tree_log(
|
123
136
|
self.scopes[scope],
|
124
137
|
list_items_limit=self.items_limit,
|
125
|
-
|
138
|
+
redact_content=self.redact_content,
|
126
139
|
):
|
127
|
-
ctx.log_info(log)
|
140
|
+
ctx.log_info(f"Metrics summary:\n{log}")
|
128
141
|
|
129
142
|
|
130
143
|
def _tree_log(
|
131
144
|
metrics: MetricsScopeStore,
|
132
145
|
list_items_limit: int | None,
|
133
|
-
|
146
|
+
redact_content: bool,
|
134
147
|
) -> str:
|
135
|
-
log: str =
|
148
|
+
log: str = (
|
149
|
+
f"⎡ @{metrics.identifier.label} [{metrics.identifier.scope_id}]({metrics.time:.2f}s):"
|
150
|
+
)
|
136
151
|
|
137
152
|
for metric in metrics.merged():
|
138
153
|
metric_log: str = ""
|
@@ -140,52 +155,52 @@ def _tree_log(
|
|
140
155
|
if value_log := _value_log(
|
141
156
|
value,
|
142
157
|
list_items_limit=list_items_limit,
|
143
|
-
|
158
|
+
redact_content=redact_content,
|
144
159
|
):
|
145
|
-
metric_log += f"\n
|
160
|
+
metric_log += f"\n├ {key}: {value_log}"
|
146
161
|
|
147
162
|
else:
|
148
|
-
continue # skip
|
163
|
+
continue # skip empty values
|
149
164
|
|
150
165
|
if not metric_log:
|
151
166
|
continue # skip empty logs
|
152
167
|
|
153
|
-
log += f"\n•
|
168
|
+
log += f"\n⎡ •{type(metric).__qualname__}:{metric_log.replace("\n", "\n| ")}\n⌊"
|
154
169
|
|
155
170
|
for nested in metrics.nested:
|
156
171
|
nested_log: str = _tree_log(
|
157
172
|
nested,
|
158
173
|
list_items_limit=list_items_limit,
|
159
|
-
|
160
|
-
)
|
174
|
+
redact_content=redact_content,
|
175
|
+
)
|
161
176
|
|
162
|
-
log += f"\n{nested_log}"
|
177
|
+
log += f"\n\n{nested_log}"
|
163
178
|
|
164
|
-
return log.strip()
|
179
|
+
return log.strip().replace("\n", "\n| ") + "\n⌊"
|
165
180
|
|
166
181
|
|
167
182
|
def _state_log(
|
168
183
|
value: State,
|
169
184
|
/,
|
170
185
|
list_items_limit: int | None,
|
171
|
-
|
186
|
+
redact_content: bool,
|
172
187
|
) -> str | None:
|
173
188
|
state_log: str = ""
|
174
189
|
for key, element in vars(value).items():
|
175
190
|
element_log: str | None = _value_log(
|
176
191
|
element,
|
177
192
|
list_items_limit=list_items_limit,
|
178
|
-
|
193
|
+
redact_content=redact_content,
|
179
194
|
)
|
180
195
|
|
181
196
|
if element_log:
|
182
|
-
state_log += f"\n
|
197
|
+
state_log += f"\n├ {key}: {element_log}"
|
183
198
|
|
184
199
|
else:
|
185
200
|
continue # skip empty logs
|
186
201
|
|
187
202
|
if state_log:
|
188
|
-
return state_log
|
203
|
+
return state_log
|
189
204
|
|
190
205
|
else:
|
191
206
|
return None # skip empty logs
|
@@ -195,17 +210,17 @@ def _dict_log(
|
|
195
210
|
value: dict[Any, Any],
|
196
211
|
/,
|
197
212
|
list_items_limit: int | None,
|
198
|
-
|
213
|
+
redact_content: bool,
|
199
214
|
) -> str | None:
|
200
215
|
dict_log: str = ""
|
201
216
|
for key, element in value.items():
|
202
217
|
element_log: str | None = _value_log(
|
203
218
|
element,
|
204
219
|
list_items_limit=list_items_limit,
|
205
|
-
|
220
|
+
redact_content=redact_content,
|
206
221
|
)
|
207
222
|
if element_log:
|
208
|
-
dict_log += f"\n
|
223
|
+
dict_log += f"\n[{key}]: {element_log}"
|
209
224
|
|
210
225
|
else:
|
211
226
|
continue # skip empty logs
|
@@ -221,7 +236,7 @@ def _list_log(
|
|
221
236
|
value: list[Any],
|
222
237
|
/,
|
223
238
|
list_items_limit: int | None,
|
224
|
-
|
239
|
+
redact_content: bool,
|
225
240
|
) -> str | None:
|
226
241
|
list_log: str = ""
|
227
242
|
enumerated: list[tuple[int, Any]] = list(enumerate(value))
|
@@ -236,10 +251,10 @@ def _list_log(
|
|
236
251
|
element_log: str | None = _value_log(
|
237
252
|
element,
|
238
253
|
list_items_limit=list_items_limit,
|
239
|
-
|
254
|
+
redact_content=redact_content,
|
240
255
|
)
|
241
256
|
if element_log:
|
242
|
-
list_log += f"\n
|
257
|
+
list_log += f"\n[{idx}] {element_log}"
|
243
258
|
|
244
259
|
else:
|
245
260
|
continue # skip empty logs
|
@@ -254,34 +269,33 @@ def _list_log(
|
|
254
269
|
def _raw_value_log(
|
255
270
|
value: Any,
|
256
271
|
/,
|
257
|
-
|
272
|
+
redact_content: bool,
|
258
273
|
) -> str | None:
|
259
274
|
if value is MISSING:
|
260
275
|
return None # skip missing
|
261
276
|
|
262
|
-
|
263
|
-
|
264
|
-
return None # skip empty logs
|
277
|
+
if redact_content:
|
278
|
+
return "[redacted]"
|
265
279
|
|
266
|
-
|
267
|
-
return
|
280
|
+
elif isinstance(value, str):
|
281
|
+
return f'"{value}"'.replace("\n", "\n| ")
|
268
282
|
|
269
283
|
else:
|
270
|
-
return
|
284
|
+
return str(value).strip().replace("\n", "\n| ")
|
271
285
|
|
272
286
|
|
273
287
|
def _value_log(
|
274
288
|
value: Any,
|
275
289
|
/,
|
276
290
|
list_items_limit: int | None,
|
277
|
-
|
291
|
+
redact_content: bool,
|
278
292
|
) -> str | None:
|
279
293
|
# try unpack dicts
|
280
294
|
if isinstance(value, dict):
|
281
295
|
return _dict_log(
|
282
296
|
cast(dict[Any, Any], value),
|
283
297
|
list_items_limit=list_items_limit,
|
284
|
-
|
298
|
+
redact_content=redact_content,
|
285
299
|
)
|
286
300
|
|
287
301
|
# try unpack lists
|
@@ -289,7 +303,7 @@ def _value_log(
|
|
289
303
|
return _list_log(
|
290
304
|
cast(list[Any], value),
|
291
305
|
list_items_limit=list_items_limit,
|
292
|
-
|
306
|
+
redact_content=redact_content,
|
293
307
|
)
|
294
308
|
|
295
309
|
# try unpack state
|
@@ -297,11 +311,11 @@ def _value_log(
|
|
297
311
|
return _state_log(
|
298
312
|
value,
|
299
313
|
list_items_limit=list_items_limit,
|
300
|
-
|
314
|
+
redact_content=redact_content,
|
301
315
|
)
|
302
316
|
|
303
317
|
else:
|
304
318
|
return _raw_value_log(
|
305
319
|
value,
|
306
|
-
|
320
|
+
redact_content=redact_content,
|
307
321
|
)
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|