dream/base/: cache-core-0.2.1 metadata and description

Simple index

Multi-backend cache abstraction layer with pluggable clients (Redis, Memory).

description_content_type text/markdown
project_urls
  • homepage, https://example.com/cache_core
  • repository, https://example.com/cache_core.git
provides_extras
  • redis
requires_dist
  • redis[hiredis]; extra == "redis"
requires_python >=3.9
File Tox results History
cache_core-0.2.1-py3-none-any.whl
Size
17 KB
Type
Python Wheel
Python
3
cache_core-0.2.1.tar.gz
Size
23 KB
Type
Source

cache_core

Multi-backend cache abstraction layer for Python — async-first, with pluggable clients for Redis and in-memory storage.

Installation

# Core (in-memory client — no external dependencies)
pip install cache_core

# With Redis support
pip install "cache_core[redis]"

# Or install in development mode from source
pip install -e .

Quick Start

import asyncio
from cache_core import CacheBuilder, create_memory_cache_client

async def main():
    cache = CacheBuilder(create_memory_cache_client())

    # Simple key-value
    await cache.key("greeting").set("hello")
    print(await cache.key("greeting").get())  # "hello"

    # Complex types auto-serialized
    await cache.key("user:1").set({"name": "Alice", "roles": ["admin"]}, ttl=3600)
    user = await cache.key("user:1").get()
    print(user["name"])  # "Alice"

asyncio.run(main())

Usage Examples

Key-Value Operations

# Set and get
await cache.key("count").set(42)
count = await cache.key("count").get()          # 42 (int)

await cache.key("name").set("Alice")
name = await cache.key("name").get()            # "Alice" (str)

# TTL (time-to-live in seconds)
await cache.key("session").set("data", ttl=300)
print(await cache.key("session").ttl())         # ~300

# Existence and deletion
exists = await cache.key("count").exists()      # True
deleted = await cache.key("count").delete()     # True
deleted = await cache.key("nope").delete()      # False

# Atomic increment / decrement
await cache.key("visits").incr()                # 1
await cache.key("visits").incr(5)               # 6
await cache.key("visits").decr(2)               # 4

Complex Types — Automatic JSON Serialization

Primitives (str, int, float, bool, None) pass through as-is. Complex types (dict, list, set, tuple) are automatically JSON-encoded on write and decoded on read.

# Dicts
await cache.key("config").set({"theme": "dark", "items": [1, 2, 3]})
config = await cache.key("config").get()        # {"theme": "dark", "items": [1, 2, 3]}

# Lists
await cache.key("ids").set([10, 20, 30])
ids = await cache.key("ids").get()              # [10, 20, 30]

# Nested structures
await cache.key("nested").set({
    "users": [
        {"id": 1, "tags": ["python", "async"]},
        {"id": 2, "tags": ["rust"]},
    ]
})

Namespace Scoping

Prefix keys with namespaces to organize your cache and avoid collisions.

users = cache.ns("users")
posts = cache.ns("posts")

await users.key("123").set({"name": "Alice"})
await users.key("456").set({"name": "Bob"})
await posts.key("1").set({"title": "Hello World"})

# Keys: "users:123", "users:456", "posts:1"

alice = await cache.key("users:123").get()      # {"name": "Alice"}
alice = await users.key("123").get()            # same result

# Nested namespaces
blog = cache.ns("blog").ns("comments")
await blog.key("post:1").set("Great post!")      # key: "blog:comments:post:1"

Lists (Arrays)

Each element is automatically serialized/deserialized individually.

# Push to the right (append)
await cache.list("queue").push("task_a", "task_b", "task_c")
await cache.list("queue").push({"id": 4, "type": "email"})

# Push to the left (prepend)
await cache.list("queue").push_left("urgent")

# Pop from the right (LIFO)
item = await cache.list("queue").pop()          # {"id": 4, "type": "email"}

# Pop from the left (FIFO)
item = await cache.list("queue").pop_left()     # "urgent"

# Range and length
items = await cache.list("queue").range(0, -1)  # all items
size = await cache.list("queue").len()          # 3

Sets

Each member is automatically serialized. Duplicates are ignored (deduplicated by JSON value).

# Add members — duplicates are silently ignored
added = await cache.set_("tags").add(
    {"name": "python"}, {"name": "rust"}, {"name": "python"}
)
print(added)  # 2 (third was a duplicate)

# Retrieve all members
members = await cache.set_("tags").members()
# [{"name": "python"}, {"name": "rust"}]

# Membership check
has_python = await cache.set_("tags").contains({"name": "python"})  # True

# Remove members
removed = await cache.set_("tags").remove({"name": "rust"})         # 1

# Cardinality
size = await cache.set_("tags").size()          # 1

Hashes (Maps)

Store field-value pairs under a single key. Each value is auto-serialized.

# Bulk set fields
await cache.hash("users").set({
    "alice": '{"name":"Alice","age":30}',
    "bob": '{"name":"Bob","age":25}',
})

# Get all fields
all_users = await cache.hash("users").getall()
# {"alice": {"name": "Alice", "age": 30}, "bob": {"name": "Bob", "age": 25}}

# Single field operations
await cache.hash("users").field("alice").set('{"name":"Alice","age":31}')
user = await cache.hash("users").field("alice").get()
# {"name": "Alice", "age": 31}

exists = await cache.hash("users").field("bob").exists()   # True
deleted = await cache.hash("users").field("bob").delete()   # 1

# List fields
fields = await cache.hash("users").keys()      # ["alice"]
count = await cache.hash("users").len()         # 1

Sorted Sets

Members ordered by score.

# Add members with scores
await cache.sorted_set("leaderboard").add({
    "alice": 1500.0,
    "bob": 1200.0,
    "carol": 1800.0,
})

# Range by score (ascending)
top = await cache.sorted_set("leaderboard").range(0, -1)
# ["bob", "alice", "carol"]

# With scores
top_scored = await cache.sorted_set("leaderboard").range(0, -1, withscores=True)
# [("bob", 1200.0), ("alice", 1500.0), ("carol", 1800.0)]

# Remove and size
await cache.sorted_set("leaderboard").remove("bob")
size = await cache.sorted_set("leaderboard").size()   # 2

Pattern-Based Batch Operations

Delete or list keys matching a glob pattern.

# Seed some keys
await cache.key("session:a").set("x")
await cache.key("session:b").set("y")
await cache.key("cache:config").set("z")

# Count matching keys
count = await cache.pattern("session:*").count()   # 2

# List matching keys
keys = await cache.pattern("session:*").keys()     # ["session:a", "session:b"]

# Batch delete
deleted = await cache.pattern("session:*").delete()  # 2

Async Context Manager

from cache_core import CacheBuilder, create_memory_cache_client

async with CacheBuilder(create_memory_cache_client()) as cache:
    await cache.key("temp").set("value")
    # ... do work ...
# Automatically disconnects on exit

Complete Usage Example

import asyncio
from cache_core import CacheBuilder, create_memory_cache_client


async def main():
    # Create client and builder (in-memory for testing; swap for Redis in production)
    cache = CacheBuilder(create_memory_cache_client())

    # 1. Cache a user profile with TTL
    user = {"id": 1, "name": "Alice", "email": "alice@example.com", "roles": ["admin"]}
    await cache.key("user:1").set(user, ttl=3600)

    # 2. Maintain a task queue
    tasks = cache.list("queue:pending")
    await tasks.push(
        {"task_id": "t1", "action": "send_email", "to": "alice@example.com"},
        {"task_id": "t2", "action": "generate_report", "user_id": 1},
    )

    # 3. Track unique tags for a post
    post_tags = cache.set_("post:42:tags")
    await post_tags.add("python", "async", "caching")
    await post_tags.add("python")  # duplicate — ignored

    # 4. Store configuration as a hash
    config = cache.hash("app:config")
    await config.set({
        "theme": "dark",
        "language": "en",
        "features": '{"search":true,"chat":false}',
    })

    # 5. Leaderboard with scores
    lb = cache.sorted_set("game:scores")
    await lb.add({"alice": 9500, "bob": 8200, "carol": 9900})

    # --- Read back ---
    print("User:", await cache.key("user:1").get())
    # User: {'id': 1, 'name': 'Alice', 'email': 'alice@example.com', 'roles': ['admin']}

    print("Next task:", await tasks.pop())
    # Next task: {'task_id': 't2', 'action': 'generate_report', 'user_id': 1}

    print("Tags:", await post_tags.members())
    # Tags: ['python', 'async', 'caching']

    print("Config:", await config.getall())
    # Config: {'theme': 'dark', 'language': 'en', 'features': {'search': True, 'chat': False}}

    print("Leaderboard:", await lb.range(0, -1, withscores=True))
    # Leaderboard: [('bob', 8200.0), ('alice', 9500.0), ('carol', 9900.0)]

    # 6. Cleanup session keys
    print("Session keys cleaned:", await cache.pattern("session:*").delete())

    # 7. Flush everything
    await cache.flush()


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

Redis Backend

import os
from cache_core import CacheBuilder, create_redis_cache_client

# From URL
client = create_redis_cache_client(url="redis://localhost:6379/0")

# From environment variables: REDIS_URL, REDIS_HOST, REDIS_PORT, REDIS_DB, REDIS_PASSWORD
client = create_redis_cache_client()

cache = CacheBuilder(client)
await cache.key("hello").set("world")

Custom Serializer

from cache_core import CacheBuilder, PickleSerializer, PassthroughSerializer, create_memory_cache_client

# Pickle — preserves full Python objects (Python-only)
cache = CacheBuilder(create_memory_cache_client(), serializer=PickleSerializer())

# Pass-through — no serialization, caller handles it
cache = CacheBuilder(create_memory_cache_client(), serializer=PassthroughSerializer())

API Reference

CacheBuilder

Method Returns Description
key(name) KeyOperation Target a single key
hash(name) HashOperation Target a hash (map)
list(name) ListOperation Target a list (array)
set_(name) SetOperation Target a set
sorted_set(name) SortedSetOperation Target a sorted set
pattern(glob) PatternOperation Target keys matching a glob pattern
ns(namespace) CacheBuilder Scope to a namespace prefix
connect() None Open backend connections
disconnect() None Close backend connections
ping() bool Health check
flush() None Remove all keys

KeyOperation

Method Returns Description
get() Any Get the value
set(value, ttl=None) None Set the value with optional TTL
delete() bool Delete the key
exists() bool Check if key exists
expire(ttl) bool Set TTL on existing key
ttl() int Get remaining TTL (-1 if none)
incr(amount=1) int Increment integer value
decr(amount=1) int Decrement integer value

ListOperation

Method Returns Description
push(*values) int Append to right (tail)
push_left(*values) int Prepend to left (head)
pop() Any Remove and return rightmost
pop_left() Any Remove and return leftmost
range(start, stop) list Slice of the list
len() int Number of elements

SetOperation

Method Returns Description
add(*members) int Add members, returns count added
remove(*members) int Remove members, returns count removed
members() list All members
contains(member) bool Membership test
size() int Cardinality

HashOperation

Method Returns Description
field(name) HashFieldOperation Target a specific field
set(mapping) int Bulk set fields
getall() dict All fields and values
delete(*fields) int Delete fields
keys() list All field names
len() int Number of fields

HashFieldOperation

Method Returns Description
get() Any Get the field value
set(value) int Set the field value
getall() dict All fields in the hash
delete() int Delete this field
exists() bool Check if field exists

SortedSetOperation

Method Returns Description
add(mapping) int Add members with scores
remove(*members) int Remove members
range(start, stop, withscores=False) list Members by score
size() int Cardinality

PatternOperation

Method Returns Description
delete() int Delete all matching keys
keys() list List matching keys
count() int Count matching keys