Access flask.g inside greenlet - python

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.

Related

Using Flask whit class - Python [duplicate]

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.

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.

When does Flask's route decorator execute?

When is decorator in Flask's route method executed? Specifically, I want to know when self.add_url_rule() is executed.
from flask import Flask
app = Flask(__name__)
#app.route("/")
def root_of_app():
load_root_of_app()
Is add_url_rule executed when the module containing root_of_app is first imported, or when root_of_app is first called by a web request?
Here is the source for the route function:
def route(self, rule, **options):
def decorator(f):
endpoint = options.pop('endpoint', None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
You can verify this yourself by adding print statements to the route decorator.
When route is called, it builds a decorator. That decorator is then applied to the view by calling it. Both these happen at import, because importing executes module-level code.
Using #app.route() registers the view, it is not deferred until the first request. The blueprint version of route is deferred until the blueprint is registered on the app, which also happens before the first request.

unit test for decorator in a python package by patching flask request

I have a python module security.py which defines a decorator authorized().
I want to test the decorator. The decorator will receive a flask request header.
The decorator is sth like this:
def authorized():
def _authorized(wrapped_func):
def _wrap(*args, **kwargs):
if 'token' not in request.headers:
LOG.warning("warning")
abort(401)
return None
return wrapped_func(*args, **kwargs)
return _wrap
return _authorized
I want to mock the flask request header using a #patch decorator.The test I wrote is sth like this:
#patch('security.request.headers', Mock(side_effect=lambda *args, **kwargs: MockHeaders({})))
def test_no_authorization_token_in_header(self):
#security.authorized()
def decorated_func(token='abc'):
return access_token
result = decorated_func()
self.assertEqual(result, None)
class MockHeaders(object):
def __init__(self, json_data):
self.json_data=json_data
but I always get the following error:
name = 'request'
def _lookup_req_object(name):
top = _request_ctx_stack.top
if top is None:
raise RuntimeError(_request_ctx_err_msg)
RuntimeError: Working outside of request context.
This typically means that you attempted to use functionality that needed
an active HTTP request. Consult the documentation on testing for
information about how to avoid this problem.
How should I do it right?
Mock the whole request object to avoid triggering the context lookup:
#patch('security.request')
and build up the mock from there:
#patch('security.request')
def test_no_authorization_token_in_header(self, mock_request):
mock_request.headers= {}
#security.authorized()
def decorated_func(token='abc'):
return token
self.assertRaises(Abort):
result = decorated_func()
Since a missing token results in an Abort exception being raised, you should explicitly test for that. Note that the request.headers attribute is not called anywhere, so side_effect or return_value attributes don't apply here.
I ignored MockHeaders altogether; your decorator is not using json_data and your implementation is lacking a __contains__ method, so in tests wouldn't work on that. A plain dictionary suffices for the current code-under-test.
Side note: authorized is a decorator factory, but it doesn't take any parameters. It'd be clearer if you didn't use a factory there at all. You should also use functools.wraps() to ensure that any metadata other decorators add are properly propagated:
from functools import wraps
def authorized(wrapped_func):
#wraps(wrapped_func)
def _wrap(*args, **kwargs):
if 'token' not in request.headers:
LOG.warning("warning")
abort(401)
return None
return wrapped_func(*args, **kwargs)
return _wrap
then use the decorator directly (so no call):
#security.authorized
def decorated_func(token='abc'):
return access_token

Equivalent of a requests Session object when unit testing a Flask RESTful API using a test_client

Building on a previous question of mine (How to unit test a Flask RESTful API), I'm trying to test a Flask RESTful API using a test_client without the app running, rather than using requests while the app is running.
As a simple example, I have an API (flaskapi2.py) with a get function which uses a login decorator:
import flask
import flask_restful
from functools import wraps
app = flask.Flask(__name__)
api = flask_restful.Api(app)
AUTH_TOKEN = "foobar"
def login_required(f):
#wraps(f)
def decorated_function(*args, **kwargs):
if flask.request.headers.get("auth_token") == AUTH_TOKEN:
return f(*args, **kwargs)
else:
return flask.abort(401) # Return HTTP status code for 'Unauthorized'
return decorated_function
class HelloWorld(flask_restful.Resource):
#login_required
def get(self):
return {'hello': 'world'}
api.add_resource(HelloWorld, '/')
if __name__ == "__main__":
app.run(debug=True)
With the app running, I run these unit tests (test_flaskapi2.py in the same directory):
import unittest
import flaskapi2
import requests
import json
AUTH_TOKEN = "foobar"
class TestFlaskApiUsingRequests(unittest.TestCase):
def setUp(self):
self.session = requests.Session()
self.session.headers.update({'auth_token': AUTH_TOKEN})
def test_hello_world(self):
response = self.session.get('http://localhost:5000')
self.assertEqual(response.json(), {'hello': 'world'})
def test_hello_world_does_not_work_without_login(self):
response = requests.get('http://localhost:5000') # Make an unauthorized GET request
self.assertEqual(response.status_code, 401) # The HTTP status code received should be 401 'Unauthorized'
class TestFlaskApi(unittest.TestCase):
def setUp(self):
self.app = flaskapi2.app.test_client()
def test_hello_world(self):
response = self.app.get('/', headers={'auth_token': AUTH_TOKEN})
self.assertEqual(json.loads(response.get_data()), {'hello': 'world'})
if __name__ == "__main__":
unittest.main()
All the tests pass. Note that the tests in TestFlaskApiUsingRequests require the app to be running, whereas those in TestFlaskApi don't.
My problem is that I haven't been able to find the equivalent of requests' Session object to 'standardize' the request headers when using the test_client. This means that if I were to write more tests, I would have to pass the headers keyword argument to each request individually, which is not DRY.
How can I make a 'session' for the test_client? (It seems like this can be done with Werkzeug's EnvironBuilder but I wasn't able to quickly figure out how to do this).
In order to keep the code DRY when adding more tests, instead of using EnvironBuilder directly I wrote a decorator authorized which adds the required headers keyword argument to any function call. Then, in the test I call authorized(self.app.get) instead of self.app.get:
def authorized(function):
def wrap_function(*args, **kwargs):
kwargs['headers'] = {'auth_token': AUTH_TOKEN}
return function(*args, **kwargs)
return wrap_function
class TestFlaskApi(unittest.TestCase):
def setUp(self):
self.app = flaskapi2.app.test_client()
def test_hello_world(self):
response = self.app.get('/', headers={'auth_token': AUTH_TOKEN})
self.assertEqual(json.loads(response.get_data()), {'hello': 'world'})
def test_hello_world_authorized(self): # Same as the previous test but using a decorator
response = authorized(self.app.get)('/')
self.assertEqual(json.loads(response.get_data()), {'hello': 'world'})
The tests all pass as desired. This answer was inspired by Python decorating functions before call, How can I pass a variable in a decorator to function's argument in a decorated function?, and Flask and Werkzeug: Testing a post request with custom headers.
Update
The definition of the authorized wrapper can be made more succinct using functools.partial:
from functools import partial
def authorized(function):
return partial(function, headers={'auth_token': AUTH_TOKEN})

Categories

Resources