Simple Remote Python Call - WebSocket-based RPC with transparent proxies
Project description
SimpleRPyC
SimpleRPyC (pronounced simple-are-pie-see), short for Simple Remote Python Call, is a WebSocket-based RPC library for Python. Inspired by RPyC, it uses transparent proxy objects to let you interact with remote Python objects as if they were local—leveraging Python's dynamic nature to bridge processes and machines seamlessly. Additionally, SimpleRPyC provides convenient module patching that allows you to use existing code with minimal changes.
Unlike traditional RPC libraries that use custom protocols, SimpleRPyC is built on WebSocket (standard, debuggable protocol) and msgpack (efficient binary serialization). This choice enables seamless numpy array transfers between different numpy versions (1.x ↔ 2.x), which is critical for running modern ML code alongside legacy environments.
Why SimpleRPyC?
Problem: Running code with incompatible dependencies in the same process.
For example, robotics simulation environments like SimplerEnv (a robot manipulation benchmark) require numpy<2.0, but modern RL policies require numpy>=2.0. You can't install both in the same environment.
Solution: Run them in separate processes and communicate via RPC.
Why Not Alternatives?
| Feature | SimpleRPyC | RPyC | Pyro5 | zero |
|---|---|---|---|---|
| Transport | WebSocket | TCP + custom protocol | TCP + custom protocol | ZMQ |
| Serialization | msgpack | brine (custom) | serpent | msgspec |
| NumPy support | ✅ Built-in | ✅ Yes | ❌ No | ❌ No |
| NumPy 1.x ↔ 2.x | ✅ Yes | ❌ No | ❌ No | ❌ No |
| Multiple args | ✅ f(a,b,c) |
✅ Yes | ✅ Yes | ❌ No |
| Module patching | ✅ Yes | ✅ Yes | ❌ No | ❌ No |
| Proxy objects | ✅ Yes | ✅ Yes | ✅ Yes | ❌ No |
RPyC: Uses TCP with custom framing/channel layer and "brine" serialization. Supports numpy arrays but breaks when transferring between numpy 1.x and 2.x due to pickle format changes. Custom serialization format makes it hard to add additional features.
Pyro5: Uses TCP with custom wire protocol and serpent serializer. No built-in numpy support - requires manual workarounds that don't handle numpy 1.x ↔ 2.x transfers.
zero: Promising msgspec+ZMQ stack, but doesn't support multiple arguments (remote_fn(a, b, c) fails) or proxy objects.
Features
- WebSocket Transport: Standard protocol, easy to debug, works through firewalls
- msgpack + NumPy: Efficient binary serialization with cross-version numpy compatibility via msgpack-numpy
- Transparent Proxies: Remote objects behave like local objects
- Explicit Materialization: Control when to transfer data from server (
materialize()) - Flexible API: Module patching (
import os) or explicit access (conn.modules.os) - Function Teleportation: Send local functions to remote server with
conn.teleport()using dill - Remote Execution: Run arbitrary code on server with
conn.eval()andconn.execute()
⚠️ Security Warning
SimpleRPyC uses unencrypted WebSocket connections (ws://). The authentication token and all data are transmitted in plaintext, making them vulnerable to network sniffing and man-in-the-middle attacks.
Use SimpleRPyC only in trusted, private networks (e.g., localhost, private LANs, VPNs). DO NOT expose SimpleRPyC servers to the public internet or use it over untrusted networks.
For production use over untrusted networks, consider:
- Running SimpleRPyC only on
localhostand using SSH tunneling for remote access - Deploying within a private network or VPN
- Adding TLS/SSL encryption layer (e.g., reverse proxy with nginx/caddy)
Installation
pip install simplerpyc
Quick Start
1. Start the Server
python -m simplerpyc.server
The server will print a token. Set it as environment variable:
export SIMPLERPYC_TOKEN='<TOKEN_FROM_SERVER>'
2. Use the Client
SimpleRPyC provides two API styles - choose what fits your use case:
Style 1: Module Patching (Import-like)
import simplerpyc
from simplerpyc import materialize
# Connect to server
conn = simplerpyc.connect("localhost", 8000)
# Patch modules to use remote versions
simplerpyc.patch_module(conn, "os")
simplerpyc.patch_module(conn, "numpy")
# Import and use as if they were local
import os
import numpy as np
cwd = materialize(os.getcwd())
arr = materialize(np.array([1, 2, 3]))
conn.disconnect()
Style 2: Explicit Remote Access
from simplerpyc import connect, materialize
# Connect to server
conn = connect("localhost", 8000)
# Access remote modules explicitly
remote_os = conn.modules.os
remote_np = conn.modules.numpy
# Everything returns proxies by default
cwd = materialize(remote_os.getcwd())
arr = materialize(remote_np.array([1, 2, 3]))
conn.disconnect()
Examples
See example_client.py for comprehensive examples including:
- Understanding proxies and materialization
- NumPy array operations (1D and 2D)
- Remote code execution (
evalandexecute) - Nested data structures with NumPy arrays
Core Concepts
Transparent Proxies
All remote operations return RPCProxy objects that behave like the actual objects:
# Remote function call returns proxy
arr_proxy = remote_np.array([1, 2, 3]) # RPCProxy, not actual array
# Proxy supports attribute access, method calls, indexing
mean_proxy = arr_proxy.mean() # RPCProxy
item_proxy = arr_proxy[0] # RPCProxy
# Chain operations without network round-trips
result_proxy = remote_np.array([1, 2, 3]).reshape(3, 1).mean() # Still proxy
Explicit Materialization
Use materialize() to transfer data from server to client:
# Materialize when you need actual values
arr = materialize(arr_proxy) # numpy array
mean = materialize(mean_proxy) # float
item = materialize(item_proxy) # int
# Materialize complex structures
env = simpler_env.make('...') # RPCProxy
obs, reward, done, truncated, info = materialize(env.step(action)) # actual values
Design principle: Minimize network transfers by keeping operations on server until results are needed.
API Reference
Connection
from simplerpyc import connect
conn = connect(host="localhost", port=8000, token=None)
# Token auto-detected from SIMPLERPYC_TOKEN env var if not provided
Basic API
Module Patching:
import simplerpyc
simplerpyc.patch_module(conn, "os") # Patch sys.modules
import os # Now uses remote version
Namespace Access:
remote_os = conn.modules.os # Access remote module
remote_len = conn.builtins.len # Access remote builtin
Utility Functions:
from simplerpyc import materialize, is_proxy
value = materialize(proxy) # Convert proxy to actual value
is_remote = is_proxy(obj) # Check if object is a proxy
Advanced Features
Remote Code Execution:
# Evaluate expression
result = materialize(conn.eval("2 + 3")) # 5
# Execute code (no return value)
conn.execute("x = 42")
x = materialize(conn.eval("x")) # 42
Function Teleportation:
# Send local function to remote
def square(x):
return x ** 2
remote_square = conn.teleport(square)
result = materialize(remote_square(5)) # 25
Connection Management:
conn.disconnect() # Close connection
Server
python -m simplerpyc.server [--host HOST] [--port PORT]
The server will print a token. Set it as environment variable:
export SIMPLERPYC_TOKEN='<TOKEN_FROM_SERVER>'
Design Decisions
Why WebSocket?
- Standard protocol: Works with existing infrastructure (proxies, load balancers)
- Bidirectional: Server can push data to client (future feature)
- Debuggable: Easy to inspect with browser dev tools or Wireshark
- Firewall-friendly: Uses standard HTTP ports
Why msgpack + msgpack-numpy?
- Efficient: Binary format, smaller than JSON
- NumPy support: Native serialization for numpy arrays
- Cross-version: Works between numpy 1.x and 2.x (unlike pickle)
- Language-agnostic: Can implement clients in other languages
Why Proxy-based Design?
Inspired by RPyC's elegant proxy pattern:
- Transparent: Remote objects behave like local objects
- Efficient: Chain operations on server without network round-trips
- Explicit control: User decides when to transfer data with
materialize()
Unlike RPyC, we use WebSocket transport and msgpack serialization for better numpy compatibility.
Architecture
simplerpyc/
├── client/
│ ├── connection.py # WebSocket connection management
│ ├── proxy.py # Minimal RPCProxy implementation
│ └── patcher.py # sys.modules patching
├── server/
│ ├── server.py # WebSocket server
│ └── executor.py # Per-client execution context
└── common/
└── serialization.py # msgpack with numpy support
Requirements
Python 3.10+ is required. Python 3.9 is not supported due to:
- WebSocket concurrency issues with nested asyncio event loops in synchronous RPC context
nest_asynciocompatibility issues causingConcurrencyErrorin websockets
Dependencies (automatically installed with pip install simplerpyc):
websockets>=12.0- WebSocket transport layer (v12.0+ for improved async support)msgpack>=1.0.0- Efficient binary serializationmsgpack-numpy>=0.4.8- NumPy array serialization with cross-version compatibility (1.x ↔ 2.x)dill>=0.3.0- Function serialization forconn.teleport()
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file simplerpyc-0.0.1a3.tar.gz.
File metadata
- Download URL: simplerpyc-0.0.1a3.tar.gz
- Upload date:
- Size: 27.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.9.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cf969401006163514f36bbea093ad78882556f374209f5c793cfddb8aa3de5ab
|
|
| MD5 |
993855447cbd865852c407ece524b488
|
|
| BLAKE2b-256 |
b9a8b199abddf3af248b15193414f9bf8d4d7212bfd6164208f4a1c390a8168d
|
File details
Details for the file simplerpyc-0.0.1a3-py3-none-any.whl.
File metadata
- Download URL: simplerpyc-0.0.1a3-py3-none-any.whl
- Upload date:
- Size: 16.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.9.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fb4edb342dee38f201319d33bdbd916394fcccfb3f1f42af2d800b5dc3336b67
|
|
| MD5 |
fb02210f501072a4816717b8894dd386
|
|
| BLAKE2b-256 |
2052dc5e88f40bba88c3f79269938128bb3f984651b5171c3a8c05bf39306eff
|