Skip to content

Conversation

@sfc-gh-aamadhavan
Copy link
Contributor

@sfc-gh-aamadhavan sfc-gh-aamadhavan commented Nov 5, 2025

Describe your changes

Adds support for callable functions in st.download_button to enable deferred/lazy data generation. Data is now generated only when the user clicks the download button, not on every script run.

API Addition:

# Before: eager evaluation (runs every time)
st.download_button("Download", data=generate_csv())

# After: lazy evaluation (runs only on click)
st.download_button("Download", data=generate_csv)  # Pass function, not call

Implementation:

  • Backend: Extended DownloadButtonDataType to accept Callable[[], DownloadButtonDataType]
  • Protocol: Added DeferredFileRequest (BackMsg) and DeferredFileResponse (ForwardMsg) messages
  • MediaFileManager: New add_deferred() registers callables, execute_deferred() runs them on-demand
  • Frontend: DownloadButton requests file generation via BackMsg, displays loading/error states

Why it works:

  • Callables stored in-memory per session, executed outside lock to prevent blocking
  • Uses existing storage backends (MemoryMediaFileStorage/SnowflakeMediaFileStorage) after execution
  • Thread-safe with proper locking, cleanup after execution prevents memory leaks
  • Backward compatible: non-callable data follows existing eager path unchanged

GitHub issues

Closes #5053

Testing Plan

Unit Tests:

  • ✅ 27 new backend tests (callable detection, execution, error handling, all return types)
  • ✅ 7 new frontend tests (loading states, errors, timeout, success flow)
  • ✅ All 54 tests passing

E2E Tests:

  • ✅ 8 new playwright tests covering CSV, binary, lambda, errors, large files

Manual Testing:

  • Run streamlit run demo_deferred_download.py to verify interactive behavior
  • Test regular downloads still work (backward compatibility)
  • Verify error messages display correctly on callable failure

Contribution License Agreement

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

@snyk-io
Copy link
Contributor

snyk-io bot commented Nov 5, 2025

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@github-actions
Copy link
Contributor

github-actions bot commented Nov 5, 2025

✅ PR preview is ready!

Name Link
📦 Wheel file https://core-previews.s3-us-west-2.amazonaws.com/pr-12942/streamlit-1.51.0-py3-none-any.whl
📦 @streamlit/component-v2-lib Download from artifacts
🕹️ Preview app pr-12942.streamlit.app (☁️ Deploy here if not accessible)

@sfc-gh-aamadhavan sfc-gh-aamadhavan added change:feature PR contains new feature or enhancement implementation impact:users PR changes affect end users security-assessment-completed Security assessment has been completed for PR labels Nov 5, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Nov 5, 2025

📉 Frontend coverage change detected

The frontend unit test (vitest) coverage has decreased by 0.0400%

  • Current PR: 86.0000% (50733 lines, 7102 missed)
  • Latest develop: 86.0400% (50577 lines, 7057 missed)

💡 Consider adding more unit tests to maintain or improve coverage.

📊 View detailed coverage comparison

@sfc-gh-aamadhavan sfc-gh-aamadhavan force-pushed the aakarshm/download_2 branch 2 times, most recently from 07190a2 to 72da76d Compare November 6, 2025 15:23
@sfc-gh-aamadhavan sfc-gh-aamadhavan marked this pull request as ready for review November 6, 2025 16:27
@sfc-gh-aamadhavan sfc-gh-aamadhavan requested a review from a team as a code owner November 6, 2025 16:27
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This pull request adds support for deferred file downloads in Streamlit download buttons. Instead of immediately generating download data when rendering the button, developers can now pass a callable that executes only when the user clicks the download button.

Key Changes:

  • Added protobuf messages for deferred file request/response communication between frontend and backend
  • Implemented deferred callable storage and execution in MediaFileManager
  • Updated download button to accept callables and handle deferred execution flow
  • Added frontend support for requesting deferred files and displaying loading/error states
  • Created comprehensive unit and e2e tests

Reviewed Changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
proto/streamlit/proto/ForwardMsg.proto Added DeferredFileResponse message for server→client responses
proto/streamlit/proto/BackMsg.proto Added DeferredFileRequest message for client→server requests
proto/streamlit/proto/DownloadButton.proto Added fields for deferred file ID and deferred flag
lib/streamlit/runtime/media_file_manager.py Implemented deferred callable storage, execution, and cleanup logic
lib/streamlit/runtime/app_session.py Added handler for deferred file requests
lib/streamlit/elements/widgets/button.py Updated download button to detect and register callables
lib/tests/streamlit/runtime/media_file_manager_test.py Added comprehensive unit tests for deferred functionality
lib/tests/streamlit/runtime/app_session_test.py Added tests for deferred request handling in AppSession
lib/tests/streamlit/elements/download_button_test.py Added tests for callable detection in download buttons
frontend/app/src/App.tsx Implemented request/response handling for deferred files
frontend/app/src/components/AppView/AppView.tsx Passed through requestDeferredFile prop
frontend/lib/src/components/core/Block/utils.ts Added requestDeferredFile to BaseBlockProps
frontend/lib/src/components/core/Block/ElementNodeRenderer.tsx Passed requestDeferredFile to widgets
frontend/lib/src/components/widgets/DownloadButton/DownloadButton.tsx Added loading state, error display, and deferred download handling
frontend/lib/src/components/widgets/DownloadButton/DownloadButton.test.tsx Added comprehensive tests for deferred download scenarios
e2e_playwright/st_download_button_deferred.py Test app demonstrating deferred download functionality
e2e_playwright/st_download_button_deferred_test.py E2E tests for deferred downloads

@sfc-gh-aamadhavan sfc-gh-aamadhavan added the flaky-verify If applied to PR will run flaky test verification workflow label Nov 7, 2025
@sfc-gh-aamadhavan sfc-gh-aamadhavan added flaky-verify If applied to PR will run flaky test verification workflow and removed flaky-verify If applied to PR will run flaky test verification workflow labels Nov 7, 2025
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggestion: We have a test utility renderWithContexts in frontend/lib/src/test_util.tsx that can be updated with the DownloadsContext and used in this test file to help manage the context value

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great, updated.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think so, its okay if the metric is going down a bit as long as we have adequate coverage where it matters

if (!isDeferred) {
// Since we use a hidden link to download, we can't use the onerror event
// to catch src url load errors. Catch with direct check instead.
void endpoints.checkSourceUrlResponse(downloadUrl, "Download Button")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggestion: We escalate source load errors for metrics through checkSourceUrlResponse - I would think even if the download is deferred we should probably still check/report - will leave to your discretion whether it should happen here or in the error catch below

Comment on lines +49 to +65
export const StyledErrorMessage = styled.small(({ theme }) => ({
color: theme.colors.redTextColor,
fontSize: theme.fontSizes.sm,
marginTop: theme.spacing.twoXS,
display: "block",
}))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Question: did we test that this renders properly in the different button layout scenarios? (ex: when explicit width is specified on the button, "content", & "stretch")

@sfc-gh-aamadhavan sfc-gh-aamadhavan added flaky-verify If applied to PR will run flaky test verification workflow and removed flaky-verify If applied to PR will run flaky test verification workflow labels Nov 12, 2025
@sfc-gh-aamadhavan sfc-gh-aamadhavan added flaky-verify If applied to PR will run flaky test verification workflow and removed flaky-verify If applied to PR will run flaky test verification workflow labels Nov 13, 2025
@sfc-gh-aamadhavan sfc-gh-aamadhavan merged commit c744c7f into develop Nov 13, 2025
94 checks passed
@sfc-gh-aamadhavan sfc-gh-aamadhavan deleted the aakarshm/download_2 branch November 13, 2025 23:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:feature PR contains new feature or enhancement implementation flaky-verify If applied to PR will run flaky test verification workflow impact:users PR changes affect end users security-assessment-completed Security assessment has been completed for PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Defered data for download button

5 participants