Skip to main content

Pydantic models with Redis as the backend

Project description

Rapyer Logo

Rapyer

Redis Atomic Pydantic Engine Reactor

An async Redis ORM that provides atomic operations for complex data models

Python 3.10+ License: MIT Redis codecov PyPI version Downloads Documentation

📚 Full Documentation | Installation | Examples | API Reference


What is Rapyer?

Rapyer (Redis Atomic Pydantic Engine Reactor) is a modern async Redis ORM that enables atomic operations on complex data models. Built with Pydantic v2, it provides type-safe Redis interactions while maintaining data consistency and preventing race conditions.

Key Features

🚀 Atomic Operations - Built-in atomic updates for complex Redis data structures
Async/Await - Full asyncio support for high-performance applications
🔒 Type Safety - Complete type validation using Pydantic v2
🌐 Universal Types - Native optimization for primitives, automatic serialization for complex types
🔄 Race Condition Safe - Lock context managers and pipeline operations
📦 Redis JSON - Efficient storage using Redis JSON with support for nested structures

Installation

pip install rapyer

Requirements:

  • Python 3.10+
  • Redis server with JSON module
  • Pydantic v2

Quick Start

import asyncio
from rapyer.base import AtomicRedisModel
from typing import List, Dict


class User(AtomicRedisModel):
    name: str
    age: int
    tags: List[str] = []
    metadata: Dict[str, str] = {}


async def main():
    # Create and save a user
    user = User(name="John", age=30)
    await user.asave()

    # Atomic operations that prevent race conditions
    await user.tags.aappend("python")
    await user.tags.aextend(["redis", "pydantic"])
    await user.metadata.aupdate(role="developer", level="senior")

    # Load user from Redis
    loaded_user = await User.aget(user.key)
    print(f"User: {loaded_user.name}, Tags: {loaded_user.tags}")

    # Atomic operations with locks for complex updates
    async with user.alock("update_profile") as locked_user:
        locked_user.age += 1
        await locked_user.tags.aappend("experienced")
        # Changes saved atomically when context exits


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

Core Concepts

Atomic Operations

Rapyer ensures data consistency with built-in atomic operations:

# These operations are atomic and race-condition safe
await user.tags.aappend("python")           # Add to list
await user.metadata.aupdate(role="dev")     # Update dict
await user.score.set(100)                   # Set value

Lock Context Manager

For complex multi-field updates:

async with user.alock("transaction") as locked_user:
    locked_user.balance -= 50
    locked_user.transaction_count += 1
    # All changes saved atomically

Pipeline Operations

Batch multiple operations for performance:

async with user.apipeline() as pipelined_user:
    await pipelined_user.tags.aappend("redis")
    await pipelined_user.metadata.aupdate(level="senior")
    # Executed as single atomic transaction

Type Support

Rapyer supports all Python types with automatic serialization:

  • Native types (str, int, List, Dict) - Optimized Redis operations
  • Complex types (dataclass, Enum, Union) - Automatic pickle serialization
  • Nested models - Full Redis functionality preserved
from dataclasses import dataclass
from enum import Enum

@dataclass
class Config:
    debug: bool = False

class User(AtomicRedisModel):
    name: str = "default"
    scores: List[int] = []
    config: Config = Config()  # Auto-serialized
    
# All types work identically
user = User()
await user.config.set(Config(debug=True))  # Automatic serialization
await user.scores.aappend(95)               # Native Redis operation

Why Choose Rapyer?

Comparison with Other Redis ORMs

Feature Rapyer Redis OM pydantic-redis orredis
🚀 Atomic Operations ✅ Built-in for all operations ❌ Manual transactions only ❌ Manual transactions only ❌ Manual transactions only
🔒 Lock Context Manager ✅ Automatic with async with model.alock() ❌ Manual implementation required ❌ Manual implementation required ❌ Manual implementation required
⚡ Pipeline Operations ✅ True atomic batching with model.apipeline() ⚠️ Basic pipeline support ❌ No pipeline support ❌ No pipeline support
🌐 Universal Type Support ✅ Native + automatic serialization for any type ⚠️ HashModel vs JsonModel limitations ⚠️ Limited complex types ⚠️ Limited complex types
🔄 Race Condition Safe ✅ Built-in prevention with Lua scripts ❌ Manual implementation required ❌ Manual implementation required ❌ Manual implementation required
📦 Redis JSON Native ✅ Optimized JSON operations ✅ Via JsonModel only ❌ Hash-based ❌ Hash-based
⚙️ Pydantic v2 Support ✅ Full compatibility ✅ Recent support ⚠️ Limited support ⚠️ Basic support
🎯 Type Safety ✅ Complete validation ✅ Good validation ✅ Good validation ⚠️ Basic validation
⚡ Performance ✅ Optimized operations ✅ Good performance ✅ Standard ✅ Rust-optimized
🔧 Nested Model Support ✅ Full Redis functionality preserved ⚠️ Limited nesting ✅ Advanced relationships ⚠️ Basic support
🎛️ Custom Primary Keys ✅ Field annotations ❌ ULIDs only ✅ Custom fields ✅ Custom fields
🧪 Extensive Test Coverage ✅ 100%+ comprehensive tests with CI ⚠️ Basic testing with CI ⚠️ Limited test coverage ⚠️ Basic test suite

🏆 What Makes Rapyer Unique

True Atomic Operations Out of the Box

# Rapyer - Atomic by default
await user.tags.aappend("python")  # Race-condition safe
await user.metadata.aupdate(role="dev")  # Always atomic

# Others - Manual transaction management required
async with redis.apipeline() as pipe:  # Manual setup
    pipe.multi()  # Manual transaction
    # ... manual Redis commands               # Error-prone
    await pipe.execute()

Intelligent Lock Management

# Rapyer - Automatic lock context
async with user.alock("profile_update") as locked_user:
    locked_user.balance -= 50
    locked_user.transaction_count += 1
    # All changes saved atomically on exit

# Others - Manual lock implementation
lock_key = f"lock:{user.key}"
while not await redis.set(lock_key, token, nx=True):  # Manual retry logic
    await asyncio.sleep(0.1)  # Race conditions possible
# ... manual cleanup required

Universal Type System

# Rapyer - Any Python type works identically
class User(AtomicRedisModel):
    scores: List[int] = []              # Native Redis operations
    config: MyDataClass = MyDataClass()  # Auto-serialized
    metadata: Dict[str, Any] = {}       # Native Redis operations

# All types support the same atomic operations
await user.config.set(new_config)      # Automatic serialization
await user.scores.aappend(95)           # Native Redis LIST operations
await user.metadata.aupdate(key="val") # Native Redis JSON operations

Pipeline with True Atomicity

# Rapyer - Everything in pipeline is atomic
async with user.apipeline() as pipelined_user:
    await pipelined_user.tags.aappend("redis")
    await pipelined_user.metadata.aupdate(level="senior")
    # Single atomic transaction - either all succeed or all fail

# Others - No built-in pipeline abstraction for ORM operations

Learn More

  • 📖 Documentation - Complete guide and API reference
  • 🗺️ Roadmap - Future features and development plans

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. Thanks for @Mizaro and @oazmiry this would not have been possible without you.

License

MIT License

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

rapyer-1.2.3.tar.gz (30.6 kB view details)

Uploaded Source

Built Distribution

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

rapyer-1.2.3-py3-none-any.whl (44.0 kB view details)

Uploaded Python 3

File details

Details for the file rapyer-1.2.3.tar.gz.

File metadata

  • Download URL: rapyer-1.2.3.tar.gz
  • Upload date:
  • Size: 30.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for rapyer-1.2.3.tar.gz
Algorithm Hash digest
SHA256 02c1db7e1e52f761ed613d091d5bc4abb7a888b3da3e54df0bf91e1052da4ef0
MD5 74c0ab3bbeb69819013ceb3d7916e395
BLAKE2b-256 b7d7402b6a1565192cb31623f2f45a662195ac35e72ba4b3f5e206fd4dbc8a04

See more details on using hashes here.

Provenance

The following attestation bundles were made for rapyer-1.2.3.tar.gz:

Publisher: publish.yml on imaginary-cherry/rapyer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rapyer-1.2.3-py3-none-any.whl.

File metadata

  • Download URL: rapyer-1.2.3-py3-none-any.whl
  • Upload date:
  • Size: 44.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for rapyer-1.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 ee937a4bb2b90e0701824fdfe7f77388dfa108a73b00c0189ae01a83665125bb
MD5 1e0d303237ae9a5703ca3ce8845e77cb
BLAKE2b-256 c23a4d081cd33f294d6d398ba9618d9e09f3dbdcbcf8e9a275b774cb150af75a

See more details on using hashes here.

Provenance

The following attestation bundles were made for rapyer-1.2.3-py3-none-any.whl:

Publisher: publish.yml on imaginary-cherry/rapyer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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