How to enable dev-only behavior in route functions

Viewed 119

I want to be able to define more or less arbitrary changes in functions decorated with @app.route that may only be run by a non-production server. A straight forward way to implement what I mean would be to put the dev-only changes into if-blocks that check the current environment:

import flask

app = flask.Flask(__name__)

@app.route('/check', methods=['POST'])
def check():
    """Performs some kind of check on the given request data."""

    # this if-block gates the dev/test/review-only behavior
    if app.config['ENV'] != 'production':
        # let a tester short-circuit the logic if they want
        if flask.request.json['payload'] == 'just give me an ok':
            return flask.jsonify('ok')

    # the only code that runs in production, or in tests where I 
    # want the full logic to run
    actual_result = some_complex_logic()
    return flask.jsonify(actual_result)

But the downside of this approach is that having something that is essentially a massive exploit sitting at the core of the entrypoint, which will undergo lots of changes during the code's lifetime, feels somewhat risky to me.

An alternative approach that I've been considering was to have conditional wrappers that add the test-functionality. It would avoid having the test code within the actual code, and might print big red warning messages on startup during ENV != production to make it obvious that the server is insecure. Downside here would be that the stack traces would look slightly different.


To get all the constraints together in a short list:

  • I don't want to fully replace the functions, so mocks won't do
  • I don't want to add special test-only parameters to the request body
  • I don't want to deploy different builds on my test and production servers, only the configuration (i.e. ENV will be either production or something different) should be different
  • Canned requests that produce the desired results are not good enough - the logic of some_complex_logic changes, the API changes, and on top of all it's a little random
  • I'm not talking about setting up a test client, I want an actual server to run and be tested on by humans

Optimally, there is just a different programming paradigm that I should follow, or maybe a middleware that I failed to google that handles this nicely and without risk.


edit:

I ended up writing and releasing a generalized variant of this problem, Flask-Shortcut. Thanks @vulpxn for suggesting the decorator form, and @davidism for linking to additional info (and maintainig Flask in the first place ).

1 Answers

Decorators are your friend.

from flask import Flask
from functools import wraps

app = Flask(__name__)

def development_shortcut(f):
    @wraps(f)
    def wrap(*args, **kwargs):
        if app.env == 'production':
            # Continue on to the /check route logic
            return f(*args, **kwargs)
        else:
            # Non-production, skip the logic
            return 'Just Giving an OK', 200
    return wrap

@app.route('/check')
@development_shortcut
def check():
    # your code here
    return 'Expensive Operation', 200

if __name__ == '__main__':
    app.env = 'development'  #change this value to see it work
    app.run()
Related