WSGI Tackle
This is a WSGI framework originated by Sam Briesemeister, derived from work in personal projects and products still in development, which needed a very light WSGI abstraction.
It adopts some inspiration, paradigms and design components from other WSGI environments, including webapp2 (Google AppEngine), but aims to maintain a degree of compatibility with core WSGI architecture.
It's currently tested and working in combination with Flask. No dependencies on a particular WSGI server, e.g. gevent
, are currently required.
Why does the world need another WSGI framework?
Frankly the world probably doesn't, but in spite of the abundance of available frameworks, none quite fit my taste.
The name: tackle, the equipment used to in a particular sport.
Dependencies
- webob
Features
- Virtual Host Routing based on hostname
- URL Routing with regular expressions to simplify RESTful interface design (which most modern WSGI frameworks also claim)
- Middleware Class for easily mixing features, in generic fashion, with existing WSGI applications.
Virtual Host Routing
In case you're using a very simplistic hosting environment which offers you a single WSGI instance, such as Gandi's Simple Hosting, you may need to integrate multiple WSGI applications from several domains.
from tackle import WSGIService
from myapp import example_com
from myapp import otherhost_com
# Pass any WSGI-compliant callable as a hander for any host.
service = WSGIService(
('www.example.com', example_com)
('www.otherhost.com', otherhost_com)
)
# Register a single WSGI callable for multiple hosts
service.register(another_app, 'abc.com', 'xyz.com')
URL Routing
This model probably isn't new to you, it's echoed in many frameworks, in various styles. Register a RequestHandler for a particular URL pattern.
from tackle import WSGIApplication, WSGIRequestHandler
app = WSGIApplication()
@app.route('/resource/<resource_id>')
class ResourceHandler(WSGIHandler):
def get(self, resource_id):
""" retrieve the resource's content """
# self.response is a webob.Response
self.response.headers['Content-Type'] = 'text/plain'
return "Looking for resource %r" % resource_id
def post(self, resource_id):
""" update the resource based on request body """
# self.request is a webob.Request
update_prop = self.request.params.get('resource_name')
pass
Currently there are no artificial constraints on HTTP verbs that will be attempted as methods on the request handler.
Middleware
A simple, generic Middleware model is provided. Any middleware derived from this class can implement one or both of the methods illustrated below, run_before
or run_after
.
from tackle import Middleware, WSGIApplication
class CustomMiddleware(Middleware):
def run_before(self, environ, start_response):
# any aspects accessible to downstream applications need to be added to environ.
# a return value from this method will override (prevent) the downstream app from handling the request.
pass
def run_after(self, environ, start_response, previous_result):
# `previous_result` is the return value from the app, or any intervening Middleware.
# this method should return previous_result, if unmodified, or return a modified form of it (a replacement).
return previous_result
# Prepare your core WSGI application.
myapp = WSGIApplication(...)
# Constructs a new WSGI callable.
# If you reassign `app` with this value, you may lose reference to your original app's properties.
wrapped_app = CustomMiddleware(option = "value").wsgi(myapp)
Static File Preemptive Route
This middleware acts as a preemptor to a primary application. When a request matches the given URL prefix, the remaining URL path is resolved within a given local directory, and served directly with a simple caching directive.
from tackle import WSGIApplication, StaticFileMiddleware
# All requests in "/static/" should resolve within "./local/app/static"
# e.g. GET /static/site.css will resolve to "local/app/static/site.css"
static = StaticFileMiddleware('local/app/static', '/static/')
app = WSGIApplication()
app = static.wsgi(app)
Rule-Based Redirection
The Redirection middleware operates preemptively, intercepting requests to matched URLs and responding with HTTP 301 or 302 redirects.
Two options are provided to incorporate common behaviors; retain_path
will first copy the path from the original request into the redirect's destination; and retain_query
will first copy the query from the original request into the redirect's destination.
These options will override statically-defined path or query components of the configured target. Variations can be achieved using regular expressions and destination templates.
from tackle import WSGIApplication, RedirectionMiddleware
app = WSGIApplication()
redir = RedirectionMiddleware(retain_path = False, retain_query = True) # defaults
redir.redirect("/help/(.*)", "http://help.mysite.com/{1}")
# named captures are also supported.
redir.redirect("/help/(?P<article>.*)", "http://help.mysite.com/{article}")
app = redir.wsgi(app)