streamlit 1.53.0__py3-none-any.whl → 1.54.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 +1 -31
- streamlit/auth_util.py +91 -2
- streamlit/cli_util.py +3 -2
- streamlit/commands/echo.py +2 -2
- streamlit/commands/execution_control.py +1 -1
- streamlit/commands/logo.py +76 -24
- streamlit/commands/navigation.py +1 -1
- streamlit/components/types/base_custom_component.py +0 -2
- streamlit/components/v1/custom_component.py +0 -2
- streamlit/components/v2/bidi_component/main.py +2 -2
- streamlit/components/v2/component_path_utils.py +17 -29
- streamlit/components/v2/manifest_scanner.py +8 -3
- streamlit/components/v2/presentation.py +1 -1
- streamlit/config.py +57 -13
- streamlit/config_util.py +5 -5
- streamlit/connections/snowflake_connection.py +6 -3
- streamlit/dataframe_util.py +10 -10
- streamlit/deprecation_util.py +19 -1
- streamlit/elements/arrow.py +18 -8
- streamlit/elements/deck_gl_json_chart.py +6 -2
- streamlit/elements/exception.py +4 -2
- streamlit/elements/form.py +1 -1
- streamlit/elements/layouts.py +1 -1
- streamlit/elements/lib/built_in_chart_utils.py +36 -13
- streamlit/elements/lib/color_util.py +21 -2
- streamlit/elements/lib/column_config_utils.py +9 -7
- streamlit/elements/lib/dialog.py +1 -1
- streamlit/elements/lib/image_utils.py +5 -5
- streamlit/elements/lib/layout_utils.py +1 -1
- streamlit/elements/lib/options_selector_utils.py +112 -18
- streamlit/elements/lib/policies.py +1 -1
- streamlit/elements/lib/streamlit_plotly_theme.py +9 -11
- streamlit/elements/lib/utils.py +1 -1
- streamlit/elements/map.py +6 -6
- streamlit/elements/plotly_chart.py +2 -2
- streamlit/elements/toast.py +1 -1
- streamlit/elements/vega_charts.py +30 -7
- streamlit/elements/widgets/button.py +3 -3
- streamlit/elements/widgets/button_group.py +3 -3
- streamlit/elements/widgets/chat.py +1 -1
- streamlit/elements/widgets/data_editor.py +6 -6
- streamlit/elements/widgets/multiselect.py +32 -8
- streamlit/elements/widgets/number_input.py +1 -1
- streamlit/elements/widgets/radio.py +91 -31
- streamlit/elements/widgets/select_slider.py +123 -37
- streamlit/elements/widgets/selectbox.py +38 -16
- streamlit/elements/widgets/slider.py +5 -5
- streamlit/elements/widgets/time_widgets.py +150 -18
- streamlit/elements/write.py +2 -3
- streamlit/env_util.py +1 -1
- streamlit/errors.py +2 -14
- streamlit/external/langchain/streamlit_callback_handler.py +1 -1
- streamlit/hello/dataframe_demo.py +1 -1
- streamlit/hello/plotting_demo.py +19 -12
- streamlit/path_security.py +98 -0
- streamlit/proto/Alert_pb2.py +2 -3
- streamlit/proto/AppPage_pb2.py +2 -3
- streamlit/proto/ArrowData_pb2.py +2 -3
- streamlit/proto/ArrowNamedDataSet_pb2.py +2 -3
- streamlit/proto/ArrowVegaLiteChart_pb2.py +2 -3
- streamlit/proto/Arrow_pb2.py +2 -3
- streamlit/proto/AudioInput_pb2.py +2 -3
- streamlit/proto/Audio_pb2.py +2 -3
- streamlit/proto/AuthRedirect_pb2.py +2 -3
- streamlit/proto/AutoRerun_pb2.py +2 -3
- streamlit/proto/BackMsg_pb2.py +2 -3
- streamlit/proto/Balloons_pb2.py +2 -3
- streamlit/proto/BidiComponent_pb2.py +2 -3
- streamlit/proto/Block_pb2.py +2 -3
- streamlit/proto/BokehChart_pb2.py +2 -3
- streamlit/proto/ButtonGroup_pb2.py +2 -3
- streamlit/proto/ButtonLikeIconPosition_pb2.py +2 -3
- streamlit/proto/Button_pb2.py +2 -3
- streamlit/proto/CameraInput_pb2.py +2 -3
- streamlit/proto/ChatInput_pb2.py +2 -3
- streamlit/proto/Checkbox_pb2.py +2 -3
- streamlit/proto/ClientState_pb2.py +2 -3
- streamlit/proto/Code_pb2.py +2 -3
- streamlit/proto/ColorPicker_pb2.py +2 -3
- streamlit/proto/Common_pb2.py +2 -3
- streamlit/proto/Components_pb2.py +2 -3
- streamlit/proto/DataFrame_pb2.py +2 -3
- streamlit/proto/DateInput_pb2.py +2 -3
- streamlit/proto/DateTimeInput_pb2.py +2 -3
- streamlit/proto/DeckGlJsonChart_pb2.py +2 -3
- streamlit/proto/Delta_pb2.py +2 -3
- streamlit/proto/DocString_pb2.py +2 -3
- streamlit/proto/DownloadButton_pb2.py +2 -3
- streamlit/proto/Element_pb2.py +2 -3
- streamlit/proto/Empty_pb2.py +2 -3
- streamlit/proto/Exception_pb2.py +2 -3
- streamlit/proto/Favicon_pb2.py +2 -3
- streamlit/proto/FileUploader_pb2.py +2 -3
- streamlit/proto/ForwardMsg_pb2.py +2 -3
- streamlit/proto/GapSize_pb2.py +2 -3
- streamlit/proto/GitInfo_pb2.py +2 -3
- streamlit/proto/GraphVizChart_pb2.py +2 -3
- streamlit/proto/Heading_pb2.py +2 -3
- streamlit/proto/HeightConfig_pb2.py +2 -3
- streamlit/proto/Html_pb2.py +2 -3
- streamlit/proto/IFrame_pb2.py +2 -3
- streamlit/proto/Image_pb2.py +2 -3
- streamlit/proto/Json_pb2.py +2 -3
- streamlit/proto/LabelVisibilityMessage_pb2.py +2 -3
- streamlit/proto/LinkButton_pb2.py +2 -3
- streamlit/proto/Logo_pb2.py +6 -5
- streamlit/proto/Logo_pb2.pyi +25 -1
- streamlit/proto/Markdown_pb2.py +2 -3
- streamlit/proto/Metric_pb2.py +2 -3
- streamlit/proto/MetricsEvent_pb2.py +2 -3
- streamlit/proto/MultiSelect_pb2.py +2 -3
- streamlit/proto/NamedDataSet_pb2.py +2 -3
- streamlit/proto/Navigation_pb2.py +2 -3
- streamlit/proto/NewSession_pb2.py +25 -24
- streamlit/proto/NewSession_pb2.pyi +28 -2
- streamlit/proto/NumberInput_pb2.py +2 -3
- streamlit/proto/PageConfig_pb2.py +2 -3
- streamlit/proto/PageInfo_pb2.py +2 -3
- streamlit/proto/PageLink_pb2.py +2 -3
- streamlit/proto/PageNotFound_pb2.py +2 -3
- streamlit/proto/PageProfile_pb2.py +2 -3
- streamlit/proto/PagesChanged_pb2.py +2 -3
- streamlit/proto/ParentMessage_pb2.py +2 -3
- streamlit/proto/PlotlyChart_pb2.py +2 -3
- streamlit/proto/Progress_pb2.py +2 -3
- streamlit/proto/Radio_pb2.py +5 -4
- streamlit/proto/Radio_pb2.pyi +20 -3
- streamlit/proto/RootContainer_pb2.py +2 -3
- streamlit/proto/Selectbox_pb2.py +2 -3
- streamlit/proto/SessionEvent_pb2.py +2 -3
- streamlit/proto/SessionStatus_pb2.py +2 -3
- streamlit/proto/Skeleton_pb2.py +2 -3
- streamlit/proto/Slider_pb2.py +7 -8
- streamlit/proto/Slider_pb2.pyi +9 -1
- streamlit/proto/Snow_pb2.py +2 -3
- streamlit/proto/Space_pb2.py +2 -3
- streamlit/proto/Spinner_pb2.py +2 -3
- streamlit/proto/TextAlignmentConfig_pb2.py +2 -3
- streamlit/proto/TextArea_pb2.py +2 -3
- streamlit/proto/TextInput_pb2.py +2 -3
- streamlit/proto/Text_pb2.py +2 -3
- streamlit/proto/TimeInput_pb2.py +2 -3
- streamlit/proto/Toast_pb2.py +2 -3
- streamlit/proto/Transient_pb2.py +2 -3
- streamlit/proto/VegaLiteChart_pb2.py +2 -3
- streamlit/proto/Video_pb2.py +2 -3
- streamlit/proto/WidgetStates_pb2.py +2 -3
- streamlit/proto/WidthConfig_pb2.py +2 -3
- streamlit/proto/openmetrics_data_model_pb2.py +2 -3
- streamlit/runtime/app_session.py +106 -60
- streamlit/runtime/caching/cache_data_api.py +3 -3
- streamlit/runtime/caching/cache_errors.py +0 -2
- streamlit/runtime/caching/cache_resource_api.py +1 -1
- streamlit/runtime/caching/cache_utils.py +2 -2
- streamlit/runtime/caching/hashing.py +1 -3
- streamlit/runtime/caching/storage/cache_storage_protocol.py +0 -3
- streamlit/runtime/connection_factory.py +1 -1
- streamlit/runtime/credentials.py +2 -2
- streamlit/runtime/metrics_util.py +3 -3
- streamlit/runtime/runtime.py +6 -6
- streamlit/runtime/scriptrunner/script_runner.py +17 -0
- streamlit/runtime/scriptrunner_utils/exceptions.py +0 -4
- streamlit/runtime/scriptrunner_utils/script_run_context.py +13 -31
- streamlit/runtime/secrets.py +3 -4
- streamlit/runtime/state/__init__.py +7 -1
- streamlit/runtime/state/common.py +13 -0
- streamlit/runtime/state/query_params.py +493 -24
- streamlit/runtime/state/session_state.py +179 -4
- streamlit/runtime/state/widgets.py +26 -1
- streamlit/runtime/stats.py +1 -10
- streamlit/static/index.html +1 -1
- streamlit/static/manifest.json +304 -304
- streamlit/static/static/js/{ErrorOutline.esm.Cxoit62D.js → ErrorOutline.esm.BWk6F-Tz.js} +1 -1
- streamlit/static/static/js/{FileDownload.esm.Cym2KVOR.js → FileDownload.esm.AllYUuOW.js} +1 -1
- streamlit/static/static/js/{FileHelper.C47VLeXF.js → FileHelper.BvVTNdmy.js} +1 -1
- streamlit/static/static/js/{FormClearHelper.CUrwwEeX.js → FormClearHelper.C__r5Llk.js} +1 -1
- streamlit/static/static/js/{InputInstructions.DyVOE42q.js → InputInstructions.DOtkdOMV.js} +1 -1
- streamlit/static/static/js/Particles.DCsqQZlE.js +1 -0
- streamlit/static/static/js/{ProgressBar.qKdiDYyx.js → ProgressBar.DLCRvt4m.js} +2 -2
- streamlit/static/static/js/{StreamlitSyntaxHighlighter.DUPp9dS3.js → StreamlitSyntaxHighlighter.CYFWoZHb.js} +1 -1
- streamlit/static/static/js/{TableChart.esm.C_g2CvCE.js → TableChart.esm.D6ydHcIm.js} +1 -1
- streamlit/static/static/js/Toolbar.BHDNzWBx.js +1 -0
- streamlit/static/static/js/{WidgetLabelHelpIconInline.Dy4yV6I2.js → WidgetLabelHelpIconInline.DEXBrVlc.js} +1 -1
- streamlit/static/static/js/{base-input.DQAb60v0.js → base-input.TSQjctlq.js} +4 -4
- streamlit/static/static/js/{checkbox.C0HE0ojW.js → checkbox.BKgfzJZV.js} +1 -1
- streamlit/static/static/js/{createDownloadLinkElement.DBMfH8_e.js → createDownloadLinkElement.CG7nr2a4.js} +1 -1
- streamlit/static/static/js/{data-grid-overlay-editor.CSZWem5Q.js → data-grid-overlay-editor.ChXO__lP.js} +1 -1
- streamlit/static/static/js/{downloader.Bp8c7mYD.js → downloader.DJ3R_zWA.js} +1 -1
- streamlit/static/static/js/embed.u3PPfLkw.js +193 -0
- streamlit/static/static/js/{es6.j7akTCaI.js → es6.C5Mfy8nd.js} +2 -2
- streamlit/static/static/js/{formatNumber.CfuUiEpF.js → formatNumber.CMRgW9EJ.js} +1 -1
- streamlit/static/static/js/{iconPosition.BVSTKfGd.js → iconPosition.B4EEXI3E.js} +1 -1
- streamlit/static/static/js/{iframeResizer.contentWindow.BZ3lugzo.js → iframeResizer.contentWindow.WSvOiTW0.js} +1 -1
- streamlit/static/static/js/index.-FOBV3nz.js +1 -0
- streamlit/static/static/js/{index.D0tXFTaW.js → index.-NF8OSF5.js} +1 -1
- streamlit/static/static/js/{index.Dk0CU4R6.js → index.4cBg8kn5.js} +1 -1
- streamlit/static/static/js/{index.DtZTtufl.js → index.B0pzzCsH.js} +1 -1
- streamlit/static/static/js/{index.DSaE74nc.js → index.BID6ND5j.js} +2 -2
- streamlit/static/static/js/index.BMp5bGjh.js +1 -0
- streamlit/static/static/js/{index.CAMxgVFm.js → index.BQcmlvas.js} +1 -1
- streamlit/static/static/js/{index.C0F0G-wg.js → index.BRcmclgI.js} +1 -1
- streamlit/static/static/js/index.BaUZR4IG.js +1 -0
- streamlit/static/static/js/{index.Cow0Hs9V.js → index.BbMJj4PN.js} +1 -1
- streamlit/static/static/js/{index.iboGgrkh.js → index.BdCTJtq3.js} +2 -2
- streamlit/static/static/js/index.BdETLMuI.js +1 -0
- streamlit/static/static/js/index.BnKMWhs1.js +1 -0
- streamlit/static/static/js/index.Br1kXwQW.js +2 -0
- streamlit/static/static/js/{index.B2fTHpId.js → index.Bt2olRE4.js} +1 -1
- streamlit/static/static/js/{index.DBIRzFM7.js → index.Bxwsv5T8.js} +1 -1
- streamlit/static/static/js/index.C4KskYz6.js +1 -0
- streamlit/static/static/js/{index.BgCYNmov.js → index.C6bmbXk0.js} +1 -1
- streamlit/static/static/js/{index.7S_sCSRx.js → index.CEfKfbta.js} +1 -1
- streamlit/static/static/js/index.CIuaA8q0.js +2 -0
- streamlit/static/static/js/{index.CWAvu1Qu.js → index.CV1sObFX.js} +1 -1
- streamlit/static/static/js/{index.C9QftD-S.js → index.CbR6dgaV.js} +1 -1
- streamlit/static/static/js/index.Cq6szKqJ.js +1 -0
- streamlit/static/static/js/index.CyouXqCz.js +1 -0
- streamlit/static/static/js/{index.BMFt07G_.js → index.D1NUgMFI.js} +1 -1
- streamlit/static/static/js/{index.Tq2okoAU.js → index.D7SWG4Om.js} +1 -1
- streamlit/static/static/js/{index.DgJeIFb5.js → index.DAYPEwLI.js} +1 -1
- streamlit/static/static/js/index.DKS75Vfg.js +11 -0
- streamlit/static/static/js/{index.FfR9SXQv.js → index.DOXrMIxB.js} +1 -1
- streamlit/static/static/js/{index.BiVJWMS-.js → index.DOzYX8yS.js} +3 -3
- streamlit/static/static/js/{index.nEa8y_He.js → index.DRFMYcC4.js} +4 -4
- streamlit/static/static/js/{index.dgs1TGpP.js → index.Divl5FCY.js} +1 -1
- streamlit/static/static/js/{index.95DldRtG.js → index.DjAJ_CUa.js} +1 -1
- streamlit/static/static/js/{index.Z0mB4zBp.js → index.Dncue2pm.js} +33 -33
- streamlit/static/static/js/{index.DFT9nVK6.js → index.Drusyo5m.js} +48 -48
- streamlit/static/static/js/{index.1PD6f3vh.js → index.DuUyDGnP.js} +1 -1
- streamlit/static/static/js/{index.DpU0Bc2F.js → index.DvgT2rB2.js} +223 -223
- streamlit/static/static/js/{index.Bukztsaz.js → index.DzutABu5.js} +2 -2
- streamlit/static/static/js/index.Dzw2iPzi.js +3 -0
- streamlit/static/static/js/{index.DYkkO_of.js → index.FsTmxLbT.js} +1 -1
- streamlit/static/static/js/{index.CTQ8QcOV.js → index.OIwPqGYN.js} +1 -1
- streamlit/static/static/js/{index.NtSfVVJe.js → index.RXLN7YFT.js} +2 -2
- streamlit/static/static/js/{index.BU3d_gp1.js → index.YYb2u0jk.js} +2 -2
- streamlit/static/static/js/{index.BXfSsjdq.js → index.h8ejt-W3.js} +1 -1
- streamlit/static/static/js/{index.gPUFpUqs.js → index.lFMCi9am.js} +1 -1
- streamlit/static/static/js/{index.BDA5l7b9.js → index.pOgf4cEj.js} +1 -1
- streamlit/static/static/js/index.s_E0s7LB.js +188 -0
- streamlit/static/static/js/{index.DysJZEAt.js → index.xLCbzoqj.js} +1 -1
- streamlit/static/static/js/{input.Pz8Lwzsi.js → input.BLG7kWaj.js} +2 -2
- streamlit/static/static/js/{main.BeiYkHRo.js → main.D_CmqChN.js} +1 -1
- streamlit/static/static/js/{memory.Dyx_JBbb.js → memory.T8u9KqIQ.js} +1 -1
- streamlit/static/static/js/{number-overlay-editor.NLIdF6b9.js → number-overlay-editor.BKBSXkAM.js} +2 -2
- streamlit/static/static/js/{pandasStylerUtils.DsNlDEqS.js → pandasStylerUtils.B4tLYMwS.js} +1 -1
- streamlit/static/static/js/{sandbox.bER7qtR1.js → sandbox.jRlkcPem.js} +1 -1
- streamlit/static/static/js/{styled-components.DcoFBb7G.js → styled-components.D2QhNwzd.js} +1 -1
- streamlit/static/static/js/{throttle.DOaQWO4U.js → throttle.Cyw_V0Dq.js} +1 -1
- streamlit/static/static/js/{timepicker.RjHB2IT4.js → timepicker.PzyuDDWl.js} +1 -1
- streamlit/static/static/js/{toConsumableArray.DFAIugL0.js → toConsumableArray.gE9fMkLj.js} +1 -1
- streamlit/static/static/js/uniqueId.B1GeHnT1.js +1 -0
- streamlit/static/static/js/{useBasicWidgetState.CTtyymrp.js → useBasicWidgetState.DFklfao0.js} +1 -1
- streamlit/static/static/js/{useIntlLocale.DG5haQGX.js → useIntlLocale.C3tUGWTU.js} +8 -8
- streamlit/static/static/js/{useTextInputAutoExpand.Cnfcep1Z.js → useTextInputAutoExpand.D9nU_y-e.js} +1 -1
- streamlit/static/static/js/useUpdateUiValue.ClTdrkJN.js +1 -0
- streamlit/static/static/js/{useWaveformController.DozaayUB.js → useWaveformController.lzTbjMW2.js} +1 -1
- streamlit/static/static/js/{withCalculatedWidth.SNNFFxhJ.js → withCalculatedWidth.Dxs9I5Oe.js} +1 -1
- streamlit/static/static/js/{withFullScreenWrapper.Dl2f8_gt.js → withFullScreenWrapper.DfpAcJxf.js} +1 -1
- streamlit/string_util.py +2 -2
- streamlit/testing/v1/app_test.py +1 -1
- streamlit/testing/v1/element_tree.py +33 -20
- streamlit/type_util.py +2 -2
- streamlit/url_util.py +2 -2
- streamlit/user_info.py +2 -41
- streamlit/util.py +1 -1
- streamlit/watcher/event_based_path_watcher.py +37 -7
- streamlit/watcher/path_watcher.py +61 -2
- streamlit/watcher/util.py +26 -10
- streamlit/web/bootstrap.py +16 -4
- streamlit/web/cli.py +1 -4
- streamlit/web/server/app_discovery.py +2 -1
- streamlit/web/server/app_static_file_handler.py +9 -0
- streamlit/web/server/bidi_component_request_handler.py +4 -4
- streamlit/web/server/component_file_utils.py +14 -6
- streamlit/web/server/component_request_handler.py +2 -2
- streamlit/web/server/oauth_authlib_routes.py +14 -42
- streamlit/web/server/server.py +1 -1
- streamlit/web/server/server_util.py +23 -1
- streamlit/web/server/starlette/starlette_app.py +7 -1
- streamlit/web/server/starlette/starlette_auth_routes.py +94 -16
- streamlit/web/server/starlette/starlette_path_security_middleware.py +97 -0
- streamlit/web/server/starlette/starlette_routes.py +16 -9
- streamlit/web/server/starlette/starlette_server.py +2 -2
- streamlit/web/server/starlette/starlette_static_routes.py +14 -4
- streamlit/web/server/stats_request_handler.py +1 -3
- {streamlit-1.53.0.dist-info → streamlit-1.54.0.dist-info}/METADATA +10 -25
- {streamlit-1.53.0.dist-info → streamlit-1.54.0.dist-info}/RECORD +291 -291
- {streamlit-1.53.0.dist-info → streamlit-1.54.0.dist-info}/WHEEL +1 -1
- streamlit/commands/experimental_query_params.py +0 -169
- streamlit/static/static/js/Particles.D5ZUTvE6.js +0 -1
- streamlit/static/static/js/Toolbar.BbO8bxwz.js +0 -1
- streamlit/static/static/js/embed.DQBlGL9Q.js +0 -195
- streamlit/static/static/js/index.5CsPRetw.js +0 -1
- streamlit/static/static/js/index.BGgra9Bb.js +0 -188
- streamlit/static/static/js/index.BGzJYcHz.js +0 -1
- streamlit/static/static/js/index.BNpEDrb2.js +0 -1
- streamlit/static/static/js/index.Bk5wGJXh.js +0 -1
- streamlit/static/static/js/index.By8GIgDH.js +0 -1
- streamlit/static/static/js/index.C8VoW8Ph.js +0 -1
- streamlit/static/static/js/index.CZzy-Gct.js +0 -1
- streamlit/static/static/js/index.CeFdbzfR.js +0 -11
- streamlit/static/static/js/index.CkmNfvPD.js +0 -1
- streamlit/static/static/js/index.CsmTnJl4.js +0 -3
- streamlit/static/static/js/index.DZGCJu4I.js +0 -2
- streamlit/static/static/js/index.svncz-Ad.js +0 -2
- streamlit/static/static/js/uniqueId.DEvFPH9n.js +0 -1
- streamlit/static/static/js/useUpdateUiValue.BWnXwmrp.js +0 -1
- streamlit-1.53.0.data/scripts/streamlit.cmd +0 -16
- {streamlit-1.53.0.dist-info → streamlit-1.54.0.dist-info}/entry_points.txt +0 -0
- {streamlit-1.53.0.dist-info → streamlit-1.54.0.dist-info}/top_level.txt +0 -0
|
@@ -290,12 +290,14 @@ def validate_and_sync_value_with_options(
|
|
|
290
290
|
opt: Sequence[T],
|
|
291
291
|
default_index: int | None,
|
|
292
292
|
key: str | int | None,
|
|
293
|
+
format_func: Callable[[Any], str] = str,
|
|
293
294
|
) -> tuple[T | None, bool]:
|
|
294
295
|
"""Validate current value against options, resetting session state if invalid.
|
|
295
296
|
|
|
296
297
|
This function has a side-effect: if the value is not found in the options
|
|
297
298
|
and a key is provided, it will update session state with the new value.
|
|
298
299
|
|
|
300
|
+
|
|
299
301
|
Parameters
|
|
300
302
|
----------
|
|
301
303
|
current_value
|
|
@@ -306,6 +308,11 @@ def validate_and_sync_value_with_options(
|
|
|
306
308
|
The default index to reset to if value is invalid.
|
|
307
309
|
key
|
|
308
310
|
The widget key for session state updates.
|
|
311
|
+
format_func
|
|
312
|
+
Function to format options for comparison. Used to compare values by their
|
|
313
|
+
string representation instead of using == directly. This is necessary because
|
|
314
|
+
widget values are deepcopied, and for custom classes without __eq__, the
|
|
315
|
+
deepcopied instances would fail identity comparison.
|
|
309
316
|
|
|
310
317
|
Returns
|
|
311
318
|
-------
|
|
@@ -315,30 +322,37 @@ def validate_and_sync_value_with_options(
|
|
|
315
322
|
if current_value is None:
|
|
316
323
|
return current_value, False
|
|
317
324
|
|
|
318
|
-
#
|
|
325
|
+
# Use format_func comparison for all values. This correctly handles:
|
|
326
|
+
# - Custom objects without __eq__ (deepcopied instances)
|
|
327
|
+
# - Enum values (already from current class due to serde deserialization)
|
|
328
|
+
formatted_options_set = {format_func(o) for o in opt}
|
|
319
329
|
try:
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
330
|
+
formatted_value = format_func(current_value)
|
|
331
|
+
if formatted_value in formatted_options_set:
|
|
332
|
+
return current_value, False
|
|
333
|
+
except Exception: # noqa: S110
|
|
334
|
+
pass # format_func failed - value is invalid, fall through to reset
|
|
335
|
+
|
|
336
|
+
# Value not in options - reset to default
|
|
337
|
+
if default_index is not None and len(opt) > 0:
|
|
338
|
+
new_value: T | None = opt[default_index]
|
|
339
|
+
else:
|
|
340
|
+
new_value = None
|
|
328
341
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
342
|
+
if key is not None:
|
|
343
|
+
# Update session_state so subsequent accesses in this run
|
|
344
|
+
# return the corrected value. Use reset_state_value to avoid
|
|
345
|
+
# the "cannot be modified after widget instantiated" error.
|
|
346
|
+
get_session_state().reset_state_value(str(key), new_value)
|
|
334
347
|
|
|
335
|
-
|
|
348
|
+
return new_value, True
|
|
336
349
|
|
|
337
350
|
|
|
338
351
|
def validate_and_sync_multiselect_value_with_options(
|
|
339
352
|
current_values: list[T] | list[T | str],
|
|
340
353
|
opt: Sequence[T],
|
|
341
354
|
key: str | int | None,
|
|
355
|
+
format_func: Callable[[Any], str] = str,
|
|
342
356
|
) -> tuple[list[T] | list[T | str], bool]:
|
|
343
357
|
"""Validate multiselect values against options, syncing session state if needed.
|
|
344
358
|
|
|
@@ -356,6 +370,11 @@ def validate_and_sync_multiselect_value_with_options(
|
|
|
356
370
|
The sequence of valid options.
|
|
357
371
|
key
|
|
358
372
|
The widget key for session state updates.
|
|
373
|
+
format_func
|
|
374
|
+
Function to format options for comparison. Used to compare values by their
|
|
375
|
+
string representation instead of using == directly. This is necessary because
|
|
376
|
+
widget values are deepcopied, and for custom classes without __eq__, the
|
|
377
|
+
deepcopied instances would fail identity comparison.
|
|
359
378
|
|
|
360
379
|
Returns
|
|
361
380
|
-------
|
|
@@ -365,13 +384,26 @@ def validate_and_sync_multiselect_value_with_options(
|
|
|
365
384
|
if not current_values:
|
|
366
385
|
return current_values, False
|
|
367
386
|
|
|
387
|
+
# Create a set of formatted options for O(1) lookup.
|
|
388
|
+
# We use format_func to compare values by their string representation
|
|
389
|
+
# instead of using == directly. This is necessary because widget values
|
|
390
|
+
# are deepcopied, and for custom classes without __eq__, the deepcopied
|
|
391
|
+
# instances would fail identity comparison.
|
|
392
|
+
formatted_options_set = {format_func(o) for o in opt}
|
|
393
|
+
|
|
368
394
|
valid_values: list[T | str] = []
|
|
369
395
|
for value in current_values:
|
|
370
396
|
try:
|
|
371
|
-
|
|
397
|
+
formatted_value = format_func(value)
|
|
398
|
+
except Exception: # noqa: S112
|
|
399
|
+
# format_func failed on this value (e.g., a string value from a previous
|
|
400
|
+
# session when format_func expects an object with specific attributes).
|
|
401
|
+
# In this case, the value is definitely not valid since the current options
|
|
402
|
+
# can be formatted successfully.
|
|
403
|
+
continue
|
|
404
|
+
|
|
405
|
+
if formatted_value in formatted_options_set:
|
|
372
406
|
valid_values.append(value)
|
|
373
|
-
except ValueError: # noqa: PERF203
|
|
374
|
-
pass
|
|
375
407
|
|
|
376
408
|
if len(valid_values) == len(current_values):
|
|
377
409
|
return current_values, False
|
|
@@ -380,3 +412,65 @@ def validate_and_sync_multiselect_value_with_options(
|
|
|
380
412
|
get_session_state().reset_state_value(str(key), valid_values)
|
|
381
413
|
|
|
382
414
|
return valid_values, True
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def validate_and_sync_range_value_with_options(
|
|
418
|
+
current_value: tuple[T, T],
|
|
419
|
+
opt: Sequence[T],
|
|
420
|
+
default_indices: list[int],
|
|
421
|
+
key: str | int | None,
|
|
422
|
+
format_func: Callable[[Any], str] = str,
|
|
423
|
+
) -> tuple[tuple[T, T], bool]:
|
|
424
|
+
"""Validate a range value (tuple of two values) against options.
|
|
425
|
+
|
|
426
|
+
If either value in the range is not found in options, the entire range is
|
|
427
|
+
reset to the default. This function has a side-effect: if the values are
|
|
428
|
+
invalid and a key is provided, it will update session state with the new value.
|
|
429
|
+
|
|
430
|
+
Parameters
|
|
431
|
+
----------
|
|
432
|
+
current_value
|
|
433
|
+
The current range value (tuple of two values) to validate.
|
|
434
|
+
opt
|
|
435
|
+
The sequence of valid options.
|
|
436
|
+
default_indices
|
|
437
|
+
The default indices to reset to if value is invalid. Should contain
|
|
438
|
+
at least one index; if only one index is provided, the second default
|
|
439
|
+
will be the last option.
|
|
440
|
+
key
|
|
441
|
+
The widget key for session state updates.
|
|
442
|
+
format_func
|
|
443
|
+
Function to format options for comparison. Used to compare values by their
|
|
444
|
+
string representation instead of using == directly.
|
|
445
|
+
|
|
446
|
+
Returns
|
|
447
|
+
-------
|
|
448
|
+
tuple[tuple[T, T], bool]
|
|
449
|
+
A tuple of (validated_value, value_was_reset).
|
|
450
|
+
"""
|
|
451
|
+
if len(opt) == 0:
|
|
452
|
+
return current_value, False
|
|
453
|
+
|
|
454
|
+
formatted_options_set = {format_func(o) for o in opt}
|
|
455
|
+
|
|
456
|
+
def is_valid(val: Any) -> bool:
|
|
457
|
+
"""Check if a value exists in options via format_func comparison."""
|
|
458
|
+
try:
|
|
459
|
+
return format_func(val) in formatted_options_set
|
|
460
|
+
except Exception:
|
|
461
|
+
return False
|
|
462
|
+
|
|
463
|
+
def get_default_range() -> tuple[T, T]:
|
|
464
|
+
"""Get the default range value."""
|
|
465
|
+
end_idx = default_indices[1] if len(default_indices) > 1 else len(opt) - 1
|
|
466
|
+
return (opt[default_indices[0]], opt[end_idx])
|
|
467
|
+
|
|
468
|
+
# Validate both values in the range.
|
|
469
|
+
if is_valid(current_value[0]) and is_valid(current_value[1]):
|
|
470
|
+
return current_value, False
|
|
471
|
+
|
|
472
|
+
# Either value is invalid - reset entire range.
|
|
473
|
+
new_value = get_default_range()
|
|
474
|
+
if key is not None:
|
|
475
|
+
get_session_state().reset_state_value(str(key), new_value)
|
|
476
|
+
return new_value, True
|
|
@@ -188,7 +188,7 @@ def maybe_raise_label_warnings(label: str | None, label_visibility: str | None)
|
|
|
188
188
|
"if needed.",
|
|
189
189
|
stack_info=True,
|
|
190
190
|
)
|
|
191
|
-
if label_visibility not in
|
|
191
|
+
if label_visibility not in {"visible", "hidden", "collapsed"}:
|
|
192
192
|
raise errors.StreamlitAPIException(
|
|
193
193
|
f"Unsupported label_visibility option '{label_visibility}'. "
|
|
194
194
|
f"Valid values are 'visible', 'hidden' or 'collapsed'."
|
|
@@ -58,7 +58,6 @@ DIVERGING_6: Final = "#000027"
|
|
|
58
58
|
DIVERGING_7: Final = "#000028"
|
|
59
59
|
DIVERGING_8: Final = "#000029"
|
|
60
60
|
DIVERGING_9: Final = "#000030"
|
|
61
|
-
DIVERGING_10: Final = "#000031"
|
|
62
61
|
|
|
63
62
|
INCREASING: Final = "#000032"
|
|
64
63
|
DECREASING: Final = "#000033"
|
|
@@ -189,16 +188,15 @@ def configure_streamlit_plotly_theme() -> None:
|
|
|
189
188
|
sequentialminus=streamlit_colorscale,
|
|
190
189
|
diverging=[
|
|
191
190
|
[0.0, DIVERGING_0],
|
|
192
|
-
[0.
|
|
193
|
-
[0.
|
|
194
|
-
[0.
|
|
195
|
-
[0.
|
|
196
|
-
[0.
|
|
197
|
-
[0.
|
|
198
|
-
[0.
|
|
199
|
-
[0.
|
|
200
|
-
[0
|
|
201
|
-
[1.0, DIVERGING_10],
|
|
191
|
+
[0.1111111111111111, DIVERGING_1],
|
|
192
|
+
[0.2222222222222222, DIVERGING_2],
|
|
193
|
+
[0.3333333333333333, DIVERGING_3],
|
|
194
|
+
[0.4444444444444444, DIVERGING_4],
|
|
195
|
+
[0.5555555555555556, DIVERGING_5],
|
|
196
|
+
[0.6666666666666666, DIVERGING_6],
|
|
197
|
+
[0.7777777777777778, DIVERGING_7],
|
|
198
|
+
[0.8888888888888888, DIVERGING_8],
|
|
199
|
+
[1.0, DIVERGING_9],
|
|
202
200
|
],
|
|
203
201
|
),
|
|
204
202
|
coloraxis=go.layout.Coloraxis(colorscale=streamlit_colorscale),
|
streamlit/elements/lib/utils.py
CHANGED
streamlit/elements/map.py
CHANGED
|
@@ -93,8 +93,8 @@ class MapMixin:
|
|
|
93
93
|
*,
|
|
94
94
|
latitude: str | None = None,
|
|
95
95
|
longitude: str | None = None,
|
|
96
|
-
color:
|
|
97
|
-
size:
|
|
96
|
+
color: str | Color | None = None,
|
|
97
|
+
size: str | float | None = None,
|
|
98
98
|
zoom: int | None = None,
|
|
99
99
|
width: WidthWithoutContent = "stretch",
|
|
100
100
|
height: HeightWithoutContent = 500,
|
|
@@ -296,8 +296,8 @@ def to_deckgl_json(
|
|
|
296
296
|
data: Data,
|
|
297
297
|
lat: str | None,
|
|
298
298
|
lon: str | None,
|
|
299
|
-
size:
|
|
300
|
-
color:
|
|
299
|
+
size: str | float | None,
|
|
300
|
+
color: str | Collection[float] | None,
|
|
301
301
|
zoom: int | None,
|
|
302
302
|
) -> str:
|
|
303
303
|
if data is None:
|
|
@@ -432,7 +432,7 @@ def _convert_color_arg_or_column(
|
|
|
432
432
|
data: DataFrame,
|
|
433
433
|
color_arg: str,
|
|
434
434
|
color_col_name: str | None,
|
|
435
|
-
) ->
|
|
435
|
+
) -> str | IntColorTuple | None:
|
|
436
436
|
"""Converts color to a format accepted by PyDeck.
|
|
437
437
|
|
|
438
438
|
For example:
|
|
@@ -443,7 +443,7 @@ def _convert_color_arg_or_column(
|
|
|
443
443
|
NOTE: This function mutates the data argument.
|
|
444
444
|
"""
|
|
445
445
|
|
|
446
|
-
color_arg_out:
|
|
446
|
+
color_arg_out: str | IntColorTuple | None = None
|
|
447
447
|
|
|
448
448
|
if color_col_name is not None:
|
|
449
449
|
# Convert color column to the right format.
|
|
@@ -677,14 +677,14 @@ class PlotlyMixin:
|
|
|
677
677
|
"options."
|
|
678
678
|
)
|
|
679
679
|
|
|
680
|
-
if theme not in
|
|
680
|
+
if theme not in {"streamlit", None}:
|
|
681
681
|
raise StreamlitAPIException(
|
|
682
682
|
f'You set theme="{theme}" while Streamlit charts only support '
|
|
683
683
|
"theme=”streamlit” or theme=None to fallback to the default "
|
|
684
684
|
"library theme."
|
|
685
685
|
)
|
|
686
686
|
|
|
687
|
-
if on_select not in
|
|
687
|
+
if on_select not in {"ignore", "rerun"} and not callable(on_select):
|
|
688
688
|
raise StreamlitAPIException(
|
|
689
689
|
f"You have passed {on_select} to `on_select`. But only 'ignore', "
|
|
690
690
|
"'rerun', or a callable is supported."
|
streamlit/elements/toast.py
CHANGED
|
@@ -151,7 +151,7 @@ class ToastMixin:
|
|
|
151
151
|
toast_proto.body = clean_text(validate_text(body))
|
|
152
152
|
toast_proto.icon = validate_icon_or_emoji(icon)
|
|
153
153
|
|
|
154
|
-
if duration in
|
|
154
|
+
if duration in {"short", "long", "infinite"} or (
|
|
155
155
|
isinstance(duration, int) and duration > 0
|
|
156
156
|
):
|
|
157
157
|
if duration == "short":
|
|
@@ -725,6 +725,10 @@ class VegaChartsMixin:
|
|
|
725
725
|
- An RGB or RGBA tuple with the red, green, blue, and alpha
|
|
726
726
|
components specified as ints from 0 to 255 or floats from 0.0 to
|
|
727
727
|
1.0.
|
|
728
|
+
- A built-in color name: "red", "orange", "yellow", "green",
|
|
729
|
+
"blue", "violet", "gray"/"grey", or "primary". These map to
|
|
730
|
+
theme colors that you can customize using ``theme.<color>Color``
|
|
731
|
+
configuration options.
|
|
728
732
|
|
|
729
733
|
For a line chart with multiple lines, where the dataframe is in
|
|
730
734
|
long format (that is, y is None or just one column), this can be:
|
|
@@ -753,7 +757,8 @@ class VegaChartsMixin:
|
|
|
753
757
|
- A list of string colors or color tuples to be used for each of
|
|
754
758
|
the lines in the chart. This list should have the same length
|
|
755
759
|
as the number of y values (e.g. ``color=["#fd0", "#f0f", "#04f"]``
|
|
756
|
-
for three lines).
|
|
760
|
+
for three lines). You can also use built-in color names in the
|
|
761
|
+
list (e.g. ``color=["red", "blue", "green"]``).
|
|
757
762
|
|
|
758
763
|
You can set the default colors in the ``theme.chartCategoryColors``
|
|
759
764
|
configuration option.
|
|
@@ -959,6 +964,10 @@ class VegaChartsMixin:
|
|
|
959
964
|
- An RGB or RGBA tuple with the red, green, blue, and alpha
|
|
960
965
|
components specified as ints from 0 to 255 or floats from 0.0 to
|
|
961
966
|
1.0.
|
|
967
|
+
- A built-in color name: "red", "orange", "yellow", "green",
|
|
968
|
+
"blue", "violet", "gray"/"grey", or "primary". These map to
|
|
969
|
+
theme colors that you can customize using ``theme.<color>Color``
|
|
970
|
+
configuration options.
|
|
962
971
|
|
|
963
972
|
For an area chart with multiple series, where the dataframe is in
|
|
964
973
|
long format (that is, y is None or just one column), this can be:
|
|
@@ -987,7 +996,8 @@ class VegaChartsMixin:
|
|
|
987
996
|
- A list of string colors or color tuples to be used for each of
|
|
988
997
|
the series in the chart. This list should have the same length
|
|
989
998
|
as the number of y values (e.g. ``color=["#fd0", "#f0f", "#04f"]``
|
|
990
|
-
for three lines).
|
|
999
|
+
for three lines). You can also use built-in color names in the
|
|
1000
|
+
list (e.g. ``color=["red", "blue", "green"]``).
|
|
991
1001
|
|
|
992
1002
|
You can set the default colors in the ``theme.chartCategoryColors``
|
|
993
1003
|
configuration option.
|
|
@@ -1248,6 +1258,10 @@ class VegaChartsMixin:
|
|
|
1248
1258
|
- An RGB or RGBA tuple with the red, green, blue, and alpha
|
|
1249
1259
|
components specified as ints from 0 to 255 or floats from 0.0 to
|
|
1250
1260
|
1.0.
|
|
1261
|
+
- A built-in color name: "red", "orange", "yellow", "green",
|
|
1262
|
+
"blue", "violet", "gray"/"grey", or "primary". These map to
|
|
1263
|
+
theme colors that you can customize using ``theme.<color>Color``
|
|
1264
|
+
configuration options.
|
|
1251
1265
|
|
|
1252
1266
|
For a bar chart with multiple series, where the dataframe is in
|
|
1253
1267
|
long format (that is, y is None or just one column), this can be:
|
|
@@ -1276,7 +1290,8 @@ class VegaChartsMixin:
|
|
|
1276
1290
|
- A list of string colors or color tuples to be used for each of
|
|
1277
1291
|
the series in the chart. This list should have the same length
|
|
1278
1292
|
as the number of y values (e.g. ``color=["#fd0", "#f0f", "#04f"]``
|
|
1279
|
-
for three lines).
|
|
1293
|
+
for three lines). You can also use built-in color names in the
|
|
1294
|
+
list (e.g. ``color=["red", "blue", "green"]``).
|
|
1280
1295
|
|
|
1281
1296
|
You can set the default colors in the ``theme.chartCategoryColors``
|
|
1282
1297
|
configuration option.
|
|
@@ -1575,6 +1590,10 @@ class VegaChartsMixin:
|
|
|
1575
1590
|
- An RGB or RGBA tuple with the red, green, blue, and alpha
|
|
1576
1591
|
components specified as ints from 0 to 255 or floats from 0.0 to
|
|
1577
1592
|
1.0.
|
|
1593
|
+
- A built-in color name: "red", "orange", "yellow", "green",
|
|
1594
|
+
"blue", "violet", "gray"/"grey", or "primary". These map to
|
|
1595
|
+
theme colors that you can customize using ``theme.<color>Color``
|
|
1596
|
+
configuration options.
|
|
1578
1597
|
- The name of a column in the dataset where the color of that
|
|
1579
1598
|
datapoint will come from.
|
|
1580
1599
|
|
|
@@ -1602,7 +1621,8 @@ class VegaChartsMixin:
|
|
|
1602
1621
|
- A list of string colors or color tuples to be used for each of
|
|
1603
1622
|
the series in the chart. This list should have the same length
|
|
1604
1623
|
as the number of y values (e.g. ``color=["#fd0", "#f0f", "#04f"]``
|
|
1605
|
-
for three series).
|
|
1624
|
+
for three series). You can also use built-in color names in the
|
|
1625
|
+
list (e.g. ``color=["red", "blue", "green"]``).
|
|
1606
1626
|
|
|
1607
1627
|
size : str, float, int, or None
|
|
1608
1628
|
The size of the circles representing each point.
|
|
@@ -2282,14 +2302,14 @@ class VegaChartsMixin:
|
|
|
2282
2302
|
|
|
2283
2303
|
See the `vega_lite_chart` method docstring for more information.
|
|
2284
2304
|
"""
|
|
2285
|
-
if theme not in
|
|
2305
|
+
if theme not in {"streamlit", None}:
|
|
2286
2306
|
raise StreamlitAPIException(
|
|
2287
2307
|
f'You set theme="{theme}" while Streamlit charts only support '
|
|
2288
2308
|
"theme=”streamlit” or theme=None to fallback to the default "
|
|
2289
2309
|
"library theme."
|
|
2290
2310
|
)
|
|
2291
2311
|
|
|
2292
|
-
if on_select not in
|
|
2312
|
+
if on_select not in {"ignore", "rerun"} and not callable(on_select):
|
|
2293
2313
|
raise StreamlitAPIException(
|
|
2294
2314
|
f"You have passed {on_select} to `on_select`. But only 'ignore', "
|
|
2295
2315
|
"'rerun', or a callable is supported."
|
|
@@ -2404,7 +2424,10 @@ class VegaChartsMixin:
|
|
|
2404
2424
|
vega_lite_proto.id = compute_and_register_element_id(
|
|
2405
2425
|
"arrow_vega_lite_chart",
|
|
2406
2426
|
user_key=key,
|
|
2407
|
-
|
|
2427
|
+
# There are some edge cases where selections can become orphaned when the data changes.
|
|
2428
|
+
# The frontend can handle this without errors, but it might be a nice enhancement
|
|
2429
|
+
# to automatically reset the backend & frontend selection state in this case.
|
|
2430
|
+
key_as_main_identity={"selection_mode"},
|
|
2408
2431
|
dg=self.dg,
|
|
2409
2432
|
vega_lite_spec=vega_lite_proto.spec,
|
|
2410
2433
|
# The data is either in vega_lite_proto.data.data
|
|
@@ -356,7 +356,7 @@ class ButtonMixin:
|
|
|
356
356
|
width = "stretch" if use_container_width else "content"
|
|
357
357
|
|
|
358
358
|
# Checks whether the entered button type is one of the allowed options
|
|
359
|
-
if type not in
|
|
359
|
+
if type not in {"primary", "secondary", "tertiary"}:
|
|
360
360
|
raise StreamlitAPIException(
|
|
361
361
|
'The type argument to st.button must be "primary", "secondary", or "tertiary". '
|
|
362
362
|
f'\nThe argument passed was "{type}".'
|
|
@@ -736,7 +736,7 @@ class ButtonMixin:
|
|
|
736
736
|
if use_container_width is not None:
|
|
737
737
|
width = "stretch" if use_container_width else "content"
|
|
738
738
|
|
|
739
|
-
if type not in
|
|
739
|
+
if type not in {"primary", "secondary", "tertiary"}:
|
|
740
740
|
raise StreamlitAPIException(
|
|
741
741
|
'The type argument to st.download_button must be "primary", "secondary", or "tertiary". \n'
|
|
742
742
|
f'The argument passed was "{type}".'
|
|
@@ -915,7 +915,7 @@ class ButtonMixin:
|
|
|
915
915
|
|
|
916
916
|
"""
|
|
917
917
|
# Checks whether the entered button type is one of the allowed options - either "primary" or "secondary"
|
|
918
|
-
if type not in
|
|
918
|
+
if type not in {"primary", "secondary", "tertiary"}:
|
|
919
919
|
raise StreamlitAPIException(
|
|
920
920
|
'The type argument to st.link_button must be "primary", "secondary", or "tertiary". '
|
|
921
921
|
f'\nThe argument passed was "{type}".'
|
|
@@ -255,7 +255,7 @@ def _build_proto(
|
|
|
255
255
|
|
|
256
256
|
def _maybe_raise_selection_mode_warning(selection_mode: SelectionMode) -> None:
|
|
257
257
|
"""Check if the selection_mode value is valid or raise exception otherwise."""
|
|
258
|
-
if selection_mode not in
|
|
258
|
+
if selection_mode not in {"single", "multi"}:
|
|
259
259
|
raise StreamlitAPIException(
|
|
260
260
|
"The selection_mode argument must be one of ['single', 'multi']. "
|
|
261
261
|
f"The argument passed was '{selection_mode}'."
|
|
@@ -410,7 +410,7 @@ class ButtonGroupMixin:
|
|
|
410
410
|
|
|
411
411
|
"""
|
|
412
412
|
|
|
413
|
-
if options not in
|
|
413
|
+
if options not in {"thumbs", "faces", "stars"}:
|
|
414
414
|
raise StreamlitAPIException(
|
|
415
415
|
"The options argument to st.feedback must be one of "
|
|
416
416
|
"['thumbs', 'faces', 'stars']. "
|
|
@@ -1062,7 +1062,7 @@ class ButtonGroupMixin:
|
|
|
1062
1062
|
"`selection_mode='single'`."
|
|
1063
1063
|
)
|
|
1064
1064
|
|
|
1065
|
-
if style not in
|
|
1065
|
+
if style not in {"borderless", "pills", "segmented_control"}:
|
|
1066
1066
|
raise StreamlitAPIException(
|
|
1067
1067
|
"The style argument must be one of ['borderless', 'pills', 'segmented_control']. "
|
|
1068
1068
|
f"The argument passed was '{style}'."
|
|
@@ -238,11 +238,11 @@ def _parse_value(
|
|
|
238
238
|
if column_data_kind == ColumnDataKind.TIMEDELTA:
|
|
239
239
|
return pd.Timedelta(value)
|
|
240
240
|
|
|
241
|
-
if column_data_kind in
|
|
241
|
+
if column_data_kind in {
|
|
242
242
|
ColumnDataKind.DATETIME,
|
|
243
243
|
ColumnDataKind.DATE,
|
|
244
244
|
ColumnDataKind.TIME,
|
|
245
|
-
|
|
245
|
+
}:
|
|
246
246
|
datetime_value = pd.Timestamp(value)
|
|
247
247
|
|
|
248
248
|
if pd.isna(datetime_value):
|
|
@@ -481,7 +481,7 @@ def _is_supported_index(df_index: pd.Index[Any]) -> bool:
|
|
|
481
481
|
|
|
482
482
|
return (
|
|
483
483
|
type(df_index)
|
|
484
|
-
in
|
|
484
|
+
in {
|
|
485
485
|
pd.RangeIndex,
|
|
486
486
|
pd.Index,
|
|
487
487
|
pd.DatetimeIndex,
|
|
@@ -490,7 +490,7 @@ def _is_supported_index(df_index: pd.Index[Any]) -> bool:
|
|
|
490
490
|
# pd.IntervalIndex,
|
|
491
491
|
# Period type isn't editable currently:
|
|
492
492
|
# pd.PeriodIndex,
|
|
493
|
-
|
|
493
|
+
}
|
|
494
494
|
# We need to check these index types without importing, since they are
|
|
495
495
|
# deprecated and planned to be removed soon.
|
|
496
496
|
or is_type(df_index, "pandas.core.indexes.numeric.Int64Index")
|
|
@@ -1029,7 +1029,7 @@ class DataEditorMixin:
|
|
|
1029
1029
|
update_column_config(
|
|
1030
1030
|
column_config_mapping, INDEX_IDENTIFIER, {"required": True}
|
|
1031
1031
|
)
|
|
1032
|
-
if num_rows in
|
|
1032
|
+
if num_rows in {"dynamic", "add"} and hide_index is True:
|
|
1033
1033
|
_LOGGER.warning(
|
|
1034
1034
|
"Setting `hide_index=True` in data editor with a non-range index will not have any effect "
|
|
1035
1035
|
"when `num_rows` is '%s'. It is required for the user to fill in index values for "
|
|
@@ -1038,7 +1038,7 @@ class DataEditorMixin:
|
|
|
1038
1038
|
num_rows,
|
|
1039
1039
|
)
|
|
1040
1040
|
|
|
1041
|
-
if hide_index is None and has_range_index and num_rows in
|
|
1041
|
+
if hide_index is None and has_range_index and num_rows in {"dynamic", "add"}:
|
|
1042
1042
|
# Temporary workaround:
|
|
1043
1043
|
# We hide range indices if num_rows allows adding rows.
|
|
1044
1044
|
# since the current way of handling this index during editing is a
|
|
@@ -82,6 +82,7 @@ class MultiSelectSerde(Generic[T]):
|
|
|
82
82
|
formatted_options: list[str]
|
|
83
83
|
formatted_option_to_option_index: dict[str, int]
|
|
84
84
|
default_options_indices: list[int]
|
|
85
|
+
format_func: Callable[[Any], str]
|
|
85
86
|
|
|
86
87
|
def __init__(
|
|
87
88
|
self,
|
|
@@ -90,6 +91,7 @@ class MultiSelectSerde(Generic[T]):
|
|
|
90
91
|
formatted_options: list[str],
|
|
91
92
|
formatted_option_to_option_index: dict[str, int],
|
|
92
93
|
default_options_indices: list[int] | None = None,
|
|
94
|
+
format_func: Callable[[Any], str] = str,
|
|
93
95
|
) -> None:
|
|
94
96
|
"""Initialize the MultiSelectSerde.
|
|
95
97
|
|
|
@@ -111,24 +113,45 @@ class MultiSelectSerde(Generic[T]):
|
|
|
111
113
|
default_option_index : int or None, optional
|
|
112
114
|
The index of the default option to use when no selection is made.
|
|
113
115
|
If None, no default option is selected.
|
|
116
|
+
format_func : Callable[[Any], str], optional
|
|
117
|
+
Function to format options for comparison. Used to compare values by their
|
|
118
|
+
string representation instead of using == directly. This is necessary because
|
|
119
|
+
widget values are deepcopied, and for custom classes without __eq__, the
|
|
120
|
+
deepcopied instances would fail identity comparison.
|
|
114
121
|
"""
|
|
115
122
|
|
|
116
123
|
self.options = options
|
|
117
124
|
self.formatted_options = formatted_options
|
|
118
125
|
self.formatted_option_to_option_index = formatted_option_to_option_index
|
|
119
126
|
self.default_options_indices = default_options_indices or []
|
|
127
|
+
self.format_func = format_func
|
|
120
128
|
|
|
121
129
|
def serialize(self, value: list[T | str] | list[T]) -> list[str]:
|
|
122
130
|
converted_value = convert_anything_to_list(value)
|
|
123
131
|
values: list[str] = []
|
|
124
132
|
for v in converted_value:
|
|
133
|
+
# Use format_func to find the formatted option instead of using
|
|
134
|
+
# self.options.index(v) which relies on == comparison. This is necessary
|
|
135
|
+
# because widget values are deepcopied, and for custom classes without
|
|
136
|
+
# __eq__, the deepcopied instances would fail identity comparison.
|
|
125
137
|
try:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
#
|
|
130
|
-
#
|
|
131
|
-
|
|
138
|
+
formatted_value = self.format_func(v)
|
|
139
|
+
except Exception:
|
|
140
|
+
# format_func failed (e.g., v is a string but format_func expects
|
|
141
|
+
# an object with specific attributes). Use str(v) to ensure we append
|
|
142
|
+
# a proper string, not the original object. This handles both cases:
|
|
143
|
+
# - v is already a string -> str(v) returns it unchanged
|
|
144
|
+
# - v is a custom object -> str(v) gives its string representation
|
|
145
|
+
values.append(str(v))
|
|
146
|
+
continue
|
|
147
|
+
|
|
148
|
+
if formatted_value in self.formatted_option_to_option_index:
|
|
149
|
+
values.append(formatted_value)
|
|
150
|
+
else:
|
|
151
|
+
# Value not found in options - it's likely a user-entered string
|
|
152
|
+
# (when accept_new_options=True) or an invalid value. Use the
|
|
153
|
+
# formatted string (not the original object) for type consistency.
|
|
154
|
+
values.append(formatted_value)
|
|
132
155
|
return values
|
|
133
156
|
|
|
134
157
|
def deserialize(self, ui_value: list[str] | None) -> list[T | str] | list[T]:
|
|
@@ -247,7 +270,7 @@ class MultiSelectMixin:
|
|
|
247
270
|
placeholder: str | None = None,
|
|
248
271
|
disabled: bool = False,
|
|
249
272
|
label_visibility: LabelVisibility = "visible",
|
|
250
|
-
accept_new_options:
|
|
273
|
+
accept_new_options: bool = False,
|
|
251
274
|
width: WidthWithoutContent = "stretch",
|
|
252
275
|
) -> list[T] | list[T | str]:
|
|
253
276
|
r"""Display a multiselect widget.
|
|
@@ -530,6 +553,7 @@ class MultiSelectMixin:
|
|
|
530
553
|
formatted_options=formatted_options,
|
|
531
554
|
formatted_option_to_option_index=formatted_option_to_option_index,
|
|
532
555
|
default_options_indices=default_values,
|
|
556
|
+
format_func=format_func,
|
|
533
557
|
)
|
|
534
558
|
|
|
535
559
|
widget_state = register_widget(
|
|
@@ -560,7 +584,7 @@ class MultiSelectMixin:
|
|
|
560
584
|
# previously selected values are no longer available.
|
|
561
585
|
current_values, value_needs_reset = (
|
|
562
586
|
validate_and_sync_multiselect_value_with_options(
|
|
563
|
-
widget_state.value, indexable_options, key
|
|
587
|
+
widget_state.value, indexable_options, key, format_func
|
|
564
588
|
)
|
|
565
589
|
)
|
|
566
590
|
|
|
@@ -512,7 +512,7 @@ class NumberInputMixin:
|
|
|
512
512
|
number_format = ("%d" if int_value else "%0.2f") if format is None else format
|
|
513
513
|
|
|
514
514
|
# Warn user if they format an int type as a float or vice versa.
|
|
515
|
-
if number_format in
|
|
515
|
+
if number_format in {"%d", "%u", "%i"} and float_value:
|
|
516
516
|
import streamlit as st
|
|
517
517
|
|
|
518
518
|
st.warning(
|