The nano web framework

pip install nanoweb==1.0


Nano Web

A small framework for WSGI applications. nanoweb is simple glue between Webob and Routes encourages the use of existing WSGI middleware instead of forcing you to use it's own solutions to already solved problems.


Here is a simple greeting application using Nano

from nanoweb import FrontController
import routes
from routes.middleware import RoutesMiddleware
from webob.dec import wsgify
from webob import Response

apps = {}

def hello(request):
    """A simple WSGI application that says, Hello, World!"""
    return Response("Hello, World!",
apps['hello'] = hello

def goodbye(request):
    """A simple WSGI application that says, Goodbye Cruel World!"""
    return Response("Goodbye Cruel World!",
apps['goodbye'] = goodbye

mapper = routes.Mapper()
mapper.connect("/hello/", application="hello")
mapper.connect("/goodbye/", application="goodbye")

application = FrontController(apps)
application = RoutesMiddleware(application, mapper)

There you have it, a simple WSGI application using nanoweb

Request Guards

nanoweb provides some guards for doing simple HTTP web services. These guards assert requirements for a request and if the request or application can not fulfill the assertion, the request is aborted with the appropriate HTTP status code.

agent_accepts(request, offers)

Checks that the client has can accept the content types an application provides. The request is aborted with 406 Not Acceptable if the application can not produce an acceptable response.

import json 
from webob import Response

def spew_json(request):
    agent_accepts(request, ["application/json"])
    return Response(json.dumps({"message": "Blegh"}))

allowed(request, method)

Guards against requests using disallowed HTTP methods

def only_get(request):
    allowed(request, ["GET"])
    return Response("Got.")

encode_body(content_type, data, encoders=encoders)

Returns serialized data given a content_type based on the dictionary, encoders. The encoders maps mime types to callables. It defaults to the following::

encoders = {
  "application/json": json.dumps


def spew_json(request):
    content_type = agent_accepts(request, ["application/json"])
    return Response(encode_body(content_type, {"message": "Blegh"}),


Asserts that the WSGI environ var "REMOTE_USER" is defined. It is the of a middleware to authenticate the request and populate the REMOTE_USER field in the WSGI environ. Aborts with 401 Unauthorized.

def private_app(request):
    return Response("Hi %s" % request.remote_user)

decode_body(request, json_schema=None, decoders=decoders)

Deserializes the request body using the decoders dictionary. Just with encoders, it is a map of mime types to callables. It defaults to the following:

decoders = {
  "application/json": json.loads

If the request's content type is not in decoders, the request aborts with a 415 Unsupported Media Type.

If json_schema is provided, the decoded body is validated against the schema using the json-schema-validator module. If the schema validation fails, the request aborts and response with a 400 Bad Request.

person_schema = {
  "type": "object",
  "properties": {"name": {"type": "string", "required": true},
                 "age":  {"type": "integer", "minimum": "0"},
                 "username": {"type": "string", "required": true}}}

def put_person(request):
    allowed(request, ["PUT"])
    data = decode_body(request, json_schema=person_schema)['username'], data)
    return HTTPOk()