haiway 0.12.1__py3-none-any.whl → 0.13.1__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/context/access.py +6 -6
- haiway/context/state.py +5 -5
- haiway/context/tasks.py +1 -1
- haiway/helpers/__init__.py +4 -1
- haiway/helpers/asynchrony.py +18 -16
- haiway/helpers/caching.py +240 -138
- haiway/helpers/retries.py +3 -3
- haiway/helpers/throttling.py +16 -16
- haiway/helpers/timeouted.py +7 -6
- haiway/utils/always.py +2 -2
- {haiway-0.12.1.dist-info → haiway-0.13.1.dist-info}/METADATA +5 -5
- {haiway-0.12.1.dist-info → haiway-0.13.1.dist-info}/RECORD +14 -14
- {haiway-0.12.1.dist-info → haiway-0.13.1.dist-info}/licenses/LICENSE +2 -2
- {haiway-0.12.1.dist-info → haiway-0.13.1.dist-info}/WHEEL +0 -0
haiway/context/access.py
CHANGED
@@ -238,8 +238,8 @@ class ScopeContext:
|
|
238
238
|
@overload
|
239
239
|
def __call__[Result, **Arguments](
|
240
240
|
self,
|
241
|
-
function: Callable[Arguments, Coroutine[
|
242
|
-
) -> Callable[Arguments, Coroutine[
|
241
|
+
function: Callable[Arguments, Coroutine[Any, Any, Result]],
|
242
|
+
) -> Callable[Arguments, Coroutine[Any, Any, Result]]: ...
|
243
243
|
|
244
244
|
@overload
|
245
245
|
def __call__[Result, **Arguments](
|
@@ -249,8 +249,8 @@ class ScopeContext:
|
|
249
249
|
|
250
250
|
def __call__[Result, **Arguments](
|
251
251
|
self,
|
252
|
-
function: Callable[Arguments, Coroutine[
|
253
|
-
) -> Callable[Arguments, Coroutine[
|
252
|
+
function: Callable[Arguments, Coroutine[Any, Any, Result]] | Callable[Arguments, Result],
|
253
|
+
) -> Callable[Arguments, Coroutine[Any, Any, Result]] | Callable[Arguments, Result]:
|
254
254
|
if iscoroutinefunction(function):
|
255
255
|
|
256
256
|
async def async_context(
|
@@ -372,7 +372,7 @@ class ctx:
|
|
372
372
|
|
373
373
|
@staticmethod
|
374
374
|
def spawn[Result, **Arguments](
|
375
|
-
function: Callable[Arguments, Coroutine[
|
375
|
+
function: Callable[Arguments, Coroutine[Any, Any, Result]],
|
376
376
|
/,
|
377
377
|
*args: Arguments.args,
|
378
378
|
**kwargs: Arguments.kwargs,
|
@@ -383,7 +383,7 @@ class ctx:
|
|
383
383
|
|
384
384
|
Parameters
|
385
385
|
----------
|
386
|
-
function: Callable[Arguments, Coroutine[
|
386
|
+
function: Callable[Arguments, Coroutine[Any, Any, Result]]
|
387
387
|
function to be called within the task group
|
388
388
|
|
389
389
|
*args: Arguments.args
|
haiway/context/state.py
CHANGED
@@ -91,11 +91,6 @@ class ScopeState:
|
|
91
91
|
class StateContext:
|
92
92
|
_context = ContextVar[ScopeState]("StateContext")
|
93
93
|
|
94
|
-
__slots__ = (
|
95
|
-
"_state",
|
96
|
-
"_token",
|
97
|
-
)
|
98
|
-
|
99
94
|
@classmethod
|
100
95
|
def current[StateType: State](
|
101
96
|
cls,
|
@@ -122,6 +117,11 @@ class StateContext:
|
|
122
117
|
except LookupError: # create root scope when missing
|
123
118
|
return cls(state=ScopeState(state))
|
124
119
|
|
120
|
+
__slots__ = (
|
121
|
+
"_state",
|
122
|
+
"_token",
|
123
|
+
)
|
124
|
+
|
125
125
|
def __init__(
|
126
126
|
self,
|
127
127
|
state: ScopeState,
|
haiway/context/tasks.py
CHANGED
@@ -16,7 +16,7 @@ class TaskGroupContext:
|
|
16
16
|
@classmethod
|
17
17
|
def run[Result, **Arguments](
|
18
18
|
cls,
|
19
|
-
function: Callable[Arguments, Coroutine[
|
19
|
+
function: Callable[Arguments, Coroutine[Any, Any, Result]],
|
20
20
|
/,
|
21
21
|
*args: Arguments.args,
|
22
22
|
**kwargs: Arguments.kwargs,
|
haiway/helpers/__init__.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
from haiway.helpers.asynchrony import asynchronous, wrap_async
|
2
|
-
from haiway.helpers.caching import cache
|
2
|
+
from haiway.helpers.caching import CacheMakeKey, CacheRead, CacheWrite, cache
|
3
3
|
from haiway.helpers.metrics import MetricsHolder, MetricsLogger
|
4
4
|
from haiway.helpers.retries import retry
|
5
5
|
from haiway.helpers.throttling import throttle
|
@@ -8,6 +8,9 @@ from haiway.helpers.tracing import ArgumentsTrace, ResultTrace, traced
|
|
8
8
|
|
9
9
|
__all__ = [
|
10
10
|
"ArgumentsTrace",
|
11
|
+
"CacheMakeKey",
|
12
|
+
"CacheRead",
|
13
|
+
"CacheWrite",
|
11
14
|
"MetricsHolder",
|
12
15
|
"MetricsLogger",
|
13
16
|
"ResultTrace",
|
haiway/helpers/asynchrony.py
CHANGED
@@ -14,9 +14,9 @@ __all__ = [
|
|
14
14
|
|
15
15
|
|
16
16
|
def wrap_async[**Args, Result](
|
17
|
-
function: Callable[Args, Coroutine[
|
17
|
+
function: Callable[Args, Coroutine[Any, Any, Result]] | Callable[Args, Result],
|
18
18
|
/,
|
19
|
-
) -> Callable[Args, Coroutine[
|
19
|
+
) -> Callable[Args, Coroutine[Any, Any, Result]]:
|
20
20
|
if iscoroutinefunction(function):
|
21
21
|
return function
|
22
22
|
|
@@ -30,10 +30,12 @@ def wrap_async[**Args, Result](
|
|
30
30
|
|
31
31
|
|
32
32
|
@overload
|
33
|
-
def asynchronous[**Args, Result]() ->
|
34
|
-
|
35
|
-
|
36
|
-
]
|
33
|
+
def asynchronous[**Args, Result]() -> (
|
34
|
+
Callable[
|
35
|
+
[Callable[Args, Result]],
|
36
|
+
Callable[Args, Coroutine[Any, Any, Result]],
|
37
|
+
]
|
38
|
+
): ...
|
37
39
|
|
38
40
|
|
39
41
|
@overload
|
@@ -43,7 +45,7 @@ def asynchronous[**Args, Result](
|
|
43
45
|
executor: Executor,
|
44
46
|
) -> Callable[
|
45
47
|
[Callable[Args, Result]],
|
46
|
-
Callable[Args, Coroutine[
|
48
|
+
Callable[Args, Coroutine[Any, Any, Result]],
|
47
49
|
]: ...
|
48
50
|
|
49
51
|
|
@@ -51,7 +53,7 @@ def asynchronous[**Args, Result](
|
|
51
53
|
def asynchronous[**Args, Result](
|
52
54
|
function: Callable[Args, Result],
|
53
55
|
/,
|
54
|
-
) -> Callable[Args, Coroutine[
|
56
|
+
) -> Callable[Args, Coroutine[Any, Any, Result]]: ...
|
55
57
|
|
56
58
|
|
57
59
|
def asynchronous[**Args, Result](
|
@@ -62,9 +64,9 @@ def asynchronous[**Args, Result](
|
|
62
64
|
) -> (
|
63
65
|
Callable[
|
64
66
|
[Callable[Args, Result]],
|
65
|
-
Callable[Args, Coroutine[
|
67
|
+
Callable[Args, Coroutine[Any, Any, Result]],
|
66
68
|
]
|
67
|
-
| Callable[Args, Coroutine[
|
69
|
+
| Callable[Args, Coroutine[Any, Any, Result]]
|
68
70
|
):
|
69
71
|
"""\
|
70
72
|
Wrapper for a sync function to convert it to an async function. \
|
@@ -90,7 +92,7 @@ def asynchronous[**Args, Result](
|
|
90
92
|
|
91
93
|
def wrap(
|
92
94
|
wrapped: Callable[Args, Result],
|
93
|
-
) -> Callable[Args, Coroutine[
|
95
|
+
) -> Callable[Args, Coroutine[Any, Any, Result]]:
|
94
96
|
assert not iscoroutinefunction(wrapped), "Cannot wrap async function in executor" # nosec: B101
|
95
97
|
|
96
98
|
return _ExecutorWrapper(
|
@@ -152,7 +154,7 @@ class _ExecutorWrapper[**Args, Result]:
|
|
152
154
|
instance: object,
|
153
155
|
owner: type | None = None,
|
154
156
|
/,
|
155
|
-
) -> Callable[Args, Coroutine[
|
157
|
+
) -> Callable[Args, Coroutine[Any, Any, Result]]:
|
156
158
|
if owner is None:
|
157
159
|
return self
|
158
160
|
|
@@ -180,8 +182,8 @@ class _ExecutorWrapper[**Args, Result]:
|
|
180
182
|
def _mimic_async[**Args, Result](
|
181
183
|
function: Callable[Args, Result],
|
182
184
|
/,
|
183
|
-
within: Callable[..., Coroutine[
|
184
|
-
) -> Callable[Args, Coroutine[
|
185
|
+
within: Callable[..., Coroutine[Any, Any, Result]],
|
186
|
+
) -> Callable[Args, Coroutine[Any, Any, Result]]:
|
185
187
|
try:
|
186
188
|
annotations: Any = getattr( # noqa: B009
|
187
189
|
function,
|
@@ -192,7 +194,7 @@ def _mimic_async[**Args, Result](
|
|
192
194
|
"__annotations__",
|
193
195
|
{
|
194
196
|
**annotations,
|
195
|
-
"return": Coroutine[
|
197
|
+
"return": Coroutine[Any, Any, annotations.get("return", Any)],
|
196
198
|
},
|
197
199
|
)
|
198
200
|
|
@@ -234,6 +236,6 @@ def _mimic_async[**Args, Result](
|
|
234
236
|
)
|
235
237
|
|
236
238
|
return cast(
|
237
|
-
Callable[Args, Coroutine[
|
239
|
+
Callable[Args, Coroutine[Any, Any, Result]],
|
238
240
|
within,
|
239
241
|
)
|
haiway/helpers/caching.py
CHANGED
@@ -1,18 +1,44 @@
|
|
1
|
-
from asyncio import
|
1
|
+
from asyncio import iscoroutinefunction
|
2
2
|
from collections import OrderedDict
|
3
3
|
from collections.abc import Callable, Coroutine, Hashable
|
4
|
-
from functools import _make_key
|
4
|
+
from functools import _make_key # pyright: ignore[reportPrivateUsage]
|
5
5
|
from time import monotonic
|
6
|
-
from typing import NamedTuple, cast, overload
|
7
|
-
from weakref import ref
|
6
|
+
from typing import Any, NamedTuple, Protocol, cast, overload
|
8
7
|
|
8
|
+
from haiway.context.access import ctx
|
9
9
|
from haiway.utils.mimic import mimic_function
|
10
10
|
|
11
11
|
__all__ = [
|
12
|
+
"CacheMakeKey",
|
13
|
+
"CacheRead",
|
14
|
+
"CacheWrite",
|
12
15
|
"cache",
|
13
16
|
]
|
14
17
|
|
15
18
|
|
19
|
+
class CacheMakeKey[**Args, Key](Protocol):
|
20
|
+
def __call__(
|
21
|
+
self,
|
22
|
+
*args: Args.args,
|
23
|
+
**kwargs: Args.kwargs,
|
24
|
+
) -> Key: ...
|
25
|
+
|
26
|
+
|
27
|
+
class CacheRead[Key, Value](Protocol):
|
28
|
+
async def __call__(
|
29
|
+
self,
|
30
|
+
key: Key,
|
31
|
+
) -> Value | None: ...
|
32
|
+
|
33
|
+
|
34
|
+
class CacheWrite[Key, Value](Protocol):
|
35
|
+
async def __call__(
|
36
|
+
self,
|
37
|
+
key: Key,
|
38
|
+
value: Value,
|
39
|
+
) -> None: ...
|
40
|
+
|
41
|
+
|
16
42
|
@overload
|
17
43
|
def cache[**Args, Result](
|
18
44
|
function: Callable[Args, Result],
|
@@ -21,58 +47,156 @@ def cache[**Args, Result](
|
|
21
47
|
|
22
48
|
|
23
49
|
@overload
|
24
|
-
def cache[**Args, Result](
|
50
|
+
def cache[**Args, Result, Key: Hashable](
|
25
51
|
*,
|
26
|
-
limit: int =
|
52
|
+
limit: int | None = None,
|
27
53
|
expiration: float | None = None,
|
54
|
+
make_key: CacheMakeKey[Args, Key] | None = None,
|
28
55
|
) -> Callable[[Callable[Args, Result]], Callable[Args, Result]]: ...
|
29
56
|
|
30
57
|
|
31
|
-
|
58
|
+
@overload
|
59
|
+
def cache[**Args, Result, Key](
|
60
|
+
*,
|
61
|
+
make_key: CacheMakeKey[Args, Key],
|
62
|
+
read: CacheRead[Key, Result],
|
63
|
+
write: CacheWrite[Key, Result],
|
64
|
+
) -> Callable[
|
65
|
+
[Callable[Args, Coroutine[Any, Any, Result]]], Callable[Args, Coroutine[Any, Any, Result]]
|
66
|
+
]: ...
|
67
|
+
|
68
|
+
|
69
|
+
def cache[**Args, Result, Key]( # noqa: PLR0913
|
32
70
|
function: Callable[Args, Result] | None = None,
|
33
71
|
*,
|
34
|
-
limit: int =
|
72
|
+
limit: int | None = None,
|
35
73
|
expiration: float | None = None,
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
74
|
+
make_key: CacheMakeKey[Args, Key] | None = None,
|
75
|
+
read: CacheRead[Key, Result] | None = None,
|
76
|
+
write: CacheWrite[Key, Result] | None = None,
|
77
|
+
) -> (
|
78
|
+
Callable[
|
79
|
+
[Callable[Args, Coroutine[Any, Any, Result]]],
|
80
|
+
Callable[Args, Coroutine[Any, Any, Result]],
|
81
|
+
]
|
82
|
+
| Callable[[Callable[Args, Result]], Callable[Args, Result]]
|
83
|
+
| Callable[Args, Result]
|
84
|
+
):
|
85
|
+
"""
|
86
|
+
Memoize the result of a function using a configurable cache.
|
42
87
|
|
43
88
|
Parameters
|
44
89
|
----------
|
45
|
-
function: Callable[
|
46
|
-
function to
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
90
|
+
function : Callable[Args, Result] | None
|
91
|
+
The function to be memoized.
|
92
|
+
When used as a simple decorator (i.e., `@cache`), this is the decorated function.
|
93
|
+
Should be omitted when cache is called with configuration arguments.
|
94
|
+
limit : int | None
|
95
|
+
The maximum number of entries to keep in the cache.
|
96
|
+
Defaults to 1 if not specified.
|
97
|
+
Ignored when using custom cache implementations (read/write).
|
98
|
+
expiration : float | None
|
99
|
+
Time in seconds after which a cache entry expires and will be recomputed.
|
100
|
+
Defaults to None, meaning entries don't expire based on time.
|
101
|
+
Ignored when using custom cache implementations (read/write).
|
102
|
+
make_key : CacheMakeKey[Args, Key] | None
|
103
|
+
Function to generate a cache key from function arguments.
|
104
|
+
If None, uses a default implementation that handles most cases.
|
105
|
+
Required when using custom cache implementations (read/write).
|
106
|
+
read : CacheRead[Key, Result] | None
|
107
|
+
Custom asynchronous function to read values from cache.
|
108
|
+
Must be provided together with `write` and `make_key`.
|
109
|
+
Only available for async functions.
|
110
|
+
write : CacheWrite[Key, Result] | None
|
111
|
+
Custom asynchronous function to write values to cache.
|
112
|
+
Must be provided together with `read` and `make_key`.
|
113
|
+
Only available for async functions.
|
51
114
|
|
52
115
|
Returns
|
53
116
|
-------
|
54
|
-
Callable
|
55
|
-
|
117
|
+
Callable
|
118
|
+
If `function` is provided as a positional argument, returns the memoized function.
|
119
|
+
Otherwise returns a decorator that can be applied to a function to memoize it
|
120
|
+
with the given configuration.
|
121
|
+
|
122
|
+
Notes
|
123
|
+
-----
|
124
|
+
This decorator supports both synchronous and asynchronous functions.
|
125
|
+
The default implementation uses a simple in-memory LRU cache.
|
126
|
+
For asynchronous functions, you can provide custom cache implementations
|
127
|
+
via the `read` and `write` parameters.
|
128
|
+
|
129
|
+
The default cache is not thread-safe and should not be used in multi-threaded
|
130
|
+
applications without external synchronization.
|
131
|
+
|
132
|
+
Examples
|
133
|
+
--------
|
134
|
+
Simple usage as a decorator:
|
135
|
+
|
136
|
+
>>> @cache
|
137
|
+
... def my_function(x: int) -> int:
|
138
|
+
... print("Function called")
|
139
|
+
... return x * 2
|
140
|
+
>>> my_function(5)
|
141
|
+
Function called
|
142
|
+
10
|
143
|
+
>>> my_function(5) # Cache hit, function body not executed
|
144
|
+
10
|
145
|
+
|
146
|
+
With configuration parameters:
|
147
|
+
|
148
|
+
>>> @cache(limit=10, expiration=60.0)
|
149
|
+
... def my_function(x: int) -> int:
|
150
|
+
... return x * 2
|
151
|
+
|
152
|
+
With custom cache for async functions:
|
153
|
+
|
154
|
+
>>> @cache(make_key=custom_key_maker, read=redis_read, write=redis_write)
|
155
|
+
... async def fetch_data(user_id: str) -> dict:
|
156
|
+
... return await api_call(user_id)
|
56
157
|
"""
|
57
158
|
|
58
159
|
def _wrap(function: Callable[Args, Result]) -> Callable[Args, Result]:
|
59
160
|
if iscoroutinefunction(function):
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
161
|
+
if read is not None and write is not None and make_key is not None:
|
162
|
+
assert limit is None and expiration is None # nosec: B101
|
163
|
+
return cast(
|
164
|
+
Callable[Args, Result],
|
165
|
+
_CustomCache(
|
166
|
+
function,
|
167
|
+
make_key=make_key,
|
168
|
+
read=read,
|
169
|
+
write=write,
|
170
|
+
),
|
171
|
+
)
|
172
|
+
|
173
|
+
else:
|
174
|
+
assert read is None and write is None # nosec: B101
|
175
|
+
return cast(
|
176
|
+
Callable[Args, Result],
|
177
|
+
_AsyncCache(
|
178
|
+
function,
|
179
|
+
limit=limit if limit is not None else 1,
|
180
|
+
expiration=expiration,
|
181
|
+
make_key=cast(
|
182
|
+
CacheMakeKey[Args, Hashable],
|
183
|
+
make_key if make_key is not None else _default_make_key,
|
184
|
+
),
|
185
|
+
),
|
186
|
+
)
|
68
187
|
|
69
188
|
else:
|
189
|
+
assert read is None and write is None, "Custom sync cache is not supported" # nosec: B101
|
70
190
|
return cast(
|
71
191
|
Callable[Args, Result],
|
72
192
|
_SyncCache(
|
73
193
|
function,
|
74
|
-
limit=limit,
|
194
|
+
limit=limit if limit is not None else 1,
|
75
195
|
expiration=expiration,
|
196
|
+
make_key=cast(
|
197
|
+
CacheMakeKey[Args, Hashable],
|
198
|
+
make_key if make_key is not None else _default_make_key,
|
199
|
+
),
|
76
200
|
),
|
77
201
|
)
|
78
202
|
|
@@ -101,6 +225,7 @@ class _SyncCache[**Args, Result]:
|
|
101
225
|
"_cached",
|
102
226
|
"_function",
|
103
227
|
"_limit",
|
228
|
+
"_make_key",
|
104
229
|
"_next_expire_time",
|
105
230
|
)
|
106
231
|
|
@@ -110,10 +235,13 @@ class _SyncCache[**Args, Result]:
|
|
110
235
|
/,
|
111
236
|
limit: int,
|
112
237
|
expiration: float | None,
|
238
|
+
make_key: CacheMakeKey[Args, Hashable],
|
113
239
|
) -> None:
|
114
240
|
self._function: Callable[Args, Result] = function
|
115
241
|
self._cached: OrderedDict[Hashable, _CacheEntry[Result]] = OrderedDict()
|
116
242
|
self._limit: int = limit
|
243
|
+
self._make_key: CacheMakeKey[Args, Hashable] = make_key
|
244
|
+
|
117
245
|
if expiration := expiration:
|
118
246
|
|
119
247
|
def next_expire_time() -> float | None:
|
@@ -135,27 +263,17 @@ class _SyncCache[**Args, Result]:
|
|
135
263
|
owner: type | None = None,
|
136
264
|
/,
|
137
265
|
) -> Callable[Args, Result]:
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
else:
|
142
|
-
return mimic_function(
|
143
|
-
self._function,
|
144
|
-
within=partial(
|
145
|
-
self.__method_call__,
|
146
|
-
instance,
|
147
|
-
),
|
148
|
-
)
|
266
|
+
assert owner is None and instance is None, "cache does not work for classes" # nosec: B101
|
267
|
+
return self
|
149
268
|
|
150
269
|
def __call__(
|
151
270
|
self,
|
152
271
|
*args: Args.args,
|
153
272
|
**kwargs: Args.kwargs,
|
154
273
|
) -> Result:
|
155
|
-
key: Hashable = _make_key(
|
156
|
-
args
|
157
|
-
|
158
|
-
typed=True,
|
274
|
+
key: Hashable = self._make_key(
|
275
|
+
*args,
|
276
|
+
**kwargs,
|
159
277
|
)
|
160
278
|
|
161
279
|
match self._cached.get(key):
|
@@ -164,7 +282,6 @@ class _SyncCache[**Args, Result]:
|
|
164
282
|
|
165
283
|
case entry:
|
166
284
|
if (expire := entry[1]) and expire < monotonic():
|
167
|
-
# if still running let it complete if able
|
168
285
|
del self._cached[key] # continue the same way as if empty
|
169
286
|
|
170
287
|
else:
|
@@ -178,47 +295,11 @@ class _SyncCache[**Args, Result]:
|
|
178
295
|
)
|
179
296
|
|
180
297
|
if len(self._cached) > self._limit:
|
181
|
-
#
|
298
|
+
# keep the size limit
|
182
299
|
self._cached.popitem(last=False)
|
183
300
|
|
184
301
|
return result
|
185
302
|
|
186
|
-
def __method_call__(
|
187
|
-
self,
|
188
|
-
__method_self: object,
|
189
|
-
*args: Args.args,
|
190
|
-
**kwargs: Args.kwargs,
|
191
|
-
) -> Result:
|
192
|
-
key: Hashable = _make_key(
|
193
|
-
args=(ref(__method_self), *args),
|
194
|
-
kwds=kwargs,
|
195
|
-
typed=True,
|
196
|
-
)
|
197
|
-
|
198
|
-
match self._cached.get(key):
|
199
|
-
case None:
|
200
|
-
pass
|
201
|
-
|
202
|
-
case entry:
|
203
|
-
if (expire := entry[1]) and expire < monotonic():
|
204
|
-
# if still running let it complete if able
|
205
|
-
del self._cached[key] # continue the same way as if empty
|
206
|
-
|
207
|
-
else:
|
208
|
-
self._cached.move_to_end(key)
|
209
|
-
return entry[0]
|
210
|
-
|
211
|
-
result: Result = self._function(__method_self, *args, **kwargs) # pyright: ignore[reportUnknownVariableType, reportCallIssue]
|
212
|
-
self._cached[key] = _CacheEntry(
|
213
|
-
value=result, # pyright: ignore[reportUnknownArgumentType]
|
214
|
-
expire=self._next_expire_time(),
|
215
|
-
)
|
216
|
-
if len(self._cached) > self._limit:
|
217
|
-
# if still running let it complete if able
|
218
|
-
self._cached.popitem(last=False)
|
219
|
-
|
220
|
-
return result # pyright: ignore[reportUnknownArgumentType, reportUnknownVariableType]
|
221
|
-
|
222
303
|
|
223
304
|
class _AsyncCache[**Args, Result]:
|
224
305
|
__slots__ = (
|
@@ -233,19 +314,23 @@ class _AsyncCache[**Args, Result]:
|
|
233
314
|
"_cached",
|
234
315
|
"_function",
|
235
316
|
"_limit",
|
317
|
+
"_make_key",
|
236
318
|
"_next_expire_time",
|
237
319
|
)
|
238
320
|
|
239
321
|
def __init__(
|
240
322
|
self,
|
241
|
-
function: Callable[Args, Coroutine[
|
323
|
+
function: Callable[Args, Coroutine[Any, Any, Result]],
|
242
324
|
/,
|
243
325
|
limit: int,
|
244
326
|
expiration: float | None,
|
327
|
+
make_key: CacheMakeKey[Args, Hashable],
|
245
328
|
) -> None:
|
246
|
-
self._function: Callable[Args, Coroutine[
|
247
|
-
self._cached: OrderedDict[Hashable, _CacheEntry[
|
329
|
+
self._function: Callable[Args, Coroutine[Any, Any, Result]] = function
|
330
|
+
self._cached: OrderedDict[Hashable, _CacheEntry[Result]] = OrderedDict()
|
248
331
|
self._limit: int = limit
|
332
|
+
self._make_key: CacheMakeKey[Args, Hashable] = make_key
|
333
|
+
|
249
334
|
if expiration := expiration:
|
250
335
|
|
251
336
|
def next_expire_time() -> float | None:
|
@@ -266,29 +351,18 @@ class _AsyncCache[**Args, Result]:
|
|
266
351
|
instance: object | None,
|
267
352
|
owner: type | None = None,
|
268
353
|
/,
|
269
|
-
) -> Callable[Args, Coroutine[
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
else:
|
274
|
-
return mimic_function(
|
275
|
-
self._function,
|
276
|
-
within=partial(
|
277
|
-
self.__method_call__,
|
278
|
-
instance,
|
279
|
-
),
|
280
|
-
)
|
354
|
+
) -> Callable[Args, Coroutine[Any, Any, Result]]:
|
355
|
+
assert owner is None and instance is None, "cache does not work for classes" # nosec: B101
|
356
|
+
return self
|
281
357
|
|
282
358
|
async def __call__(
|
283
359
|
self,
|
284
360
|
*args: Args.args,
|
285
361
|
**kwargs: Args.kwargs,
|
286
362
|
) -> Result:
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
kwds=kwargs,
|
291
|
-
typed=True,
|
363
|
+
key: Hashable = self._make_key(
|
364
|
+
*args,
|
365
|
+
**kwargs,
|
292
366
|
)
|
293
367
|
|
294
368
|
match self._cached.get(key):
|
@@ -297,60 +371,88 @@ class _AsyncCache[**Args, Result]:
|
|
297
371
|
|
298
372
|
case entry:
|
299
373
|
if (expire := entry[1]) and expire < monotonic():
|
300
|
-
# if still running let it complete if able
|
301
374
|
del self._cached[key] # continue the same way as if empty
|
302
375
|
|
303
376
|
else:
|
304
377
|
self._cached.move_to_end(key)
|
305
|
-
return
|
378
|
+
return entry[0]
|
306
379
|
|
307
|
-
|
380
|
+
result: Result = await self._function(*args, **kwargs)
|
308
381
|
self._cached[key] = _CacheEntry(
|
309
|
-
value=
|
382
|
+
value=result,
|
310
383
|
expire=self._next_expire_time(),
|
311
384
|
)
|
312
385
|
if len(self._cached) > self._limit:
|
313
|
-
#
|
386
|
+
# keep the size limit
|
314
387
|
self._cached.popitem(last=False)
|
315
388
|
|
316
|
-
return
|
389
|
+
return result
|
390
|
+
|
391
|
+
|
392
|
+
class _CustomCache[**Args, Result, Key]:
|
393
|
+
__slots__ = (
|
394
|
+
"__annotations__",
|
395
|
+
"__defaults__",
|
396
|
+
"__doc__",
|
397
|
+
"__globals__",
|
398
|
+
"__kwdefaults__",
|
399
|
+
"__name__",
|
400
|
+
"__qualname__",
|
401
|
+
"__wrapped__",
|
402
|
+
"_expiration",
|
403
|
+
"_function",
|
404
|
+
"_make_key",
|
405
|
+
"_read",
|
406
|
+
"_write",
|
407
|
+
)
|
408
|
+
|
409
|
+
def __init__(
|
410
|
+
self,
|
411
|
+
function: Callable[Args, Coroutine[Any, Any, Result]],
|
412
|
+
/,
|
413
|
+
make_key: CacheMakeKey[Args, Key],
|
414
|
+
read: CacheRead[Key, Result],
|
415
|
+
write: CacheWrite[Key, Result],
|
416
|
+
) -> None:
|
417
|
+
self._function: Callable[Args, Coroutine[Any, Any, Result]] = function
|
418
|
+
self._make_key: CacheMakeKey[Args, Key] = make_key
|
419
|
+
self._read: CacheRead[Key, Result] = read
|
420
|
+
self._write: CacheWrite[Key, Result] = write
|
317
421
|
|
318
|
-
|
422
|
+
# mimic function attributes if able
|
423
|
+
mimic_function(function, within=self)
|
424
|
+
|
425
|
+
async def __call__(
|
319
426
|
self,
|
320
|
-
__method_self: object,
|
321
427
|
*args: Args.args,
|
322
428
|
**kwargs: Args.kwargs,
|
323
429
|
) -> Result:
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
kwds=kwargs,
|
328
|
-
typed=True,
|
430
|
+
key: Key = self._make_key(
|
431
|
+
*args,
|
432
|
+
**kwargs,
|
329
433
|
)
|
330
434
|
|
331
|
-
match self.
|
435
|
+
match await self._read(key):
|
332
436
|
case None:
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
437
|
+
result: Result = await self._function(*args, **kwargs)
|
438
|
+
ctx.spawn( # write the value asnychronously
|
439
|
+
self._write,
|
440
|
+
key=key,
|
441
|
+
value=result,
|
442
|
+
)
|
339
443
|
|
340
|
-
|
341
|
-
self._cached.move_to_end(key)
|
342
|
-
return await shield(entry[0])
|
444
|
+
return result
|
343
445
|
|
344
|
-
|
345
|
-
|
346
|
-
)
|
347
|
-
self._cached[key] = _CacheEntry(
|
348
|
-
value=task,
|
349
|
-
expire=self._next_expire_time(),
|
350
|
-
)
|
446
|
+
case entry:
|
447
|
+
return entry
|
351
448
|
|
352
|
-
if len(self._cached) > self._limit:
|
353
|
-
# if still running let it complete if able
|
354
|
-
self._cached.popitem(last=False)
|
355
449
|
|
356
|
-
|
450
|
+
def _default_make_key[**Args](
|
451
|
+
*args: Args.args,
|
452
|
+
**kwargs: Args.kwargs,
|
453
|
+
) -> Hashable:
|
454
|
+
return _make_key(
|
455
|
+
args=args,
|
456
|
+
kwds=kwargs,
|
457
|
+
typed=True,
|
458
|
+
)
|
haiway/helpers/retries.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
from asyncio import CancelledError, iscoroutinefunction, sleep
|
2
2
|
from collections.abc import Callable, Coroutine
|
3
3
|
from time import sleep as sleep_sync
|
4
|
-
from typing import cast, overload
|
4
|
+
from typing import Any, cast, overload
|
5
5
|
|
6
6
|
from haiway.context import ctx
|
7
7
|
from haiway.utils import mimic_function
|
@@ -178,12 +178,12 @@ def _wrap_sync[**Args, Result](
|
|
178
178
|
|
179
179
|
|
180
180
|
def _wrap_async[**Args, Result](
|
181
|
-
function: Callable[Args, Coroutine[
|
181
|
+
function: Callable[Args, Coroutine[Any, Any, Result]],
|
182
182
|
*,
|
183
183
|
limit: int,
|
184
184
|
delay: Callable[[int, Exception], float] | float | None,
|
185
185
|
catching: set[type[Exception]] | tuple[type[Exception], ...],
|
186
|
-
) -> Callable[Args, Coroutine[
|
186
|
+
) -> Callable[Args, Coroutine[Any, Any, Result]]:
|
187
187
|
assert limit > 0, "Limit has to be greater than zero" # nosec: B101
|
188
188
|
|
189
189
|
@mimic_function(function)
|
haiway/helpers/throttling.py
CHANGED
@@ -7,7 +7,7 @@ from collections import deque
|
|
7
7
|
from collections.abc import Callable, Coroutine
|
8
8
|
from datetime import timedelta
|
9
9
|
from time import monotonic
|
10
|
-
from typing import cast, overload
|
10
|
+
from typing import Any, cast, overload
|
11
11
|
|
12
12
|
from haiway.utils.mimic import mimic_function
|
13
13
|
|
@@ -18,9 +18,9 @@ __all__ = [
|
|
18
18
|
|
19
19
|
@overload
|
20
20
|
def throttle[**Args, Result](
|
21
|
-
function: Callable[Args, Coroutine[
|
21
|
+
function: Callable[Args, Coroutine[Any, Any, Result]],
|
22
22
|
/,
|
23
|
-
) -> Callable[Args, Coroutine[
|
23
|
+
) -> Callable[Args, Coroutine[Any, Any, Result]]: ...
|
24
24
|
|
25
25
|
|
26
26
|
@overload
|
@@ -29,21 +29,21 @@ def throttle[**Args, Result](
|
|
29
29
|
limit: int = 1,
|
30
30
|
period: timedelta | float = 1,
|
31
31
|
) -> Callable[
|
32
|
-
[Callable[Args, Coroutine[
|
32
|
+
[Callable[Args, Coroutine[Any, Any, Result]]], Callable[Args, Coroutine[Any, Any, Result]]
|
33
33
|
]: ...
|
34
34
|
|
35
35
|
|
36
36
|
def throttle[**Args, Result](
|
37
|
-
function: Callable[Args, Coroutine[
|
37
|
+
function: Callable[Args, Coroutine[Any, Any, Result]] | None = None,
|
38
38
|
*,
|
39
39
|
limit: int = 1,
|
40
40
|
period: timedelta | float = 1,
|
41
41
|
) -> (
|
42
42
|
Callable[
|
43
|
-
[Callable[Args, Coroutine[
|
44
|
-
Callable[Args, Coroutine[
|
43
|
+
[Callable[Args, Coroutine[Any, Any, Result]]],
|
44
|
+
Callable[Args, Coroutine[Any, Any, Result]],
|
45
45
|
]
|
46
|
-
| Callable[Args, Coroutine[
|
46
|
+
| Callable[Args, Coroutine[Any, Any, Result]]
|
47
47
|
):
|
48
48
|
"""\
|
49
49
|
Throttle for function calls with custom limit and period time. \
|
@@ -53,7 +53,7 @@ def throttle[**Args, Result](
|
|
53
53
|
|
54
54
|
Parameters
|
55
55
|
----------
|
56
|
-
function: Callable[Args, Coroutine[
|
56
|
+
function: Callable[Args, Coroutine[Any, Any, Result]]
|
57
57
|
function to wrap in throttle
|
58
58
|
limit: int
|
59
59
|
limit of executions in given period, if no period was specified
|
@@ -63,17 +63,17 @@ def throttle[**Args, Result](
|
|
63
63
|
|
64
64
|
Returns
|
65
65
|
-------
|
66
|
-
Callable[[Callable[Args, Coroutine[
|
67
|
-
| Callable[Args, Coroutine[
|
66
|
+
Callable[[Callable[Args, Coroutine[Any, Any, Result]]], Callable[Args, Coroutine[Any, Any, Result]]] \
|
67
|
+
| Callable[Args, Coroutine[Any, Any, Result]]
|
68
68
|
provided function wrapped in throttle
|
69
69
|
""" # noqa: E501
|
70
70
|
|
71
71
|
def _wrap(
|
72
|
-
function: Callable[Args, Coroutine[
|
73
|
-
) -> Callable[Args, Coroutine[
|
72
|
+
function: Callable[Args, Coroutine[Any, Any, Result]],
|
73
|
+
) -> Callable[Args, Coroutine[Any, Any, Result]]:
|
74
74
|
assert iscoroutinefunction(function) # nosec: B101
|
75
75
|
return cast(
|
76
|
-
Callable[Args, Coroutine[
|
76
|
+
Callable[Args, Coroutine[Any, Any, Result]],
|
77
77
|
_AsyncThrottle(
|
78
78
|
function,
|
79
79
|
limit=limit,
|
@@ -107,12 +107,12 @@ class _AsyncThrottle[**Args, Result]:
|
|
107
107
|
|
108
108
|
def __init__(
|
109
109
|
self,
|
110
|
-
function: Callable[Args, Coroutine[
|
110
|
+
function: Callable[Args, Coroutine[Any, Any, Result]],
|
111
111
|
/,
|
112
112
|
limit: int,
|
113
113
|
period: timedelta | float,
|
114
114
|
) -> None:
|
115
|
-
self._function: Callable[Args, Coroutine[
|
115
|
+
self._function: Callable[Args, Coroutine[Any, Any, Result]] = function
|
116
116
|
self._entries: deque[float] = deque()
|
117
117
|
self._lock: Lock = Lock()
|
118
118
|
self._limit: int = limit
|
haiway/helpers/timeouted.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
from asyncio import AbstractEventLoop, Future, Task, TimerHandle, get_running_loop
|
2
2
|
from collections.abc import Callable, Coroutine
|
3
|
+
from typing import Any
|
3
4
|
|
4
5
|
from haiway.utils.mimic import mimic_function
|
5
6
|
|
@@ -12,8 +13,8 @@ def timeout[**Args, Result](
|
|
12
13
|
timeout: float,
|
13
14
|
/,
|
14
15
|
) -> Callable[
|
15
|
-
[Callable[Args, Coroutine[
|
16
|
-
Callable[Args, Coroutine[
|
16
|
+
[Callable[Args, Coroutine[Any, Any, Result]]],
|
17
|
+
Callable[Args, Coroutine[Any, Any, Result]],
|
17
18
|
]:
|
18
19
|
"""\
|
19
20
|
Timeout wrapper for a function call. \
|
@@ -34,8 +35,8 @@ def timeout[**Args, Result](
|
|
34
35
|
"""
|
35
36
|
|
36
37
|
def _wrap(
|
37
|
-
function: Callable[Args, Coroutine[
|
38
|
-
) -> Callable[Args, Coroutine[
|
38
|
+
function: Callable[Args, Coroutine[Any, Any, Result]],
|
39
|
+
) -> Callable[Args, Coroutine[Any, Any, Result]]:
|
39
40
|
return _AsyncTimeout(
|
40
41
|
function,
|
41
42
|
timeout=timeout,
|
@@ -60,11 +61,11 @@ class _AsyncTimeout[**Args, Result]:
|
|
60
61
|
|
61
62
|
def __init__(
|
62
63
|
self,
|
63
|
-
function: Callable[Args, Coroutine[
|
64
|
+
function: Callable[Args, Coroutine[Any, Any, Result]],
|
64
65
|
/,
|
65
66
|
timeout: float,
|
66
67
|
) -> None:
|
67
|
-
self._function: Callable[Args, Coroutine[
|
68
|
+
self._function: Callable[Args, Coroutine[Any, Any, Result]] = function
|
68
69
|
self._timeout: float = timeout
|
69
70
|
|
70
71
|
# mimic function attributes if able
|
haiway/utils/always.py
CHANGED
@@ -37,7 +37,7 @@ def always[Value](
|
|
37
37
|
def async_always[Value](
|
38
38
|
value: Value,
|
39
39
|
/,
|
40
|
-
) -> Callable[..., Coroutine[
|
40
|
+
) -> Callable[..., Coroutine[Any, Any, Value]]:
|
41
41
|
"""
|
42
42
|
Factory method creating async functions returning always the same value.
|
43
43
|
|
@@ -48,7 +48,7 @@ def async_always[Value](
|
|
48
48
|
|
49
49
|
Returns
|
50
50
|
-------
|
51
|
-
Callable[..., Coroutine[
|
51
|
+
Callable[..., Coroutine[Any, Any, Value]]
|
52
52
|
async function ignoring arguments and always returning the provided value.
|
53
53
|
"""
|
54
54
|
|
@@ -1,13 +1,13 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: haiway
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.13.1
|
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
|
7
7
|
Maintainer-email: Kacper Kaliński <kacper.kalinski@miquido.com>
|
8
8
|
License: MIT License
|
9
9
|
|
10
|
-
Copyright (c) 2024 Miquido
|
10
|
+
Copyright (c) 2024-2025 Miquido
|
11
11
|
|
12
12
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
13
13
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -40,12 +40,12 @@ Requires-Dist: pyright~=1.1; extra == 'dev'
|
|
40
40
|
Requires-Dist: pytest-asyncio~=0.23; extra == 'dev'
|
41
41
|
Requires-Dist: pytest-cov~=4.1; extra == 'dev'
|
42
42
|
Requires-Dist: pytest~=7.4; extra == 'dev'
|
43
|
-
Requires-Dist: ruff~=0.
|
43
|
+
Requires-Dist: ruff~=0.11; extra == 'dev'
|
44
44
|
Description-Content-Type: text/markdown
|
45
45
|
|
46
46
|
# 🚗 haiway 🚕 🚚 🚙
|
47
47
|
|
48
|
-
haiway is a framework
|
48
|
+
haiway is a framework designed to facilitate the development of applications using the functional programming paradigm combined with structured concurrency concepts. Unlike traditional object-oriented frameworks, haiway emphasizes immutability, pure functions, and context-based state management, enabling developers to build scalable and maintainable applications. By leveraging context managers combined with context vars, haiway ensures safe state propagation in concurrent environments and simplifies dependency injection through function implementation propagation.
|
49
49
|
|
50
50
|
## 🖥️ Install
|
51
51
|
|
@@ -65,7 +65,7 @@ We welcome any feedback and suggestions! Feel free to open an issue or pull requ
|
|
65
65
|
|
66
66
|
MIT License
|
67
67
|
|
68
|
-
Copyright (c) 2024 Miquido
|
68
|
+
Copyright (c) 2024-2025 Miquido
|
69
69
|
|
70
70
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
71
71
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -1,21 +1,21 @@
|
|
1
1
|
haiway/__init__.py,sha256=ONC4Hk0GaPzhQ3oYmgh6Z4kJdXQiyJ8ZQcM_hCUz-IY,2045
|
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=CXGe-qkKeG5352_Bo7D4UusnEEQ_SYmRG94RyK5fG8Q,18951
|
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=
|
10
|
-
haiway/context/tasks.py,sha256=
|
9
|
+
haiway/context/state.py,sha256=qskYoNwN5Ad0OgnyhL-PyGzTZltwVVdE9CEqWWn4lm8,4554
|
10
|
+
haiway/context/tasks.py,sha256=MKfsa-921cIpQ_BKskwokjR27suCHkHZa3O9kOE8UOg,2826
|
11
11
|
haiway/context/types.py,sha256=VvJA7wAPZ3ISpgyThVguioYUXqhHf0XkPfRd0M1ERiQ,142
|
12
|
-
haiway/helpers/__init__.py,sha256=
|
13
|
-
haiway/helpers/asynchrony.py,sha256
|
14
|
-
haiway/helpers/caching.py,sha256=
|
12
|
+
haiway/helpers/__init__.py,sha256=ZKDlL3twDqXyI1a9FDgRy3m1-Dfycvke6BJ4C3CndEk,671
|
13
|
+
haiway/helpers/asynchrony.py,sha256=YHLK5Hjc-5UWlQRypC11yHeEQyeAtHqrMoBTBfqQBvQ,6286
|
14
|
+
haiway/helpers/caching.py,sha256=EU5usTHGDzf0SO3bMW4hHB9oZlLlE7BxO_2ckbjYBw8,13274
|
15
15
|
haiway/helpers/metrics.py,sha256=lCSvat3IrkmytFdqTvsqkVqYcVOK_bByfwYAe0hJIWg,13614
|
16
|
-
haiway/helpers/retries.py,sha256=
|
17
|
-
haiway/helpers/throttling.py,sha256=
|
18
|
-
haiway/helpers/timeouted.py,sha256=
|
16
|
+
haiway/helpers/retries.py,sha256=3m1SsJW_YY_HPufX9LEzcd_MEyRRFNXvSExLeEti8W8,7539
|
17
|
+
haiway/helpers/throttling.py,sha256=r9HnUuo4nX36Pf-oMFHUJk-ZCDeXQ__JTDHlkSltRhA,4121
|
18
|
+
haiway/helpers/timeouted.py,sha256=DthIm4ytKhmiIKf-pcO_vrO1X-ImZh-sLNCWcLY9gfw,3337
|
19
19
|
haiway/helpers/tracing.py,sha256=8Gpcc_DguuHAdaxM4rGP0mB-S-8E7DKt7ZGym9f6x6Q,4018
|
20
20
|
haiway/state/__init__.py,sha256=emTuwGFn7HyjyTJ_ass69J5jQIA7_WHO4teZz_dR05Y,355
|
21
21
|
haiway/state/attributes.py,sha256=3chvq3ENoIX688RSYiqZnOCpxbzt-kQ2Wl8Fc3vVyMo,23311
|
@@ -28,7 +28,7 @@ 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
30
|
haiway/utils/__init__.py,sha256=YBq9hYhrHFB-4d_M53A620-2KEz5SMU31GDBW6gXFnQ,804
|
31
|
-
haiway/utils/always.py,sha256=
|
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
|
34
34
|
haiway/utils/freezing.py,sha256=K34ZIMzbkpgkHKH-KF73plEbXExsajNRkRTYp9nJEf4,620
|
@@ -36,7 +36,7 @@ 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-0.
|
40
|
-
haiway-0.
|
41
|
-
haiway-0.
|
42
|
-
haiway-0.
|
39
|
+
haiway-0.13.1.dist-info/METADATA,sha256=9YaEi2sxzl_QL76iqwkhmKrWJh_0EQsoaucu9Yf0eXo,4299
|
40
|
+
haiway-0.13.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
41
|
+
haiway-0.13.1.dist-info/licenses/LICENSE,sha256=3phcpHVNBP8jsi77gOO0E7rgKeDeu99Pi7DSnK9YHoQ,1069
|
42
|
+
haiway-0.13.1.dist-info/RECORD,,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
MIT License
|
2
2
|
|
3
|
-
Copyright (c) 2024 Miquido
|
3
|
+
Copyright (c) 2024-2025 Miquido
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
18
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
-
SOFTWARE.
|
21
|
+
SOFTWARE.
|
File without changes
|