7

I have a flask view which is executed to load some information in a generator (I am using a generator so that I can continuously yield the progress - how much information has loaded). Here is what the view looks like:

@app.route("/progress", methods=['GET'])
def progress():
     gen = get_user_saved_tracks(session['token'], session['spotify_id'], session)
     return Response(gen, mimetype= 'text/event-stream')

def get_user_saved_tracks(token, id, session):
    #load information and keep yielding
    session['info'] = info

I would like to store the information which is loaded in a session variable inside the generator (This generator function is defined in a different file, outside the request context). But when I try to access the session variable, I get the following error:

RuntimeError: Working outside of request context.

So, is there a way to write the information to the session in this way? I am using FileSystemSessionInterface right now but willing to use redis sessions if that will solve my issue.

Update:

As suggested by Sraw, I tried the following changes:

from flask import current_app
app = current_app._get_current_object()
def get_user_saved_tracks(token, id,session):
    with app.app_context():
        session['info'] = info

But I still get the same error.

Update 2:

So, I need to use the actual app instance instead of using current_app (app object is being created in a different file - app.py)

from app import app
def get_user_saved_tracks(token, id,session):
    with app.app_context():
        session['info'] = info

On doing this, I get the same error:

RuntimeError: Working outside of request context.

Update 3:

Following is the code for get_user_saved_tracks:

def get_user_saved_tracks(token, id, session, j, service):
    tracks = []
    for i in range(100):
        a = service.current_user_saved_tracks(limit=50, offset=i*50)
        if len(a['items']) == 0:
            break
        yield "data:" + "lib" + str((float(i+1)/(j))*100) + "\n\n"
        time.sleep(0.5)
        tracks.extend(a)

    session['tracks'] = tracks
    session.modified = True
    yield "data:" + "close" + "\n\n"
Karan
  • 11,509
  • 8
  • 34
  • 38
  • https://stackoverflow.com/a/34123343/5588279 – Sraw Jul 04 '18 at 09:56
  • @Sraw : Thanks! Added update to the question. – Karan Jul 05 '18 at 10:02
  • Not, you cannot use `current_app` in this way. You should directly use the `app` instance. I mean, the `app` reference created by `Flask(__name__, xxxx)`. – Sraw Jul 05 '18 at 10:04
  • @Sraw Thanks! Added another update. – Karan Jul 05 '18 at 10:14
  • I think we need more information to help you. It seems you are not running that code during handling a request. – Sraw Jul 05 '18 at 10:18
  • Correct. When a request comes, I am returning a generator function as a event-stream response. I am trying to access the session inside that generator function. – Karan Jul 05 '18 at 10:19
  • Sorry I misunderstood you a little. So you are trying to access `session` after request context is poped. According to doc: http://flask.pocoo.org/docs/1.0/reqcontext/#manually-push-a-context, you cannot do this as at this time, `session` is not bound to current request. – Sraw Jul 05 '18 at 10:35
  • pls. show full code of *get_user_saved_track(...)* since I don't see it do any *yield* statement – hootnot Jul 05 '18 at 10:57

2 Answers2

0

In similar setup I get the same error as you do. But by using:

return Response(stream_with_context(....),
                mimetype='text/event-stream')

it is resolved. So in your case that would mean:

from flask import Flask, stream_with_context, request, Response, session
....

return Response(stream_with_context(get_user_saved_tracks(session['token'], session['spotify_id'], session),
                mimetype='text/event-stream')

EDIT:

A pure technical approach by using a callback function to get done what you want to accomplish. If this is something you want this way is a different question. You introduced 2 additional parameters j and service (?).

def get_user_saved_tracks(cb, token, id, j, service):
    #tracks = []
    for i in range(100):
        a = service.current_user_saved_tracks(limit=50, offset=i*50)
        if len(a['items']) == 0:
            break
        yield "data:" + "lib" + str((float(i+1)/(j))*100) + "\n\n"
        time.sleep(0.5)
        #tracks.extend(a)
        cb(a)  # <<--------

    #self.session['tracks'] = tracks
    #self.session.modified = True
    yield "data:" + "close" + "\n\n"

@app.route("/progress", methods=['GET'])
def progress():
    # add a callback function to update the session for the 'a' you
    # calculate in get_user_saved_tracks(). The callback updates the session
    session['tracks'] = []
    def cb(l):
        session['tracks'].extend(l)
    session.modied = True

    return Response(stream_with_context(get_user_saved_tracks(cb, session['token'], session['spotify_id']),  # j and service ?
            mimetype='text/event-stream')
hootnot
  • 1,005
  • 8
  • 13
  • Hi! Yes, the error is resolved. However, writing to session variable in get_user_saved_tracks still doesn't work (the written information is not available in the next request), even after doing session.modified=True – Karan Jul 05 '18 at 12:03
  • you should post the code of *get_user_saved_tracks(...)* – hootnot Jul 05 '18 at 12:08
  • Just to be sure: does you session mechanism work OK ? simply storing somehing like *session['name'] = "Santa"* and getting it from another url by: *session.get('name')* works OK? – hootnot Jul 05 '18 at 12:54
  • See also: https://stackoverflow.com/questions/34122949/working-outside-of-application-context-flask?noredirect=1&lq=1 – hootnot Jul 06 '18 at 10:50
0

I just solved our problem. Call this to save session anywhere.

def _session_save():
    from flask.globals import _request_ctx_stack
    app.session_interface.save_session(app, _request_ctx_stack.top.session, flask.Response())

I haven't checked it properly yet, so the final solution might look like this:

def _session_save(session):  # pass flask.session from context
    app.session_interface.save_session(app, session, flask.Response())
user14229198
  • 83
  • 1
  • 2