Name | FlipCache JSON |
Version |
1.1
JSON |
| download |
home_page | None |
Summary | Redis-backed hybrid caching for lightning-fast Python data access |
upload_time | 2024-12-13 19:13:42 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.6 |
license | MIT |
keywords |
cache
fast_access
memory
redis
wrapper
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# ⚡️ FlipCache
[![Downloads total](https://pepy.tech/badge/flipcache)](https://pepy.tech/project/flipcache)
[![Downloads monthly](https://img.shields.io/pypi/dm/flipcache.svg)](https://pypi.python.org/pypi/flipcache)
[![PyPi Package Version](https://img.shields.io/pypi/v/flipcache.svg)](https://pypi.python.org/pypi/flipcache)
[![PyPi status](https://img.shields.io/pypi/status/flipcache.svg)](https://pypi.python.org/pypi/flipcache)
[![Supported python versions](https://img.shields.io/pypi/pyversions/flipcache.svg)](https://pypi.python.org/pypi/flipcache)
[![Github issues](https://img.shields.io/github/issues/goodeejay/flipcache.svg)](https://github.com/goodeejay/flipcache/issues)
[![MIT License](https://img.shields.io/pypi/l/flipcache.svg)](https://opensource.org/licenses/MIT)
![Views](https://komarev.com/ghpvc/?username=flipcache&label=views)
<img align="right" src="assets/logo.jpg" alt="PyCache Logo" height="250px" width="auto">
Redis-backed hybrid caching for lightning-fast Python data access
### 🤷♂️ Why FlipCache?
- Seamlessly integrate Redis for accelerated data retrieval in your Python projects.
- Optimize performance with an in-memory cache layer backed by Redis persistence.
- Enjoy ease-of-use and flexible configuration
<br/>
## 📥 Installation
```bash
pip install flipcache
```
## 🚀 Key Features
- **Hybrid Caching**: Transparent in-memory caching combined with Redis for scalable persistence.
- **Expire Times**: Set custom expiration times for cached data.
- **Configurable**: Tailor cache size, data types, and more.
## 👨💻 Usage Examples
### Basic
```python
from flipcache import FlipCache
cache = FlipCache("my_cache")
cache["my_key"] = "my_value"
print(cache["my_key"]) # Outputs: "my_value"
print(cache["unknown"]) # Outputs: None
print("my_key" in cache) # Outputs: True
```
Pros compared to using simple dictionary:
- Data persistence backed by Redis
- Seamless data conversion from Redis to Python
- Fast data access, compared to pure redis
- Returns None instead of raising an error on key indexing
### Expiring Cache
```python
import time
from flipcache import FlipCache
expiring_cache = FlipCache("expiring", local_max=0, expire_time=5)
expiring_cache["data"] = "This will expire"
time.sleep(6)
print(expiring_cache["data"]) # Outputs: None
```
In order to expiring-feature work with its full potential, we need to set `local_max` to `0`, removing the caching layer.
You lose out on faster data retrieval, in order to get precise expiration results.
We can combine `expire_time` and `local_max`, in that case we can access data from cache memory that could have been expired.
### JSON Cache
```python
from flipcache import FlipCache, et
user_data = FlipCache(
"user_data",
local_max=100,
expire_time=et.THREE_DAYS,
value_type="json"
)
data = {
"state": 1,
"orders": [1, 2, 3, 4],
"items": {
"foo": 1,
"bar": True,
"baz": []
}
}
# Store data
user_data["some-uuid"] = data
print(user_data["some-uuid"]) # {'state': 1, 'orders': [1, 2, 3, 4], 'items': {'foo': 1, 'bar': True, 'baz': []}}
# Update data
data["state"] = 2
data["items"]["bar"] = False
user_data["some-uuid"] = data
print(user_data["some-uuid"]) # {'state': 2, 'orders': [1, 2, 3, 4], 'items': {'foo': 1, 'bar': False, 'baz': []}}
# Delete data
del user_data["some-uuid"]
print(user_data["some-uuid"]) # None
```
### Custom Encoder/Decoder
```python
from flipcache import FlipCache
from dataclasses import dataclass, field
@dataclass
class Shape:
name: str = "default"
dimensions: list[float] = field(default_factory=list)
edges: int = 0
area: float = 0
def __post_init__(self):
if not self.area and self.dimensions:
self.area = self.dimensions[0] * self.dimensions[1]
def encode_shape(shape: Shape) -> str:
return f"{shape.name}||{shape.dimensions}||{shape.edges}||{shape.area}"
def decode_shape(shape: str) -> Shape:
data = shape.split("||")
return Shape(
name=data[0],
dimensions=[float(num) for num in data[1].strip('[]').split(',') if num],
edges=int(data[2]),
area=float(data[3])
)
my_shape = Shape(name='womp', dimensions=[4.1, 3.4], edges=6, area=16.38)
shape2 = Shape(name='wat', dimensions=[11, 22])
custom = FlipCache(
"custom",
local_max=0,
key_type='int',
value_type='custom',
value_default=Shape(),
value_encoder=encode_shape,
value_decoder=decode_shape
)
custom[123] = my_shape
custom[456] = shape2
print(custom[123]) # Shape(name='womp', dimensions=[4.1, 3.4], edges=6, area=16.38)
print(custom[321]) # Shape(name='default', dimensions=[], edges=0, area=0.0)
print(custom[456]) # Shape(name='wat', dimensions=[11.0, 22.0], edges=0, area=242.0)
```
For more usage examples and details, see [examples](./examples)
## ⚙️ Configuration Options
- `local_max`: Maximum items in the in-memory cache.
- `expire_time`: Redis key expiration time.
- `key_type`: Expected key data type.
- `value_type`: Expected value data type.
- `value_encoder`: Custom function used to encode the value for redis
- `value_decoder`: Custom function used to decode the value from redis
- `refresh_expire_time_on_get`: Refresh Redis key expiration on access
- `redis_protocol`: custom redis.Redis instance to be passed
## 📊 Benchmarks
<details>
<summary>Setup</summary>
```python
from flipcache import FlipCache
from redis import Redis
KEYS = 1_000
rdp = Redis(decode_responses=True)
cache = FlipCache(name="my_cache", redis_protocol=rdp, local_max=KEYS)
def redis_set():
for i in range(KEYS):
rdp.set(f"my_cache:{i}", i * 2)
def pycache_set():
for i in range(KEYS):
cache[i] = i * 2
def redis_get():
for _ in range(100):
for i in range(KEYS):
v = rdp.get(f"my_cache:{i}")
def pycache_get():
for _ in range(100):
for i in range(KEYS):
v = cache[i]
```
</details>
| Benchmark Name | Mean Time (s) | Standard Deviation |
|----------------|---------------|--------------------|
| redis_set | 0.252 | 0.013 |
| flipcache_set | 0.242 | 0.003 |
| redis_get | 22.986 | 0.518 |
| flipcache_get | 0.0172 | 0.000 |
## 📋 Plans for future releases
- Make it possible to use other redis implementations such as aioredis
- Create readthedocs site for detailed documentation
- Optimize and add new functionality
- Make it threadsafe
- Add tests
Raw data
{
"_id": null,
"home_page": null,
"name": "FlipCache",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.6",
"maintainer_email": "Jasur Yusupov <jasuryusupov14@gmail.com>",
"keywords": "cache, fast_access, memory, redis, wrapper",
"author": null,
"author_email": "Jasur Yusupov <jasuryusupov14@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/6c/32/a34d5dd4f02aff1f91535cae2b72413013ef46c79fc31bd5c67858e8a938/flipcache-1.1.tar.gz",
"platform": null,
"description": "# \u26a1\ufe0f FlipCache\n\n[![Downloads total](https://pepy.tech/badge/flipcache)](https://pepy.tech/project/flipcache)\n[![Downloads monthly](https://img.shields.io/pypi/dm/flipcache.svg)](https://pypi.python.org/pypi/flipcache)\n[![PyPi Package Version](https://img.shields.io/pypi/v/flipcache.svg)](https://pypi.python.org/pypi/flipcache)\n[![PyPi status](https://img.shields.io/pypi/status/flipcache.svg)](https://pypi.python.org/pypi/flipcache)\n[![Supported python versions](https://img.shields.io/pypi/pyversions/flipcache.svg)](https://pypi.python.org/pypi/flipcache)\n[![Github issues](https://img.shields.io/github/issues/goodeejay/flipcache.svg)](https://github.com/goodeejay/flipcache/issues)\n[![MIT License](https://img.shields.io/pypi/l/flipcache.svg)](https://opensource.org/licenses/MIT)\n![Views](https://komarev.com/ghpvc/?username=flipcache&label=views)\n\n<img align=\"right\" src=\"assets/logo.jpg\" alt=\"PyCache Logo\" height=\"250px\" width=\"auto\">\n\nRedis-backed hybrid caching for lightning-fast Python data access\n\n### \ud83e\udd37\u200d\u2642\ufe0f Why FlipCache?\n\n- Seamlessly integrate Redis for accelerated data retrieval in your Python projects.\n- Optimize performance with an in-memory cache layer backed by Redis persistence.\n- Enjoy ease-of-use and flexible configuration\n\n<br/>\n\n## \ud83d\udce5 Installation\n```bash\npip install flipcache\n```\n\n## \ud83d\ude80 Key Features\n- **Hybrid Caching**: Transparent in-memory caching combined with Redis for scalable persistence.\n- **Expire Times**: Set custom expiration times for cached data.\n- **Configurable**: Tailor cache size, data types, and more.\n\n## \ud83d\udc68\u200d\ud83d\udcbb Usage Examples\n### Basic\n\n```python\nfrom flipcache import FlipCache\n\ncache = FlipCache(\"my_cache\")\n\ncache[\"my_key\"] = \"my_value\"\nprint(cache[\"my_key\"]) # Outputs: \"my_value\"\nprint(cache[\"unknown\"]) # Outputs: None\nprint(\"my_key\" in cache) # Outputs: True\n\n```\nPros compared to using simple dictionary: \n- Data persistence backed by Redis\n- Seamless data conversion from Redis to Python\n- Fast data access, compared to pure redis\n- Returns None instead of raising an error on key indexing\n\n### Expiring Cache\n\n```python\nimport time\nfrom flipcache import FlipCache\n\nexpiring_cache = FlipCache(\"expiring\", local_max=0, expire_time=5)\n\nexpiring_cache[\"data\"] = \"This will expire\"\ntime.sleep(6)\nprint(expiring_cache[\"data\"]) # Outputs: None\n```\nIn order to expiring-feature work with its full potential, we need to set `local_max` to `0`, removing the caching layer. \nYou lose out on faster data retrieval, in order to get precise expiration results.\nWe can combine `expire_time` and `local_max`, in that case we can access data from cache memory that could have been expired.\n\n### JSON Cache\n\n```python\nfrom flipcache import FlipCache, et\n\nuser_data = FlipCache(\n \"user_data\",\n local_max=100,\n expire_time=et.THREE_DAYS,\n value_type=\"json\"\n)\n\ndata = {\n \"state\": 1,\n \"orders\": [1, 2, 3, 4],\n \"items\": {\n \"foo\": 1,\n \"bar\": True,\n \"baz\": []\n }\n}\n\n# Store data\nuser_data[\"some-uuid\"] = data\nprint(user_data[\"some-uuid\"]) # {'state': 1, 'orders': [1, 2, 3, 4], 'items': {'foo': 1, 'bar': True, 'baz': []}}\n\n# Update data\ndata[\"state\"] = 2\ndata[\"items\"][\"bar\"] = False\nuser_data[\"some-uuid\"] = data\nprint(user_data[\"some-uuid\"]) # {'state': 2, 'orders': [1, 2, 3, 4], 'items': {'foo': 1, 'bar': False, 'baz': []}}\n\n# Delete data\ndel user_data[\"some-uuid\"]\nprint(user_data[\"some-uuid\"]) # None\n```\n\n### Custom Encoder/Decoder\n\n```python\nfrom flipcache import FlipCache\nfrom dataclasses import dataclass, field\n\n\n@dataclass\nclass Shape:\n name: str = \"default\"\n dimensions: list[float] = field(default_factory=list)\n edges: int = 0\n area: float = 0\n\n def __post_init__(self):\n if not self.area and self.dimensions:\n self.area = self.dimensions[0] * self.dimensions[1]\n\n\ndef encode_shape(shape: Shape) -> str:\n return f\"{shape.name}||{shape.dimensions}||{shape.edges}||{shape.area}\"\n\n\ndef decode_shape(shape: str) -> Shape:\n data = shape.split(\"||\")\n return Shape(\n name=data[0],\n dimensions=[float(num) for num in data[1].strip('[]').split(',') if num],\n edges=int(data[2]),\n area=float(data[3])\n )\n\n\nmy_shape = Shape(name='womp', dimensions=[4.1, 3.4], edges=6, area=16.38)\nshape2 = Shape(name='wat', dimensions=[11, 22])\n\ncustom = FlipCache(\n \"custom\",\n local_max=0,\n key_type='int',\n value_type='custom',\n value_default=Shape(),\n value_encoder=encode_shape,\n value_decoder=decode_shape\n)\n\ncustom[123] = my_shape\ncustom[456] = shape2\nprint(custom[123]) # Shape(name='womp', dimensions=[4.1, 3.4], edges=6, area=16.38)\nprint(custom[321]) # Shape(name='default', dimensions=[], edges=0, area=0.0)\nprint(custom[456]) # Shape(name='wat', dimensions=[11.0, 22.0], edges=0, area=242.0)\n```\n\nFor more usage examples and details, see [examples](./examples)\n\n## \u2699\ufe0f Configuration Options\n- `local_max`: Maximum items in the in-memory cache.\n- `expire_time`: Redis key expiration time.\n- `key_type`: Expected key data type.\n- `value_type`: Expected value data type.\n- `value_encoder`: Custom function used to encode the value for redis\n- `value_decoder`: Custom function used to decode the value from redis\n- `refresh_expire_time_on_get`: Refresh Redis key expiration on access\n- `redis_protocol`: custom redis.Redis instance to be passed\n\n## \ud83d\udcca Benchmarks\n\n<details>\n <summary>Setup</summary>\n\n```python\nfrom flipcache import FlipCache\nfrom redis import Redis\n\nKEYS = 1_000\nrdp = Redis(decode_responses=True)\ncache = FlipCache(name=\"my_cache\", redis_protocol=rdp, local_max=KEYS)\n\n\ndef redis_set():\n for i in range(KEYS):\n rdp.set(f\"my_cache:{i}\", i * 2)\n\n\ndef pycache_set():\n for i in range(KEYS):\n cache[i] = i * 2\n\n\ndef redis_get():\n for _ in range(100):\n for i in range(KEYS):\n v = rdp.get(f\"my_cache:{i}\")\n\n\ndef pycache_get():\n for _ in range(100):\n for i in range(KEYS):\n v = cache[i]\n```\n</details>\n\n| Benchmark Name | Mean Time (s) | Standard Deviation |\n|----------------|---------------|--------------------|\n| redis_set | 0.252 | 0.013 |\n| flipcache_set | 0.242 | 0.003 |\n| redis_get | 22.986 | 0.518 |\n| flipcache_get | 0.0172 | 0.000 |\n\n\n\n## \ud83d\udccb Plans for future releases\n- Make it possible to use other redis implementations such as aioredis\n- Create readthedocs site for detailed documentation\n- Optimize and add new functionality\n- Make it threadsafe\n- Add tests\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Redis-backed hybrid caching for lightning-fast Python data access",
"version": "1.1",
"project_urls": {
"Homepage": "https://github.com/goodeejay/FlipCache",
"Issues": "https://github.com/goodeejay/FlipCache/issues"
},
"split_keywords": [
"cache",
" fast_access",
" memory",
" redis",
" wrapper"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "beba82c9633d9d954de3104af6ebe29af6d3ff0b48f06847475eb095d288fec6",
"md5": "b36aa89260ce16b39f1418b99375a3e1",
"sha256": "a6e6e73d1e002ffa3cf52d57ff96f7442930d606894b15e080275be4ad8cab36"
},
"downloads": -1,
"filename": "flipcache-1.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "b36aa89260ce16b39f1418b99375a3e1",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.6",
"size": 7109,
"upload_time": "2024-12-13T19:13:39",
"upload_time_iso_8601": "2024-12-13T19:13:39.147844Z",
"url": "https://files.pythonhosted.org/packages/be/ba/82c9633d9d954de3104af6ebe29af6d3ff0b48f06847475eb095d288fec6/flipcache-1.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "6c32a34d5dd4f02aff1f91535cae2b72413013ef46c79fc31bd5c67858e8a938",
"md5": "2f5c955f0faf4761a3b6836acca06679",
"sha256": "f288eb8d3309a5c28870ab37034b85840a7d48b05f4e96cc28033ab10c7825f3"
},
"downloads": -1,
"filename": "flipcache-1.1.tar.gz",
"has_sig": false,
"md5_digest": "2f5c955f0faf4761a3b6836acca06679",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 88427,
"upload_time": "2024-12-13T19:13:42",
"upload_time_iso_8601": "2024-12-13T19:13:42.337525Z",
"url": "https://files.pythonhosted.org/packages/6c/32/a34d5dd4f02aff1f91535cae2b72413013ef46c79fc31bd5c67858e8a938/flipcache-1.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-12-13 19:13:42",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "goodeejay",
"github_project": "FlipCache",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "flipcache"
}