Metadata-Version: 2.4
Name: remotepy
Version: 0.0.3
Summary: remotepy allows Python functions to be called remotely from multiple languages including JavaScript, CSharp and Python
Home-page: https://www.remotepy.com
Author: Faraz Farukh Tamboli
Author-email: faraz.tamboli@gmail.com
License: Proprietary
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: License :: Other/Proprietary License
Classifier: Operating System :: OS Independent
Description-Content-Type: text/markdown
Requires-Dist: bcrypt
Requires-Dist: service_identity
Requires-Dist: twisted
Requires-Dist: cryptography
Requires-Dist: autobahn[asyncio,encryption,serialization,serializers]
Requires-Dist: shortuuid
Requires-Dist: numpy
Requires-Dist: mysqleasy
Requires-Dist: pyOpenSSL
Requires-Dist: zmq
Requires-Dist: msgpack
Requires-Dist: python-dotenv
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: license
Dynamic: requires-dist
Dynamic: summary

# Remote Py

Remote Py is a powerful RPC (Remote Procedure Call) framework that allows you to write Python functions and expose them as remote services. It provides client interfaces in multiple languages including JavaScript, C#, and Python, enabling seamless cross-platform communication over WebSockets.

## Features

- **Multi-language Support**: Call Python functions from JavaScript (browser), C#, and Python clients
- **Bidirectional Communication**: Full-duplex communication using WebSockets for real-time interaction
- **Easy-to-Use API**: Simple decorator-based API (`@remotepy_func`, `@remotepy_class`) for exposing functions
- **Session Management**: Built-in session handling and authentication support with `SessionServer`
- **Real-time Messaging**: Pub/Sub broadcasting capabilities for event-driven architectures
- **Async Support**: Full support for async/await Python functions and generators
- **Streaming**: Support for generators and async generators for streaming large datasets
- **Security Features**: 
  - Rate limiting (100 calls per function per minute by default)
  - Input validation
  - Password sanitization in logs
  - SQL injection protection
- **SSL/TLS Support**: Secure WebSocket connections with SSL/TLS
- **Binary Protocol**: Efficient message serialization using MessagePack
- **Error Handling**: Comprehensive error handling with sanitized error messages

## Installation

### Production PyPI

```bash
pip install remotepy
```

### Test PyPI

If installing from test.pypi.org, you must also specify the regular PyPI as an extra index because test PyPI doesn't have all dependencies:

```bash
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ remotepy
```

## Requirements

All required dependencies are automatically installed with the package. The main dependencies include:

- `numpy` - Numerical operations
- `service_identity` - Service identity verification
- `twisted` - Network framework
- `autobahn` - WebSocket implementation
- `cryptography` - Cryptographic functions
- `zmq` - ZeroMQ messaging
- `msgpack` - Binary serialization
- `python-dotenv` - Environment variable management

## Quick Start

### Basic Server Example

Create a server file (e.g., `server.py`):

```python
from remotepy import *

@remotepy_class
class MyPythonServer(WebSocketRPCServerProtocol, SessionServer):
    def onConnect(self, request):
        print("Client connecting: {}".format(request.peer))
    
    @remotepy_func
    def add(self, a, b):
        """
        Adds two numbers together.
        
        :param a: First number
        :param b: Second number
        :return: Sum of a and b
        """
        return a + b
    
    @remotepy_func
    def greet(self, name):
        """Greets a user by name."""
        return f"Hello, {name}!"
    
    def onClose(self, wasClean, code, reason):
        print("Client closing connection: ", code, reason)

# Initialize server with database configuration (optional)
db_config = {
    'host': 'localhost',
    'user': 'your_user',
    'password': 'your_password',
    'database': 'your_database'
}

server = MyPythonServer(db_config)
server.run("127.0.0.1", 8082)
```

### Server with SSL/TLS

For secure connections:

```python
server = MyPythonServer(db_config)
server.run_ssl(
    "0.0.0.0", 
    8443, 
    "path/to/private.pem", 
    "path/to/fullchain.pem"
)
```

## Advanced Features

### Async Functions

Remote Py supports both synchronous and asynchronous Python functions:

```python
@remotepy_class
class MyAsyncServer(WebSocketRPCServerProtocol):
    @remotepy_func
    async def async_process_data(self, data):
        """Process data asynchronously."""
        # Simulate async operation
        await asyncio.sleep(1)
        return {"processed": data, "status": "complete"}
    
    @remotepy_func
    def sync_process_data(self, data):
        """Process data synchronously."""
        return {"processed": data, "status": "complete"}
```

### Streaming with Generators

Stream large datasets efficiently using generators:

```python
@remotepy_class
class DataServer(WebSocketRPCServerProtocol):
    @remotepy_func
    def stream_data(self, count):
        """Stream data items one by one."""
        for i in range(count):
            yield {"index": i, "data": f"item_{i}"}
    
    @remotepy_func
    async def async_stream_data(self, count):
        """Stream data asynchronously."""
        for i in range(count):
            await asyncio.sleep(0.1)
            yield {"index": i, "data": f"item_{i}"}
```

### Session Management

Use `SessionServer` for user authentication and session handling:

```python
from remotepy import *

@remotepy_class
class SecureServer(WebSocketRPCServerProtocol, SessionServer):
    def __init__(self, db_config):
        super().__init__(db_config)
        # Configure email settings for password reset
        self.set_email_config(
            user="your-email@gmail.com",
            server="smtp.gmail.com",
            port=587
        )
    
    @remotepy_method
    def validateLogin(self, sessionid, username, password, remember, currentUrl, afterLoginUrl):
        """Validate user login credentials."""
        # Implementation handled by SessionServer
        pass
    
    @remotepy_method
    def getSessionId(self):
        """Get current session ID."""
        return self.SessionState.getSessionId()
```

### Protected Functions

Use decorators to protect functions that require authentication:

```python
from remotepy.websocket.remotepy import remotepy_login_required, remotepy_permitted_to

@remotepy_class
class ProtectedServer(WebSocketRPCServerProtocol, SessionServer):
    @remotepy_func
    @remotepy_login_required
    def get_user_data(self):
        """Requires user to be logged in."""
        return {"user": "data"}
    
    @remotepy_func
    @remotepy_permitted_to("admin")
    def admin_function(self):
        """Requires admin permission."""
        return {"admin": "data"}
```

### Pub/Sub Broadcasting

Use `PubSubBroadcastServerFactory` for real-time event broadcasting:

```python
from remotepy import PubSubBroadcastServerFactory

@remotepy_class
class BroadcastServer(WebSocketRPCServerProtocol):
    def __init__(self):
        super().__init__()
        # Your initialization code
    
    @remotepy_func
    def broadcast_message(self, message):
        """Broadcast a message to all connected clients."""
        # Implementation for broadcasting
        pass

# Use PubSubBroadcastServerFactory for pub/sub functionality
server = BroadcastServer()
server.run("127.0.0.1", 8082, ServerFactory=PubSubBroadcastServerFactory)
```

## Configuration

### Environment Variables

Remote Py supports configuration via environment variables. Create a `.env` file:

```env
REMOTEPY_EMAIL_USER=your-email@gmail.com
REMOTEPY_EMAIL_PASSWORD=your-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
```

If environment variables are not set, Remote Py will use default values for backward compatibility.

### Rate Limiting

Rate limiting is enabled by default (100 calls per function per 60 seconds). You can customize this:

```python
# In your server class __init__ or after instantiation
server._rate_limit_window = 60  # Time window in seconds
server._rate_limit_max_calls = 200  # Max calls per window
```

## Client Examples

### JavaScript Client

**Installation:**

The JavaScript client is available as a separate package. For web applications, include the client library in your HTML:

```html
<script language="JavaScript" src="js/remotepy.1.0.0.min.js"></script>
```

**Usage Example:**

```html
<!DOCTYPE html>
<html>
<head>
    <title>RemotePy Client</title>
</head>
<body>
    <script language="JavaScript" src="js/remotepy.1.0.0.min.js"></script>
    <script language="JavaScript">
        var RemotePy = new RemotePyClient();
        
        window.onload = function() {
            RemotePy.serverName = 'ws://localhost:8082';
            RemotePy.start();
        }

        RemotePy.onopen = function() {
            console.log("Connected to server");
            
            // Call a remote function
            RemotePy.MyPythonServer.add(2, 4, function(sum) {
                console.log("Result:", sum);  // Output: 6
            });
            
            // Call with error handling
            RemotePy.MyPythonServer.greet("World", function(result) {
                console.log(result);  // Output: "Hello, World!"
            }, function(error) {
                console.error("Error:", error);
            });
        }

        RemotePy.onclose = function() {
            console.log("Connection closed");
        }
        
        RemotePy.onerror = function(error) {
            console.error("Connection error:", error);
        }
    </script>
</body>
</html>
```

### Python Client

**Installation:**

```bash
pip install remotepy_client
```

**Usage Example:**

```python
from remotepy_client.remotepy_rpc_client import RemotePyRPCClientSync

# Server configuration
broker = "ws://localhost"
port = 8082
verbose = False
server_url = f"{broker}:{port}"

# Connect to remotepy server to get the metadata for offered functions
client = RemotePyRPCClientSync(server_url, verbose)

# Build the client connectivity code for all the server functions
client.buildService('')

# Get access to available services
MyPythonServerEngine = client.getService('MyPythonServer')

# Connect to the remotepy server to call server functions
MyPythonServer = MyPythonServerEngine(f"{broker}:{port}", verbose)

# Call remotepy server function synchronously
result = MyPythonServer.add(a=5, b=6, callback=None)
print(result)  # Output: 11

# Call with callback
def handle_result(result):
    print(f"Received result: {result}")

MyPythonServer.greet(name="Python Client", callback=handle_result)

# Wait for all the threads to finish
client.thread().join()
```

**Async Python Client Example:**

```python
import asyncio
from remotepy_client.remotepy_rpc_client import RemotePyRPCClientAsync

async def main():
    server_url = "ws://localhost:8082"
    client = RemotePyRPCClientAsync(server_url)
    
    await client.connect()
    client.buildService('')
    
    MyPythonServer = client.getService('MyPythonServer')(server_url)
    
    # Call async function
    result = await MyPythonServer.async_process_data({"key": "value"})
    print(result)
    
    await client.close()

asyncio.run(main())
```

## Error Handling

Remote Py provides comprehensive error handling:

- **Input Validation**: Automatic validation of RPC call parameters
- **Rate Limiting**: Protection against excessive function calls
- **Error Sanitization**: Passwords and sensitive data are automatically redacted from error messages
- **Connection Handling**: Graceful handling of disconnected clients

Error responses follow this format:

```json
{
    "funcName": "function_name",
    "callId": "call_id",
    "error": "Error message (with sensitive data redacted)"
}
```

## Security Features

### Rate Limiting

By default, each function is rate-limited to 100 calls per 60-second window. This prevents abuse and DoS attacks.

### Input Validation

All RPC calls are validated:
- Function names must be strings (max 256 characters)
- Arguments must be dictionaries
- Potentially dangerous function names are logged (but not blocked for compatibility)

### Password Protection

- Passwords are never logged
- Error messages are sanitized to remove password information
- Session IDs are validated and cleaned up after use

### SQL Injection Protection

- Schema names are sanitized before use in SQL queries
- Parameterized queries are used for all database operations

## API Reference

### Decorators

- `@remotepy_class`: Decorates a class to make it a RemotePy server
- `@remotepy_func`: Exposes a function for remote calls
- `@remotepy_method`: Exposes a method for remote calls (used with SessionServer)
- `@remotepy_login_required`: Requires user to be logged in
- `@remotepy_permitted_to(action)`: Requires specific permission

### Server Methods

- `server.run(ip, port, ServerFactory=None)`: Start server on specified IP and port
- `server.run_ssl(ip, port, private_pem_file, fullchain_pem_file, ServerFactory=None)`: Start server with SSL/TLS

### SessionServer Methods

- `validateLogin(sessionid, username, password, remember, currentUrl, afterLoginUrl)`: Validate user credentials
- `getSessionId()`: Get current session ID
- `getNewSessionId()`: Generate a new session ID
- `isLoggedIn(sessionid)`: Check if user is logged in
- `logOut()`: Log out current session
- `set_email_config(...)`: Configure email settings for password reset

## Best Practices

1. **Use Type Hints**: Add type hints to your functions for better documentation
2. **Error Handling**: Always handle errors appropriately in your functions
3. **Session Management**: Use `SessionServer` for applications requiring authentication
4. **Rate Limiting**: Adjust rate limits based on your application's needs
5. **SSL/TLS**: Always use SSL/TLS in production environments
6. **Environment Variables**: Store sensitive configuration in environment variables
7. **Logging**: Use Python's logging module instead of print statements

## Troubleshooting

### Connection Issues

- Ensure the server is running and accessible
- Check firewall settings
- Verify WebSocket URL format (`ws://` or `wss://`)

### Rate Limiting

If you encounter rate limit errors, either:
- Increase the rate limit settings
- Reduce the frequency of function calls
- Implement client-side throttling

### Session Issues

- Ensure database configuration is correct
- Verify session IDs are being passed correctly
- Check that `SessionServer` is properly initialized

## Documentation

For more information, visit [https://www.remotepy.com](https://www.remotepy.com)

## License

This project is proprietary software. All rights reserved. See the LICENSE file for details.

## Author

**Faraz Farukh Tamboli**

- Email: faraz.tamboli@gmail.com
