Skip to main content

To make RPC clients and servers with Python

Project description

Introduction

It is a Python package to make Hobby-RPC clients and servers.

It depends on:

To install:

uv add hobbyrpc

Server

To create a server:

from hobbyrpc import Server

rpc = Server()

@rpc.fun
class FunctionName:
    def call(self):
        return 'any object serializable to JSON'

@rpc.fun
class OtherFunctionName:
    def call(self):
        return ['any', 'object', 'serializable', 'to', 'JSON']

In the above example, rpc is a WSGI application. With multiple options for deployment.

To provide it via a TCP socket with Gunicorn:

uv run gunicorn --bind '127.0.0.1:8000' 'module_name:rpc'

Via Unix socket:

uv run gunicorn --bind unix:/tmp/rpc.socket 'module_name:rpc'

self.input

The raw input of a function can be accessed via self.input. Here is an example of a function echoing its input back to the client:

@rpc.fun
class Echo:
    def call(self):
        return self.input

Pydantic

Validation

In most cases, it is advisable to rely on Pydantic for validating input instead of parsing self.input by other means.

To do so, input fields shall be passed as class annotations. Like so:

@rpc.fun
class ContrivedConcatenation:
    first: str
    second: str

    def call(self):
        # self.pydantic provides an instance of Pydantic's BaseModel
        # its fields are available as self attributes too:
        #   self.pydantic.first == self.first
        #   self.pydantic.second == self.second
        return f'{self.first} and {self.second}'

If a client does not pass an input with all required fields or pass a value of a wrong type, the server responds with 400 Bad Request.

Output

Instances of Pydantic's BaseModel can also be used as outputs of RPC functions:

from pydantic import BaseModel

class User(BaseModel):
    id: int
    name: str = 'John Doe'

@rpc.fun
class SomeUser:
    def call(self):
        user = User(id=1)
        return user

@rpc.auth

By default, any client is allowed to call the functions. To restrict that, use @rpc.auth decorator to define find_user function as follows:

@rpc.auth
def find_user(token):
    if token == 'ValidToken':
        return User(id=1)

find_user should be a function that takes one argument, token string. token is what clients are supposed to pass in the Authorization header.

If find_user returns a falsy value or raises en error, the server responds with 403 Forbidden.

If find_user returns something else, the server will assume it is something that represents the current user and will make it available as self.user:

@rpc.fun
class CurrentUser:
    def call(self):
        return self.user

CORS

By default, the server sets permissive CORS headers(Access-Control-Allow-Origin: *) for requests from any origin. To restrict that, pass a list of allowed domains in origins as follows:

from hobbyrpc import Server

CORS = {
    'origins': [
        'https://allowed.domain',
        'https://www.allowed.domain',
    ]
}

rpc = Server(
    CORS=CORS,
)

Client

Initialize a Client by passing it an address and, optionally, a token too. Like so:

from hobbyrpc import Client

# via tcp
call = Client(
    address='http://127.0.0.1:8080',
    token="authorization token",
)

# or via unix socket
call = Client(
    address='/tmp/path/to/rpc.socket',
    token="authorization token",
)

Call functions remotely. Like so:

input = {
    'first': 1,
    'second': 2,
}

output = call('SomeUnaryFunction', **input)
output = call('SomeNullaryFunction')

# a call to Compile that compiles CoffeeScript to JavaScript
output = call(
    'Compile',
    code='answer = 42',
    bare=True,
)

input and output are Python objects serializable to and deserializable from JSON.

Development

uv run pytest to run the tests.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hobbyrpc-0.1.2.tar.gz (5.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hobbyrpc-0.1.2-py3-none-any.whl (9.4 kB view details)

Uploaded Python 3

File details

Details for the file hobbyrpc-0.1.2.tar.gz.

File metadata

  • Download URL: hobbyrpc-0.1.2.tar.gz
  • Upload date:
  • Size: 5.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for hobbyrpc-0.1.2.tar.gz
Algorithm Hash digest
SHA256 b3a64a6493c992b0dbfd92bbf471c4a223b9342160b5458078110b00a5009617
MD5 bfbd0d041c3ab139e0c84885d233ace4
BLAKE2b-256 b6684a513d05a893a96fdc0dfec70f77eb5736527226badd183a777d388454d7

See more details on using hashes here.

File details

Details for the file hobbyrpc-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: hobbyrpc-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 9.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for hobbyrpc-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 a7261d421cb749f764571a15fd24ba8f98fcacf4612602e5a565de2ca89a6297
MD5 f72c2f6966131e6c7914711eeaaba9da
BLAKE2b-256 f9cdc8ef771ff5e8faf8f6cc94fa8a45ae6160d9b4b7dc45632118279aafaa2a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page