Metadata-Version: 2.4
Name: tranq
Version: 0.3.0
Summary: Calm error handling for Python
Author-email: RaptorVampire <mhman884@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/RaptorVampire/tranq
Project-URL: Bug Tracker, https://github.com/RaptorVampire/tranq/issues
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
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: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-asyncio; extra == "dev"
Requires-Dist: black; extra == "dev"
Requires-Dist: isort; extra == "dev"
Requires-Dist: rich; extra == "dev"
Provides-Extra: rich
Requires-Dist: rich; extra == "rich"
Dynamic: license-file

# 🌿 tranq

<p align="center">
  <b>Calm error handling for Python – decorator-based, zero boilerplate.</b><br><br>
  <a href="https://github.com/RaptorVampire/tranq">
    <img src="https://img.shields.io/badge/GitHub-RaptorVampire%2Ftranq-blue?logo=github&style=flat-square" alt="GitHub">
  </a>
  <a href="https://pypi.org/project/tranq/">
    <img src="https://img.shields.io/badge/PyPI-tranq-blue?style=flat-square" alt="PyPI">
  </a>
  <img src="https://img.shields.io/badge/python-3.9%2B-green?style=flat-square" alt="Python 3.9+">
  <a href="https://opensource.org/licenses/MIT">
    <img src="https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square" alt="License: MIT">
  </a>
</p>

---

## 🧘 Why tranq?

Writing repetitive `try/except` blocks clutters your code. **tranq** gives you **declarative error handling** with decorators, context managers, and smart retry strategies, so you can focus on business logic.

| Feature | Description |
|---|---|
| 🧘 **Tranquil** | Clean, readable, and maintainable. |
| 🔁 **Smart retries** | Exponential, linear, Fibonacci backoff, jitter, and max delay. |
| 🚦 **Circuit Breaker** | Prevent cascading failures (sync & async). |
| 🧪 **Conditional retry** | On specific exceptions **or** result values. |
| 📦 **Retry groups** | All-or-nothing execution for multiple functions. |
| 📊 **Built‑in metrics** | Monitor performance and error rates. |
| 📝 **Pluggable reporters** | Send errors to files (JSON), Sentry, Slack, or custom. |
| 🧩 **Context manager** | Use `with tranq.retry(...):` when decorators aren't ideal. |
| 🔧 **Stateful retry** | Persist attempt count across calls (thread/async safe). |
| 🎭 **Mock errors** | Test your error handling with ease. |
| 💉 **Dependency Injection** | Inject dependencies into decorated functions. |
| 🌐 **Global Policy** | Set defaults once, override per function. |

---

## 📦 Installation

```bash
pip install tranq
```

> Requires **Python 3.9** or later.

---

## ⚡ Quick Start

### Decorator (`@handle`)

```python
import tranq

@tranq.handle(on=ValueError, retry=3, delay=0.5, backoff=2.0)
def risky():
    ...
```

### Async (`@handle_async`)

```python
@tranq.handle_async(on=ConnectionError, retry=2, fallback=lambda: "offline")
async def fetch_data():
    ...
```

### Circuit Breaker

```python
cb = tranq.CircuitBreaker(failure_threshold=5, timeout=60)

@tranq.handle(circuit_breaker=cb)
def call_unstable_service():
    ...
```

### Context Manager

```python
with tranq.retry(on=ValueError, retry=2) as ctx:
    result = ctx.run(my_function, arg1, arg2)
```

### Retry Group (all-or-nothing)

```python
group = tranq.retry_group(step1, step2, step3, on=Exception, retry=1)
results = group.run()
```

---

## 🔍 Features in Depth

### 1. Retry with Backoff

Choose from **exponential**, **linear**, or **Fibonacci** backoff. Add **jitter** to avoid thundering herds.

```python
@tranq.handle(
    on=TimeoutError,
    retry=5,
    delay=0.1,
    backoff=2.0,
    backoff_strategy="exponential",  # "linear", "fibonacci", or custom callable
    max_delay=10.0,
    jitter=True,
)
def fetch():
    ...
```

### 2. Conditional Retry

- `retry_if` – retry only when the exception matches a condition.
- `retry_on_result` – retry if the result is unacceptable (e.g., `None`).

```python
@tranq.handle(
    on=requests.RequestException,
    retry_if=lambda e: e.response.status_code == 429,  # rate-limit
    retry=3,
)
def call_api():
    ...
```

### 3. Global Policy

Set defaults for your entire application once.

```python
tranq.set_global_policy(tranq.Policy(
    retry=3,
    delay=0.5,
    backoff=2.0,
    reraise=False,
))

# All @handle calls now inherit these defaults
@tranq.handle(on=ValueError)
def my_func():
    ...
```

### 4. Reporters (JSON file, Sentry, Slack)

```python
from tranq import FileReporter, SentryReporter, SlackReporter

reporters = [
    FileReporter("/var/log/tranq_errors.json"),  # Writes JSON lines
    SentryReporter(dsn="..."),
    SlackReporter(webhook_url="..."),
]

@tranq.handle(on=Exception, reporters=reporters)
def critical_task():
    ...
```

### 5. Metrics & Profiling

```python
@tranq.handle(metrics=True, metric_prefix="myapp")
def expensive_op():
    ...

from tranq import get_metrics, profile, get_profile

@profile
def heavy_computation():
    ...

print(get_metrics())
print(get_profile("heavy_computation"))
```

---

## 🚀 Advanced Example

Combining multiple features for a robust API call:

```python
cb = tranq.CircuitBreaker(failure_threshold=3, timeout=60)

@tranq.handle(
    on=requests.RequestException,
    retry=5,
    backoff_strategy="fibonacci",
    max_delay=30,
    jitter=True,
    retry_if=lambda e: e.response.status_code in (429, 503),
    circuit_breaker=cb,
    metrics=True,
    metric_prefix="api",
    reporters=[tranq.FileReporter("api_errors.json")],
    fallback=lambda: {"status": "fallback"},
)
def fetch_from_external_api():
    ...
```

---

## 📚 API Reference

- **Decorators:** `handle(...)`, `handle_async(...)`
- **Context Manager:** `retry(...)`
- **Retry Groups:** `retry_group(...)`, `async_retry_group(...)`
- **Circuit Breakers:** `CircuitBreaker(...)`, `AsyncCircuitBreaker(...)`
- **Policies:** `Policy`, `set_global_policy()`, `get_global_policy()`
- **Reporters:** `Reporter`, `FileReporter`, `SentryReporter`, `SlackReporter`
- **Utilities:** `get_metrics()`, `reset_metrics()`, `profile()`, `get_profile()`, `mock_errors()`
- **Exceptions:** `TranqError`, `RetryExhaustedError`, `CircuitBreakerError`, `ResultNotAcceptedError`, `RetryGroupError`

> 📖 **Full documentation, 20 runnable examples, and 120+ tests are available on [GitHub](https://github.com/RaptorVampire/tranq).**

---

## 📄 License

**MIT** © [RaptorVampire](https://github.com/RaptorVampire)

---

<p align="center">Happy error handling! 🧘</p>
