Metadata-Version: 2.4
Name: grelmicro
Version: 0.14.1
Summary: grelmicro is a lightweight toolkit for building Python applications that need to coordinate work across processes
Keywords: microservice,microservice-patterns,self-contained-systems,cloud-native,kubernetes,containerized,distributed-systems,distributed-lock,leader-election,rate-limiter,circuit-breaker,cache,health-checks,task-scheduler,logging,tracing,opentelemetry,redis,postgresql,fastapi,asyncio,anyio,async,pydantic
Author: Loïc Gremaud
Author-email: Loïc Gremaud <grelinfo@gmail.com>
License-Expression: MIT
Classifier: Intended Audience :: Information Technology
Classifier: Intended Audience :: System Administrators
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python
Classifier: Topic :: Internet
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development
Classifier: Typing :: Typed
Classifier: Development Status :: 1 - Planning
Classifier: Environment :: Web Environment
Classifier: Framework :: AsyncIO
Classifier: Framework :: FastAPI
Classifier: Framework :: Pydantic
Classifier: Framework :: Pydantic :: 2
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Dist: anyio>=4.0.0
Requires-Dist: pydantic>=2.5.0
Requires-Dist: fast-depends>=2.0.0
Requires-Dist: pydantic-settings>=2.5.0
Requires-Dist: pydantic-extra-types>=2.11.0
Requires-Dist: lightkube>=0.15.0 ; extra == 'kubernetes'
Requires-Dist: opentelemetry-api>=1.20.0 ; extra == 'opentelemetry'
Requires-Dist: opentelemetry-sdk>=1.20.0 ; extra == 'opentelemetry'
Requires-Dist: asyncpg>=0.30.0 ; extra == 'postgres'
Requires-Dist: redis>=5.0.0 ; extra == 'redis'
Requires-Dist: aiosqlite>=0.20.0 ; extra == 'sqlite'
Requires-Dist: orjson>=3.10.11 ; extra == 'standard'
Requires-Dist: structlog>=24.1.0 ; extra == 'structlog'
Requires-Dist: orjson>=3.10.11 ; extra == 'structlog'
Requires-Python: >=3.11
Project-URL: Documentation, https://grelinfo.github.io/grelmicro
Project-URL: Repository, https://github.com/grelinfo/grelmicro.git
Project-URL: Issues, https://github.com/grelinfo/grelmicro/issues
Provides-Extra: kubernetes
Provides-Extra: opentelemetry
Provides-Extra: postgres
Provides-Extra: redis
Provides-Extra: sqlite
Provides-Extra: standard
Provides-Extra: structlog
Description-Content-Type: text/markdown

<p align="center">
  <a href="https://grelinfo.github.io/grelmicro">
    <picture>
      <source media="(prefers-color-scheme: dark)" srcset="docs/img/logo/wordmark-dark.svg">
      <img alt="grelmicro" src="docs/img/logo/wordmark.svg" width="520">
    </picture>
  </a>
</p>

<p align="center">
  <em>Micro by design. Fast by default. Import only what you need.</em>
</p>

<p align="center">
  A toolkit of Python primitives built for production needs, for services that coordinate work across processes, workers, or clusters.
</p>

[![PyPI - Version](https://img.shields.io/pypi/v/grelmicro)](https://pypi.org/project/grelmicro/)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/grelmicro)](https://pypi.org/project/grelmicro/)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/grelinfo/grelmicro/blob/main/LICENSE)
[![codecov](https://codecov.io/gh/grelinfo/grelmicro/graph/badge.svg?token=GDFY0AEFWR)](https://codecov.io/gh/grelinfo/grelmicro)
[![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![ty](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ty/main/assets/badge/v0.json)](https://github.com/astral-sh/ty)

> **Project status: Active development.** grelmicro is pre-1.0, so APIs can change between minor releases. Every change ships with a clear deprecation path: `DeprecationWarning`, PEP 702 `@deprecated`, and a one-minor removal window.

______________________________________________________________________

**Documentation**: [https://grelinfo.github.io/grelmicro/](https://grelinfo.github.io/grelmicro)

**Source Code**: [https://github.com/grelinfo/grelmicro](https://github.com/grelinfo/grelmicro)

______________________________________________________________________

## Why grelmicro

grelmicro gives you the building blocks every Python service needs: locks, rate limits, circuit breakers, cache, logging, health checks, and task scheduling. Each is a small, focused module with a pluggable backend.

It is built for any Python application, from a standalone script to full **microservice patterns** and **self-contained systems**, with a strong focus on solving **distributed system** problems. It fits naturally into **cloud-native applications**, **containerized apps**, and **Kubernetes** deployments.

- **Micro**: one focused primitive per module. Import only what your code touches. Nothing else is loaded.
- **Fast**: thin wrappers with low per-call overhead. Redis operations are server-side Lua scripts; in-memory paths skip I/O entirely.
- **Async-first**: every I/O call is `async` / `await`. Drops into FastAPI, FastStream, and any AnyIO-based stack.
- **Backend-agnostic**: each primitive is a protocol. Swap Redis for PostgreSQL or SQLite without touching application code.
- **Production-ready**: 100% test coverage, full type hints, and explicit deprecation paths (`DeprecationWarning` + PEP 702 `@deprecated`) with a one-minor-version removal window.

## Modules

| Module | Summary |
|---|---|
| [**Cache**](docs/cache.md) | `@cached` decorator with per-key stampede protection. In-memory `TTLCache` or `RedisCacheBackend`. |
| [**Synchronization**](docs/sync.md) | Distributed `Lock`, `TaskLock`, `LeaderElection`. Redis, PostgreSQL, SQLite, Kubernetes, in-memory. |
| [**Task Scheduler**](docs/task.md) | Periodic task execution with optional distributed locking. Lightweight, not a Celery replacement. |
| [**Resilience**](docs/resilience/index.md) | [Circuit Breaker](docs/resilience/circuit-breaker.md) and [Rate Limiter](docs/resilience/rate-limiter.md) with pluggable algorithms (`TokenBucket`, `GCRA`). |
| [**Logging**](docs/logging.md) | 12-factor logging with JSON, LOGFMT, TEXT, or PRETTY output, structured error rendering, and OpenTelemetry trace context. |
| [**Tracing**](docs/tracing.md) | Unified instrumentation. `@instrument` creates OpenTelemetry spans and enriches log records with structured context. |
| [**Health**](docs/health.md) | Health check registry with concurrent runners and FastAPI liveness / readiness integration. |
| [**JSON**](docs/json.md) | Fast JSON via `orjson` when available, with automatic fallback to stdlib `json`. |

## Installation

```bash
pip install grelmicro
```

See the [Installation guide](https://grelinfo.github.io/grelmicro/installation/) for `uv` and `poetry` commands, plus optional extras for Redis, PostgreSQL, SQLite, Kubernetes, OpenTelemetry, and structlog.

## Example

### FastAPI integration

Create a file `main.py` with:

```python
import logging
from contextlib import asynccontextmanager

from fastapi import FastAPI, HTTPException, Request

from grelmicro.cache import JsonSerializer, TTLCache, cached
from grelmicro.cache.redis import RedisCacheBackend
from grelmicro.logging import configure_logging
from grelmicro.resilience import GCRA
from grelmicro.resilience.circuitbreaker import CircuitBreaker
from grelmicro.resilience.errors import RateLimitExceededError
from grelmicro.resilience.ratelimiter import RateLimiter
from grelmicro.resilience.redis import RedisRateLimiterBackend
from grelmicro.sync import LeaderElection, Lock
from grelmicro.sync.redis import RedisSyncBackend
from grelmicro.task import TaskManager

logger = logging.getLogger(__name__)

# === grelmicro ===
task = TaskManager()
sync_backend = RedisSyncBackend("redis://localhost:6379/0")
cache_backend = RedisCacheBackend("redis://localhost:6379/0", prefix="myapp:")
rate_limit_backend = RedisRateLimiterBackend("redis://localhost:6379/0")
leader_election = LeaderElection("leader-election")
task.add_task(leader_election)

cache = TTLCache(ttl=300, serializer=JsonSerializer())


# === FastAPI ===
@asynccontextmanager
async def lifespan(app):
    configure_logging()
    async with sync_backend, cache_backend, rate_limit_backend, task:
        yield


app = FastAPI(lifespan=lifespan)


# --- Cache: avoid redundant database queries ---
@cached(cache)
async def get_user(user_id: int) -> dict:
    return {"id": user_id, "name": "Alice"}


@app.get("/users/{user_id}")
async def read_user(user_id: int):
    return await get_user(user_id)


# --- Circuit Breaker: protect calls to an unreliable service ---
cb = CircuitBreaker("my-service")


@app.get("/")
async def read_root():
    async with cb:
        return {"Hello": "World"}


# --- Rate Limiter: protect endpoints from overload ---
api_limiter = RateLimiter("api", algorithm=GCRA(limit=100, window=60))


@app.get("/api")
async def api_endpoint(request: Request):
    try:
        await api_limiter.acquire_or_raise(key=request.client.host)
    except RateLimitExceededError as exc:
        raise HTTPException(
            status_code=429,
            detail="Too many requests",
            headers={"Retry-After": str(int(exc.retry_after))},
        )
    return {"status": "ok"}


# --- Distributed Lock: synchronize access to a shared resource ---
lock = Lock("shared-resource")


@app.get("/protected")
async def protected():
    async with lock:
        return {"status": "ok"}


# --- Interval Task: run locally on every worker ---
@task.interval(seconds=5)
def heartbeat():
    logger.info("heartbeat")


# --- Distributed Task: run once per interval across all workers ---
@task.interval(seconds=60, max_lock_seconds=300)
def cleanup():
    logger.info("cleanup")


# --- Leader-gated Task: only the leader executes ---
@task.interval(seconds=10, leader=leader_election)
def leader_only_task():
    logger.info("leader task")
```

## License

This project is licensed under the terms of the [MIT license](https://github.com/grelinfo/grelmicro/blob/main/LICENSE).
