Twirp Python Client/Server (via Twirpy)

0.0.7 · active · verified Sun Apr 12

Twirp is a simple RPC framework built on Protocol Buffers, prioritizing simplicity over expansive features. It enables service-to-service communication by generating routing and serialization code from .proto definitions, running over standard HTTP 1.1 with support for both Protobuf and JSON serialization. The `twirp` Python package (version 0.0.7) serves as the runtime library for 'Twirpy', which is the Python implementation of the Twirp framework, supporting Twirp Wire Protocol v7. While the `twirp` package itself has not seen recent updates, its underlying implementation, `twirpy`, is actively maintained with periodic releases.

Warnings

Install

Imports

Quickstart

A Twirp service is defined in a `.proto` file. The `protoc` compiler, along with the `protoc-gen-twirpy` plugin, generates Python server and client stubs from this definition. You then implement the service logic in a Python class and expose it via a `TwirpASGIApp`. The example demonstrates a simple `HaberdasherService` and illustrates how to set up the ASGI application. For a complete, runnable example, you would need to define `haberdasher.proto` and generate the `haberdasher_twirp` and `haberdasher_pb2` modules.

import random
from twirp.asgi import TwirpASGIApp
from twirp.exceptions import InvalidArgument
from twirp.context import Context

# Assuming you have a haberdasher.proto and generated code like this:
# protoc --python_out=. --twirpy_out=. haberdasher.proto
# from . import haberdasher_twirp, haberdasher_pb2
# For demonstration, we'll mock these imports

# Mocking generated code for quickstart demonstration
class MockHat:
    def __init__(self, size, color, name):
        self.size = size
        self.color = color
        self.name = name

class MockSize:
    def __init__(self, inches):
        self.inches = inches

class MockHaberdasherPb2:
    def Hat(self, size, color, name):
        return MockHat(size, color, name)

class MockHaberdasherTwirp:
    class HaberdasherServer:
        def __init__(self, service, server_path_prefix='/twirp'):
            self.service = service
            self.server_path_prefix = server_path_prefix

    class HaberdasherClient:
        def __init__(self, base_url, server_path_prefix='/twirp'):
            self.base_url = base_url
            self.server_path_prefix = server_path_prefix

        def MakeHat(self, ctx, request):
            # In a real scenario, this would make an HTTP call
            print(f"Client: Making hat for size {request.inches} inches")
            # Simulate a successful response
            return MockHat(size=request.inches, color="mock_color", name="mock_hat")

haberdasher_pb2 = MockHaberdasherPb2()
haberdasher_twirp = MockHaberdasherTwirp()

class HaberdasherService(object):
    def MakeHat(self, context, size):
        if size.inches <= 0:
            raise InvalidArgument(argument="inches", error="I can't make a hat that small!")
        return haberdasher_pb2.Hat(
            size=size.inches,
            color=random.choice(["white", "black", "brown", "red", "blue"]),
            name=random.choice(["bowler", "baseball cap", "top hat", "derby"])
        )

# Server setup
service = haberdasher_twirp.HaberdasherServer(service=HaberdasherService())
app = TwirpASGIApp()
app.add_service(service)

print("Twirp server (mocked) app created. Run with 'uvicorn your_module:app --port=3000' (after code generation).")

# Client usage (demonstrative, requires running server)
# client = haberdasher_twirp.HaberdasherClient("http://localhost:3000")
# try:
#     response = client.MakeHat(ctx=Context(), request=haberdasher_pb2.Size(inches=12))
#     print(f"Client received: {response.name} {response.color} hat, size {response.size}")
# except TwirpServerException as e:
#     print(f"Client error: {e.code} - {e.message}")

view raw JSON →