Metadata-Version: 2.4
Name: pyro3
Version: 0.2.1
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Rust
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Hardware :: Symmetric Multi-processing
License-File: LICENSE-APACHE
License-File: LICENSE-COFFEE
License-File: LICENSE-MIT
Summary: A lock-free, high-concurrency task broker for Python, powered by Rust.
Keywords: rust,python,pyo3,concurrency,lock-free,task-broker,background-tasks,gil-free,multithreading,performance
Requires-Python: >=3.8
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Bug Tracker, https://github.com/emivvvvv/pyroxide/issues
Project-URL: Homepage, https://github.com/emivvvvv/pyroxide
Project-URL: Repository, https://github.com/emivvvvv/pyroxide

<!-- PROJECT LOGO -->
<br />
<div align="center">
  <a href="https://github.com/emivvvvv/pyroxide">
    <img src="https://raw.githubusercontent.com/emivvvvv/pyroxide/main/pyroxide.svg" alt="Logo" width="80" height="80">
  </a>

  <h3 align="center">Pyroxide</h3>

  <p align="center">
    A lock-free, high-concurrency background task broker for Python, powered by Rust.
    <br />
    <br />
    <a href="https://www.rust-lang.org/"><img src="https://img.shields.io/badge/rust-stable-brightgreen.svg" alt="Rust" style="display:inline;margin:0 2px;"></a>
    <a href="https://www.python.org/"><img src="https://img.shields.io/badge/python-3.8+-blue.svg" alt="Python" style="display:inline;margin:0 2px;"></a>
    <a href="LICENSE-MIT"><img src="https://img.shields.io/badge/License-MIT%2FApache--2.0%2FCoffee-blue.svg" alt="License: MIT/Apache-2.0/Coffee" style="display:inline;margin:0 2px;"></a>
    <br />
    <br />
    <a href="https://emivvvvv.github.io/pyroxide/"><strong>Explore the Docs »</strong></a>
    <br />
    <br />
    <a href="https://emivvvvv.github.io/pyroxide/api/pyroxide.html">API Reference</a>
    &middot;
    <a href="https://github.com/emivvvvv/pyroxide/tree/main/examples">See Examples</a>
    &middot;
    <a href="https://github.com/emivvvvv/pyroxide/issues/new?labels=bug">Report Bug</a>
    &middot;
    <a href="https://github.com/emivvvvv/pyroxide/issues/new?labels=enhancement">Request Feature</a>
  </p>
</div>

---

Pyroxide is a high-concurrency, lock-free background task broker designed to bridge Python and Rust. It allows CPU-bound or blocking workloads to bypass the Python Global Interpreter Lock (GIL) with minimal memory overhead and zero CPU-sleep polling.

With Pyroxide, you can seamlessly offload tasks from Python to a background native OS thread pool. Tasks block natively on the OS kernel level using signaling primitives (`Condvar`) rather than CPU-burning sleep loops, allowing Python to yield control instantly.

## Key Features

*   **Bypass the Python GIL**: Explicitly release the Python GIL via PyO3 thread-detaching, running heavy computations concurrently on native OS threads.
*   **Zero-Overhead Status Tracking**: Avoids global lock contention using an atomic-state (`AtomicU8`) task tracking structure per task slot under a concurrent sharded/read-lock Slab architecture.
*   **Instant Condvar Signaling**: Replaces latency-inducing polling loops (`time.sleep`) with native Rust `Condvar` waking, waking waiting Python threads in microseconds with 0% CPU consumption.
*   **Dynamic Task Execution**: Seamlessly offloads dynamic Python callbacks (running inside temporary attached-GIL scopes) or executes native Rust functions completely GIL-free.
*   **Configurable Concurrency**: Set worker thread pool size dynamically at startup via environment variables.
*   **Panic Safety**: Wrapped task execution prevents Rust worker panics from crashing the host Python interpreter, gracefully marking tasks as `Failed` instead.
*   **Zero-Copy Byte Buffers**: Easily pass byte arrays, memoryviews, and columnar buffers (e.g., Apache Arrow) across the C-ABI without copy overhead.
*   **Full Type-Hinting**: Exposes advanced typing generic `@overload` signatures, offering full autocomplete support for modern IDEs.

---

## Installation

### From PyPI (Recommended)

```bash
pip install pyro3
```

### Build and Install locally

Ensure you have Rust, Python (3.8+), and `maturin` installed. Compile and install the C-extension into your active virtual environment:

```bash
# Clone the repository
git clone https://github.com/emivvvvv/pyroxide.git
cd pyroxide

# Compile and install editable build using maturin
pip install maturin
maturin develop
```

---

## Performance & Validation

We benchmarked Pyroxide against a baseline Python-based task queue using standard thread-polling loops (`time.sleep(0.01)`) and lock-guarded queues, isolated using a 1ms simulated execution payload to highlight broker overhead:

### 1. Latency (Single-Threaded Sequential Wait)
| Tasks | Baseline (10ms Polling Loop) | Pyroxide (Condvar + Lock-Free) | **Latency Reduction** |
| :--- | :--- | :--- | :--- |
| **10 Tasks** | `1.0180s` | `0.0002s` (0.2ms) | **~5,000x faster** |
| **50 Tasks** | `3.5289s` | `0.0008s` (0.8ms) | **~4,400x faster** |
| **Avg. Latency** | **`70.58ms`** | **`0.02ms` (20µs)** | **3,500x less overhead** |

### 2. Multi-Threaded Throughput (40 Concurrent Submissions)
| Threads | Baseline (Lock Contention) | Pyroxide (Lock-Free) | **Speedup** |
| :--- | :--- | :--- | :--- |
| **2 Threads** | `10.1848s` | `0.0032s` (3.2ms) | **3,180x faster** |
| **4 Threads** | `5.1193s` | `0.0013s` (1.3ms) | **3,930x faster** |
| **8 Threads** | `2.5624s` | `0.0015s` (1.5ms) | **1,700x faster** |

### 3. Automated Test Suite (Pytest)

A senior developer can inspect and run our comprehensive, production-grade automated verification suite under the `tests/` directory:

```bash
# Install pytest and run the suite
pip install pytest
pytest -v tests/
```

The test suite covers:
- **True Concurrency (GIL Bypass)**: Verifies that 4 concurrent native tasks running on 4 background worker threads execute in parallel in a single task duration (~100ms instead of ~400ms), proving the Python GIL is successfully released during processing.
- **Main Thread Responsiveness**: Asserts that long-running native background execution does not block or latency-contend the main Python thread.
- **Panic Safety & Recovery**: Verifies that Rust worker panics are caught gracefully, marking the task status as `Failed` and throwing a Python `RuntimeError` on result retrieval without crashing the interpreter or leaking worker thread health.
- **Deterministic Slab Memory Eviction**: Validates that Slab task allocations are cleanly deallocated immediately via explicit consumption (`consume=True`) or automatically via garbage collection destructor bindings (`__del__` GC eviction) when a `TaskHandle` is deleted.
- **High Churn Stress Testing**: Runs a concurrent ThreadPoolExecutor stress test submitting 1,600 tasks across multiple client threads to verify zero lock contentions or memory leaks under heavy thread churn.

---

## Configuration

Pyroxide can be configured via environment variables before the engine initializes:

*   `PYROXIDE_WORKERS`: Sets the number of background worker threads in the pool. Defaults to the number of CPU cores (`available_parallelism`).

```bash
export PYROXIDE_WORKERS=4
python my_app.py
```

---

## Quick Start

### 1. Offloading Python Callables (Default)
By default, `@task` runs the decorated Python function in the background pool.

```python
from pyroxide import task

@task
def calculate_square(x: int) -> int:
    # Runs on background OS threads in the Rust pool
    return x * x

# Submit and get a handle immediately
handle = calculate_square(12)
print(f"Task status: {handle.status}")

# Blocks natively (0% CPU) until complete, then returns result
# Automatically evicts the task from the Rust Slab once retrieved (consume=True)
result = handle.result()
print(f"Result: {result}") # Output: 144
```

### 2. Offloading Native Rust Tasks (GIL-Free)
To completely bypass the Python GIL and run logic natively in Rust:

```python
from pyroxide import task

# Passes the string to Rust which processes it completely GIL-free
@task(native=True)
def native_uppercase(payload: str) -> None:
    pass

handle = native_uppercase("hello pyroxide")
result = handle.result()
print(f"Result: {result}") # Output: b"HELLO PYROXIDE"
```

### 3. Graceful Memory Reclamation & Retaining Results
By default, `result()` consumes and evicts the task. If you want to keep the task in the Slab (e.g. to check status or result again later), set `consume=False`. It will be automatically cleaned up later via the Python garbage collector when the handle reference is deleted:

```python
import gc
from pyroxide import task
from pyroxide._pyroxide import get_slab_size

handle = calculate_square(10)
print(get_slab_size()) # Output: 1

# Retrieve result but retain task in the Slab
result = handle.result(consume=False)

# Deleting the Python TaskHandle reference forces GC eviction in the Rust Slab
del handle
gc.collect()

print(get_slab_size()) # Output: 0 (No memory leaked)
```

### 4. Asynchronous Awaiting (asyncio support)
For modern asynchronous web servers (e.g. FastAPI, Sanic), you can non-blockingly await task execution to yield control back to the event loop:

```python
import asyncio
from pyroxide import task

@task
def calculate_square(x: int) -> int:
    return x * x

async def main():
    handle = calculate_square(12)
    # Yields control non-blockingly to the event loop while waiting
    result = await handle.result_async()
    print(f"Result: {result}") # Output: 144

asyncio.run(main())
```

### 5. Batch Task Submission
To submit multiple tasks under a single write lock (avoiding lock-contention overhead for high-churn operations), use the `.batch()` helper:

```python
from pyroxide import task

@task
def calculate_square(x: int) -> int:
    return x * x

# Submits all 5 tasks under a single lock acquisition
handles = calculate_square.batch([1, 2, 3, 4, 5])

# Retrieve results
results = [h.result() for h in handles]
print(results) # Output: [1, 4, 9, 16, 25]
```

### 6. Task Cancellation
Tasks can be aborted before or during execution. Calling `.cancel()` transitions the task status to `Cancelled` and immediately terminates sleep loops in native workers:

```python
from pyroxide import task

@task(native=True)
def native_sleep(payload: str) -> None:
    pass

# Submit long-running task
handle = native_sleep("SLEEP:5000")

# Abort task execution
cancelled = handle.cancel()
print(f"Cancelled: {cancelled} | Status: {handle.status}") 
# Output: Cancelled: True | Status: Cancelled

try:
    handle.result()
except RuntimeError as e:
    print(e) # Output: Task cancelled
```

### 7. Background Exception Tracebacks
When a background Python task fails, Pyroxide captures its traceback inside the worker thread and propagates it to the main thread's exception, ensuring clean diagnostics:

```python
from pyroxide import task

@task
def fail_task(x: int) -> int:
    raise ValueError("Something went wrong!")

handle = fail_task(10)
try:
    handle.result()
except RuntimeError as e:
    # Prints the exception message AND the exact background stack trace!
    print(e)
```

---

## Performance & Benchmarks

Pyroxide is engineered for ultra-high throughput and low-overhead Python-to-Rust communication. To measure the exact performance under different submission models, run:

```bash
python examples/benchmark.py
```

### Benchmark Results (CPython 3.11, Apple M3 Max)

For **200 background tasks**, Pyroxide demonstrates the following performance profiles:

| Submission Mode | Tasks | Total Time | Avg Latency | Overhead Highlights |
|---|---|---|---|---|
| **Single Threaded** | 200 | 0.0028s | 0.01ms (14 μs) | Standard sequential submission |
| **Batch Submission** | 200 | 0.0017s | 0.01ms (8 μs) | **Lock-free batch optimization (~2x speedup)** |
| **Asyncio Non-blocking** | 200 | 0.0103s | 0.05ms (50 μs) | Parallel await without blocking Python event loop |
| **Multi-Threaded** | 40 | 0.0019s | 0.04ms (47 μs) | Safe concurrent channel enqueueing |

*Note: Batch submissions are processed with single-acquisition write locks on the internal task slab, avoiding locking contention overhead and maximizing core utilization.*

---

## Contributing

Contributions are welcome! If you'd like to improve Pyroxide or add support for additional features, feel free to open an issue or submit a pull request on GitHub.

## License

Pyroxide is licensed under any of:

* MIT License ([LICENSE-MIT](LICENSE-MIT))
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE))
* Coffeeware License ([LICENSE-COFFEE](LICENSE-COFFEE))

at your option.

