Polyester makes it possible to use R from Python by running R in a background process and communicating through a lightweight message protocol.
The goal is simplicity and reliability — especially on Windows — while keeping the design extensible to other languages in the future.
from polyester import RInterpreter
# Start an R interpreter
R = RInterpreter(path=r"C:\Program Files\R\R-4.5.2\bin\Rscript.exe")
# Access an R module (namespace)
rbase = R.module("base")
# Simple calculations
x = R.eval("sin(100)")
y = rbase.cos(100)
# Get results in python
print(R.get(x), R.get(y))
# Bring a dataframe from R to Python
iris_rdf = R.eval("iris") # RemoteObject
iris_df = R.get(iris_rdf, "pandas") # Python/pandas object
print(iris_df.head())Polyester revolves around a single concept: an interpreter.
An interpreter manages:
- A background R process
- A private remote environment
- Communication over JSON Lines
- Data exchange using Apache Arrow
An interpreter supports the following operations:
| Operation | Parameters | Returns | Description |
|---|---|---|---|
insert |
x: simple/dataframe |
RemoteObject |
Send Python data to R |
get |
x: Remote |
simple/dataframe |
Retrieve data from R |
R.objects.name |
name: str |
RemoteName (lazy) |
Reference a remote symbol |
R.objects.name = value |
name: str, value: simple/Remote |
– | Assign remotely |
eval |
code: str |
RemoteObject |
Evaluate R code |
exec |
code: str |
– | Execute R code (no return value) |
call |
f: Remote, *args, **kwargs |
RemoteObject |
Call a remote function |
-
RemoteObject A concrete object that exists in the remote R environment. Automatically cleaned up when the Python object is deleted.
-
RemoteName A lazy reference to a symbol or expression in R. It may or may not exist until evaluated.
Example:
R.objects.x = 10
result = R.get(R.objects.x) # 10DataFrames are transferred using Apache Arrow files for efficiency.
You can request a specific backend when retrieving:
df = R.get(iris_rdf, "pandas")If no df_backend is provided, polars will be used.
⚠ Do not print to stdout from R.
Polyester uses stdout for protocol communication.
Printing to stdout() inside R will corrupt the communication channel.
If you need logging inside R, use:
message("debug info")or write to stderr().
rpy2 is a mature and powerful solution.
However:
- rpy2 is currently difficult to use on Windows in many environments.
- Polyester runs R as a subprocess and avoids tight binary coupling.
- The architecture is language-agnostic and may support additional languages (e.g., Julia) in the future.
If rpy2 becomes reliably usable in all target environments, Polyester may optionally integrate with it.
- ✅ Windows support
- ✅ Minimal external dependencies
- ✅ Simple, explicit API
- ✅ Subprocess isolation
- 🔄 Possible future multi-language support
Performance is important, but clarity and robustness are higher priorities at this stage.
- R is started as a background subprocess.
- Communication happens over JSON Lines.
- DataFrames are exchanged via Apache Arrow files.
- Remote objects are reference-tracked and cleaned up automatically.
- Start R subprocess in background
- JSON Lines protocol for communication
- DataFrame transfer using Apache Arrow
- Remote object lifecycle management