impcache


Nameimpcache JSON
Version 1.0.2 PyPI version JSON
download
home_page
SummarySimple async cache with high performance. Easy to use & ready for production.
upload_time2023-01-06 02:15:43
maintainer
docs_urlNone
author
requires_python>=3.11
license
keywords asyncio cache cachemanager redis
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # impcache
*Simple async cache with high performance. Easy to use & ready for production.*

[![License: MIT](https://img.shields.io/badge/License-MIT-brightgreen.svg)](https://github.com/impsite/impcache/blob/main/LICENSE)
[![python](https://img.shields.io/badge/Python-3.11-brightgreen.svg?style=flat&logo=python&logoColor=white)](https://www.python.org)
[![mypy](https://img.shields.io/badge/mypy-checked-brightgreen.svg?style=flat)](http://mypy-lang.org/)
[![Code coverage](https://img.shields.io/badge/code%20coverage-100%25-brightgreen)](https://github.com/PyCQA/pylint)
[![PyPI](https://img.shields.io/pypi/v/impcache?color=brightgreen&label=pypi%20package)](https://pypi.python.org/pypi/impcache/)
[![Linting: pylint](https://img.shields.io/badge/linting-pylint-brightgreen)](https://github.com/PyCQA/pylint)
[![Code style: flake8](https://img.shields.io/badge/code%20style-flake8-brightgreen.svg)](https://github.com/psf/black)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)

[![Tests](https://github.com/impsite/impcache/actions/workflows/tests.yaml/badge.svg?branch=main&event=push)](https://github.com/impsite/impcache/actions/workflows/tests.yaml)

## Usage

### Cache backend
impcache uses [Redis](https://redis.io) as a cache backend. To begin you’ll need a Redis server 
running either locally or on a remote machine.

### Dependencies
Under the hood, impcache uses async [redis-py](https://pypi.org/project/redis/) with hiredis support 
for faster performance. For serialization and deserialization we're using 
[orjson](https://pypi.org/project/orjson/) - the fastest Python library for JSON 
([benchmarks](https://github.com/ijl/orjson#performance)).

### Install

To install a wheel from PyPI:
```sh
pip install --upgrade impcache
```

### Quickstart

This is an example of setting and getting key from cache:

```python
import asyncio

from impcache import Cache, RedisCacheRepository


async def main():
    cache = Cache(
        repository=RedisCacheRepository(dsn="redis://redis:6379/0"),
        key_prefix="cache",
        version=1,
    )
    await cache.set("key", "value", expire=100)
    result = await cache.get("key")
    print(result)


if __name__ == "__main__":
    asyncio.run(main())
```

### Cache arguments
Cache can be configured to control caching behavior. These settings are provided as arguments for the Cache class. 
Valid arguments are as follows:
- **repository**: Only _RedisCacheRepository_ supported at the moment.
- **key_prefix**: A string that will be automatically prepended to all cache keys. 
See the [cache key prefixing](#cache-key-prefixing) for more information.
- **version**: The default version number generated for cache keys (can be string or integer). 
See the [cache versioning](#cache-versioning) for more information.

### Cache key prefixing
To prevent cache key collision, impcache provides the ability to prefix all cache keys. When a particular cache key 
is saved or retrieved, impcache will automatically prefix the cache key with the value of the **key_prefix** argument.

### Cache versioning
When you change running code that uses cached values, you may need to purge any existing cached values. 
The easiest way to do this is to use the version identifier, specified using the **version** argument for the Cache 
class or on primitive cache functions level.

By default, any key automatically includes the version "1".

For example:

```python
>>> # Set version 2 of a cache key
>>> await cache.set("key", "value", expire=100, version=2)
>>> # Get the default version (assuming version=1)
>>> await cache.get("key")
None
# Get version 2 of the same key
>>> await cache.get("key", version=2)
value
```

### Cache key format
As described in the previous two sections, the cache key provided by a user is not used verbatim – it is combined 
with the cache prefix and key version to provide a final cache key. By default, the three parts are joined 
using colons to produce a final string:
```python
f"{key_prefix}:{key_version}:{key}"
```

### Cache API
#### set(key: str, value: JSON, expire: int, version: Optional[int | str] = None) -> Literal[True]:

Sets the value at key name to value with expiration.
```python
>>> await cache.set("key", "value", expire=100)
True
```

#### set_nx(key: str, value: JSON, expire: int, version: Optional[int | str] = None) -> bool:

Sets the value at key name to value with expiration only if key does not exist. 
Returns False if key exists, True otherwise.

```python
>>> await cache.set_nx("key", "value", expire=100)
True
>>> await cache.set_nx("key", "value", expire=100)
False
```

#### set_many(data: dict[str, JSON], expire: int, version: Optional[int | str] = None) -> Literal[True]:

Sets key/values based on a dictionary of key-value pairs.

```python
>>> await cache.set_many({"key1": "value1", "key2": "value2"}, expire=100)
True
```

#### get(key: str, version: Optional[int | str] = None) -> Optional[JSON]:

Returns the value at key name, or None if the key doesn't exist.

```python
>>> await cache.set("key", "value", expire=100)
True
>>> await cache.get("key")
value
>>> await cache.get("non-existing-key")
None
```

#### get_many(keys: list[str], version: Optional[int | str] = None) -> list[Optional[JSON]]:

Returns a list of values ordered identically to keys, for every key that does not hold a value or does not exist, 
None is returned.

```python
>>> await cache.set_many({"key1": "value1", "key2": "value2"}, expire=100)
True
>>> await cache.get_many(["key1", "non-existing-key", "key2"])
["value1", None, "value2"]
```

#### delete(key: str, version: Optional[int | str] = None) -> int:

Deletes the key, returns the number of keys removed.

```python
>>> await cache.set("key", "value", expire=100)
True
>>> await cache.delete("key")
1
```

#### delete_many(keys: list[str], version: Optional[int | str] = None) -> int:

Deletes keys specified by keys list, returns the number of keys removed.

```python
>>> await cache.set_many({"key1": "value1", "key2": "value2"}, expire=100)
True
>>> await cache.delete_many(["key1", "non-existing-key", "key2"])
2
```

#### delete_pattern(pattern: str, version: Optional[int | str] = None) -> int:

Deletes keys specified by pattern, returns the number of keys removed.

Supported patterns:
- h?llo matches hello, hallo and hxllo
- h*llo matches hllo and heeeello
- h[ae]llo matches hello and hallo, but not hillo
- h[^e]llo matches hallo, hbllo, ... but not hello
- h[a-b]llo matches hallo and hbllo

```python
>>> await cache.set_many({"hllo": "value1", "heeeello": "value2"}, expire=100)
True
>>> # h*llo matches hllo and heeeello
>>> await cache.delete_pattern("h*llo")
2
```

## Examples

### FastAPI

All dependencies should be async to avoid running in the external threadpool, even if they just return an instance. 
See the [FastAPI dependencies](https://fastapi.tiangolo.com/async/#dependencies) documentation for more information.

depencies.py:
```python
from impcache import Cache, RedisCacheRepository

async def cache_dependency() -> Cache:
    return Cache(
        repository=RedisCacheRepository(dsn="redis://redis:6379/0"),
        key_prefix="cache",
        version=1,
    )
```

api.py:
```python
from fastapi import APIRouter, Depends
from impcache import ICache

from .depencies import cache_dependency

router = APIRouter()


@router.get("/test-cache")
async def test_cache(
        cache: ICache = Depends(cache_dependency),
):
    await cache.set("key", "value", expire=100)
    cached_value = await cache.get("key")
    return {"cached_value": cached_value}
```

## License
This project is licensed under the terms of the MIT license.

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "impcache",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.11",
    "maintainer_email": "",
    "keywords": "asyncio,cache,cachemanager,redis",
    "author": "",
    "author_email": "Andrew Timonin <timonin.andrew@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/55/95/f987c8df566a47fff5bb585e13ebf6c885030f365ed00654d99a044b6fdd/impcache-1.0.2.tar.gz",
    "platform": null,
    "description": "# impcache\n*Simple async cache with high performance. Easy to use & ready for production.*\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-brightgreen.svg)](https://github.com/impsite/impcache/blob/main/LICENSE)\n[![python](https://img.shields.io/badge/Python-3.11-brightgreen.svg?style=flat&logo=python&logoColor=white)](https://www.python.org)\n[![mypy](https://img.shields.io/badge/mypy-checked-brightgreen.svg?style=flat)](http://mypy-lang.org/)\n[![Code coverage](https://img.shields.io/badge/code%20coverage-100%25-brightgreen)](https://github.com/PyCQA/pylint)\n[![PyPI](https://img.shields.io/pypi/v/impcache?color=brightgreen&label=pypi%20package)](https://pypi.python.org/pypi/impcache/)\n[![Linting: pylint](https://img.shields.io/badge/linting-pylint-brightgreen)](https://github.com/PyCQA/pylint)\n[![Code style: flake8](https://img.shields.io/badge/code%20style-flake8-brightgreen.svg)](https://github.com/psf/black)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n\n[![Tests](https://github.com/impsite/impcache/actions/workflows/tests.yaml/badge.svg?branch=main&event=push)](https://github.com/impsite/impcache/actions/workflows/tests.yaml)\n\n## Usage\n\n### Cache backend\nimpcache uses [Redis](https://redis.io) as a cache backend. To begin you\u2019ll need a Redis server \nrunning either locally or on a remote machine.\n\n### Dependencies\nUnder the hood, impcache uses async [redis-py](https://pypi.org/project/redis/) with hiredis support \nfor faster performance. For serialization and deserialization we're using \n[orjson](https://pypi.org/project/orjson/) - the fastest Python library for JSON \n([benchmarks](https://github.com/ijl/orjson#performance)).\n\n### Install\n\nTo install a wheel from PyPI:\n```sh\npip install --upgrade impcache\n```\n\n### Quickstart\n\nThis is an example of setting and getting key from cache:\n\n```python\nimport asyncio\n\nfrom impcache import Cache, RedisCacheRepository\n\n\nasync def main():\n    cache = Cache(\n        repository=RedisCacheRepository(dsn=\"redis://redis:6379/0\"),\n        key_prefix=\"cache\",\n        version=1,\n    )\n    await cache.set(\"key\", \"value\", expire=100)\n    result = await cache.get(\"key\")\n    print(result)\n\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\n### Cache arguments\nCache can be configured to control caching behavior. These settings are provided as arguments for the Cache class. \nValid arguments are as follows:\n- **repository**: Only _RedisCacheRepository_ supported at the moment.\n- **key_prefix**: A string that will be automatically prepended to all cache keys. \nSee the [cache key prefixing](#cache-key-prefixing) for more information.\n- **version**: The default version number generated for cache keys (can be string or integer). \nSee the [cache versioning](#cache-versioning) for more information.\n\n### Cache key prefixing\nTo prevent cache key collision, impcache provides the ability to prefix all cache keys. When a particular cache key \nis saved or retrieved, impcache will automatically prefix the cache key with the value of the **key_prefix** argument.\n\n### Cache versioning\nWhen you change running code that uses cached values, you may need to purge any existing cached values. \nThe easiest way to do this is to use the version identifier, specified using the **version** argument for the Cache \nclass or on primitive cache functions level.\n\nBy default, any key automatically includes the version \"1\".\n\nFor example:\n\n```python\n>>> # Set version 2 of a cache key\n>>> await cache.set(\"key\", \"value\", expire=100, version=2)\n>>> # Get the default version (assuming version=1)\n>>> await cache.get(\"key\")\nNone\n# Get version 2 of the same key\n>>> await cache.get(\"key\", version=2)\nvalue\n```\n\n### Cache key format\nAs described in the previous two sections, the cache key provided by a user is not used verbatim \u2013 it is combined \nwith the cache prefix and key version to provide a final cache key. By default, the three parts are joined \nusing colons to produce a final string:\n```python\nf\"{key_prefix}:{key_version}:{key}\"\n```\n\n### Cache API\n#### set(key: str, value: JSON, expire: int, version: Optional[int | str] = None) -> Literal[True]:\n\nSets the value at key name to value with expiration.\n```python\n>>> await cache.set(\"key\", \"value\", expire=100)\nTrue\n```\n\n#### set_nx(key: str, value: JSON, expire: int, version: Optional[int | str] = None) -> bool:\n\nSets the value at key name to value with expiration only if key does not exist. \nReturns False if key exists, True otherwise.\n\n```python\n>>> await cache.set_nx(\"key\", \"value\", expire=100)\nTrue\n>>> await cache.set_nx(\"key\", \"value\", expire=100)\nFalse\n```\n\n#### set_many(data: dict[str, JSON], expire: int, version: Optional[int | str] = None) -> Literal[True]:\n\nSets key/values based on a dictionary of key-value pairs.\n\n```python\n>>> await cache.set_many({\"key1\": \"value1\", \"key2\": \"value2\"}, expire=100)\nTrue\n```\n\n#### get(key: str, version: Optional[int | str] = None) -> Optional[JSON]:\n\nReturns the value at key name, or None if the key doesn't exist.\n\n```python\n>>> await cache.set(\"key\", \"value\", expire=100)\nTrue\n>>> await cache.get(\"key\")\nvalue\n>>> await cache.get(\"non-existing-key\")\nNone\n```\n\n#### get_many(keys: list[str], version: Optional[int | str] = None) -> list[Optional[JSON]]:\n\nReturns a list of values ordered identically to keys, for every key that does not hold a value or does not exist, \nNone is returned.\n\n```python\n>>> await cache.set_many({\"key1\": \"value1\", \"key2\": \"value2\"}, expire=100)\nTrue\n>>> await cache.get_many([\"key1\", \"non-existing-key\", \"key2\"])\n[\"value1\", None, \"value2\"]\n```\n\n#### delete(key: str, version: Optional[int | str] = None) -> int:\n\nDeletes the key, returns the number of keys removed.\n\n```python\n>>> await cache.set(\"key\", \"value\", expire=100)\nTrue\n>>> await cache.delete(\"key\")\n1\n```\n\n#### delete_many(keys: list[str], version: Optional[int | str] = None) -> int:\n\nDeletes keys specified by keys list, returns the number of keys removed.\n\n```python\n>>> await cache.set_many({\"key1\": \"value1\", \"key2\": \"value2\"}, expire=100)\nTrue\n>>> await cache.delete_many([\"key1\", \"non-existing-key\", \"key2\"])\n2\n```\n\n#### delete_pattern(pattern: str, version: Optional[int | str] = None) -> int:\n\nDeletes keys specified by pattern, returns the number of keys removed.\n\nSupported patterns:\n- h?llo matches hello, hallo and hxllo\n- h*llo matches hllo and heeeello\n- h[ae]llo matches hello and hallo, but not hillo\n- h[^e]llo matches hallo, hbllo, ... but not hello\n- h[a-b]llo matches hallo and hbllo\n\n```python\n>>> await cache.set_many({\"hllo\": \"value1\", \"heeeello\": \"value2\"}, expire=100)\nTrue\n>>> # h*llo matches hllo and heeeello\n>>> await cache.delete_pattern(\"h*llo\")\n2\n```\n\n## Examples\n\n### FastAPI\n\nAll dependencies should be async to avoid running in the external threadpool, even if they just return an instance. \nSee the [FastAPI dependencies](https://fastapi.tiangolo.com/async/#dependencies) documentation for more information.\n\ndepencies.py:\n```python\nfrom impcache import Cache, RedisCacheRepository\n\nasync def cache_dependency() -> Cache:\n    return Cache(\n        repository=RedisCacheRepository(dsn=\"redis://redis:6379/0\"),\n        key_prefix=\"cache\",\n        version=1,\n    )\n```\n\napi.py:\n```python\nfrom fastapi import APIRouter, Depends\nfrom impcache import ICache\n\nfrom .depencies import cache_dependency\n\nrouter = APIRouter()\n\n\n@router.get(\"/test-cache\")\nasync def test_cache(\n        cache: ICache = Depends(cache_dependency),\n):\n    await cache.set(\"key\", \"value\", expire=100)\n    cached_value = await cache.get(\"key\")\n    return {\"cached_value\": cached_value}\n```\n\n## License\nThis project is licensed under the terms of the MIT license.\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "Simple async cache with high performance. Easy to use & ready for production.",
    "version": "1.0.2",
    "split_keywords": [
        "asyncio",
        "cache",
        "cachemanager",
        "redis"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "c5770700553401228fb2a1e459acd6a5dfb185d959f81882c0a223ec24f0b950",
                "md5": "235af4d936709b54c4a3a33483adbb65",
                "sha256": "31f8da593a69ff44dc0c9d62b106f47b009bb70c00dabeaaadba7571e4011ab7"
            },
            "downloads": -1,
            "filename": "impcache-1.0.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "235af4d936709b54c4a3a33483adbb65",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11",
            "size": 8451,
            "upload_time": "2023-01-06T02:15:42",
            "upload_time_iso_8601": "2023-01-06T02:15:42.511460Z",
            "url": "https://files.pythonhosted.org/packages/c5/77/0700553401228fb2a1e459acd6a5dfb185d959f81882c0a223ec24f0b950/impcache-1.0.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "5595f987c8df566a47fff5bb585e13ebf6c885030f365ed00654d99a044b6fdd",
                "md5": "1afbacba661b2dc23985534c23bc693b",
                "sha256": "2a456a40cb79f5f0c951cf26e575c335ac9dc33b5886df7c74eedda62835f0db"
            },
            "downloads": -1,
            "filename": "impcache-1.0.2.tar.gz",
            "has_sig": false,
            "md5_digest": "1afbacba661b2dc23985534c23bc693b",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11",
            "size": 9354,
            "upload_time": "2023-01-06T02:15:43",
            "upload_time_iso_8601": "2023-01-06T02:15:43.924150Z",
            "url": "https://files.pythonhosted.org/packages/55/95/f987c8df566a47fff5bb585e13ebf6c885030f365ed00654d99a044b6fdd/impcache-1.0.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-01-06 02:15:43",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "lcname": "impcache"
}
        
Elapsed time: 0.07226s