Skip to main content

wsgi microframework suitable for building modular DRY RESTful APIs

Project description

madness

Madness orchestrates the HTTP request-response cycle using middleware for abstractions and routes for transformations.

It is built upon WSGI and the fabulous werkzeug routing system.

Guiding Principles

Don't repeat yourself

Dependency inversion principle

Do One Thing and Do It Well.

The Zen of Python

Installing

$ pip install -U madness

A Simple Example

from madness import run

def hello():
    return 'Hello, world!'

if __name__ == '__main__':
    run(hello)

Routing

a route may be specified with methods, middleware and defaults

from madness import routes, route, get, post, put, delete, patch, index

users = routes(
  index(lambda: 'users!'), # GET /
  get('/<int:user_id>', lambda user_id: f'id is {user_id}')
)

site = routes(
  get('/', lambda: 'homepage'), # GET / .. aka index
  route(my_function) # /my_function, any method
  # add users routes under the /users prefix
  routes(users, path = '/users'),
  # limit the request methods
  route('/foo', lambda: 'bar', methods = ['GET', 'POST']),
  post(my_other_function), # POST /my_other_function
)

Application / Serving

initialize an application to serve your routes through WSGI

handling routing errors using middleware

from madness import application, NotFound, index

def custom404():
  try:
    yield # application tries to find a route
  except NotFound:
    yield 'custom 404 body', 404

app = application(
  index(lambda: 'Hello, world!'),
  middleware = [custom404]
)

if __name__ == '__main__':
  app.run()

Nesting routes / middleware

you can nest routes and middleware as much as you need

from madness import routes, cors, route, index, get, abort

api = routes(

  # public API
  index(lambda: '1.0.0'),
  post('/login', lambda: abort(401)),

  # login required
  routes(
    routes(
      users,
      path = '/users',
      middleware = [
        # middleware to abstract the user database implementation
        users_database_implementation
      ]
    ),
    get('/groups', lambda: 'groups resource')
    middleware = [
      # check that the client is logged in
      authorize
    ]
  ),

  middleware = [
    # authenticate any client that accesses the API
    authenticate
  ]

)

app = application(
  index(lambda: 'Welcome to my app!'),
  # enable CORS for our API and static resources
  cors(
    routes(api, path = '/api'),
    route('/static/<path:filename>', lambda: 'not implemented'),
    # CORS settings
    origin = '*'
  ),
  middleware = [my_application_middleware]
)

if __name__ == '__main__':
  app.run()

Middleware

use madness.context to store abstractions for your higher level middleware/routes

rule args are added to context after successful routing

middleware has full access to request/response/exceptions

passing middleware to a madness.application will allow you to catch routing errors

the response/exception is bubbled through the middleware

from madness import request, json

def overkill_middleware():
    """demonstrates every """
    # before_request
    if request.headers.get('x-api-key') != 'valid-api-key':
        # abort
        yield json.response({'message': 'invalid api key'}, status = 403)
    else:
        try:
            response = yield
        except MyException as exception:
            yield json.response({'message': exception.message}, status = 500)
        else:
            # modify the response headers
            response.headers['x-added-by-context'] = 'value'
            # abort
            yield json.response('we decided to not send the original response, isn\'t that weird?')
        finally:
            # after_request
            pass

RESTful routes

inspired by Ruby on Rails' resources and these links

https://gist.github.com/alexpchin/09939db6f81d654af06b

https://medium.com/@shubhangirajagrawal/the-7-restful-routes-a8e84201f206

from madness import routes, index, new, create, show, edit, update, destroy

users = routes(
  index(get_all_users),
  new(new_user_form),
  create(add_user),
  show(get_one_user),
  edit(edit_user_form),
  update(update_user),
  destroy(delete_user),
  path = '/users'
)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

madness-0.5.0.tar.gz (6.4 kB view hashes)

Uploaded Source

Built Distribution

madness-0.5.0-py3-none-any.whl (8.4 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page