Skip to content

Instantly share code, notes, and snippets.

@radix
Last active November 25, 2015 21:33
Show Gist options
  • Save radix/2bc32a16face0c452f7e to your computer and use it in GitHub Desktop.
Save radix/2bc32a16face0c452f7e to your computer and use it in GitHub Desktop.
A purely functional microwrapper for Flask, using Effect (https://effect.readthedocs.org/)
"""
A microwrapper for Flask that gives you a pure-functional API.
- Request objects are passed to your handlers, instead of relying on the global
``flask.request``.
- Route handlers can return Effect objects (see https://effect.readthedocs.org/),
so you can represent your side-effects as pure values.
"""
from functools import wraps
from effect import Effect, sync_perform
from flask import Flask, request
class FunctionalFlask(object):
def __init__(self, name='FP Flask'):
self.routes = []
self.flask = Flask(name)
def route(self, *route_args, **route_kwargs):
"""
Register a route, just like with Flask.route, but with two differences:
- The Request object will be passed to your handler, instead of relying
on the global `flask.request`.
- Your handler can optionally return an Effect object.
"""
def decorator(f):
self.routes.append((f, route_args, route_kwargs))
return f
return decorator
def _register_route(self, dispatcher, handler, route_args, route_kwargs):
@self.flask.route(*route_args, **route_kwargs)
@wraps(handler)
def wrapper(*args, **kwargs):
# avoid giving global mutable state to the route-handler;
# `flask.request` is usually a magical global proxy to changing
# state. `_get_current_object()` gives us the inert underlying
# object.
req = request._get_current_object()
result = handler(req, *args, **kwargs)
if isinstance(result, Effect):
return sync_perform(dispatcher, result)
else:
return result
def run(self, dispatcher):
"""
Run the Flask application with the given Effect dispatcher.
"""
for (handler, route_args, route_kwargs) in self.routes:
self._register_route(dispatcher, handler, route_args, route_kwargs)
self.flask.run()
if __name__ == '__main__':
# Example
from effect import base_dispatcher, ComposedDispatcher
from effect.ref import Reference, reference_dispatcher
from effect.do import do, do_return
app = FunctionalFlask('My App')
counter_ref = Reference(0)
@app.route('/')
@do
def root(request):
result = yield counter_ref.modify(lambda i: i + 1)
yield do_return(str(result))
app.flask.config.update(PROPAGATE_EXCEPTIONS=True)
app.run(ComposedDispatcher([base_dispatcher, reference_dispatcher]))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment