Falcon Overview

http://falconframework.org/

What is Falcon?

Falcon is a high-performance Python framework for building cloud APIs. It encourages the REST architectural style, and tries to do as little as possible while remaining highly effective.

As they say:

Ok, another WSGI server...

But light, fast and flexible!

Benchmarks

  • GET request on each framework's app.
  • Parse a route template with a single embedded parameter
  • read a query parameter and a header
  • set an x-header on the response, 10 KiB plain-text

Scenario

eag@MacKenzie-2:src (task/CYCLOPS-11 *)$pip install pecan
eag@MacKenzie-2:src (task/CYCLOPS-11 *)$pip install bottle

eag@MacKenzie-2:src (task/CYCLOPS-11 *)$ falcon-bench
No module named flask
Skipping missing library: flask
No module named werkzeug.wrappers
Skipping missing library: werkzeug

Benchmarking, Trial 1 of 3....done.
Benchmarking, Trial 2 of 3....done.
Benchmarking, Trial 3 of 3....done.

Results:

1. falcon.........25,638 req/sec or 39.00 μs/req (6x)
2. falcon-ext.....19,256 req/sec or 51.93 μs/req (4x)
3. bottle.........14,359 req/sec or 69.64 μs/req (3x)
4. pecan...........4,504 req/sec or 222.02 μs/req (1x)

Try it yourself ...

pip install falcon
falcon-bench
print "hola"

So fast and about coding?

You dont have any aysnc/sync dependency.

You should add your things.

Only create falcon API, add resources and speak WSGI!

import falcon
import json
from datetime import datetime


# REST resources ...
class MyResource:
   def on_get(self, req, resp):
       """Handles GET requests"""
       resp.status = falcon.HTTP_200  

       def handlerDatetime(obj):
           if isinstance(obj, datetime):
               return obj.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
           else:
               raise TypeError(
                 'Object of type %s with value of %s is not JSON serializable'.format(type(obj),
                                                                                repr(obj)))
       resp.body = json.dumps({'status': 'ok', 'time': datetime.utcnow()}, default=handlerDatetime)

# falcon.API instances are callable WSGI apps
app = falcon.API()

# things will handle all requests to the '/things' URL path
app.add_route('/my_path', MyResource())

A basic falcon app

# Hooks
def token_is_valid(token, user_id):
    return True 


def auth(req, resp, params):
    # Alternatively, use Talons or do this in WSGI middleware...
    token = req.get_header('X-Auth-Token')

    if token is None:
        raise falcon.HTTPUnauthorized('Auth token required', 'Auth token required')

    if not token_is_valid(token, params['user_id']):
        raise falcon.HTTPUnauthorized('Invalid token', 'The token is invalid')

def check_media_type(req, resp, params):
    if not req.client_accepts_json:
        raise falcon.HTTPUnsupportedMediaType('only JSON please')

What more can do?

set hooks globally or with falcon.before

# Configure your WSGI server to load "things.app" (app is a WSGI callable)
app = falcon.API(before=[auth, check_media_type])
app.add_route('/users/{user_id}', MySuperResource())
class MySuperResource:
    def __init__(self):
        self.db = {'frodo': 'Frodo bolson', 'gandalf': 'Gandalf el blanco'}

    def on_get(self, req, resp, user_id):
        """Handles GET requests"""
        name_friends = req.get_param_as_list('name_friends', transform=str.upper, required=True)
        easy_param = req.get_param('easy_param')
        full_names = {name: self.db[key] for name in name_friends if key in self.db}
        
        resp.set_header('X-Powered-By', 'Telefonica')
        resp.status = falcon.HTTP_200
        resp.body = json.dumps(full_names)
    
    def on_post(self, req, resp, user_id):
        # req.stream is wsgi.input but wrapped with Body for easy read

        if req.stream.stream_len == 0:
            raise falcon.HTTPBadRequest("Empty Body", "It was expected a json body")
        body = req.stream.read()
        try:
            json_body = ujson.loads(body)
        except ValueError as e:
            print "Invalid json body %s", e
            raise falcon.HTTPUnsupportedMediaType("Body is not a valid JSON object/array")
        self.db[json_body['name']] = json_body['full_name']

        resp.status = falcon.HTTP_201
        resp.location = '/users/%s/amigos/%s' % (user_id, json_body['name'])

Headers, body, location

    def on_put(self, request, response, user_id):
        raise Exception('Can not put')


def handleException(ex, req, resp, params):
        resp.body = json.dumps({'error': 'Exception'})
        resp.status = falcon.HTTP_500


app.add_error_handler(Exception, handleException)


# Useful for debugging problems in your API; works with pdb.set_trace()
if __name__ == '__main__':
    httpd = simple_server.make_server('127.0.0.1', 8000, app)
    httpd.serve_forever()


# or uwsgi, apache, gunicorn, ...

Error handling...

QUESTIONS?

FALCON

By Eduardo Alonso García