Metadata-Version: 2.3
Name: caesura
Version: 0.1.0
Summary: Mid-execution distributed locking with zero boilerplate.
Keywords: serialization,locks,distributed,concurrency,decorator
Author: Wannes Vantorre
Author-email: Wannes Vantorre <vantorrewannes@gmail.com>
License: MIT
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.14
Project-URL: Homepage, https://codeberg.org/ProductionCode/caesura
Project-URL: Repository, https://codeberg.org/ProductionCode/caesura
Project-URL: Issues, https://codeberg.org/ProductionCode/caesura/issues
Description-Content-Type: text/markdown

# caesura 📜

**Mid-execution distributed locking with zero boilerplate.**

Standard distributed locks force you to wrap your entire function in a `with` block, even if you need to do heavy data parsing or database lookups just to find out _what_ needs locking.

`caesura` solves this by letting you safely acquire locks mid-execution. By using the native `yield` keyword, you signal exactly where the blocking I/O happens, and the decorator safely manages the teardown—even if the function crashes.

```bash
uv add caesura
```

## The Difference

| Concept                     | Standard Context Managers      | `caesura`                               |
| :-------------------------- | :----------------------------- | :------------------------------------ |
| **Lock Boundary**           | The entire function.           | Exactly where it matters.             |
| **Deadlock Prevention**     | Manual ordering required.      | Mathematically guaranteed via Tuples. |
| **Infrastructure Coupling** | High (Imports Redis/Postgres). | Zero (Accepts any Context Manager).   |

## Usage

Simply use the `@caesura.serialize` decorator and `yield` your standard context managers when you are ready to lock.

```python
import caesura

@caesura.serialize
def process_webhook(payload):
    # 1. Do heavy, lock-free work first
    parsed_data = heavy_json_parse(payload)

    # 2. Yield hands the lock to the decorator.
    # This visually signals the blocking I/O to the reader.
    yield redis.lock(f"user:{parsed_data.user_id}")

    # 3. Execution resumes safely locked.
    db.update_balance(parsed_data.user_id)

    # When the function returns (or if it crashes!),
    # caesura safely releases all acquired locks via Python's native ExitStack.
    return True
```

### Deadlock Prevention

The Dining Philosophers deadlock occurs when Worker A locks `alice` then `bob`, and Worker B locks `bob` then `alice`.

`caesura` solves this statelessly. If your transaction requires multiple resources, yield them as a Python `tuple`. `caesura` will intercept the tuple, lexicographically sort the locks by their identifier, and acquire them in an identical, deterministic order across all workers.

```python
@caesura.serialize
def transfer_funds(sender_id, receiver_id):
    # Even if receiver_id evaluates first alphabetically,
    # caesura sorts them before acquiring. Deadlocks are mathematically impossible.
    yield (
        redis.lock(sender_id),
        redis.lock(receiver_id)
    )

    db.execute_transfer(sender_id, receiver_id)
```
