# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['theine', 'theine.adapters']

package_data = \
{'': ['*']}

install_requires = \
['theine-core>=0.2.0,<0.3.0', 'typing-extensions>=4.4.0,<5.0.0']

setup_kwargs = {
    'name': 'theine',
    'version': '0.2.0',
    'description': 'high performance in-memory cache',
    'long_description': '# Theine\nHigh performance in-memory cache inspired by [Caffeine](https://github.com/ben-manes/caffeine).\n\n- High performance [Rust core](https://github.com/Yiling-J/theine-core)\n- High hit ratio with [W-TinyLFU eviction policy](https://arxiv.org/pdf/1512.00727.pdf)\n- Expired data are removed automatically using [hierarchical timer wheel](http://www.cs.columbia.edu/~nahum/w6998/papers/ton97-timing-wheels.pdf)\n\n  > TTL must be considered in in-memory caching because\nit limits the effective (unexpired) working set size. Efficiently removing expired objects from cache needs to be\nprioritized over cache eviction. - [A large scale analysis of hundreds of in-memory\ncache clusters at Twitter](https://www.usenix.org/system/files/osdi20-yang.pdf)\n- Simple API\n- Django cache backend\n\n## Table of Contents\n\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [API](#api)\n- [Decorator](#decorator)\n- [Django Cache Backend](#django-cache-backend)\n- [Benchmarks](#benchmarks)\n  * [continuous benchmark](#continuous-benchmark)\n  * [10k requests](#10k-requests)\n  * [hit ratios](#hit-ratios)\n\n## Requirements\nPython 3.7+\n\n## Installation\n```\npip install theine\n```\n\n## API\n\nKey should be a **Hashable** object, and value can be any **Python object**. If key type is not **str/int**, Theine will generate a unique key string automatically, this unique str will use extra space in memory and increase get/set/remove overhead.\n\nPlease be aware the Cache class is **not** thread-safe.\n\n```Python\nfrom theine import Cache\nfrom datetime import timedelta\n\ncache = Cache("tlfu", 10000)\n# without default, return None on miss\nv = cache.get("key")\n\n# with default, return default on miss\nsentinel = object()\nv = cache.get("key", sentinel)\n\n# set with ttl\ncache.set("key", {"foo": "bar"}, timedelta(seconds=100))\n\n# delete from cache\ncache.delete("key")\n```\n\n## Decorator\nTheine support hashable keys, so to use a decorator, a function to convert input signatures to hashable is necessary. **The recommended way is specifying the function explicitly**, this is approach 1, Theine also support generating key automatically, this is approach 2. Same as Theine API, if key function return type is not **str/int**, Theine will generate a unique key string automatically, this unique str will use extra space in memory and increase get/set/remove overhead.\n\n**- explicit key function**\n\n```python\nfrom theine import Cache, Memoize\nfrom datetime import timedelta\n\n@Memoize(Cache("tlfu", 10000), timedelta(seconds=100))\ndef foo(a:int) -> int:\n    return a\n\n@foo.key\ndef _(a:int) -> str:\n    return f"a:{a}"\n\nfoo(1)\n\n# asyncio\n@Memoize(Cache("tlfu", 10000), timedelta(seconds=100))\nasync def foo_a(a:int) -> int:\n    return a\n\n@foo_a.key\ndef _(a:int) -> str:\n    return f"a:{a}"\n\nawait foo_a(1)\n\n```\n\n**Pros**\n- Both sync and async support.\n- Explicitly control how key is generated. Most remote cache(redis, memcached...) only allow string keys, return a string in key function make it easier when you want to use remote cache later.\n- Thundering herd protection(multithreading: set `lock=True` in `Memoize`, asyncio: always enabled).\n- Type checked. Mypy can check key function to make sure it has same input signature as original function and return a hashable.\n\n**Cons**\n- You have to use 2 functions.\n- Performance. Theine API: around 8ms/10k requests ->> decorator: around 12ms/10k requests.\n\n**- auto key function**\n\n```python\nfrom theine import Cache, Memoize\nfrom datetime import timedelta\n\n@Memoize(Cache("tlfu", 10000), timedelta(seconds=100), typed=True)\ndef foo(a:int) -> int:\n    return a\n\nfoo(1)\n\n# asyncio\n@Memoize(Cache("tlfu", 10000), timedelta(seconds=100), typed=True)\nasync def foo_a(a:int) -> int:\n    return a\n\nawait foo_a(1)\n\n```\n**Pros**\n- Same as explicit key version.\n- No extra key function.\n\n**Cons**\n- Worse performance: around 18ms/10k requests.\n- Unexpected memory usage. The auto key function use same methods as Python\'s lru_cache. Take a look [this issue](https://github.com/python/cpython/issues/88476) or [this one](https://github.com/python/cpython/issues/64058).\n\n\n## Django Cache Backend\n\n```Python\nCACHES = {\n    "default": {\n        "BACKEND": "theine.adapters.django.Cache",\n        "TIMEOUT": 300,\n        "OPTIONS": {"MAX_ENTRIES": 10000},\n    },\n}\n```\n\n## Benchmarks\n### continuous benchmark\nhttps://github.com/Yiling-J/cacheme-benchmark\n\n### 10k requests\nCachetools: https://github.com/tkem/cachetools\n\nCacheout: https://github.com/dgilland/cacheout\n\nSource Code: https://github.com/Yiling-J/theine/blob/main/benchmarks/benchmark_test.py\n\nWrite and Mix Zipf use 1k max cache size, so you can see the high cost of traditional LFU eviction policy here.\n\n|                                        | Read     | Write     | Mix Zipf  |\n|----------------------------------------|----------|-----------|-----------|\n| Theine(W-TinyLFU) API                  | 5.34 ms  | 13.10 ms  |           |\n| Theine(W-TinyLFU) Auto-Key Decorator   | 8.90 ms  | 21.69 ms  | 14.75 ms  |\n| Theine(W-TinyLFU) Custom-Key Decorator | 7.79 ms  | 20.95 ms  | 13.58 ms  |\n| Cachetools LFU Decorator               | 15.70 ms | 627.10 ms | 191.04 ms |\n| Cacheout LFU Decorator                 | 50.05 ms | 704.70 ms | 250.95 ms |\n| Theine(LRU) Custom-Key Decorator       | 7.14 ms  | 19.36 ms  | 13.53 ms  |\n| Cachetools LRU Decorator               | 14.05 ms | 61.06 ms  | 36.89 ms  |\n| Cacheout LRU Decorator                 | 47.90 ms | 94.94 ms  | 68.25 ms  |\n\n### hit ratios\n\nAll hit ratio benchmarks use small datasets and finish in seconds/minutes, better to try Theine yourself and focus on whether the cache exceeds your performance needs and has the desired capabilities. You may also notice that in ucb bench, LRU has a higher hit ratio. Take a look this: [cache performing worse than LRU](https://github.com/ben-manes/caffeine/issues/106) if you are interested.\n\nSource Code: https://github.com/Yiling-J/theine/blob/main/benchmarks/trace_bench.py\n\n**zipf**\n\n![hit ratios](benchmarks/zipf.png)\n**search**\n\nThis trace is described as "disk read accesses initiated by a large commercial search engine in response to various web search requests."\n![hit ratios](benchmarks/s3.png)\n**database**\n\nThis trace is described as "a database server running at a commercial site running an ERP application on top of a commercial database."\n![hit ratios](benchmarks/ds1.png)\n**UC Berkeley web traces**\n\nThis trace consists of 2 days\' worth of HTTP traces gathered from the Home IP service offered by UC Berkeley to its students.\n![hit ratios](benchmarks/ucb.png)\n',
    'author': 'Yiling-J',
    'author_email': 'njjyl723@gmail.com',
    'maintainer': 'None',
    'maintainer_email': 'None',
    'url': 'None',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.7,<4.0',
}


setup(**setup_kwargs)
