Skip to main content

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 asyncio with 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_by for 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 calls refresh_func to repopulate the cache.

CachingConfig

Configuration for a cache.

  • ttl: float | None – seconds after which an entry is considered stale (default None = never expires).
  • max_size: int – maximum number of entries; 0 means unlimited (default 0).
  • disabled: bool – if True, caching is disabled (default False).

Limitations

  • Keyword arguments only – the decorated function must use only keyword arguments (or at least those used in make_pk must 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

best_simple_caching-1.0.3.tar.gz (9.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

best_simple_caching-1.0.3-py3-none-any.whl (9.0 kB view details)

Uploaded Python 3

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

Hashes for best_simple_caching-1.0.3.tar.gz
Algorithm Hash digest
SHA256 0d51819392bd19ed24518dea87b5113a1eebdbd4351843d4d2ea9b921913950e
MD5 f4dafed9ddaf8df96060967662ebc133
BLAKE2b-256 27856629c83d48262cd2897b516ccfcb05aeec9f36f06b5ac766066815dff536

See more details on using hashes here.

File details

Details for the file best_simple_caching-1.0.3-py3-none-any.whl.

File metadata

File hashes

Hashes for best_simple_caching-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 c01a8515824c733deb123f4f2f5d71d10ba32ac07f629237937b7bc12b38a827
MD5 e6e37880f46552d33c9019213eabf0f1
BLAKE2b-256 c912b6f6ea9ac48767ab6e295c08988bfa4e668bae719e9fea794eb374fb0f48

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page