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/__init__.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
from haiway.context import (
|
2
|
-
|
2
|
+
ContextPreset,
|
3
3
|
Disposable,
|
4
4
|
Disposables,
|
5
5
|
MissingContext,
|
@@ -25,7 +25,6 @@ from haiway.helpers import (
|
|
25
25
|
stream_concurrently,
|
26
26
|
throttle,
|
27
27
|
timeout,
|
28
|
-
traced,
|
29
28
|
)
|
30
29
|
from haiway.state import AttributePath, AttributeRequirement, Immutable, State
|
31
30
|
from haiway.types import (
|
@@ -65,7 +64,7 @@ __all__ = (
|
|
65
64
|
"AsyncStream",
|
66
65
|
"AttributePath",
|
67
66
|
"AttributeRequirement",
|
68
|
-
"
|
67
|
+
"ContextPreset",
|
69
68
|
"Default",
|
70
69
|
"DefaultValue",
|
71
70
|
"Disposable",
|
@@ -112,7 +111,6 @@ __all__ = (
|
|
112
111
|
"stream_concurrently",
|
113
112
|
"throttle",
|
114
113
|
"timeout",
|
115
|
-
"traced",
|
116
114
|
"unwrap_missing",
|
117
115
|
"without_missing",
|
118
116
|
)
|
haiway/context/__init__.py
CHANGED
@@ -14,13 +14,13 @@ from haiway.context.observability import (
|
|
14
14
|
ObservabilityScopeExiting,
|
15
15
|
ObservabilityTraceIdentifying,
|
16
16
|
)
|
17
|
-
from haiway.context.presets import
|
17
|
+
from haiway.context.presets import ContextPreset
|
18
18
|
from haiway.context.state import StateContext
|
19
19
|
from haiway.context.types import MissingContext, MissingState
|
20
20
|
from haiway.state import Immutable
|
21
21
|
|
22
22
|
__all__ = (
|
23
|
-
"
|
23
|
+
"ContextPreset",
|
24
24
|
"Disposable",
|
25
25
|
"Disposables",
|
26
26
|
"Immutable",
|
haiway/context/access.py
CHANGED
@@ -13,6 +13,7 @@ from collections.abc import (
|
|
13
13
|
Iterable,
|
14
14
|
Mapping,
|
15
15
|
)
|
16
|
+
from contextlib import AbstractAsyncContextManager, AbstractContextManager
|
16
17
|
from logging import Logger
|
17
18
|
from types import TracebackType
|
18
19
|
from typing import Any, final, overload
|
@@ -25,38 +26,28 @@ from haiway.context.observability import (
|
|
25
26
|
ObservabilityContext,
|
26
27
|
ObservabilityLevel,
|
27
28
|
)
|
29
|
+
|
30
|
+
# Import after other imports to avoid circular dependencies
|
28
31
|
from haiway.context.presets import (
|
29
|
-
|
30
|
-
|
31
|
-
ContextPresetsRegistryContext,
|
32
|
+
ContextPreset,
|
33
|
+
ContextPresetRegistryContext,
|
32
34
|
)
|
33
35
|
from haiway.context.state import ScopeState, StateContext
|
34
36
|
from haiway.context.tasks import TaskGroupContext
|
35
37
|
from haiway.state import Immutable, State
|
38
|
+
from haiway.utils.collections import as_list
|
36
39
|
from haiway.utils.stream import AsyncStream
|
37
40
|
|
38
41
|
__all__ = ("ctx",)
|
39
42
|
|
40
43
|
|
41
44
|
class ScopeContext(Immutable):
|
42
|
-
"""
|
43
|
-
Context manager for executing code within a defined scope.
|
44
|
-
|
45
|
-
ScopeContext manages scope-related data and behavior including identity, state,
|
46
|
-
observability, and task coordination. It enforces immutability and provides both
|
47
|
-
synchronous and asynchronous context management interfaces.
|
48
|
-
|
49
|
-
This class should not be instantiated directly; use the ctx.scope() factory method
|
50
|
-
to create scope contexts.
|
51
|
-
"""
|
52
|
-
|
53
45
|
_identifier: ScopeIdentifier
|
54
46
|
_state: Collection[State]
|
55
|
-
|
56
|
-
_resolved_state_context: StateContext | None
|
47
|
+
_state_context: StateContext | None
|
57
48
|
_disposables: Disposables | None
|
58
|
-
|
59
|
-
|
49
|
+
_preset: ContextPreset | None
|
50
|
+
_preset_disposables: Disposables | None
|
60
51
|
_observability_context: ObservabilityContext
|
61
52
|
_task_group_context: TaskGroupContext | None
|
62
53
|
|
@@ -65,6 +56,7 @@ class ScopeContext(Immutable):
|
|
65
56
|
name: str,
|
66
57
|
task_group: TaskGroup | None,
|
67
58
|
state: tuple[State, ...],
|
59
|
+
preset: ContextPreset | None,
|
68
60
|
disposables: Disposables | None,
|
69
61
|
observability: Observability | Logger | None,
|
70
62
|
) -> None:
|
@@ -79,16 +71,10 @@ class ScopeContext(Immutable):
|
|
79
71
|
"_state",
|
80
72
|
state,
|
81
73
|
)
|
82
|
-
# capture current contextual state (without new additions)
|
83
|
-
object.__setattr__(
|
84
|
-
self,
|
85
|
-
"_captured_state",
|
86
|
-
StateContext.current_state(),
|
87
|
-
)
|
88
74
|
# placeholder for temporary, resolved state context
|
89
75
|
object.__setattr__(
|
90
76
|
self,
|
91
|
-
"
|
77
|
+
"_state_context",
|
92
78
|
None,
|
93
79
|
)
|
94
80
|
object.__setattr__(
|
@@ -98,12 +84,12 @@ class ScopeContext(Immutable):
|
|
98
84
|
)
|
99
85
|
object.__setattr__(
|
100
86
|
self,
|
101
|
-
"
|
102
|
-
|
87
|
+
"_preset",
|
88
|
+
preset if preset is not None else ContextPresetRegistryContext.select(name),
|
103
89
|
)
|
104
90
|
object.__setattr__(
|
105
91
|
self,
|
106
|
-
"
|
92
|
+
"_preset_disposables",
|
107
93
|
None,
|
108
94
|
)
|
109
95
|
object.__setattr__(
|
@@ -123,80 +109,28 @@ class ScopeContext(Immutable):
|
|
123
109
|
else None,
|
124
110
|
)
|
125
111
|
|
126
|
-
def __enter__(self) -> str:
|
127
|
-
assert ( # nosec: B101
|
128
|
-
self._task_group_context is None or self._identifier.is_root
|
129
|
-
), "Can't enter synchronous context with task group"
|
130
|
-
assert self._disposables is None, "Can't enter synchronous context with disposables" # nosec: B101
|
131
|
-
assert self._resolved_state_context is None # nosec: B101
|
132
|
-
assert self._presets is None, "Can't enter synchronous context with presets" # nosec: B101
|
133
|
-
self._identifier.__enter__()
|
134
|
-
self._observability_context.__enter__()
|
135
|
-
# For sync context, only use explicit state (no presets or disposables allowed)
|
136
|
-
resolved_state_context: StateContext = StateContext(
|
137
|
-
_state=ScopeState((*self._captured_state, *self._state))
|
138
|
-
)
|
139
|
-
object.__setattr__(
|
140
|
-
self,
|
141
|
-
"_resolved_state_context",
|
142
|
-
resolved_state_context,
|
143
|
-
)
|
144
|
-
resolved_state_context.__enter__()
|
145
|
-
|
146
|
-
return str(self._observability_context.observability.trace_identifying(self._identifier))
|
147
|
-
|
148
|
-
def __exit__(
|
149
|
-
self,
|
150
|
-
exc_type: type[BaseException] | None,
|
151
|
-
exc_val: BaseException | None,
|
152
|
-
exc_tb: TracebackType | None,
|
153
|
-
) -> None:
|
154
|
-
assert self._resolved_state_context is not None # nosec: B101
|
155
|
-
self._resolved_state_context.__exit__(
|
156
|
-
exc_type=exc_type,
|
157
|
-
exc_val=exc_val,
|
158
|
-
exc_tb=exc_tb,
|
159
|
-
)
|
160
|
-
object.__setattr__(
|
161
|
-
self,
|
162
|
-
"_resolved_state_context",
|
163
|
-
None,
|
164
|
-
)
|
165
|
-
self._observability_context.__exit__(
|
166
|
-
exc_type=exc_type,
|
167
|
-
exc_val=exc_val,
|
168
|
-
exc_tb=exc_tb,
|
169
|
-
)
|
170
|
-
self._identifier.__exit__(
|
171
|
-
exc_type=exc_type,
|
172
|
-
exc_val=exc_val,
|
173
|
-
exc_tb=exc_tb,
|
174
|
-
)
|
175
|
-
|
176
112
|
async def __aenter__(self) -> str:
|
177
|
-
assert self.
|
178
|
-
assert self.
|
113
|
+
assert self._preset_disposables is None # nosec: B101
|
114
|
+
assert self._state_context is None # nosec: B101
|
179
115
|
self._identifier.__enter__()
|
180
116
|
self._observability_context.__enter__()
|
181
117
|
|
182
|
-
if
|
183
|
-
await
|
118
|
+
if self._task_group_context is not None:
|
119
|
+
await self._task_group_context.__aenter__()
|
184
120
|
|
185
121
|
# Collect all state sources in priority order (lowest to highest priority)
|
186
|
-
collected_state: list[State] = []
|
187
|
-
|
188
122
|
# 1. Add contextual state first (lowest priority)
|
189
|
-
collected_state.
|
123
|
+
collected_state: list[State] = as_list(StateContext.current_state())
|
190
124
|
|
191
125
|
# 2. Add preset state (low priority, overrides contextual)
|
192
|
-
if self.
|
193
|
-
|
126
|
+
if self._preset is not None:
|
127
|
+
preset_disposables: Disposables = await self._preset.prepare()
|
194
128
|
object.__setattr__(
|
195
129
|
self,
|
196
|
-
"
|
197
|
-
|
130
|
+
"_preset_disposables",
|
131
|
+
preset_disposables,
|
198
132
|
)
|
199
|
-
collected_state.extend(await
|
133
|
+
collected_state.extend(await preset_disposables.prepare())
|
200
134
|
|
201
135
|
# 3. Add explicit disposables state (medium priority)
|
202
136
|
if self._disposables is not None:
|
@@ -212,7 +146,7 @@ class ScopeContext(Immutable):
|
|
212
146
|
resolved_state_context.__enter__()
|
213
147
|
object.__setattr__(
|
214
148
|
self,
|
215
|
-
"
|
149
|
+
"_state_context",
|
216
150
|
resolved_state_context,
|
217
151
|
)
|
218
152
|
|
@@ -224,7 +158,7 @@ class ScopeContext(Immutable):
|
|
224
158
|
exc_val: BaseException | None,
|
225
159
|
exc_tb: TracebackType | None,
|
226
160
|
) -> None:
|
227
|
-
assert self.
|
161
|
+
assert self._state_context is not None # nosec: B101
|
228
162
|
if self._disposables is not None:
|
229
163
|
await self._disposables.dispose(
|
230
164
|
exc_type=exc_type,
|
@@ -232,42 +166,40 @@ class ScopeContext(Immutable):
|
|
232
166
|
exc_tb=exc_tb,
|
233
167
|
)
|
234
168
|
|
235
|
-
if self.
|
236
|
-
await self.
|
169
|
+
if self._preset_disposables is not None:
|
170
|
+
await self._preset_disposables.dispose(
|
237
171
|
exc_type=exc_type,
|
238
172
|
exc_val=exc_val,
|
239
173
|
exc_tb=exc_tb,
|
240
174
|
)
|
241
175
|
object.__setattr__(
|
242
176
|
self,
|
243
|
-
"
|
177
|
+
"_preset_disposables",
|
244
178
|
None,
|
245
179
|
)
|
246
180
|
|
247
|
-
if
|
248
|
-
await
|
181
|
+
if self._task_group_context is not None:
|
182
|
+
await self._task_group_context.__aexit__(
|
249
183
|
exc_type=exc_type,
|
250
184
|
exc_val=exc_val,
|
251
185
|
exc_tb=exc_tb,
|
252
186
|
)
|
253
187
|
|
254
|
-
self.
|
188
|
+
self._state_context.__exit__(
|
255
189
|
exc_type=exc_type,
|
256
190
|
exc_val=exc_val,
|
257
191
|
exc_tb=exc_tb,
|
258
192
|
)
|
259
193
|
object.__setattr__(
|
260
194
|
self,
|
261
|
-
"
|
195
|
+
"_state_context",
|
262
196
|
None,
|
263
197
|
)
|
264
|
-
|
265
198
|
self._observability_context.__exit__(
|
266
199
|
exc_type=exc_type,
|
267
200
|
exc_val=exc_val,
|
268
201
|
exc_tb=exc_tb,
|
269
202
|
)
|
270
|
-
|
271
203
|
self._identifier.__exit__(
|
272
204
|
exc_type=exc_type,
|
273
205
|
exc_val=exc_val,
|
@@ -275,6 +207,59 @@ class ScopeContext(Immutable):
|
|
275
207
|
)
|
276
208
|
|
277
209
|
|
210
|
+
class DisposablesContext(Immutable):
|
211
|
+
_disposables: Disposables
|
212
|
+
_state_context: StateContext | None
|
213
|
+
|
214
|
+
def __init__(
|
215
|
+
self,
|
216
|
+
disposables: Disposables,
|
217
|
+
) -> None:
|
218
|
+
object.__setattr__(
|
219
|
+
self,
|
220
|
+
"_disposables",
|
221
|
+
disposables,
|
222
|
+
)
|
223
|
+
object.__setattr__(
|
224
|
+
self,
|
225
|
+
"_state_context",
|
226
|
+
None,
|
227
|
+
)
|
228
|
+
|
229
|
+
async def __aenter__(self) -> None:
|
230
|
+
assert self._state_context is None # nosec: B101
|
231
|
+
state_context: StateContext = StateContext.updated(await self._disposables.prepare())
|
232
|
+
state_context.__enter__()
|
233
|
+
object.__setattr__(
|
234
|
+
self,
|
235
|
+
"_state_context",
|
236
|
+
state_context,
|
237
|
+
)
|
238
|
+
|
239
|
+
async def __aexit__(
|
240
|
+
self,
|
241
|
+
exc_type: type[BaseException] | None,
|
242
|
+
exc_val: BaseException | None,
|
243
|
+
exc_tb: TracebackType | None,
|
244
|
+
) -> None:
|
245
|
+
assert self._state_context is not None # nosec: B101
|
246
|
+
await self._disposables.dispose(
|
247
|
+
exc_type=exc_type,
|
248
|
+
exc_val=exc_val,
|
249
|
+
exc_tb=exc_tb,
|
250
|
+
)
|
251
|
+
self._state_context.__exit__(
|
252
|
+
exc_type=exc_type,
|
253
|
+
exc_val=exc_val,
|
254
|
+
exc_tb=exc_tb,
|
255
|
+
)
|
256
|
+
object.__setattr__(
|
257
|
+
self,
|
258
|
+
"_state_context",
|
259
|
+
None,
|
260
|
+
)
|
261
|
+
|
262
|
+
|
278
263
|
@final
|
279
264
|
class ctx:
|
280
265
|
"""
|
@@ -318,8 +303,8 @@ class ctx:
|
|
318
303
|
|
319
304
|
@staticmethod
|
320
305
|
def presets(
|
321
|
-
*presets:
|
322
|
-
) ->
|
306
|
+
*presets: ContextPreset,
|
307
|
+
) -> AbstractContextManager[None]:
|
323
308
|
"""
|
324
309
|
Create a context manager for a preset registry.
|
325
310
|
|
@@ -331,15 +316,19 @@ class ctx:
|
|
331
316
|
for use with ctx.scope(). The presets are looked up by their name when
|
332
317
|
creating scopes.
|
333
318
|
|
319
|
+
Note: For single preset usage, consider passing the preset directly to
|
320
|
+
ctx.scope() using the preset parameter instead of using this registry.
|
321
|
+
Presets only work with async contexts.
|
322
|
+
|
334
323
|
Parameters
|
335
324
|
----------
|
336
|
-
*presets:
|
325
|
+
*presets: ContextPreset
|
337
326
|
Variable number of preset configurations to register. Each preset
|
338
327
|
must have a unique name within the registry.
|
339
328
|
|
340
329
|
Returns
|
341
330
|
-------
|
342
|
-
|
331
|
+
AbstractContextManager[None]
|
343
332
|
A context manager that makes the presets available in nested scopes
|
344
333
|
|
345
334
|
Examples
|
@@ -347,19 +336,19 @@ class ctx:
|
|
347
336
|
Basic preset usage:
|
348
337
|
|
349
338
|
>>> from haiway import ctx, State
|
350
|
-
>>> from haiway.context import
|
339
|
+
>>> from haiway.context import ContextPreset
|
351
340
|
>>>
|
352
341
|
>>> class ApiConfig(State):
|
353
342
|
... base_url: str
|
354
343
|
... timeout: int = 30
|
355
344
|
>>>
|
356
345
|
>>> # Define presets
|
357
|
-
>>> dev_preset =
|
346
|
+
>>> dev_preset = ContextPreset(
|
358
347
|
... name="development",
|
359
348
|
... _state=[ApiConfig(base_url="https://dev-api.example.com")]
|
360
349
|
... )
|
361
350
|
>>>
|
362
|
-
>>> prod_preset =
|
351
|
+
>>> prod_preset = ContextPreset(
|
363
352
|
... name="production",
|
364
353
|
... _state=[ApiConfig(base_url="https://api.example.com", timeout=60)]
|
365
354
|
... )
|
@@ -373,7 +362,7 @@ class ctx:
|
|
373
362
|
Nested preset registries:
|
374
363
|
|
375
364
|
>>> base_presets = [dev_preset, prod_preset]
|
376
|
-
>>> override_preset =
|
365
|
+
>>> override_preset = ContextPreset(
|
377
366
|
... name="development",
|
378
367
|
... _state=[ApiConfig(base_url="https://staging.example.com")]
|
379
368
|
... )
|
@@ -388,27 +377,29 @@ class ctx:
|
|
388
377
|
|
389
378
|
See Also
|
390
379
|
--------
|
391
|
-
|
380
|
+
ContextPreset : For creating individual preset configurations
|
392
381
|
ctx.scope : For creating scopes that can use presets
|
393
382
|
"""
|
394
|
-
return
|
395
|
-
registry=ContextPresetsRegistry(
|
396
|
-
presets=presets,
|
397
|
-
),
|
398
|
-
)
|
383
|
+
return ContextPresetRegistryContext(presets=presets)
|
399
384
|
|
400
385
|
@staticmethod
|
401
386
|
def scope(
|
402
387
|
name: str,
|
403
388
|
/,
|
404
389
|
*state: State | None,
|
390
|
+
preset: ContextPreset | None = None,
|
405
391
|
disposables: Disposables | Iterable[Disposable] | None = None,
|
406
392
|
task_group: TaskGroup | None = None,
|
407
393
|
observability: Observability | Logger | None = None,
|
408
|
-
) ->
|
394
|
+
) -> AbstractAsyncContextManager[str]:
|
409
395
|
"""
|
410
|
-
Prepare scope context with given parameters.
|
411
|
-
|
396
|
+
Prepare scope context with given parameters.
|
397
|
+
|
398
|
+
When called within an existing context, it becomes nested with current context
|
399
|
+
as its parent.
|
400
|
+
|
401
|
+
Note: Presets can only be used with async contexts. Synchronous contexts
|
402
|
+
do not support preset functionality.
|
412
403
|
|
413
404
|
State Priority System
|
414
405
|
---------------------
|
@@ -428,28 +419,71 @@ class ctx:
|
|
428
419
|
name of the scope context, can be associated with state presets
|
429
420
|
|
430
421
|
*state: State | None
|
431
|
-
state propagated within the scope context, will be merged with current state by
|
432
|
-
|
422
|
+
state propagated within the scope context, will be merged with current state by
|
423
|
+
replacing current with provided on conflict.
|
424
|
+
|
425
|
+
preset: ContextPreset | None = None
|
426
|
+
context preset to be used within the scope context. The preset's state and
|
427
|
+
disposables will be applied to the scope with lower priority than explicit state.
|
428
|
+
Only works with async contexts.
|
433
429
|
|
434
430
|
disposables: Disposables | Iterable[Disposable] | None
|
435
|
-
disposables consumed within the context when entered. Produced state will automatically
|
436
|
-
|
437
|
-
|
431
|
+
disposables consumed within the context when entered. Produced state will automatically
|
432
|
+
be added to the scope state. Using asynchronous context is required if any disposables
|
433
|
+
were provided.
|
438
434
|
|
439
435
|
task_group: TaskGroup | None
|
440
436
|
task group used for spawning and joining tasks within the context. Root scope will
|
441
|
-
|
437
|
+
always have task group created even when not set.
|
442
438
|
|
443
439
|
observability: Observability | Logger | None = None
|
444
|
-
observability solution responsible for recording and storing metrics, logs and events
|
445
|
-
|
440
|
+
observability solution responsible for recording and storing metrics, logs and events.
|
441
|
+
Assigning observability within existing context will result in an error.
|
446
442
|
When not provided, logger with the scope name will be requested and used.
|
447
443
|
|
448
444
|
Returns
|
449
445
|
-------
|
450
|
-
|
451
|
-
context object intended to enter
|
452
|
-
|
446
|
+
AbstractAsyncContextManager[str]
|
447
|
+
context manager object intended to enter the scope with.
|
448
|
+
context manager will provide trace_id of current scope.
|
449
|
+
|
450
|
+
Examples
|
451
|
+
--------
|
452
|
+
Using a preset directly:
|
453
|
+
|
454
|
+
>>> from haiway import ctx, State
|
455
|
+
>>> from haiway.context import ContextPreset
|
456
|
+
>>>
|
457
|
+
>>> class ApiConfig(State):
|
458
|
+
... base_url: str
|
459
|
+
... timeout: int = 30
|
460
|
+
>>>
|
461
|
+
>>> api_preset = ContextPreset(
|
462
|
+
... name="api",
|
463
|
+
... state=[ApiConfig(base_url="https://api.example.com")]
|
464
|
+
... )
|
465
|
+
>>>
|
466
|
+
>>> # Direct preset usage
|
467
|
+
>>> async with ctx.scope("main", preset=api_preset):
|
468
|
+
... config = ctx.state(ApiConfig)
|
469
|
+
... # Uses preset configuration
|
470
|
+
>>>
|
471
|
+
>>> # Override preset state with explicit state
|
472
|
+
>>> async with ctx.scope("main", ApiConfig(timeout=60), preset=api_preset):
|
473
|
+
... config = ctx.state(ApiConfig)
|
474
|
+
... # base_url from preset, timeout overridden to 60
|
475
|
+
|
476
|
+
Using preset registry (original approach):
|
477
|
+
|
478
|
+
>>> # Multiple presets registered
|
479
|
+
>>> with ctx.presets(dev_preset, prod_preset):
|
480
|
+
... async with ctx.scope("development"): # Matches dev_preset by name
|
481
|
+
... config = ctx.state(ApiConfig)
|
482
|
+
|
483
|
+
See Also
|
484
|
+
--------
|
485
|
+
ctx.presets : For registering multiple presets by name
|
486
|
+
ContextPreset : For creating preset configurations
|
453
487
|
"""
|
454
488
|
|
455
489
|
resolved_disposables: Disposables | None
|
@@ -467,6 +501,7 @@ class ctx:
|
|
467
501
|
name=name,
|
468
502
|
task_group=task_group,
|
469
503
|
state=tuple(element for element in state if element is not None),
|
504
|
+
preset=preset,
|
470
505
|
disposables=resolved_disposables,
|
471
506
|
observability=observability,
|
472
507
|
)
|
@@ -474,21 +509,23 @@ class ctx:
|
|
474
509
|
@staticmethod
|
475
510
|
def updated(
|
476
511
|
*state: State | None,
|
477
|
-
) ->
|
512
|
+
) -> AbstractContextManager[None]:
|
478
513
|
"""
|
479
|
-
Update scope context with given state.
|
480
|
-
|
514
|
+
Update scope context with given state.
|
515
|
+
|
516
|
+
When called within an existing context, it becomes nested with current
|
517
|
+
context as its predecessor.
|
481
518
|
|
482
519
|
Parameters
|
483
520
|
----------
|
484
521
|
*state: State | None
|
485
|
-
state propagated within the updated scope context, will be merged with current if any
|
486
|
-
|
522
|
+
state propagated within the updated scope context, will be merged with current if any
|
523
|
+
by replacing current with provided on conflict
|
487
524
|
|
488
525
|
Returns
|
489
526
|
-------
|
490
|
-
|
491
|
-
|
527
|
+
AbstractContextManager[None]
|
528
|
+
context manager object intended to enter updated state context with it
|
492
529
|
"""
|
493
530
|
|
494
531
|
return StateContext.updated(element for element in state if element is not None)
|
@@ -496,7 +533,7 @@ class ctx:
|
|
496
533
|
@staticmethod
|
497
534
|
def disposables(
|
498
535
|
*disposables: Disposable | None,
|
499
|
-
) ->
|
536
|
+
) -> AbstractAsyncContextManager[None]:
|
500
537
|
"""
|
501
538
|
Create a container for managing multiple disposable resources.
|
502
539
|
|
@@ -513,9 +550,9 @@ class ctx:
|
|
513
550
|
|
514
551
|
Returns
|
515
552
|
-------
|
516
|
-
|
517
|
-
A
|
518
|
-
and propagates their state to the context
|
553
|
+
AbstractAsyncContextManager[None]
|
554
|
+
A context manager that manages the lifecycle of all provided disposables
|
555
|
+
and propagates their state to the context, similar to ctx.scope()
|
519
556
|
|
520
557
|
Examples
|
521
558
|
--------
|
@@ -524,16 +561,19 @@ class ctx:
|
|
524
561
|
>>> from haiway import ctx
|
525
562
|
>>> async def main():
|
526
563
|
...
|
527
|
-
... async with ctx.
|
528
|
-
...
|
529
|
-
... disposables=(database_connection(),)
|
564
|
+
... async with ctx.disposables(
|
565
|
+
... database_connection(),
|
530
566
|
... ):
|
531
567
|
... # ConnectionState is now available in context
|
532
568
|
... conn_state = ctx.state(ConnectionState)
|
533
569
|
... await conn_state.connection.execute("SELECT 1")
|
534
570
|
"""
|
535
571
|
|
536
|
-
return
|
572
|
+
return DisposablesContext(
|
573
|
+
disposables=Disposables(
|
574
|
+
*(disposable for disposable in disposables if disposable is not None)
|
575
|
+
)
|
576
|
+
)
|
537
577
|
|
538
578
|
@staticmethod
|
539
579
|
def spawn[Result, **Arguments](
|
@@ -543,8 +583,9 @@ class ctx:
|
|
543
583
|
**kwargs: Arguments.kwargs,
|
544
584
|
) -> Task[Result]:
|
545
585
|
"""
|
546
|
-
Spawn an async task within current scope context task group.
|
547
|
-
|
586
|
+
Spawn an async task within current scope context task group.
|
587
|
+
|
588
|
+
When called outside of context, it will spawn detached task instead.
|
548
589
|
|
549
590
|
Parameters
|
550
591
|
----------
|
@@ -593,7 +634,7 @@ class ctx:
|
|
593
634
|
"""
|
594
635
|
|
595
636
|
output_stream = AsyncStream[Element]()
|
596
|
-
stream_scope:
|
637
|
+
stream_scope: AbstractAsyncContextManager[str] = ctx.scope("stream")
|
597
638
|
|
598
639
|
async def stream() -> None:
|
599
640
|
async with stream_scope:
|
@@ -754,8 +795,9 @@ class ctx:
|
|
754
795
|
**extra: Any,
|
755
796
|
) -> None:
|
756
797
|
"""
|
757
|
-
Log using ERROR level within current scope context.
|
758
|
-
|
798
|
+
Log using ERROR level within current scope context.
|
799
|
+
|
800
|
+
When there is no current scope, root logger will be used without additional details.
|
759
801
|
|
760
802
|
Parameters
|
761
803
|
----------
|
@@ -790,8 +832,9 @@ class ctx:
|
|
790
832
|
**extra: Any,
|
791
833
|
) -> None:
|
792
834
|
"""
|
793
|
-
Log using WARNING level within current scope context.
|
794
|
-
|
835
|
+
Log using WARNING level within current scope context.
|
836
|
+
|
837
|
+
When there is no current scope, root logger will be used without additional details.
|
795
838
|
|
796
839
|
Parameters
|
797
840
|
----------
|
@@ -825,8 +868,9 @@ class ctx:
|
|
825
868
|
**extra: Any,
|
826
869
|
) -> None:
|
827
870
|
"""
|
828
|
-
Log using INFO level within current scope context.
|
829
|
-
|
871
|
+
Log using INFO level within current scope context.
|
872
|
+
|
873
|
+
When there is no current scope, root logger will be used without additional details.
|
830
874
|
|
831
875
|
Parameters
|
832
876
|
----------
|
@@ -858,8 +902,9 @@ class ctx:
|
|
858
902
|
**extra: Any,
|
859
903
|
) -> None:
|
860
904
|
"""
|
861
|
-
Log using DEBUG level within current scope context.
|
862
|
-
|
905
|
+
Log using DEBUG level within current scope context.
|
906
|
+
|
907
|
+
When there is no current scope, root logger will be used without additional details.
|
863
908
|
|
864
909
|
Parameters
|
865
910
|
----------
|