21

I'm using Flask to expose some data-crunching code as a web service. I'd like to have some class variables that my Flask functions can access.

Let me walk you through where I'm stuck:

from flask import Flask
app = Flask(__name__)

class MyServer:
  def __init__(self):
    globalData = json.load(filename)

  @app.route('/getSomeData')
  def getSomeData():
    return random.choice(globalData) #select some random data to return

if __name__ == "__main__":
  app.run(host='0.0.0.0')

When I run getSomeData() outside of Flask, it works fine. But, when I run this with Flask, I get 500 internal server error. There's no magic here, and Flask has no idea that it's supposed to initialize a MyServer object. How can I feed an instance of MyServer to the app.run() command?

I could admit defeat and put globalData into a database instead. But, is there an other way?

Daniel
  • 39,063
  • 4
  • 50
  • 76
solvingPuzzles
  • 7,683
  • 14
  • 62
  • 110

5 Answers5

16

You can create an instance of MyServer just outside the scope of your endpoints and access its attributes. This worked for me:

class MyServer:
    def __init__(self):
        self.globalData = "hello"

from flask import Flask
app = Flask(__name__)

my_server = MyServer()

@app.route("/getSomeData")
def getSomeData():
    return my_server.globalData

if __name__ == "__main__":
    app.run(host="0.0.0.0")
Muntaser Ahmed
  • 3,797
  • 1
  • 13
  • 16
  • This is a decent alternative, but you still can't write end-to-end tests AND mock out dependencies (like network or filesystem IO) because your dependencies are not injected. You can do something hacky like `unittest.mock.patch`, but then you have to take care to construct your real server the same way you construct your test server (except for the mocks, of course). This is basically the Singleton antipattern – weberc2 Sep 16 '15 at 20:25
9

I know this is a late reply, but I came across this question while facing a similar issue. I found flask-classful really good. You inherit your class from FlaskView and register the Flask app with your MyServer class

http://flask-classful.teracy.org/#

In this case, with flask-classful, your code would look like this:

from flask import Flask
from flask_classful import FlaskView, route

app = Flask(__name__)

class MyServer(FlaskView):
  def __init__(self):
    globalData = json.load(filename)

  @route('/getSomeData')
  def getSomeData():
    return random.choice(globalData) #select some random data to return


MyServer.register(app, base_route="/")


if __name__ == "__main__":
  app.run(host='0.0.0.0')
tristndev
  • 199
  • 14
Abhimanyu Seth
  • 101
  • 1
  • 5
5

The least-coupled solution is to apply the routes at runtime (instead of at load time):

def init_app(flask_app, database_interface, filesystem_interface):
    server = MyServer(database_interface, filesystem_interface)
    flask_app.route('get_data', methods=['GET'])(server.get_data)

This is very testable--just invoke init_app() in your test code with the mocked/faked dependencies (database_interface and filesystem_interface) and a flask app that has been configured for testing (app.config["TESTING"]=True or something like that) and you're all-set to write tests that cover your entire application (including the flask routing).

The only downside is this isn't very "Flasky" (or so I've been told); the Flask idiom is to use @app.route(), which is applied at load time and is necessarily tightly coupled because dependencies are hard-coded into the implementation instead of injected into some constructor or factory method (and thus complicated to test).

weberc2
  • 6,371
  • 3
  • 35
  • 50
2

a bit late but heres a quick implementation that i use to register routes at init time

from flask import Flask,request,render_template
from functools import partial


registered_routes = {}
def register_route(route=None):
    #simple decorator for class based views
    def inner(fn):
        registered_routes[route] = fn
        return fn
    return inner

class MyServer(Flask):
    def __init__(self,*args,**kwargs):
        if not args:
            kwargs.setdefault('import_name',__name__)
        Flask.__init__(self,*args ,**kwargs)
        # register the routes from the decorator
        for route,fn in registered_routes.items():
            partial_fn = partial(fn,self)
            partial_fn.__name__ = fn.__name__
            self.route(route)(partial_fn)


    @register_route("/")
    def index(self):
        return render_template("my_template.html")

if __name__ == "__main__":
    MyServer(template_folder=os.path.dirname(__file__)).run(debug=True)
Joran Beasley
  • 93,863
  • 11
  • 131
  • 160
2

if you wish to approach MyServer class as a resource
I believe that flask_restful can help you:

from flask import Flask
from flask_restful import Resource, Api
import json
import numpy as np

app = Flask(__name__)
api = Api(app)

class MyServer(Resource):
    def __init__(self):
        self.globalData = json.load(filename)

    def get(self):
        return np.random.choice(self.globalData)

api.add_resource(MyServer, '/')

if __name__ == '__main__':
    app.run()
talp
  • 21
  • 4