moka-py 0.1.19__cp313-cp313t-musllinux_1_2_i686.whl → 0.2.4__cp313-cp313t-musllinux_1_2_i686.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.

Potentially problematic release.


This version of moka-py might be problematic. Click here for more details.

moka_py/__init__.py CHANGED
@@ -1,32 +1,56 @@
1
1
  import asyncio as _asyncio
2
- from functools import wraps as _wraps, _make_key
3
- from .moka_py import Moka, get_version as _get_version
2
+ from functools import _make_key
3
+ from functools import wraps as _wraps
4
+ from typing import Any as _Any
4
5
 
6
+ from .moka_py import Moka
7
+ from .moka_py import get_version as _get_version
5
8
 
6
- __all__ = ["Moka", "cached", "VERSION"]
9
+ __all__ = ["VERSION", "Moka", "cached"]
7
10
 
8
11
  VERSION = _get_version()
9
12
 
10
13
 
11
- def cached(maxsize=128, typed=False, *, ttl=None, tti=None, wait_concurrent=False, policy="tiny_lfu"):
14
+ def cached(
15
+ maxsize=128,
16
+ typed=False,
17
+ *,
18
+ ttl=None,
19
+ tti=None,
20
+ wait_concurrent=False,
21
+ policy="tiny_lfu",
22
+ ):
23
+ """Cache decorator for sync and async functions with TTL/TTI and optional concurrent-waiting.
24
+
25
+ - For sync functions: returns cached value if present, otherwise computes and stores it.
26
+ - For async functions: returns an awaitable; with wait_concurrent=True a single shared task is created per key
27
+ so concurrent awaiters share the same result or exception.
28
+ """
12
29
  cache = Moka(maxsize, ttl=ttl, tti=tti, policy=policy)
13
30
  empty = object()
14
31
 
15
32
  def dec(fn):
16
33
  if _asyncio.iscoroutinefunction(fn):
17
- if wait_concurrent:
18
- raise NotImplementedError("wait_concurrent is not yet supported for async functions")
19
34
 
20
35
  @_wraps(fn)
21
36
  async def inner(*args, **kwargs):
22
37
  key = _make_key(args, kwargs, typed)
23
- maybe_value = cache.get(key, empty)
24
- if maybe_value is not empty:
25
- return maybe_value
26
- value = await fn(*args, **kwargs)
27
- cache.set(key, value)
28
- return value
38
+ if wait_concurrent:
39
+ # Store a shared Task in cache while computation is in-flight
40
+ def init() -> _Any:
41
+ return _asyncio.create_task(fn(*args, **kwargs))
42
+
43
+ task = cache.get_with(key, init)
44
+ return await task
45
+ else:
46
+ maybe_value = cache.get(key, empty)
47
+ if maybe_value is not empty:
48
+ return maybe_value
49
+ value = await fn(*args, **kwargs)
50
+ cache.set(key, value)
51
+ return value
29
52
  else:
53
+
30
54
  @_wraps(fn)
31
55
  def inner(*args, **kwargs):
32
56
  key = _make_key(args, kwargs, typed)
moka_py/__init__.pyi CHANGED
@@ -1,5 +1,5 @@
1
- from typing import TypeVar, Optional, Generic, Hashable, Union, Callable, Any, overload, Literal
2
-
1
+ from collections.abc import Callable, Hashable
2
+ from typing import Any, Generic, Literal, TypeVar, overload
3
3
 
4
4
  K = TypeVar("K", bound=Hashable)
5
5
  V = TypeVar("V")
@@ -8,51 +8,48 @@ Fn = TypeVar("Fn", bound=Callable[..., Any])
8
8
  Cause = Literal["explicit", "size", "expired", "replaced"]
9
9
  Policy = Literal["tiny_lfu", "lru"]
10
10
 
11
-
12
11
  class Moka(Generic[K, V]):
13
12
  def __init__(
14
- self,
15
- capacity: int,
16
- ttl: Optional[Union[int, float]] = None,
17
- tti: Optional[Union[int, float]] = None,
18
- eviction_listener: Optional[Callable[[K, V, Cause], None]] = None,
19
- policy: Policy = "tiny_lfu",
13
+ self,
14
+ capacity: int,
15
+ ttl: int | float | None = None,
16
+ tti: int | float | None = None,
17
+ eviction_listener: Callable[[K, V, Cause], None] | None = None,
18
+ policy: Policy = "tiny_lfu",
20
19
  ): ...
21
-
22
20
  def set(self, key: K, value: V) -> None: ...
23
-
24
21
  @overload
25
- def get(self, key: K, default: D) -> Union[V, D]: ...
26
-
22
+ def get(self, key: K, default: D) -> V | D: ...
27
23
  @overload
28
- def get(self, key: K, default: Optional[D] = None) -> Optional[Union[V, D]]: ...
29
-
24
+ def get(self, key: K, default: D | None = None) -> V | D | None: ...
30
25
  def get_with(self, key: K, initializer: Callable[[], V]) -> V:
31
- """
32
- Lookups for a key in the cache and only if there is no value set, calls the `initializer`
33
- function to set the key's value.
34
- If multiple threads call `get_with` with the same key, only one of them calls
35
- `initializer`, and the others wait until the value is set.
26
+ """Lookup or initialize a value for the key.
27
+
28
+ If multiple threads call `get_with` with the same key, only one calls `initializer`,
29
+ the others wait until the value is set.
36
30
  """
37
31
 
38
32
  @overload
39
- def remove(self, key: K, default: D) -> Union[V, D]: ...
40
-
33
+ def remove(self, key: K, default: D) -> V | D: ...
41
34
  @overload
42
- def remove(self, key: K, default: Optional[D] = None) -> Optional[Union[V, D]]: ...
43
-
35
+ def remove(self, key: K, default: D | None = None) -> V | D | None: ...
44
36
  def clear(self) -> None: ...
45
-
46
37
  def count(self) -> int: ...
47
38
 
48
-
49
39
  def cached(
50
- maxsize: int = 128,
51
- typed: bool = False,
52
- *,
53
- ttl: Optional[Union[int, float]] = None,
54
- tti: Optional[Union[int, float]] = None,
55
- wait_concurrent: bool = False,
56
- policy: Policy = "tiny_lfu",
40
+ maxsize: int = 128,
41
+ typed: bool = False,
42
+ *,
43
+ ttl: int | float | None = None,
44
+ tti: int | float | None = None,
45
+ wait_concurrent: bool = False,
46
+ policy: Policy = "tiny_lfu",
57
47
  ) -> Callable[[Fn], Fn]:
58
- ...
48
+ """Decorator for caching function results in a thread-safe in-memory cache.
49
+
50
+ - If the decorated function is synchronous: returns the cached value or computes and stores it.
51
+ - If the decorated function is asynchronous: returns an awaitable which yields the cached result.
52
+ - If wait_concurrent=True: concurrent calls with the same arguments wait on a single in-flight computation.
53
+ For async functions this is implemented via a shared asyncio.Task; all awaiters receive the same result
54
+ or the same exception.
55
+ """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: moka-py
3
- Version: 0.1.19
3
+ Version: 0.2.4
4
4
  Classifier: Programming Language :: Python :: 3.9
5
5
  Classifier: Programming Language :: Python :: 3.10
6
6
  Classifier: Programming Language :: Python :: 3.11
@@ -21,37 +21,32 @@ Project-URL: Issues, https://github.com/deliro/moka-py/issues
21
21
 
22
22
  # moka-py
23
23
 
24
- * * *
25
-
26
- **moka-py** is a Python binding for the highly efficient [Moka](https://github.com/moka-rs/moka) caching library written
27
- in Rust. This library allows you to leverage the power of Moka's high-performance, feature-rich cache in your Python
28
- projects.
24
+ **moka-py** is a Python binding to the [Moka](https://github.com/moka-rs/moka) cache written in Rust. It brings Moka’s high-performance, feature‑rich caching to Python.
29
25
 
30
26
  ## Features
31
27
 
32
- - **Synchronous Cache:** Supports thread-safe, in-memory caching for Python applications.
33
- - **TTL Support:** Automatically evicts entries after a configurable time-to-live (TTL).
34
- - **TTI Support:** Automatically evicts entries after a configurable time-to-idle (TTI).
35
- - **Size-based Eviction:** Automatically removes items when the cache exceeds its size limit using TinyLFU or LRU
36
- policy.
37
- - **Concurrency:** Optimized for high-performance, concurrent access in multithreaded environments.
28
+ - **Synchronous cache:** Thread-safe in-memory caching for Python.
29
+ - **TTL:** Evicts entries after a configurable time to live (TTL).
30
+ - **TTI:** Evicts entries after a configurable time to idle (TTI).
31
+ - **Size-based eviction:** Removes items when capacity is exceeded using TinyLFU or LRU.
32
+ - **Concurrency:** Optimized for high-throughput, concurrent access.
38
33
  - **Fully typed:** `mypy` and `pyright` friendly.
39
34
 
40
35
  ## Installation
41
36
 
42
- You can install `moka-py` using `uv`:
37
+ Install with `uv`:
43
38
 
44
39
  ```bash
45
40
  uv add moka-py
46
41
  ```
47
42
 
48
- `poetry`:
43
+ Or with `poetry`:
49
44
 
50
45
  ```bash
51
46
  poetry add moka-py
52
47
  ```
53
48
 
54
- Or, if you still stick to `pip` for some reason:
49
+ Or with `pip`:
55
50
 
56
51
  ```bash
57
52
  pip install moka-py
@@ -64,8 +59,8 @@ pip install moka-py
64
59
  - [Usage](#usage)
65
60
  - [Using moka_py.Moka](#using-moka_pymoka)
66
61
  - [@cached decorator](#as-a-decorator)
67
- - [async support](#async-support)
68
- - [Do not call a function if another function is in progress](#do-not-call-a-function-if-another-function-is-in-progress)
62
+ - [Async support](#async-support)
63
+ - [Coalesce concurrent calls (wait_concurrent)](#coalesce-concurrent-calls-wait_concurrent)
69
64
  - [Eviction listener](#eviction-listener)
70
65
  - [Removing entries](#removing-entries)
71
66
  - [How it works](#how-it-works)
@@ -82,16 +77,16 @@ from time import sleep
82
77
  from moka_py import Moka
83
78
 
84
79
 
85
- # Create a cache with a capacity of 100 entries, with a TTL of 30 seconds
86
- # and a TTI of 5.2 seconds. Entries are always removed after 30 seconds
87
- # and are removed after 5.2 seconds if there are no `get`s happened for this time.
88
- #
89
- # Both TTL and TTI settings are optional. In the absence of an entry,
80
+ # Create a cache with a capacity of 100 entries, with a TTL of 10.0 seconds
81
+ # and a TTI of 0.1 seconds. Entries are always removed after 10 seconds
82
+ # and are removed after 0.1 seconds if there are no `get`s happened for this time.
83
+ #
84
+ # Both TTL and TTI settings are optional. In the absence of an entry,
90
85
  # the corresponding policy will not expire it.
91
86
 
92
87
  # The default eviction policy is "tiny_lfu" which is optimal for most workloads,
93
88
  # but you can choose "lru" as well.
94
- cache: Moka[str, list[int]] = Moka(capacity=100, ttl=30, tti=5.2, policy="lru")
89
+ cache: Moka[str, list[int]] = Moka(capacity=100, ttl=10.0, tti=0.1, policy="lru")
95
90
 
96
91
  # Insert a value.
97
92
  cache.set("key", [3, 2, 1])
@@ -99,8 +94,8 @@ cache.set("key", [3, 2, 1])
99
94
  # Retrieve the value.
100
95
  assert cache.get("key") == [3, 2, 1]
101
96
 
102
- # Wait for 5.2+ seconds, and the entry will be automatically evicted.
103
- sleep(5.3)
97
+ # Wait for 0.1+ seconds, and the entry will be automatically evicted.
98
+ sleep(0.12)
104
99
  assert cache.get("key") is None
105
100
  ```
106
101
 
@@ -113,16 +108,21 @@ from time import sleep
113
108
  from moka_py import cached
114
109
 
115
110
 
116
- @cached(maxsize=1024, ttl=10.0, tti=1.0)
111
+ calls = []
112
+
113
+
114
+ @cached(maxsize=1024, ttl=5.0, tti=0.05)
117
115
  def f(x, y):
118
- print("hard computations")
116
+ calls.append((x, y))
119
117
  return x + y
120
118
 
121
119
 
122
- f(1, 2) # calls computations
123
- f(1, 2) # gets from the cache
124
- sleep(1.1)
125
- f(1, 2) # calls computations (since TTI has passed)
120
+ assert f(1, 2) == 3 # calls computations
121
+ assert f(1, 2) == 3 # gets from the cache
122
+ assert len(calls) == 1
123
+ sleep(0.06)
124
+ assert f(1, 2) == 3 # calls computations again (since TTI has passed)
125
+ assert len(calls) == 2
126
126
  ```
127
127
 
128
128
  ### Async support
@@ -135,22 +135,27 @@ from time import perf_counter
135
135
  from moka_py import cached
136
136
 
137
137
 
138
- @cached(maxsize=1024, ttl=10.0, tti=1.0)
138
+ calls = []
139
+
140
+
141
+ @cached(maxsize=1024, ttl=5.0, tti=0.1)
139
142
  async def f(x, y):
140
- print("http request happening")
141
- await asyncio.sleep(2.0)
143
+ calls.append((x, y))
144
+ await asyncio.sleep(0.05)
142
145
  return x + y
143
146
 
144
147
 
145
148
  start = perf_counter()
146
149
  assert asyncio.run(f(5, 6)) == 11
147
- assert asyncio.run(f(5, 6)) == 11 # got from cache
148
- assert perf_counter() - start < 4.0
150
+ assert asyncio.run(f(5, 6)) == 11 # from cache
151
+ elapsed = perf_counter() - start
152
+ assert elapsed < 0.2
153
+ assert len(calls) == 1
149
154
  ```
150
155
 
151
- ### Do not call a function if another function is in progress
156
+ ### Coalesce concurrent calls (wait_concurrent)
152
157
 
153
- moka-py can synchronize threads on keys
158
+ `moka-py` can synchronize threads on keys
154
159
 
155
160
  ```python
156
161
  import moka_py
@@ -166,7 +171,7 @@ calls = []
166
171
  @moka_py.cached(ttl=5, wait_concurrent=True)
167
172
  def get_user(id_: int) -> dict[str, Any]:
168
173
  calls.append(id_)
169
- sleep(0.3) # simulation of HTTP request
174
+ sleep(0.02) # simulate an HTTP request (short for tests)
170
175
  return {
171
176
  "id": id_,
172
177
  "first_name": "Jack",
@@ -176,13 +181,11 @@ def get_user(id_: int) -> dict[str, Any]:
176
181
 
177
182
  def process_request(path: str, user_id: int) -> None:
178
183
  user = get_user(user_id)
179
- print(f"user #{user_id} came to {path}, their info is {user}")
180
184
  ...
181
185
 
182
186
 
183
187
  def charge_money(from_user_id: int, amount: Decimal) -> None:
184
188
  user = get_user(from_user_id)
185
- print(f"charging {amount} money from user #{from_user_id} ({user['first_name']} {user['last_name']})")
186
189
  ...
187
190
 
188
191
 
@@ -194,20 +197,23 @@ if __name__ == '__main__':
194
197
  request_processing.join()
195
198
  money_charging.join()
196
199
 
197
- # only one call occurred. without the `wait_concurrent` option, each thread would go for an HTTP request
198
- # since no cache key was set
199
- assert len(calls) == 1
200
+ # Only one call occurred. Without `wait_concurrent`, each thread would issue its own HTTP request
201
+ # before the cache entry is set.
202
+ assert len(calls) == 1
200
203
  ```
201
204
 
202
- > **_ATTENTION:_** `wait_concurrent` is not yet supported for async functions and will throw `NotImplementedError`
205
+ ### Async wait_concurrent
206
+
207
+ When using `wait_concurrent=True` with async functions, `moka-py` creates a shared `asyncio.Task` per cache key. All
208
+ concurrent callers `await` the same task and receive the same result or exception. This eliminates duplicate in-flight
209
+ work for identical arguments.
203
210
 
204
211
  ### Eviction listener
205
212
 
206
- moka-py supports adding of an eviction listener that's called whenever a key is dropped
207
- from the cache for some reason. The listener must be a 3-arguments function `(key, value, cause)`. The arguments
208
- are passed as positional (not keyword).
213
+ `moka-py` supports an eviction listener, called whenever a key is removed.
214
+ The listener must be a three-argument function `(key, value, cause)` and uses positional arguments only.
209
215
 
210
- There are 4 reasons why a key may be dropped:
216
+ Possible reasons:
211
217
 
212
218
  1. `"expired"`: The entry's expiration timestamp has passed.
213
219
  2. `"explicit"`: The entry was manually removed by the user (`.remove()` is called).
@@ -222,41 +228,37 @@ from time import sleep
222
228
 
223
229
 
224
230
  def key_evicted(
225
- k: str,
226
- v: list[int],
227
- cause: Literal["explicit", "size", "expired", "replaced"]
231
+ k: str,
232
+ v: list[int],
233
+ cause: Literal["explicit", "size", "expired", "replaced"]
228
234
  ):
229
- print(f"entry {k}:{v} was evicted. {cause=}")
235
+ events.append((k, v, cause))
236
+
237
+
238
+ events: list[tuple[str, list[int], str]] = []
230
239
 
231
240
 
232
- moka: Moka[str, list[int]] = Moka(2, eviction_listener=key_evicted, ttl=0.1)
241
+ moka: Moka[str, list[int]] = Moka(2, eviction_listener=key_evicted, ttl=0.5)
233
242
  moka.set("hello", [1, 2, 3])
234
- moka.set("hello", [3, 2, 1])
235
- moka.set("foo", [4])
236
- moka.set("bar", [])
237
- sleep(1)
238
- moka.get("foo")
239
-
240
- # will print
241
- # entry hello:[1, 2, 3] was evicted. cause='replaced'
242
- # entry bar:[] was evicted. cause='size'
243
- # entry hello:[3, 2, 1] was evicted. cause='expired'
244
- # entry foo:[4] was evicted. cause='expired'
243
+ moka.set("hello", [3, 2, 1]) # replaced
244
+ moka.set("foo", [4]) # expired
245
+ moka.set("baz", "size")
246
+ moka.remove("foo") # explicit
247
+ sleep(1.0)
248
+ moka.get("anything") # this will trigger eviction for expired
249
+
250
+ causes = {c for _, _, c in events}
251
+ assert causes == {"size", "expired", "replaced", "explicit"}, events
245
252
  ```
246
253
 
247
- > **_IMPORTANT NOTES_**:
248
- > 1. It's not guaranteed that the listener will be called just in time. Also, the underlying `moka` doesn't use any
249
- background threads or tasks, hence, the listener is never called in "background"
250
- > 2. The listener must never raise any kind of `Exception`. If an exception is raised, it might be raised to any of the
251
- moka-py method in any of the threads that call this method.
252
- > 3. The listener must be fast. Since it's called only when you're interacting with `moka-py` (via `.get()` / `.set()` /
253
- etc.), the listener will slow down these operations. It's terrible idea to do some sort of IO in the listener. If
254
- you need so, run a `ThreadPoolExecutor` somewhere and call `.submit()` inside of the listener or commit an async
255
- task via `asyncio.create_task()`
254
+ > IMPORTANT NOTES
255
+ > 1) The listener is not called just-in-time. `moka` has no background threads or tasks; it runs only during cache operations.
256
+ > 2) The listener must not raise exceptions. If it does, the exception may surface from any `moka-py` method on any thread.
257
+ > 3) Keep the listener fast. Heavy work (especially I/O) will slow `.get()`, `.set()`, etc. Offload via `ThreadPoolExecutor.submit()` or `asyncio.create_task()`
256
258
 
257
259
  ### Removing entries
258
260
 
259
- An entry can be removed using `Moka.remove(key)`. If a value was set, it is returned; otherwise, `None` is returned.
261
+ Remove an entry with `Moka.remove(key)`. It returns the previous value if present; otherwise `None`.
260
262
 
261
263
  ```python
262
264
  from moka_py import Moka
@@ -268,8 +270,7 @@ assert c.remove("hello") == "world"
268
270
  assert c.get("hello") is None
269
271
  ```
270
272
 
271
- In some cases you may want `None`s to be a valid cache value. In this case you need to distinguish between `None` as a
272
- value and `None` as the absence of a value. Use `Moka.remove(key, default=...)`:
273
+ If `None` is a valid cached value, distinguish it from absence using `Moka.remove(key, default=...)`:
273
274
 
274
275
  ```python
275
276
  from moka_py import Moka
@@ -277,19 +278,18 @@ from moka_py import Moka
277
278
 
278
279
  c = Moka(128)
279
280
  c.set("hello", None)
280
- assert c.remove("hello", default="WAS_NOT_SET") is None # None is returned since is was set
281
+ assert c.remove("hello", default="WAS_NOT_SET") is None # None was set explicitly
281
282
 
282
- # Now entry with key "hello" doesn't exist so `default` argument is returned
283
- assert c.remove("hello", default="WAS_NOT_SET") == "WAS_NOT_SET"
283
+ # Now the entry "hello" does not exist, so `default` is returned
284
+ assert c.remove("hello", default="WAS_NOT_SET") == "WAS_NOT_SET"
284
285
  ```
285
286
 
286
287
  ## How it works
287
288
 
288
- `Moka` object stores Python object references
289
- (by [`INCREF`ing](https://docs.python.org/3/c-api/refcounting.html#c.Py_INCREF) `PyObject`s) and doesn't use
290
- serialization or deserialization. This means you can use any Python object as a value and any Hashable object as a
291
- key (`Moka` calls keys' `__hash__` magic methods). But also you need to remember that mutable objects stored in `Moka`
292
- are still mutable:
289
+ `Moka` stores Python object references
290
+ (by [`Py_INCREF`](https://docs.python.org/3/c-api/refcounting.html#c.Py_INCREF)) and does not serialize or deserialize values.
291
+ You can use any Python object as a value and any hashable object as a key (`__hash__` is used).
292
+ Mutable objects remain mutable:
293
293
 
294
294
  ```python
295
295
  from moka_py import Moka
@@ -305,8 +305,8 @@ assert my_list == [1, 2, 3, 4]
305
305
 
306
306
  ## Eviction policies
307
307
 
308
- moka-py uses the TinyLFU eviction policy as default, with LRU option. You can learn more about the
309
- policies [here](https://github.com/moka-rs/moka/wiki#admission-and-eviction-policies)
308
+ `moka-py` uses TinyLFU by default, with an LRU option. Learn more in the
309
+ [Moka wiki](https://github.com/moka-rs/moka/wiki#admission-and-eviction-policies).
310
310
 
311
311
  ## Performance
312
312
 
@@ -330,5 +330,5 @@ test_bench_set[lru] 637.3014 (6.32) 644.4533 (5.92) 640.3
330
330
 
331
331
  ## License
332
332
 
333
- moka-py is distributed under the [MIT license](LICENSE)
333
+ `moka-py` is distributed under the [MIT license](LICENSE).
334
334
 
@@ -0,0 +1,9 @@
1
+ moka_py-0.2.4.dist-info/METADATA,sha256=e-jmd2RcuG_uvXEOaMjJCkLE6QHQR5FZzkkvEFy6wfQ,11475
2
+ moka_py-0.2.4.dist-info/WHEEL,sha256=oHwEJ_sHUIKwY8lfSLMj5-lqV-wVA1FvC_ywV4lvspY,106
3
+ moka_py-0.2.4.dist-info/licenses/LICENSE,sha256=CUj5ca53JXgIACVKNEOFOlbMWtxY4RXXj9cELIv2R04,1069
4
+ moka_py.libs/libgcc_s-27e5a392.so.1,sha256=x5sO63liVwXxrjGGP371wB0RyQe1KEnIynYm82T0G0M,449745
5
+ moka_py/__init__.py,sha256=yb0K713ikrp4a6raS0p16W_9CVX17ReTil-M8-rmjv0,2291
6
+ moka_py/__init__.pyi,sha256=_mBsBklSY575Gxl-StCX7mv4QGugyh_Z47Rxnz0XlaE,2075
7
+ moka_py/moka_py.cpython-313t-i386-linux-musl.so,sha256=qHkadq-eikRqLShneneHpaPKHLm8udLDfKI5SOg6oho,571845
8
+ moka_py/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ moka_py-0.2.4.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: maturin (1.9.4)
2
+ Generator: maturin (1.9.6)
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp313-cp313t-musllinux_1_2_i686
@@ -1,9 +0,0 @@
1
- moka_py-0.1.19.dist-info/METADATA,sha256=gywOXWmAzickgFshSpu0pcRFPM9NOIjVZR5j9xrKss0,12348
2
- moka_py-0.1.19.dist-info/WHEEL,sha256=NFo86IM_WtVR-TFY-Pc6byqaWwE8xyk54G9_i5f5pvE,106
3
- moka_py-0.1.19.dist-info/licenses/LICENSE,sha256=CUj5ca53JXgIACVKNEOFOlbMWtxY4RXXj9cELIv2R04,1069
4
- moka_py.libs/libgcc_s-27e5a392.so.1,sha256=x5sO63liVwXxrjGGP371wB0RyQe1KEnIynYm82T0G0M,449745
5
- moka_py/__init__.py,sha256=Vm6tQsXweLtjD_QjBSWtJRquv9g7wq3zrQZJ7EBQGko,1572
6
- moka_py/__init__.pyi,sha256=wfOnFVeRxogZpBwhUMWqlAVw_0yAa5xuCO28GJNTkGg,1777
7
- moka_py/moka_py.cpython-313t-i386-linux-musl.so,sha256=bYkbLhGaRL90NQG2MCv4_PImfWyVkeJQNzbi7g8OfRQ,571845
8
- moka_py/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- moka_py-0.1.19.dist-info/RECORD,,