haiway 0.13.1__py3-none-any.whl → 0.14.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 -0
- haiway/context/access.py +26 -44
- haiway/context/state.py +1 -1
- haiway/helpers/metrics.py +46 -24
- haiway/utils/__init__.py +2 -0
- haiway/utils/stream.py +97 -0
- {haiway-0.13.1.dist-info → haiway-0.14.0.dist-info}/METADATA +1 -1
- {haiway-0.13.1.dist-info → haiway-0.14.0.dist-info}/RECORD +10 -9
- {haiway-0.13.1.dist-info → haiway-0.14.0.dist-info}/WHEEL +0 -0
- {haiway-0.13.1.dist-info → haiway-0.14.0.dist-info}/licenses/LICENSE +0 -0
haiway/__init__.py
CHANGED
@@ -40,6 +40,7 @@ from haiway.types import (
|
|
40
40
|
)
|
41
41
|
from haiway.utils import (
|
42
42
|
AsyncQueue,
|
43
|
+
AsyncStream,
|
43
44
|
always,
|
44
45
|
as_dict,
|
45
46
|
as_list,
|
@@ -63,6 +64,7 @@ __all__ = [
|
|
63
64
|
"MISSING",
|
64
65
|
"ArgumentsTrace",
|
65
66
|
"AsyncQueue",
|
67
|
+
"AsyncStream",
|
66
68
|
"AttributePath",
|
67
69
|
"AttributeRequirement",
|
68
70
|
"Default",
|
haiway/context/access.py
CHANGED
@@ -11,7 +11,6 @@ from collections.abc import (
|
|
11
11
|
Coroutine,
|
12
12
|
Iterable,
|
13
13
|
)
|
14
|
-
from contextvars import Context, copy_context
|
15
14
|
from logging import Logger
|
16
15
|
from types import TracebackType
|
17
16
|
from typing import Any, final, overload
|
@@ -24,6 +23,7 @@ from haiway.context.state import StateContext
|
|
24
23
|
from haiway.context.tasks import TaskGroupContext
|
25
24
|
from haiway.state import State
|
26
25
|
from haiway.utils import mimic_function
|
26
|
+
from haiway.utils.stream import AsyncStream
|
27
27
|
|
28
28
|
__all__ = [
|
29
29
|
"ctx",
|
@@ -37,7 +37,6 @@ class ScopeContext:
|
|
37
37
|
"_identifier",
|
38
38
|
"_logger_context",
|
39
39
|
"_metrics_context",
|
40
|
-
"_state",
|
41
40
|
"_state_context",
|
42
41
|
"_task_group_context",
|
43
42
|
)
|
@@ -67,13 +66,12 @@ class ScopeContext:
|
|
67
66
|
)
|
68
67
|
# postponing task group creation to include only when needed
|
69
68
|
self._task_group_context: TaskGroupContext
|
70
|
-
#
|
69
|
+
# prepare state context to capture current state
|
71
70
|
self._state_context: StateContext
|
72
|
-
self._state: tuple[State, ...]
|
73
71
|
object.__setattr__(
|
74
72
|
self,
|
75
|
-
"
|
76
|
-
state,
|
73
|
+
"_state_context",
|
74
|
+
StateContext.updated(state),
|
77
75
|
)
|
78
76
|
self._disposables: Disposables | None
|
79
77
|
object.__setattr__(
|
@@ -115,12 +113,6 @@ class ScopeContext:
|
|
115
113
|
assert self._disposables is None, "Can't enter synchronous context with disposables" # nosec: B101
|
116
114
|
self._identifier.__enter__()
|
117
115
|
self._logger_context.__enter__()
|
118
|
-
# lazily initialize state
|
119
|
-
object.__setattr__(
|
120
|
-
self,
|
121
|
-
"_state_context",
|
122
|
-
StateContext.updated(self._state),
|
123
|
-
)
|
124
116
|
self._state_context.__enter__()
|
125
117
|
self._metrics_context.__enter__()
|
126
118
|
|
@@ -169,24 +161,17 @@ class ScopeContext:
|
|
169
161
|
|
170
162
|
# lazily initialize state to include disposables results
|
171
163
|
if self._disposables is not None:
|
164
|
+
assert self._state_context._token is None # nosec: B101
|
172
165
|
object.__setattr__(
|
173
166
|
self,
|
174
167
|
"_state_context",
|
175
|
-
StateContext
|
176
|
-
(
|
177
|
-
|
178
|
-
|
179
|
-
)
|
168
|
+
StateContext(
|
169
|
+
state=self._state_context._state.updated(
|
170
|
+
await self._disposables.__aenter__(),
|
171
|
+
),
|
180
172
|
),
|
181
173
|
)
|
182
174
|
|
183
|
-
else:
|
184
|
-
object.__setattr__(
|
185
|
-
self,
|
186
|
-
"_state_context",
|
187
|
-
StateContext.updated(self._state),
|
188
|
-
)
|
189
|
-
|
190
175
|
self._state_context.__enter__()
|
191
176
|
self._metrics_context.__enter__()
|
192
177
|
|
@@ -401,12 +386,12 @@ class ctx:
|
|
401
386
|
return TaskGroupContext.run(function, *args, **kwargs)
|
402
387
|
|
403
388
|
@staticmethod
|
404
|
-
def stream[
|
405
|
-
source: Callable[Arguments, AsyncGenerator[
|
389
|
+
def stream[Element, **Arguments](
|
390
|
+
source: Callable[Arguments, AsyncGenerator[Element, None]],
|
406
391
|
/,
|
407
392
|
*args: Arguments.args,
|
408
393
|
**kwargs: Arguments.kwargs,
|
409
|
-
) -> AsyncIterator[
|
394
|
+
) -> AsyncIterator[Element]:
|
410
395
|
"""
|
411
396
|
Stream results produced by a generator within the proper context state.
|
412
397
|
|
@@ -427,25 +412,22 @@ class ctx:
|
|
427
412
|
iterator for accessing generated results
|
428
413
|
"""
|
429
414
|
|
430
|
-
|
431
|
-
context_snapshot: Context = copy_context()
|
432
|
-
|
433
|
-
# prepare nested context
|
434
|
-
streaming_context: ScopeContext = ctx.scope(
|
435
|
-
getattr(
|
436
|
-
source,
|
437
|
-
"__name__",
|
438
|
-
"streaming",
|
439
|
-
)
|
440
|
-
)
|
415
|
+
output_stream = AsyncStream[Element]()
|
441
416
|
|
442
|
-
|
443
|
-
|
417
|
+
@ctx.scope("stream")
|
418
|
+
async def stream() -> None:
|
419
|
+
try:
|
444
420
|
async for result in source(*args, **kwargs):
|
445
|
-
|
421
|
+
await output_stream.send(result)
|
422
|
+
|
423
|
+
except BaseException as exc:
|
424
|
+
output_stream.finish(exception=exc)
|
425
|
+
|
426
|
+
else:
|
427
|
+
output_stream.finish()
|
446
428
|
|
447
|
-
|
448
|
-
return
|
429
|
+
TaskGroupContext.run(stream)
|
430
|
+
return output_stream
|
449
431
|
|
450
432
|
@staticmethod
|
451
433
|
def check_cancellation() -> None:
|
@@ -488,7 +470,7 @@ class ctx:
|
|
488
470
|
StateType
|
489
471
|
resolved state instance
|
490
472
|
"""
|
491
|
-
return StateContext.
|
473
|
+
return StateContext.state(
|
492
474
|
state,
|
493
475
|
default=default,
|
494
476
|
)
|
haiway/context/state.py
CHANGED
haiway/helpers/metrics.py
CHANGED
@@ -15,6 +15,7 @@ __all_ = [
|
|
15
15
|
|
16
16
|
class MetricsScopeStore:
|
17
17
|
__slots__ = (
|
18
|
+
"allow_exit",
|
18
19
|
"entered",
|
19
20
|
"exited",
|
20
21
|
"identifier",
|
@@ -31,6 +32,7 @@ class MetricsScopeStore:
|
|
31
32
|
self.entered: float = monotonic()
|
32
33
|
self.metrics: dict[type[State], State] = {}
|
33
34
|
self.exited: float | None = None
|
35
|
+
self.allow_exit: bool = False
|
34
36
|
self.nested: list[MetricsScopeStore] = []
|
35
37
|
|
36
38
|
@property
|
@@ -115,7 +117,7 @@ class MetricsHolder:
|
|
115
117
|
|
116
118
|
def __init__(self) -> None:
|
117
119
|
self.root_scope: ScopeIdentifier | None = None
|
118
|
-
self.scopes: dict[
|
120
|
+
self.scopes: dict[str, MetricsScopeStore] = {}
|
119
121
|
|
120
122
|
def record(
|
121
123
|
self,
|
@@ -124,10 +126,10 @@ class MetricsHolder:
|
|
124
126
|
metric: State,
|
125
127
|
) -> None:
|
126
128
|
assert self.root_scope is not None # nosec: B101
|
127
|
-
assert scope in self.scopes # nosec: B101
|
129
|
+
assert scope.scope_id in self.scopes # nosec: B101
|
128
130
|
|
129
131
|
metric_type: type[State] = type(metric)
|
130
|
-
metrics: dict[type[State], State] = self.scopes[scope].metrics
|
132
|
+
metrics: dict[type[State], State] = self.scopes[scope.scope_id].metrics
|
131
133
|
if (current := metrics.get(metric_type)) and hasattr(current, "__add__"):
|
132
134
|
metrics[type(metric)] = current.__add__(metric) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
|
133
135
|
|
@@ -142,29 +144,29 @@ class MetricsHolder:
|
|
142
144
|
merged: bool,
|
143
145
|
) -> Metric | None:
|
144
146
|
assert self.root_scope is not None # nosec: B101
|
145
|
-
assert scope in self.scopes # nosec: B101
|
147
|
+
assert scope.scope_id in self.scopes # nosec: B101
|
146
148
|
|
147
149
|
if merged:
|
148
|
-
return self.scopes[scope].merged(metric)
|
150
|
+
return self.scopes[scope.scope_id].merged(metric)
|
149
151
|
|
150
152
|
else:
|
151
|
-
return cast(Metric | None, self.scopes[scope].metrics.get(metric))
|
153
|
+
return cast(Metric | None, self.scopes[scope.scope_id].metrics.get(metric))
|
152
154
|
|
153
155
|
def enter_scope[Metric: State](
|
154
156
|
self,
|
155
157
|
scope: ScopeIdentifier,
|
156
158
|
/,
|
157
159
|
) -> None:
|
158
|
-
assert scope not in self.scopes # nosec: B101
|
160
|
+
assert scope.scope_id not in self.scopes # nosec: B101
|
159
161
|
scope_metrics = MetricsScopeStore(scope)
|
160
|
-
self.scopes[scope] = scope_metrics
|
162
|
+
self.scopes[scope.scope_id] = scope_metrics
|
161
163
|
|
162
164
|
if self.root_scope is None:
|
163
165
|
self.root_scope = scope
|
164
166
|
|
165
167
|
else:
|
166
168
|
for key in self.scopes.keys():
|
167
|
-
if key
|
169
|
+
if key == scope.parent_id:
|
168
170
|
self.scopes[key].nested.append(scope_metrics)
|
169
171
|
return
|
170
172
|
|
@@ -177,8 +179,18 @@ class MetricsHolder:
|
|
177
179
|
scope: ScopeIdentifier,
|
178
180
|
/,
|
179
181
|
) -> None:
|
180
|
-
assert
|
181
|
-
self.scopes
|
182
|
+
assert self.root_scope is not None # nosec: B101
|
183
|
+
assert scope.scope_id in self.scopes # nosec: B101
|
184
|
+
|
185
|
+
self.scopes[scope.scope_id].allow_exit = True
|
186
|
+
|
187
|
+
if not all(nested.exited for nested in self.scopes[scope.scope_id].nested):
|
188
|
+
return # not completed yet
|
189
|
+
|
190
|
+
self.scopes[scope.scope_id].exited = monotonic()
|
191
|
+
|
192
|
+
if scope != self.root_scope and self.scopes[scope.parent_id].allow_exit:
|
193
|
+
self.exit_scope(self.scopes[scope.parent_id].identifier)
|
182
194
|
|
183
195
|
|
184
196
|
@final
|
@@ -213,7 +225,7 @@ class MetricsLogger:
|
|
213
225
|
redact_content: bool,
|
214
226
|
) -> None:
|
215
227
|
self.root_scope: ScopeIdentifier | None = None
|
216
|
-
self.scopes: dict[
|
228
|
+
self.scopes: dict[str, MetricsScopeStore] = {}
|
217
229
|
self.items_limit: int | None = items_limit
|
218
230
|
self.redact_content: bool = redact_content
|
219
231
|
|
@@ -224,10 +236,10 @@ class MetricsLogger:
|
|
224
236
|
metric: State,
|
225
237
|
) -> None:
|
226
238
|
assert self.root_scope is not None # nosec: B101
|
227
|
-
assert scope in self.scopes # nosec: B101
|
239
|
+
assert scope.scope_id in self.scopes # nosec: B101
|
228
240
|
|
229
241
|
metric_type: type[State] = type(metric)
|
230
|
-
metrics: dict[type[State], State] = self.scopes[scope].metrics
|
242
|
+
metrics: dict[type[State], State] = self.scopes[scope.scope_id].metrics
|
231
243
|
if (current := metrics.get(metric_type)) and hasattr(current, "__add__"):
|
232
244
|
metrics[type(metric)] = current.__add__(metric) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
|
233
245
|
|
@@ -248,29 +260,29 @@ class MetricsLogger:
|
|
248
260
|
merged: bool,
|
249
261
|
) -> Metric | None:
|
250
262
|
assert self.root_scope is not None # nosec: B101
|
251
|
-
assert scope in self.scopes # nosec: B101
|
263
|
+
assert scope.scope_id in self.scopes # nosec: B101
|
252
264
|
|
253
265
|
if merged:
|
254
|
-
return self.scopes[scope].merged(metric)
|
266
|
+
return self.scopes[scope.scope_id].merged(metric)
|
255
267
|
|
256
268
|
else:
|
257
|
-
return cast(Metric | None, self.scopes[scope].metrics.get(metric))
|
269
|
+
return cast(Metric | None, self.scopes[scope.scope_id].metrics.get(metric))
|
258
270
|
|
259
271
|
def enter_scope[Metric: State](
|
260
272
|
self,
|
261
273
|
scope: ScopeIdentifier,
|
262
274
|
/,
|
263
275
|
) -> None:
|
264
|
-
assert scope not in self.scopes # nosec: B101
|
276
|
+
assert scope.scope_id not in self.scopes # nosec: B101
|
265
277
|
scope_metrics = MetricsScopeStore(scope)
|
266
|
-
self.scopes[scope] = scope_metrics
|
278
|
+
self.scopes[scope.scope_id] = scope_metrics
|
267
279
|
|
268
280
|
if self.root_scope is None:
|
269
281
|
self.root_scope = scope
|
270
282
|
|
271
283
|
else:
|
272
284
|
for key in self.scopes.keys():
|
273
|
-
if key
|
285
|
+
if key == scope.parent_id:
|
274
286
|
self.scopes[key].nested.append(scope_metrics)
|
275
287
|
return
|
276
288
|
|
@@ -283,12 +295,22 @@ class MetricsLogger:
|
|
283
295
|
scope: ScopeIdentifier,
|
284
296
|
/,
|
285
297
|
) -> None:
|
286
|
-
assert
|
287
|
-
self.scopes
|
298
|
+
assert self.root_scope is not None # nosec: B101
|
299
|
+
assert scope.scope_id in self.scopes # nosec: B101
|
300
|
+
|
301
|
+
self.scopes[scope.scope_id].allow_exit = True
|
302
|
+
|
303
|
+
if not all(nested.exited for nested in self.scopes[scope.scope_id].nested):
|
304
|
+
return # not completed yet
|
305
|
+
|
306
|
+
self.scopes[scope.scope_id].exited = monotonic()
|
307
|
+
|
308
|
+
if scope != self.root_scope and self.scopes[scope.parent_id].allow_exit:
|
309
|
+
self.exit_scope(self.scopes[scope.parent_id].identifier)
|
288
310
|
|
289
|
-
|
311
|
+
elif scope == self.root_scope and self.scopes[self.root_scope.scope_id].finished:
|
290
312
|
if log := _tree_log(
|
291
|
-
self.scopes[scope],
|
313
|
+
self.scopes[scope.scope_id],
|
292
314
|
list_items_limit=self.items_limit,
|
293
315
|
redact_content=self.redact_content,
|
294
316
|
):
|
haiway/utils/__init__.py
CHANGED
@@ -13,9 +13,11 @@ from haiway.utils.logs import setup_logging
|
|
13
13
|
from haiway.utils.mimic import mimic_function
|
14
14
|
from haiway.utils.noop import async_noop, noop
|
15
15
|
from haiway.utils.queue import AsyncQueue
|
16
|
+
from haiway.utils.stream import AsyncStream
|
16
17
|
|
17
18
|
__all__ = [
|
18
19
|
"AsyncQueue",
|
20
|
+
"AsyncStream",
|
19
21
|
"always",
|
20
22
|
"as_dict",
|
21
23
|
"as_list",
|
haiway/utils/stream.py
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
from asyncio import (
|
2
|
+
AbstractEventLoop,
|
3
|
+
CancelledError,
|
4
|
+
Future,
|
5
|
+
get_running_loop,
|
6
|
+
)
|
7
|
+
from collections.abc import AsyncIterator
|
8
|
+
|
9
|
+
__all__ = [
|
10
|
+
"AsyncStream",
|
11
|
+
]
|
12
|
+
|
13
|
+
|
14
|
+
class AsyncStream[Element](AsyncIterator[Element]):
|
15
|
+
def __init__(
|
16
|
+
self,
|
17
|
+
loop: AbstractEventLoop | None = None,
|
18
|
+
) -> None:
|
19
|
+
self._loop: AbstractEventLoop = loop or get_running_loop()
|
20
|
+
self._ready: Future[None] = self._loop.create_future()
|
21
|
+
self._waiting: Future[Element] | None = None
|
22
|
+
self._finish_reason: BaseException | None = None
|
23
|
+
|
24
|
+
@property
|
25
|
+
def finished(self) -> bool:
|
26
|
+
return self._finish_reason is not None
|
27
|
+
|
28
|
+
async def send(
|
29
|
+
self,
|
30
|
+
element: Element,
|
31
|
+
/,
|
32
|
+
) -> None:
|
33
|
+
if self._finish_reason is not None:
|
34
|
+
return # already finished
|
35
|
+
|
36
|
+
# wait for readiness
|
37
|
+
await self._ready
|
38
|
+
# we could finish while waiting
|
39
|
+
if self._finish_reason is not None:
|
40
|
+
return # already finished
|
41
|
+
|
42
|
+
assert self._waiting is not None and not self._waiting.done() # nosec: B101
|
43
|
+
# send the element
|
44
|
+
self._waiting.set_result(element)
|
45
|
+
# and create new readiness future afterwards
|
46
|
+
self._ready = self._loop.create_future()
|
47
|
+
|
48
|
+
def finish(
|
49
|
+
self,
|
50
|
+
exception: BaseException | None = None,
|
51
|
+
) -> None:
|
52
|
+
if self.finished:
|
53
|
+
return # already finished, ignore
|
54
|
+
|
55
|
+
self._finish_reason = exception or StopAsyncIteration()
|
56
|
+
|
57
|
+
if not self._ready.done():
|
58
|
+
if get_running_loop() is not self._loop:
|
59
|
+
self._loop.call_soon_threadsafe(
|
60
|
+
self._ready.set_result,
|
61
|
+
None,
|
62
|
+
)
|
63
|
+
|
64
|
+
else:
|
65
|
+
self._ready.set_result(None)
|
66
|
+
|
67
|
+
if self._waiting is not None and not self._waiting.done():
|
68
|
+
if get_running_loop() is not self._loop:
|
69
|
+
self._loop.call_soon_threadsafe(
|
70
|
+
self._waiting.set_exception,
|
71
|
+
self._finish_reason,
|
72
|
+
)
|
73
|
+
|
74
|
+
else:
|
75
|
+
self._waiting.set_exception(self._finish_reason)
|
76
|
+
|
77
|
+
def cancel(self) -> None:
|
78
|
+
self.finish(exception=CancelledError())
|
79
|
+
|
80
|
+
async def __anext__(self) -> Element:
|
81
|
+
assert self._waiting is None, "AsyncStream can't be reused" # nosec: B101
|
82
|
+
|
83
|
+
if self._finish_reason:
|
84
|
+
raise self._finish_reason
|
85
|
+
|
86
|
+
try:
|
87
|
+
assert not self._ready.done() # nosec: B101
|
88
|
+
# create new waiting future
|
89
|
+
self._waiting = self._loop.create_future()
|
90
|
+
# and notify readiness
|
91
|
+
self._ready.set_result(None)
|
92
|
+
# and wait for the result
|
93
|
+
return await self._waiting
|
94
|
+
|
95
|
+
finally:
|
96
|
+
# cleanup waiting future
|
97
|
+
self._waiting = None
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: haiway
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.14.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,18 +1,18 @@
|
|
1
|
-
haiway/__init__.py,sha256=
|
1
|
+
haiway/__init__.py,sha256=RhW9HOIAVQ3srQ-v23tPghJ20dWcn_uAyt8U8Hhn868,2081
|
2
2
|
haiway/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
haiway/context/__init__.py,sha256=feqd0eJnGQwh4B8BZXpS0fQRE-DqoFCFOHipF1jOY8A,762
|
4
|
-
haiway/context/access.py,sha256=
|
4
|
+
haiway/context/access.py,sha256=1Oq70pcp54bC9NNp-zMocDewVJpcZgepi99_xeqz98o,18502
|
5
5
|
haiway/context/disposables.py,sha256=vcsh8jRaJ8Q1ob7oh5LsrSPw9f5AMTcaD_p_Gb7tXAI,2588
|
6
6
|
haiway/context/identifier.py,sha256=lz-FuspOtsaEsfb7QPrEVWYfbcMJgd3A6BGG3kLbaV0,3914
|
7
7
|
haiway/context/logging.py,sha256=F3dr6MLjodg3MX5WTInxn3r3JuihG-giBzumI0GGUQw,5590
|
8
8
|
haiway/context/metrics.py,sha256=N20XQtC8au_e_3iWrsZdej78YBEIWF44fdtWcZBWono,5223
|
9
|
-
haiway/context/state.py,sha256=
|
9
|
+
haiway/context/state.py,sha256=7pXb5gvyPOWiRbxX-sSfO-hjaHcTUIp_uTKhjaSLeRo,4552
|
10
10
|
haiway/context/tasks.py,sha256=MKfsa-921cIpQ_BKskwokjR27suCHkHZa3O9kOE8UOg,2826
|
11
11
|
haiway/context/types.py,sha256=VvJA7wAPZ3ISpgyThVguioYUXqhHf0XkPfRd0M1ERiQ,142
|
12
12
|
haiway/helpers/__init__.py,sha256=ZKDlL3twDqXyI1a9FDgRy3m1-Dfycvke6BJ4C3CndEk,671
|
13
13
|
haiway/helpers/asynchrony.py,sha256=YHLK5Hjc-5UWlQRypC11yHeEQyeAtHqrMoBTBfqQBvQ,6286
|
14
14
|
haiway/helpers/caching.py,sha256=EU5usTHGDzf0SO3bMW4hHB9oZlLlE7BxO_2ckbjYBw8,13274
|
15
|
-
haiway/helpers/metrics.py,sha256=
|
15
|
+
haiway/helpers/metrics.py,sha256=VNxgPgV8pgt-51f2CANy1IVx8VMYIAxT3F849t3IeQs,14604
|
16
16
|
haiway/helpers/retries.py,sha256=3m1SsJW_YY_HPufX9LEzcd_MEyRRFNXvSExLeEti8W8,7539
|
17
17
|
haiway/helpers/throttling.py,sha256=r9HnUuo4nX36Pf-oMFHUJk-ZCDeXQ__JTDHlkSltRhA,4121
|
18
18
|
haiway/helpers/timeouted.py,sha256=DthIm4ytKhmiIKf-pcO_vrO1X-ImZh-sLNCWcLY9gfw,3337
|
@@ -27,7 +27,7 @@ haiway/types/__init__.py,sha256=-j4uDN6ix3GBXLBqXC-k_QOJSDlO6zvNCxDej8vVzek,342
|
|
27
27
|
haiway/types/default.py,sha256=h38-zFkbn_UPEiw1SdDF5rkObVmD9UJpmyhOgS1gQ9U,2208
|
28
28
|
haiway/types/frozen.py,sha256=CZhFCXnWAKEhuWSfILxA8smfdpMd5Ku694ycfLh98R8,76
|
29
29
|
haiway/types/missing.py,sha256=rDnyA2wxPkTbJl0L-zbo0owp7IJ04xkCIp6xD6wh8NI,1712
|
30
|
-
haiway/utils/__init__.py,sha256=
|
30
|
+
haiway/utils/__init__.py,sha256=JYo5EVquL2BCBsHtvySPTio_x5hSVDJCfu_naWzbqKE,867
|
31
31
|
haiway/utils/always.py,sha256=u1tssiErzm0Q3ASc3CV1rLhcMQ54MjpMlC_bRJMQhK4,1230
|
32
32
|
haiway/utils/collections.py,sha256=IzD-XSEyngKyzLTNG9sr7QjXIneoAzi3oRsDmbRHtzU,3276
|
33
33
|
haiway/utils/env.py,sha256=-hI4CgLkzdyueuECVjm-TfR3lQjE2bDsc72w7vNC4nQ,5339
|
@@ -36,7 +36,8 @@ haiway/utils/logs.py,sha256=oDsc1ZdqKDjlTlctLbDcp9iX98Acr-1tdw-Pyg3DElo,1577
|
|
36
36
|
haiway/utils/mimic.py,sha256=BkVjTVP2TxxC8GChPGyDV6UXVwJmiRiSWeOYZNZFHxs,1828
|
37
37
|
haiway/utils/noop.py,sha256=qgbZlOKWY6_23Zs43OLukK2HagIQKRyR04zrFVm5rWI,344
|
38
38
|
haiway/utils/queue.py,sha256=Tk1bXvuNbEgapeC3-h_PYBASqVjhEoL8mUvtJnM29xI,4000
|
39
|
-
haiway
|
40
|
-
haiway-0.
|
41
|
-
haiway-0.
|
42
|
-
haiway-0.
|
39
|
+
haiway/utils/stream.py,sha256=Vqyi0EwcupkVyKQ7eple6z9DkcbSHkE-6yMw85mak9Q,2832
|
40
|
+
haiway-0.14.0.dist-info/METADATA,sha256=e01xN8K8-d8RiPRaV2ibtsxEHfITllUZmxZA_VzEpBs,4299
|
41
|
+
haiway-0.14.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
42
|
+
haiway-0.14.0.dist-info/licenses/LICENSE,sha256=3phcpHVNBP8jsi77gOO0E7rgKeDeu99Pi7DSnK9YHoQ,1069
|
43
|
+
haiway-0.14.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|