Asynchronous caching library with per-key locking, TTL, LRU and decorators
Project description
Short Description
A lightweight, asynchronous caching library for Python with per‑key locking, TTL, LRU eviction, and convenient decorators. Built with asyncio and Pydantic.
Best Simple Cache
Best Simple Cache is an asynchronous caching library designed for modern Python applications that use asyncio and Pydantic models. It provides:
- Per‑key locking for thread‑safe concurrent access.
- Time‑to‑live (TTL) and LRU eviction.
- Decorator‑based integration – add caching to any async/sync function with minimal boilerplate.
- Invalidation hooks and automatic cache refresh.
- Flexible primary key generation – define your own key structure.
Features
- ✅ Async‑first – built on
asynciowith per‑key locks to avoid race conditions. - ✅ TTL & LRU – automatically evict stale or least‑recently used entries.
- ✅ Decoupled storage – implement your own cache backend by subclassing
EntityCache. - ✅ Decorator suite –
@cache,@invalidate_after, and@refresh_after_byfor common patterns. - ✅ Mixed sync/async support – works with both synchronous and asynchronous functions.
- ✅ Type hints – fully typed for better IDE support.
Installation
pip install best_simple_caching
Requirements: Python 3.10+, Pydantic (v2).
Quick Start
import asyncio
from pydantic import BaseModel
from best_simple_cache import EntityCache, CacheDecorator, CachingConfig
# 1. Define your Pydantic model
class User(BaseModel):
id: int
name: str
# 2. Define the primary key (must be hashable)
class UserPK:
def __init__(self, user_id: int):
self.user_id = user_id
def __hash__(self):
return hash(self.user_id)
def __eq__(self, other):
return isinstance(other, UserPK) and self.user_id == other.user_id
# 3. Implement your cache class
class UserCache(EntityCache[User, UserPK]):
def make_pk(self, user_id: int, **_kwargs) -> UserPK:
return UserPK(user_id)
# 4. Create a decorator instance
user_cache = CacheDecorator(
entity_cache=UserCache(
entity_name="user",
model=User,
config=CachingConfig(ttl=60, max_size=1000)
)
)
# 5. Decorate your function
@user_cache.cache
async def get_user(*, user_id: int) -> User:
# Simulate an expensive API call
await asyncio.sleep(1)
return User(id=user_id, name="Alice")
async def main():
# First call – runs the function, stores result
user = await get_user(user_id=42)
print(user)
# Second call – returns from cache
user = await get_user(user_id=42)
print(user) # Instant
asyncio.run(main())
Advanced Usage
Invalidation
@user_cache.invalidate_after
async def update_user(*, user_id: int) -> User:
# This function updates the user and then invalidates the cache
updated_user = ... # some update logic
return updated_user
Refresh After Write
@user_cache.refresh_after_by(get_user) # get_user is a cached function
async def update_user(*, user_id: int) -> None:
# Perform update, then the cache will be invalidated and refreshed
...
Custom Key Generation
The make_pk method receives the same keyword arguments as the decorated function. Use it to build a hashable key.
class MyCache(EntityCache[MyModel, MyPK]):
def make_pk(self, id: int, region: str = "default", **_kwargs) -> MyPK:
return MyPK(id, region)
API Reference
EntityCache(ABC, Generic[T, PK])
Abstract base class for a cache backend.
__init__(entity_name: str, model: type[T], config: CachingConfig | None)async set(entity: T, **kwargs) -> None– store an entity.async get(**kwargs) -> T | None– retrieve an entity by keyword arguments.async get_by_pk(pk: PK) -> T | None– retrieve directly by primary key.async invalidate(entity_pk: PK | None = None, **kwargs) -> None– remove an entry.abstractmethod make_pk(**kwargs) -> PK– build a primary key from arguments.
CacheDecorator(Generic[T, PK])
Creates decorators bound to a specific EntityCache.
cache– decorator that caches the function result.invalidate_after– decorator that invalidates the cache after the function runs.refresh_after_by(refresh_func)– decorator that invalidates and then callsrefresh_functo repopulate the cache.
CachingConfig
Configuration for a cache.
ttl: float | None– seconds after which an entry is considered stale (defaultNone= never expires).max_size: int– maximum number of entries;0means unlimited (default0).disabled: bool– ifTrue, caching is disabled (defaultFalse).
Limitations
- Keyword arguments only – the decorated function must use only keyword arguments (or at least those used in
make_pkmust be named). Positional arguments are not supported. - Pydantic models – the library assumes cached entities are subclasses of
BaseModel. You can extend it to work with other types by overriding type checks. - In‑memory only – the default implementation stores data in an
OrderedDict. For distributed caching, implement your own backend.
Contributing
Contributions are welcome! Please open an issue or submit a pull request.
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file best_simple_caching-1.0.3.tar.gz.
File metadata
- Download URL: best_simple_caching-1.0.3.tar.gz
- Upload date:
- Size: 9.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0d51819392bd19ed24518dea87b5113a1eebdbd4351843d4d2ea9b921913950e
|
|
| MD5 |
f4dafed9ddaf8df96060967662ebc133
|
|
| BLAKE2b-256 |
27856629c83d48262cd2897b516ccfcb05aeec9f36f06b5ac766066815dff536
|
File details
Details for the file best_simple_caching-1.0.3-py3-none-any.whl.
File metadata
- Download URL: best_simple_caching-1.0.3-py3-none-any.whl
- Upload date:
- Size: 9.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c01a8515824c733deb123f4f2f5d71d10ba32ac07f629237937b7bc12b38a827
|
|
| MD5 |
e6e37880f46552d33c9019213eabf0f1
|
|
| BLAKE2b-256 |
c912b6f6ea9ac48767ab6e295c08988bfa4e668bae719e9fea794eb374fb0f48
|