Skip to content

Instantly share code, notes, and snippets.

@Tythos
Created May 20, 2021 05:55
Show Gist options
  • Save Tythos/79d5b439ff710e8dbe315eab0a96754e to your computer and use it in GitHub Desktop.
Save Tythos/79d5b439ff710e8dbe315eab0a96754e to your computer and use it in GitHub Desktop.
WsgiApp.py
"""This is a convenient wrapping/mapping pattern for Flask-based WSGI
applications. Specifically, a WSGI app may require state (for database
connections, for example) that is poorly-managed by function-based WSGI
routes. Instead, this pattern lets a specific class instance define it's
server state, with methods being defined and decorated for the usual Flask
route syntax. The URL map is then dynamically constructed from the pattern
property that the decorator attaches to route methods, with support for
optional route arguments (like method=[]) as well.
"""
import flask
def route(pattern, **options):
"""This decorator can be used in place of the normal @APP.route pattern to
flag specific methods with specific URL routes.
"""
def wrapper(method):
method.pattern = pattern
method.options = options
return method
return wrapper
class WsgiApp(flask.Flask):
"""WSGI application object, derived from flask.Flask class.
"""
def __init__(self, *args, **kwargs):
"""Forwards to most standard constructor behavior, but also invokes the
dynamic routing assignment logic in *addMethodRoutes()*.
"""
super().__init__(__name__, *args, **kwargs)
self.addMethodRoutes()
def addMethodRoutes(self):
"""Looks up unique methods with the "pattern" attribute, which is then
used to add a URL rule for those methods.
"""
for name in dir(self):
if hasattr(self, name):
attr = getattr(self, name)
if hasattr(attr, "pattern"):
pattern = getattr(attr, "pattern")
options = getattr(attr, "options")
self.add_url_rule(pattern, name, attr, **options)
@route("/", methods=["GET"])
def index(self):
"""This is an example of how the standard Flask routing logic can be
adapted to a class method using the decorator. In this case, @route
actually points to the locally-defined decorator, which is then
mapped dynamically upon instantiation. We also demonstrate how
URL-specific options can be propagated to the URL map.
"""
return b"OK", 200, {"Content-Type": "text/plain"}
def main():
"""By default, starts a demo instance of the WsgiApp. Since this is
derived from flask.Flask, it can be transparently served by any WSGI
server, as well.
"""
WsgiApp().run()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment