2

I have a Flask app where I need to store temporary files for different instances of the app inside a folder whose name is the time at which the app was opened. I have an index function as follows:

@app.route('/')
def index():
    session["now"] = time.time() * 100.0
    os.mkdir(str(session["now"]))

The above code makes a directory where the name of the directory is the the time when the app was opened. When I open the app on two separate tabs on Chrome, two different directories are made as expected.

However, there is one issue. I have a text area in my UI whose text needs to be saved in a text file in the directory created above. I have used a POST request to send the data. The following is the Flask-Python method:

@app.route('/rt3/', methods=['POST']) 
def fn3():
    x = request.get_json()
    data = x['textbox1']
    f = open("./" + str(session["now"]) + "/mytext.txt", "w")
    f.write(data)
    f.close()

The issue is this: No matter on which Chrome tab I click the "run" button on, the text file always gets saved in the directory of the session created later. Hence the same text file is getting overwritten again and again. Since I have used session variables, shouldn't it save in the directory of its own session? Why is session variable getting overwritten?

Nitesh Goyal
  • 137
  • 9

1 Answers1

3

This is perfectly expected. When running Flask locally, you most probably use one single process/instance which processes all your requests (multithreaded by default in newer Flask versions, so you can process multiple requests in parallel).

So be aware that you DON'T have different instances of the app but only one, no matter how many Chrome tabs you open. You'd need to use gunicorn or any other WSGI to spawn multiple instances and make them completely independent.

In your example, when you do session["now"] = time.time() * 100.0 you simply override every time the content of session["now"]. So, when accessing it from fn3(), you'll obviously get the last version (last override).

Now, if you REALLY want to achieve this, you'd need to pass an explicit "directory" identifier (not necessarily based on time) in every request so you can use it to create the directory and write files to it.

EDIT (20.02.2020)

After a bit more research, Flask session's are stored in a cookie on client side (see doc)

As a consequence, when you call session from 2 requests coming from 2 different tabs (from the same browser 'instance'), you access the same cookie, thus the same value. However, if you open another browser (or a new Chrome Incognito tab for example), you'll have a new cookie so you'll use a different session value.

Lucas
  • 1,833
  • 1
  • 18
  • 19
  • Thanks! Although this explains my question, I'm still confused regarding this: https://stackoverflow.com/questions/60259006/what-happens-when-you-open-a-flask-app-multiple-times-on-chrome?noredirect=1#comment106588984_60259006 If what you say is the case, why does the case in the above link occur? – Nitesh Goyal Feb 18 '20 at 09:23
  • 1
    Good point. Actually, in newer Flask version, `flask run` will be multithreaded (look at this SO [post](https://stackoverflow.com/questions/14814201/can-i-serve-multiple-clients-using-just-flask-app-run-as-standalone)), meaning your Flask app is able to handle multiple requests simultaneously. Side note: multithreaded means that all the process's memory is shared with its threads. – Lucas Feb 18 '20 at 09:40
  • Also, I did considering passing the directory identifier with each request. However the "now" variable is getting created at the backend. And the function already returns render_template(index.html). So, how would I let my frontend know the "now" value corresponding to that tab? – Nitesh Goyal Feb 18 '20 at 09:43
  • I still don't get what you're trying to achieve here. Could you be more specific? – Lucas Feb 18 '20 at 09:44
  • Well, you could simply pass the ```now``` variable as part of the [template's context](https://flask.palletsprojects.com/en/1.1.x/api/#flask.render_template). Then, you can use it in your frontend as you wish (make it part of a form, of a link, etc.). – Lucas Feb 18 '20 at 09:52
  • So basically I have an Angular application that I'm serving from Flask and am going to host it soon. Right now it supports single user and is running on local to store temporary files. For multiple users, however, I don't want the files to get overwritten when many users are using the app simultaneously. That's why I was creating directories with unique names to store the files. – Nitesh Goyal Feb 18 '20 at 10:04
  • Ok I get it now. Assuming your users will access your app from the root (```/```), I would generate a unique session id from the backend (you could reuse what you did based on the timestamp, but you still have a chance to have the same id if 2 users connect at the exact same time. Not very likely though). Then, you transmit that session id to the Angular app and you pass it when you send POST requests to ```/rt3```. That way, you make sure you write files in separated user directories. In case you want to be able to map users and directories, you might need to come up with another identifier – Lucas Feb 18 '20 at 10:11
  • Yes, that's exactly it. However the WSGI approach sounds better since I will be able to manage sessions properly that way. – Nitesh Goyal Feb 18 '20 at 10:22
  • So if Flask 1.0+ creates threads automatically, why is my session variable getting overwritten. Is memory(variables) common between all the threads? – Nitesh Goyal Feb 19 '20 at 07:06
  • I'd say in this context, the `session` is beyond multithreaded or not. It seems that the `session` object is stored server-side. Quick question: where does the `session` variable come from? – Lucas Feb 19 '20 at 08:03
  • Session variable is from the session library in flask – Nitesh Goyal Feb 19 '20 at 08:25
  • Okay so I ran one instance on Chrome and another on Incognito tab on Chrome and it worked. Can someone explain this please? – Nitesh Goyal Feb 20 '20 at 05:11
  • @Nitesh, well spotted. I edited my answer to reply to your question. – Lucas Feb 20 '20 at 08:32
  • Understood! Thanks a lot! – Nitesh Goyal Feb 20 '20 at 11:27
  • Can we force Chrome to generate new cookie for each tab? – Nitesh Goyal Feb 20 '20 at 11:41
  • I'm no expert on the subject but I'd guess you can't, otherwise that would go against the principle of using the same cookie across different transactions/requests. In your case, why don't you simply create the directory on-the-fly (based on the current timestamp) and write the user input in it? Do you absolutely need to know the directory name on frontend side? – Lucas Feb 20 '20 at 12:10