# r_bridge

> Call a persistent R interpreter from Python — with a Pythonic API, full numpy/pandas support, and zero ceremony.

```python
from r_bridge import RBridge

with RBridge() as r:
    r.x = [1, 2, 3, 4, 5]
    print(r.mean(r.x))   # 3.0
```

## Why r_bridge?

Python and R each have unique strengths. Rather than rewriting R code or shelling out
to `Rscript` for every call, r_bridge keeps a **single R process alive** for the
lifetime of your session. Calls are fast, state is shared, and the API feels natural
on both sides.

- 🚀 **No subprocess-per-call overhead** — R starts once, stays running
- 🔄 **Automatic type conversion** — Python lists, numpy arrays, pandas DataFrames and datetimes all round-trip transparently
- 🔒 **Thread-safe** — a single lock serialises concurrent calls
- 🛡️ **Robust** — a sentinel-prefixed protocol means stray `cat()` output never corrupts the stream

## How it works

r_bridge spawns `Rscript` once and communicates over **stdin/stdout** using
line-delimited JSON with a `__RBRIDGE__:` sentinel prefix on every protocol line.

```
🐍 Python ──► __RBRIDGE__:{"id":"…","op":"call_func","payload":{…}}
📊 R      ──► __RBRIDGE__:{"id":"…","status":"ok","payload":{…},"warnings":[]}
```

A dedicated daemon thread drains R's stderr continuously to prevent pipe-buffer
deadlocks. A single `threading.Lock` serialises concurrent Python calls.
