Search code examples
pythonflaskgeventgreenlets

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.


Solution

  • 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.