{"id":22794081,"url":"https://github.com/deliro/moka-py","last_synced_at":"2026-01-14T10:18:17.376Z","repository":{"id":263683115,"uuid":"882091551","full_name":"deliro/moka-py","owner":"deliro","description":"A high performance caching library for Python written in Rust","archived":false,"fork":false,"pushed_at":"2025-10-26T22:30:43.000Z","size":106,"stargazers_count":271,"open_issues_count":0,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-10-26T22:47:44.297Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/deliro.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-11-01T21:39:41.000Z","updated_at":"2025-10-26T22:30:15.000Z","dependencies_parsed_at":"2025-04-17T15:27:20.707Z","dependency_job_id":"4e884267-8ccb-4a30-b9e5-d3b8255c0e34","html_url":"https://github.com/deliro/moka-py","commit_stats":null,"previous_names":["deliro/moka-py"],"tags_count":25,"template":false,"template_full_name":null,"purl":"pkg:github/deliro/moka-py","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deliro%2Fmoka-py","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deliro%2Fmoka-py/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deliro%2Fmoka-py/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deliro%2Fmoka-py/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/deliro","download_url":"https://codeload.github.com/deliro/moka-py/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deliro%2Fmoka-py/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28416855,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T10:18:03.274Z","status":"ssl_error","status_checked_at":"2026-01-14T10:16:11.865Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-12T04:01:20.680Z","updated_at":"2026-01-14T10:18:17.371Z","avatar_url":"https://github.com/deliro.png","language":"Python","funding_links":[],"categories":["Python","Performance \u0026 Caching"],"sub_categories":[],"readme":"# moka-py\n\n**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.\n\n## Features\n\n- **Synchronous cache:** Thread-safe in-memory caching for Python.\n- **TTL:** Evicts entries after a configurable time to live (TTL).\n- **TTI:** Evicts entries after a configurable time to idle (TTI).\n- **Size-based eviction:** Removes items when capacity is exceeded using TinyLFU or LRU.\n- **Concurrency:** Optimized for high-throughput, concurrent access.\n- **Fully typed:** `mypy` and `pyright` friendly.\n\n## Installation\n\nInstall with `uv`:\n\n```bash\nuv add moka-py\n```\n\nOr with `poetry`:\n\n```bash\npoetry add moka-py\n```\n\nOr with `pip`:\n\n```bash\npip install moka-py\n```\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Features](#features)\n- [Usage](#usage)\n    - [Using moka_py.Moka](#using-moka_pymoka)\n    - [@cached decorator](#as-a-decorator)\n    - [Async support](#async-support)\n    - [Coalesce concurrent calls (wait_concurrent)](#coalesce-concurrent-calls-wait_concurrent)\n    - [Eviction listener](#eviction-listener)\n    - [Removing entries](#removing-entries)\n- [How it works](#how-it-works)\n- [Eviction policies](#eviction-policies)\n- [Performance](#performance)\n- [License](#license)\n\n## Usage\n\n### Using moka_py.Moka\n\n```python\nfrom time import sleep\nfrom moka_py import Moka\n\n\n# Create a cache with a capacity of 100 entries, with a TTL of 10.0 seconds\n# and a TTI of 0.1 seconds. Entries are always removed after 10 seconds\n# and are removed after 0.1 seconds if there are no `get`s happened for this time.\n#\n# Both TTL and TTI settings are optional. In the absence of an entry,\n# the corresponding policy will not expire it.\n\n# The default eviction policy is \"tiny_lfu\" which is optimal for most workloads,\n# but you can choose \"lru\" as well.\ncache: Moka[str, list[int]] = Moka(capacity=100, ttl=10.0, tti=0.1, policy=\"lru\")\n\n# Insert a value.\ncache.set(\"key\", [3, 2, 1])\n\n# Retrieve the value.\nassert cache.get(\"key\") == [3, 2, 1]\n\n# Wait for 0.1+ seconds, and the entry will be automatically evicted.\nsleep(0.12)\nassert cache.get(\"key\") is None\n```\n\n### As a decorator\n\nmoka-py can be used as a drop-in replacement for `@lru_cache()` with TTL + TTI support:\n\n```python\nfrom time import sleep\nfrom moka_py import cached\n\n\ncalls = []\n\n\n@cached(maxsize=1024, ttl=5.0, tti=0.05)\ndef f(x, y):\n    calls.append((x, y))\n    return x + y\n\n\nassert f(1, 2) == 3  # calls computations\nassert f(1, 2) == 3  # gets from the cache\nassert len(calls) == 1\nsleep(0.06)\nassert f(1, 2) == 3  # calls computations again (since TTI has passed)\nassert len(calls) == 2\n```\n\n### Async support\n\nUnlike `@lru_cache()`, `@moka_py.cached()` supports async functions:\n\n```python\nimport asyncio\nfrom time import perf_counter\nfrom moka_py import cached\n\n\ncalls = []\n\n\n@cached(maxsize=1024, ttl=5.0, tti=0.1)\nasync def f(x, y):\n    calls.append((x, y))\n    await asyncio.sleep(0.05)\n    return x + y\n\n\nstart = perf_counter()\nassert asyncio.run(f(5, 6)) == 11\nassert asyncio.run(f(5, 6)) == 11  # from cache\nelapsed = perf_counter() - start\nassert elapsed \u003c 0.2\nassert len(calls) == 1\n```\n\n### Coalesce concurrent calls (wait_concurrent)\n\n`moka-py` can synchronize threads on keys\n\n```python\nimport moka_py\nfrom typing import Any\nfrom time import sleep\nimport threading\nfrom decimal import Decimal\n\n\ncalls = []\n\n\n@moka_py.cached(ttl=5, wait_concurrent=True)\ndef get_user(id_: int) -\u003e dict[str, Any]:\n    calls.append(id_)\n    sleep(0.02)  # simulate an HTTP request (short for tests)\n    return {\n        \"id\": id_,\n        \"first_name\": \"Jack\",\n        \"last_name\": \"Pot\",\n    }\n\n\ndef process_request(path: str, user_id: int) -\u003e None:\n    user = get_user(user_id)\n    ...\n\n\ndef charge_money(from_user_id: int, amount: Decimal) -\u003e None:\n    user = get_user(from_user_id)\n    ...\n\n\nif __name__ == '__main__':\n    request_processing = threading.Thread(target=process_request, args=(\"/user/info/123\", 123))\n    money_charging = threading.Thread(target=charge_money, args=(123, Decimal(\"3.14\")))\n    request_processing.start()\n    money_charging.start()\n    request_processing.join()\n    money_charging.join()\n\n    # Only one call occurred. Without `wait_concurrent`, each thread would issue its own HTTP request\n    # before the cache entry is set.\n    assert len(calls) == 1\n```\n\n### Async wait_concurrent\n\nWhen using `wait_concurrent=True` with async functions, `moka-py` creates a shared `asyncio.Task` per cache key. All\nconcurrent callers `await` the same task and receive the same result or exception. This eliminates duplicate in-flight\nwork for identical arguments.\n\n### Eviction listener\n\n`moka-py` supports an eviction listener, called whenever a key is removed.\nThe listener must be a three-argument function `(key, value, cause)` and uses positional arguments only.\n\nPossible reasons:\n\n1. `\"expired\"`: The entry's expiration timestamp has passed.\n2. `\"explicit\"`: The entry was manually removed by the user (`.remove()` is called).\n3. `\"replaced\"`: The entry itself was not actually removed, but its value was replaced by the user (`.set()` is\n   called for an existing entry).\n4. `\"size\"`: The entry was evicted due to size constraints.\n\n```python\nfrom typing import Literal\nfrom moka_py import Moka\nfrom time import sleep\n\n\ndef key_evicted(\n    k: str,\n    v: list[int],\n    cause: Literal[\"explicit\", \"size\", \"expired\", \"replaced\"]\n):\n    events.append((k, v, cause))\n\n\nevents: list[tuple[str, list[int], str]] = []\n\n\nmoka: Moka[str, list[int]] = Moka(2, eviction_listener=key_evicted, ttl=0.5)\nmoka.set(\"hello\", [1, 2, 3])\nmoka.set(\"hello\", [3, 2, 1])  # replaced\nmoka.set(\"foo\", [4])  # expired\nmoka.set(\"baz\", \"size\")\nmoka.remove(\"foo\")  # explicit\nsleep(1.0)\nmoka.get(\"anything\")  # this will trigger eviction for expired\n\ncauses = {c for _, _, c in events}\nassert causes == {\"size\", \"expired\", \"replaced\", \"explicit\"}, events\n```\n\n\u003e IMPORTANT NOTES\n\u003e 1) The listener is not called just-in-time. `moka` has no background threads or tasks; it runs only during cache operations.\n\u003e 2) The listener must not raise exceptions. If it does, the exception may surface from any `moka-py` method on any thread.\n\u003e 3) Keep the listener fast. Heavy work (especially I/O) will slow `.get()`, `.set()`, etc. Offload via `ThreadPoolExecutor.submit()` or `asyncio.create_task()`\n\n### Removing entries\n\nRemove an entry with `Moka.remove(key)`. It returns the previous value if present; otherwise `None`.\n\n```python\nfrom moka_py import Moka\n\n\nc = Moka(128)\nc.set(\"hello\", \"world\")\nassert c.remove(\"hello\") == \"world\"\nassert c.get(\"hello\") is None\n```\n\nIf `None` is a valid cached value, distinguish it from absence using `Moka.remove(key, default=...)`:\n\n```python\nfrom moka_py import Moka\n\n\nc = Moka(128)\nc.set(\"hello\", None)\nassert c.remove(\"hello\", default=\"WAS_NOT_SET\") is None  # None was set explicitly\n\n# Now the entry \"hello\" does not exist, so `default` is returned\nassert c.remove(\"hello\", default=\"WAS_NOT_SET\") == \"WAS_NOT_SET\"\n```\n\n## How it works\n\n`Moka` stores Python object references\n(by [`Py_INCREF`](https://docs.python.org/3/c-api/refcounting.html#c.Py_INCREF)) and does not serialize or deserialize values.\nYou can use any Python object as a value and any hashable object as a key (`__hash__` is used).\nMutable objects remain mutable:\n\n```python\nfrom moka_py import Moka\n\n\nc = Moka(128)\nmy_list = [1, 2, 3]\nc.set(\"hello\", my_list)\nstill_the_same = c.get(\"hello\")\nstill_the_same.append(4)\nassert my_list == [1, 2, 3, 4]\n```\n\n## Eviction policies\n\n`moka-py` uses TinyLFU by default, with an LRU option. Learn more in the\n[Moka wiki](https://github.com/moka-rs/moka/wiki#admission-and-eviction-policies).\n\n## Performance\n\n*Measured using MacBook Pro 2021 with Apple M1 Pro processor and 16GiB RAM*\n\n```\n-------------------------------------------------------------------------------------------- benchmark: 9 tests -------------------------------------------------------------------------------------------\nName (time in ns)                       Min                 Max                Mean            StdDev              Median               IQR            Outliers  OPS (Mops/s)            Rounds  Iterations\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\ntest_bench_remove                  100.8775 (1.0)      108.9191 (1.0)      102.6757 (1.0)      3.4992 (34.54)    101.0640 (1.0)      2.4234 (15.49)         1;1        9.7394 (1.0)           5    10000000\ntest_bench_get[lru-False]          112.8452 (1.12)     113.0924 (1.04)     112.9415 (1.10)     0.1013 (1.0)      112.9176 (1.12)     0.1565 (1.0)           1;0        8.8541 (0.91)          5    10000000\ntest_bench_get[tiny_lfu-False]     135.0147 (1.34)     135.6069 (1.25)     135.2916 (1.32)     0.2246 (2.22)     135.2849 (1.34)     0.3164 (2.02)          2;0        7.3914 (0.76)          5    10000000\ntest_bench_get[lru-True]           135.1628 (1.34)     135.7813 (1.25)     135.4712 (1.32)     0.2231 (2.20)     135.4765 (1.34)     0.2477 (1.58)          2;0        7.3816 (0.76)          5    10000000\ntest_bench_get[tiny_lfu-True]      135.2461 (1.34)     135.6612 (1.25)     135.4463 (1.32)     0.1802 (1.78)     135.4026 (1.34)     0.3192 (2.04)          2;0        7.3830 (0.76)          5    10000000\ntest_bench_get_with                290.5307 (2.88)     291.0418 (2.67)     290.8393 (2.83)     0.1893 (1.87)     290.8867 (2.88)     0.1873 (1.20)          2;0        3.4383 (0.35)          5    10000000\ntest_bench_set[tiny_lfu]           515.7514 (5.11)     518.6080 (4.76)     517.4876 (5.04)     1.1196 (11.05)    517.6572 (5.12)     1.5465 (9.88)          2;0        1.9324 (0.20)          5     1912971\ntest_bench_set_str_key             516.1032 (5.12)     533.7330 (4.90)     525.7461 (5.12)     6.3386 (62.57)    526.8491 (5.21)     6.1052 (39.01)         2;0        1.9021 (0.20)          5     1918471\ntest_bench_set[lru]                637.3014 (6.32)     644.4533 (5.92)     640.3571 (6.24)     2.8981 (28.61)    639.8821 (6.33)     4.6131 (29.48)         2;0        1.5616 (0.16)          5     1581738\n-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n```\n\n## License\n\n`moka-py` is distributed under the [MIT license](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeliro%2Fmoka-py","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdeliro%2Fmoka-py","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeliro%2Fmoka-py/lists"}