Home

Awesome

Twirp Python Server Generator

Generate a Python WSGI application that can answer Twirp requests.

The general design of this generator was taken from Chris Gaffney's ruby client generator.

Install

go get -u github.com/daroot/protoc-gen-twirp_python_srv

Requirements

Code from the generator requires the following libraries:

Additionally in order to use the resulting applicaiton, you'll need a WSGI capable server (such as gunicorn or uwsgi) or a library capable of acting as a standalone server (like bjoern, gevent, or werkzeug's internal debugging server).

Usage

protoc --proto_path=$GOPATH/src:. --twirp_python_srv_out=. --python_out=. path/to/service.proto

Create service, using bjoern as a WSGI server:

import random

import bjoern
import haberdasher_pb2 as pb
from haberdasher_twirp_srv import (Errors, HaberdasherImpl, HaberdasherServer,
                                   TwirpException)

class MadHaberdasher(HaberdasherImpl):
    def MakeHat(self, size):
        if size.Inches <= 0:
            raise TwirpException(Errors.InvalidArgument,
                                 "I can't make a hat that small")
        return pb.Hat(Size=size.Inches,
                      Color=random.choice("white", "black", "brown", "red"),
                      Name=random.choice("bowler", "top hat", "derby"))

if __name__ == "__main__":
    app = HaberdasherServer(MadHaberdasher())
    bjoern.run(app, "0.0.0.0", 8080)

Signals

The generated server code uses the Blinker library to emit signals at various points in the request processing to match the capabilities of Twirp Go's context.Context and twirp.ServerHooks.

Each signal handler receives a 'context' dictionary, which contains the current state of processing. The same context object is used throughout the entire request/response processing, so it may be used to store temporary data such as timing or latency information for use in later signal handlers.

Applications can connect to one or more of the following signals in order to do authentication, metrics, logging, tracing, or other similar per-request tasks:

Any number of handlers may be connected to each signal so that independent packages can handle various tasks, but per Blinker's documentation, the order of notification is not defined.

import time
from haberdasher_twirp_srv import request_received, request_sent

@request_recieved.connect
def start_metrics(ctx):
    ctx["start_time"] = time.time()

@request_sent.connect
def finish_metrics(ctx):
    duration = time.time() - ctx["start_time"]
    log.info("Request to %s endpoint took %0.4f seconds",
             ctx["endpoint"], duration)