streamlit-nightly 1.53.2.dev20260125__py3-none-any.whl → 1.53.2.dev20260128__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- streamlit/commands/logo.py +81 -25
- streamlit/config.py +11 -0
- streamlit/deprecation_util.py +19 -1
- streamlit/elements/arrow.py +2 -1
- streamlit/elements/lib/built_in_chart_utils.py +2 -2
- streamlit/elements/lib/options_selector_utils.py +72 -22
- streamlit/elements/widgets/select_slider.py +123 -37
- streamlit/hello/plotting_demo.py +19 -12
- streamlit/proto/Logo_pb2.py +5 -3
- streamlit/proto/Logo_pb2.pyi +25 -1
- streamlit/proto/NewSession_pb2.py +24 -22
- streamlit/proto/NewSession_pb2.pyi +23 -1
- streamlit/proto/Slider_pb2.py +6 -6
- streamlit/proto/Slider_pb2.pyi +9 -1
- streamlit/runtime/app_session.py +19 -0
- streamlit/runtime/scriptrunner/script_runner.py +17 -0
- streamlit/runtime/scriptrunner_utils/script_run_context.py +13 -10
- streamlit/runtime/state/__init__.py +7 -1
- streamlit/runtime/state/common.py +13 -0
- streamlit/runtime/state/query_params.py +494 -6
- streamlit/runtime/state/session_state.py +178 -3
- streamlit/runtime/state/widgets.py +26 -1
- streamlit/static/index.html +1 -1
- streamlit/static/manifest.json +299 -299
- streamlit/static/static/js/{ErrorOutline.esm.CIFYUdwC.js → ErrorOutline.esm.D71F8ziR.js} +1 -1
- streamlit/static/static/js/{FileDownload.esm.DWVTnTHm.js → FileDownload.esm.yTkppsJy.js} +1 -1
- streamlit/static/static/js/{FileHelper.BPYQIPd1.js → FileHelper.hUOqtbwa.js} +1 -1
- streamlit/static/static/js/{FormClearHelper.CypmvhYZ.js → FormClearHelper.DN8D_YXO.js} +1 -1
- streamlit/static/static/js/{InputInstructions.Bi62hDTQ.js → InputInstructions.DbssY6d4.js} +1 -1
- streamlit/static/static/js/{Particles.yebG0VuV.js → Particles.BznyVdfo.js} +1 -1
- streamlit/static/static/js/{ProgressBar.Dy9CI6w4.js → ProgressBar.C5uBOtcx.js} +1 -1
- streamlit/static/static/js/{StreamlitSyntaxHighlighter.Btk92CPv.js → StreamlitSyntaxHighlighter.Nf1895x-.js} +1 -1
- streamlit/static/static/js/{TableChart.esm.DBeVaFNt.js → TableChart.esm.DHKzVs3a.js} +1 -1
- streamlit/static/static/js/{Toolbar.DC2Tp-qb.js → Toolbar.CQsWYXer.js} +1 -1
- streamlit/static/static/js/{WidgetLabelHelpIconInline.3DnEd9BK.js → WidgetLabelHelpIconInline.6xCU76OE.js} +1 -1
- streamlit/static/static/js/{base-input.7Sj6pVk0.js → base-input.Cs-E6S71.js} +1 -1
- streamlit/static/static/js/{checkbox.CcUx3XuQ.js → checkbox.OTGupu18.js} +1 -1
- streamlit/static/static/js/{createDownloadLinkElement.DZuwkCqy.js → createDownloadLinkElement.DnBEQQbK.js} +1 -1
- streamlit/static/static/js/{data-grid-overlay-editor.Dw-AewlN.js → data-grid-overlay-editor.COiiMi5r.js} +1 -1
- streamlit/static/static/js/{downloader.Bsx5M2Du.js → downloader.K0GUNeuj.js} +1 -1
- streamlit/static/static/js/embed.o8HvK3mH.js +193 -0
- streamlit/static/static/js/{es6.BpAqZaR_.js → es6.BHy5pqTP.js} +2 -2
- streamlit/static/static/js/{formatNumber.DjehVPVS.js → formatNumber.BK7h0k2z.js} +1 -1
- streamlit/static/static/js/{iconPosition.D02OPE-d.js → iconPosition.2YynQUxu.js} +1 -1
- streamlit/static/static/js/{iframeResizer.contentWindow.xtstqPd7.js → iframeResizer.contentWindow.D5h3hQuU.js} +1 -1
- streamlit/static/static/js/{index.5H98WqjT.js → index.5zqfJ-in.js} +1 -1
- streamlit/static/static/js/{index.B5tD5YeV.js → index.6c-qDsD7.js} +1 -1
- streamlit/static/static/js/{index.CA0RmxJF.js → index.8MlRyIxN.js} +1 -1
- streamlit/static/static/js/{index.DSSapl3Q.js → index.BIqcOZ_u.js} +1 -1
- streamlit/static/static/js/{index.DJjSqPAx.js → index.BPdmXoYW.js} +1 -1
- streamlit/static/static/js/{index.iXzAofuY.js → index.BZ-GJVxB.js} +2 -2
- streamlit/static/static/js/{index.CKUBdVQ9.js → index.BfMPq234.js} +1 -1
- streamlit/static/static/js/{index.B8-HOwf1.js → index.Bfo1cXfC.js} +1 -1
- streamlit/static/static/js/{index.-faJDV20.js → index.Bgf49D1Z.js} +1 -1
- streamlit/static/static/js/{index.CgARjn28.js → index.Bqmx23jK.js} +1 -1
- streamlit/static/static/js/{index.D6Z9hKJY.js → index.BtRWcqZV.js} +1 -1
- streamlit/static/static/js/{index.ZIA43eTF.js → index.BtuskCwg.js} +1 -1
- streamlit/static/static/js/{index.BV6XgCij.js → index.BzTVI_BY.js} +1 -1
- streamlit/static/static/js/{index.DDu_qTm0.js → index.C2EoeVjP.js} +1 -1
- streamlit/static/static/js/{index.8FPw0_gD.js → index.C65jHNhe.js} +1 -1
- streamlit/static/static/js/{index.D6J2UPzF.js → index.C6wyTXhz.js} +1 -1
- streamlit/static/static/js/{index.CGbvkEtg.js → index.C7wst9Tm.js} +1 -1
- streamlit/static/static/js/{index.BIcJe97b.js → index.COh5V_89.js} +1 -1
- streamlit/static/static/js/index.CSPY26T2.js +1 -0
- streamlit/static/static/js/{index.CEwnDCn9.js → index.CUkhn-vu.js} +1 -1
- streamlit/static/static/js/{index.DO2T-QzF.js → index.CX0KdFyR.js} +1 -1
- streamlit/static/static/js/{index.BDlI2pRp.js → index.CYhhEdja.js} +1 -1
- streamlit/static/static/js/{index.DgLRJfs3.js → index.CZf7Go1Z.js} +1 -1
- streamlit/static/static/js/{index.DZv5AoR1.js → index.Cb03y5I8.js} +1 -1
- streamlit/static/static/js/{index.BVhVdVeE.js → index.CdsyTabv.js} +1 -1
- streamlit/static/static/js/{index.JL0uGAeJ.js → index.CgVv04GM.js} +1 -1
- streamlit/static/static/js/index.CjRU8O1O.js +2 -0
- streamlit/static/static/js/{index.BqfJJr3c.js → index.CwtpGPHA.js} +1 -1
- streamlit/static/static/js/{index.iF5zYERg.js → index.CxWzt6oi.js} +1 -1
- streamlit/static/static/js/{index.m3dn5Bai.js → index.DBPWUJsj.js} +5 -5
- streamlit/static/static/js/{index.D9RL5sRp.js → index.DJfMW0Gy.js} +1 -1
- streamlit/static/static/js/{index.BOkpEbJS.js → index.DLUSo6de.js} +1 -1
- streamlit/static/static/js/{index.S-mjkUeF.js → index.DL_yE83J.js} +1 -1
- streamlit/static/static/js/{index.BK9S5qug.js → index.DVRCyxMp.js} +1 -1
- streamlit/static/static/js/{index.D_TIyPF4.js → index.Dc5-tFdw.js} +1 -1
- streamlit/static/static/js/index.DcngUOyD.js +2 -0
- streamlit/static/static/js/{index.B9gbSNsw.js → index.Dh3PJIlq.js} +1 -1
- streamlit/static/static/js/{index.BvZbnSMC.js → index.DlgcEr0f.js} +1 -1
- streamlit/static/static/js/{index.Bo1ztye0.js → index.DxGXuhh6.js} +1 -1
- streamlit/static/static/js/{index.x1B588Xu.js → index.DxfYCrPp.js} +1 -1
- streamlit/static/static/js/{index.CyDHwK5P.js → index.HmRK3HyC.js} +1 -1
- streamlit/static/static/js/{index.DdxofXV8.js → index.TjMWsKSH.js} +3 -3
- streamlit/static/static/js/{index.Bri1T2TS.js → index.VwDKazgt.js} +1 -1
- streamlit/static/static/js/{index.BB_iwaVr.js → index.aCorc3Yt.js} +34 -34
- streamlit/static/static/js/{index.CdRwiHPm.js → index.cfuZ69LI.js} +1 -1
- streamlit/static/static/js/{index.C9v49R-a.js → index.hlAfdSqC.js} +1 -1
- streamlit/static/static/js/{index.BGTMh3Uu.js → index.iUV9rb8C.js} +1 -1
- streamlit/static/static/js/{index.XFMDBL5n.js → index.q0ceUXt6.js} +1 -1
- streamlit/static/static/js/{input.VYKyGuhi.js → input.CXGIJ7D6.js} +1 -1
- streamlit/static/static/js/{main.u5Bb3MY7.js → main.CCVkbuxC.js} +1 -1
- streamlit/static/static/js/{memory.BOMt4yAV.js → memory.CNbnYs2A.js} +1 -1
- streamlit/static/static/js/{number-overlay-editor.CihlAHgl.js → number-overlay-editor.CvI6wkld.js} +1 -1
- streamlit/static/static/js/{pandasStylerUtils.BuqSgXpk.js → pandasStylerUtils.CFSReOTm.js} +1 -1
- streamlit/static/static/js/{sandbox.COGR4pqz.js → sandbox.Bld0L3us.js} +1 -1
- streamlit/static/static/js/{styled-components.BEf3c4IJ.js → styled-components.BoUHK6TA.js} +1 -1
- streamlit/static/static/js/{throttle.Bl-XsA9N.js → throttle.ByDFm7WV.js} +1 -1
- streamlit/static/static/js/{timepicker.B-HgBYlK.js → timepicker.CN6CUZEL.js} +1 -1
- streamlit/static/static/js/{toConsumableArray.BrQebwtE.js → toConsumableArray.DwMycSpg.js} +1 -1
- streamlit/static/static/js/uniqueId.DcCWa2cf.js +1 -0
- streamlit/static/static/js/{useBasicWidgetState.8WwISl9r.js → useBasicWidgetState.Bg0ZMUt5.js} +1 -1
- streamlit/static/static/js/{useIntlLocale.D37LWdCR.js → useIntlLocale.DgBUDcPA.js} +1 -1
- streamlit/static/static/js/{useTextInputAutoExpand.Bb_KqJvq.js → useTextInputAutoExpand.DDBezxks.js} +1 -1
- streamlit/static/static/js/{useUpdateUiValue.D1BLS5t7.js → useUpdateUiValue.Df1h6fXC.js} +1 -1
- streamlit/static/static/js/{useWaveformController.Ce0-qTws.js → useWaveformController.DbWw5MEk.js} +1 -1
- streamlit/static/static/js/{withCalculatedWidth.BX2K3UVv.js → withCalculatedWidth.YaK0HIIP.js} +1 -1
- streamlit/static/static/js/{withFullScreenWrapper.CqfGs8T2.js → withFullScreenWrapper.CcWCKoY8.js} +1 -1
- streamlit/testing/v1/element_tree.py +23 -8
- streamlit/web/bootstrap.py +5 -2
- streamlit/web/server/server_util.py +22 -0
- {streamlit_nightly-1.53.2.dev20260125.dist-info → streamlit_nightly-1.53.2.dev20260128.dist-info}/METADATA +1 -1
- {streamlit_nightly-1.53.2.dev20260125.dist-info → streamlit_nightly-1.53.2.dev20260128.dist-info}/RECORD +120 -120
- streamlit/static/static/js/embed.C7by6AoE.js +0 -195
- streamlit/static/static/js/index.Bhy8EBYI.js +0 -2
- streamlit/static/static/js/index.C5ehUqNt.js +0 -2
- streamlit/static/static/js/index.m4WkwGMu.js +0 -1
- streamlit/static/static/js/uniqueId.8R4hbkYl.js +0 -1
- {streamlit_nightly-1.53.2.dev20260125.data → streamlit_nightly-1.53.2.dev20260128.data}/scripts/streamlit.cmd +0 -0
- {streamlit_nightly-1.53.2.dev20260125.dist-info → streamlit_nightly-1.53.2.dev20260128.dist-info}/WHEEL +0 -0
- {streamlit_nightly-1.53.2.dev20260125.dist-info → streamlit_nightly-1.53.2.dev20260128.dist-info}/entry_points.txt +0 -0
- {streamlit_nightly-1.53.2.dev20260125.dist-info → streamlit_nightly-1.53.2.dev20260128.dist-info}/top_level.txt +0 -0
|
@@ -30,6 +30,7 @@ from typing import (
|
|
|
30
30
|
from streamlit import config, util
|
|
31
31
|
from streamlit.delta_generator_singletons import get_dg_singleton_instance
|
|
32
32
|
from streamlit.errors import StreamlitAPIException, UnserializableSessionStateError
|
|
33
|
+
from streamlit.logger import get_logger
|
|
33
34
|
from streamlit.proto.WidgetStates_pb2 import WidgetState as WidgetStateProto
|
|
34
35
|
from streamlit.proto.WidgetStates_pb2 import WidgetStates as WidgetStatesProto
|
|
35
36
|
from streamlit.runtime.scriptrunner_utils.script_run_context import get_script_run_ctx
|
|
@@ -45,7 +46,7 @@ from streamlit.runtime.state.common import (
|
|
|
45
46
|
is_keyed_element_id,
|
|
46
47
|
)
|
|
47
48
|
from streamlit.runtime.state.presentation import apply_presenter
|
|
48
|
-
from streamlit.runtime.state.query_params import QueryParams
|
|
49
|
+
from streamlit.runtime.state.query_params import QueryParams, parse_url_param
|
|
49
50
|
from streamlit.runtime.stats import (
|
|
50
51
|
CACHE_MEMORY_FAMILY,
|
|
51
52
|
CacheStat,
|
|
@@ -53,6 +54,8 @@ from streamlit.runtime.stats import (
|
|
|
53
54
|
group_cache_stats,
|
|
54
55
|
)
|
|
55
56
|
|
|
57
|
+
_LOGGER: Final = get_logger(__name__)
|
|
58
|
+
|
|
56
59
|
if TYPE_CHECKING:
|
|
57
60
|
from streamlit.runtime.session_manager import SessionManager
|
|
58
61
|
|
|
@@ -868,6 +871,17 @@ class SessionState:
|
|
|
868
871
|
)
|
|
869
872
|
}
|
|
870
873
|
|
|
874
|
+
# Remove query param bindings and URL params for stale widgets.
|
|
875
|
+
# For fragment runs, preserve widgets outside the running fragment(s).
|
|
876
|
+
# Note: For MPA page transitions, query param filtering is performed
|
|
877
|
+
# via populate_from_query_string() in script_runner.py before this cleanup,
|
|
878
|
+
# so bindings for non-active pages are already filtered by script hash.
|
|
879
|
+
self.query_params.remove_stale_bindings(
|
|
880
|
+
active_widget_ids,
|
|
881
|
+
ctx.fragment_ids_this_run,
|
|
882
|
+
self._new_widget_state.widget_metadata,
|
|
883
|
+
)
|
|
884
|
+
|
|
871
885
|
def _get_widget_metadata(self, widget_id: str) -> WidgetMetadata[Any] | None:
|
|
872
886
|
"""Return the metadata for a widget id from the current widget state."""
|
|
873
887
|
return self._new_widget_state.widget_metadata.get(widget_id)
|
|
@@ -910,9 +924,20 @@ class SessionState:
|
|
|
910
924
|
# If the widget has a user_key, update its user_key:widget_id mapping
|
|
911
925
|
self._set_key_widget_mapping(widget_id, user_key)
|
|
912
926
|
|
|
913
|
-
|
|
927
|
+
# Handle query param binding
|
|
928
|
+
url_value_seeded = False
|
|
929
|
+
if metadata.bind == "query-params" and user_key is not None:
|
|
930
|
+
url_value_seeded = self._handle_query_param_binding(
|
|
931
|
+
metadata, user_key, widget_id
|
|
932
|
+
)
|
|
933
|
+
|
|
934
|
+
if (
|
|
935
|
+
widget_id not in self
|
|
936
|
+
and (user_key is None or user_key not in self)
|
|
937
|
+
and not url_value_seeded
|
|
938
|
+
):
|
|
914
939
|
# This is the first time the widget is registered, so we save its
|
|
915
|
-
# value in widget state.
|
|
940
|
+
# value in widget state (unless we already seeded from URL).
|
|
916
941
|
deserializer = metadata.deserializer
|
|
917
942
|
initial_widget_value = deepcopy(deserializer(None))
|
|
918
943
|
self._new_widget_state.set_from_value(widget_id, initial_widget_value)
|
|
@@ -931,6 +956,156 @@ class SessionState:
|
|
|
931
956
|
|
|
932
957
|
return RegisterWidgetResult(widget_value, widget_value_changed)
|
|
933
958
|
|
|
959
|
+
def _handle_query_param_binding(
|
|
960
|
+
self, metadata: WidgetMetadata[T], user_key: str, widget_id: str
|
|
961
|
+
) -> bool:
|
|
962
|
+
"""Handle query param binding for a widget.
|
|
963
|
+
|
|
964
|
+
Registers the binding, then attempts to seed the widget's value from URL
|
|
965
|
+
based on priority rules:
|
|
966
|
+
|
|
967
|
+
- On initial load, URL wins (enables shareable URLs)
|
|
968
|
+
- On subsequent reruns, session_state values win
|
|
969
|
+
- User interaction (frontend value) always wins
|
|
970
|
+
|
|
971
|
+
Returns True if the widget's value was seeded from URL, False otherwise.
|
|
972
|
+
"""
|
|
973
|
+
# Register the widget binding
|
|
974
|
+
ctx = get_script_run_ctx()
|
|
975
|
+
script_hash = ctx.active_script_hash if ctx is not None else ""
|
|
976
|
+
self.query_params.bind_widget(
|
|
977
|
+
param_key=user_key,
|
|
978
|
+
widget_id=widget_id,
|
|
979
|
+
value_type=metadata.value_type,
|
|
980
|
+
script_hash=script_hash,
|
|
981
|
+
)
|
|
982
|
+
|
|
983
|
+
# Check priority rules - skip seeding if user/code has already set a value
|
|
984
|
+
if widget_id in self._new_widget_state: # User interacted with widget
|
|
985
|
+
return False
|
|
986
|
+
is_initial_load = widget_id not in self._old_state
|
|
987
|
+
if not is_initial_load and user_key in self._new_session_state:
|
|
988
|
+
return False # Code set value after first run
|
|
989
|
+
|
|
990
|
+
url_value = self.query_params.get_initial_value(user_key)
|
|
991
|
+
if url_value is None:
|
|
992
|
+
return False
|
|
993
|
+
|
|
994
|
+
return self._seed_widget_from_url(metadata, user_key, widget_id, url_value)
|
|
995
|
+
|
|
996
|
+
def _seed_widget_from_url(
|
|
997
|
+
self,
|
|
998
|
+
metadata: WidgetMetadata[T],
|
|
999
|
+
user_key: str,
|
|
1000
|
+
widget_id: str,
|
|
1001
|
+
url_value: str | list[str],
|
|
1002
|
+
) -> bool:
|
|
1003
|
+
"""Parse URL value, seed widget state, and auto-correct URL if needed.
|
|
1004
|
+
|
|
1005
|
+
This method:
|
|
1006
|
+
1. Parses the raw URL string using the widget's value_type
|
|
1007
|
+
2. Deserializes to the widget's native value format
|
|
1008
|
+
3. Handles invalid values (clears URL param, returns False)
|
|
1009
|
+
4. Stores valid values in both widget state and session state
|
|
1010
|
+
5. Auto-corrects the URL if the value was clamped/filtered
|
|
1011
|
+
|
|
1012
|
+
Returns True if seeding succeeded, False if the URL value was invalid.
|
|
1013
|
+
"""
|
|
1014
|
+
try:
|
|
1015
|
+
parsed_value = parse_url_param(url_value, metadata.value_type)
|
|
1016
|
+
deserialized_value = metadata.deserializer(parsed_value)
|
|
1017
|
+
|
|
1018
|
+
# Handle case where all URL values were invalid (filtered to empty list)
|
|
1019
|
+
if isinstance(deserialized_value, list) and len(deserialized_value) == 0:
|
|
1020
|
+
url_had_values = (
|
|
1021
|
+
isinstance(parsed_value, list) and len(parsed_value) > 0
|
|
1022
|
+
) or (isinstance(parsed_value, str) and len(parsed_value) > 0)
|
|
1023
|
+
if url_had_values:
|
|
1024
|
+
self._clear_url_param(user_key)
|
|
1025
|
+
return False
|
|
1026
|
+
|
|
1027
|
+
# Store the value in widget and session state
|
|
1028
|
+
self._new_widget_state.set_from_value(widget_id, deserialized_value)
|
|
1029
|
+
self._new_session_state[user_key] = deserialized_value
|
|
1030
|
+
|
|
1031
|
+
# Auto-correct URL if value was clamped/filtered
|
|
1032
|
+
self._auto_correct_url_if_needed(
|
|
1033
|
+
metadata, user_key, parsed_value, deserialized_value
|
|
1034
|
+
)
|
|
1035
|
+
return True
|
|
1036
|
+
|
|
1037
|
+
except (ValueError, TypeError, IndexError) as e:
|
|
1038
|
+
_LOGGER.debug(
|
|
1039
|
+
"Invalid URL value for bound widget '%s', clearing param: %s",
|
|
1040
|
+
user_key,
|
|
1041
|
+
e,
|
|
1042
|
+
)
|
|
1043
|
+
self._clear_url_param(user_key)
|
|
1044
|
+
return False
|
|
1045
|
+
|
|
1046
|
+
def _clear_url_param(self, user_key: str) -> None:
|
|
1047
|
+
"""Clear an invalid URL parameter and notify frontend."""
|
|
1048
|
+
self.query_params.remove_param(user_key)
|
|
1049
|
+
|
|
1050
|
+
def _auto_correct_url_if_needed(
|
|
1051
|
+
self,
|
|
1052
|
+
metadata: WidgetMetadata[T],
|
|
1053
|
+
user_key: str,
|
|
1054
|
+
parsed_value: Any,
|
|
1055
|
+
deserialized_value: Any,
|
|
1056
|
+
) -> None:
|
|
1057
|
+
"""Auto-correct URL if the value was clamped or filtered.
|
|
1058
|
+
|
|
1059
|
+
For selection widgets using human-readable strings in URLs, we preserve
|
|
1060
|
+
the original strings unless values were actually filtered out.
|
|
1061
|
+
"""
|
|
1062
|
+
serialized_value = metadata.serializer(deserialized_value)
|
|
1063
|
+
if serialized_value == parsed_value:
|
|
1064
|
+
return # No correction needed
|
|
1065
|
+
|
|
1066
|
+
# TODO(query-params): Remove this formatted_options handling once all selection
|
|
1067
|
+
# widgets use string-based wire formats (string_value/string_array_value).
|
|
1068
|
+
# For index-based widgets, don't auto-correct valid strings to indices -
|
|
1069
|
+
# only correct if values were actually filtered.
|
|
1070
|
+
string_option_types = ("int_value", "int_array_value", "double_array_value")
|
|
1071
|
+
use_formatted_options = False
|
|
1072
|
+
|
|
1073
|
+
if metadata.value_type in string_option_types:
|
|
1074
|
+
# Check if parsed value contained strings
|
|
1075
|
+
if isinstance(parsed_value, list):
|
|
1076
|
+
parsed_has_strings = any(isinstance(v, str) for v in parsed_value)
|
|
1077
|
+
parsed_len = len(parsed_value)
|
|
1078
|
+
else:
|
|
1079
|
+
parsed_has_strings = isinstance(parsed_value, str)
|
|
1080
|
+
parsed_len = 1
|
|
1081
|
+
|
|
1082
|
+
if parsed_has_strings:
|
|
1083
|
+
serialized_len = (
|
|
1084
|
+
len(serialized_value) if isinstance(serialized_value, list) else 1
|
|
1085
|
+
)
|
|
1086
|
+
if serialized_len == parsed_len:
|
|
1087
|
+
return # No filtering, keep original strings in URL
|
|
1088
|
+
use_formatted_options = bool(metadata.formatted_options)
|
|
1089
|
+
|
|
1090
|
+
# Build corrected value, converting indices to formatted strings if needed
|
|
1091
|
+
corrected_value = serialized_value
|
|
1092
|
+
if use_formatted_options and metadata.formatted_options:
|
|
1093
|
+
fmt_opts = metadata.formatted_options
|
|
1094
|
+
if isinstance(serialized_value, list):
|
|
1095
|
+
corrected_value = [
|
|
1096
|
+
fmt_opts[idx]
|
|
1097
|
+
for idx in serialized_value
|
|
1098
|
+
if isinstance(idx, int) and 0 <= idx < len(fmt_opts)
|
|
1099
|
+
]
|
|
1100
|
+
elif isinstance(serialized_value, int) and 0 <= serialized_value < len(
|
|
1101
|
+
fmt_opts
|
|
1102
|
+
):
|
|
1103
|
+
corrected_value = fmt_opts[serialized_value]
|
|
1104
|
+
|
|
1105
|
+
self.query_params._set_corrected_value(
|
|
1106
|
+
user_key, corrected_value, metadata.value_type
|
|
1107
|
+
)
|
|
1108
|
+
|
|
934
1109
|
def __contains__(self, key: str) -> bool:
|
|
935
1110
|
try:
|
|
936
1111
|
self[key]
|
|
@@ -18,6 +18,7 @@ from typing import TYPE_CHECKING
|
|
|
18
18
|
|
|
19
19
|
from streamlit.errors import StreamlitAPIException
|
|
20
20
|
from streamlit.runtime.state.common import (
|
|
21
|
+
BindOption,
|
|
21
22
|
RegisterWidgetResult,
|
|
22
23
|
T,
|
|
23
24
|
ValueFieldName,
|
|
@@ -47,6 +48,10 @@ def register_widget(
|
|
|
47
48
|
kwargs: WidgetKwargs | None = None,
|
|
48
49
|
value_type: ValueFieldName,
|
|
49
50
|
presenter: WidgetValuePresenter | None = None,
|
|
51
|
+
bind: BindOption = None,
|
|
52
|
+
# TODO(query-params): Remove formatted_options once all selection widgets use
|
|
53
|
+
# string-based wire formats (string_value/string_array_value).
|
|
54
|
+
formatted_options: list[str] | None = None,
|
|
50
55
|
) -> RegisterWidgetResult[T]:
|
|
51
56
|
"""Register a widget with Streamlit, and return its current value.
|
|
52
57
|
NOTE: This function should be called after the proto has been filled.
|
|
@@ -81,7 +86,15 @@ def register_widget(
|
|
|
81
86
|
presenter : WidgetValuePresenter or None
|
|
82
87
|
An optional hook that allows a widget to customize how its value should be
|
|
83
88
|
presented.
|
|
84
|
-
|
|
89
|
+
bind : BindOption
|
|
90
|
+
Optional binding for the widget's value to external state.
|
|
91
|
+
Currently only "query-params" is supported, which binds the widget
|
|
92
|
+
value to a URL query parameter. Requires a user-provided key.
|
|
93
|
+
formatted_options : list[str] or None
|
|
94
|
+
**Temporary** - will be removed once all selection widgets use string-based
|
|
95
|
+
wire formats. Currently used for index-based widgets (pills, segmented_control,
|
|
96
|
+
select_slider) to convert indices back to human-readable option strings
|
|
97
|
+
in URLs when auto-correcting filtered values.
|
|
85
98
|
|
|
86
99
|
Returns
|
|
87
100
|
-------
|
|
@@ -112,6 +125,16 @@ def register_widget(
|
|
|
112
125
|
"Cannot provide both `on_change` and `callbacks` to a widget."
|
|
113
126
|
)
|
|
114
127
|
|
|
128
|
+
# Validate that widget with bind="query-params" has a provided key
|
|
129
|
+
if bind == "query-params":
|
|
130
|
+
user_key = user_key_from_element_id(element_id)
|
|
131
|
+
if user_key is None:
|
|
132
|
+
raise StreamlitAPIException(
|
|
133
|
+
"When using bind='query-params', the widget must have a unique 'key' "
|
|
134
|
+
"parameter specified. This 'key' will be used as the name of the "
|
|
135
|
+
"query parameter."
|
|
136
|
+
)
|
|
137
|
+
|
|
115
138
|
# Create the widget's updated metadata, and register it with session_state.
|
|
116
139
|
metadata = WidgetMetadata(
|
|
117
140
|
element_id,
|
|
@@ -124,6 +147,8 @@ def register_widget(
|
|
|
124
147
|
callback_kwargs=kwargs,
|
|
125
148
|
fragment_id=ctx.current_fragment_id if ctx else None,
|
|
126
149
|
presenter=presenter,
|
|
150
|
+
bind=bind,
|
|
151
|
+
formatted_options=formatted_options,
|
|
127
152
|
)
|
|
128
153
|
return register_widget_from_metadata(metadata, ctx)
|
|
129
154
|
|
streamlit/static/index.html
CHANGED
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
<script>
|
|
38
38
|
window.prerenderReady = false
|
|
39
39
|
</script>
|
|
40
|
-
<script type="module" crossorigin src="./static/js/index.
|
|
40
|
+
<script type="module" crossorigin src="./static/js/index.aCorc3Yt.js"></script>
|
|
41
41
|
<link rel="stylesheet" crossorigin href="./static/css/index.BUP6fTcR.css">
|
|
42
42
|
</head>
|
|
43
43
|
<body>
|