streamlit 1.49.1__py3-none-any.whl → 1.51.0__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/__init__.py +4 -1
- streamlit/column_config.py +2 -0
- streamlit/commands/navigation.py +7 -7
- streamlit/commands/page_config.py +4 -6
- streamlit/components/v1/custom_component.py +17 -42
- streamlit/components/v2/__init__.py +458 -0
- streamlit/components/v2/bidi_component/__init__.py +20 -0
- streamlit/components/v2/bidi_component/constants.py +29 -0
- streamlit/components/v2/bidi_component/main.py +386 -0
- streamlit/components/v2/bidi_component/serialization.py +265 -0
- streamlit/components/v2/bidi_component/state.py +92 -0
- streamlit/components/v2/component_definition_resolver.py +143 -0
- streamlit/components/v2/component_file_watcher.py +403 -0
- streamlit/components/v2/component_manager.py +431 -0
- streamlit/components/v2/component_manifest_handler.py +122 -0
- streamlit/components/v2/component_path_utils.py +245 -0
- streamlit/components/v2/component_registry.py +409 -0
- streamlit/components/v2/get_bidi_component_manager.py +51 -0
- streamlit/components/v2/manifest_scanner.py +615 -0
- streamlit/components/v2/presentation.py +198 -0
- streamlit/components/v2/types.py +324 -0
- streamlit/config.py +741 -32
- streamlit/config_option.py +4 -1
- streamlit/config_util.py +650 -1
- streamlit/connections/base_connection.py +4 -2
- streamlit/dataframe_util.py +18 -10
- streamlit/delta_generator.py +8 -7
- streamlit/delta_generator_singletons.py +3 -1
- streamlit/deprecation_util.py +17 -6
- streamlit/elements/arrow.py +90 -42
- streamlit/elements/deck_gl_json_chart.py +98 -39
- streamlit/elements/dialog_decorator.py +2 -1
- streamlit/elements/exception.py +3 -1
- streamlit/elements/form.py +6 -6
- streamlit/elements/graphviz_chart.py +24 -9
- streamlit/elements/heading.py +3 -5
- streamlit/elements/iframe.py +0 -2
- streamlit/elements/image.py +12 -13
- streamlit/elements/layouts.py +89 -22
- streamlit/elements/lib/built_in_chart_utils.py +95 -31
- streamlit/elements/lib/color_util.py +8 -18
- streamlit/elements/lib/column_config_utils.py +9 -8
- streamlit/elements/lib/column_types.py +595 -148
- streamlit/elements/lib/dialog.py +3 -2
- streamlit/elements/lib/image_utils.py +3 -5
- streamlit/elements/lib/layout_utils.py +50 -13
- streamlit/elements/lib/mutable_status_container.py +2 -2
- streamlit/elements/lib/options_selector_utils.py +2 -2
- streamlit/elements/lib/pandas_styler_utils.py +30 -14
- streamlit/elements/lib/utils.py +21 -9
- streamlit/elements/map.py +81 -40
- streamlit/elements/media.py +7 -7
- streamlit/elements/metric.py +11 -35
- streamlit/elements/pdf.py +2 -4
- streamlit/elements/plotly_chart.py +142 -26
- streamlit/elements/progress.py +2 -4
- streamlit/elements/pyplot.py +6 -6
- streamlit/elements/space.py +113 -0
- streamlit/elements/vega_charts.py +400 -143
- streamlit/elements/widgets/audio_input.py +52 -4
- streamlit/elements/widgets/button.py +29 -29
- streamlit/elements/widgets/button_group.py +33 -6
- streamlit/elements/widgets/camera_input.py +3 -4
- streamlit/elements/widgets/chat.py +7 -0
- streamlit/elements/widgets/checkbox.py +1 -0
- streamlit/elements/widgets/color_picker.py +1 -0
- streamlit/elements/widgets/data_editor.py +34 -29
- streamlit/elements/widgets/file_uploader.py +6 -10
- streamlit/elements/widgets/multiselect.py +14 -3
- streamlit/elements/widgets/number_input.py +5 -4
- streamlit/elements/widgets/radio.py +10 -2
- streamlit/elements/widgets/select_slider.py +8 -4
- streamlit/elements/widgets/selectbox.py +9 -2
- streamlit/elements/widgets/slider.py +38 -41
- streamlit/elements/widgets/text_widgets.py +6 -0
- streamlit/elements/widgets/time_widgets.py +15 -12
- streamlit/elements/write.py +28 -23
- streamlit/emojis.py +1 -1
- streamlit/errors.py +115 -0
- streamlit/git_util.py +65 -43
- streamlit/hello/hello.py +8 -0
- streamlit/hello/utils.py +2 -1
- streamlit/material_icon_names.py +1 -1
- streamlit/navigation/page.py +4 -1
- streamlit/proto/ArrowData_pb2.py +27 -0
- streamlit/proto/ArrowData_pb2.pyi +46 -0
- streamlit/proto/Arrow_pb2.py +10 -8
- streamlit/proto/Arrow_pb2.pyi +31 -2
- streamlit/proto/AudioInput_pb2.py +2 -2
- streamlit/proto/AudioInput_pb2.pyi +6 -2
- streamlit/proto/BidiComponent_pb2.py +34 -0
- streamlit/proto/BidiComponent_pb2.pyi +153 -0
- streamlit/proto/Block_pb2.py +11 -11
- streamlit/proto/Block_pb2.pyi +9 -1
- streamlit/proto/DeckGlJsonChart_pb2.py +10 -4
- streamlit/proto/DeckGlJsonChart_pb2.pyi +9 -3
- streamlit/proto/Element_pb2.py +5 -3
- streamlit/proto/Element_pb2.pyi +14 -4
- streamlit/proto/HeightConfig_pb2.py +2 -2
- streamlit/proto/HeightConfig_pb2.pyi +6 -3
- streamlit/proto/NewSession_pb2.py +18 -16
- streamlit/proto/NewSession_pb2.pyi +158 -6
- streamlit/proto/PlotlyChart_pb2.py +8 -6
- streamlit/proto/PlotlyChart_pb2.pyi +3 -1
- streamlit/proto/Space_pb2.py +27 -0
- streamlit/proto/Space_pb2.pyi +42 -0
- streamlit/proto/WidgetStates_pb2.py +2 -2
- streamlit/proto/WidgetStates_pb2.pyi +13 -3
- streamlit/proto/WidthConfig_pb2.py +2 -2
- streamlit/proto/WidthConfig_pb2.pyi +6 -3
- streamlit/runtime/app_session.py +45 -6
- streamlit/runtime/caching/cache_data_api.py +4 -4
- streamlit/runtime/caching/cache_errors.py +4 -1
- streamlit/runtime/caching/cache_resource_api.py +3 -2
- streamlit/runtime/caching/cache_utils.py +2 -1
- streamlit/runtime/caching/cached_message_replay.py +3 -3
- streamlit/runtime/caching/hashing.py +3 -4
- streamlit/runtime/caching/legacy_cache_api.py +2 -1
- streamlit/runtime/connection_factory.py +1 -3
- streamlit/runtime/forward_msg_queue.py +4 -1
- streamlit/runtime/fragment.py +2 -1
- streamlit/runtime/memory_media_file_storage.py +1 -1
- streamlit/runtime/metrics_util.py +6 -2
- streamlit/runtime/runtime.py +14 -0
- streamlit/runtime/scriptrunner/exec_code.py +2 -1
- streamlit/runtime/scriptrunner/script_runner.py +2 -2
- streamlit/runtime/scriptrunner_utils/script_run_context.py +3 -6
- streamlit/runtime/secrets.py +2 -4
- streamlit/runtime/session_manager.py +3 -1
- streamlit/runtime/state/common.py +30 -5
- streamlit/runtime/state/presentation.py +85 -0
- streamlit/runtime/state/safe_session_state.py +2 -2
- streamlit/runtime/state/session_state.py +220 -16
- streamlit/runtime/state/widgets.py +19 -3
- streamlit/runtime/theme_util.py +148 -0
- streamlit/runtime/websocket_session_manager.py +3 -1
- streamlit/source_util.py +2 -2
- streamlit/static/index.html +2 -2
- streamlit/static/manifest.json +244 -227
- streamlit/static/static/css/{index.C8X8rNzw.css → index.BpABIXK9.css} +1 -1
- streamlit/static/static/css/index.DgR7E2CV.css +1 -0
- streamlit/static/static/js/{ErrorOutline.esm.DcGrhbBP.js → ErrorOutline.esm.YoJdlW1p.js} +1 -1
- streamlit/static/static/js/{FileDownload.esm.DgBvV6Pq.js → FileDownload.esm.Ddx8VEYy.js} +1 -1
- streamlit/static/static/js/{FileHelper.M6AAaeuA.js → FileHelper.90EtOmj9.js} +1 -1
- streamlit/static/static/js/{FormClearHelper.DHh1GFzm.js → FormClearHelper.BB1Km6eP.js} +1 -1
- streamlit/static/static/js/InputInstructions.jhH15PqV.js +1 -0
- streamlit/static/static/js/{Particles.DDVT-6Qc.js → Particles.DUsputn1.js} +1 -1
- streamlit/static/static/js/{ProgressBar.BEY0cXXV.js → ProgressBar.DLY8H6nE.js} +2 -2
- streamlit/static/static/js/Toolbar.D8nHCkuz.js +1 -0
- streamlit/static/static/js/{base-input.CK3UVGp1.js → base-input.CJGiNqed.js} +3 -3
- streamlit/static/static/js/{checkbox.D8W881TL.js → checkbox.Cpdd482O.js} +1 -1
- streamlit/static/static/js/{createSuper.B6W-Dh9S.js → createSuper.CuQIogbW.js} +1 -1
- streamlit/static/static/js/data-grid-overlay-editor.2Ufgxc6y.js +1 -0
- streamlit/static/static/js/{downloader.DiKpuU_S.js → downloader.CN0K7xlu.js} +1 -1
- streamlit/static/static/js/{es6.B8zRNPZ-.js → es6.BJcsVXQ0.js} +2 -2
- streamlit/static/static/js/{iframeResizer.contentWindow.DIewJmmh.js → iframeResizer.contentWindow.XzUvQqcZ.js} +1 -1
- streamlit/static/static/js/index.B1ZQh4P1.js +1 -0
- streamlit/static/static/js/index.BKstZk0M.js +27 -0
- streamlit/static/static/js/{index.Bte_9Lyq.js → index.BMcFsUee.js} +1 -1
- streamlit/static/static/js/{index.qhs54UAB.js → index.BR-IdcTb.js} +1 -1
- streamlit/static/static/js/{index.CejBxbg1.js → index.B_dWA3vd.js} +1 -1
- streamlit/static/static/js/{index.D5naqx-J.js → index.BgnZEMVh.js} +1 -1
- streamlit/static/static/js/{index.C7fRKRs4.js → index.BohqXifI.js} +1 -1
- streamlit/static/static/js/{index.cnnXF7xQ.js → index.Br5nxKNj.js} +1 -1
- streamlit/static/static/js/index.BrIKVbNc.js +3 -0
- streamlit/static/static/js/index.BtWUPzle.js +1 -0
- streamlit/static/static/js/index.C0RLraek.js +1 -0
- streamlit/static/static/js/{index.CP5TD2z1.js → index.CAIjskgG.js} +1 -1
- streamlit/static/static/js/{index.CD8HuT3N.js → index.CAj-7vWz.js} +135 -162
- streamlit/static/static/js/{index.DtYN2x4k.js → index.CMtEit2O.js} +1 -1
- streamlit/static/static/js/index.CkRlykEE.js +12 -0
- streamlit/static/static/js/{index.Ts_0SdB9.js → index.CmN3FXfI.js} +2 -2
- streamlit/static/static/js/{index.BnEpvLEz.js → index.CwbFI1_-.js} +1 -1
- streamlit/static/static/js/{index.CcJf6BCU.js → index.CxIUUfab.js} +27 -27
- streamlit/static/static/js/index.D2KPNy7e.js +1 -0
- streamlit/static/static/js/{index.Ch7MBCx0.js → index.D3GPA5k4.js} +47 -47
- streamlit/static/static/js/{index.ho6NIXGl.js → index.DGAh7DMq.js} +1 -1
- streamlit/static/static/js/index.DKb_NvmG.js +197 -0
- streamlit/static/static/js/{index.CvYYtxD_.js → index.DMqgUYKq.js} +1 -1
- streamlit/static/static/js/{index.zecpGxtj.js → index.DOFlg3dS.js} +1 -1
- streamlit/static/static/js/{index.B9mjBcgE.js → index.DPUXkcQL.js} +1 -1
- streamlit/static/static/js/index.DX1xY89g.js +1 -0
- streamlit/static/static/js/index.DYATBCsq.js +2 -0
- streamlit/static/static/js/{index.D2-atlaQ.js → index.DaSmGJ76.js} +3 -3
- streamlit/static/static/js/index.Dd7bMeLP.js +1 -0
- streamlit/static/static/js/{index.4eF4NxG2.js → index.DjmmgI5U.js} +1 -1
- streamlit/static/static/js/index.Dq56CyM2.js +1 -0
- streamlit/static/static/js/index.DuiXaS5_.js +7 -0
- streamlit/static/static/js/index.DvFidMLe.js +2 -0
- streamlit/static/static/js/{index.452cqrrL.js → index.DwkhC5Pc.js} +1 -1
- streamlit/static/static/js/{index.Dk4C7X3i.js → index.Q-3sFn1v.js} +1 -1
- streamlit/static/static/js/{index.CjXWwH-y.js → index.QJ5QO9sJ.js} +1 -1
- streamlit/static/static/js/{index.B6U8LQo3.js → index.VwTaeety.js} +1 -1
- streamlit/static/static/js/index.YOqQbeX8.js +1 -0
- streamlit/static/static/js/{input.nzVJphXi.js → input.D4MN_FzN.js} +1 -1
- streamlit/static/static/js/{memory.CjCgTQz3.js → memory.DrZjtdGT.js} +1 -1
- streamlit/static/static/js/{number-overlay-editor.DaRFzZEO.js → number-overlay-editor.DRwAw1In.js} +1 -1
- streamlit/static/static/js/{possibleConstructorReturn.DgiPnZ9N.js → possibleConstructorReturn.exeeJQEP.js} +1 -1
- streamlit/static/static/js/record.B-tDciZb.js +1 -0
- streamlit/static/static/js/{sandbox.mithfq7Z.js → sandbox.ClO3IuUr.js} +1 -1
- streamlit/static/static/js/{timepicker.Dbl5KFh6.js → timepicker.DAhu-vcF.js} +4 -4
- streamlit/static/static/js/{toConsumableArray.D-Dx88BQ.js → toConsumableArray.DNbljYEC.js} +1 -1
- streamlit/static/static/js/{uniqueId.Bh26R_3S.js → uniqueId.oG4Gvj1v.js} +1 -1
- streamlit/static/static/js/{useBasicWidgetState.DeK-QJpD.js → useBasicWidgetState.D6sOH6oI.js} +1 -1
- streamlit/static/static/js/{useTextInputAutoExpand.4iAdLWD-.js → useTextInputAutoExpand.4u3_GcuN.js} +2 -2
- streamlit/static/static/js/{useUpdateUiValue.CmT7_nJN.js → useUpdateUiValue.F2R3eTeR.js} +1 -1
- streamlit/static/static/js/wavesurfer.esm.vI8Eid4k.js +73 -0
- streamlit/static/static/js/withFullScreenWrapper.zothJIsI.js +1 -0
- streamlit/static/static/media/MaterialSymbols-Rounded.C7IFxh57.woff2 +0 -0
- streamlit/string_util.py +56 -1
- streamlit/testing/v1/app_test.py +2 -2
- streamlit/testing/v1/element_tree.py +23 -9
- streamlit/testing/v1/util.py +2 -2
- streamlit/type_util.py +3 -4
- streamlit/url_util.py +1 -3
- streamlit/user_info.py +1 -2
- streamlit/util.py +3 -1
- streamlit/watcher/event_based_path_watcher.py +23 -12
- streamlit/watcher/local_sources_watcher.py +11 -1
- streamlit/watcher/path_watcher.py +9 -6
- streamlit/watcher/polling_path_watcher.py +4 -1
- streamlit/watcher/util.py +2 -2
- streamlit/web/bootstrap.py +0 -31
- streamlit/web/cli.py +51 -22
- streamlit/web/server/bidi_component_request_handler.py +193 -0
- streamlit/web/server/component_file_utils.py +97 -0
- streamlit/web/server/component_request_handler.py +8 -21
- streamlit/web/server/oidc_mixin.py +3 -1
- streamlit/web/server/routes.py +18 -5
- streamlit/web/server/server.py +10 -0
- streamlit/web/server/server_util.py +3 -1
- streamlit/web/server/upload_file_request_handler.py +3 -1
- {streamlit-1.49.1.dist-info → streamlit-1.51.0.dist-info}/METADATA +4 -5
- {streamlit-1.49.1.dist-info → streamlit-1.51.0.dist-info}/RECORD +238 -209
- streamlit/static/static/css/index.COe1010n.css +0 -1
- streamlit/static/static/js/Hooks.DGu1od_L.js +0 -1
- streamlit/static/static/js/InputInstructions.z6sVgyYt.js +0 -1
- streamlit/static/static/js/Toolbar.DSnK1fUh.js +0 -1
- streamlit/static/static/js/data-grid-overlay-editor.DRTHOydk.js +0 -1
- streamlit/static/static/js/index.BXYmrqnf.js +0 -1
- streamlit/static/static/js/index.B_8AnktO.js +0 -1
- streamlit/static/static/js/index.Bl7zGQSh.js +0 -7
- streamlit/static/static/js/index.BnJIOYn9.js +0 -73
- streamlit/static/static/js/index.C1HcTl5K.js +0 -1
- streamlit/static/static/js/index.C7lSmSOP.js +0 -1
- streamlit/static/static/js/index.C_tmcx4B.js +0 -1
- streamlit/static/static/js/index.D3K5nOu9.js +0 -197
- streamlit/static/static/js/index.DkKT3LUI.js +0 -1
- streamlit/static/static/js/index.MTPPBDHk.js +0 -2
- streamlit/static/static/js/index.pqW9AMJD.js +0 -3
- streamlit/static/static/js/index.urHgTgMQ.js +0 -12
- streamlit/static/static/js/index.wzkv_11M.js +0 -1
- streamlit/static/static/js/index.yF5AncHY.js +0 -1
- streamlit/static/static/js/withFullScreenWrapper.DLp1ENGm.js +0 -1
- streamlit/static/static/media/MaterialSymbols-Rounded.CBxVaFdk.woff2 +0 -0
- {streamlit-1.49.1.data → streamlit-1.51.0.data}/scripts/streamlit.cmd +0 -0
- {streamlit-1.49.1.dist-info → streamlit-1.51.0.dist-info}/WHEEL +0 -0
- {streamlit-1.49.1.dist-info → streamlit-1.51.0.dist-info}/entry_points.txt +0 -0
- {streamlit-1.49.1.dist-info → streamlit-1.51.0.dist-info}/top_level.txt +0 -0
|
@@ -16,26 +16,29 @@
|
|
|
16
16
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
|
+
from collections.abc import Callable
|
|
19
20
|
from dataclasses import dataclass, field
|
|
20
21
|
from typing import (
|
|
22
|
+
TYPE_CHECKING,
|
|
21
23
|
Any,
|
|
22
|
-
Callable,
|
|
23
24
|
Final,
|
|
24
25
|
Generic,
|
|
25
26
|
Literal,
|
|
27
|
+
TypeAlias,
|
|
28
|
+
TypeGuard,
|
|
26
29
|
TypeVar,
|
|
27
|
-
Union,
|
|
28
30
|
cast,
|
|
29
31
|
get_args,
|
|
30
32
|
)
|
|
31
33
|
|
|
32
|
-
from typing_extensions import TypeAlias, TypeGuard
|
|
33
|
-
|
|
34
34
|
from streamlit import util
|
|
35
35
|
from streamlit.errors import (
|
|
36
36
|
StreamlitAPIException,
|
|
37
37
|
)
|
|
38
38
|
|
|
39
|
+
if TYPE_CHECKING:
|
|
40
|
+
from streamlit.runtime.state.session_state import SessionState
|
|
41
|
+
|
|
39
42
|
GENERATED_ELEMENT_ID_PREFIX: Final = "$$ID"
|
|
40
43
|
TESTING_KEY = "$$STREAMLIT_INTERNAL_KEY_TESTING"
|
|
41
44
|
|
|
@@ -44,7 +47,7 @@ T = TypeVar("T")
|
|
|
44
47
|
T_co = TypeVar("T_co", covariant=True)
|
|
45
48
|
|
|
46
49
|
|
|
47
|
-
WidgetArgs: TypeAlias =
|
|
50
|
+
WidgetArgs: TypeAlias = tuple[Any, ...] | list[Any]
|
|
48
51
|
WidgetKwargs: TypeAlias = dict[str, Any]
|
|
49
52
|
WidgetCallback: TypeAlias = Callable[..., None]
|
|
50
53
|
|
|
@@ -93,6 +96,7 @@ ValueFieldName: TypeAlias = Literal[
|
|
|
93
96
|
"file_uploader_state_value",
|
|
94
97
|
"int_value",
|
|
95
98
|
"json_value",
|
|
99
|
+
"json_trigger_value",
|
|
96
100
|
"string_value",
|
|
97
101
|
"trigger_value",
|
|
98
102
|
"string_trigger_value",
|
|
@@ -104,6 +108,13 @@ def is_array_value_field_name(obj: object) -> TypeGuard[ArrayValueFieldName]:
|
|
|
104
108
|
return obj in _ARRAY_VALUE_FIELD_NAMES
|
|
105
109
|
|
|
106
110
|
|
|
111
|
+
# Optional hook that allows a widget to customize how its value should be
|
|
112
|
+
# presented in `st.session_state` without altering the underlying stored value
|
|
113
|
+
# or callback semantics. The presenter receives the widget's base value and the
|
|
114
|
+
# SessionState instance in case it needs to access additional widget state.
|
|
115
|
+
WidgetValuePresenter: TypeAlias = Callable[[Any, "SessionState"], Any]
|
|
116
|
+
|
|
117
|
+
|
|
107
118
|
@dataclass(frozen=True)
|
|
108
119
|
class WidgetMetadata(Generic[T]):
|
|
109
120
|
"""Metadata associated with a single widget. Immutable."""
|
|
@@ -117,11 +128,25 @@ class WidgetMetadata(Generic[T]):
|
|
|
117
128
|
# Widget callbacks are called at the start of a script run, before the
|
|
118
129
|
# body of the script is executed.
|
|
119
130
|
callback: WidgetCallback | None = None
|
|
131
|
+
|
|
132
|
+
# An optional dictionary of event names to user-code callbacks. These are
|
|
133
|
+
# invoked when the corresponding widget event occurs. Callbacks are called
|
|
134
|
+
# at the start of a script run, before the body of the script is executed.
|
|
135
|
+
# Right now, multiple callbacks are only supported for widgets with a
|
|
136
|
+
# `value_type` of `json_value` or `json_trigger_value`. The keys in this
|
|
137
|
+
# dictionary should correspond to keys in the widget's JSON state.
|
|
138
|
+
callbacks: dict[str, WidgetCallback] | None = None
|
|
120
139
|
callback_args: WidgetArgs | None = None
|
|
121
140
|
callback_kwargs: WidgetKwargs | None = None
|
|
122
141
|
|
|
123
142
|
fragment_id: str | None = None
|
|
124
143
|
|
|
144
|
+
# Optional presenter hook used for customizing the user-visible value in
|
|
145
|
+
# st.session_state. This is intended for advanced widgets (e.g. Custom
|
|
146
|
+
# Components v2) that need to synthesize a presentation-only value from
|
|
147
|
+
# multiple internal widget states.
|
|
148
|
+
presenter: WidgetValuePresenter | None = None
|
|
149
|
+
|
|
125
150
|
def __repr__(self) -> str:
|
|
126
151
|
return util.repr_(self)
|
|
127
152
|
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from typing import TYPE_CHECKING, Any, Final
|
|
18
|
+
|
|
19
|
+
from streamlit.logger import get_logger
|
|
20
|
+
|
|
21
|
+
_LOGGER: Final = get_logger(__name__)
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from streamlit.runtime.state.session_state import SessionState
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def apply_presenter(
|
|
28
|
+
session_state: SessionState, widget_id: str, base_value: Any
|
|
29
|
+
) -> Any:
|
|
30
|
+
"""Return the user-visible value for a widget if it has a ``presenter``.
|
|
31
|
+
|
|
32
|
+
If the widget's metadata defines a ``presenter`` callable, it is used to
|
|
33
|
+
transform the stored value into its presentation form. Any exception raised
|
|
34
|
+
while resolving metadata or invoking the presenter is swallowed and
|
|
35
|
+
``base_value`` is returned, so presentation never interferes with core
|
|
36
|
+
behavior.
|
|
37
|
+
|
|
38
|
+
Notes
|
|
39
|
+
-----
|
|
40
|
+
Presentation is applied exclusively for user-facing access paths such as:
|
|
41
|
+
- `st.session_state[... ]` via `SessionState.__getitem__`
|
|
42
|
+
- `SessionState.filtered_state`
|
|
43
|
+
|
|
44
|
+
Internal serialization paths (for example `WStates.as_widget_states()` and
|
|
45
|
+
`SessionState.get_widget_states()`) must operate on base (unpresented)
|
|
46
|
+
values to ensure stable and lossless serialization. Do not use
|
|
47
|
+
`apply_presenter` in serialization code paths.
|
|
48
|
+
|
|
49
|
+
Parameters
|
|
50
|
+
----------
|
|
51
|
+
session_state : SessionState
|
|
52
|
+
The current session state object that holds widget state and metadata.
|
|
53
|
+
widget_id : str
|
|
54
|
+
The identifier of the widget whose value is being presented.
|
|
55
|
+
base_value : Any
|
|
56
|
+
The raw value stored for the widget.
|
|
57
|
+
|
|
58
|
+
Returns
|
|
59
|
+
-------
|
|
60
|
+
Any
|
|
61
|
+
The value that should be shown to the user.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
meta = session_state._get_widget_metadata(widget_id)
|
|
66
|
+
presenter = getattr(meta, "presenter", None) if meta is not None else None
|
|
67
|
+
if presenter is None:
|
|
68
|
+
return base_value
|
|
69
|
+
|
|
70
|
+
# Ensure the presenter is callable to avoid silently failing on a
|
|
71
|
+
# TypeError when attempting to invoke a non-callable value.
|
|
72
|
+
if not callable(presenter):
|
|
73
|
+
_LOGGER.warning(
|
|
74
|
+
"Widget '%s' has a non-callable presenter (%r); returning base value.",
|
|
75
|
+
widget_id,
|
|
76
|
+
presenter,
|
|
77
|
+
)
|
|
78
|
+
return base_value
|
|
79
|
+
try:
|
|
80
|
+
return presenter(base_value, session_state)
|
|
81
|
+
except Exception:
|
|
82
|
+
return base_value
|
|
83
|
+
except Exception:
|
|
84
|
+
# If metadata is unavailable or any other error occurs, degrade gracefully.
|
|
85
|
+
return base_value
|
|
@@ -16,10 +16,10 @@ from __future__ import annotations
|
|
|
16
16
|
|
|
17
17
|
import threading
|
|
18
18
|
from contextlib import contextmanager
|
|
19
|
-
from typing import TYPE_CHECKING, Any
|
|
19
|
+
from typing import TYPE_CHECKING, Any
|
|
20
20
|
|
|
21
21
|
if TYPE_CHECKING:
|
|
22
|
-
from collections.abc import Iterator
|
|
22
|
+
from collections.abc import Callable, Iterator
|
|
23
23
|
|
|
24
24
|
from streamlit.proto.WidgetStates_pb2 import WidgetState as WidgetStateProto
|
|
25
25
|
from streamlit.proto.WidgetStates_pb2 import WidgetStates as WidgetStatesProto
|
|
@@ -23,14 +23,12 @@ from typing import (
|
|
|
23
23
|
TYPE_CHECKING,
|
|
24
24
|
Any,
|
|
25
25
|
Final,
|
|
26
|
-
|
|
26
|
+
TypeAlias,
|
|
27
27
|
cast,
|
|
28
28
|
)
|
|
29
29
|
|
|
30
|
-
from typing_extensions import TypeAlias
|
|
31
|
-
|
|
32
|
-
import streamlit as st
|
|
33
30
|
from streamlit import config, util
|
|
31
|
+
from streamlit.delta_generator_singletons import get_dg_singleton_instance
|
|
34
32
|
from streamlit.errors import StreamlitAPIException, UnserializableSessionStateError
|
|
35
33
|
from streamlit.proto.WidgetStates_pb2 import WidgetState as WidgetStateProto
|
|
36
34
|
from streamlit.proto.WidgetStates_pb2 import WidgetStates as WidgetStatesProto
|
|
@@ -39,11 +37,14 @@ from streamlit.runtime.state.common import (
|
|
|
39
37
|
RegisterWidgetResult,
|
|
40
38
|
T,
|
|
41
39
|
ValueFieldName,
|
|
40
|
+
WidgetArgs,
|
|
41
|
+
WidgetCallback,
|
|
42
42
|
WidgetMetadata,
|
|
43
43
|
is_array_value_field_name,
|
|
44
44
|
is_element_id,
|
|
45
45
|
is_keyed_element_id,
|
|
46
46
|
)
|
|
47
|
+
from streamlit.runtime.state.presentation import apply_presenter
|
|
47
48
|
from streamlit.runtime.state.query_params import QueryParams
|
|
48
49
|
from streamlit.runtime.stats import CacheStat, CacheStatsProvider, group_stats
|
|
49
50
|
|
|
@@ -71,7 +72,7 @@ class Value:
|
|
|
71
72
|
value: Any
|
|
72
73
|
|
|
73
74
|
|
|
74
|
-
WState: TypeAlias =
|
|
75
|
+
WState: TypeAlias = Value | Serialized
|
|
75
76
|
|
|
76
77
|
|
|
77
78
|
@dataclass
|
|
@@ -228,7 +229,7 @@ class WStates(MutableMapping[str, Any]):
|
|
|
228
229
|
if is_array_value_field_name(field):
|
|
229
230
|
arr = getattr(widget, field)
|
|
230
231
|
arr.data.extend(serialized)
|
|
231
|
-
elif field
|
|
232
|
+
elif field in {"json_value", "json_trigger_value"}:
|
|
232
233
|
setattr(widget, field, json.dumps(serialized))
|
|
233
234
|
elif field == "file_uploader_state_value":
|
|
234
235
|
widget.file_uploader_state_value.CopyFrom(serialized)
|
|
@@ -401,7 +402,7 @@ class SessionState:
|
|
|
401
402
|
|
|
402
403
|
@property
|
|
403
404
|
def filtered_state(self) -> dict[str, Any]:
|
|
404
|
-
"""The combined session and widget state, excluding keyless widgets."""
|
|
405
|
+
"""The combined session and widget state, excluding keyless widgets and internal widgets."""
|
|
405
406
|
|
|
406
407
|
wid_key_map = self._key_id_mapper.id_key_mapping
|
|
407
408
|
|
|
@@ -414,9 +415,10 @@ class SessionState:
|
|
|
414
415
|
for k in self._keys():
|
|
415
416
|
if not is_element_id(k) and not _is_internal_key(k):
|
|
416
417
|
state[k] = self[k]
|
|
417
|
-
elif is_keyed_element_id(k):
|
|
418
|
+
elif is_keyed_element_id(k) and not _is_internal_key(k):
|
|
418
419
|
try:
|
|
419
420
|
key = wid_key_map[k]
|
|
421
|
+
# Value returned by __getitem__ is already presented.
|
|
420
422
|
state[key] = self[k]
|
|
421
423
|
except KeyError:
|
|
422
424
|
# Widget id no longer maps to a key, it is a not yet
|
|
@@ -466,7 +468,12 @@ class SessionState:
|
|
|
466
468
|
# the "key" is a raw widget id, so get its associated user key for lookup
|
|
467
469
|
key = wid_key_map[widget_id]
|
|
468
470
|
try:
|
|
469
|
-
|
|
471
|
+
base_value = self._getitem(widget_id, key)
|
|
472
|
+
return (
|
|
473
|
+
apply_presenter(self, widget_id, base_value)
|
|
474
|
+
if widget_id is not None
|
|
475
|
+
else base_value
|
|
476
|
+
)
|
|
470
477
|
except KeyError:
|
|
471
478
|
raise KeyError(_missing_key_error_message(key))
|
|
472
479
|
|
|
@@ -576,19 +583,210 @@ class SessionState:
|
|
|
576
583
|
self._call_callbacks()
|
|
577
584
|
|
|
578
585
|
def _call_callbacks(self) -> None:
|
|
579
|
-
"""Call
|
|
580
|
-
changed between the previous and current script runs.
|
|
581
|
-
"""
|
|
586
|
+
"""Call callbacks for widgets whose value changed or whose trigger fired."""
|
|
582
587
|
from streamlit.runtime.scriptrunner import RerunException
|
|
583
588
|
|
|
584
|
-
|
|
585
|
-
|
|
589
|
+
# Path 1: single callback.
|
|
590
|
+
changed_widget_ids_for_single_callback = [
|
|
591
|
+
wid
|
|
592
|
+
for wid in self._new_widget_state
|
|
593
|
+
if self._widget_changed(wid)
|
|
594
|
+
and (metadata := self._new_widget_state.widget_metadata.get(wid))
|
|
595
|
+
is not None
|
|
596
|
+
and metadata.callback is not None
|
|
586
597
|
]
|
|
587
|
-
|
|
598
|
+
|
|
599
|
+
for wid in changed_widget_ids_for_single_callback:
|
|
588
600
|
try:
|
|
589
601
|
self._new_widget_state.call_callback(wid)
|
|
590
602
|
except RerunException: # noqa: PERF203
|
|
591
|
-
|
|
603
|
+
get_dg_singleton_instance().main_dg.warning(
|
|
604
|
+
"Calling st.rerun() within a callback is a no-op."
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
# Path 2: multiple callbacks.
|
|
608
|
+
widget_ids_to_process = list(self._new_widget_state.states.keys())
|
|
609
|
+
|
|
610
|
+
for wid in widget_ids_to_process:
|
|
611
|
+
metadata = self._new_widget_state.widget_metadata.get(wid)
|
|
612
|
+
if not metadata or metadata.callbacks is None:
|
|
613
|
+
continue
|
|
614
|
+
|
|
615
|
+
args = metadata.callback_args or ()
|
|
616
|
+
kwargs = metadata.callback_kwargs or {}
|
|
617
|
+
|
|
618
|
+
# 1) Trigger dispatch: bool + JSON trigger aggregator
|
|
619
|
+
self._dispatch_trigger_callbacks(wid, metadata, args, kwargs)
|
|
620
|
+
|
|
621
|
+
# 2) JSON value change dispatch
|
|
622
|
+
if metadata.value_type == "json_value":
|
|
623
|
+
self._dispatch_json_change_callbacks(wid, metadata, args, kwargs)
|
|
624
|
+
|
|
625
|
+
def _execute_widget_callback(
|
|
626
|
+
self,
|
|
627
|
+
callback_fn: WidgetCallback,
|
|
628
|
+
cb_metadata: WidgetMetadata[Any],
|
|
629
|
+
cb_args: WidgetArgs,
|
|
630
|
+
cb_kwargs: dict[str, Any],
|
|
631
|
+
) -> None:
|
|
632
|
+
"""Execute a widget callback with fragment-aware context.
|
|
633
|
+
|
|
634
|
+
If the widget belongs to a fragment, temporarily marks the current
|
|
635
|
+
script context as being inside a fragment callback to adapt rerun
|
|
636
|
+
semantics. Attempts to call ``st.rerun()`` inside a widget callback are
|
|
637
|
+
converted to a user-visible warning and treated as a no-op.
|
|
638
|
+
|
|
639
|
+
Parameters
|
|
640
|
+
----------
|
|
641
|
+
callback_fn : WidgetCallback
|
|
642
|
+
The user-provided callback to execute.
|
|
643
|
+
cb_metadata : WidgetMetadata[Any]
|
|
644
|
+
Metadata of the widget associated with the callback.
|
|
645
|
+
cb_args : WidgetArgs
|
|
646
|
+
Positional arguments passed to the callback.
|
|
647
|
+
cb_kwargs : dict[str, Any]
|
|
648
|
+
Keyword arguments passed to the callback.
|
|
649
|
+
"""
|
|
650
|
+
from streamlit.runtime.scriptrunner import RerunException
|
|
651
|
+
|
|
652
|
+
ctx = get_script_run_ctx()
|
|
653
|
+
if ctx and cb_metadata.fragment_id is not None:
|
|
654
|
+
ctx.in_fragment_callback = True
|
|
655
|
+
try:
|
|
656
|
+
callback_fn(*cb_args, **cb_kwargs)
|
|
657
|
+
except RerunException:
|
|
658
|
+
get_dg_singleton_instance().main_dg.warning(
|
|
659
|
+
"Calling st.rerun() within a callback is a no-op."
|
|
660
|
+
)
|
|
661
|
+
finally:
|
|
662
|
+
ctx.in_fragment_callback = False
|
|
663
|
+
else:
|
|
664
|
+
try:
|
|
665
|
+
callback_fn(*cb_args, **cb_kwargs)
|
|
666
|
+
except RerunException:
|
|
667
|
+
get_dg_singleton_instance().main_dg.warning(
|
|
668
|
+
"Calling st.rerun() within a callback is a no-op."
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
def _dispatch_trigger_callbacks(
|
|
672
|
+
self,
|
|
673
|
+
wid: str,
|
|
674
|
+
metadata: WidgetMetadata[Any],
|
|
675
|
+
args: WidgetArgs,
|
|
676
|
+
kwargs: dict[str, Any],
|
|
677
|
+
) -> None:
|
|
678
|
+
"""Dispatch trigger-style callbacks for a widget.
|
|
679
|
+
|
|
680
|
+
Handles the JSON trigger aggregator. The JSON payload may be a single
|
|
681
|
+
event dict or a list of event dicts; each event must contain an
|
|
682
|
+
``"event"`` field that maps to the corresponding callback name in
|
|
683
|
+
``metadata.callbacks``.
|
|
684
|
+
|
|
685
|
+
Examples
|
|
686
|
+
--------
|
|
687
|
+
A component with a "submit" callback:
|
|
688
|
+
|
|
689
|
+
>>> metadata.callbacks = {"submit": on_submit}
|
|
690
|
+
|
|
691
|
+
The frontend can send a single event payload:
|
|
692
|
+
|
|
693
|
+
>>> {"event": "submit", "value": "payload"}
|
|
694
|
+
|
|
695
|
+
Or a list of event payloads to be processed in order:
|
|
696
|
+
|
|
697
|
+
>>> [{"event": "edit", ...}, {"event": "submit", ...}]
|
|
698
|
+
|
|
699
|
+
Parameters
|
|
700
|
+
----------
|
|
701
|
+
wid : str
|
|
702
|
+
The widget ID.
|
|
703
|
+
metadata : WidgetMetadata[Any]
|
|
704
|
+
Metadata for the widget, including registered callbacks.
|
|
705
|
+
args : WidgetArgs
|
|
706
|
+
Positional arguments forwarded to the callback.
|
|
707
|
+
kwargs : dict[str, Any]
|
|
708
|
+
Keyword arguments forwarded to the callback.
|
|
709
|
+
"""
|
|
710
|
+
widget_proto_state = self._new_widget_state.get_serialized(wid)
|
|
711
|
+
if not widget_proto_state:
|
|
712
|
+
return
|
|
713
|
+
|
|
714
|
+
# JSON trigger aggregator: value is deserialized by metadata.deserializer
|
|
715
|
+
if widget_proto_state.json_trigger_value:
|
|
716
|
+
try:
|
|
717
|
+
deserialized = self._new_widget_state[wid]
|
|
718
|
+
except KeyError:
|
|
719
|
+
deserialized = None
|
|
720
|
+
|
|
721
|
+
payloads: list[object]
|
|
722
|
+
if isinstance(deserialized, list):
|
|
723
|
+
payloads = deserialized
|
|
724
|
+
else:
|
|
725
|
+
payloads = [deserialized]
|
|
726
|
+
|
|
727
|
+
for payload in payloads:
|
|
728
|
+
if isinstance(payload, dict):
|
|
729
|
+
event_name = payload.get("event")
|
|
730
|
+
if isinstance(event_name, str) and metadata.callbacks:
|
|
731
|
+
cb = metadata.callbacks.get(event_name)
|
|
732
|
+
if cb is not None:
|
|
733
|
+
self._execute_widget_callback(cb, metadata, args, kwargs)
|
|
734
|
+
|
|
735
|
+
def _dispatch_json_change_callbacks(
|
|
736
|
+
self,
|
|
737
|
+
wid: str,
|
|
738
|
+
metadata: WidgetMetadata[Any],
|
|
739
|
+
args: WidgetArgs,
|
|
740
|
+
kwargs: dict[str, Any],
|
|
741
|
+
) -> None:
|
|
742
|
+
"""Dispatch change callbacks for JSON-valued widgets.
|
|
743
|
+
|
|
744
|
+
Computes a shallow diff between the new and old JSON maps and invokes
|
|
745
|
+
callbacks for keys that changed or were added/removed.
|
|
746
|
+
|
|
747
|
+
Parameters
|
|
748
|
+
----------
|
|
749
|
+
wid : str
|
|
750
|
+
The widget ID.
|
|
751
|
+
metadata : WidgetMetadata[Any]
|
|
752
|
+
Metadata for the widget, including registered callbacks.
|
|
753
|
+
args : WidgetArgs
|
|
754
|
+
Positional arguments forwarded to the callback.
|
|
755
|
+
kwargs : dict[str, Any]
|
|
756
|
+
Keyword arguments forwarded to the callback.
|
|
757
|
+
"""
|
|
758
|
+
if not metadata.callbacks:
|
|
759
|
+
return
|
|
760
|
+
|
|
761
|
+
try:
|
|
762
|
+
new_val = self._new_widget_state.get(wid)
|
|
763
|
+
except KeyError:
|
|
764
|
+
new_val = None
|
|
765
|
+
old_val = self._old_state.get(wid)
|
|
766
|
+
|
|
767
|
+
def unwrap(obj: object) -> dict[str, object]:
|
|
768
|
+
if not isinstance(obj, dict):
|
|
769
|
+
return {}
|
|
770
|
+
|
|
771
|
+
obj = cast("dict[str, Any]", obj)
|
|
772
|
+
if set(obj.keys()) == {"value"}:
|
|
773
|
+
value = obj.get("value")
|
|
774
|
+
if isinstance(value, dict):
|
|
775
|
+
return dict(value) # shallow copy
|
|
776
|
+
|
|
777
|
+
return dict(obj)
|
|
778
|
+
|
|
779
|
+
new_map = unwrap(new_val)
|
|
780
|
+
old_map = unwrap(old_val)
|
|
781
|
+
|
|
782
|
+
if new_map or old_map:
|
|
783
|
+
all_keys = new_map.keys() | old_map.keys()
|
|
784
|
+
changed_keys = {k for k in all_keys if old_map.get(k) != new_map.get(k)}
|
|
785
|
+
|
|
786
|
+
for key in changed_keys:
|
|
787
|
+
cb = metadata.callbacks.get(key)
|
|
788
|
+
if cb is not None:
|
|
789
|
+
self._execute_widget_callback(cb, metadata, args, kwargs)
|
|
592
790
|
|
|
593
791
|
def _widget_changed(self, widget_id: str) -> bool:
|
|
594
792
|
"""True if the given widget's value changed between the previous
|
|
@@ -623,6 +821,7 @@ class SessionState:
|
|
|
623
821
|
elif metadata.value_type in {
|
|
624
822
|
"string_trigger_value",
|
|
625
823
|
"chat_input_value",
|
|
824
|
+
"json_trigger_value",
|
|
626
825
|
}:
|
|
627
826
|
self._new_widget_state[state_id] = Value(None)
|
|
628
827
|
|
|
@@ -634,6 +833,7 @@ class SessionState:
|
|
|
634
833
|
elif metadata.value_type in {
|
|
635
834
|
"string_trigger_value",
|
|
636
835
|
"chat_input_value",
|
|
836
|
+
"json_trigger_value",
|
|
637
837
|
}:
|
|
638
838
|
self._old_state[state_id] = None
|
|
639
839
|
|
|
@@ -663,6 +863,10 @@ class SessionState:
|
|
|
663
863
|
)
|
|
664
864
|
}
|
|
665
865
|
|
|
866
|
+
def _get_widget_metadata(self, widget_id: str) -> WidgetMetadata[Any] | None:
|
|
867
|
+
"""Return the metadata for a widget id from the current widget state."""
|
|
868
|
+
return self._new_widget_state.widget_metadata.get(widget_id)
|
|
869
|
+
|
|
666
870
|
def _set_widget_metadata(self, widget_metadata: WidgetMetadata[Any]) -> None:
|
|
667
871
|
"""Set a widget's metadata."""
|
|
668
872
|
widget_id = widget_metadata.id
|
|
@@ -16,6 +16,7 @@ from __future__ import annotations
|
|
|
16
16
|
|
|
17
17
|
from typing import TYPE_CHECKING
|
|
18
18
|
|
|
19
|
+
from streamlit.errors import StreamlitAPIException
|
|
19
20
|
from streamlit.runtime.state.common import (
|
|
20
21
|
RegisterWidgetResult,
|
|
21
22
|
T,
|
|
@@ -26,6 +27,7 @@ from streamlit.runtime.state.common import (
|
|
|
26
27
|
WidgetKwargs,
|
|
27
28
|
WidgetMetadata,
|
|
28
29
|
WidgetSerializer,
|
|
30
|
+
WidgetValuePresenter,
|
|
29
31
|
user_key_from_element_id,
|
|
30
32
|
)
|
|
31
33
|
|
|
@@ -39,10 +41,12 @@ def register_widget(
|
|
|
39
41
|
deserializer: WidgetDeserializer[T],
|
|
40
42
|
serializer: WidgetSerializer[T],
|
|
41
43
|
ctx: ScriptRunContext | None,
|
|
44
|
+
callbacks: dict[str, WidgetCallback] | None = None,
|
|
42
45
|
on_change_handler: WidgetCallback | None = None,
|
|
43
46
|
args: WidgetArgs | None = None,
|
|
44
47
|
kwargs: WidgetKwargs | None = None,
|
|
45
48
|
value_type: ValueFieldName,
|
|
49
|
+
presenter: WidgetValuePresenter | None = None,
|
|
46
50
|
) -> RegisterWidgetResult[T]:
|
|
47
51
|
"""Register a widget with Streamlit, and return its current value.
|
|
48
52
|
NOTE: This function should be called after the proto has been filled.
|
|
@@ -58,13 +62,15 @@ def register_widget(
|
|
|
58
62
|
Called to convert a widget's value to its protobuf representation.
|
|
59
63
|
ctx : ScriptRunContext or None
|
|
60
64
|
Used to ensure uniqueness of widget IDs, and to look up widget values.
|
|
65
|
+
callbacks : dict[str, WidgetCallback] or None
|
|
66
|
+
A dictionary of callbacks for multi-callback support.
|
|
61
67
|
on_change_handler : WidgetCallback or None
|
|
62
68
|
An optional callback invoked when the widget's value changes.
|
|
63
69
|
args : WidgetArgs or None
|
|
64
|
-
|
|
70
|
+
Positional arguments to pass to the `on_change_handler` or `callbacks`.
|
|
65
71
|
kwargs : WidgetKwargs or None
|
|
66
|
-
|
|
67
|
-
value_type:
|
|
72
|
+
Keyword arguments to pass to the `on_change_handler` or `callbacks`.
|
|
73
|
+
value_type: ValueFieldName
|
|
68
74
|
The value_type the widget is going to use.
|
|
69
75
|
We use this information to start with a best-effort guess for the value_type
|
|
70
76
|
of each widget. Once we actually receive a proto for a widget from the
|
|
@@ -72,6 +78,9 @@ def register_widget(
|
|
|
72
78
|
not able to always rely on the proto as the type may be needed earlier.
|
|
73
79
|
Thankfully, in these cases (when value_type == "trigger_value"), the static
|
|
74
80
|
table here being slightly inaccurate should never pose a problem.
|
|
81
|
+
presenter : WidgetValuePresenter or None
|
|
82
|
+
An optional hook that allows a widget to customize how its value should be
|
|
83
|
+
presented.
|
|
75
84
|
|
|
76
85
|
|
|
77
86
|
Returns
|
|
@@ -98,6 +107,11 @@ def register_widget(
|
|
|
98
107
|
For both paths a widget return value is provided, allowing the widgets
|
|
99
108
|
to be used in a non-streamlit setting.
|
|
100
109
|
"""
|
|
110
|
+
if on_change_handler is not None and callbacks is not None:
|
|
111
|
+
raise StreamlitAPIException(
|
|
112
|
+
"Cannot provide both `on_change` and `callbacks` to a widget."
|
|
113
|
+
)
|
|
114
|
+
|
|
101
115
|
# Create the widget's updated metadata, and register it with session_state.
|
|
102
116
|
metadata = WidgetMetadata(
|
|
103
117
|
element_id,
|
|
@@ -105,9 +119,11 @@ def register_widget(
|
|
|
105
119
|
serializer,
|
|
106
120
|
value_type=value_type,
|
|
107
121
|
callback=on_change_handler,
|
|
122
|
+
callbacks=callbacks,
|
|
108
123
|
callback_args=args,
|
|
109
124
|
callback_kwargs=kwargs,
|
|
110
125
|
fragment_id=ctx.current_fragment_id if ctx else None,
|
|
126
|
+
presenter=presenter,
|
|
111
127
|
)
|
|
112
128
|
return register_widget_from_metadata(metadata, ctx)
|
|
113
129
|
|