remotepy allows Python functions to be called remotely from multiple languages including JavaScript, CSharp and Python
Project description
remotepy
remotepy is a powerful RPC (Remote Procedure Call) framework that lets you write Python functions and call them remotely from JavaScript, C#, or Python over WebSockets.
Table of contents
- Features
- Installation
- Quick-start (5 minutes, no database)
- Automated setup wizard
- Session management — choosing a backend
- MySQL setup guide
- Advanced features
- Client examples
- Configuration reference
- Security features
- API reference
- Best practices
- Troubleshooting
Features
- Multi-language support — call Python from JavaScript (browser), C#, or Python
- Decorator API — expose functions with
@remotepy_func/@remotepy_class - Session management — built-in, works with or without MySQL
- In-memory sessions — zero-dependency mode, no database needed
- MySQL sessions — persistent sessions backed by MySQL
- Pluggable storage — bring your own session backend (Redis, SQLite, etc.)
- Async / streaming —
async def, generators, async generators - CPU-bound work —
@cpu_boundruns heavy tasks in aProcessPoolExecutor - SSL/TLS —
run_ssl()for securewss://connections - Pub/Sub —
PubSubBroadcastServerFactoryfor real-time broadcasting - Security — rate limiting, input validation, password sanitization in logs
- Setup wizard —
remotepy-setupgenerates a working server in seconds
Installation
Without MySQL (in-memory sessions)
pip install remotepy
With MySQL support
pip install remotepy[mysql]
Quick-start — no database
You can run a full session-managed server with zero external dependencies:
# server.py
from remotepy import *
from remotepy.session.SimpleSessionServer import SimpleSessionServer
@remotepy_class
class MyServer(WebSocketRPCServerProtocol, SimpleSessionServer):
def __init__(self):
super().__init__()
# Seed users programmatically — or call self.load_users_from_env()
self.add_user("alice", "password123", "alice@example.com")
self.add_user("bob", "s3cret", "bob@example.com")
def onConnect(self, request):
print("Client connecting:", request.peer)
@remotepy_method
def hello(self, name):
"""Say hello."""
return f"Hello, {name}!"
def onClose(self, wasClean, code, reason):
print("Client closed:", code, reason)
if __name__ == "__main__":
import logging
logging.basicConfig(level=logging.INFO)
server = MyServer()
server.run("0.0.0.0", 8082)
Run it:
python server.py
Automated setup wizard
The fastest way to get started:
pip install remotepy
remotepy-setup
The wizard:
- Asks whether you want in-memory or MySQL session storage
- Generates a ready-to-run
server.py - Generates an
.env.examplewith all settings documented - (MySQL only) generates
schema.sqlto create all required tables - (MySQL only) optionally generates
docker-compose.ymlfor local dev MySQL
Session management
remotepy provides three ways to handle sessions. Pick what fits your project:
Option 1 — In-memory, no database (simplest)
Use SimpleSessionServer. Zero setup, zero dependencies beyond remotepy itself.
Users and sessions are stored in RAM — state is lost on restart.
Best for: development, prototyping, small apps, microservices.
from remotepy import *
from remotepy.session.SimpleSessionServer import SimpleSessionServer
@remotepy_class
class MyServer(WebSocketRPCServerProtocol, SimpleSessionServer):
def __init__(self):
super().__init__()
self.add_user("admin", "secret", "admin@example.com")
Load users from an environment variable instead of hardcoding them:
export REMOTEPY_USERS="admin:secret:admin@example.com,guest:guest:guest@example.com"
def __init__(self):
super().__init__()
self.load_users_from_env()
Option 2 — Custom auth, in-memory sessions (flexible)
Inherit from SessionServer with db_config=None and override _authenticate_user:
from remotepy import *
@remotepy_class
class MyServer(WebSocketRPCServerProtocol, SessionServer):
def __init__(self):
super().__init__(None) # None → InMemorySessionStorage
def _authenticate_user(self, username: str, password: str) -> bool:
# Your own auth logic — check a file, API, LDAP, etc.
return username == "admin" and password == "secret"
@remotepy_method
def hello(self, name):
return f"Hello, {name}!"
Option 3 — MySQL (production)
Full persistent sessions + user management:
from remotepy import *
@remotepy_class
class MyServer(WebSocketRPCServerProtocol, SessionServer):
def __init__(self):
super().__init__(db_config={
"host": "localhost",
"user": "remotepy",
"password": "changeme",
"database": "myschema",
}, use_schema_name="myschema")
self.set_email_config() # reads REMOTEPY_EMAIL_* env vars
Option 4 — Custom storage backend
Implement SessionStorageBackend and pass it directly:
from remotepy import *
from remotepy.session.storage.base import SessionStorageBackend
class RedisSessionStorage(SessionStorageBackend):
# ... implement all abstract methods ...
@remotepy_class
class MyServer(WebSocketRPCServerProtocol, SessionServer):
def __init__(self):
super().__init__(storage_backend=RedisSessionStorage("redis://localhost"))
MySQL setup guide
Step 1 — Install with MySQL extra
pip install remotepy[mysql]
Step 2 — Create the schema
Use the automated wizard (recommended):
remotepy-setup
# Choose option 2 (MySQL)
# This generates schema.sql automatically
Or run the schema manually:
mysql -u root -p < schema.sql
The schema creates these tables in your chosen database:
| Table | Purpose |
|---|---|
session_state |
One row per active WebSocket connection |
session_variable_blob |
Key-value store for session variables |
new_session_ids |
One-time session tokens |
user |
User accounts (username, bcrypt password, email, address) |
address |
Postal addresses linked to users |
Step 3 — Local dev with Docker
The setup wizard generates a docker-compose.yml. Start MySQL with:
docker compose up -d
Credentials default to: user remotepy, password changeme, port 3306.
Step 4 — Configure via .env
Copy .env.example to .env and fill in your values:
DB_HOST=localhost
DB_USER=remotepy
DB_PASSWORD=changeme
DB_NAME=myschema
REMOTEPY_EMAIL_USER=your@email.com
REMOTEPY_EMAIL_PASSWORD=app-password
REMOTEPY_EMAIL_SERVER=smtp.gmail.com
REMOTEPY_EMAIL_PORT=587
REMOTEPY_RESET_PASSWORD_EMAIL=noreply@yourdomain.com
REMOTEPY_DOMAIN_NAME=https://www.yourdomain.com
Advanced features
Async functions
@remotepy_class
class AsyncServer(WebSocketRPCServerProtocol):
@remotepy_func
async def fetch_data(self, url):
import aiohttp
async with aiohttp.ClientSession() as s:
async with s.get(url) as r:
return await r.json()
Streaming with generators
@remotepy_func
def stream_rows(self, count):
for i in range(count):
yield {"index": i, "data": f"item_{i}"}
@remotepy_func
async def stream_async(self, count):
for i in range(count):
await asyncio.sleep(0.05)
yield {"index": i, "data": f"item_{i}"}
CPU-bound work
from remotepy import *
import numpy as np
@remotepy_class
class MLServer(WebSocketRPCServerProtocol):
@remotepy_method
@cpu_bound # runs in ProcessPoolExecutor — bypasses the GIL
def run_inference(self, data):
arr = np.array(data)
return heavy_model(arr).tolist()
@remotepy_method # runs in ThreadPoolExecutor (default)
def get_status(self):
return {"status": "ok"}
Decorator order matters:
@cpu_boundmust be below@remotepy_method/@remotepy_func.
| Your function does… | Use |
|---|---|
| Database queries, HTTP calls, file I/O | async def with await |
| Lightweight sync logic | def (thread pool) |
| numpy, pandas, ML inference, image processing | def + @cpu_bound |
| LLM streaming | async def with yield |
SSL/TLS
server = MyServer()
server.run_ssl(
"0.0.0.0", 8443,
"/etc/ssl/private/server.key",
"/etc/ssl/certs/fullchain.pem",
)
Protected functions
from remotepy.websocket.remotepy import remotepy_login_required, remotepy_permitted_to
@remotepy_class
class ProtectedServer(WebSocketRPCServerProtocol, SessionServer):
@remotepy_func
@remotepy_login_required
def sensitive_data(self):
return {"secret": "data"}
@remotepy_func
@remotepy_permitted_to("admin")
def admin_action(self):
return {"ok": True}
Pub/Sub broadcasting
from remotepy import PubSubBroadcastServerFactory
server = MyServer()
server.run("0.0.0.0", 8082, ServerFactory=PubSubBroadcastServerFactory)
Client examples
JavaScript
<script src="js/remotepy.1.0.0.min.js"></script>
<script>
var RemotePy = new RemotePyClient();
window.onload = function() {
RemotePy.serverName = 'ws://localhost:8082';
RemotePy.start();
};
RemotePy.onopen = function() {
RemotePy.MyServer.hello("World", function(result) {
console.log(result); // "Hello, World!"
});
};
</script>
Python
pip install remotepy_client
from remotepy_client.remotepy_rpc_client import RemotePyRPCClientSync
client = RemotePyRPCClientSync("ws://localhost:8082", verbose=False)
client.buildService('')
MyServer = client.getService('MyServer')("ws://localhost:8082", False)
result = MyServer.hello(name="Python", callback=None)
print(result) # "Hello, Python!"
client.thread().join()
Configuration reference
Environment variables
| Variable | Default | Description |
|---|---|---|
REMOTEPY_EMAIL_USER |
— | SMTP user for password-reset emails |
REMOTEPY_EMAIL_PASSWORD |
— | SMTP password |
REMOTEPY_EMAIL_SERVER |
smtp-relay.sendinblue.com |
SMTP server |
REMOTEPY_EMAIL_PORT |
587 |
SMTP port |
REMOTEPY_RESET_PASSWORD_EMAIL |
info@kubloy.com |
From address for reset emails |
REMOTEPY_DOMAIN_NAME |
http://www.alpharithmic.com |
Base URL for reset links |
REMOTEPY_USERS |
— | user:pass:email,... for SimpleSessionServer |
Rate limiting
Default: 100 calls per function per 60 seconds. Customise:
server._rate_limit_window = 60 # seconds
server._rate_limit_max_calls = 200
Security features
- Rate limiting — per-function call-rate cap (default 100/min)
- Input validation — function names and args are validated before dispatch
- Password sanitization — passwords are redacted from all log messages and errors
- Bcrypt hashing — all user passwords are stored as bcrypt hashes
- Single-use session tokens —
new_session_idstokens are consumed on first use - SQL injection protection — schema names are sanitized; all queries use parameterized args
API reference
Decorators
| Decorator | Description |
|---|---|
@remotepy_class |
Makes a class a remotepy server |
@remotepy_func |
Exposes a function for remote calls |
@remotepy_method |
Same as @remotepy_func, used with SessionServer |
@cpu_bound |
Runs in ProcessPoolExecutor (stacks below @remotepy_method) |
@remotepy_login_required |
Requires authenticated session |
@remotepy_permitted_to(action) |
Requires specific permission |
Server methods
| Method | Description |
|---|---|
server.run(ip, port) |
Start plain WebSocket server |
server.run_ssl(ip, port, key, cert) |
Start TLS WebSocket server |
SessionServer RPC methods (all storage backends)
| Method | Description |
|---|---|
getNewSessionId() |
Generate a one-time session token |
startSessionIfNotStarted(sessionid) |
Start session tracking |
isLoggedIn(sessionid) |
Check auth status |
validateLogin(sessionid, username, password, remember, currentUrl, afterLoginUrl) |
Authenticate |
logOut() |
De-authenticate current session |
getSessionId() |
Return current session ID |
SessionServer RPC methods (MySQL or SimpleSessionServer)
| Method | Description |
|---|---|
registerLogin(sessionid, username, password, first, middle, last, email, street, city, country) |
Register new user |
registerLoginShort(sessionid, username, password, email) |
Register (email + username only) |
checkIfUsernameExists(username) |
Check if username/email is taken |
forgotPassword(sessionid, email) |
Send password-reset email |
resetPassword(sessionid, code, new_password, repeat_password) |
Apply reset |
getUserProfile(sessionid) |
Get logged-in user's profile |
updateUserProfile(sessionid, ...) |
Update profile |
SimpleSessionServer extra methods (non-RPC)
| Method | Description |
|---|---|
add_user(username, plain_password, email, ...) |
Add a user before run() |
load_users_from_env() |
Load users from REMOTEPY_USERS env var |
Storage backends
| Class | Import | Notes |
|---|---|---|
InMemorySessionStorage |
remotepy.session.storage.memory |
No dependencies, RAM-only |
MySQLSessionStorage |
remotepy.session.storage.mysql |
Requires remotepy[mysql] |
SessionStorageBackend (ABC) |
remotepy.session.storage.base |
Base for custom backends |
Best practices
- Choose the right session backend early — in-memory for dev, MySQL for prod
- Use
SimpleSessionServerfor apps that don't need a database at all - Use SSL in production — always
run_ssl()in production environments - Store config in
.env— never hardcode passwords in source code - Use
async deffor I/O anddef + @cpu_boundfor heavy computation - Adjust rate limits to match your workload
- Override
_authenticate_userto integrate with existing auth systems
Troubleshooting
"MySQLSessionStorage requires the 'mysqleasy' package"
Install the MySQL extra:
pip install remotepy[mysql]
"registerLogin requires MySQL storage"
You are calling a user-management method on a server with in-memory session storage. Either:
- Switch to
SimpleSessionServerfor full in-memory user management, or - Pass a
db_configto use MySQL storage
Rate limit errors
server._rate_limit_max_calls = 500 # increase cap
Connection issues
- Confirm the server is running:
python server.py - Check firewall rules for the port
- WebSocket URL format:
ws://host:port(plain) orwss://host:port(TLS)
Documentation
For more information, visit https://www.remotepy.com
License
Proprietary software. All rights reserved. See the LICENSE file for details.
Author
Faraz Farukh Tamboli — faraz.tamboli@gmail.com
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 Distributions
Built Distributions
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 remotepy-0.2.0-cp312-cp312-win_amd64.whl.
File metadata
- Download URL: remotepy-0.2.0-cp312-cp312-win_amd64.whl
- Upload date:
- Size: 820.1 kB
- Tags: CPython 3.12, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
32feddc6898825018f101bfea9114f2677433ab46574f9fa19c0b560d2f0bf28
|
|
| MD5 |
e6f63011ce5a6ed7529fcbaf24a72ec2
|
|
| BLAKE2b-256 |
48cb617bc081b2910be8460052381674fdaf1b18e3a81a12cc7a83f70dc75389
|
File details
Details for the file remotepy-0.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.
File metadata
- Download URL: remotepy-0.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
- Upload date:
- Size: 6.5 MB
- Tags: CPython 3.12, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b4b7bf614375ea1a50fd3ee94b9bacfcf5fceca3d103b0378239f3cb9257a3b2
|
|
| MD5 |
c388456ab2dbd4d2a377c2397657d67c
|
|
| BLAKE2b-256 |
ebe0fcd1c20c6ba0f7d3acba6f2d61de48d2b8c1d83d5acf5c7ba8566064b6ff
|
File details
Details for the file remotepy-0.2.0-cp311-cp311-win_amd64.whl.
File metadata
- Download URL: remotepy-0.2.0-cp311-cp311-win_amd64.whl
- Upload date:
- Size: 840.8 kB
- Tags: CPython 3.11, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fc3d884103e4ee6d34ddb2f5495f678b6c040130177062adaf847c325bfbc93e
|
|
| MD5 |
70991a21e80b116ad4facf419f336bc8
|
|
| BLAKE2b-256 |
9aa6f51eab8ed9a173762d9bf04aac7ec1dbf8754ea320e9bce24e04bb500a3a
|
File details
Details for the file remotepy-0.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.
File metadata
- Download URL: remotepy-0.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
- Upload date:
- Size: 6.5 MB
- Tags: CPython 3.11, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
54c59cb4fb3ae1598462cadbd2baa66ecd2a96d3e75e7e7082f9733c8590cbaf
|
|
| MD5 |
eb27aad4205f8e87547509af6545f6bf
|
|
| BLAKE2b-256 |
1dbbf91239be13f447d1bc14112897cee97735a874edbf12652f742f26d9a1db
|
File details
Details for the file remotepy-0.2.0-cp310-cp310-win_amd64.whl.
File metadata
- Download URL: remotepy-0.2.0-cp310-cp310-win_amd64.whl
- Upload date:
- Size: 838.0 kB
- Tags: CPython 3.10, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1ee9531aae5532ad0ffad050a81cdd630e3068b790c0e5da6db36667b3fe32fe
|
|
| MD5 |
92b69e2388a78208560fe4363d044e3d
|
|
| BLAKE2b-256 |
fbe338534a19858be425e3e6c9d8781f7ad0be1898028aaa491ec5d6eb44c2c5
|
File details
Details for the file remotepy-0.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.
File metadata
- Download URL: remotepy-0.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
- Upload date:
- Size: 6.1 MB
- Tags: CPython 3.10, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60e37d8780333b9e728cfa21d1ef84c3f38ba79ecd561cc9c90725c77fa066a9
|
|
| MD5 |
be13db86e7a30e19524a66e92cad2307
|
|
| BLAKE2b-256 |
d32ec3557db246473493be7129ff08bc4931b92675af70185c6d0c67448f0c2f
|
File details
Details for the file remotepy-0.2.0-cp39-cp39-win_amd64.whl.
File metadata
- Download URL: remotepy-0.2.0-cp39-cp39-win_amd64.whl
- Upload date:
- Size: 830.1 kB
- Tags: CPython 3.9, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
16459ac7a8c59c78553627d6df357e27dae1d7ea11845f170b4fc182ecb974e6
|
|
| MD5 |
af3dd643e51074039542dfa78723d055
|
|
| BLAKE2b-256 |
fafa92b8d6b0867c4c0b80a65a9470298a7c8d28af76afcf9d79db5cda5a5e9e
|
File details
Details for the file remotepy-0.2.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.
File metadata
- Download URL: remotepy-0.2.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
- Upload date:
- Size: 6.1 MB
- Tags: CPython 3.9, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e652c7ef1a45670a47e24296a6eee028d5a0eea6e135650a88da1af6091f45d3
|
|
| MD5 |
83c1d6a0d07b1e6e3ca0d42c7a98b127
|
|
| BLAKE2b-256 |
8f04f05b9ee9c59db8db8d1e159a978270082da0d06236f66a03484d1c5fc877
|