haiway 0.12.1__py3-none-any.whl → 0.13.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/context/state.py +5 -5
- haiway/helpers/caching.py +234 -135
- {haiway-0.12.1.dist-info → haiway-0.13.0.dist-info}/METADATA +5 -5
- {haiway-0.12.1.dist-info → haiway-0.13.0.dist-info}/RECORD +6 -6
- {haiway-0.12.1.dist-info → haiway-0.13.0.dist-info}/licenses/LICENSE +2 -2
- {haiway-0.12.1.dist-info → haiway-0.13.0.dist-info}/WHEEL +0 -0
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/helpers/caching.py
CHANGED
@@ -1,11 +1,11 @@
|
|
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 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__ = [
|
@@ -13,6 +13,29 @@ __all__ = [
|
|
13
13
|
]
|
14
14
|
|
15
15
|
|
16
|
+
class CacheMakeKey[**Args, Key](Protocol):
|
17
|
+
def __call__(
|
18
|
+
self,
|
19
|
+
*args: Args.args,
|
20
|
+
**kwargs: Args.kwargs,
|
21
|
+
) -> Key: ...
|
22
|
+
|
23
|
+
|
24
|
+
class CacheRead[Key, Value](Protocol):
|
25
|
+
async def __call__(
|
26
|
+
self,
|
27
|
+
key: Key,
|
28
|
+
) -> Value | None: ...
|
29
|
+
|
30
|
+
|
31
|
+
class CacheWrite[Key, Value](Protocol):
|
32
|
+
async def __call__(
|
33
|
+
self,
|
34
|
+
key: Key,
|
35
|
+
value: Value,
|
36
|
+
) -> None: ...
|
37
|
+
|
38
|
+
|
16
39
|
@overload
|
17
40
|
def cache[**Args, Result](
|
18
41
|
function: Callable[Args, Result],
|
@@ -21,58 +44,156 @@ def cache[**Args, Result](
|
|
21
44
|
|
22
45
|
|
23
46
|
@overload
|
24
|
-
def cache[**Args, Result](
|
47
|
+
def cache[**Args, Result, Key: Hashable](
|
25
48
|
*,
|
26
|
-
limit: int =
|
49
|
+
limit: int | None = None,
|
27
50
|
expiration: float | None = None,
|
51
|
+
make_key: CacheMakeKey[Args, Key] | None = None,
|
28
52
|
) -> Callable[[Callable[Args, Result]], Callable[Args, Result]]: ...
|
29
53
|
|
30
54
|
|
31
|
-
|
55
|
+
@overload
|
56
|
+
def cache[**Args, Result, Key](
|
57
|
+
*,
|
58
|
+
make_key: CacheMakeKey[Args, Key],
|
59
|
+
read: CacheRead[Key, Result],
|
60
|
+
write: CacheWrite[Key, Result],
|
61
|
+
) -> Callable[
|
62
|
+
[Callable[Args, Coroutine[None, None, Result]]], Callable[Args, Coroutine[None, None, Result]]
|
63
|
+
]: ...
|
64
|
+
|
65
|
+
|
66
|
+
def cache[**Args, Result, Key]( # noqa: PLR0913
|
32
67
|
function: Callable[Args, Result] | None = None,
|
33
68
|
*,
|
34
|
-
limit: int =
|
69
|
+
limit: int | None = None,
|
35
70
|
expiration: float | None = None,
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
71
|
+
make_key: CacheMakeKey[Args, Key] | None = None,
|
72
|
+
read: CacheRead[Key, Result] | None = None,
|
73
|
+
write: CacheWrite[Key, Result] | None = None,
|
74
|
+
) -> (
|
75
|
+
Callable[
|
76
|
+
[Callable[Args, Coroutine[None, None, Result]]],
|
77
|
+
Callable[Args, Coroutine[None, None, Result]],
|
78
|
+
]
|
79
|
+
| Callable[[Callable[Args, Result]], Callable[Args, Result]]
|
80
|
+
| Callable[Args, Result]
|
81
|
+
):
|
82
|
+
"""
|
83
|
+
Memoize the result of a function using a configurable cache.
|
42
84
|
|
43
85
|
Parameters
|
44
86
|
----------
|
45
|
-
function: Callable[
|
46
|
-
function to
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
87
|
+
function : Callable[Args, Result] | None
|
88
|
+
The function to be memoized.
|
89
|
+
When used as a simple decorator (i.e., `@cache`), this is the decorated function.
|
90
|
+
Should be omitted when cache is called with configuration arguments.
|
91
|
+
limit : int | None
|
92
|
+
The maximum number of entries to keep in the cache.
|
93
|
+
Defaults to 1 if not specified.
|
94
|
+
Ignored when using custom cache implementations (read/write).
|
95
|
+
expiration : float | None
|
96
|
+
Time in seconds after which a cache entry expires and will be recomputed.
|
97
|
+
Defaults to None, meaning entries don't expire based on time.
|
98
|
+
Ignored when using custom cache implementations (read/write).
|
99
|
+
make_key : CacheMakeKey[Args, Key] | None
|
100
|
+
Function to generate a cache key from function arguments.
|
101
|
+
If None, uses a default implementation that handles most cases.
|
102
|
+
Required when using custom cache implementations (read/write).
|
103
|
+
read : CacheRead[Key, Result] | None
|
104
|
+
Custom asynchronous function to read values from cache.
|
105
|
+
Must be provided together with `write` and `make_key`.
|
106
|
+
Only available for async functions.
|
107
|
+
write : CacheWrite[Key, Result] | None
|
108
|
+
Custom asynchronous function to write values to cache.
|
109
|
+
Must be provided together with `read` and `make_key`.
|
110
|
+
Only available for async functions.
|
51
111
|
|
52
112
|
Returns
|
53
113
|
-------
|
54
|
-
Callable
|
55
|
-
|
114
|
+
Callable
|
115
|
+
If `function` is provided as a positional argument, returns the memoized function.
|
116
|
+
Otherwise returns a decorator that can be applied to a function to memoize it
|
117
|
+
with the given configuration.
|
118
|
+
|
119
|
+
Notes
|
120
|
+
-----
|
121
|
+
This decorator supports both synchronous and asynchronous functions.
|
122
|
+
The default implementation uses a simple in-memory LRU cache.
|
123
|
+
For asynchronous functions, you can provide custom cache implementations
|
124
|
+
via the `read` and `write` parameters.
|
125
|
+
|
126
|
+
The default cache is not thread-safe and should not be used in multi-threaded
|
127
|
+
applications without external synchronization.
|
128
|
+
|
129
|
+
Examples
|
130
|
+
--------
|
131
|
+
Simple usage as a decorator:
|
132
|
+
|
133
|
+
>>> @cache
|
134
|
+
... def my_function(x: int) -> int:
|
135
|
+
... print("Function called")
|
136
|
+
... return x * 2
|
137
|
+
>>> my_function(5)
|
138
|
+
Function called
|
139
|
+
10
|
140
|
+
>>> my_function(5) # Cache hit, function body not executed
|
141
|
+
10
|
142
|
+
|
143
|
+
With configuration parameters:
|
144
|
+
|
145
|
+
>>> @cache(limit=10, expiration=60.0)
|
146
|
+
... def my_function(x: int) -> int:
|
147
|
+
... return x * 2
|
148
|
+
|
149
|
+
With custom cache for async functions:
|
150
|
+
|
151
|
+
>>> @cache(make_key=custom_key_maker, read=redis_read, write=redis_write)
|
152
|
+
... async def fetch_data(user_id: str) -> dict:
|
153
|
+
... return await api_call(user_id)
|
56
154
|
"""
|
57
155
|
|
58
156
|
def _wrap(function: Callable[Args, Result]) -> Callable[Args, Result]:
|
59
157
|
if iscoroutinefunction(function):
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
158
|
+
if read is not None and write is not None and make_key is not None:
|
159
|
+
assert limit is None and expiration is None # nosec: B101
|
160
|
+
return cast(
|
161
|
+
Callable[Args, Result],
|
162
|
+
_CustomCache(
|
163
|
+
function,
|
164
|
+
make_key=make_key,
|
165
|
+
read=read,
|
166
|
+
write=write,
|
167
|
+
),
|
168
|
+
)
|
169
|
+
|
170
|
+
else:
|
171
|
+
assert read is None and write is None # nosec: B101
|
172
|
+
return cast(
|
173
|
+
Callable[Args, Result],
|
174
|
+
_AsyncCache(
|
175
|
+
function,
|
176
|
+
limit=limit if limit is not None else 1,
|
177
|
+
expiration=expiration,
|
178
|
+
make_key=cast(
|
179
|
+
CacheMakeKey[Args, Hashable],
|
180
|
+
make_key if make_key is not None else _default_make_key,
|
181
|
+
),
|
182
|
+
),
|
183
|
+
)
|
68
184
|
|
69
185
|
else:
|
186
|
+
assert read is None and write is None, "Custom sync cache is not supported" # nosec: B101
|
70
187
|
return cast(
|
71
188
|
Callable[Args, Result],
|
72
189
|
_SyncCache(
|
73
190
|
function,
|
74
|
-
limit=limit,
|
191
|
+
limit=limit if limit is not None else 1,
|
75
192
|
expiration=expiration,
|
193
|
+
make_key=cast(
|
194
|
+
CacheMakeKey[Args, Hashable],
|
195
|
+
make_key if make_key is not None else _default_make_key,
|
196
|
+
),
|
76
197
|
),
|
77
198
|
)
|
78
199
|
|
@@ -101,6 +222,7 @@ class _SyncCache[**Args, Result]:
|
|
101
222
|
"_cached",
|
102
223
|
"_function",
|
103
224
|
"_limit",
|
225
|
+
"_make_key",
|
104
226
|
"_next_expire_time",
|
105
227
|
)
|
106
228
|
|
@@ -110,10 +232,13 @@ class _SyncCache[**Args, Result]:
|
|
110
232
|
/,
|
111
233
|
limit: int,
|
112
234
|
expiration: float | None,
|
235
|
+
make_key: CacheMakeKey[Args, Hashable],
|
113
236
|
) -> None:
|
114
237
|
self._function: Callable[Args, Result] = function
|
115
238
|
self._cached: OrderedDict[Hashable, _CacheEntry[Result]] = OrderedDict()
|
116
239
|
self._limit: int = limit
|
240
|
+
self._make_key: CacheMakeKey[Args, Hashable] = make_key
|
241
|
+
|
117
242
|
if expiration := expiration:
|
118
243
|
|
119
244
|
def next_expire_time() -> float | None:
|
@@ -135,27 +260,17 @@ class _SyncCache[**Args, Result]:
|
|
135
260
|
owner: type | None = None,
|
136
261
|
/,
|
137
262
|
) -> 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
|
-
)
|
263
|
+
assert owner is None and instance is None, "cache does not work for classes" # nosec: B101
|
264
|
+
return self
|
149
265
|
|
150
266
|
def __call__(
|
151
267
|
self,
|
152
268
|
*args: Args.args,
|
153
269
|
**kwargs: Args.kwargs,
|
154
270
|
) -> Result:
|
155
|
-
key: Hashable = _make_key(
|
156
|
-
args
|
157
|
-
|
158
|
-
typed=True,
|
271
|
+
key: Hashable = self._make_key(
|
272
|
+
*args,
|
273
|
+
**kwargs,
|
159
274
|
)
|
160
275
|
|
161
276
|
match self._cached.get(key):
|
@@ -164,7 +279,6 @@ class _SyncCache[**Args, Result]:
|
|
164
279
|
|
165
280
|
case entry:
|
166
281
|
if (expire := entry[1]) and expire < monotonic():
|
167
|
-
# if still running let it complete if able
|
168
282
|
del self._cached[key] # continue the same way as if empty
|
169
283
|
|
170
284
|
else:
|
@@ -178,47 +292,11 @@ class _SyncCache[**Args, Result]:
|
|
178
292
|
)
|
179
293
|
|
180
294
|
if len(self._cached) > self._limit:
|
181
|
-
#
|
295
|
+
# keep the size limit
|
182
296
|
self._cached.popitem(last=False)
|
183
297
|
|
184
298
|
return result
|
185
299
|
|
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
300
|
|
223
301
|
class _AsyncCache[**Args, Result]:
|
224
302
|
__slots__ = (
|
@@ -233,6 +311,7 @@ class _AsyncCache[**Args, Result]:
|
|
233
311
|
"_cached",
|
234
312
|
"_function",
|
235
313
|
"_limit",
|
314
|
+
"_make_key",
|
236
315
|
"_next_expire_time",
|
237
316
|
)
|
238
317
|
|
@@ -242,10 +321,13 @@ class _AsyncCache[**Args, Result]:
|
|
242
321
|
/,
|
243
322
|
limit: int,
|
244
323
|
expiration: float | None,
|
324
|
+
make_key: CacheMakeKey[Args, Hashable],
|
245
325
|
) -> None:
|
246
326
|
self._function: Callable[Args, Coroutine[None, None, Result]] = function
|
247
|
-
self._cached: OrderedDict[Hashable, _CacheEntry[
|
327
|
+
self._cached: OrderedDict[Hashable, _CacheEntry[Result]] = OrderedDict()
|
248
328
|
self._limit: int = limit
|
329
|
+
self._make_key: CacheMakeKey[Args, Hashable] = make_key
|
330
|
+
|
249
331
|
if expiration := expiration:
|
250
332
|
|
251
333
|
def next_expire_time() -> float | None:
|
@@ -267,28 +349,17 @@ class _AsyncCache[**Args, Result]:
|
|
267
349
|
owner: type | None = None,
|
268
350
|
/,
|
269
351
|
) -> Callable[Args, Coroutine[None, None, Result]]:
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
else:
|
274
|
-
return mimic_function(
|
275
|
-
self._function,
|
276
|
-
within=partial(
|
277
|
-
self.__method_call__,
|
278
|
-
instance,
|
279
|
-
),
|
280
|
-
)
|
352
|
+
assert owner is None and instance is None, "cache does not work for classes" # nosec: B101
|
353
|
+
return self
|
281
354
|
|
282
355
|
async def __call__(
|
283
356
|
self,
|
284
357
|
*args: Args.args,
|
285
358
|
**kwargs: Args.kwargs,
|
286
359
|
) -> Result:
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
kwds=kwargs,
|
291
|
-
typed=True,
|
360
|
+
key: Hashable = self._make_key(
|
361
|
+
*args,
|
362
|
+
**kwargs,
|
292
363
|
)
|
293
364
|
|
294
365
|
match self._cached.get(key):
|
@@ -297,60 +368,88 @@ class _AsyncCache[**Args, Result]:
|
|
297
368
|
|
298
369
|
case entry:
|
299
370
|
if (expire := entry[1]) and expire < monotonic():
|
300
|
-
# if still running let it complete if able
|
301
371
|
del self._cached[key] # continue the same way as if empty
|
302
372
|
|
303
373
|
else:
|
304
374
|
self._cached.move_to_end(key)
|
305
|
-
return
|
375
|
+
return entry[0]
|
306
376
|
|
307
|
-
|
377
|
+
result: Result = await self._function(*args, **kwargs)
|
308
378
|
self._cached[key] = _CacheEntry(
|
309
|
-
value=
|
379
|
+
value=result,
|
310
380
|
expire=self._next_expire_time(),
|
311
381
|
)
|
312
382
|
if len(self._cached) > self._limit:
|
313
|
-
#
|
383
|
+
# keep the size limit
|
314
384
|
self._cached.popitem(last=False)
|
315
385
|
|
316
|
-
return
|
386
|
+
return result
|
387
|
+
|
317
388
|
|
318
|
-
|
389
|
+
class _CustomCache[**Args, Result, Key]:
|
390
|
+
__slots__ = (
|
391
|
+
"__annotations__",
|
392
|
+
"__defaults__",
|
393
|
+
"__doc__",
|
394
|
+
"__globals__",
|
395
|
+
"__kwdefaults__",
|
396
|
+
"__name__",
|
397
|
+
"__qualname__",
|
398
|
+
"__wrapped__",
|
399
|
+
"_expiration",
|
400
|
+
"_function",
|
401
|
+
"_make_key",
|
402
|
+
"_read",
|
403
|
+
"_write",
|
404
|
+
)
|
405
|
+
|
406
|
+
def __init__(
|
407
|
+
self,
|
408
|
+
function: Callable[Args, Coroutine[None, None, Result]],
|
409
|
+
/,
|
410
|
+
make_key: CacheMakeKey[Args, Key],
|
411
|
+
read: CacheRead[Key, Result],
|
412
|
+
write: CacheWrite[Key, Result],
|
413
|
+
) -> None:
|
414
|
+
self._function: Callable[Args, Coroutine[None, None, Result]] = function
|
415
|
+
self._make_key: CacheMakeKey[Args, Key] = make_key
|
416
|
+
self._read: CacheRead[Key, Result] = read
|
417
|
+
self._write: CacheWrite[Key, Result] = write
|
418
|
+
|
419
|
+
# mimic function attributes if able
|
420
|
+
mimic_function(function, within=self)
|
421
|
+
|
422
|
+
async def __call__(
|
319
423
|
self,
|
320
|
-
__method_self: object,
|
321
424
|
*args: Args.args,
|
322
425
|
**kwargs: Args.kwargs,
|
323
426
|
) -> Result:
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
kwds=kwargs,
|
328
|
-
typed=True,
|
427
|
+
key: Key = self._make_key(
|
428
|
+
*args,
|
429
|
+
**kwargs,
|
329
430
|
)
|
330
431
|
|
331
|
-
match self.
|
432
|
+
match await self._read(key):
|
332
433
|
case None:
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
434
|
+
result: Result = await self._function(*args, **kwargs)
|
435
|
+
ctx.spawn( # write the value asnychronously
|
436
|
+
self._write,
|
437
|
+
key=key,
|
438
|
+
value=result,
|
439
|
+
)
|
339
440
|
|
340
|
-
|
341
|
-
self._cached.move_to_end(key)
|
342
|
-
return await shield(entry[0])
|
441
|
+
return result
|
343
442
|
|
344
|
-
|
345
|
-
|
346
|
-
)
|
347
|
-
self._cached[key] = _CacheEntry(
|
348
|
-
value=task,
|
349
|
-
expire=self._next_expire_time(),
|
350
|
-
)
|
443
|
+
case entry:
|
444
|
+
return entry
|
351
445
|
|
352
|
-
if len(self._cached) > self._limit:
|
353
|
-
# if still running let it complete if able
|
354
|
-
self._cached.popitem(last=False)
|
355
446
|
|
356
|
-
|
447
|
+
def _default_make_key[**Args](
|
448
|
+
*args: Args.args,
|
449
|
+
**kwargs: Args.kwargs,
|
450
|
+
) -> Hashable:
|
451
|
+
return _make_key(
|
452
|
+
args=args,
|
453
|
+
kwds=kwargs,
|
454
|
+
typed=True,
|
455
|
+
)
|
@@ -1,13 +1,13 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: haiway
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.13.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
|
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
|
@@ -6,12 +6,12 @@ haiway/context/disposables.py,sha256=vcsh8jRaJ8Q1ob7oh5LsrSPw9f5AMTcaD_p_Gb7tXAI
|
|
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=qskYoNwN5Ad0OgnyhL-PyGzTZltwVVdE9CEqWWn4lm8,4554
|
10
10
|
haiway/context/tasks.py,sha256=J1BFQJis_15SIXbFclppxL-AOIThg2KS4SX8Hg_-YRY,2828
|
11
11
|
haiway/context/types.py,sha256=VvJA7wAPZ3ISpgyThVguioYUXqhHf0XkPfRd0M1ERiQ,142
|
12
12
|
haiway/helpers/__init__.py,sha256=8XRJWNhidWuBKqRZ1Hyc2xqt7DeWLcoOs2V-oexl8VY,579
|
13
13
|
haiway/helpers/asynchrony.py,sha256=-yJsttRhKw0GgnzMc0FqIigS5UJl_G0YgkK12V5IzJg,6292
|
14
|
-
haiway/helpers/caching.py,sha256=
|
14
|
+
haiway/helpers/caching.py,sha256=iy2upZnlpLWc1FjQP0EjAu8j-Vl0yHZmIZ93K6Gc-yY,13232
|
15
15
|
haiway/helpers/metrics.py,sha256=lCSvat3IrkmytFdqTvsqkVqYcVOK_bByfwYAe0hJIWg,13614
|
16
16
|
haiway/helpers/retries.py,sha256=gIkyUlqJLDYaxIZd3qzeqGFY9y5Gp8dgZLlZ6hs8hoc,7538
|
17
17
|
haiway/helpers/throttling.py,sha256=RfQn8GGPqTuPWzA1CJvZvb1s00vryVpo-eqz5EY9vD4,4150
|
@@ -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.0.dist-info/METADATA,sha256=vlLkeTbiQjf4ilZEEqTwrj18ZzgFzl_rPSHExuTxb0A,4299
|
40
|
+
haiway-0.13.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
41
|
+
haiway-0.13.0.dist-info/licenses/LICENSE,sha256=3phcpHVNBP8jsi77gOO0E7rgKeDeu99Pi7DSnK9YHoQ,1069
|
42
|
+
haiway-0.13.0.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
|