Skip to main content

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.
  • ✅ Batch invalidation – invalidate_many for multiple primary keys.
  • ✅ TTL presets – short, medium, long_lived, catalog helpers.
  • ✅ 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
    ...

Batch invalidation

await user_cache.invalidate_many(
    keys=[{"user_id": 1}, {"user_id": 2}],
)

Primary keys can also be passed directly:

await user_cache.invalidate_many([UserPK(user_id=1), UserPK(user_id=2)])

TTL presets

from best_simple_cache import short, medium, long_lived, catalog

config = short(max_size=2000)       # ttl=120
config = medium(max_size=5000)      # ttl=300
config = long_lived(max_size=1000)  # ttl=600
config = catalog(max_size=1000)     # ttl=600

Constants: SHORT_TTL, MEDIUM_TTL, LONG_TTL, CATALOG_TTL.

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.
  • async invalidate_many(pks: Iterable[PK] | None = None, *, keys: Iterable[Mapping[str, Any]] | None = None) -> None – remove multiple entries.
  • 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 / invalidate_many – passthrough to the underlying cache.
  • 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).

presets

  • short, medium, long_lived, catalog – return CachingConfig with preset TTL (120 / 300 / 600 / 600 seconds).
  • SHORT_TTL, MEDIUM_TTL, LONG_TTL, CATALOG_TTL – TTL constants.

Changelog

1.0.5

  • invalidate_many on EntityCache and CacheDecorator.
  • TTL preset helpers in best_simple_cache.presets.
  • pytest suite for core cache behavior.

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

Metadata

Release files for best-simple-caching 1.0.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for best-simple-caching 1.0.5
File Size Uploaded
best_simple_caching-1.0.5.tar.gz 12.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for best-simple-caching 1.0.5
File Interpreter ABI Platform
best_simple_caching-1.0.5-py3-none-any.whl Python 3 none any Details

Total release size: 22.9 kB

Release files / best_simple_caching-1.0.5.tar.gz

Download URL best_simple_caching-1.0.5.tar.gz
Size 12.8 kB
Tags Source
SHA-256 checksum
How to use checksums
00e77e975487d46ec984099b7113b81e8a84f5b76b948a38cdd7af48e7f940c5
BLAKE2b-256 checksum
How to use checksums
646a763c76b65efec01fa3d0aa28c739cfb98d58394e6d06324b1c9e7647a00c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.4

Release files / best_simple_caching-1.0.5-py3-none-any.whl

Download URL best_simple_caching-1.0.5-py3-none-any.whl
Size 10.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e5c552a40b8162d21650edfe417366a03c0fb8611932c24f9471a76431ffa090
BLAKE2b-256 checksum
How to use checksums
bfafcce7880187777b99eb59bb902b5ff6eb8699ec9bf18f4c2a9ebafaf5f116
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.4

Release history Release notifications | RSS feed

This release

1.0.5 This release

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

1 release file

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page