haiway 0.1.0__py3-none-any.whl → 0.3.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 +10 -18
- haiway/context/__init__.py +4 -5
- haiway/context/access.py +85 -51
- haiway/context/disposables.py +68 -0
- haiway/context/metrics.py +11 -11
- haiway/context/state.py +15 -17
- haiway/context/types.py +0 -5
- haiway/helpers/__init__.py +6 -6
- haiway/helpers/{cache.py → cached.py} +4 -4
- haiway/helpers/{retry.py → retries.py} +20 -7
- haiway/helpers/{timeout.py → timeouted.py} +2 -2
- haiway/state/__init__.py +2 -2
- haiway/state/attributes.py +1 -1
- haiway/state/structure.py +17 -17
- {haiway-0.1.0.dist-info → haiway-0.3.0.dist-info}/METADATA +1 -1
- haiway-0.3.0.dist-info/RECORD +35 -0
- haiway/context/dependencies.py +0 -61
- haiway-0.1.0.dist-info/RECORD +0 -35
- {haiway-0.1.0.dist-info → haiway-0.3.0.dist-info}/LICENSE +0 -0
- {haiway-0.1.0.dist-info → haiway-0.3.0.dist-info}/WHEEL +0 -0
- {haiway-0.1.0.dist-info → haiway-0.3.0.dist-info}/top_level.txt +0 -0
haiway/__init__.py
CHANGED
@@ -1,20 +1,13 @@
|
|
1
1
|
from haiway.context import (
|
2
|
-
|
3
|
-
|
2
|
+
Disposable,
|
3
|
+
Disposables,
|
4
4
|
MissingContext,
|
5
|
-
MissingDependency,
|
6
5
|
MissingState,
|
7
6
|
ScopeMetrics,
|
8
7
|
ctx,
|
9
8
|
)
|
10
|
-
from haiway.helpers import
|
11
|
-
|
12
|
-
auto_retry,
|
13
|
-
cached,
|
14
|
-
throttle,
|
15
|
-
with_timeout,
|
16
|
-
)
|
17
|
-
from haiway.state import Structure
|
9
|
+
from haiway.helpers import asynchronous, cache, retry, throttle, timeout
|
10
|
+
from haiway.state import State
|
18
11
|
from haiway.types import (
|
19
12
|
MISSING,
|
20
13
|
Missing,
|
@@ -45,11 +38,10 @@ __all__ = [
|
|
45
38
|
"async_noop",
|
46
39
|
"asynchronous",
|
47
40
|
"AsyncQueue",
|
48
|
-
"
|
49
|
-
"cached",
|
41
|
+
"cache",
|
50
42
|
"ctx",
|
51
|
-
"
|
52
|
-
"
|
43
|
+
"Disposable",
|
44
|
+
"Disposables",
|
53
45
|
"freeze",
|
54
46
|
"frozenlist",
|
55
47
|
"getenv_bool",
|
@@ -62,14 +54,14 @@ __all__ = [
|
|
62
54
|
"Missing",
|
63
55
|
"MISSING",
|
64
56
|
"MissingContext",
|
65
|
-
"MissingDependency",
|
66
57
|
"MissingState",
|
67
58
|
"noop",
|
68
59
|
"not_missing",
|
60
|
+
"retry",
|
69
61
|
"ScopeMetrics",
|
70
62
|
"setup_logging",
|
71
|
-
"
|
63
|
+
"State",
|
72
64
|
"throttle",
|
65
|
+
"timeout",
|
73
66
|
"when_missing",
|
74
|
-
"with_timeout",
|
75
67
|
]
|
haiway/context/__init__.py
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
from haiway.context.access import ctx
|
2
|
-
from haiway.context.
|
2
|
+
from haiway.context.disposables import Disposable, Disposables
|
3
3
|
from haiway.context.metrics import ScopeMetrics
|
4
|
-
from haiway.context.types import MissingContext,
|
4
|
+
from haiway.context.types import MissingContext, MissingState
|
5
5
|
|
6
6
|
__all__ = [
|
7
7
|
"ctx",
|
8
|
-
"
|
9
|
-
"
|
8
|
+
"Disposable",
|
9
|
+
"Disposables",
|
10
10
|
"MissingContext",
|
11
|
-
"MissingDependency",
|
12
11
|
"MissingState",
|
13
12
|
"ScopeMetrics",
|
14
13
|
]
|
haiway/context/access.py
CHANGED
@@ -5,16 +5,17 @@ from asyncio import (
|
|
5
5
|
from collections.abc import (
|
6
6
|
Callable,
|
7
7
|
Coroutine,
|
8
|
+
Iterable,
|
8
9
|
)
|
9
10
|
from logging import Logger
|
10
11
|
from types import TracebackType
|
11
12
|
from typing import Any, final
|
12
13
|
|
13
|
-
from haiway.context.
|
14
|
+
from haiway.context.disposables import Disposable, Disposables
|
14
15
|
from haiway.context.metrics import MetricsContext, ScopeMetrics
|
15
16
|
from haiway.context.state import StateContext
|
16
17
|
from haiway.context.tasks import TaskGroupContext
|
17
|
-
from haiway.state import
|
18
|
+
from haiway.state import State
|
18
19
|
from haiway.utils import freeze
|
19
20
|
|
20
21
|
__all__ = [
|
@@ -24,25 +25,41 @@ __all__ = [
|
|
24
25
|
|
25
26
|
@final
|
26
27
|
class ScopeContext:
|
27
|
-
def __init__(
|
28
|
+
def __init__( # noqa: PLR0913
|
28
29
|
self,
|
30
|
+
trace_id: str | None,
|
31
|
+
name: str,
|
32
|
+
logger: Logger | None,
|
33
|
+
state: tuple[State, ...],
|
34
|
+
disposables: Disposables | None,
|
29
35
|
task_group: TaskGroupContext,
|
30
|
-
state: StateContext,
|
31
|
-
metrics: MetricsContext,
|
32
36
|
completion: Callable[[ScopeMetrics], Coroutine[None, None, None]] | None,
|
33
37
|
) -> None:
|
34
38
|
self._task_group: TaskGroupContext = task_group
|
35
|
-
self.
|
36
|
-
self.
|
39
|
+
self._logger: Logger | None = logger
|
40
|
+
self._trace_id: str | None = trace_id
|
41
|
+
self._name: str = name
|
42
|
+
self._state_context: StateContext
|
43
|
+
self._state: tuple[State, ...] = state
|
44
|
+
self._disposables: Disposables | None = disposables
|
45
|
+
self._metrics_context: MetricsContext
|
37
46
|
self._completion: Callable[[ScopeMetrics], Coroutine[None, None, None]] | None = completion
|
38
47
|
|
39
48
|
freeze(self)
|
40
49
|
|
41
50
|
def __enter__(self) -> None:
|
42
51
|
assert self._completion is None, "Can't enter synchronous context with completion" # nosec: B101
|
52
|
+
assert self._disposables is None, "Can't enter synchronous context with disposables" # nosec: B101
|
53
|
+
|
54
|
+
self._state_context = StateContext.updated(self._state)
|
55
|
+
self._metrics_context = MetricsContext.scope(
|
56
|
+
self._name,
|
57
|
+
logger=self._logger,
|
58
|
+
trace_id=self._trace_id,
|
59
|
+
)
|
43
60
|
|
44
|
-
self.
|
45
|
-
self.
|
61
|
+
self._state_context.__enter__()
|
62
|
+
self._metrics_context.__enter__()
|
46
63
|
|
47
64
|
def __exit__(
|
48
65
|
self,
|
@@ -50,49 +67,71 @@ class ScopeContext:
|
|
50
67
|
exc_val: BaseException | None,
|
51
68
|
exc_tb: TracebackType | None,
|
52
69
|
) -> None:
|
53
|
-
self.
|
70
|
+
self._metrics_context.__exit__(
|
54
71
|
exc_type=exc_type,
|
55
72
|
exc_val=exc_val,
|
56
73
|
exc_tb=exc_tb,
|
57
74
|
)
|
58
75
|
|
59
|
-
self.
|
76
|
+
self._state_context.__exit__(
|
60
77
|
exc_type=exc_type,
|
61
78
|
exc_val=exc_val,
|
62
79
|
exc_tb=exc_tb,
|
63
80
|
)
|
64
81
|
|
65
82
|
async def __aenter__(self) -> None:
|
66
|
-
self._state.__enter__()
|
67
|
-
self._metrics.__enter__()
|
68
83
|
await self._task_group.__aenter__()
|
69
84
|
|
85
|
+
if self._disposables:
|
86
|
+
self._state_context = StateContext.updated(
|
87
|
+
(*self._state, *await self._disposables.__aenter__())
|
88
|
+
)
|
89
|
+
|
90
|
+
else:
|
91
|
+
self._state_context = StateContext.updated(self._state)
|
92
|
+
|
93
|
+
self._metrics_context = MetricsContext.scope(
|
94
|
+
self._name,
|
95
|
+
logger=self._logger,
|
96
|
+
trace_id=self._trace_id,
|
97
|
+
)
|
98
|
+
|
99
|
+
self._state_context.__enter__()
|
100
|
+
self._metrics_context.__enter__()
|
101
|
+
|
70
102
|
async def __aexit__(
|
71
103
|
self,
|
72
104
|
exc_type: type[BaseException] | None,
|
73
105
|
exc_val: BaseException | None,
|
74
106
|
exc_tb: TracebackType | None,
|
75
107
|
) -> None:
|
108
|
+
if self._disposables:
|
109
|
+
await self._disposables.__aexit__(
|
110
|
+
exc_type=exc_type,
|
111
|
+
exc_val=exc_val,
|
112
|
+
exc_tb=exc_tb,
|
113
|
+
)
|
114
|
+
|
76
115
|
await self._task_group.__aexit__(
|
77
116
|
exc_type=exc_type,
|
78
117
|
exc_val=exc_val,
|
79
118
|
exc_tb=exc_tb,
|
80
119
|
)
|
81
120
|
|
82
|
-
self.
|
121
|
+
self._metrics_context.__exit__(
|
83
122
|
exc_type=exc_type,
|
84
123
|
exc_val=exc_val,
|
85
124
|
exc_tb=exc_tb,
|
86
125
|
)
|
87
126
|
|
88
|
-
self.
|
127
|
+
self._state_context.__exit__(
|
89
128
|
exc_type=exc_type,
|
90
129
|
exc_val=exc_val,
|
91
130
|
exc_tb=exc_tb,
|
92
131
|
)
|
93
132
|
|
94
133
|
if completion := self._completion:
|
95
|
-
await completion(self.
|
134
|
+
await completion(self._metrics_context._metrics) # pyright: ignore[reportPrivateUsage]
|
96
135
|
|
97
136
|
|
98
137
|
@final
|
@@ -101,7 +140,8 @@ class ctx:
|
|
101
140
|
def scope(
|
102
141
|
name: str,
|
103
142
|
/,
|
104
|
-
*state:
|
143
|
+
*state: State,
|
144
|
+
disposables: Disposables | Iterable[Disposable] | None = None,
|
105
145
|
logger: Logger | None = None,
|
106
146
|
trace_id: str | None = None,
|
107
147
|
completion: Callable[[ScopeMetrics], Coroutine[None, None, None]] | None = None,
|
@@ -115,9 +155,14 @@ class ctx:
|
|
115
155
|
name: Value
|
116
156
|
name of the scope context
|
117
157
|
|
118
|
-
*state:
|
119
|
-
state propagated within the scope context, will be merged with current
|
120
|
-
|
158
|
+
*state: State | Disposable
|
159
|
+
state propagated within the scope context, will be merged with current state by\
|
160
|
+
replacing current with provided on conflict.
|
161
|
+
|
162
|
+
disposables: Disposables | list[Disposable] | None
|
163
|
+
disposables consumed within the context when entered. Produced state will automatically\
|
164
|
+
be added to the scope state. Using asynchronous context is required if any disposables\
|
165
|
+
were provided.
|
121
166
|
|
122
167
|
logger: Logger | None
|
123
168
|
logger used within the scope context, when not provided current logger will be used\
|
@@ -139,20 +184,30 @@ class ctx:
|
|
139
184
|
context object intended to enter context manager with it
|
140
185
|
"""
|
141
186
|
|
187
|
+
resolved_disposables: Disposables | None
|
188
|
+
match disposables:
|
189
|
+
case None:
|
190
|
+
resolved_disposables = None
|
191
|
+
|
192
|
+
case Disposables() as disposables:
|
193
|
+
resolved_disposables = disposables
|
194
|
+
|
195
|
+
case iterable:
|
196
|
+
resolved_disposables = Disposables(*iterable)
|
197
|
+
|
142
198
|
return ScopeContext(
|
199
|
+
trace_id=trace_id,
|
200
|
+
name=name,
|
201
|
+
logger=logger,
|
202
|
+
state=state,
|
203
|
+
disposables=resolved_disposables,
|
143
204
|
task_group=TaskGroupContext(),
|
144
|
-
metrics=MetricsContext.scope(
|
145
|
-
name,
|
146
|
-
logger=logger,
|
147
|
-
trace_id=trace_id,
|
148
|
-
),
|
149
|
-
state=StateContext.updated(state),
|
150
205
|
completion=completion,
|
151
206
|
)
|
152
207
|
|
153
208
|
@staticmethod
|
154
209
|
def updated(
|
155
|
-
*state:
|
210
|
+
*state: State,
|
156
211
|
) -> StateContext:
|
157
212
|
"""
|
158
213
|
Update scope context with given state. When called within an existing context\
|
@@ -160,7 +215,7 @@ class ctx:
|
|
160
215
|
|
161
216
|
Parameters
|
162
217
|
----------
|
163
|
-
*state:
|
218
|
+
*state: State
|
164
219
|
state propagated within the updated scope context, will be merged with current if any\
|
165
220
|
by replacing current with provided on conflict
|
166
221
|
|
@@ -215,28 +270,7 @@ class ctx:
|
|
215
270
|
raise RuntimeError("Attempting to cancel context out of asyncio task")
|
216
271
|
|
217
272
|
@staticmethod
|
218
|
-
|
219
|
-
dependency: type[DependencyType],
|
220
|
-
/,
|
221
|
-
) -> DependencyType:
|
222
|
-
"""
|
223
|
-
Access current dependency by its type.
|
224
|
-
|
225
|
-
Parameters
|
226
|
-
----------
|
227
|
-
dependency: type[DependencyType]
|
228
|
-
type of requested dependency
|
229
|
-
|
230
|
-
Returns
|
231
|
-
-------
|
232
|
-
DependencyType
|
233
|
-
resolved dependency instance
|
234
|
-
"""
|
235
|
-
|
236
|
-
return await Dependencies.dependency(dependency)
|
237
|
-
|
238
|
-
@staticmethod
|
239
|
-
def state[StateType: Structure](
|
273
|
+
def state[StateType: State](
|
240
274
|
state: type[StateType],
|
241
275
|
/,
|
242
276
|
default: StateType | None = None,
|
@@ -261,7 +295,7 @@ class ctx:
|
|
261
295
|
)
|
262
296
|
|
263
297
|
@staticmethod
|
264
|
-
def record[Metric:
|
298
|
+
def record[Metric: State](
|
265
299
|
metric: Metric,
|
266
300
|
/,
|
267
301
|
merge: Callable[[Metric, Metric], Metric] = lambda lhs, rhs: rhs,
|
@@ -0,0 +1,68 @@
|
|
1
|
+
from asyncio import gather, shield
|
2
|
+
from collections.abc import Iterable
|
3
|
+
from types import TracebackType
|
4
|
+
from typing import Protocol, final, runtime_checkable
|
5
|
+
|
6
|
+
from haiway.state import State
|
7
|
+
|
8
|
+
__all__ = [
|
9
|
+
"Disposable",
|
10
|
+
"Disposables",
|
11
|
+
]
|
12
|
+
|
13
|
+
|
14
|
+
@runtime_checkable
|
15
|
+
class Disposable(Protocol):
|
16
|
+
async def initialize(self) -> State | None: ...
|
17
|
+
async def dispose(self) -> None: ...
|
18
|
+
|
19
|
+
|
20
|
+
@final
|
21
|
+
class Disposables:
|
22
|
+
def __init__(
|
23
|
+
self,
|
24
|
+
*disposables: Disposable,
|
25
|
+
) -> None:
|
26
|
+
self._disposables: tuple[Disposable, ...] = disposables
|
27
|
+
|
28
|
+
async def initialize(self) -> Iterable[State]:
|
29
|
+
return [
|
30
|
+
state
|
31
|
+
for state in await gather(
|
32
|
+
*[disposable.initialize() for disposable in self._disposables],
|
33
|
+
)
|
34
|
+
if state is not None
|
35
|
+
]
|
36
|
+
|
37
|
+
async def dispose(self) -> None:
|
38
|
+
results: list[BaseException | None] = await shield(
|
39
|
+
gather(
|
40
|
+
*[disposable.dispose() for disposable in self._disposables],
|
41
|
+
return_exceptions=True,
|
42
|
+
),
|
43
|
+
)
|
44
|
+
|
45
|
+
self._disposables = ()
|
46
|
+
exceptions: list[BaseException] = [
|
47
|
+
exception for exception in results if exception is not None
|
48
|
+
]
|
49
|
+
|
50
|
+
if len(exceptions) > 1:
|
51
|
+
raise BaseExceptionGroup("Disposing errors", exceptions)
|
52
|
+
|
53
|
+
elif exceptions:
|
54
|
+
raise exceptions[0]
|
55
|
+
|
56
|
+
def __bool__(self) -> bool:
|
57
|
+
return len(self._disposables) > 0
|
58
|
+
|
59
|
+
async def __aenter__(self) -> Iterable[State]:
|
60
|
+
return await self.initialize()
|
61
|
+
|
62
|
+
async def __aexit__(
|
63
|
+
self,
|
64
|
+
exc_type: type[BaseException] | None,
|
65
|
+
exc_val: BaseException | None,
|
66
|
+
exc_tb: TracebackType | None,
|
67
|
+
) -> None:
|
68
|
+
await self.dispose()
|
haiway/context/metrics.py
CHANGED
@@ -9,7 +9,7 @@ from types import TracebackType
|
|
9
9
|
from typing import Any, Self, cast, final, overload
|
10
10
|
from uuid import uuid4
|
11
11
|
|
12
|
-
from haiway.state import
|
12
|
+
from haiway.state import State
|
13
13
|
from haiway.utils import freeze
|
14
14
|
|
15
15
|
__all__ = [
|
@@ -30,7 +30,7 @@ class ScopeMetrics:
|
|
30
30
|
self.trace_id: str = trace_id or uuid4().hex
|
31
31
|
self._label: str = f"{self.trace_id}|{scope}" if scope else self.trace_id
|
32
32
|
self._logger: Logger = logger or getLogger(name=scope)
|
33
|
-
self._metrics: dict[type[
|
33
|
+
self._metrics: dict[type[State], State] = {}
|
34
34
|
self._nested: list[ScopeMetrics] = []
|
35
35
|
self._timestamp: float = monotonic()
|
36
36
|
self._completed: Future[float] = get_event_loop().create_future()
|
@@ -46,11 +46,11 @@ class ScopeMetrics:
|
|
46
46
|
def metrics(
|
47
47
|
self,
|
48
48
|
*,
|
49
|
-
merge: Callable[[
|
50
|
-
) -> list[
|
51
|
-
metrics: dict[type[
|
49
|
+
merge: Callable[[State, State], State] = lambda lhs, rhs: lhs,
|
50
|
+
) -> list[State]:
|
51
|
+
metrics: dict[type[State], State] = copy(self._metrics)
|
52
52
|
for metric in chain.from_iterable(nested.metrics(merge=merge) for nested in self._nested):
|
53
|
-
metric_type: type[
|
53
|
+
metric_type: type[State] = type(metric)
|
54
54
|
if current := metrics.get(metric_type):
|
55
55
|
metrics[metric_type] = merge(current, metric)
|
56
56
|
|
@@ -60,21 +60,21 @@ class ScopeMetrics:
|
|
60
60
|
return list(metrics.values())
|
61
61
|
|
62
62
|
@overload
|
63
|
-
def read[Metric:
|
63
|
+
def read[Metric: State](
|
64
64
|
self,
|
65
65
|
metric: type[Metric],
|
66
66
|
/,
|
67
67
|
) -> Metric | None: ...
|
68
68
|
|
69
69
|
@overload
|
70
|
-
def read[Metric:
|
70
|
+
def read[Metric: State](
|
71
71
|
self,
|
72
72
|
metric: type[Metric],
|
73
73
|
/,
|
74
74
|
default: Metric,
|
75
75
|
) -> Metric: ...
|
76
76
|
|
77
|
-
def read[Metric:
|
77
|
+
def read[Metric: State](
|
78
78
|
self,
|
79
79
|
metric: type[Metric],
|
80
80
|
/,
|
@@ -82,7 +82,7 @@ class ScopeMetrics:
|
|
82
82
|
) -> Metric | None:
|
83
83
|
return cast(Metric | None, self._metrics.get(metric, default))
|
84
84
|
|
85
|
-
def record[Metric:
|
85
|
+
def record[Metric: State](
|
86
86
|
self,
|
87
87
|
metric: Metric,
|
88
88
|
/,
|
@@ -187,7 +187,7 @@ class MetricsContext:
|
|
187
187
|
)
|
188
188
|
|
189
189
|
@classmethod
|
190
|
-
def record[Metric:
|
190
|
+
def record[Metric: State](
|
191
191
|
cls,
|
192
192
|
metric: Metric,
|
193
193
|
/,
|
haiway/context/state.py
CHANGED
@@ -4,7 +4,7 @@ from types import TracebackType
|
|
4
4
|
from typing import Self, cast, final
|
5
5
|
|
6
6
|
from haiway.context.types import MissingContext, MissingState
|
7
|
-
from haiway.state import
|
7
|
+
from haiway.state import State
|
8
8
|
from haiway.utils import freeze
|
9
9
|
|
10
10
|
__all__ = [
|
@@ -17,28 +17,26 @@ __all__ = [
|
|
17
17
|
class ScopeState:
|
18
18
|
def __init__(
|
19
19
|
self,
|
20
|
-
state: Iterable[
|
20
|
+
state: Iterable[State],
|
21
21
|
) -> None:
|
22
|
-
self._state: dict[type[
|
23
|
-
type(element): element for element in state
|
24
|
-
}
|
22
|
+
self._state: dict[type[State], State] = {type(element): element for element in state}
|
25
23
|
freeze(self)
|
26
24
|
|
27
|
-
def state[
|
25
|
+
def state[StateType: State](
|
28
26
|
self,
|
29
|
-
state: type[
|
27
|
+
state: type[StateType],
|
30
28
|
/,
|
31
|
-
default:
|
32
|
-
) ->
|
29
|
+
default: StateType | None = None,
|
30
|
+
) -> StateType:
|
33
31
|
if state in self._state:
|
34
|
-
return cast(
|
32
|
+
return cast(StateType, self._state[state])
|
35
33
|
|
36
34
|
elif default is not None:
|
37
35
|
return default
|
38
36
|
|
39
37
|
else:
|
40
38
|
try:
|
41
|
-
initialized:
|
39
|
+
initialized: StateType = state()
|
42
40
|
self._state[state] = initialized
|
43
41
|
return initialized
|
44
42
|
|
@@ -50,7 +48,7 @@ class ScopeState:
|
|
50
48
|
|
51
49
|
def updated(
|
52
50
|
self,
|
53
|
-
state: Iterable[
|
51
|
+
state: Iterable[State],
|
54
52
|
) -> Self:
|
55
53
|
if state:
|
56
54
|
return self.__class__(
|
@@ -69,12 +67,12 @@ class StateContext:
|
|
69
67
|
_context = ContextVar[ScopeState]("StateContext")
|
70
68
|
|
71
69
|
@classmethod
|
72
|
-
def current[
|
70
|
+
def current[StateType: State](
|
73
71
|
cls,
|
74
|
-
state: type[
|
72
|
+
state: type[StateType],
|
75
73
|
/,
|
76
|
-
default:
|
77
|
-
) ->
|
74
|
+
default: StateType | None = None,
|
75
|
+
) -> StateType:
|
78
76
|
try:
|
79
77
|
return cls._context.get().state(state, default=default)
|
80
78
|
|
@@ -84,7 +82,7 @@ class StateContext:
|
|
84
82
|
@classmethod
|
85
83
|
def updated(
|
86
84
|
cls,
|
87
|
-
state: Iterable[
|
85
|
+
state: Iterable[State],
|
88
86
|
/,
|
89
87
|
) -> Self:
|
90
88
|
try:
|
haiway/context/types.py
CHANGED
haiway/helpers/__init__.py
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
from haiway.helpers.asynchronous import asynchronous
|
2
|
-
from haiway.helpers.
|
3
|
-
from haiway.helpers.
|
2
|
+
from haiway.helpers.cached import cache
|
3
|
+
from haiway.helpers.retries import retry
|
4
4
|
from haiway.helpers.throttling import throttle
|
5
|
-
from haiway.helpers.
|
5
|
+
from haiway.helpers.timeouted import timeout
|
6
6
|
|
7
7
|
__all__ = [
|
8
8
|
"asynchronous",
|
9
|
-
"
|
10
|
-
"
|
9
|
+
"cache",
|
10
|
+
"retry",
|
11
11
|
"throttle",
|
12
|
-
"
|
12
|
+
"timeout",
|
13
13
|
]
|
@@ -9,26 +9,26 @@ from weakref import ref
|
|
9
9
|
from haiway.utils.mimic import mimic_function
|
10
10
|
|
11
11
|
__all__ = [
|
12
|
-
"
|
12
|
+
"cache",
|
13
13
|
]
|
14
14
|
|
15
15
|
|
16
16
|
@overload
|
17
|
-
def
|
17
|
+
def cache[**Args, Result](
|
18
18
|
function: Callable[Args, Result],
|
19
19
|
/,
|
20
20
|
) -> Callable[Args, Result]: ...
|
21
21
|
|
22
22
|
|
23
23
|
@overload
|
24
|
-
def
|
24
|
+
def cache[**Args, Result](
|
25
25
|
*,
|
26
26
|
limit: int = 1,
|
27
27
|
expiration: float | None = None,
|
28
28
|
) -> Callable[[Callable[Args, Result]], Callable[Args, Result]]: ...
|
29
29
|
|
30
30
|
|
31
|
-
def
|
31
|
+
def cache[**Args, Result](
|
32
32
|
function: Callable[Args, Result] | None = None,
|
33
33
|
*,
|
34
34
|
limit: int = 1,
|
@@ -1,17 +1,18 @@
|
|
1
1
|
from asyncio import CancelledError, iscoroutinefunction, sleep
|
2
2
|
from collections.abc import Callable, Coroutine
|
3
|
+
from time import sleep as sleep_sync
|
3
4
|
from typing import cast, overload
|
4
5
|
|
5
6
|
from haiway.context import ctx
|
6
7
|
from haiway.utils import mimic_function
|
7
8
|
|
8
9
|
__all__ = [
|
9
|
-
"
|
10
|
+
"retry",
|
10
11
|
]
|
11
12
|
|
12
13
|
|
13
14
|
@overload
|
14
|
-
def
|
15
|
+
def retry[**Args, Result](
|
15
16
|
function: Callable[Args, Result],
|
16
17
|
/,
|
17
18
|
) -> Callable[Args, Result]:
|
@@ -34,7 +35,7 @@ def auto_retry[**Args, Result](
|
|
34
35
|
|
35
36
|
|
36
37
|
@overload
|
37
|
-
def
|
38
|
+
def retry[**Args, Result](
|
38
39
|
*,
|
39
40
|
limit: int = 1,
|
40
41
|
delay: Callable[[int, Exception], float] | float | None = None,
|
@@ -66,7 +67,7 @@ def auto_retry[**Args, Result](
|
|
66
67
|
"""
|
67
68
|
|
68
69
|
|
69
|
-
def
|
70
|
+
def retry[**Args, Result](
|
70
71
|
function: Callable[Args, Result] | None = None,
|
71
72
|
*,
|
72
73
|
limit: int = 1,
|
@@ -115,11 +116,12 @@ def auto_retry[**Args, Result](
|
|
115
116
|
catching=catching if isinstance(catching, set | tuple) else {catching},
|
116
117
|
),
|
117
118
|
)
|
119
|
+
|
118
120
|
else:
|
119
|
-
assert delay is None, "Delay is not supported in sync wrapper" # nosec: B101
|
120
121
|
return _wrap_sync(
|
121
122
|
function,
|
122
123
|
limit=limit,
|
124
|
+
delay=delay,
|
123
125
|
catching=catching if isinstance(catching, set | tuple) else {catching},
|
124
126
|
)
|
125
127
|
|
@@ -133,6 +135,7 @@ def _wrap_sync[**Args, Result](
|
|
133
135
|
function: Callable[Args, Result],
|
134
136
|
*,
|
135
137
|
limit: int,
|
138
|
+
delay: Callable[[int, Exception], float] | float | None,
|
136
139
|
catching: set[type[Exception]] | tuple[type[Exception], ...],
|
137
140
|
) -> Callable[Args, Result]:
|
138
141
|
assert limit > 0, "Limit has to be greater than zero" # nosec: B101
|
@@ -158,6 +161,16 @@ def _wrap_sync[**Args, Result](
|
|
158
161
|
exc,
|
159
162
|
)
|
160
163
|
|
164
|
+
match delay:
|
165
|
+
case None:
|
166
|
+
continue
|
167
|
+
|
168
|
+
case float(strict):
|
169
|
+
sleep_sync(strict)
|
170
|
+
|
171
|
+
case make_delay: # type: Callable[[], float]
|
172
|
+
sleep_sync(make_delay(attempt, exc)) # pyright: ignore[reportCallIssue, reportUnknownArgumentType]
|
173
|
+
|
161
174
|
else:
|
162
175
|
raise exc
|
163
176
|
|
@@ -199,10 +212,10 @@ def _wrap_async[**Args, Result](
|
|
199
212
|
continue
|
200
213
|
|
201
214
|
case float(strict):
|
202
|
-
await sleep(
|
215
|
+
await sleep(strict)
|
203
216
|
|
204
217
|
case make_delay: # type: Callable[[], float]
|
205
|
-
await sleep(
|
218
|
+
await sleep(make_delay(attempt, exc)) # pyright: ignore[reportCallIssue, reportUnknownArgumentType]
|
206
219
|
|
207
220
|
else:
|
208
221
|
raise exc
|
@@ -4,11 +4,11 @@ from collections.abc import Callable, Coroutine
|
|
4
4
|
from haiway.utils.mimic import mimic_function
|
5
5
|
|
6
6
|
__all__ = [
|
7
|
-
"
|
7
|
+
"timeout",
|
8
8
|
]
|
9
9
|
|
10
10
|
|
11
|
-
def
|
11
|
+
def timeout[**Args, Result](
|
12
12
|
timeout: float,
|
13
13
|
/,
|
14
14
|
) -> Callable[
|
haiway/state/__init__.py
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
from haiway.state.attributes import AttributeAnnotation, attribute_annotations
|
2
|
-
from haiway.state.structure import
|
2
|
+
from haiway.state.structure import State
|
3
3
|
|
4
4
|
__all__ = [
|
5
5
|
"attribute_annotations",
|
6
6
|
"AttributeAnnotation",
|
7
|
-
"
|
7
|
+
"State",
|
8
8
|
]
|
haiway/state/attributes.py
CHANGED
@@ -52,7 +52,7 @@ def attribute_annotations(
|
|
52
52
|
|
53
53
|
self_annotation = AttributeAnnotation(
|
54
54
|
origin=cls,
|
55
|
-
arguments=[], # ignore self arguments here,
|
55
|
+
arguments=[], # ignore self arguments here, State will have them resolved at this stage
|
56
56
|
)
|
57
57
|
localns: dict[str, Any] = {cls.__name__: cls}
|
58
58
|
recursion_guard: dict[Any, AttributeAnnotation] = {cls: self_annotation}
|
haiway/state/structure.py
CHANGED
@@ -19,12 +19,12 @@ from haiway.state.validation import attribute_type_validator
|
|
19
19
|
from haiway.types.missing import MISSING, Missing
|
20
20
|
|
21
21
|
__all__ = [
|
22
|
-
"
|
22
|
+
"State",
|
23
23
|
]
|
24
24
|
|
25
25
|
|
26
26
|
@final
|
27
|
-
class
|
27
|
+
class StateAttribute[Value]:
|
28
28
|
def __init__(
|
29
29
|
self,
|
30
30
|
annotation: AttributeAnnotation,
|
@@ -48,7 +48,7 @@ class StructureAttribute[Value]:
|
|
48
48
|
frozen_default=True,
|
49
49
|
field_specifiers=(),
|
50
50
|
)
|
51
|
-
class
|
51
|
+
class StateMeta(type):
|
52
52
|
def __new__(
|
53
53
|
cls,
|
54
54
|
/,
|
@@ -58,7 +58,7 @@ class StructureMeta(type):
|
|
58
58
|
type_parameters: dict[str, Any] | None = None,
|
59
59
|
**kwargs: Any,
|
60
60
|
) -> Any:
|
61
|
-
|
61
|
+
state_type = type.__new__(
|
62
62
|
cls,
|
63
63
|
name,
|
64
64
|
bases,
|
@@ -66,28 +66,28 @@ class StructureMeta(type):
|
|
66
66
|
**kwargs,
|
67
67
|
)
|
68
68
|
|
69
|
-
attributes: dict[str,
|
69
|
+
attributes: dict[str, StateAttribute[Any]] = {}
|
70
70
|
|
71
71
|
if bases: # handle base class
|
72
72
|
for key, annotation in attribute_annotations(
|
73
|
-
|
73
|
+
state_type,
|
74
74
|
type_parameters=type_parameters,
|
75
75
|
).items():
|
76
76
|
# do not include ClassVars and dunder items
|
77
77
|
if ((get_origin(annotation) or annotation) is ClassVar) or key.startswith("__"):
|
78
78
|
continue
|
79
79
|
|
80
|
-
attributes[key] =
|
80
|
+
attributes[key] = StateAttribute(
|
81
81
|
annotation=annotation,
|
82
|
-
default=getattr(
|
82
|
+
default=getattr(state_type, key, MISSING),
|
83
83
|
validator=attribute_type_validator(annotation),
|
84
84
|
)
|
85
85
|
|
86
|
-
|
87
|
-
|
88
|
-
|
86
|
+
state_type.__ATTRIBUTES__ = attributes # pyright: ignore[reportAttributeAccessIssue]
|
87
|
+
state_type.__slots__ = frozenset(attributes.keys()) # pyright: ignore[reportAttributeAccessIssue]
|
88
|
+
state_type.__match_args__ = state_type.__slots__ # pyright: ignore[reportAttributeAccessIssue]
|
89
89
|
|
90
|
-
return
|
90
|
+
return state_type
|
91
91
|
|
92
92
|
|
93
93
|
_types_cache: WeakValueDictionary[
|
@@ -99,12 +99,12 @@ _types_cache: WeakValueDictionary[
|
|
99
99
|
] = WeakValueDictionary()
|
100
100
|
|
101
101
|
|
102
|
-
class
|
102
|
+
class State(metaclass=StateMeta):
|
103
103
|
"""
|
104
104
|
Base class for immutable data structures.
|
105
105
|
"""
|
106
106
|
|
107
|
-
__ATTRIBUTES__: ClassVar[dict[str,
|
107
|
+
__ATTRIBUTES__: ClassVar[dict[str, StateAttribute[Any]]]
|
108
108
|
|
109
109
|
def __class_getitem__(
|
110
110
|
cls,
|
@@ -151,7 +151,7 @@ class Structure(metaclass=StructureMeta):
|
|
151
151
|
name: str = f"{cls.__name__}[{parameter_names}]"
|
152
152
|
bases: tuple[type[Self]] = (cls,)
|
153
153
|
|
154
|
-
parametrized_type: type[Self] =
|
154
|
+
parametrized_type: type[Self] = StateMeta.__new__(
|
155
155
|
cls.__class__,
|
156
156
|
name=name,
|
157
157
|
bases=bases,
|
@@ -211,7 +211,7 @@ class Structure(metaclass=StructureMeta):
|
|
211
211
|
value: Any,
|
212
212
|
) -> Any:
|
213
213
|
raise AttributeError(
|
214
|
-
f"Can't modify immutable
|
214
|
+
f"Can't modify immutable state {self.__class__.__qualname__},"
|
215
215
|
f" attribute - '{name}' cannot be modified"
|
216
216
|
)
|
217
217
|
|
@@ -220,7 +220,7 @@ class Structure(metaclass=StructureMeta):
|
|
220
220
|
name: str,
|
221
221
|
) -> None:
|
222
222
|
raise AttributeError(
|
223
|
-
f"Can't modify immutable
|
223
|
+
f"Can't modify immutable state {self.__class__.__qualname__},"
|
224
224
|
f" attribute - '{name}' cannot be deleted"
|
225
225
|
)
|
226
226
|
|
@@ -0,0 +1,35 @@
|
|
1
|
+
haiway/__init__.py,sha256=tjFQ9bVnUHs2BMyCPxeNYxqHtqyIZQDk1Wk2nFB_G2U,1138
|
2
|
+
haiway/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
+
haiway/context/__init__.py,sha256=21Y3zvRo1bHASZD6B_FNkU28k1-g88RdmUyqxvYXJxg,336
|
4
|
+
haiway/context/access.py,sha256=E9aIC1RUupKk2LXik5qb13oHpW2s_tNQC0UxGcO9xr0,12761
|
5
|
+
haiway/context/disposables.py,sha256=VQX9jVo1pjqkmOYzWpsbYyF45y0XtpjorIIaeMBCwTU,1771
|
6
|
+
haiway/context/metrics.py,sha256=upkqUp47NymDJE8UQznr99AgaE9yw6i28l_cdCE1IeM,8612
|
7
|
+
haiway/context/state.py,sha256=GxGwPQTK8FdSprBd83lQbA9veubp0o93_1Yk3gb7HMc,3000
|
8
|
+
haiway/context/tasks.py,sha256=xXtXIUwXOra0EePTdkcEbMOmpWwFcO3hCRfR_IfvAHk,1978
|
9
|
+
haiway/context/types.py,sha256=VvJA7wAPZ3ISpgyThVguioYUXqhHf0XkPfRd0M1ERiQ,142
|
10
|
+
haiway/helpers/__init__.py,sha256=G8cbw11yb7VxGkAqjVmXPcYKR0eGe-UHa0LUtgHA3Jc,318
|
11
|
+
haiway/helpers/asynchronous.py,sha256=FYcV_ERrGy7f47y18oKeUk7fcbk-i_SOuPQ7lHhX5iI,6119
|
12
|
+
haiway/helpers/cached.py,sha256=Ok_WE5Whe7XqnIuLZo4rNNBFeWap-aUWX799s4b1JAQ,9536
|
13
|
+
haiway/helpers/retries.py,sha256=gIkyUlqJLDYaxIZd3qzeqGFY9y5Gp8dgZLlZ6hs8hoc,7538
|
14
|
+
haiway/helpers/throttling.py,sha256=zo0OwFq64si5KUwhd58cFHLmGAmYwRbFRJMbv9suhPs,3844
|
15
|
+
haiway/helpers/timeouted.py,sha256=1xU09hQnFdj6p48BwZl5xUvtIr3zC0ZUXehkdrduCjs,3074
|
16
|
+
haiway/state/__init__.py,sha256=dh7l_ZImy0uHHDGD-fzMhQFmz_ej8WU8WEE2OmIoyVM,204
|
17
|
+
haiway/state/attributes.py,sha256=kkIYNlvWCM1NkgiCbE6gZDwgBVOk_TkmqWv67MmU0to,13399
|
18
|
+
haiway/state/structure.py,sha256=G-Ln72hoQtE0FmKHeZdNmXf_FA3f5-e5AGbmJ2yMNb4,7003
|
19
|
+
haiway/state/validation.py,sha256=V4Q94Ik4p9t7f-7EIwK3Q9Zki8VkLOjPIGWGwLRVCoc,2873
|
20
|
+
haiway/types/__init__.py,sha256=cAJQzDgFi8AKRqpzY3HWrutaPR69tnJqeJK_mQVtYUk,252
|
21
|
+
haiway/types/frozen.py,sha256=CZhFCXnWAKEhuWSfILxA8smfdpMd5Ku694ycfLh98R8,76
|
22
|
+
haiway/types/missing.py,sha256=3t2bcZuw5fAKiycP-0Aly2TDUWtM3xyHy3KDCT91TLs,1660
|
23
|
+
haiway/utils/__init__.py,sha256=UA9h8YDvYI5rYujvsIS9t5Q-SWYImmk30uhR_42flqs,608
|
24
|
+
haiway/utils/always.py,sha256=2abp8Lm9rQkrfS3rm1Iqhb-IcWyVfH1BULab3KMxgOw,1234
|
25
|
+
haiway/utils/env.py,sha256=lKPOBZWyRD_gQariDGBjVLYTm0740nytPCSQpK2oRyE,3136
|
26
|
+
haiway/utils/immutable.py,sha256=K34ZIMzbkpgkHKH-KF73plEbXExsajNRkRTYp9nJEf4,620
|
27
|
+
haiway/utils/logs.py,sha256=oDsc1ZdqKDjlTlctLbDcp9iX98Acr-1tdw-Pyg3DElo,1577
|
28
|
+
haiway/utils/mimic.py,sha256=BkVjTVP2TxxC8GChPGyDV6UXVwJmiRiSWeOYZNZFHxs,1828
|
29
|
+
haiway/utils/noop.py,sha256=qgbZlOKWY6_23Zs43OLukK2HagIQKRyR04zrFVm5rWI,344
|
30
|
+
haiway/utils/queue.py,sha256=WGW8kSusIwRYHsYRIKD2CaqhhC1pUtVgtNHFDXDtYrw,2443
|
31
|
+
haiway-0.3.0.dist-info/LICENSE,sha256=GehQEW_I1pkmxkkj3NEa7rCTQKYBn7vTPabpDYJlRuo,1063
|
32
|
+
haiway-0.3.0.dist-info/METADATA,sha256=rABSQCD4Sboe2cC-p3hruWyxM0DrxggLozH0Ma-MIYM,3872
|
33
|
+
haiway-0.3.0.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
|
34
|
+
haiway-0.3.0.dist-info/top_level.txt,sha256=_LdXVLzUzgkvAGQnQJj5kQfoFhpPW6EF4Kj9NapniLg,7
|
35
|
+
haiway-0.3.0.dist-info/RECORD,,
|
haiway/context/dependencies.py
DELETED
@@ -1,61 +0,0 @@
|
|
1
|
-
from abc import ABC, abstractmethod
|
2
|
-
from asyncio import Lock, gather, shield
|
3
|
-
from typing import ClassVar, Self, cast, final
|
4
|
-
|
5
|
-
__all__ = [
|
6
|
-
"Dependencies",
|
7
|
-
"Dependency",
|
8
|
-
]
|
9
|
-
|
10
|
-
|
11
|
-
class Dependency(ABC):
|
12
|
-
@classmethod
|
13
|
-
@abstractmethod
|
14
|
-
async def prepare(cls) -> Self: ...
|
15
|
-
|
16
|
-
async def dispose(self) -> None: # noqa: B027
|
17
|
-
pass
|
18
|
-
|
19
|
-
|
20
|
-
@final
|
21
|
-
class Dependencies:
|
22
|
-
_lock: ClassVar[Lock] = Lock()
|
23
|
-
_dependencies: ClassVar[dict[type[Dependency], Dependency]] = {}
|
24
|
-
|
25
|
-
def __init__(self) -> None:
|
26
|
-
raise NotImplementedError("Can't instantiate Dependencies")
|
27
|
-
|
28
|
-
@classmethod
|
29
|
-
async def dependency[Requested: Dependency](
|
30
|
-
cls,
|
31
|
-
dependency: type[Requested],
|
32
|
-
/,
|
33
|
-
) -> Requested:
|
34
|
-
async with cls._lock:
|
35
|
-
if dependency not in cls._dependencies:
|
36
|
-
cls._dependencies[dependency] = await dependency.prepare()
|
37
|
-
|
38
|
-
return cast(Requested, cls._dependencies[dependency])
|
39
|
-
|
40
|
-
@classmethod
|
41
|
-
async def register(
|
42
|
-
cls,
|
43
|
-
dependency: Dependency,
|
44
|
-
/,
|
45
|
-
) -> None:
|
46
|
-
async with cls._lock:
|
47
|
-
if current := cls._dependencies.get(dependency.__class__):
|
48
|
-
await current.dispose()
|
49
|
-
|
50
|
-
cls._dependencies[dependency.__class__] = dependency
|
51
|
-
|
52
|
-
@classmethod
|
53
|
-
async def dispose(cls) -> None:
|
54
|
-
async with cls._lock:
|
55
|
-
await shield(
|
56
|
-
gather(
|
57
|
-
*[dependency.dispose() for dependency in cls._dependencies.values()],
|
58
|
-
return_exceptions=False,
|
59
|
-
)
|
60
|
-
)
|
61
|
-
cls._dependencies.clear()
|
haiway-0.1.0.dist-info/RECORD
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
haiway/__init__.py,sha256=20WZqVGbImtVKnZLFw1CU1usQbXjLSEHcu1J07OTEhA,1243
|
2
|
-
haiway/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
-
haiway/context/__init__.py,sha256=iD_AjgtyJthrkF0Ys2vHxSDLDK54GkcLk-h33HyD4P0,383
|
4
|
-
haiway/context/access.py,sha256=pdc0pZIwQlW24hDcojoE3lUrpC2zvyH-PtoOOGS7D9g,11263
|
5
|
-
haiway/context/dependencies.py,sha256=US1TYM78AHujat9SxLKrP7Ow1dctdLN-OqEx3grBv0Y,1609
|
6
|
-
haiway/context/metrics.py,sha256=OF06T9Er62SJPCZmQF9SnMW8lO9HGxDP3qUkvk6mXUo,8672
|
7
|
-
haiway/context/state.py,sha256=aQgUE3tAWPEYgGv4P16Yk9RkOouY8UpkEpjhzv0AwkA,3014
|
8
|
-
haiway/context/tasks.py,sha256=xXtXIUwXOra0EePTdkcEbMOmpWwFcO3hCRfR_IfvAHk,1978
|
9
|
-
haiway/context/types.py,sha256=OS3d2A0F2CbOpz_hxAhBitSH2_Bs8Aw-kIL6mEBmZdw,214
|
10
|
-
haiway/helpers/__init__.py,sha256=WBGQ3pF6EhCjEQLpMfpW7LwF5u-iR26fr9q6l7xTEJE,335
|
11
|
-
haiway/helpers/asynchronous.py,sha256=FYcV_ERrGy7f47y18oKeUk7fcbk-i_SOuPQ7lHhX5iI,6119
|
12
|
-
haiway/helpers/cache.py,sha256=oONE0xKIfhvPA_wnY8JV_2CtOeSrHAoNpu5n7t-jd8w,9540
|
13
|
-
haiway/helpers/retry.py,sha256=qFeLvOiqAktF-awkh-hTdhyT2zf6kGV0VxAIBrUVdn0,7135
|
14
|
-
haiway/helpers/throttling.py,sha256=zo0OwFq64si5KUwhd58cFHLmGAmYwRbFRJMbv9suhPs,3844
|
15
|
-
haiway/helpers/timeout.py,sha256=iNfDotZ4G9rb_zEuEgzaZP37QqkkXQyEFHClA4Eckl0,3084
|
16
|
-
haiway/state/__init__.py,sha256=sFEo9tWwMK5r6RR_D6d2xRL2Tcb7NgWIEUHx-FCbvbI,212
|
17
|
-
haiway/state/attributes.py,sha256=DIjr8NAhabJJiHHiNc_t7-t9AncobiXg_JRZ3lJ-_G0,13403
|
18
|
-
haiway/state/structure.py,sha256=AQIVz-DUBMFMgBHXbOvLrplmdSCmL3xRjCDwNhFTLV8,7079
|
19
|
-
haiway/state/validation.py,sha256=V4Q94Ik4p9t7f-7EIwK3Q9Zki8VkLOjPIGWGwLRVCoc,2873
|
20
|
-
haiway/types/__init__.py,sha256=cAJQzDgFi8AKRqpzY3HWrutaPR69tnJqeJK_mQVtYUk,252
|
21
|
-
haiway/types/frozen.py,sha256=CZhFCXnWAKEhuWSfILxA8smfdpMd5Ku694ycfLh98R8,76
|
22
|
-
haiway/types/missing.py,sha256=3t2bcZuw5fAKiycP-0Aly2TDUWtM3xyHy3KDCT91TLs,1660
|
23
|
-
haiway/utils/__init__.py,sha256=UA9h8YDvYI5rYujvsIS9t5Q-SWYImmk30uhR_42flqs,608
|
24
|
-
haiway/utils/always.py,sha256=2abp8Lm9rQkrfS3rm1Iqhb-IcWyVfH1BULab3KMxgOw,1234
|
25
|
-
haiway/utils/env.py,sha256=lKPOBZWyRD_gQariDGBjVLYTm0740nytPCSQpK2oRyE,3136
|
26
|
-
haiway/utils/immutable.py,sha256=K34ZIMzbkpgkHKH-KF73plEbXExsajNRkRTYp9nJEf4,620
|
27
|
-
haiway/utils/logs.py,sha256=oDsc1ZdqKDjlTlctLbDcp9iX98Acr-1tdw-Pyg3DElo,1577
|
28
|
-
haiway/utils/mimic.py,sha256=BkVjTVP2TxxC8GChPGyDV6UXVwJmiRiSWeOYZNZFHxs,1828
|
29
|
-
haiway/utils/noop.py,sha256=qgbZlOKWY6_23Zs43OLukK2HagIQKRyR04zrFVm5rWI,344
|
30
|
-
haiway/utils/queue.py,sha256=WGW8kSusIwRYHsYRIKD2CaqhhC1pUtVgtNHFDXDtYrw,2443
|
31
|
-
haiway-0.1.0.dist-info/LICENSE,sha256=GehQEW_I1pkmxkkj3NEa7rCTQKYBn7vTPabpDYJlRuo,1063
|
32
|
-
haiway-0.1.0.dist-info/METADATA,sha256=ZHdNdTn7cjJQUK6pK8onjkNS0QmOhjpFCWLsQyqQE0o,3872
|
33
|
-
haiway-0.1.0.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
|
34
|
-
haiway-0.1.0.dist-info/top_level.txt,sha256=_LdXVLzUzgkvAGQnQJj5kQfoFhpPW6EF4Kj9NapniLg,7
|
35
|
-
haiway-0.1.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|