96

I am trying to save a cache dictionary in my flask application.

As far as I understand it, the Application Context, in particular the flask.g object should be used for this.

Setup:

import flask as f app = f.Flask(__name__) 

Now if I do:

with app.app_context(): f.g.foo = "bar" print f.g.foo 

It prints bar.

Continuing with the following:

with app.app_context(): print f.g.foo AttributeError: '_AppCtxGlobals' object has no attribute 'foo' 

I don’t understand it and the docs are not helping at all. If I read them correctly the state should have been preserved.

Another idea I had was to simply use module-wide variables:

cache = {} def some_function(): cache['foo'] = "bar" 

But it seems like these get reset with every request.

How to do this correctly?

Edit: Flask 10.1

3
  • 13
    No, thanks for the link, but it’s such a simple task that I’d like to keep the dependenies low atm. Commented Oct 9, 2013 at 16:24
  • I think I went with a browser cookie in the end. But you are of course welcome to post a solution once you find one. Commented Jan 3, 2014 at 8:23
  • Session might be solution, but session is kept per user so it has it's limitations. Other option is memcache. It's simple key->value storage that is easy to configure and is shared among all threads and processes. Sometimes however (like in my case) it's a bit too much complication, so I am thinking to just use dictionary in global scope. Risk is data integrity (not a factor for me) but same dict will be shared between some processes. Commented Mar 30, 2016 at 12:49

3 Answers 3

89

Based on your question, I think you're confused about the definition of "global".

In a stock Flask setup, you have a Flask server with multiple threads and potentially multiple processes handling requests. Suppose you had a stock global variable like "itemlist = []", and you wanted to keep adding to it in every request - say, every time someone made a POST request to an endpoint. This is totally possible in theory and practice. It's also a really bad idea.

The problem is that you can't easily control which threads and processes "win" - the list could up in a really wonky order, or get corrupted entirely. So now you need to talk about locks, mutexs, and other primitives. This is hard and annoying.

You should keep the webserver itself as stateless as possible. Each request should be totally independent and not share any state in the server. Instead, use a database or caching layer which will handle the state for you. This seems more complicated but is actually simpler in practice. Check out SQLite for example ; it's pretty simple.

To address the 'flask.g' object, that is a global object on a per request basis.

http://flask.pocoo.org/docs/api/#flask.g

It's "wiped clean" between requests and cannot be used to share state between them.

Sign up to request clarification or add additional context in comments.

13 Comments

I understand the database approach, but not familiar with caching layers. Could you recommend some caching layer libraries/frameworks?
This is no longer true. Starting with flask.10 flask.g is tied to the application context and not the request context. flask.pocoo.org/docs/0.12/api/#flask.g
Had to -1 this answer. There are instances when you want global objects that are not refreshed between the request, for example, I want to load path of some files in memory and not modify it at all. If I do want to modify it I will restart the server. This answer doesn't help.
Yeah, i'm looking at loading a large pre-trained model for inference and the load time is significant so can't be stateless.
@CpILL Any luck yet? Same use case (unless your model is dynamic?). Documentation pretty incoherent and most SO questions on the topic get shut down as being dups..
|
26

I've done something similar to your "module-wide variables" idea that I use in a flask server that I use to integrate two pieces of software where I know I will only ever have one simultaneous "user" (being the sender software).

My app.py looks like this:

from flask import Flask from flask.json import jsonify app = Flask(__name__) cache = {} @app.route("/create") def create(): cache['foo'] = 0 return jsonify(cache['foo']) @app.route("/increment") def increment(): cache['foo'] = cache['foo'] + 1 return jsonify(cache['foo']) @app.route("/read") def read(): return jsonify(cache['foo']) if __name__ == '__main__': app.run() 

You can test it like this:

import requests print(requests.get('http://127.0.0.1:5000/create').json()) print(requests.get('http://127.0.0.1:5000/increment').json()) print(requests.get('http://127.0.0.1:5000/increment').json()) print(requests.get('http://127.0.0.1:5000/read').json()) print(requests.get('http://127.0.0.1:5000/increment').json()) print(requests.get('http://127.0.0.1:5000/create').json()) print(requests.get('http://127.0.0.1:5000/read').json()) 

Outputs:

0 1 2 2 3 0 0 

Use with caution as I expect this to not behave in a proper multi user web server environment.

6 Comments

This will work in situations where you have a SINGLE uwsgi worker globablly. You also have to worry about multiple access to the variable, so you may have to implement locks or similar depending on the use. It is another tool in the bag though.
Watch out for this type of test... The test Flask server is single-threaded and it's a single process, you will not be able to find multi-threading-related issues with this test.
I'm new to Flask, is there a reason why this dictionary method persists between requests but creating a variable such as an INT doesn't?
@MajorMajor there is no difference to declaring an int and a dictionary in regard to its persistence. If they persist or not depends on where they are declared. If you've got further questions regarding this I encourage you to ask a new question.
I found that str don't persist. Be aware!!
|
12

This line

with app.app_context(): f.g.foo = "bar" 

Since you are using the "with" keyword, once this loop is executed, it calls the __exit__ method of the AppContext class. See this. So the 'foo' is popped out once done. Thats why you don't have it available again. You can instead try:

ctx = app.app_context() f.g.foo = 'bar' ctx.push() 

Until you call the following, g.foo should be available

ctx.pop() 

I am howver not sure if you want to use this for the purpose of caching.

2 Comments

What should I use then? I thought this was the means to store global values in an app?
The application context is created and destroyed as necessary. It never moves between threads and it will not be shared between requests. (flask.pocoo.org/docs/appcontext)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.