haiway 0.25.0__py3-none-any.whl → 0.26.0__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 +2 -4
- haiway/context/__init__.py +2 -2
- haiway/context/access.py +200 -155
- haiway/context/presets.py +104 -86
- haiway/context/state.py +2 -42
- haiway/helpers/__init__.py +0 -2
- {haiway-0.25.0.dist-info → haiway-0.26.0.dist-info}/METADATA +1 -1
- {haiway-0.25.0.dist-info → haiway-0.26.0.dist-info}/RECORD +10 -11
- haiway/helpers/tracing.py +0 -185
- {haiway-0.25.0.dist-info → haiway-0.26.0.dist-info}/WHEEL +0 -0
- {haiway-0.25.0.dist-info → haiway-0.26.0.dist-info}/licenses/LICENSE +0 -0
haiway/context/presets.py
CHANGED
@@ -1,32 +1,32 @@
|
|
1
|
+
from asyncio import gather
|
1
2
|
from collections.abc import Collection, Iterable, Mapping
|
2
3
|
from contextvars import ContextVar, Token
|
3
4
|
from types import TracebackType
|
4
5
|
from typing import ClassVar, Protocol, Self, cast
|
5
6
|
|
6
7
|
from haiway.context.disposables import Disposable, Disposables
|
8
|
+
from haiway.context.state import StateContext
|
7
9
|
from haiway.state import Immutable, State
|
8
|
-
from haiway.types.default import Default
|
9
10
|
|
10
11
|
__all__ = (
|
11
|
-
"
|
12
|
-
"
|
13
|
-
"ContextPresetsRegistryContext",
|
12
|
+
"ContextPreset",
|
13
|
+
"ContextPresetRegistryContext",
|
14
14
|
)
|
15
15
|
|
16
16
|
|
17
|
-
class
|
17
|
+
class ContextPresetStatePreparing(Protocol):
|
18
18
|
async def __call__(self) -> Iterable[State] | State: ...
|
19
19
|
|
20
20
|
|
21
|
-
class
|
21
|
+
class ContextPresetDisposablesPreparing(Protocol):
|
22
22
|
async def __call__(self) -> Iterable[Disposable] | Disposable: ...
|
23
23
|
|
24
24
|
|
25
|
-
class
|
25
|
+
class ContextPreset(Immutable):
|
26
26
|
"""
|
27
27
|
A configuration preset for context scopes.
|
28
28
|
|
29
|
-
|
29
|
+
ContextPreset allows you to define reusable combinations of state and disposables
|
30
30
|
that can be applied to scopes by name. This provides a convenient way to manage
|
31
31
|
complex application configurations and resource setups.
|
32
32
|
|
@@ -43,15 +43,15 @@ class ContextPresets(Immutable):
|
|
43
43
|
Basic preset with static state:
|
44
44
|
|
45
45
|
>>> from haiway import State
|
46
|
-
>>> from haiway.context import
|
46
|
+
>>> from haiway.context import ContextPreset
|
47
47
|
>>>
|
48
48
|
>>> class DatabaseConfig(State):
|
49
49
|
... connection_string: str
|
50
50
|
... pool_size: int = 10
|
51
51
|
>>>
|
52
|
-
>>> db_preset =
|
52
|
+
>>> db_preset = ContextPreset(
|
53
53
|
... name="database",
|
54
|
-
...
|
54
|
+
... state=[DatabaseConfig(connection_string="postgresql://localhost/app")]
|
55
55
|
... )
|
56
56
|
|
57
57
|
Preset with dynamic state factory:
|
@@ -60,9 +60,9 @@ class ContextPresets(Immutable):
|
|
60
60
|
... # Load configuration from environment or config file
|
61
61
|
... return DatabaseConfig(connection_string=os.getenv("DB_URL"))
|
62
62
|
>>>
|
63
|
-
>>> dynamic_preset =
|
63
|
+
>>> dynamic_preset = ContextPreset(
|
64
64
|
... name="dynamic_db",
|
65
|
-
...
|
65
|
+
... state=[load_config]
|
66
66
|
... )
|
67
67
|
|
68
68
|
Preset with disposables:
|
@@ -80,10 +80,10 @@ class ContextPresets(Immutable):
|
|
80
80
|
>>> async def connection_factory():
|
81
81
|
... return database_connection()
|
82
82
|
>>>
|
83
|
-
>>> db_preset =
|
83
|
+
>>> db_preset = ContextPreset(
|
84
84
|
... name="database",
|
85
|
-
...
|
86
|
-
...
|
85
|
+
... state=[DatabaseConfig(connection_string="...")],
|
86
|
+
... disposables=[connection_factory]
|
87
87
|
... )
|
88
88
|
|
89
89
|
Using presets:
|
@@ -97,8 +97,31 @@ class ContextPresets(Immutable):
|
|
97
97
|
"""
|
98
98
|
|
99
99
|
name: str
|
100
|
-
_state: Collection[
|
101
|
-
_disposables: Collection[
|
100
|
+
_state: Collection[ContextPresetStatePreparing | State]
|
101
|
+
_disposables: Collection[ContextPresetDisposablesPreparing]
|
102
|
+
|
103
|
+
def __init__(
|
104
|
+
self,
|
105
|
+
name: str,
|
106
|
+
*,
|
107
|
+
state: Collection[ContextPresetStatePreparing | State] = (),
|
108
|
+
disposables: Collection[ContextPresetDisposablesPreparing] = (),
|
109
|
+
) -> None:
|
110
|
+
object.__setattr__(
|
111
|
+
self,
|
112
|
+
"name",
|
113
|
+
name,
|
114
|
+
)
|
115
|
+
object.__setattr__(
|
116
|
+
self,
|
117
|
+
"_state",
|
118
|
+
state,
|
119
|
+
)
|
120
|
+
object.__setattr__(
|
121
|
+
self,
|
122
|
+
"_disposables",
|
123
|
+
disposables,
|
124
|
+
)
|
102
125
|
|
103
126
|
def extended(
|
104
127
|
self,
|
@@ -114,38 +137,38 @@ class ContextPresets(Immutable):
|
|
114
137
|
Parameters
|
115
138
|
----------
|
116
139
|
other : Self
|
117
|
-
Another
|
140
|
+
Another ContextPreset instance to merge with this one.
|
118
141
|
|
119
142
|
Returns
|
120
143
|
-------
|
121
144
|
Self
|
122
|
-
A new
|
145
|
+
A new ContextPreset instance with combined state and disposables.
|
123
146
|
"""
|
124
147
|
return self.__class__(
|
125
148
|
name=self.name,
|
126
|
-
|
127
|
-
|
149
|
+
state=(*self._state, *other._state),
|
150
|
+
disposables=(*self._disposables, *other._disposables),
|
128
151
|
)
|
129
152
|
|
130
153
|
def with_state(
|
131
154
|
self,
|
132
|
-
*state:
|
155
|
+
*state: ContextPresetStatePreparing | State,
|
133
156
|
) -> Self:
|
134
157
|
"""
|
135
158
|
Create a new preset with additional state.
|
136
159
|
|
137
|
-
Returns a new
|
160
|
+
Returns a new ContextPreset instance with the provided state objects
|
138
161
|
or state factories added to the existing state collection.
|
139
162
|
|
140
163
|
Parameters
|
141
164
|
----------
|
142
|
-
*state :
|
165
|
+
*state : ContextPresetStatePreparing | State
|
143
166
|
Additional state objects or state factory functions to include.
|
144
167
|
|
145
168
|
Returns
|
146
169
|
-------
|
147
170
|
Self
|
148
|
-
A new
|
171
|
+
A new ContextPreset instance with the additional state, or the
|
149
172
|
same instance if no state was provided.
|
150
173
|
"""
|
151
174
|
if not state:
|
@@ -153,29 +176,29 @@ class ContextPresets(Immutable):
|
|
153
176
|
|
154
177
|
return self.__class__(
|
155
178
|
name=self.name,
|
156
|
-
|
157
|
-
|
179
|
+
state=(*self._state, *state),
|
180
|
+
disposables=self._disposables,
|
158
181
|
)
|
159
182
|
|
160
183
|
def with_disposable(
|
161
184
|
self,
|
162
|
-
*disposable:
|
185
|
+
*disposable: ContextPresetDisposablesPreparing,
|
163
186
|
) -> Self:
|
164
187
|
"""
|
165
188
|
Create a new preset with additional disposables.
|
166
189
|
|
167
|
-
Returns a new
|
190
|
+
Returns a new ContextPreset instance with the provided disposable
|
168
191
|
factory functions added to the existing disposables collection.
|
169
192
|
|
170
193
|
Parameters
|
171
194
|
----------
|
172
|
-
*disposable :
|
195
|
+
*disposable : ContextPresetDisposablesPreparing
|
173
196
|
Additional disposable factory functions to include.
|
174
197
|
|
175
198
|
Returns
|
176
199
|
-------
|
177
200
|
Self
|
178
|
-
A new
|
201
|
+
A new ContextPreset instance with the additional disposables, or the
|
179
202
|
same instance if no disposables were provided.
|
180
203
|
"""
|
181
204
|
if not disposable:
|
@@ -183,8 +206,8 @@ class ContextPresets(Immutable):
|
|
183
206
|
|
184
207
|
return self.__class__(
|
185
208
|
name=self.name,
|
186
|
-
|
187
|
-
|
209
|
+
state=self._state,
|
210
|
+
disposables=(*self._disposables, *disposable),
|
188
211
|
)
|
189
212
|
|
190
213
|
async def prepare(self) -> Disposables:
|
@@ -210,37 +233,48 @@ class ContextPresets(Immutable):
|
|
210
233
|
This method is called automatically when using presets with ctx.scope(),
|
211
234
|
so you typically don't need to call it directly.
|
212
235
|
"""
|
213
|
-
|
214
|
-
|
236
|
+
collected_state: Collection[State] = await self._collect_state()
|
237
|
+
|
238
|
+
collected_disposables: Collection[Disposable]
|
239
|
+
if collected_state:
|
240
|
+
# use available state immediately when preparing disposables
|
241
|
+
with StateContext.updated(collected_state):
|
242
|
+
collected_disposables = (
|
243
|
+
DisposableState(_state=collected_state),
|
244
|
+
*await self._collect_disposables(),
|
245
|
+
)
|
246
|
+
|
247
|
+
else:
|
248
|
+
collected_disposables = await self._collect_disposables()
|
249
|
+
|
250
|
+
return Disposables(*collected_disposables)
|
251
|
+
|
252
|
+
async def _collect_state(self) -> Collection[State]:
|
253
|
+
collected_state: list[State] = []
|
215
254
|
for state in self._state:
|
216
255
|
if isinstance(state, State):
|
217
|
-
|
256
|
+
collected_state.append(state)
|
257
|
+
|
218
258
|
else:
|
219
259
|
resolved_state: Iterable[State] | State = await state()
|
220
260
|
if isinstance(resolved_state, State):
|
221
|
-
|
261
|
+
collected_state.append(resolved_state)
|
222
262
|
|
223
263
|
else:
|
224
|
-
|
225
|
-
|
226
|
-
collected_disposables: list[Disposable]
|
227
|
-
if collected_states:
|
228
|
-
collected_disposables = [DisposableState(_state=collected_states)]
|
264
|
+
collected_state.extend(resolved_state)
|
229
265
|
|
230
|
-
|
231
|
-
collected_disposables = []
|
266
|
+
return collected_state
|
232
267
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
collected_disposables.append(cast(Disposable, resolved_disposable))
|
268
|
+
async def _collect_disposables(self) -> Collection[Disposable]:
|
269
|
+
collected_disposables: list[Disposable] = []
|
270
|
+
for disposable in await gather(*(factory() for factory in self._disposables)):
|
271
|
+
if hasattr(disposable, "__aenter__") and hasattr(disposable, "__aexit__"):
|
272
|
+
collected_disposables.append(cast(Disposable, disposable))
|
239
273
|
|
240
274
|
else:
|
241
|
-
collected_disposables.extend(cast(Iterable[Disposable],
|
275
|
+
collected_disposables.extend(cast(Iterable[Disposable], disposable))
|
242
276
|
|
243
|
-
return
|
277
|
+
return collected_disposables
|
244
278
|
|
245
279
|
|
246
280
|
class DisposableState(Immutable):
|
@@ -258,55 +292,32 @@ class DisposableState(Immutable):
|
|
258
292
|
pass
|
259
293
|
|
260
294
|
|
261
|
-
class
|
262
|
-
|
263
|
-
|
264
|
-
def __init__(
|
265
|
-
self,
|
266
|
-
presets: Collection[ContextPresets],
|
267
|
-
) -> None:
|
268
|
-
object.__setattr__(
|
269
|
-
self,
|
270
|
-
"_presets",
|
271
|
-
{preset.name: preset for preset in presets},
|
272
|
-
)
|
273
|
-
|
274
|
-
def select(
|
275
|
-
self,
|
276
|
-
name: str,
|
277
|
-
/,
|
278
|
-
) -> ContextPresets | None:
|
279
|
-
return self._presets.get(name)
|
280
|
-
|
281
|
-
|
282
|
-
class ContextPresetsRegistryContext(Immutable):
|
283
|
-
_context: ClassVar[ContextVar[ContextPresetsRegistry]] = ContextVar[ContextPresetsRegistry](
|
284
|
-
"ContextPresetsRegistryContext"
|
285
|
-
)
|
295
|
+
class ContextPresetRegistryContext(Immutable):
|
296
|
+
_context: ClassVar[ContextVar[Self]] = ContextVar[Self]("ContextPresetRegistryContext")
|
286
297
|
|
287
298
|
@classmethod
|
288
299
|
def select(
|
289
300
|
cls,
|
290
301
|
name: str,
|
291
302
|
/,
|
292
|
-
) ->
|
303
|
+
) -> ContextPreset | None:
|
293
304
|
try:
|
294
|
-
return cls._context.get().
|
305
|
+
return cls._context.get().preset(name)
|
295
306
|
|
296
307
|
except LookupError:
|
297
308
|
return None # no presets
|
298
309
|
|
299
|
-
_registry:
|
300
|
-
_token: Token[
|
310
|
+
_registry: Mapping[str, ContextPreset]
|
311
|
+
_token: Token[Self] | None = None
|
301
312
|
|
302
313
|
def __init__(
|
303
314
|
self,
|
304
|
-
|
315
|
+
presets: Iterable[ContextPreset],
|
305
316
|
) -> None:
|
306
317
|
object.__setattr__(
|
307
318
|
self,
|
308
319
|
"_registry",
|
309
|
-
|
320
|
+
{preset.name: preset for preset in presets},
|
310
321
|
)
|
311
322
|
object.__setattr__(
|
312
323
|
self,
|
@@ -314,12 +325,19 @@ class ContextPresetsRegistryContext(Immutable):
|
|
314
325
|
None,
|
315
326
|
)
|
316
327
|
|
328
|
+
def preset(
|
329
|
+
self,
|
330
|
+
name: str,
|
331
|
+
/,
|
332
|
+
) -> ContextPreset | None:
|
333
|
+
return self._registry.get(name)
|
334
|
+
|
317
335
|
def __enter__(self) -> None:
|
318
336
|
assert self._token is None, "Context reentrance is not allowed" # nosec: B101
|
319
337
|
object.__setattr__(
|
320
338
|
self,
|
321
339
|
"_token",
|
322
|
-
|
340
|
+
ContextPresetRegistryContext._context.set(self),
|
323
341
|
)
|
324
342
|
|
325
343
|
def __exit__(
|
@@ -329,7 +347,7 @@ class ContextPresetsRegistryContext(Immutable):
|
|
329
347
|
exc_tb: TracebackType | None,
|
330
348
|
) -> None:
|
331
349
|
assert self._token is not None, "Unbalanced context enter/exit" # nosec: B101
|
332
|
-
|
350
|
+
ContextPresetRegistryContext._context.reset(self._token) # pyright: ignore[reportArgumentType]
|
333
351
|
object.__setattr__(
|
334
352
|
self,
|
335
353
|
"_token",
|
haiway/context/state.py
CHANGED
@@ -1,13 +1,11 @@
|
|
1
|
-
from
|
2
|
-
from collections.abc import Callable, Collection, Coroutine, Iterable, MutableMapping
|
1
|
+
from collections.abc import Collection, Iterable, MutableMapping
|
3
2
|
from contextvars import ContextVar, Token
|
4
3
|
from threading import Lock
|
5
4
|
from types import TracebackType
|
6
|
-
from typing import
|
5
|
+
from typing import ClassVar, Self, cast
|
7
6
|
|
8
7
|
from haiway.context.types import MissingContext, MissingState
|
9
8
|
from haiway.state import Immutable, State
|
10
|
-
from haiway.utils.mimic import mimic_function
|
11
9
|
|
12
10
|
__all__ = (
|
13
11
|
"ScopeState",
|
@@ -356,41 +354,3 @@ class StateContext(Immutable):
|
|
356
354
|
"_token",
|
357
355
|
None,
|
358
356
|
)
|
359
|
-
|
360
|
-
@overload
|
361
|
-
def __call__[Result, **Arguments](
|
362
|
-
self,
|
363
|
-
function: Callable[Arguments, Coroutine[Any, Any, Result]],
|
364
|
-
) -> Callable[Arguments, Coroutine[Any, Any, Result]]: ...
|
365
|
-
|
366
|
-
@overload
|
367
|
-
def __call__[Result, **Arguments](
|
368
|
-
self,
|
369
|
-
function: Callable[Arguments, Result],
|
370
|
-
) -> Callable[Arguments, Result]: ...
|
371
|
-
|
372
|
-
def __call__[Result, **Arguments](
|
373
|
-
self,
|
374
|
-
function: Callable[Arguments, Coroutine[Any, Any, Result]] | Callable[Arguments, Result],
|
375
|
-
) -> Callable[Arguments, Coroutine[Any, Any, Result]] | Callable[Arguments, Result]:
|
376
|
-
if iscoroutinefunction(function):
|
377
|
-
|
378
|
-
async def async_context(
|
379
|
-
*args: Arguments.args,
|
380
|
-
**kwargs: Arguments.kwargs,
|
381
|
-
) -> Result:
|
382
|
-
with self:
|
383
|
-
return await function(*args, **kwargs)
|
384
|
-
|
385
|
-
return mimic_function(function, within=async_context)
|
386
|
-
|
387
|
-
else:
|
388
|
-
|
389
|
-
def sync_context(
|
390
|
-
*args: Arguments.args,
|
391
|
-
**kwargs: Arguments.kwargs,
|
392
|
-
) -> Result:
|
393
|
-
with self:
|
394
|
-
return function(*args, **kwargs) # pyright: ignore[reportReturnType]
|
395
|
-
|
396
|
-
return mimic_function(function, within=sync_context) # pyright: ignore[reportReturnType]
|
haiway/helpers/__init__.py
CHANGED
@@ -10,7 +10,6 @@ from haiway.helpers.observability import LoggerObservability
|
|
10
10
|
from haiway.helpers.retries import retry
|
11
11
|
from haiway.helpers.throttling import throttle
|
12
12
|
from haiway.helpers.timeouting import timeout
|
13
|
-
from haiway.helpers.tracing import traced
|
14
13
|
|
15
14
|
__all__ = (
|
16
15
|
"CacheMakeKey",
|
@@ -27,5 +26,4 @@ __all__ = (
|
|
27
26
|
"stream_concurrently",
|
28
27
|
"throttle",
|
29
28
|
"timeout",
|
30
|
-
"traced",
|
31
29
|
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: haiway
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.26.0
|
4
4
|
Summary: Framework for dependency injection and state management within structured concurrency model.
|
5
5
|
Project-URL: Homepage, https://miquido.com
|
6
6
|
Project-URL: Repository, https://github.com/miquido/haiway.git
|
@@ -1,15 +1,15 @@
|
|
1
|
-
haiway/__init__.py,sha256=
|
1
|
+
haiway/__init__.py,sha256=tJpU6TzK-o-Pt8joGrJah5eC08STVoRzvhXeLn3oTKo,2095
|
2
2
|
haiway/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
-
haiway/context/__init__.py,sha256=
|
4
|
-
haiway/context/access.py,sha256=
|
3
|
+
haiway/context/__init__.py,sha256=DKcf1AHGEqLWF8Kki30YKQ07GjonUjpAA8I51P4AxAg,1296
|
4
|
+
haiway/context/access.py,sha256=g8WdrKNvg6sO3X7Km_9X9syafxS0Shm_EP-JKqRLyxI,31367
|
5
5
|
haiway/context/disposables.py,sha256=7Jo-5qzS3UQvZUf4yOqUgfnueMg8I65jwHDp-4g6w54,7998
|
6
6
|
haiway/context/identifier.py,sha256=ps7YM1ZnUrj66SPVyxqMhTRMaYOMNSb82J3FfMRVHm4,4690
|
7
7
|
haiway/context/observability.py,sha256=rZoZT7g4ZM5_OKIFV0uA0rJodHxondw8X2bMSf_W6_s,20244
|
8
|
-
haiway/context/presets.py,sha256=
|
9
|
-
haiway/context/state.py,sha256=
|
8
|
+
haiway/context/presets.py,sha256=NVRv-PzuzonxdzIEJmt8SRMRO0lUgKb-jR-2ixUtkzI,10760
|
9
|
+
haiway/context/state.py,sha256=1oeON23_vaX7IgyckPcA5jWMXije93TNuSh0l9dQqNE,9920
|
10
10
|
haiway/context/tasks.py,sha256=0LdoxkQW0op4-QhAA-IDQO0PQr6Q3Vp4mO5ssEFbclU,4930
|
11
11
|
haiway/context/types.py,sha256=LoW8238TTdbUgmxyHDi0LVc8M8ZwTHLWKkAPttTsTeg,746
|
12
|
-
haiway/helpers/__init__.py,sha256=
|
12
|
+
haiway/helpers/__init__.py,sha256=gyKM1mWyuQwSx_2ajpI0UF1nA8l5D7wrzZOt9XUkWJA,780
|
13
13
|
haiway/helpers/asynchrony.py,sha256=Ddj8UdXhVczAbAC-rLpyhWa4RJ_W2Eolo45Veorq7_4,5362
|
14
14
|
haiway/helpers/caching.py,sha256=BqgcUGQSAmXsuLi5V8EwlZzuGyutHOn1V4k7BHsGKeg,14347
|
15
15
|
haiway/helpers/concurrent.py,sha256=fU6je62XvbySylZKDgzC_AGKPC7Kimmd5SmkVpySBUo,13115
|
@@ -18,7 +18,6 @@ haiway/helpers/observability.py,sha256=R4md41g7iTslzvtRaY5W9pgXqmuzJuGByjFb6vsO4
|
|
18
18
|
haiway/helpers/retries.py,sha256=OH__I9e-PUFxcSwuQLIzJ9F1MwXgbz1Ur4jEjJiOmjQ,8974
|
19
19
|
haiway/helpers/throttling.py,sha256=KBWUSHdKVMC5_nRMmmoPNwfp-3AcerQ6OczJa9gNLM0,5796
|
20
20
|
haiway/helpers/timeouting.py,sha256=GQ8-btb36f0Jq7TnorAPYXyKScNmf0nxHXCYxqGl-o8,3949
|
21
|
-
haiway/helpers/tracing.py,sha256=NHipA5UlngwFcAaKhXg1jTuJ-ti6AqSNxE7u7-92vWo,5409
|
22
21
|
haiway/opentelemetry/__init__.py,sha256=TV-1C14mDAtcHhFZ29ActFQdrGH6x5KuGV9w-JlKYJg,91
|
23
22
|
haiway/opentelemetry/observability.py,sha256=uFgSuvwOgW7IbffROY6Kc4ZRJGoQV6rEWqIQltU_Iho,27365
|
24
23
|
haiway/state/__init__.py,sha256=mtYgg2TojOBNjFsfoRjYkfZPDhKV5sPJXxDGFBvB8-0,417
|
@@ -41,7 +40,7 @@ haiway/utils/mimic.py,sha256=xaZiUKp096QFfdSw7cNIKEWt2UIS7vf880KF54gny38,1831
|
|
41
40
|
haiway/utils/noop.py,sha256=U8ocfoCgt-pY0owJDPtrRrj53cabeIXH9qCKWMQnoRk,1336
|
42
41
|
haiway/utils/queue.py,sha256=6v2u3pA6A44IuCCTOjmCt3yLyOcm7PCRnrIGo25j-1o,6402
|
43
42
|
haiway/utils/stream.py,sha256=lXaeveTY0-AYG5xVzcQYaiC6SUD5fUtHoMXiQcrQAAM,5723
|
44
|
-
haiway-0.
|
45
|
-
haiway-0.
|
46
|
-
haiway-0.
|
47
|
-
haiway-0.
|
43
|
+
haiway-0.26.0.dist-info/METADATA,sha256=IWQSIN2nqYPQETpjzyJYJ6dv2L--xrP-RG9J9qoQXoc,4919
|
44
|
+
haiway-0.26.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
45
|
+
haiway-0.26.0.dist-info/licenses/LICENSE,sha256=3phcpHVNBP8jsi77gOO0E7rgKeDeu99Pi7DSnK9YHoQ,1069
|
46
|
+
haiway-0.26.0.dist-info/RECORD,,
|
haiway/helpers/tracing.py
DELETED
@@ -1,185 +0,0 @@
|
|
1
|
-
from asyncio import iscoroutinefunction
|
2
|
-
from collections.abc import Callable, Coroutine
|
3
|
-
from typing import Any, cast, overload
|
4
|
-
|
5
|
-
from haiway.context import ctx
|
6
|
-
from haiway.context.observability import ObservabilityLevel
|
7
|
-
from haiway.types import MISSING
|
8
|
-
from haiway.utils import mimic_function
|
9
|
-
from haiway.utils.formatting import format_str
|
10
|
-
|
11
|
-
__all__ = ("traced",)
|
12
|
-
|
13
|
-
|
14
|
-
@overload
|
15
|
-
def traced[**Args, Result](
|
16
|
-
function: Callable[Args, Result],
|
17
|
-
/,
|
18
|
-
) -> Callable[Args, Result]: ...
|
19
|
-
|
20
|
-
|
21
|
-
@overload
|
22
|
-
def traced[**Args, Result](
|
23
|
-
*,
|
24
|
-
level: ObservabilityLevel = ObservabilityLevel.DEBUG,
|
25
|
-
label: str,
|
26
|
-
) -> Callable[[Callable[Args, Result]], Callable[Args, Result]]: ...
|
27
|
-
|
28
|
-
|
29
|
-
def traced[**Args, Result](
|
30
|
-
function: Callable[Args, Result] | None = None,
|
31
|
-
/,
|
32
|
-
*,
|
33
|
-
level: ObservabilityLevel = ObservabilityLevel.DEBUG,
|
34
|
-
label: str | None = None,
|
35
|
-
) -> Callable[[Callable[Args, Result]], Callable[Args, Result]] | Callable[Args, Result]:
|
36
|
-
"""
|
37
|
-
Decorator that adds tracing to functions, recording inputs, outputs, and exceptions.
|
38
|
-
|
39
|
-
Automatically records function arguments, return values, and any exceptions
|
40
|
-
within the current observability context. The recorded data can be used for
|
41
|
-
debugging, performance analysis, and understanding program execution flow.
|
42
|
-
|
43
|
-
In non-debug builds (when __debug__ is False), this decorator has no effect
|
44
|
-
and returns the original function to avoid performance impact in production.
|
45
|
-
|
46
|
-
Parameters
|
47
|
-
----------
|
48
|
-
function: Callable[Args, Result] | None
|
49
|
-
The function to be traced
|
50
|
-
level: ObservabilityLevel
|
51
|
-
The observability level at which to record trace information (default: DEBUG)
|
52
|
-
label: str | None
|
53
|
-
Custom label for the trace; defaults to the function name if not provided
|
54
|
-
|
55
|
-
Returns
|
56
|
-
-------
|
57
|
-
Callable
|
58
|
-
A decorated function that performs the same operation as the original
|
59
|
-
but with added tracing
|
60
|
-
|
61
|
-
Notes
|
62
|
-
-----
|
63
|
-
Works with both synchronous and asynchronous functions. For asynchronous
|
64
|
-
functions, properly awaits the result before recording it.
|
65
|
-
"""
|
66
|
-
|
67
|
-
def wrap(
|
68
|
-
wrapped: Callable[Args, Result],
|
69
|
-
) -> Callable[Args, Result]:
|
70
|
-
if __debug__:
|
71
|
-
if iscoroutinefunction(wrapped):
|
72
|
-
return cast(
|
73
|
-
Callable[Args, Result],
|
74
|
-
_traced_async(
|
75
|
-
wrapped,
|
76
|
-
label=label or wrapped.__name__,
|
77
|
-
level=level,
|
78
|
-
),
|
79
|
-
)
|
80
|
-
|
81
|
-
else:
|
82
|
-
return _traced_sync(
|
83
|
-
wrapped,
|
84
|
-
label=label or wrapped.__name__,
|
85
|
-
level=level,
|
86
|
-
)
|
87
|
-
|
88
|
-
else: # do not trace on non debug runs
|
89
|
-
return wrapped
|
90
|
-
|
91
|
-
if function := function:
|
92
|
-
return wrap(wrapped=function)
|
93
|
-
|
94
|
-
else:
|
95
|
-
return wrap
|
96
|
-
|
97
|
-
|
98
|
-
def _traced_sync[**Args, Result](
|
99
|
-
function: Callable[Args, Result],
|
100
|
-
/,
|
101
|
-
label: str,
|
102
|
-
level: ObservabilityLevel,
|
103
|
-
) -> Callable[Args, Result]:
|
104
|
-
def traced(
|
105
|
-
*args: Args.args,
|
106
|
-
**kwargs: Args.kwargs,
|
107
|
-
) -> Result:
|
108
|
-
with ctx.scope(label):
|
109
|
-
ctx.record(
|
110
|
-
level,
|
111
|
-
attributes={
|
112
|
-
f"[{idx}]": f"{arg}" for idx, arg in enumerate(args) if arg is not MISSING
|
113
|
-
},
|
114
|
-
)
|
115
|
-
ctx.record(
|
116
|
-
level,
|
117
|
-
attributes={key: f"{arg}" for key, arg in kwargs.items() if arg is not MISSING},
|
118
|
-
)
|
119
|
-
|
120
|
-
try:
|
121
|
-
result: Result = function(*args, **kwargs)
|
122
|
-
ctx.record(
|
123
|
-
level,
|
124
|
-
event="result",
|
125
|
-
attributes={"value": format_str(result)},
|
126
|
-
)
|
127
|
-
return result
|
128
|
-
|
129
|
-
except BaseException as exc:
|
130
|
-
ctx.record(
|
131
|
-
level,
|
132
|
-
event="result",
|
133
|
-
attributes={"error": f"{type(exc)}: {exc}"},
|
134
|
-
)
|
135
|
-
raise exc
|
136
|
-
|
137
|
-
return mimic_function(
|
138
|
-
function,
|
139
|
-
within=traced,
|
140
|
-
)
|
141
|
-
|
142
|
-
|
143
|
-
def _traced_async[**Args, Result](
|
144
|
-
function: Callable[Args, Coroutine[Any, Any, Result]],
|
145
|
-
/,
|
146
|
-
label: str,
|
147
|
-
level: ObservabilityLevel,
|
148
|
-
) -> Callable[Args, Coroutine[Any, Any, Result]]:
|
149
|
-
async def traced(
|
150
|
-
*args: Args.args,
|
151
|
-
**kwargs: Args.kwargs,
|
152
|
-
) -> Result:
|
153
|
-
with ctx.scope(label):
|
154
|
-
ctx.record(
|
155
|
-
level,
|
156
|
-
attributes={
|
157
|
-
f"[{idx}]": f"{arg}" for idx, arg in enumerate(args) if arg is not MISSING
|
158
|
-
},
|
159
|
-
)
|
160
|
-
ctx.record(
|
161
|
-
level,
|
162
|
-
attributes={key: f"{arg}" for key, arg in kwargs.items() if arg is not MISSING},
|
163
|
-
)
|
164
|
-
|
165
|
-
try:
|
166
|
-
result: Result = await function(*args, **kwargs)
|
167
|
-
ctx.record(
|
168
|
-
level,
|
169
|
-
event="result",
|
170
|
-
attributes={"value": format_str(result)},
|
171
|
-
)
|
172
|
-
return result
|
173
|
-
|
174
|
-
except BaseException as exc:
|
175
|
-
ctx.record(
|
176
|
-
level,
|
177
|
-
event="result",
|
178
|
-
attributes={"error": f"{type(exc)}: {exc}"},
|
179
|
-
)
|
180
|
-
raise exc
|
181
|
-
|
182
|
-
return mimic_function(
|
183
|
-
function,
|
184
|
-
within=traced,
|
185
|
-
)
|
File without changes
|
File without changes
|