Using Flask whit class - Python [duplicate] - python

I'm learning Flask and am a bit confused about how to structure my code. So I tried to extend Flask main class as follows:
from flask import Flask, ...
class App(Flask):
def __init__(self, import_name, *args, **kwargs):
super(App, self).__init__(import_name, *args, **kwargs)
Note that I am aware of that this may be a completely wrong approach.
So that when I want to start the app I do:
app = App(__name__)
if __name__ == '__main__':
app.run()
This way I can order my methods and routes in the class, but the problem is when using self-decorators:
#route('/')
def home(self, context=None):
context = context or dict()
return render_template('home.html', **context)
Which raises an error as unresolved reference 'route'. I guess this is not the way I should be structuring the app. How should I do it instead or how do I get the error fixed?

Doing this doesn't make sense. You would subclass Flask to change its internal behavior, not to define your routes as class methods.
Instead, you're looking for blueprints and the app factory pattern. Blueprints divide your views into groups without requiring an app, and the factory creates and sets up the app only when called.
my_app/users/__init__.py
from flask import Blueprint
bp = Blueprint('users', __name__, url_prefix='/users')
my_app/users/views.py
from flask import render_template
from my_app.users import bp
#bp.route('/')
def index():
return render_template('users/index.html')
my_app/__init__.py
def create_app():
app = Flask(__name__)
# set up the app here
# for example, register a blueprint
from my_app.users import bp
app.register_blueprint(bp)
return app
run.py
from my_app import create_app
app = create_app()
Run the dev server with:
FLASK_APP=run.py
FLASK_DEBUG=True
flask run
If you need access to the app in a view, use current_app, just like request gives access to the request in the view.
from flask import current_app
from itsdangerous import URLSafeSerializer
#bp.route('/token')
def token():
s = URLSafeSerializer(current_app.secret_key)
return s.dumps('secret')
If you really want to define routes as methods of a Flask subclass, you'll need to use self.add_url_rule in __init__ rather than decorating each route locally.
class MyFlask(Flask):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.add_url_rule('/', view_func=self.index)
def index(self):
return render_template('index.html')
The reason route (and self) won't work is because it's an instance method, but you don't have an instance when you're defining the class.

Related

How to decorate class functions with flask-socketio if the socketIO instance is a class member variable?

I'm trying to create a hello world example for a flask application with socketio, that is wrapped into a class.
I want to wrap the whole application into a class, that can be embedded into other applications. For this purpose, I am creating the Flask application in the constructor of my class, and also create the SocketIO instance as a member too.
The problem is that I get a NameError exception tellimg me that 'socketio' is not defined.
I have tried to adapt the minimum working example from : the flask-socketio tutorial (https://flask-socketio.readthedocs.io/en/latest/)
Here is the example code I'm trying to get to work:
from flask import Flask
from flask_socketio import SocketIO, emit
class ApplicationExample:
def __init__(self):
self.app = Flask(__name__)
self.socketio = SocketIO(self.app)
#socketio.on('ping')
def pongResponse(self, message):
emit('pong')
def run(self):
self.socketio.run(service.app, host='0.0.0.0')
if __name__ == '__main__':
service = ApplicationExample()
service.run()
I would like to bind the pongResponse function to the socketio instance inside my class. How is it possible to decorate the function while having the SocketIO class as a member?
According to the documentation you can use the below instead of a decorator
def my_function_handler(data):
pass
socketio.on_event('my event', my_function_handler, namespace='/test')
Which would become something like
from flask import Flask
from flask_socketio import SocketIO, emit
class ApplicationExample:
def __init__(self):
self.app = Flask(__name__)
self.socketio = SocketIO(self.app)
self.socketio.on_event('ping', self.pongResponse, namespace='/test')
def pongResponse(self, message):
emit('pong')
def run(self):
self.socketio.run(service.app, host='0.0.0.0')
Since decorating a function simply calls the decorator and passes the decorated function as the first argument you can write:
def __init__(self):
...
self.pongResponse = self.socketio.on('ping')(self._pongResponse)
def _pongResponse(self, message):
...
A method beginning with a _ denotes that is not part of the public API of the class (thus this simply is a convention). Also note that in python you should use snake_caseinstead of camelCase to name your functions and variables, although this is also just a convention.

Flask app created twice during python unittest

I have an app.py file which creates an flask app
def create_app():
app = Flask(__name__)
return app
I am trying to write an unittest for my module and below is the file
from app import create_app
class TestCase(unittest.TestCase):
def setUp(self):
self.app = create_app()
self.client = self.app.test_client()
ctx = self.app.app_context()
ctx.push()
def test_healthcheck(self):
res = self.client.get("/")
self.assertEqual(res.status_code, 200)
def test_tenant_creation(self):
res = self.client.post("/tenants")
self.assertEqual(res.status_code, 200)
When i run individual test methods it is working fine. But when i run the entire test case , the create app is called again which causes issues since my create app has dependencies which needs to be called only once.
Is it possible to create app only once ?
setUp gets called before each test method. Therefore, if you run the whole test case, it will be called twice (one for each test method).
To run something only once for the TestCase, you could try overriding the __init__ method (see this SO question), or setUpClass or setUpModule. YMMV depending on which python version and test runners you are using.
IMO, the problem may related with context. You should create a tearDown() method to destroy the application context you created in setUp():
class TestCase(unittest.TestCase):
def setUp(self):
self.app = create_app()
self.client = self.app.test_client()
self.ctx = self.app.app_context()
self.ctx.push()
def tearDown(self):
self.ctx.pop()

Access flask.g inside greenlet

I'm using Flask + gevent and want to access the flask.g application global inside the target function of a greenlet.
I'm using the copy_current_request_context decorator and have a situation pretty similar to example given in the docs:
import gevent
from flask import copy_current_request_context, g
#app.route('/')
def index():
g.user_data = 'foobar'
g.more_user_data = 'baz'
#copy_current_request_context
def do_some_work():
some_func(g.user_data, g.more_user_data)
...
gevent.spawn(do_some_work)
return 'Regular response'
However, I get the following error:
AttributeError: '_AppCtxGlobals' object has no attribute 'user_data'
I think a new application context is pushed when the request context is copied? I set a trace in the Flask code here and that seems to be the case. So the error isn't all that surprising because the flask.g object is application context scoped as of 0.10 (see http://flask.pocoo.org/docs/0.12/api/#flask.Flask.app_ctx_globals_class).
Obviously, I can just pass the user data into the target function as arguments:
import gevent
from flask import g
#app.route('/')
def index():
g.user_data = 'foobar'
g.more_user_data = 'baz'
def do_some_work(user_data, more_user_data):
some_func(user_data, more_user_data)
...
gevent.spawn(do_some_work, g.user_data, g.more_user_data)
return 'Regular response'
And this works just fine, but I was hoping to use flask.g if possible.
flask.g is bound with the app context, not on request context, as the doc says:
Starting with Flask 0.10 this is stored on the application context and no longer on the request context ...
copy_current_request_context() only copy request context, but give you a new app context. You could create one to pass current app context with closure:
def copy_current_app_context(f):
from flask.globals import _app_ctx_stack
appctx = _app_ctx_stack.top
def _(*args, **kwargs):
with appctx:
return f(*args, **kwargs)
return _
However, I prefer pass data to greenlet explicitly via arguments, which is cleaner.

Using routes in classes

I am trying to rewrite some working code as a class.
A minimal working code:
from flask import Flask
app = Flask(__name__)
#app.route("/1")
def func_1():
return "view 1"
#app.route("/2")
def func_2():
return "view 2"
app.run()
How to write it as a class with the route defined during the object instantiation?
I only want it clean: after instantiating an object I want the respective route already working with no extra lines of code.
This is the closest I get to:
from flask import Flask
class NewView:
def __init__(self, url, string):
self.string = string
self.server = Flask(__name__)
self.server.add_url_rule(url, 'index', self.index)
def index(self):
return self.string
v1 = NewView("/1", "view 1")
v2 = NewView("/2", "view 2")
v1.server.run()
This, of course, recognizes /1 as route for v1.index(), but /2 doesn't work.
The ideal would be something like the following but I cannot make it work:
from flask import Flask
app = Flask(__name__)
class NewView:
def __init__(self, url, string):
....
app.add_url_rule(url, ...?..., self.index)
def index(self):
return self.string
v1 = NewView("/1", "view 1")
v2 = NewView("/2", "view 2")
app.run()
First, your mistake:
def __init__(self, url, string):
self.string = string
self.server = Flask(__name__)
self.server.add_url_rule(url, 'index', self.index)
Because you have two instances of this class, there are two Flask objects. You only run the second one.
What you're immediately trying to do can be done like this:
import flask
# There can be only one!
app = flask.Flask(__name__)
class MyView:
def __init__(self, url, name, string):
self.url = url
self.string = string
app.add_url_rule(url, name, self.serve)
def serve(self):
return self.string
view1 = MyView('/1', name='view1', string='This is View 1.')
view2 = MyView('/2', name='view2', string='This is View 2, not view 1.')
app.run()
The above code will work and do what you expect. Something to note is that, since Flask likes names for unique routes, I have you passing in a name for each route. That way, url_for('view1') and url_for('view2') work.
Having said all that, the community has largely already accomplished much of this Pluggable Views. Check it out.
I think that if your goal is to keep code clean, you should avoid creating objects that are never used. The class based views in flask. The as_view() method that is being passes is class method, so also here there is no need to create a never used object. The process of registring urls belongs to creation of an app, not separate objects (that's how it works in Django for instance). If I were you I would go with something similar to this:
from flask import Flask
from flask.views import View
def init_app():
app = Flask(__name__)
app.add_url_rule('/', view_func=NewView.as_view('index'))
return app
class NewView(View):
def dispatch_request(self):
return 'Test'
app = init_app()
app.run()

Flask blueprint unit-testing

Is there a good practice to unit-test a flask blueprint?
http://flask.pocoo.org/docs/testing/
I didn't found something that helped me or that is simple enough.
// Edit
Here are my code:
# -*- coding: utf-8 -*-
import sys
import os
import unittest
import flask
sys.path = [os.path.abspath('')] + sys.path
from app import create_app
from views import bp
class SimplepagesTestCase(unittest.TestCase):
def setUp(self):
self.app = create_app('development.py')
self.test_client = self.app.test_client()
def tearDown(self):
pass
def test_show(self):
page = self.test_client.get('/')
assert '404 Not Found' not in page.data
if __name__ == '__main__':
unittest.main()
In this case, i test the blueprint. Not the entire app. To test the blueprint i've added the root path of the app to sys.path. Now i can import the create_app function to ...create the app. I also init the test_client.
I think i've found a good solution. Or will is there a better way?
I did the following if this helps anyone. I basically made the test file my Flask application
from flask import Flask
import unittest
app = Flask(__name__)
from blueprint_file import blueprint
app.register_blueprint(blueprint, url_prefix='')
class BluePrintTestCase(unittest.TestCase):
def setUp(self):
self.app = app.test_client()
def test_health(self):
rv = self.app.get('/blueprint_path')
print rv.data
if __name__ == '__main__':
unittest.main()
Blueprints are very similar to application. I guess that you want test test_client requests.
If you want test blueprint as part of your application then look like no differences there are with application.
If you want test blueprint as extension then you can create test application with own blueprint and test it.
I have multiple APIs in one app and therefore multiple blueprints with url_prefix. I did not like that I have to prefix all paths when testing on of the APIs. I used the following class to wrap the test_client for blueprints:
class BlueprintClient():
def __init__(self, app_client, blueprint_url_prefix):
self.app_client = app_client
self.blueprint_url_prefix = blueprint_url_prefix.strip('/')
def _delegate(self, method, path, *args, **kwargs):
app_client_function = getattr(self.app_client, method)
prefixed_path = '/%s/%s' % (self.blueprint_url_prefix, path.lstrip('/'))
return app_client_function(prefixed_path, *args, **kwargs)
def get(self, *args, **kwargs):
return self._delegate('get', *args, **kwargs)
def post(self, *args, **kwargs):
return self._delegate('post', *args, **kwargs)
def put(self, *args, **kwargs):
return self._delegate('put', *args, **kwargs)
def delete(self, *args, **kwargs):
return self._delegate('delete', *args, **kwargs)
app_client = app.test_client()
api_client = BlueprintClient(app_client, '/api/v1')
api2_client = BlueprintClient(app_client, '/api/v2')

Categories

Resources