Allow dynamic changes to multiselect options when key is provided#13448
Allow dynamic changes to multiselect options when key is provided#13448lukasmasuch merged 13 commits intodevelopfrom
key is provided#13448Conversation
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
✅ PR preview is ready!
|
…multiselect-options
key is provided
There was a problem hiding this comment.
Pull request overview
This pull request enables dynamic changes to st.multiselect options when a key is provided, maintaining widget identity and preserving valid selections while filtering out invalid ones when the options list changes.
Key changes:
- Removed
optionsandformat_funcfrom the identity computation when a key is provided, allowing these to change without resetting widget state - Added validation logic to filter multiselect values against current options, preserving valid selections and removing invalid ones
- Refactored selectbox validation logic into reusable utility functions
Reviewed changes
Copilot reviewed 7 out of 10 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
lib/streamlit/elements/widgets/multiselect.py |
Modified identity computation to exclude options and format_func from whitelisted params; added validation logic to filter invalid selections when options change |
lib/streamlit/elements/widgets/selectbox.py |
Refactored validation logic to use new validate_value_against_options utility function |
lib/streamlit/elements/lib/options_selector_utils.py |
Added two new validation functions: validate_value_against_options for selectbox and validate_multiselect_value_against_options for multiselect |
lib/tests/streamlit/elements/multiselect_test.py |
Updated stable ID tests to reflect new whitelisted params; added three new AppTest tests covering option expansion, filtering, and complete removal scenarios |
lib/tests/streamlit/elements/lib/options_selector_utils_test.py |
Added comprehensive parameterized tests for both new validation utility functions |
e2e_playwright/st_multiselect_test.py |
Enhanced test_dynamic_multiselect_props to test both filtering and preservation behaviors with improved documentation |
e2e_playwright/st_multiselect.py |
Updated test app to change both options and format_func between initial and updated states; changed accept_new_options to False consistently to avoid identity changes |
|
@cursor review |
SummaryThis PR enables dynamic changes to
The implementation introduces two new validation utility functions that are shared between Key Changes:
Code QualityStrengths:
Minor Observations:
Test CoverageUnit Tests:
E2E Tests:
Test Quality Notes:
Backwards Compatibility✅ This change is backwards compatible.
Migration Note: Users relying on the previous behavior where changing options would reset state might see different behavior now - selections that remain valid will be preserved. This is the intended fix for issues #7855 and #11277. Security & Risk✅ No security concerns identified.
Risk Assessment:
RecommendationsThe PR is well-implemented and ready for merge. A few minor suggestions for consideration:
VerdictAPPROVED: This PR provides a well-tested, clean implementation that fixes a real user pain point (#7855, #11277). The code follows existing patterns, has comprehensive test coverage, and is backwards compatible. The validation logic is correctly shared between selectbox and multiselect widgets. This is an automated AI review. Please verify the feedback and use your judgment. |
| ) | ||
|
|
||
|
|
||
| def validate_value_against_options( |
There was a problem hiding this comment.
suggestion (non-blocking): Because these functions validate and also have a possible side-effect of changing state, consider either:
- Giving them either a name that makes the potential for side-effects clearer.
- Refactoring this into 2 different functions and have the widget layer compose the functions so that we can keep validation logic pure.
There was a problem hiding this comment.
Renamed the function to make it a bit more explicit
📉 Frontend coverage change detectedThe frontend unit test (vitest) coverage has decreased by 0.0400%
💡 Consider adding more unit tests to maintain or improve coverage. |
…#13646) When using st.multiselect() with custom class objects and a format_func, selections would be cleared after each script rerun in v1.53.0. This was a regression introduced by PR #13448. The issue occurred because register_widget() deepcopies widget values, and the validation logic used == comparison which falls back to identity checks for objects without __eq__. Deepcopied instances fail identity comparison, causing valid selections to be filtered out. The fix changes the validation to use format_func() for comparisons instead, comparing by formatted string representation. This is more robust and works correctly with custom objects regardless of __eq__ implementation. The solution also includes error handling for edge cases where format_func fails on incompatible value types. - Modified validate_and_sync_multiselect_value_with_options() to accept and use format_func parameter - Added comprehensive unit tests for custom objects with and without __eq__ - Added edge case test for format_func failures - Added E2E test to verify selections persist across script reruns Co-Authored-By: Claude <[email protected]>
…13648) ## Describe your changes Fixes issue #13646 where `st.multiselect()` and `st.selectbox` clears user selections after each script rerun when using custom class objects without `__eq__` implementation and a `format_func` parameter. This was a regression introduced in PR #13448. **Root Cause**: The validation logic compared multiselect values using `==`, which falls back to identity comparison for objects without `__eq__`. Since `register_widget()` deepcopies widget values, the new instances fail identity comparison with original options, causing valid selections to be filtered out. **Solution**: Changed validation to compare values using `format_func()` by their formatted string representation instead of using `==`. This is more robust and works correctly regardless of whether custom classes implement `__eq__`. ## Testing Plan - Unit Tests: Added 4 comprehensive test cases covering custom objects without `__eq__`, partial matches, objects with `__str__`, and edge cases where `format_func` fails on incompatible types - E2E Tests: Added `test_multiselect_custom_objects_without_eq` to verify selections persist across script reruns with custom class objects - Backward Compatibility: The `format_func` parameter has a default value of `str`, so existing code continues to work without changes --- **Contribution License Agreement** By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license. --------- Co-authored-by: Claude <[email protected]>
…13648) ## Describe your changes Fixes issue #13646 where `st.multiselect()` and `st.selectbox` clears user selections after each script rerun when using custom class objects without `__eq__` implementation and a `format_func` parameter. This was a regression introduced in PR #13448. **Root Cause**: The validation logic compared multiselect values using `==`, which falls back to identity comparison for objects without `__eq__`. Since `register_widget()` deepcopies widget values, the new instances fail identity comparison with original options, causing valid selections to be filtered out. **Solution**: Changed validation to compare values using `format_func()` by their formatted string representation instead of using `==`. This is more robust and works correctly regardless of whether custom classes implement `__eq__`. ## Testing Plan - Unit Tests: Added 4 comprehensive test cases covering custom objects without `__eq__`, partial matches, objects with `__str__`, and edge cases where `format_func` fails on incompatible types - E2E Tests: Added `test_multiselect_custom_objects_without_eq` to verify selections persist across script reruns with custom class objects - Backward Compatibility: The `format_func` parameter has a default value of `str`, so existing code continues to work without changes --- **Contribution License Agreement** By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license. --------- Co-authored-by: Claude <[email protected]>
Describe your changes
Allow dynamically changing the options for
st.multiselectwithout triggering an identity change / state reset. If the current selected options isn't in the list of available option, it will be reset to the default value.GitHub Issue Link (if applicable)
st.multiselectreturns empty on dynamically changing options. #7855Testing Plan
Contribution License Agreement
By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.