{"id":4820,"library":"twirp","title":"Twirp Python Client/Server (via Twirpy)","description":"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.","status":"active","version":"0.0.7","language":"en","source_language":"en","source_url":"https://github.com/verloop/twirpy","tags":["rpc","protobuf","microservices","code-generation","http","asgi"],"install":[{"cmd":"pip install twirp uvicorn","lang":"bash","label":"Install runtime and ASGI server"},{"cmd":"protoc --python_out=. --twirpy_out=. ./your_service.proto","lang":"bash","label":"Generate service code (requires protoc and protoc-gen-twirpy)"},{"cmd":"go install github.com/verloop/twirpy/protoc-gen-twirpy@latest","lang":"bash","label":"Install Twirpy protoc plugin (requires Go and Protoc)"}],"dependencies":[{"reason":"ASGI server to run Twirp services.","package":"uvicorn","optional":false},{"reason":"Protocol Buffers runtime and compiler (protoc) are essential for defining services and generating code. The `protoc-gen-twirpy` plugin is also required for Python code generation.","package":"protobuf","optional":false}],"imports":[{"symbol":"TwirpASGIApp","correct":"from twirp.asgi import TwirpASGIApp"},{"symbol":"InvalidArgument","correct":"from twirp.exceptions import InvalidArgument"},{"symbol":"TwirpServerException","correct":"from twirp.exceptions import TwirpServerException"},{"symbol":"Context","correct":"from twirp.context import Context"}],"quickstart":{"code":"import random\nfrom twirp.asgi import TwirpASGIApp\nfrom twirp.exceptions import InvalidArgument\nfrom twirp.context import Context\n\n# Assuming you have a haberdasher.proto and generated code like this:\n# protoc --python_out=. --twirpy_out=. haberdasher.proto\n# from . import haberdasher_twirp, haberdasher_pb2\n# For demonstration, we'll mock these imports\n\n# Mocking generated code for quickstart demonstration\nclass MockHat:\n    def __init__(self, size, color, name):\n        self.size = size\n        self.color = color\n        self.name = name\n\nclass MockSize:\n    def __init__(self, inches):\n        self.inches = inches\n\nclass MockHaberdasherPb2:\n    def Hat(self, size, color, name):\n        return MockHat(size, color, name)\n\nclass MockHaberdasherTwirp:\n    class HaberdasherServer:\n        def __init__(self, service, server_path_prefix='/twirp'):\n            self.service = service\n            self.server_path_prefix = server_path_prefix\n\n    class HaberdasherClient:\n        def __init__(self, base_url, server_path_prefix='/twirp'):\n            self.base_url = base_url\n            self.server_path_prefix = server_path_prefix\n\n        def MakeHat(self, ctx, request):\n            # In a real scenario, this would make an HTTP call\n            print(f\"Client: Making hat for size {request.inches} inches\")\n            # Simulate a successful response\n            return MockHat(size=request.inches, color=\"mock_color\", name=\"mock_hat\")\n\nhaberdasher_pb2 = MockHaberdasherPb2()\nhaberdasher_twirp = MockHaberdasherTwirp()\n\nclass HaberdasherService(object):\n    def MakeHat(self, context, size):\n        if size.inches <= 0:\n            raise InvalidArgument(argument=\"inches\", error=\"I can't make a hat that small!\")\n        return haberdasher_pb2.Hat(\n            size=size.inches,\n            color=random.choice([\"white\", \"black\", \"brown\", \"red\", \"blue\"]),\n            name=random.choice([\"bowler\", \"baseball cap\", \"top hat\", \"derby\"])\n        )\n\n# Server setup\nservice = haberdasher_twirp.HaberdasherServer(service=HaberdasherService())\napp = TwirpASGIApp()\napp.add_service(service)\n\nprint(\"Twirp server (mocked) app created. Run with 'uvicorn your_module:app --port=3000' (after code generation).\")\n\n# Client usage (demonstrative, requires running server)\n# client = haberdasher_twirp.HaberdasherClient(\"http://localhost:3000\")\n# try:\n#     response = client.MakeHat(ctx=Context(), request=haberdasher_pb2.Size(inches=12))\n#     print(f\"Client received: {response.name} {response.color} hat, size {response.size}\")\n# except TwirpServerException as e:\n#     print(f\"Client error: {e.code} - {e.message}\")\n","lang":"python","description":"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."},"warnings":[{"fix":"Review and update server URL prefixes in both server and client configurations. Explicitly set `server_path_prefix` if a custom prefix is desired, or ensure consistency with the default `/twirp`.","message":"Twirp Wire Protocol v7 introduces breaking changes from v5, notably affecting server URL prefixes. The default prefix is now `/twirp`, but custom prefixes can be set via `server_path_prefix` in server and client constructors. Ensure compatibility when upgrading or interacting with services using different protocol versions.","severity":"breaking","affected_versions":"All versions supporting v7 protocol (e.g., twirp 0.0.7 / twirpy 0.1.0+)."},{"fix":"Install `protoc` (e.g., `brew install protobuf` on macOS) and the `protoc-gen-twirpy` plugin (e.g., `go install github.com/verloop/twirpy/protoc-gen-twirpy@latest`) and ensure `protoc-gen-twirpy` is in your system's PATH before attempting code generation.","message":"The `twirp` Python package provides the runtime library, but the actual server/client code generation requires the `protoc` compiler and the `protoc-gen-twirpy` Go plugin, which must be installed separately.","severity":"gotcha","affected_versions":"All versions."},{"fix":"For services handling large messages, increase the `max_receive_message_length` parameter when constructing `TwirpASGIApp` (e.g., `app = TwirpASGIApp(max_receive_message_length=1024 * 1024)` for 1MB).","message":"The default maximum message body length is 100KB. Larger messages will result in errors unless this limit is explicitly overridden.","severity":"gotcha","affected_versions":"All versions."},{"fix":"For the most up-to-date features and bug fixes, consider installing `twirpy` directly (`pip install twirpy`) and referring to its documentation and GitHub repository. Be aware of potential versioning discrepancies and ensure your generated code and runtime library are compatible.","message":"The `twirp` PyPI package (version 0.0.7) is effectively a wrapper for the `twirpy` project. While `twirp` 0.0.7 mentions support for Protocol v7, active development and newer versions (e.g., 0.3.0.dev2) are found under the `twirpy` PyPI package. This can cause confusion regarding the 'latest' version or documentation sources.","severity":"gotcha","affected_versions":"Users of `twirp` PyPI package 0.0.7 and potentially older versions of `twirpy`."}],"env_vars":null,"last_verified":"2026-04-12T00:00:00.000Z","next_check":"2026-07-11T00:00:00.000Z"}