Metadata-Version: 2.4
Name: aetherrpc
Version: 0.1.0
Summary: A zero-dependency RPC framework for Python
Author-email: Aadinath Sreejith <aadinathsreejith8@gmail.com>
License-Expression: MIT
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# AetherRPC

**A lightweight, zero-dependency RPC framework for Python.**

AetherRPC lets Python applications communicate with each other over TCP using a simple remote-procedure-call interface.

It supports synchronous and asynchronous RPC calls, streaming, authentication, concurrent calls, custom Python objects, remote exception propagation, middleware, and dynamic client proxies.

## Features

* 🚀 **Zero runtime dependencies**
* 🔌 TCP-based RPC
* ⚡ Synchronous and asynchronous clients
* 🔄 Synchronous and asynchronous server handlers
* 🌊 Streaming RPC support
* 🔐 Optional token authentication
* 🧵 Concurrent asynchronous calls
* 🪄 Dynamic client method proxies
* 📦 Custom Python object round-tripping
* 💥 Remote exception propagation
* ⏱️ Configurable connection and call timeouts
* 🧩 Server middleware hooks
* 🛠️ Simple decorator-based server API
* 🐍 Python **3.10+**

## Installation

Install from PyPI:

```bash
pip install aetherrpc
```

## Quick Start

### 1. Create a server

```python
from aetherrpc import AetherRPCServer

server = AetherRPCServer(
    host="127.0.0.1",
    port=9000,
)

@server.register
def add(a, b):
    return a + b

server.start()
```

The function registered with `@server.register` becomes available to RPC clients.

### 2. Create a client

```python
from aetherrpc import AetherRPCClient

client = AetherRPCClient(
    host="127.0.0.1",
    port=9000,
)

client.connect()

result = client.add(2, 3)

print(result)  # 5

client.close()
```

The client dynamically resolves public method names, so there is no need to manually create a proxy for every registered server method.

## Context Manager

Clients can also be used as context managers:

```python
from aetherrpc import AetherRPCClient

client = AetherRPCClient(
    host="127.0.0.1",
    port=9000,
)

with client as rpc:
    print(rpc.add(2, 3))
```

## Authentication

Enable token authentication on the server:

```python
from aetherrpc import AetherRPCServer

server = AetherRPCServer(
    host="127.0.0.1",
    port=9000,
    auth_token="my-secret-token",
)

@server.register
def add(a, b):
    return a + b

server.start()
```

Then provide the same token to the client:

```python
from aetherrpc import AetherRPCClient

client = AetherRPCClient(
    host="127.0.0.1",
    port=9000,
    auth_token="my-secret-token",
)

with client as rpc:
    print(rpc.add(10, 20))
```

Clients with an invalid or missing token are rejected when authentication is enabled.

## Async RPC

AetherRPC supports asynchronous client calls:

```python
import asyncio

from aetherrpc import AetherRPCClient


async def main():
    client = AetherRPCClient(
        host="127.0.0.1",
        port=9000,
    )

    client.connect()

    result = await client.async_add(2, 3)

    print(result)

    client.close()


asyncio.run(main())
```

Async server functions are supported as well:

```python
@server.register
async def add(a, b):
    return a + b
```

## Aliases

Server methods can be registered with an alias:

```python
@server.register(alias="sum")
def add_numbers(a, b):
    return a + b
```

Clients can then call:

```python
result = client.sum(2, 3)
```

## Streaming

AetherRPC supports streaming RPC operations for both synchronous and asynchronous handlers.

This makes it possible to send results progressively instead of waiting for a single final value.

See the test suite for streaming examples.

## Exceptions

Exceptions raised by remote procedures can be propagated back to the client as AetherRPC errors.

For example:

```python
@server.register
def divide(a, b):
    return a / b
```

A failed remote call can then be handled normally by the client:

```python
try:
    result = client.divide(10, 0)
except Exception as exc:
    print(f"RPC failed: {exc}")
```

AetherRPC also provides its own exception hierarchy for connection, timeout, streaming, and RPC-related failures.

## Configuration

The client supports configuration such as:

```python
client = AetherRPCClient(
    host="127.0.0.1",
    port=9000,
    auth_token=None,
    connect_timeout=5,
)
```

The default client configuration uses:

```text
Host: 127.0.0.1
Port: 9000
Authentication: disabled
```

## Command-Line Entry Point

AetherRPC provides an `aetherrpc` command through its Python package entry point for demo.

```bash
aetherrpc {server|client}
```

## Testing

AetherRPC uses Python's built-in `unittest` framework.

Run the complete test suite with:

```bash
python -m unittest discover -s tests -v
```

The project currently includes tests covering:

* Client configuration
* Client connections
* Context managers
* Dynamic method proxies
* Connection failures
* Synchronous RPC
* Asynchronous RPC
* Streaming
* Authentication
* Concurrent calls
* Call identity
* Custom object serialization
* Remote exceptions
* Timeouts
* Protocol framing
* Token handling
* Server lifecycle
* Middleware
* Function registration
* Aliases
* Server request processing

## Project Structure

```text
aetherrpc/
├── src/
│   └── aetherrpc/
│       ├── client.py
│       ├── protocol.py
│       ├── server.py
│       ├── __init__.py
│       └── __main__.py
│
├── tests/
│   ├── test_client.py
│   ├── test_integration.py
│   ├── test_protocol.py
│   ├── test_server.py
│   └── __init__.py
│
├── LICENSE
├── README.md
└── pyproject.toml
```

## Requirements

* Python **3.10 or newer**
* No additional runtime dependencies

## License

AetherRPC is released under the **MIT License**.

See [LICENSE](LICENSE) for the full license text.

## Status

AetherRPC is currently at **version 0.1.0**.

The API may evolve as the project develops.
