Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

file_uploader does not repopulate the UploadedFile stream when the app is rerun #2213

Closed
durandg12 opened this issue Oct 16, 2020 · 5 comments
Labels

Comments

@durandg12
Copy link

Summary

I use st.file_uploader to allow the user to submit their own data. Then I do an action based on this data and other user-provided parameters (coming from an st.slider for instance). Re-running the app produces an error. This includes when the parameters are changed (for example by moving the st.slider). After investigating, I discovered that it is because, after the first reading of the UploadedFile output by st.file_uploader, the UploadedFile stays empty. It is not re-populated by the uploaded file in spite of the app re-running.

Steps to reproduce

  1. Save the following JSON as "test.json":
{"a": 3, "b": 2}
  1. Save the following piece of code as "my_app.py" (or any other name):
import json
import streamlit as st

st.title("Let's add a number to the elements of a json")

uploaded_file = st.sidebar.file_uploader(
    "Upload your json", type=["json"]
)

to_add = st.sidebar.slider(
    "Choose a number", min_value=1, max_value=5, value=3
)

st.markdown("The result is:")

default_file = open("test.json", "r")
if uploaded_file is None:
    dico = json.load(default_file)
else:
    dico = json.load(uploaded_file)
st.write({k: v + to_add for k, v in dico.items()})

default_file.close()
  1. Run streamlit run my_app.py
  2. In the app, upload test.json in the file uploader. You should see that:

Capture d’écran 2020-10-16 à 01 28 35

5. Move the slider, here is the bug, you should see that:

Capture d’écran 2020-10-16 à 01 28 41

6. At this point, re-uploading works, but the user shouldn't have to re-upload the file after each slider change or each plain re-run. Especially since, as we see in the screenshot above, the widget still displays the informations of the uploaded file. Here is how it looks after re-uploading:

Capture d’écran 2020-10-16 à 01 32 20

Expected behavior:

We shouldn't ever get to the second screenshot. We should pass from the first to the third simply by moving the slider and without re-uploading the file every time.

Actual behavior:

As explained above, after the first run the stream is empty and not reset when the app is re-run. This is not specific to the JSON format, although the error message displayed above is. I have run the same test with a CSV file and pandas.read_csv. The underlying cause is the same: an empty stream.

Note that I have also tried to make an auxiliary function that encapsulates the dico = json.load(uploaded_file) part and which is decorated by st.cache. It still doesn't work.

Is this a regression?

I don't know, it's the first time I use st.file_uploader.

Debug info

I have two different settings.

An AWS distant machine (I forward the port to my local machine):

  • Streamlit version: 0.69.1
  • Python version: 3.7.6
  • Using Conda
  • OS version: I don't know

My local machine:

  • Streamlit version: 0.68.0
  • Python version: 3.8.5
  • Not using Conda
  • OS version: macOS 10.13.6
  • Browser version: Firefox 81.0.1

Additional information

Side note on the documentation of st.file_uploader: at the some point it says to use StringIO(uploaded_file.decode("utf-8")) but this doesn't work and raises AttributeError: 'UploadedFile' object has no attribute 'decode'. Using StringIO(uploaded_file.read().decode("utf-8")) does work, though.

@durandg12 durandg12 added type:bug Something isn't working status:needs-triage Has not been triaged by the Streamlit team labels Oct 16, 2020
@nthmost nthmost added feature:st.file_uploader and removed status:needs-triage Has not been triaged by the Streamlit team labels Oct 16, 2020
@FrankKr
Copy link

FrankKr commented Oct 20, 2020

Exalate commented: FrankKr commented: Update:
Actually, it appears the UploadedFile stream is available, but the cursor is at the end of the stream after the first evaluation.
A work-around for me was to reset the cursor by including
uploaded_file.seek(0)
after the check if uploaded_file is None. Would very much prefer this workaround to become obsolete.

in your example:

default_file = open("test.json", "r")
if uploaded_file is None:
    uploaded_file.seek(0)
    dico = json.load(default_file)
else:
    dico = json.load(uploaded_file)

st.write({k: v + to_add for k, v in dico.items()})

default_file.close()

original:
experiencing the same issue after upgrading streamlit from version 0.64.0 to 0.69.0

@vik-s
Copy link

vik-s commented Oct 20, 2020

Exalate commented: vik-s commented: Update:
Ok, I get it now. The NoneType object didn't have the seek() method (duh!). Just resetting the cursor to the start every time fixes the issue.

So, something like:

if uploaded_file is not None:
    uploaded_file.seek(0)
    dico = json.load(uploaded_file)
else:
    dico = json.load(default_file)

Original:
I was facing the same issue with file_uploader. Things seemed to be working okay in 0.67.

Thanks for your workaround. When I tried it, I get:

AttributeError: 'NoneType' object has no attribute 'seek'

Is there something specific I must do to access that method?

edit: typo

@FrankKr
Copy link

FrankKr commented Oct 21, 2020

you're right, I posted a little too fast and my suggestion contained an error. I corrected it for future reads. Good luck!

@durandg12
Copy link
Author

Thanks for finding this much better workaround, and for finiding the real underlying reason of the bug.

@karriebear
Copy link
Contributor

Closing this issue as a duplicate of #2235. A fix for this should be coming in the next release. Please track #2235 for updates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants