Skip to content

Conversation

@karriebear
Copy link
Contributor

@karriebear karriebear commented Jan 13, 2021

Issue: #2561

Description:
Unclear why the deploy button caused a regression, potentially because of new message type. Looking at the changes for the deploy button, it looks correct. Instead cleaned up areas of file uploader to become more stable.

Changes include

  1. on_files_updated was being signaled more than required. Minimized requests to update.
  2. FileUploadRequestHandler POST and DELETE will handle updating the file count instead of a separate request. On file upload, pass in the total number of files that should be uploaded for the session/widget
  3. Always remove file on delete in parallel to the request. We remove it anyways on error.

Tests added

  1. Upload and delete for single and multiple file uploader
  2. Replace file on upload in single file uploader

Test cases
[ ] Works with multiple sessions
[ ] Upload single file
[ ] Upload another single file replaces initial file
[ ] Upload multiple files
[ ] Delete file(s)


Contribution License Agreement

By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.

@karriebear karriebear requested a review from a team January 13, 2021 17:48
Copy link
Contributor

@tconkling tconkling left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without tests, it's hard to understand what the broken behavior currently is, and how this fixes it.

If I understand correctly, there are circumstances where UploadedFileManager.on_files_updated is being emitted too frequently, and this PR fixes that issue?

(What's the harm caused the the signal being emitted too frequently?)

Is it possible to add a test that fails on the current, broken behavior and succeeds with this PR, so that we can prevent a regression?

Same with FileUploader.tsx - is it possible to add a test that validates the setState is called the expected number of times (or whatever) for file removals?

@karriebear
Copy link
Contributor Author

That's kind of the difficult part. We do have a test that was failing but it seems like the regression itself was flaky (#2567). And tbh I don't quite get how all this relates to the deploy button change. However as I started looking into the regression I noticed that we were making a lot of calls that wasn't really needed. When I optimized all that it seems like I can't reproduce the issue anymore 🤷‍♀️

I'll add in some unit tests that specifically tests the replace functionality and a unit test to make sure removeFile calls updateFileCount with the right params

@karriebear karriebear force-pushed the file-uploader-deploy-regression branch 2 times, most recently from 7bee202 to ca46e77 Compare January 15, 2021 22:11
@karriebear karriebear force-pushed the file-uploader-deploy-regression branch from ca46e77 to 7fe18bf Compare January 19, 2021 06:16
@karriebear karriebear force-pushed the file-uploader-deploy-regression branch from 6772958 to 58dcb34 Compare January 19, 2021 07:13
Copy link
Contributor

@tconkling tconkling left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, I'm being a total pain in the ass, and I apologize :( - I'm just finding a lot of little things to be surprising or confusing. I've left a number of requests for comments.

I'm also wondering if there's a cleaner way to track "number of uploaded files":

  • In Python, why are we tracking UploadedFileManager._file_counts_by_id, and how does that number relate to the lengths of the UploadedFileManager._files_by_id lists?
  • In the frontend, why are we tracking FileUploader.state.validFiles and how does that number relate to the length of the FileUploader.state.files array?

In general, I'm wary of using multiple independent bits of state to track a single thing, because they can get out of sync, and it makes future changes more tricky to reason about. Is there a more atomic way of tracking these things?

Comment on lines 164 to 168
this.props.uploadClient
.uploadFiles(
this.props.element.id,
[file],
[updatedFile],
this.state.validFiles,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't your doing, but I find the updatedFile vs file thing happening here to be really confusing. It looks like this.handleFile is returning a new object, but it's actually just mutating its passed-in object, which means that file and updatedFile are the same thing. (I wish that "handleFile" just didn't return the passed-in file.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was my doing :(

Comment on lines 265 to 274
public removeFile = (fileId: string): void => {
this.setState(state => {
const filteredFiles = state.files.filter(file => file.id !== fileId)

return {
status: filteredFiles.length ? FileStatus.UPLOADED : FileStatus.READY,
errorMessage: undefined,
files: filteredFiles,
validFiles: state.validFiles - 1,
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if this function is called with a non-existent fileId? (Or with a fileId that's not "valid"). Is that possible? It looks like validFiles would be improperly decremented. Is there a sanity check we can do here, to throw an error if someone down the road calls the function improperly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, we can do something like state.validFiles - (state.files.length - filteredFiles.length)

Comment on lines 152 to 166
try:
total_files = self._require_arg(args, "totalFiles")
self._file_mgr.update_file_count(
session_id=session_id,
widget_id=widget_id,
file_count=int(total_files),
)

except Exception as e:
self._file_mgr.update_file_count(
session_id=session_id,
widget_id=widget_id,
file_count=1,
)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this try/except handling errors thrown by _require_arg or update_file_count or both? Why would we want to swallow an error thrown here? (My assumption from glancing at the code is that it's to catch a missing "totalFiles" argument - if that's the case, why would this argument be missing? Is it optional from the client?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It handles the errors from _require_arg. It's not optional from what we have currently on the client side but I left it open so that it could work with in the future with totalFiles as an optional param. We can make it an optional parameter on the client side if that's preferred.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. How about this slight tweak:

try:
  total_files = int(self._require_arg(args, "totalFiles"))
except:
  total_files = 1

self._file_mgr.update_file_count(
    session_id=session_id,
    widget_id=widget_id,
    file_count=total_files
)

This way we don't erroneously catch exceptions thrown by update_file_count

@karriebear karriebear force-pushed the file-uploader-deploy-regression branch 2 times, most recently from dc395c2 to 7063301 Compare January 20, 2021 17:54
@karriebear karriebear force-pushed the file-uploader-deploy-regression branch from 7063301 to dd8ad16 Compare January 20, 2021 22:49
Copy link
Contributor

@tconkling tconkling left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! The additional comments you added make a huge difference.

Comment on lines +138 to +142

// Can delete
cy.get("[data-testid='fileDeleteBtn'] button")
.first()
.click();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this missing a check that the file's actually been deleted, after the button is clicked?

Comment on lines 152 to 166
try:
total_files = self._require_arg(args, "totalFiles")
self._file_mgr.update_file_count(
session_id=session_id,
widget_id=widget_id,
file_count=int(total_files),
)

except Exception as e:
self._file_mgr.update_file_count(
session_id=session_id,
widget_id=widget_id,
file_count=1,
)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. How about this slight tweak:

try:
  total_files = int(self._require_arg(args, "totalFiles"))
except:
  total_files = 1

self._file_mgr.update_file_count(
    session_id=session_id,
    widget_id=widget_id,
    file_count=total_files
)

This way we don't erroneously catch exceptions thrown by update_file_count

@asaini
Copy link

asaini commented Feb 2, 2021

LGTM. Verified functionality with multiple CSV files.

Normal file uploader functionality also looks good!

@karriebear karriebear merged commit 8b8a1a2 into streamlit:develop Feb 2, 2021
tconkling added a commit to tconkling/streamlit that referenced this pull request Feb 2, 2021
* develop:
  Fix svg rendering in st.image() (streamlit#2666)
  Indicate widgets need to have labels (streamlit#2688)
  Clean up file update calls (streamlit#2597)
  Revert "🔧 "Run on save" now defaults to true (streamlit#2641)" (streamlit#2694)
tconkling added a commit to tconkling/streamlit that referenced this pull request Feb 2, 2021
* develop:
  Fix svg rendering in st.image() (streamlit#2666)
  Indicate widgets need to have labels (streamlit#2688)
  Clean up file update calls (streamlit#2597)
  Revert "🔧 "Run on save" now defaults to true (streamlit#2641)" (streamlit#2694)
karriebear added a commit that referenced this pull request Feb 3, 2021
* Clean up file update calls

* Add more tests

* Move updating of file count to upload file request handler

* Fix tests

* Fix deleting

* Address PR comments

* PR cleanup

* Fix test
tconkling added a commit that referenced this pull request Feb 8, 2021
* develop:
  Autofocus "clear cache" button (#2739)
  Fix checkbox spacing (#2738)
  Fix progress bar alignment (#2734)
  Fix select menu not autoselecting first item (#2732)
  Show user command line in disconnect dialog (#2735)
  Up version to 0.76.0
  Update change log
  Improve usage warning wording and formatting
  Finally remove beta versions of set_page_config and color_picker (#2711)
  Finally remove beta versions of set_page_config and color_picker (#2711)
  Clean up file update calls (#2597)
tconkling added a commit to tconkling/streamlit that referenced this pull request Feb 11, 2021
* develop:
  Slider thumb values are always visible (streamlit#2724)
  Update component-template submodule to latest (streamlit#2767)
  `allow_multiple_files` -> `accept_multiple_files` (streamlit#2761)
  Autofocus "clear cache" button (streamlit#2739)
  Fix checkbox spacing (streamlit#2738)
  Fix progress bar alignment (streamlit#2734)
  Fix select menu not autoselecting first item (streamlit#2732)
  Show user command line in disconnect dialog (streamlit#2735)
  Up version to 0.76.0
  Update change log
  Improve usage warning wording and formatting
  Finally remove beta versions of set_page_config and color_picker (streamlit#2711)
  Finally remove beta versions of set_page_config and color_picker (streamlit#2711)
  Clean up file update calls (streamlit#2597)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants