streamlit-nightly 1.43.2.dev20250307__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 +306 -0
- streamlit/__main__.py +20 -0
- streamlit/auth_util.py +218 -0
- streamlit/cli_util.py +105 -0
- streamlit/column_config.py +56 -0
- streamlit/commands/__init__.py +13 -0
- streamlit/commands/echo.py +126 -0
- streamlit/commands/execution_control.py +238 -0
- streamlit/commands/experimental_query_params.py +169 -0
- streamlit/commands/logo.py +189 -0
- streamlit/commands/navigation.py +385 -0
- streamlit/commands/page_config.py +311 -0
- streamlit/components/__init__.py +13 -0
- streamlit/components/lib/__init__.py +13 -0
- streamlit/components/lib/local_component_registry.py +84 -0
- streamlit/components/types/__init__.py +13 -0
- streamlit/components/types/base_component_registry.py +99 -0
- streamlit/components/types/base_custom_component.py +150 -0
- streamlit/components/v1/__init__.py +31 -0
- streamlit/components/v1/component_arrow.py +141 -0
- streamlit/components/v1/component_registry.py +130 -0
- streamlit/components/v1/components.py +38 -0
- streamlit/components/v1/custom_component.py +243 -0
- streamlit/config.py +1513 -0
- streamlit/config_option.py +311 -0
- streamlit/config_util.py +177 -0
- streamlit/connections/__init__.py +28 -0
- streamlit/connections/base_connection.py +174 -0
- streamlit/connections/snowflake_connection.py +562 -0
- streamlit/connections/snowpark_connection.py +213 -0
- streamlit/connections/sql_connection.py +425 -0
- streamlit/connections/util.py +97 -0
- streamlit/cursor.py +210 -0
- streamlit/dataframe_util.py +1416 -0
- streamlit/delta_generator.py +602 -0
- streamlit/delta_generator_singletons.py +204 -0
- streamlit/deprecation_util.py +209 -0
- streamlit/development.py +21 -0
- streamlit/elements/__init__.py +13 -0
- streamlit/elements/alert.py +234 -0
- streamlit/elements/arrow.py +962 -0
- streamlit/elements/balloons.py +47 -0
- streamlit/elements/bokeh_chart.py +133 -0
- streamlit/elements/code.py +114 -0
- streamlit/elements/deck_gl_json_chart.py +546 -0
- streamlit/elements/dialog_decorator.py +267 -0
- streamlit/elements/doc_string.py +558 -0
- streamlit/elements/empty.py +130 -0
- streamlit/elements/exception.py +331 -0
- streamlit/elements/form.py +354 -0
- streamlit/elements/graphviz_chart.py +150 -0
- streamlit/elements/heading.py +302 -0
- streamlit/elements/html.py +105 -0
- streamlit/elements/iframe.py +191 -0
- streamlit/elements/image.py +196 -0
- streamlit/elements/json.py +139 -0
- streamlit/elements/layouts.py +879 -0
- streamlit/elements/lib/__init__.py +13 -0
- streamlit/elements/lib/built_in_chart_utils.py +1157 -0
- streamlit/elements/lib/color_util.py +263 -0
- streamlit/elements/lib/column_config_utils.py +542 -0
- streamlit/elements/lib/column_types.py +2188 -0
- streamlit/elements/lib/dialog.py +147 -0
- streamlit/elements/lib/dicttools.py +154 -0
- streamlit/elements/lib/event_utils.py +37 -0
- streamlit/elements/lib/file_uploader_utils.py +66 -0
- streamlit/elements/lib/form_utils.py +77 -0
- streamlit/elements/lib/image_utils.py +441 -0
- streamlit/elements/lib/js_number.py +105 -0
- streamlit/elements/lib/mutable_status_container.py +183 -0
- streamlit/elements/lib/options_selector_utils.py +250 -0
- streamlit/elements/lib/pandas_styler_utils.py +274 -0
- streamlit/elements/lib/policies.py +194 -0
- streamlit/elements/lib/streamlit_plotly_theme.py +207 -0
- streamlit/elements/lib/subtitle_utils.py +176 -0
- streamlit/elements/lib/utils.py +250 -0
- streamlit/elements/map.py +508 -0
- streamlit/elements/markdown.py +277 -0
- streamlit/elements/media.py +793 -0
- streamlit/elements/metric.py +301 -0
- streamlit/elements/plotly_chart.py +546 -0
- streamlit/elements/progress.py +156 -0
- streamlit/elements/pyplot.py +194 -0
- streamlit/elements/snow.py +47 -0
- streamlit/elements/spinner.py +113 -0
- streamlit/elements/text.py +76 -0
- streamlit/elements/toast.py +98 -0
- streamlit/elements/vega_charts.py +1984 -0
- streamlit/elements/widgets/__init__.py +13 -0
- streamlit/elements/widgets/audio_input.py +310 -0
- streamlit/elements/widgets/button.py +1123 -0
- streamlit/elements/widgets/button_group.py +1008 -0
- streamlit/elements/widgets/camera_input.py +263 -0
- streamlit/elements/widgets/chat.py +647 -0
- streamlit/elements/widgets/checkbox.py +352 -0
- streamlit/elements/widgets/color_picker.py +265 -0
- streamlit/elements/widgets/data_editor.py +983 -0
- streamlit/elements/widgets/file_uploader.py +486 -0
- streamlit/elements/widgets/multiselect.py +338 -0
- streamlit/elements/widgets/number_input.py +545 -0
- streamlit/elements/widgets/radio.py +407 -0
- streamlit/elements/widgets/select_slider.py +437 -0
- streamlit/elements/widgets/selectbox.py +366 -0
- streamlit/elements/widgets/slider.py +880 -0
- streamlit/elements/widgets/text_widgets.py +628 -0
- streamlit/elements/widgets/time_widgets.py +970 -0
- streamlit/elements/write.py +574 -0
- streamlit/emojis.py +34 -0
- streamlit/env_util.py +61 -0
- streamlit/error_util.py +105 -0
- streamlit/errors.py +452 -0
- streamlit/external/__init__.py +13 -0
- streamlit/external/langchain/__init__.py +23 -0
- streamlit/external/langchain/streamlit_callback_handler.py +406 -0
- streamlit/file_util.py +247 -0
- streamlit/git_util.py +173 -0
- streamlit/hello/__init__.py +13 -0
- streamlit/hello/animation_demo.py +82 -0
- streamlit/hello/dataframe_demo.py +71 -0
- streamlit/hello/hello.py +37 -0
- streamlit/hello/mapping_demo.py +114 -0
- streamlit/hello/plotting_demo.py +55 -0
- streamlit/hello/streamlit_app.py +55 -0
- streamlit/hello/utils.py +28 -0
- streamlit/logger.py +130 -0
- streamlit/material_icon_names.py +25 -0
- streamlit/navigation/__init__.py +13 -0
- streamlit/navigation/page.py +302 -0
- streamlit/net_util.py +125 -0
- streamlit/platform.py +33 -0
- streamlit/proto/Alert_pb2.py +29 -0
- streamlit/proto/Alert_pb2.pyi +90 -0
- streamlit/proto/AppPage_pb2.py +27 -0
- streamlit/proto/AppPage_pb2.pyi +64 -0
- streamlit/proto/ArrowNamedDataSet_pb2.py +28 -0
- streamlit/proto/ArrowNamedDataSet_pb2.pyi +57 -0
- streamlit/proto/ArrowVegaLiteChart_pb2.py +29 -0
- streamlit/proto/ArrowVegaLiteChart_pb2.pyi +84 -0
- streamlit/proto/Arrow_pb2.py +33 -0
- streamlit/proto/Arrow_pb2.pyi +188 -0
- streamlit/proto/AudioInput_pb2.py +28 -0
- streamlit/proto/AudioInput_pb2.pyi +58 -0
- streamlit/proto/Audio_pb2.py +27 -0
- streamlit/proto/Audio_pb2.pyi +58 -0
- streamlit/proto/AuthRedirect_pb2.py +27 -0
- streamlit/proto/AuthRedirect_pb2.pyi +41 -0
- streamlit/proto/AutoRerun_pb2.py +27 -0
- streamlit/proto/AutoRerun_pb2.pyi +45 -0
- streamlit/proto/BackMsg_pb2.py +29 -0
- streamlit/proto/BackMsg_pb2.pyi +105 -0
- streamlit/proto/Balloons_pb2.py +27 -0
- streamlit/proto/Balloons_pb2.pyi +43 -0
- streamlit/proto/Block_pb2.py +53 -0
- streamlit/proto/Block_pb2.pyi +322 -0
- streamlit/proto/BokehChart_pb2.py +27 -0
- streamlit/proto/BokehChart_pb2.pyi +49 -0
- streamlit/proto/ButtonGroup_pb2.py +36 -0
- streamlit/proto/ButtonGroup_pb2.pyi +169 -0
- streamlit/proto/Button_pb2.py +27 -0
- streamlit/proto/Button_pb2.pyi +71 -0
- streamlit/proto/CameraInput_pb2.py +28 -0
- streamlit/proto/CameraInput_pb2.pyi +58 -0
- streamlit/proto/ChatInput_pb2.py +31 -0
- streamlit/proto/ChatInput_pb2.pyi +111 -0
- streamlit/proto/Checkbox_pb2.py +30 -0
- streamlit/proto/Checkbox_pb2.pyi +90 -0
- streamlit/proto/ClientState_pb2.py +30 -0
- streamlit/proto/ClientState_pb2.pyi +90 -0
- streamlit/proto/Code_pb2.py +27 -0
- streamlit/proto/Code_pb2.pyi +55 -0
- streamlit/proto/ColorPicker_pb2.py +28 -0
- streamlit/proto/ColorPicker_pb2.pyi +67 -0
- streamlit/proto/Common_pb2.py +51 -0
- streamlit/proto/Common_pb2.pyi +293 -0
- streamlit/proto/Components_pb2.py +35 -0
- streamlit/proto/Components_pb2.pyi +172 -0
- streamlit/proto/DataFrame_pb2.py +56 -0
- streamlit/proto/DataFrame_pb2.pyi +397 -0
- streamlit/proto/DateInput_pb2.py +28 -0
- streamlit/proto/DateInput_pb2.pyi +83 -0
- streamlit/proto/DeckGlJsonChart_pb2.py +29 -0
- streamlit/proto/DeckGlJsonChart_pb2.pyi +102 -0
- streamlit/proto/Delta_pb2.py +31 -0
- streamlit/proto/Delta_pb2.pyi +74 -0
- streamlit/proto/DocString_pb2.py +29 -0
- streamlit/proto/DocString_pb2.pyi +93 -0
- streamlit/proto/DownloadButton_pb2.py +27 -0
- streamlit/proto/DownloadButton_pb2.pyi +70 -0
- streamlit/proto/Element_pb2.py +78 -0
- streamlit/proto/Element_pb2.pyi +312 -0
- streamlit/proto/Empty_pb2.py +27 -0
- streamlit/proto/Empty_pb2.pyi +36 -0
- streamlit/proto/Exception_pb2.py +27 -0
- streamlit/proto/Exception_pb2.pyi +72 -0
- streamlit/proto/Favicon_pb2.py +27 -0
- streamlit/proto/Favicon_pb2.pyi +40 -0
- streamlit/proto/FileUploader_pb2.py +28 -0
- streamlit/proto/FileUploader_pb2.pyi +78 -0
- streamlit/proto/ForwardMsg_pb2.py +53 -0
- streamlit/proto/ForwardMsg_pb2.pyi +293 -0
- streamlit/proto/GitInfo_pb2.py +29 -0
- streamlit/proto/GitInfo_pb2.pyi +83 -0
- streamlit/proto/GraphVizChart_pb2.py +27 -0
- streamlit/proto/GraphVizChart_pb2.pyi +53 -0
- streamlit/proto/Heading_pb2.py +27 -0
- streamlit/proto/Heading_pb2.pyi +56 -0
- streamlit/proto/Html_pb2.py +27 -0
- streamlit/proto/Html_pb2.pyi +42 -0
- streamlit/proto/IFrame_pb2.py +27 -0
- streamlit/proto/IFrame_pb2.pyi +59 -0
- streamlit/proto/Image_pb2.py +29 -0
- streamlit/proto/Image_pb2.pyi +84 -0
- streamlit/proto/Json_pb2.py +27 -0
- streamlit/proto/Json_pb2.pyi +53 -0
- streamlit/proto/LabelVisibilityMessage_pb2.py +29 -0
- streamlit/proto/LabelVisibilityMessage_pb2.pyi +68 -0
- streamlit/proto/LinkButton_pb2.py +27 -0
- streamlit/proto/LinkButton_pb2.pyi +58 -0
- streamlit/proto/Logo_pb2.py +27 -0
- streamlit/proto/Logo_pb2.pyi +51 -0
- streamlit/proto/Markdown_pb2.py +29 -0
- streamlit/proto/Markdown_pb2.pyi +86 -0
- streamlit/proto/Metric_pb2.py +32 -0
- streamlit/proto/Metric_pb2.pyi +101 -0
- streamlit/proto/MetricsEvent_pb2.py +30 -0
- streamlit/proto/MetricsEvent_pb2.pyi +200 -0
- streamlit/proto/MultiSelect_pb2.py +28 -0
- streamlit/proto/MultiSelect_pb2.pyi +81 -0
- streamlit/proto/NamedDataSet_pb2.py +28 -0
- streamlit/proto/NamedDataSet_pb2.pyi +59 -0
- streamlit/proto/Navigation_pb2.py +30 -0
- streamlit/proto/Navigation_pb2.pyi +84 -0
- streamlit/proto/NewSession_pb2.py +51 -0
- streamlit/proto/NewSession_pb2.pyi +481 -0
- streamlit/proto/NumberInput_pb2.py +30 -0
- streamlit/proto/NumberInput_pb2.pyi +121 -0
- streamlit/proto/PageConfig_pb2.py +33 -0
- streamlit/proto/PageConfig_pb2.pyi +126 -0
- streamlit/proto/PageInfo_pb2.py +27 -0
- streamlit/proto/PageInfo_pb2.pyi +43 -0
- streamlit/proto/PageLink_pb2.py +27 -0
- streamlit/proto/PageLink_pb2.pyi +63 -0
- streamlit/proto/PageNotFound_pb2.py +27 -0
- streamlit/proto/PageNotFound_pb2.pyi +42 -0
- streamlit/proto/PageProfile_pb2.py +31 -0
- streamlit/proto/PageProfile_pb2.pyi +127 -0
- streamlit/proto/PagesChanged_pb2.py +28 -0
- streamlit/proto/PagesChanged_pb2.pyi +48 -0
- streamlit/proto/ParentMessage_pb2.py +27 -0
- streamlit/proto/ParentMessage_pb2.pyi +46 -0
- streamlit/proto/PlotlyChart_pb2.py +31 -0
- streamlit/proto/PlotlyChart_pb2.pyi +131 -0
- streamlit/proto/Progress_pb2.py +27 -0
- streamlit/proto/Progress_pb2.pyi +43 -0
- streamlit/proto/Radio_pb2.py +28 -0
- streamlit/proto/Radio_pb2.pyi +84 -0
- streamlit/proto/RootContainer_pb2.py +27 -0
- streamlit/proto/RootContainer_pb2.pyi +56 -0
- streamlit/proto/Selectbox_pb2.py +28 -0
- streamlit/proto/Selectbox_pb2.pyi +80 -0
- streamlit/proto/SessionEvent_pb2.py +28 -0
- streamlit/proto/SessionEvent_pb2.pyi +62 -0
- streamlit/proto/SessionStatus_pb2.py +27 -0
- streamlit/proto/SessionStatus_pb2.pyi +57 -0
- streamlit/proto/Skeleton_pb2.py +29 -0
- streamlit/proto/Skeleton_pb2.pyi +71 -0
- streamlit/proto/Slider_pb2.py +32 -0
- streamlit/proto/Slider_pb2.pyi +142 -0
- streamlit/proto/Snow_pb2.py +27 -0
- streamlit/proto/Snow_pb2.pyi +43 -0
- streamlit/proto/Spinner_pb2.py +27 -0
- streamlit/proto/Spinner_pb2.pyi +49 -0
- streamlit/proto/TextArea_pb2.py +28 -0
- streamlit/proto/TextArea_pb2.pyi +80 -0
- streamlit/proto/TextInput_pb2.py +30 -0
- streamlit/proto/TextInput_pb2.pyi +107 -0
- streamlit/proto/Text_pb2.py +27 -0
- streamlit/proto/Text_pb2.pyi +46 -0
- streamlit/proto/TimeInput_pb2.py +28 -0
- streamlit/proto/TimeInput_pb2.pyi +74 -0
- streamlit/proto/Toast_pb2.py +27 -0
- streamlit/proto/Toast_pb2.pyi +45 -0
- streamlit/proto/VegaLiteChart_pb2.py +29 -0
- streamlit/proto/VegaLiteChart_pb2.pyi +71 -0
- streamlit/proto/Video_pb2.py +31 -0
- streamlit/proto/Video_pb2.pyi +117 -0
- streamlit/proto/WidgetStates_pb2.py +31 -0
- streamlit/proto/WidgetStates_pb2.pyi +126 -0
- streamlit/proto/__init__.py +15 -0
- streamlit/proto/openmetrics_data_model_pb2.py +60 -0
- streamlit/proto/openmetrics_data_model_pb2.pyi +522 -0
- streamlit/py.typed +0 -0
- streamlit/runtime/__init__.py +50 -0
- streamlit/runtime/app_session.py +982 -0
- streamlit/runtime/caching/__init__.py +98 -0
- streamlit/runtime/caching/cache_data_api.py +665 -0
- streamlit/runtime/caching/cache_errors.py +142 -0
- streamlit/runtime/caching/cache_resource_api.py +527 -0
- streamlit/runtime/caching/cache_type.py +33 -0
- streamlit/runtime/caching/cache_utils.py +523 -0
- streamlit/runtime/caching/cached_message_replay.py +290 -0
- streamlit/runtime/caching/hashing.py +637 -0
- streamlit/runtime/caching/legacy_cache_api.py +169 -0
- streamlit/runtime/caching/storage/__init__.py +29 -0
- streamlit/runtime/caching/storage/cache_storage_protocol.py +239 -0
- streamlit/runtime/caching/storage/dummy_cache_storage.py +60 -0
- streamlit/runtime/caching/storage/in_memory_cache_storage_wrapper.py +145 -0
- streamlit/runtime/caching/storage/local_disk_cache_storage.py +223 -0
- streamlit/runtime/connection_factory.py +436 -0
- streamlit/runtime/context.py +280 -0
- streamlit/runtime/credentials.py +364 -0
- streamlit/runtime/forward_msg_cache.py +296 -0
- streamlit/runtime/forward_msg_queue.py +240 -0
- streamlit/runtime/fragment.py +477 -0
- streamlit/runtime/media_file_manager.py +234 -0
- streamlit/runtime/media_file_storage.py +143 -0
- streamlit/runtime/memory_media_file_storage.py +181 -0
- streamlit/runtime/memory_session_storage.py +77 -0
- streamlit/runtime/memory_uploaded_file_manager.py +138 -0
- streamlit/runtime/metrics_util.py +486 -0
- streamlit/runtime/pages_manager.py +165 -0
- streamlit/runtime/runtime.py +792 -0
- streamlit/runtime/runtime_util.py +106 -0
- streamlit/runtime/script_data.py +46 -0
- streamlit/runtime/scriptrunner/__init__.py +38 -0
- streamlit/runtime/scriptrunner/exec_code.py +159 -0
- streamlit/runtime/scriptrunner/magic.py +273 -0
- streamlit/runtime/scriptrunner/magic_funcs.py +32 -0
- streamlit/runtime/scriptrunner/script_cache.py +89 -0
- streamlit/runtime/scriptrunner/script_runner.py +756 -0
- streamlit/runtime/scriptrunner_utils/__init__.py +19 -0
- streamlit/runtime/scriptrunner_utils/exceptions.py +48 -0
- streamlit/runtime/scriptrunner_utils/script_requests.py +307 -0
- streamlit/runtime/scriptrunner_utils/script_run_context.py +287 -0
- streamlit/runtime/secrets.py +534 -0
- streamlit/runtime/session_manager.py +394 -0
- streamlit/runtime/state/__init__.py +41 -0
- streamlit/runtime/state/common.py +191 -0
- streamlit/runtime/state/query_params.py +205 -0
- streamlit/runtime/state/query_params_proxy.py +218 -0
- streamlit/runtime/state/safe_session_state.py +138 -0
- streamlit/runtime/state/session_state.py +772 -0
- streamlit/runtime/state/session_state_proxy.py +153 -0
- streamlit/runtime/state/widgets.py +135 -0
- streamlit/runtime/stats.py +109 -0
- streamlit/runtime/uploaded_file_manager.py +148 -0
- streamlit/runtime/websocket_session_manager.py +167 -0
- streamlit/source_util.py +98 -0
- streamlit/static/favicon.png +0 -0
- streamlit/static/index.html +61 -0
- streamlit/static/static/css/index.Bmkmz40k.css +1 -0
- streamlit/static/static/css/index.DpJG_94W.css +1 -0
- streamlit/static/static/css/index.DzuxGC_t.css +1 -0
- streamlit/static/static/js/FileDownload.esm.Bp9m5jrx.js +1 -0
- streamlit/static/static/js/FileHelper.D_3pbilj.js +5 -0
- streamlit/static/static/js/FormClearHelper.Ct2rwLXo.js +1 -0
- streamlit/static/static/js/Hooks.BKdzj5MJ.js +1 -0
- streamlit/static/static/js/InputInstructions.DB3QGNJP.js +1 -0
- streamlit/static/static/js/ProgressBar.D40A5xc2.js +2 -0
- streamlit/static/static/js/RenderInPortalIfExists.DLUCooTN.js +1 -0
- streamlit/static/static/js/Toolbar.BiGGIQun.js +1 -0
- streamlit/static/static/js/UploadFileInfo.C-jY39rj.js +1 -0
- streamlit/static/static/js/base-input.CQBQT24M.js +4 -0
- streamlit/static/static/js/checkbox.Buj8gd_M.js +9 -0
- streamlit/static/static/js/createDownloadLinkElement.DZMwyjvU.js +1 -0
- streamlit/static/static/js/createSuper.CesK3I23.js +1 -0
- streamlit/static/static/js/data-grid-overlay-editor.B69OOFM4.js +1 -0
- streamlit/static/static/js/downloader.BZQhlBNT.js +1 -0
- streamlit/static/static/js/es6.D9Zhqujy.js +2 -0
- streamlit/static/static/js/iframeResizer.contentWindow.CAzcBpCC.js +1 -0
- streamlit/static/static/js/index.08vcOOvb.js +1 -0
- streamlit/static/static/js/index.0uqKfJUS.js +1 -0
- streamlit/static/static/js/index.B02M5u69.js +203 -0
- streamlit/static/static/js/index.B7mcZKMx.js +1 -0
- streamlit/static/static/js/index.BAQDHFA_.js +1 -0
- streamlit/static/static/js/index.BI60cMVr.js +2 -0
- streamlit/static/static/js/index.BLug2inK.js +1 -0
- streamlit/static/static/js/index.BM6TMY8g.js +2 -0
- streamlit/static/static/js/index.BZ9p1t7G.js +1 -0
- streamlit/static/static/js/index.BZqa87a1.js +2 -0
- streamlit/static/static/js/index.BcsRUzZZ.js +1 -0
- streamlit/static/static/js/index.BgVMiY_P.js +197 -0
- streamlit/static/static/js/index.BtuGy7By.js +6 -0
- streamlit/static/static/js/index.BuDuBmrs.js +1 -0
- streamlit/static/static/js/index.BvXU2oKV.js +1 -0
- streamlit/static/static/js/index.BxcwPacT.js +73 -0
- streamlit/static/static/js/index.CWX8KB81.js +1 -0
- streamlit/static/static/js/index.CXzZTo_q.js +1 -0
- streamlit/static/static/js/index.CcRWp_KL.js +1 -0
- streamlit/static/static/js/index.Cd-_xe55.js +3 -0
- streamlit/static/static/js/index.CdG2PXln.js +4537 -0
- streamlit/static/static/js/index.CjXvXmcP.js +1 -0
- streamlit/static/static/js/index.D1HZENZx.js +776 -0
- streamlit/static/static/js/index.D21Efo64.js +1617 -0
- streamlit/static/static/js/index.D9WgGVBx.js +7 -0
- streamlit/static/static/js/index.DEcsHtvb.js +12 -0
- streamlit/static/static/js/index.DFeMfr_K.js +1 -0
- streamlit/static/static/js/index.DHFBoItz.js +1 -0
- streamlit/static/static/js/index.D_PrBKnJ.js +3 -0
- streamlit/static/static/js/index.DmuRkekN.js +3855 -0
- streamlit/static/static/js/index.Do6eY8sf.js +1 -0
- streamlit/static/static/js/index.Dz3lP2P-.js +1 -0
- streamlit/static/static/js/index.Dz_UqF-s.js +1 -0
- streamlit/static/static/js/index.GkSUsPhJ.js +1 -0
- streamlit/static/static/js/index.H1U1IC_d.js +3 -0
- streamlit/static/static/js/index.g6p_4DPr.js +1 -0
- streamlit/static/static/js/index.g9x_GKss.js +1 -0
- streamlit/static/static/js/index.zo9jm08y.js +1 -0
- streamlit/static/static/js/input.DnaFglHq.js +2 -0
- streamlit/static/static/js/inputUtils.CQWz5UKz.js +1 -0
- streamlit/static/static/js/memory.Crb9x4-F.js +1 -0
- streamlit/static/static/js/mergeWith.ouAz0sK3.js +1 -0
- streamlit/static/static/js/number-overlay-editor._UaN-O48.js +9 -0
- streamlit/static/static/js/possibleConstructorReturn.CtGjGFHz.js +1 -0
- streamlit/static/static/js/sandbox.CBybYOhV.js +1 -0
- streamlit/static/static/js/sprintf.D7DtBTRn.js +1 -0
- streamlit/static/static/js/textarea.Cb_uJt5U.js +2 -0
- streamlit/static/static/js/threshold.DjX0wlsa.js +1 -0
- streamlit/static/static/js/timepicker.DKT7pfoF.js +4 -0
- streamlit/static/static/js/timer.CAwTRJ_g.js +1 -0
- streamlit/static/static/js/toConsumableArray.05Ikp13-.js +3 -0
- streamlit/static/static/js/uniqueId.D2FMWUEI.js +1 -0
- streamlit/static/static/js/useBasicWidgetState.urnZLANY.js +1 -0
- streamlit/static/static/js/useOnInputChange.BOKIIdJ1.js +1 -0
- streamlit/static/static/js/value.CgPGBV_l.js +1 -0
- streamlit/static/static/js/withFullScreenWrapper.C_N8J0Xx.js +1 -0
- streamlit/static/static/media/KaTeX_AMS-Regular.BQhdFMY1.woff2 +0 -0
- streamlit/static/static/media/KaTeX_AMS-Regular.DMm9YOAa.woff +0 -0
- streamlit/static/static/media/KaTeX_AMS-Regular.DRggAlZN.ttf +0 -0
- streamlit/static/static/media/KaTeX_Caligraphic-Bold.ATXxdsX0.ttf +0 -0
- streamlit/static/static/media/KaTeX_Caligraphic-Bold.BEiXGLvX.woff +0 -0
- streamlit/static/static/media/KaTeX_Caligraphic-Bold.Dq_IR9rO.woff2 +0 -0
- streamlit/static/static/media/KaTeX_Caligraphic-Regular.CTRA-rTL.woff +0 -0
- streamlit/static/static/media/KaTeX_Caligraphic-Regular.Di6jR-x-.woff2 +0 -0
- streamlit/static/static/media/KaTeX_Caligraphic-Regular.wX97UBjC.ttf +0 -0
- streamlit/static/static/media/KaTeX_Fraktur-Bold.BdnERNNW.ttf +0 -0
- streamlit/static/static/media/KaTeX_Fraktur-Bold.BsDP51OF.woff +0 -0
- streamlit/static/static/media/KaTeX_Fraktur-Bold.CL6g_b3V.woff2 +0 -0
- streamlit/static/static/media/KaTeX_Fraktur-Regular.CB_wures.ttf +0 -0
- streamlit/static/static/media/KaTeX_Fraktur-Regular.CTYiF6lA.woff2 +0 -0
- streamlit/static/static/media/KaTeX_Fraktur-Regular.Dxdc4cR9.woff +0 -0
- streamlit/static/static/media/KaTeX_Main-Bold.Cx986IdX.woff2 +0 -0
- streamlit/static/static/media/KaTeX_Main-Bold.Jm3AIy58.woff +0 -0
- streamlit/static/static/media/KaTeX_Main-Bold.waoOVXN0.ttf +0 -0
- streamlit/static/static/media/KaTeX_Main-BoldItalic.DxDJ3AOS.woff2 +0 -0
- streamlit/static/static/media/KaTeX_Main-BoldItalic.DzxPMmG6.ttf +0 -0
- streamlit/static/static/media/KaTeX_Main-BoldItalic.SpSLRI95.woff +0 -0
- streamlit/static/static/media/KaTeX_Main-Italic.3WenGoN9.ttf +0 -0
- streamlit/static/static/media/KaTeX_Main-Italic.BMLOBm91.woff +0 -0
- streamlit/static/static/media/KaTeX_Main-Italic.NWA7e6Wa.woff2 +0 -0
- streamlit/static/static/media/KaTeX_Main-Regular.B22Nviop.woff2 +0 -0
- streamlit/static/static/media/KaTeX_Main-Regular.Dr94JaBh.woff +0 -0
- streamlit/static/static/media/KaTeX_Main-Regular.ypZvNtVU.ttf +0 -0
- streamlit/static/static/media/KaTeX_Math-BoldItalic.B3XSjfu4.ttf +0 -0
- streamlit/static/static/media/KaTeX_Math-BoldItalic.CZnvNsCZ.woff2 +0 -0
- streamlit/static/static/media/KaTeX_Math-BoldItalic.iY-2wyZ7.woff +0 -0
- streamlit/static/static/media/KaTeX_Math-Italic.DA0__PXp.woff +0 -0
- streamlit/static/static/media/KaTeX_Math-Italic.flOr_0UB.ttf +0 -0
- streamlit/static/static/media/KaTeX_Math-Italic.t53AETM-.woff2 +0 -0
- streamlit/static/static/media/KaTeX_SansSerif-Bold.CFMepnvq.ttf +0 -0
- streamlit/static/static/media/KaTeX_SansSerif-Bold.D1sUS0GD.woff2 +0 -0
- streamlit/static/static/media/KaTeX_SansSerif-Bold.DbIhKOiC.woff +0 -0
- streamlit/static/static/media/KaTeX_SansSerif-Italic.C3H0VqGB.woff2 +0 -0
- streamlit/static/static/media/KaTeX_SansSerif-Italic.DN2j7dab.woff +0 -0
- streamlit/static/static/media/KaTeX_SansSerif-Italic.YYjJ1zSn.ttf +0 -0
- streamlit/static/static/media/KaTeX_SansSerif-Regular.BNo7hRIc.ttf +0 -0
- streamlit/static/static/media/KaTeX_SansSerif-Regular.CS6fqUqJ.woff +0 -0
- streamlit/static/static/media/KaTeX_SansSerif-Regular.DDBCnlJ7.woff2 +0 -0
- streamlit/static/static/media/KaTeX_Script-Regular.C5JkGWo-.ttf +0 -0
- streamlit/static/static/media/KaTeX_Script-Regular.D3wIWfF6.woff2 +0 -0
- streamlit/static/static/media/KaTeX_Script-Regular.D5yQViql.woff +0 -0
- streamlit/static/static/media/KaTeX_Size1-Regular.C195tn64.woff +0 -0
- streamlit/static/static/media/KaTeX_Size1-Regular.Dbsnue_I.ttf +0 -0
- streamlit/static/static/media/KaTeX_Size1-Regular.mCD8mA8B.woff2 +0 -0
- streamlit/static/static/media/KaTeX_Size2-Regular.B7gKUWhC.ttf +0 -0
- streamlit/static/static/media/KaTeX_Size2-Regular.Dy4dx90m.woff2 +0 -0
- streamlit/static/static/media/KaTeX_Size2-Regular.oD1tc_U0.woff +0 -0
- streamlit/static/static/media/KaTeX_Size3-Regular.CTq5MqoE.woff +0 -0
- streamlit/static/static/media/KaTeX_Size3-Regular.DgpXs0kz.ttf +0 -0
- streamlit/static/static/media/KaTeX_Size4-Regular.BF-4gkZK.woff +0 -0
- streamlit/static/static/media/KaTeX_Size4-Regular.DWFBv043.ttf +0 -0
- streamlit/static/static/media/KaTeX_Size4-Regular.Dl5lxZxV.woff2 +0 -0
- streamlit/static/static/media/KaTeX_Typewriter-Regular.C0xS9mPB.woff +0 -0
- streamlit/static/static/media/KaTeX_Typewriter-Regular.CO6r4hn1.woff2 +0 -0
- streamlit/static/static/media/KaTeX_Typewriter-Regular.D3Ib7_Hf.ttf +0 -0
- streamlit/static/static/media/MaterialSymbols-Rounded.DcZbplWk.woff2 +0 -0
- streamlit/static/static/media/SourceCodePro-Bold.CFEfr7-q.woff2 +0 -0
- streamlit/static/static/media/SourceCodePro-BoldItalic.C-LkFXxa.woff2 +0 -0
- streamlit/static/static/media/SourceCodePro-Italic.CxFOx7N-.woff2 +0 -0
- streamlit/static/static/media/SourceCodePro-Regular.CBOlD63d.woff2 +0 -0
- streamlit/static/static/media/SourceCodePro-SemiBold.CFHwW3Wd.woff2 +0 -0
- streamlit/static/static/media/SourceCodePro-SemiBoldItalic.Cg2yRu82.woff2 +0 -0
- streamlit/static/static/media/SourceSansPro-Bold.-6c9oR8J.woff2 +0 -0
- streamlit/static/static/media/SourceSansPro-BoldItalic.DmM_grLY.woff2 +0 -0
- streamlit/static/static/media/SourceSansPro-Italic.I1ipWe7Q.woff2 +0 -0
- streamlit/static/static/media/SourceSansPro-Regular.DZLUzqI4.woff2 +0 -0
- streamlit/static/static/media/SourceSansPro-SemiBold.sKQIyTMz.woff2 +0 -0
- streamlit/static/static/media/SourceSansPro-SemiBoldItalic.C0wP0icr.woff2 +0 -0
- streamlit/static/static/media/SourceSerifPro-Bold.8TUnKj4x.woff2 +0 -0
- streamlit/static/static/media/SourceSerifPro-BoldItalic.CBVO7Ve7.woff2 +0 -0
- streamlit/static/static/media/SourceSerifPro-Italic.DkFgL2HZ.woff2 +0 -0
- streamlit/static/static/media/SourceSerifPro-Regular.CNJNET2S.woff2 +0 -0
- streamlit/static/static/media/SourceSerifPro-SemiBold.CHyh9GC5.woff2 +0 -0
- streamlit/static/static/media/SourceSerifPro-SemiBoldItalic.CBtz8sWN.woff2 +0 -0
- streamlit/static/static/media/balloon-0.Czj7AKwE.png +0 -0
- streamlit/static/static/media/balloon-1.CNvFFrND.png +0 -0
- streamlit/static/static/media/balloon-2.DTvC6B1t.png +0 -0
- streamlit/static/static/media/balloon-3.CgSk4tbL.png +0 -0
- streamlit/static/static/media/balloon-4.mbtFrzxf.png +0 -0
- streamlit/static/static/media/balloon-5.CSwkUfRA.png +0 -0
- streamlit/static/static/media/fireworks.B4d-_KUe.gif +0 -0
- streamlit/static/static/media/flake-0.DgWaVvm5.png +0 -0
- streamlit/static/static/media/flake-1.B2r5AHMK.png +0 -0
- streamlit/static/static/media/flake-2.BnWSExPC.png +0 -0
- streamlit/static/static/media/snowflake.JU2jBHL8.svg +11 -0
- streamlit/string_util.py +203 -0
- streamlit/temporary_directory.py +56 -0
- streamlit/testing/__init__.py +13 -0
- streamlit/testing/v1/__init__.py +17 -0
- streamlit/testing/v1/app_test.py +1050 -0
- streamlit/testing/v1/element_tree.py +2083 -0
- streamlit/testing/v1/local_script_runner.py +180 -0
- streamlit/testing/v1/util.py +53 -0
- streamlit/time_util.py +75 -0
- streamlit/type_util.py +460 -0
- streamlit/url_util.py +122 -0
- streamlit/user_info.py +519 -0
- streamlit/util.py +72 -0
- streamlit/vendor/__init__.py +0 -0
- streamlit/vendor/pympler/__init__.py +0 -0
- streamlit/vendor/pympler/asizeof.py +2869 -0
- streamlit/version.py +18 -0
- streamlit/watcher/__init__.py +28 -0
- streamlit/watcher/event_based_path_watcher.py +406 -0
- streamlit/watcher/folder_black_list.py +82 -0
- streamlit/watcher/local_sources_watcher.py +233 -0
- streamlit/watcher/path_watcher.py +185 -0
- streamlit/watcher/polling_path_watcher.py +124 -0
- streamlit/watcher/util.py +207 -0
- streamlit/web/__init__.py +13 -0
- streamlit/web/bootstrap.py +353 -0
- streamlit/web/cache_storage_manager_config.py +38 -0
- streamlit/web/cli.py +369 -0
- streamlit/web/server/__init__.py +26 -0
- streamlit/web/server/app_static_file_handler.py +93 -0
- streamlit/web/server/authlib_tornado_integration.py +60 -0
- streamlit/web/server/browser_websocket_handler.py +246 -0
- streamlit/web/server/component_request_handler.py +116 -0
- streamlit/web/server/media_file_handler.py +141 -0
- streamlit/web/server/oauth_authlib_routes.py +176 -0
- streamlit/web/server/oidc_mixin.py +108 -0
- streamlit/web/server/routes.py +295 -0
- streamlit/web/server/server.py +479 -0
- streamlit/web/server/server_util.py +161 -0
- streamlit/web/server/stats_request_handler.py +95 -0
- streamlit/web/server/upload_file_request_handler.py +137 -0
- streamlit/web/server/websocket_headers.py +56 -0
- streamlit_nightly-1.43.2.dev20250307.data/scripts/streamlit.cmd +16 -0
- streamlit_nightly-1.43.2.dev20250307.dist-info/METADATA +207 -0
- streamlit_nightly-1.43.2.dev20250307.dist-info/RECORD +563 -0
- streamlit_nightly-1.43.2.dev20250307.dist-info/WHEEL +5 -0
- streamlit_nightly-1.43.2.dev20250307.dist-info/entry_points.txt +2 -0
- streamlit_nightly-1.43.2.dev20250307.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2083 @@
|
|
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
|
+
from __future__ import annotations
|
15
|
+
|
16
|
+
import textwrap
|
17
|
+
from abc import ABC, abstractmethod
|
18
|
+
from collections.abc import Sequence
|
19
|
+
from dataclasses import dataclass, field, fields, is_dataclass
|
20
|
+
from datetime import date, datetime, time, timedelta
|
21
|
+
from typing import (
|
22
|
+
TYPE_CHECKING,
|
23
|
+
Any,
|
24
|
+
Callable,
|
25
|
+
Generic,
|
26
|
+
TypeVar,
|
27
|
+
Union,
|
28
|
+
cast,
|
29
|
+
overload,
|
30
|
+
)
|
31
|
+
|
32
|
+
from typing_extensions import TypeAlias
|
33
|
+
|
34
|
+
from streamlit import dataframe_util, util
|
35
|
+
from streamlit.elements.heading import HeadingProtoTag
|
36
|
+
from streamlit.elements.widgets.select_slider import SelectSliderSerde
|
37
|
+
from streamlit.elements.widgets.slider import (
|
38
|
+
SliderSerde,
|
39
|
+
SliderStep,
|
40
|
+
SliderValueT,
|
41
|
+
)
|
42
|
+
from streamlit.elements.widgets.time_widgets import (
|
43
|
+
DateInputSerde,
|
44
|
+
DateWidgetReturn,
|
45
|
+
TimeInputSerde,
|
46
|
+
_parse_date_value,
|
47
|
+
)
|
48
|
+
from streamlit.proto.Alert_pb2 import Alert as AlertProto
|
49
|
+
from streamlit.proto.Checkbox_pb2 import Checkbox as CheckboxProto
|
50
|
+
from streamlit.proto.Markdown_pb2 import Markdown as MarkdownProto
|
51
|
+
from streamlit.proto.Slider_pb2 import Slider as SliderProto
|
52
|
+
from streamlit.proto.WidgetStates_pb2 import WidgetState, WidgetStates
|
53
|
+
from streamlit.runtime.state.common import TESTING_KEY, user_key_from_element_id
|
54
|
+
|
55
|
+
if TYPE_CHECKING:
|
56
|
+
from pandas import DataFrame as PandasDataframe
|
57
|
+
|
58
|
+
from streamlit.proto.Arrow_pb2 import Arrow as ArrowProto
|
59
|
+
from streamlit.proto.Block_pb2 import Block as BlockProto
|
60
|
+
from streamlit.proto.Button_pb2 import Button as ButtonProto
|
61
|
+
from streamlit.proto.ButtonGroup_pb2 import ButtonGroup as ButtonGroupProto
|
62
|
+
from streamlit.proto.ChatInput_pb2 import ChatInput as ChatInputProto
|
63
|
+
from streamlit.proto.Code_pb2 import Code as CodeProto
|
64
|
+
from streamlit.proto.ColorPicker_pb2 import ColorPicker as ColorPickerProto
|
65
|
+
from streamlit.proto.DateInput_pb2 import DateInput as DateInputProto
|
66
|
+
from streamlit.proto.Element_pb2 import Element as ElementProto
|
67
|
+
from streamlit.proto.Exception_pb2 import Exception as ExceptionProto
|
68
|
+
from streamlit.proto.ForwardMsg_pb2 import ForwardMsg
|
69
|
+
from streamlit.proto.Heading_pb2 import Heading as HeadingProto
|
70
|
+
from streamlit.proto.Json_pb2 import Json as JsonProto
|
71
|
+
from streamlit.proto.Metric_pb2 import Metric as MetricProto
|
72
|
+
from streamlit.proto.MultiSelect_pb2 import MultiSelect as MultiSelectProto
|
73
|
+
from streamlit.proto.NumberInput_pb2 import NumberInput as NumberInputProto
|
74
|
+
from streamlit.proto.Radio_pb2 import Radio as RadioProto
|
75
|
+
from streamlit.proto.Selectbox_pb2 import Selectbox as SelectboxProto
|
76
|
+
from streamlit.proto.Text_pb2 import Text as TextProto
|
77
|
+
from streamlit.proto.TextArea_pb2 import TextArea as TextAreaProto
|
78
|
+
from streamlit.proto.TextInput_pb2 import TextInput as TextInputProto
|
79
|
+
from streamlit.proto.TimeInput_pb2 import TimeInput as TimeInputProto
|
80
|
+
from streamlit.proto.Toast_pb2 import Toast as ToastProto
|
81
|
+
from streamlit.runtime.state.safe_session_state import SafeSessionState
|
82
|
+
from streamlit.testing.v1.app_test import AppTest
|
83
|
+
|
84
|
+
T = TypeVar("T")
|
85
|
+
|
86
|
+
|
87
|
+
@dataclass
|
88
|
+
class InitialValue:
|
89
|
+
"""This class is used to represent the initial value of a widget."""
|
90
|
+
|
91
|
+
pass
|
92
|
+
|
93
|
+
|
94
|
+
# TODO: This class serves as a fallback option for elements that have not
|
95
|
+
# been implemented yet, as well as providing implementations of some
|
96
|
+
# trivial methods. It may have significantly reduced scope once all elements
|
97
|
+
# have been implemented.
|
98
|
+
# This class will not be sufficient implementation for most elements.
|
99
|
+
# Widgets need their own classes to translate interactions into the appropriate
|
100
|
+
# WidgetState and provide higher level interaction interfaces, and other elements
|
101
|
+
# have enough variation in how to get their values that most will need their
|
102
|
+
# own classes too.
|
103
|
+
@dataclass
|
104
|
+
class Element(ABC):
|
105
|
+
"""
|
106
|
+
Element base class for testing.
|
107
|
+
|
108
|
+
This class's methods and attributes are universal for all elements
|
109
|
+
implemented in testing. For example, ``Caption``, ``Code``, ``Text``, and
|
110
|
+
``Title`` inherit from ``Element``. All widget classes also
|
111
|
+
inherit from Element, but have additional methods specific to each
|
112
|
+
widget type. See the ``AppTest`` class for the full list of supported
|
113
|
+
elements.
|
114
|
+
|
115
|
+
For all element classes, parameters of the original element can be obtained
|
116
|
+
as properties. For example, ``Button.label``, ``Caption.help``, and
|
117
|
+
``Toast.icon``.
|
118
|
+
|
119
|
+
"""
|
120
|
+
|
121
|
+
type: str = field(repr=False)
|
122
|
+
proto: Any = field(repr=False)
|
123
|
+
root: ElementTree = field(repr=False)
|
124
|
+
key: str | None
|
125
|
+
|
126
|
+
@abstractmethod
|
127
|
+
def __init__(self, proto: ElementProto, root: ElementTree): ...
|
128
|
+
|
129
|
+
def __iter__(self):
|
130
|
+
yield self
|
131
|
+
|
132
|
+
@property
|
133
|
+
@abstractmethod
|
134
|
+
def value(self) -> Any:
|
135
|
+
"""The value or contents of the element."""
|
136
|
+
...
|
137
|
+
|
138
|
+
def __getattr__(self, name: str) -> Any:
|
139
|
+
"""Fallback attempt to get an attribute from the proto"""
|
140
|
+
return getattr(self.proto, name)
|
141
|
+
|
142
|
+
def run(self, *, timeout: float | None = None) -> AppTest:
|
143
|
+
"""Run the ``AppTest`` script which contains the element.
|
144
|
+
|
145
|
+
Parameters
|
146
|
+
----------
|
147
|
+
timeout
|
148
|
+
The maximum number of seconds to run the script. None means
|
149
|
+
use the AppTest's default.
|
150
|
+
"""
|
151
|
+
return self.root.run(timeout=timeout)
|
152
|
+
|
153
|
+
def __repr__(self):
|
154
|
+
return util.repr_(self)
|
155
|
+
|
156
|
+
|
157
|
+
@dataclass(repr=False)
|
158
|
+
class UnknownElement(Element):
|
159
|
+
def __init__(self, proto: ElementProto, root: ElementTree):
|
160
|
+
ty = proto.WhichOneof("type")
|
161
|
+
assert ty is not None
|
162
|
+
self.proto = getattr(proto, ty)
|
163
|
+
self.root = root
|
164
|
+
self.type = ty
|
165
|
+
self.key = None
|
166
|
+
|
167
|
+
@property
|
168
|
+
def value(self) -> Any:
|
169
|
+
try:
|
170
|
+
state = self.root.session_state
|
171
|
+
assert state is not None
|
172
|
+
return state[self.proto.id]
|
173
|
+
except ValueError:
|
174
|
+
# No id field, not a widget
|
175
|
+
return self.proto.value
|
176
|
+
|
177
|
+
|
178
|
+
@dataclass(repr=False)
|
179
|
+
class Widget(Element, ABC):
|
180
|
+
"""Widget base class for testing."""
|
181
|
+
|
182
|
+
id: str = field(repr=False)
|
183
|
+
disabled: bool
|
184
|
+
key: str | None
|
185
|
+
_value: Any
|
186
|
+
|
187
|
+
def __init__(self, proto: Any, root: ElementTree):
|
188
|
+
self.proto = proto
|
189
|
+
self.root = root
|
190
|
+
self.key = user_key_from_element_id(self.id)
|
191
|
+
self._value = None
|
192
|
+
|
193
|
+
def set_value(self, v: Any):
|
194
|
+
"""Set the value of the widget."""
|
195
|
+
self._value = v
|
196
|
+
return self
|
197
|
+
|
198
|
+
@property
|
199
|
+
@abstractmethod
|
200
|
+
def _widget_state(self) -> WidgetState: ...
|
201
|
+
|
202
|
+
|
203
|
+
El_co = TypeVar("El_co", bound=Element, covariant=True)
|
204
|
+
|
205
|
+
|
206
|
+
class ElementList(Generic[El_co]):
|
207
|
+
def __init__(self, els: Sequence[El_co]):
|
208
|
+
self._list: Sequence[El_co] = els
|
209
|
+
|
210
|
+
def __len__(self) -> int:
|
211
|
+
return len(self._list)
|
212
|
+
|
213
|
+
@property
|
214
|
+
def len(self) -> int:
|
215
|
+
return len(self)
|
216
|
+
|
217
|
+
@overload
|
218
|
+
def __getitem__(self, idx: int) -> El_co: ...
|
219
|
+
|
220
|
+
@overload
|
221
|
+
def __getitem__(self, idx: slice) -> ElementList[El_co]: ...
|
222
|
+
|
223
|
+
def __getitem__(self, idx: int | slice) -> El_co | ElementList[El_co]:
|
224
|
+
if isinstance(idx, slice):
|
225
|
+
return ElementList(self._list[idx])
|
226
|
+
else:
|
227
|
+
return self._list[idx]
|
228
|
+
|
229
|
+
def __iter__(self):
|
230
|
+
yield from self._list
|
231
|
+
|
232
|
+
def __repr__(self):
|
233
|
+
return util.repr_(self)
|
234
|
+
|
235
|
+
def __eq__(self, other: ElementList[El_co] | object) -> bool:
|
236
|
+
if isinstance(other, ElementList):
|
237
|
+
return self._list == other._list
|
238
|
+
else:
|
239
|
+
return self._list == other
|
240
|
+
|
241
|
+
@property
|
242
|
+
def values(self) -> Sequence[Any]:
|
243
|
+
return [e.value for e in self]
|
244
|
+
|
245
|
+
|
246
|
+
W_co = TypeVar("W_co", bound=Widget, covariant=True)
|
247
|
+
|
248
|
+
|
249
|
+
class WidgetList(ElementList[W_co], Generic[W_co]):
|
250
|
+
def __call__(self, key: str) -> W_co:
|
251
|
+
for e in self._list:
|
252
|
+
if e.key == key:
|
253
|
+
return e
|
254
|
+
|
255
|
+
raise KeyError(key)
|
256
|
+
|
257
|
+
|
258
|
+
@dataclass(repr=False)
|
259
|
+
class AlertBase(Element):
|
260
|
+
proto: AlertProto = field(repr=False)
|
261
|
+
icon: str
|
262
|
+
|
263
|
+
def __init__(self, proto: AlertProto, root: ElementTree):
|
264
|
+
self.proto = proto
|
265
|
+
self.key = None
|
266
|
+
self.root = root
|
267
|
+
|
268
|
+
@property
|
269
|
+
def value(self) -> str:
|
270
|
+
return self.proto.body
|
271
|
+
|
272
|
+
|
273
|
+
@dataclass(repr=False)
|
274
|
+
class Error(AlertBase):
|
275
|
+
def __init__(self, proto: AlertProto, root: ElementTree):
|
276
|
+
super().__init__(proto, root)
|
277
|
+
self.type = "error"
|
278
|
+
|
279
|
+
|
280
|
+
@dataclass(repr=False)
|
281
|
+
class Warning(AlertBase):
|
282
|
+
def __init__(self, proto: AlertProto, root: ElementTree):
|
283
|
+
super().__init__(proto, root)
|
284
|
+
self.type = "warning"
|
285
|
+
|
286
|
+
|
287
|
+
@dataclass(repr=False)
|
288
|
+
class Info(AlertBase):
|
289
|
+
def __init__(self, proto: AlertProto, root: ElementTree):
|
290
|
+
super().__init__(proto, root)
|
291
|
+
self.type = "info"
|
292
|
+
|
293
|
+
|
294
|
+
@dataclass(repr=False)
|
295
|
+
class Success(AlertBase):
|
296
|
+
def __init__(self, proto: AlertProto, root: ElementTree):
|
297
|
+
super().__init__(proto, root)
|
298
|
+
self.type = "success"
|
299
|
+
|
300
|
+
|
301
|
+
@dataclass(repr=False)
|
302
|
+
class Button(Widget):
|
303
|
+
"""A representation of ``st.button`` and ``st.form_submit_button``."""
|
304
|
+
|
305
|
+
_value: bool
|
306
|
+
|
307
|
+
proto: ButtonProto = field(repr=False)
|
308
|
+
label: str
|
309
|
+
help: str
|
310
|
+
form_id: str
|
311
|
+
|
312
|
+
def __init__(self, proto: ButtonProto, root: ElementTree):
|
313
|
+
super().__init__(proto, root)
|
314
|
+
self._value = False
|
315
|
+
self.type = "button"
|
316
|
+
|
317
|
+
@property
|
318
|
+
def _widget_state(self) -> WidgetState:
|
319
|
+
ws = WidgetState()
|
320
|
+
ws.id = self.id
|
321
|
+
ws.trigger_value = self._value
|
322
|
+
return ws
|
323
|
+
|
324
|
+
@property
|
325
|
+
def value(self) -> bool:
|
326
|
+
"""The value of the button. (bool)"""
|
327
|
+
if self._value:
|
328
|
+
return self._value
|
329
|
+
else:
|
330
|
+
state = self.root.session_state
|
331
|
+
assert state
|
332
|
+
return cast(bool, state[TESTING_KEY][self.id])
|
333
|
+
|
334
|
+
def set_value(self, v: bool) -> Button:
|
335
|
+
"""Set the value of the button."""
|
336
|
+
self._value = v
|
337
|
+
return self
|
338
|
+
|
339
|
+
def click(self) -> Button:
|
340
|
+
"""Set the value of the button to True."""
|
341
|
+
return self.set_value(True)
|
342
|
+
|
343
|
+
|
344
|
+
@dataclass(repr=False)
|
345
|
+
class ChatInput(Widget):
|
346
|
+
"""A representation of ``st.chat_input``."""
|
347
|
+
|
348
|
+
_value: str | None
|
349
|
+
proto: ChatInputProto = field(repr=False)
|
350
|
+
placeholder: str
|
351
|
+
|
352
|
+
def __init__(self, proto: ChatInputProto, root: ElementTree):
|
353
|
+
super().__init__(proto, root)
|
354
|
+
self.type = "chat_input"
|
355
|
+
|
356
|
+
def set_value(self, v: str | None) -> ChatInput:
|
357
|
+
"""Set the value of the widget."""
|
358
|
+
self._value = v
|
359
|
+
return self
|
360
|
+
|
361
|
+
@property
|
362
|
+
def _widget_state(self) -> WidgetState:
|
363
|
+
ws = WidgetState()
|
364
|
+
ws.id = self.id
|
365
|
+
if self._value is not None:
|
366
|
+
ws.string_trigger_value.data = self._value
|
367
|
+
return ws
|
368
|
+
|
369
|
+
@property
|
370
|
+
def value(self) -> str | None:
|
371
|
+
"""The value of the widget. (str)"""
|
372
|
+
if self._value:
|
373
|
+
return self._value
|
374
|
+
else:
|
375
|
+
state = self.root.session_state
|
376
|
+
assert state
|
377
|
+
return state[TESTING_KEY][self.id] # type: ignore
|
378
|
+
|
379
|
+
|
380
|
+
@dataclass(repr=False)
|
381
|
+
class Checkbox(Widget):
|
382
|
+
"""A representation of ``st.checkbox``."""
|
383
|
+
|
384
|
+
_value: bool | None
|
385
|
+
|
386
|
+
proto: CheckboxProto = field(repr=False)
|
387
|
+
label: str
|
388
|
+
help: str
|
389
|
+
form_id: str
|
390
|
+
|
391
|
+
def __init__(self, proto: CheckboxProto, root: ElementTree):
|
392
|
+
super().__init__(proto, root)
|
393
|
+
self.type = "checkbox"
|
394
|
+
|
395
|
+
@property
|
396
|
+
def _widget_state(self) -> WidgetState:
|
397
|
+
ws = WidgetState()
|
398
|
+
ws.id = self.id
|
399
|
+
ws.bool_value = self.value
|
400
|
+
return ws
|
401
|
+
|
402
|
+
@property
|
403
|
+
def value(self) -> bool:
|
404
|
+
"""The value of the widget. (bool)"""
|
405
|
+
if self._value is not None:
|
406
|
+
return self._value
|
407
|
+
else:
|
408
|
+
state = self.root.session_state
|
409
|
+
assert state
|
410
|
+
return cast(bool, state[self.id])
|
411
|
+
|
412
|
+
def set_value(self, v: bool) -> Checkbox:
|
413
|
+
"""Set the value of the widget."""
|
414
|
+
self._value = v
|
415
|
+
return self
|
416
|
+
|
417
|
+
def check(self) -> Checkbox:
|
418
|
+
"""Set the value of the widget to True."""
|
419
|
+
return self.set_value(True)
|
420
|
+
|
421
|
+
def uncheck(self) -> Checkbox:
|
422
|
+
"""Set the value of the widget to False."""
|
423
|
+
return self.set_value(False)
|
424
|
+
|
425
|
+
|
426
|
+
@dataclass(repr=False)
|
427
|
+
class Code(Element):
|
428
|
+
"""A representation of ``st.code``."""
|
429
|
+
|
430
|
+
proto: CodeProto = field(repr=False)
|
431
|
+
|
432
|
+
language: str
|
433
|
+
show_line_numbers: bool
|
434
|
+
key: None
|
435
|
+
|
436
|
+
def __init__(self, proto: CodeProto, root: ElementTree):
|
437
|
+
self.proto = proto
|
438
|
+
self.key = None
|
439
|
+
self.root = root
|
440
|
+
self.type = "code"
|
441
|
+
|
442
|
+
@property
|
443
|
+
def value(self) -> str:
|
444
|
+
"""The value of the element. (str)"""
|
445
|
+
return self.proto.code_text
|
446
|
+
|
447
|
+
|
448
|
+
@dataclass(repr=False)
|
449
|
+
class ColorPicker(Widget):
|
450
|
+
"""A representation of ``st.color_picker``."""
|
451
|
+
|
452
|
+
_value: str | None
|
453
|
+
label: str
|
454
|
+
help: str
|
455
|
+
form_id: str
|
456
|
+
|
457
|
+
proto: ColorPickerProto = field(repr=False)
|
458
|
+
|
459
|
+
def __init__(self, proto: ColorPickerProto, root: ElementTree):
|
460
|
+
super().__init__(proto, root)
|
461
|
+
self.type = "color_picker"
|
462
|
+
|
463
|
+
@property
|
464
|
+
def value(self) -> str:
|
465
|
+
"""The currently selected value as a hex string. (str)"""
|
466
|
+
if self._value is not None:
|
467
|
+
return self._value
|
468
|
+
else:
|
469
|
+
state = self.root.session_state
|
470
|
+
assert state
|
471
|
+
return cast(str, state[self.id])
|
472
|
+
|
473
|
+
@property
|
474
|
+
def _widget_state(self) -> WidgetState:
|
475
|
+
"""Protobuf message representing the state of the widget, including
|
476
|
+
any interactions that have happened.
|
477
|
+
Should be the same as the frontend would produce for those interactions.
|
478
|
+
"""
|
479
|
+
ws = WidgetState()
|
480
|
+
ws.id = self.id
|
481
|
+
ws.string_value = self.value
|
482
|
+
return ws
|
483
|
+
|
484
|
+
def set_value(self, v: str) -> ColorPicker:
|
485
|
+
"""Set the value of the widget as a hex string."""
|
486
|
+
self._value = v
|
487
|
+
return self
|
488
|
+
|
489
|
+
def pick(self, v: str) -> ColorPicker:
|
490
|
+
"""Set the value of the widget as a hex string. May omit the "#" prefix."""
|
491
|
+
if not v.startswith("#"):
|
492
|
+
v = f"#{v}"
|
493
|
+
return self.set_value(v)
|
494
|
+
|
495
|
+
|
496
|
+
@dataclass(repr=False)
|
497
|
+
class Dataframe(Element):
|
498
|
+
proto: ArrowProto = field(repr=False)
|
499
|
+
|
500
|
+
def __init__(self, proto: ArrowProto, root: ElementTree):
|
501
|
+
self.key = None
|
502
|
+
self.proto = proto
|
503
|
+
self.root = root
|
504
|
+
self.type = "arrow_data_frame"
|
505
|
+
|
506
|
+
@property
|
507
|
+
def value(self) -> PandasDataframe:
|
508
|
+
return dataframe_util.convert_arrow_bytes_to_pandas_df(self.proto.data)
|
509
|
+
|
510
|
+
|
511
|
+
SingleDateValue: TypeAlias = Union[date, datetime]
|
512
|
+
DateValue: TypeAlias = Union[SingleDateValue, Sequence[SingleDateValue], None]
|
513
|
+
|
514
|
+
|
515
|
+
@dataclass(repr=False)
|
516
|
+
class DateInput(Widget):
|
517
|
+
"""A representation of ``st.date_input``."""
|
518
|
+
|
519
|
+
_value: DateValue | None | InitialValue
|
520
|
+
proto: DateInputProto = field(repr=False)
|
521
|
+
label: str
|
522
|
+
min: date
|
523
|
+
max: date
|
524
|
+
is_range: bool
|
525
|
+
help: str
|
526
|
+
form_id: str
|
527
|
+
|
528
|
+
def __init__(self, proto: DateInputProto, root: ElementTree):
|
529
|
+
super().__init__(proto, root)
|
530
|
+
self._value = InitialValue()
|
531
|
+
self.type = "date_input"
|
532
|
+
self.min = datetime.strptime(proto.min, "%Y/%m/%d").date()
|
533
|
+
self.max = datetime.strptime(proto.max, "%Y/%m/%d").date()
|
534
|
+
|
535
|
+
def set_value(self, v: DateValue) -> DateInput:
|
536
|
+
"""Set the value of the widget."""
|
537
|
+
self._value = v
|
538
|
+
return self
|
539
|
+
|
540
|
+
@property
|
541
|
+
def _widget_state(self) -> WidgetState:
|
542
|
+
ws = WidgetState()
|
543
|
+
ws.id = self.id
|
544
|
+
|
545
|
+
serde = DateInputSerde(None) # type: ignore
|
546
|
+
ws.string_array_value.data[:] = serde.serialize(self.value)
|
547
|
+
return ws
|
548
|
+
|
549
|
+
@property
|
550
|
+
def value(self) -> DateWidgetReturn:
|
551
|
+
"""The value of the widget. (date or Tuple of date)"""
|
552
|
+
if not isinstance(self._value, InitialValue):
|
553
|
+
parsed, _ = _parse_date_value(self._value)
|
554
|
+
return tuple(parsed) if parsed is not None else None # type: ignore
|
555
|
+
else:
|
556
|
+
state = self.root.session_state
|
557
|
+
assert state
|
558
|
+
return state[self.id] # type: ignore
|
559
|
+
|
560
|
+
|
561
|
+
@dataclass(repr=False)
|
562
|
+
class Exception(Element):
|
563
|
+
message: str
|
564
|
+
is_markdown: bool
|
565
|
+
stack_trace: list[str]
|
566
|
+
is_warning: bool
|
567
|
+
|
568
|
+
def __init__(self, proto: ExceptionProto, root: ElementTree):
|
569
|
+
self.key = None
|
570
|
+
self.root = root
|
571
|
+
self.proto = proto
|
572
|
+
self.type = "exception"
|
573
|
+
|
574
|
+
self.is_markdown = proto.message_is_markdown
|
575
|
+
self.stack_trace = list(proto.stack_trace)
|
576
|
+
|
577
|
+
@property
|
578
|
+
def value(self) -> str:
|
579
|
+
return self.message
|
580
|
+
|
581
|
+
|
582
|
+
@dataclass(repr=False)
|
583
|
+
class HeadingBase(Element, ABC):
|
584
|
+
proto: HeadingProto = field(repr=False)
|
585
|
+
|
586
|
+
tag: str
|
587
|
+
anchor: str | None
|
588
|
+
hide_anchor: bool
|
589
|
+
key: None
|
590
|
+
|
591
|
+
def __init__(self, proto: HeadingProto, root: ElementTree, type_: str):
|
592
|
+
self.proto = proto
|
593
|
+
self.key = None
|
594
|
+
self.root = root
|
595
|
+
self.type = type_
|
596
|
+
|
597
|
+
@property
|
598
|
+
def value(self) -> str:
|
599
|
+
return self.proto.body
|
600
|
+
|
601
|
+
|
602
|
+
@dataclass(repr=False)
|
603
|
+
class Header(HeadingBase):
|
604
|
+
def __init__(self, proto: HeadingProto, root: ElementTree):
|
605
|
+
super().__init__(proto, root, "header")
|
606
|
+
|
607
|
+
|
608
|
+
@dataclass(repr=False)
|
609
|
+
class Subheader(HeadingBase):
|
610
|
+
def __init__(self, proto: HeadingProto, root: ElementTree):
|
611
|
+
super().__init__(proto, root, "subheader")
|
612
|
+
|
613
|
+
|
614
|
+
@dataclass(repr=False)
|
615
|
+
class Title(HeadingBase):
|
616
|
+
def __init__(self, proto: HeadingProto, root: ElementTree):
|
617
|
+
super().__init__(proto, root, "title")
|
618
|
+
|
619
|
+
|
620
|
+
@dataclass(repr=False)
|
621
|
+
class Json(Element):
|
622
|
+
proto: JsonProto = field(repr=False)
|
623
|
+
|
624
|
+
expanded: bool
|
625
|
+
|
626
|
+
def __init__(self, proto: JsonProto, root: ElementTree):
|
627
|
+
self.proto = proto
|
628
|
+
self.key = None
|
629
|
+
self.root = root
|
630
|
+
self.type = "json"
|
631
|
+
|
632
|
+
@property
|
633
|
+
def value(self) -> str:
|
634
|
+
return self.proto.body
|
635
|
+
|
636
|
+
|
637
|
+
@dataclass(repr=False)
|
638
|
+
class Markdown(Element):
|
639
|
+
proto: MarkdownProto = field(repr=False)
|
640
|
+
|
641
|
+
is_caption: bool
|
642
|
+
allow_html: bool
|
643
|
+
key: None
|
644
|
+
|
645
|
+
def __init__(self, proto: MarkdownProto, root: ElementTree):
|
646
|
+
self.proto = proto
|
647
|
+
self.key = None
|
648
|
+
self.root = root
|
649
|
+
self.type = "markdown"
|
650
|
+
|
651
|
+
@property
|
652
|
+
def value(self) -> str:
|
653
|
+
return self.proto.body
|
654
|
+
|
655
|
+
|
656
|
+
@dataclass(repr=False)
|
657
|
+
class Caption(Markdown):
|
658
|
+
def __init__(self, proto: MarkdownProto, root: ElementTree):
|
659
|
+
super().__init__(proto, root)
|
660
|
+
self.type = "caption"
|
661
|
+
|
662
|
+
|
663
|
+
@dataclass(repr=False)
|
664
|
+
class Divider(Markdown):
|
665
|
+
def __init__(self, proto: MarkdownProto, root: ElementTree):
|
666
|
+
super().__init__(proto, root)
|
667
|
+
self.type = "divider"
|
668
|
+
|
669
|
+
|
670
|
+
@dataclass(repr=False)
|
671
|
+
class Latex(Markdown):
|
672
|
+
def __init__(self, proto: MarkdownProto, root: ElementTree):
|
673
|
+
super().__init__(proto, root)
|
674
|
+
self.type = "latex"
|
675
|
+
|
676
|
+
|
677
|
+
@dataclass(repr=False)
|
678
|
+
class Metric(Element):
|
679
|
+
proto: MetricProto
|
680
|
+
label: str
|
681
|
+
delta: str
|
682
|
+
color: str
|
683
|
+
help: str
|
684
|
+
|
685
|
+
def __init__(self, proto: MetricProto, root: ElementTree):
|
686
|
+
self.proto = proto
|
687
|
+
self.key = None
|
688
|
+
self.root = root
|
689
|
+
self.type = "metric"
|
690
|
+
|
691
|
+
@property
|
692
|
+
def value(self) -> str:
|
693
|
+
return self.proto.body
|
694
|
+
|
695
|
+
|
696
|
+
@dataclass(repr=False)
|
697
|
+
class ButtonGroup(Widget, Generic[T]):
|
698
|
+
"""A representation of button_group that is used by ``st.feedback``."""
|
699
|
+
|
700
|
+
_value: list[T] | None
|
701
|
+
|
702
|
+
proto: ButtonGroupProto = field(repr=False)
|
703
|
+
options: list[ButtonGroupProto.Option]
|
704
|
+
form_id: str
|
705
|
+
|
706
|
+
def __init__(self, proto: ButtonGroupProto, root: ElementTree):
|
707
|
+
super().__init__(proto, root)
|
708
|
+
self.type = "button_group"
|
709
|
+
self.options = list(proto.options)
|
710
|
+
|
711
|
+
@property
|
712
|
+
def _widget_state(self) -> WidgetState:
|
713
|
+
"""Protobuf message representing the state of the widget, including
|
714
|
+
any interactions that have happened.
|
715
|
+
Should be the same as the frontend would produce for those interactions.
|
716
|
+
"""
|
717
|
+
ws = WidgetState()
|
718
|
+
ws.id = self.id
|
719
|
+
ws.int_array_value.data[:] = self.indices
|
720
|
+
return ws
|
721
|
+
|
722
|
+
@property
|
723
|
+
def value(self) -> list[T]:
|
724
|
+
"""The currently selected values from the options. (list)"""
|
725
|
+
if self._value is not None:
|
726
|
+
return self._value
|
727
|
+
else:
|
728
|
+
state = self.root.session_state
|
729
|
+
assert state
|
730
|
+
return cast(list[T], state[self.id])
|
731
|
+
|
732
|
+
@property
|
733
|
+
def indices(self) -> Sequence[int]:
|
734
|
+
"""The indices of the currently selected values from the options. (list)"""
|
735
|
+
return [self.options.index(self.format_func(v)) for v in self.value]
|
736
|
+
|
737
|
+
@property
|
738
|
+
def format_func(self) -> Callable[[Any], Any]:
|
739
|
+
"""The widget's formatting function for displaying options. (callable)"""
|
740
|
+
ss = self.root.session_state
|
741
|
+
return cast(Callable[[Any], Any], ss[TESTING_KEY][self.id])
|
742
|
+
|
743
|
+
def set_value(self, v: list[T]) -> ButtonGroup[T]:
|
744
|
+
"""Set the value of the multiselect widget. (list)"""
|
745
|
+
|
746
|
+
self._value = v
|
747
|
+
return self
|
748
|
+
|
749
|
+
def select(self, v: T) -> ButtonGroup[T]:
|
750
|
+
"""
|
751
|
+
Add a selection to the widget. Do nothing if the value is already selected.\
|
752
|
+
If testing a multiselect widget with repeated options, use ``set_value``\
|
753
|
+
instead.
|
754
|
+
"""
|
755
|
+
current = self.value
|
756
|
+
if v in current:
|
757
|
+
return self
|
758
|
+
else:
|
759
|
+
new = current.copy()
|
760
|
+
new.append(v)
|
761
|
+
self.set_value(new)
|
762
|
+
return self
|
763
|
+
|
764
|
+
def unselect(self, v: T) -> ButtonGroup[T]:
|
765
|
+
"""
|
766
|
+
Remove a selection from the widget. Do nothing if the value is not\
|
767
|
+
already selected. If a value is selected multiple times, the first\
|
768
|
+
instance is removed.
|
769
|
+
"""
|
770
|
+
current = self.value
|
771
|
+
if v not in current:
|
772
|
+
return self
|
773
|
+
else:
|
774
|
+
new = current.copy()
|
775
|
+
while v in new:
|
776
|
+
new.remove(v)
|
777
|
+
self.set_value(new)
|
778
|
+
return self
|
779
|
+
|
780
|
+
|
781
|
+
@dataclass(repr=False)
|
782
|
+
class Multiselect(Widget, Generic[T]):
|
783
|
+
"""A representation of ``st.multiselect``."""
|
784
|
+
|
785
|
+
_value: list[T] | None
|
786
|
+
|
787
|
+
proto: MultiSelectProto = field(repr=False)
|
788
|
+
label: str
|
789
|
+
options: list[str]
|
790
|
+
max_selections: int
|
791
|
+
help: str
|
792
|
+
form_id: str
|
793
|
+
|
794
|
+
def __init__(self, proto: MultiSelectProto, root: ElementTree):
|
795
|
+
super().__init__(proto, root)
|
796
|
+
self.type = "multiselect"
|
797
|
+
self.options = list(proto.options)
|
798
|
+
|
799
|
+
@property
|
800
|
+
def _widget_state(self) -> WidgetState:
|
801
|
+
"""Protobuf message representing the state of the widget, including
|
802
|
+
any interactions that have happened.
|
803
|
+
Should be the same as the frontend would produce for those interactions.
|
804
|
+
"""
|
805
|
+
ws = WidgetState()
|
806
|
+
ws.id = self.id
|
807
|
+
ws.int_array_value.data[:] = self.indices
|
808
|
+
return ws
|
809
|
+
|
810
|
+
@property
|
811
|
+
def value(self) -> list[T]:
|
812
|
+
"""The currently selected values from the options. (list)"""
|
813
|
+
if self._value is not None:
|
814
|
+
return self._value
|
815
|
+
else:
|
816
|
+
state = self.root.session_state
|
817
|
+
assert state
|
818
|
+
return cast(list[T], state[self.id])
|
819
|
+
|
820
|
+
@property
|
821
|
+
def indices(self) -> Sequence[int]:
|
822
|
+
"""The indices of the currently selected values from the options. (list)"""
|
823
|
+
return [self.options.index(self.format_func(v)) for v in self.value]
|
824
|
+
|
825
|
+
@property
|
826
|
+
def format_func(self) -> Callable[[Any], Any]:
|
827
|
+
"""The widget's formatting function for displaying options. (callable)"""
|
828
|
+
ss = self.root.session_state
|
829
|
+
return cast(Callable[[Any], Any], ss[TESTING_KEY][self.id])
|
830
|
+
|
831
|
+
def set_value(self, v: list[T]) -> Multiselect[T]:
|
832
|
+
"""Set the value of the multiselect widget. (list)"""
|
833
|
+
|
834
|
+
self._value = v
|
835
|
+
return self
|
836
|
+
|
837
|
+
def select(self, v: T) -> Multiselect[T]:
|
838
|
+
"""
|
839
|
+
Add a selection to the widget. Do nothing if the value is already selected.\
|
840
|
+
If testing a multiselect widget with repeated options, use ``set_value``\
|
841
|
+
instead.
|
842
|
+
"""
|
843
|
+
current = self.value
|
844
|
+
if v in current:
|
845
|
+
return self
|
846
|
+
else:
|
847
|
+
new = current.copy()
|
848
|
+
new.append(v)
|
849
|
+
self.set_value(new)
|
850
|
+
return self
|
851
|
+
|
852
|
+
def unselect(self, v: T) -> Multiselect[T]:
|
853
|
+
"""
|
854
|
+
Remove a selection from the widget. Do nothing if the value is not\
|
855
|
+
already selected. If a value is selected multiple times, the first\
|
856
|
+
instance is removed.
|
857
|
+
"""
|
858
|
+
current = self.value
|
859
|
+
if v not in current:
|
860
|
+
return self
|
861
|
+
else:
|
862
|
+
new = current.copy()
|
863
|
+
while v in new:
|
864
|
+
new.remove(v)
|
865
|
+
self.set_value(new)
|
866
|
+
return self
|
867
|
+
|
868
|
+
|
869
|
+
Number = Union[int, float]
|
870
|
+
|
871
|
+
|
872
|
+
@dataclass(repr=False)
|
873
|
+
class NumberInput(Widget):
|
874
|
+
"""A representation of ``st.number_input``."""
|
875
|
+
|
876
|
+
_value: Number | None | InitialValue
|
877
|
+
proto: NumberInputProto = field(repr=False)
|
878
|
+
label: str
|
879
|
+
min: Number | None
|
880
|
+
max: Number | None
|
881
|
+
step: Number
|
882
|
+
help: str
|
883
|
+
form_id: str
|
884
|
+
|
885
|
+
def __init__(self, proto: NumberInputProto, root: ElementTree):
|
886
|
+
super().__init__(proto, root)
|
887
|
+
self._value = InitialValue()
|
888
|
+
self.type = "number_input"
|
889
|
+
self.min = proto.min if proto.has_min else None
|
890
|
+
self.max = proto.max if proto.has_max else None
|
891
|
+
|
892
|
+
def set_value(self, v: Number | None) -> NumberInput:
|
893
|
+
"""Set the value of the ``st.number_input`` widget."""
|
894
|
+
self._value = v
|
895
|
+
return self
|
896
|
+
|
897
|
+
@property
|
898
|
+
def _widget_state(self) -> WidgetState:
|
899
|
+
ws = WidgetState()
|
900
|
+
ws.id = self.id
|
901
|
+
if self.value is not None:
|
902
|
+
ws.double_value = self.value
|
903
|
+
return ws
|
904
|
+
|
905
|
+
@property
|
906
|
+
def value(self) -> Number | None:
|
907
|
+
"""Get the current value of the ``st.number_input`` widget."""
|
908
|
+
if not isinstance(self._value, InitialValue):
|
909
|
+
return self._value
|
910
|
+
else:
|
911
|
+
state = self.root.session_state
|
912
|
+
assert state
|
913
|
+
|
914
|
+
# Awkward to do this with `cast`
|
915
|
+
return state[self.id] # type: ignore
|
916
|
+
|
917
|
+
def increment(self) -> NumberInput:
|
918
|
+
"""Increment the ``st.number_input`` widget as if the user clicked "+"."""
|
919
|
+
if self.value is None:
|
920
|
+
return self
|
921
|
+
|
922
|
+
v = min(self.value + self.step, self.max or float("inf"))
|
923
|
+
return self.set_value(v)
|
924
|
+
|
925
|
+
def decrement(self) -> NumberInput:
|
926
|
+
"""Decrement the ``st.number_input`` widget as if the user clicked "-"."""
|
927
|
+
if self.value is None:
|
928
|
+
return self
|
929
|
+
|
930
|
+
v = max(self.value - self.step, self.min or float("-inf"))
|
931
|
+
return self.set_value(v)
|
932
|
+
|
933
|
+
|
934
|
+
@dataclass(repr=False)
|
935
|
+
class Radio(Widget, Generic[T]):
|
936
|
+
"""A representation of ``st.radio``."""
|
937
|
+
|
938
|
+
_value: T | None | InitialValue
|
939
|
+
|
940
|
+
proto: RadioProto = field(repr=False)
|
941
|
+
label: str
|
942
|
+
options: list[str]
|
943
|
+
horizontal: bool
|
944
|
+
help: str
|
945
|
+
form_id: str
|
946
|
+
|
947
|
+
def __init__(self, proto: RadioProto, root: ElementTree):
|
948
|
+
super().__init__(proto, root)
|
949
|
+
self._value = InitialValue()
|
950
|
+
self.type = "radio"
|
951
|
+
self.options = list(proto.options)
|
952
|
+
|
953
|
+
@property
|
954
|
+
def index(self) -> int | None:
|
955
|
+
"""The index of the current selection. (int)"""
|
956
|
+
if self.value is None:
|
957
|
+
return None
|
958
|
+
return self.options.index(self.format_func(self.value))
|
959
|
+
|
960
|
+
@property
|
961
|
+
def value(self) -> T | None:
|
962
|
+
"""The currently selected value from the options. (Any)"""
|
963
|
+
if not isinstance(self._value, InitialValue):
|
964
|
+
return self._value
|
965
|
+
else:
|
966
|
+
state = self.root.session_state
|
967
|
+
assert state
|
968
|
+
return cast(T, state[self.id])
|
969
|
+
|
970
|
+
@property
|
971
|
+
def format_func(self) -> Callable[[Any], Any]:
|
972
|
+
"""The widget's formatting function for displaying options. (callable)"""
|
973
|
+
ss = self.root.session_state
|
974
|
+
return cast(Callable[[Any], Any], ss[TESTING_KEY][self.id])
|
975
|
+
|
976
|
+
def set_value(self, v: T | None) -> Radio[T]:
|
977
|
+
"""Set the selection by value."""
|
978
|
+
self._value = v
|
979
|
+
return self
|
980
|
+
|
981
|
+
@property
|
982
|
+
def _widget_state(self) -> WidgetState:
|
983
|
+
"""Protobuf message representing the state of the widget, including
|
984
|
+
any interactions that have happened.
|
985
|
+
Should be the same as the frontend would produce for those interactions.
|
986
|
+
"""
|
987
|
+
ws = WidgetState()
|
988
|
+
ws.id = self.id
|
989
|
+
if self.index is not None:
|
990
|
+
ws.int_value = self.index
|
991
|
+
return ws
|
992
|
+
|
993
|
+
|
994
|
+
@dataclass(repr=False)
|
995
|
+
class Selectbox(Widget, Generic[T]):
|
996
|
+
"""A representation of ``st.selectbox``."""
|
997
|
+
|
998
|
+
_value: T | None | InitialValue
|
999
|
+
|
1000
|
+
proto: SelectboxProto = field(repr=False)
|
1001
|
+
label: str
|
1002
|
+
options: list[str]
|
1003
|
+
help: str
|
1004
|
+
form_id: str
|
1005
|
+
|
1006
|
+
def __init__(self, proto: SelectboxProto, root: ElementTree):
|
1007
|
+
super().__init__(proto, root)
|
1008
|
+
self._value = InitialValue()
|
1009
|
+
self.type = "selectbox"
|
1010
|
+
self.options = list(proto.options)
|
1011
|
+
|
1012
|
+
@property
|
1013
|
+
def index(self) -> int | None:
|
1014
|
+
"""The index of the current selection. (int)"""
|
1015
|
+
if self.value is None:
|
1016
|
+
return None
|
1017
|
+
|
1018
|
+
if len(self.options) == 0:
|
1019
|
+
return 0
|
1020
|
+
return self.options.index(self.format_func(self.value))
|
1021
|
+
|
1022
|
+
@property
|
1023
|
+
def value(self) -> T | None:
|
1024
|
+
"""The currently selected value from the options. (Any)"""
|
1025
|
+
if not isinstance(self._value, InitialValue):
|
1026
|
+
return self._value
|
1027
|
+
else:
|
1028
|
+
state = self.root.session_state
|
1029
|
+
assert state
|
1030
|
+
return cast(T, state[self.id])
|
1031
|
+
|
1032
|
+
@property
|
1033
|
+
def format_func(self) -> Callable[[Any], Any]:
|
1034
|
+
"""The widget's formatting function for displaying options. (callable)"""
|
1035
|
+
ss = self.root.session_state
|
1036
|
+
return cast(Callable[[Any], Any], ss[TESTING_KEY][self.id])
|
1037
|
+
|
1038
|
+
def set_value(self, v: T | None) -> Selectbox[T]:
|
1039
|
+
"""Set the selection by value."""
|
1040
|
+
self._value = v
|
1041
|
+
return self
|
1042
|
+
|
1043
|
+
def select(self, v: T | None) -> Selectbox[T]:
|
1044
|
+
"""Set the selection by value."""
|
1045
|
+
return self.set_value(v)
|
1046
|
+
|
1047
|
+
def select_index(self, index: int | None) -> Selectbox[T]:
|
1048
|
+
"""Set the selection by index."""
|
1049
|
+
if index is None:
|
1050
|
+
return self.set_value(None)
|
1051
|
+
return self.set_value(cast(T, self.options[index]))
|
1052
|
+
|
1053
|
+
@property
|
1054
|
+
def _widget_state(self) -> WidgetState:
|
1055
|
+
"""Protobuf message representing the state of the widget, including
|
1056
|
+
any interactions that have happened.
|
1057
|
+
Should be the same as the frontend would produce for those interactions.
|
1058
|
+
"""
|
1059
|
+
ws = WidgetState()
|
1060
|
+
ws.id = self.id
|
1061
|
+
if self.index is not None:
|
1062
|
+
ws.int_value = self.index
|
1063
|
+
return ws
|
1064
|
+
|
1065
|
+
|
1066
|
+
@dataclass(repr=False)
|
1067
|
+
class SelectSlider(Widget, Generic[T]):
|
1068
|
+
"""A representation of ``st.select_slider``."""
|
1069
|
+
|
1070
|
+
_value: T | Sequence[T] | None
|
1071
|
+
|
1072
|
+
proto: SliderProto = field(repr=False)
|
1073
|
+
label: str
|
1074
|
+
data_type: SliderProto.DataType.ValueType
|
1075
|
+
options: list[str]
|
1076
|
+
help: str
|
1077
|
+
form_id: str
|
1078
|
+
|
1079
|
+
def __init__(self, proto: SliderProto, root: ElementTree):
|
1080
|
+
super().__init__(proto, root)
|
1081
|
+
self.type = "select_slider"
|
1082
|
+
self.options = list(proto.options)
|
1083
|
+
|
1084
|
+
def set_value(self, v: T | Sequence[T]) -> SelectSlider[T]:
|
1085
|
+
"""Set the (single) selection by value."""
|
1086
|
+
self._value = v
|
1087
|
+
return self
|
1088
|
+
|
1089
|
+
@property
|
1090
|
+
def _widget_state(self) -> WidgetState:
|
1091
|
+
serde = SelectSliderSerde(self.options, [], False)
|
1092
|
+
try:
|
1093
|
+
v = serde.serialize(self.format_func(self.value))
|
1094
|
+
except (ValueError, TypeError):
|
1095
|
+
try:
|
1096
|
+
v = serde.serialize([self.format_func(val) for val in self.value]) # type: ignore
|
1097
|
+
except: # noqa: E722
|
1098
|
+
raise ValueError(f"Could not find index for {self.value}")
|
1099
|
+
|
1100
|
+
ws = WidgetState()
|
1101
|
+
ws.id = self.id
|
1102
|
+
ws.double_array_value.data[:] = v
|
1103
|
+
return ws
|
1104
|
+
|
1105
|
+
@property
|
1106
|
+
def value(self) -> T | Sequence[T]:
|
1107
|
+
"""The currently selected value or range. (Any or Sequence of Any)"""
|
1108
|
+
if self._value is not None:
|
1109
|
+
return self._value
|
1110
|
+
else:
|
1111
|
+
state = self.root.session_state
|
1112
|
+
assert state
|
1113
|
+
# Awkward to do this with `cast`
|
1114
|
+
return state[self.id] # type: ignore
|
1115
|
+
|
1116
|
+
@property
|
1117
|
+
def format_func(self) -> Callable[[Any], Any]:
|
1118
|
+
"""The widget's formatting function for displaying options. (callable)"""
|
1119
|
+
ss = self.root.session_state
|
1120
|
+
return cast(Callable[[Any], Any], ss[TESTING_KEY][self.id])
|
1121
|
+
|
1122
|
+
def set_range(self, lower: T, upper: T) -> SelectSlider[T]:
|
1123
|
+
"""Set the ranged selection by values."""
|
1124
|
+
return self.set_value([lower, upper])
|
1125
|
+
|
1126
|
+
|
1127
|
+
@dataclass(repr=False)
|
1128
|
+
class Slider(Widget, Generic[SliderValueT]):
|
1129
|
+
"""A representation of ``st.slider``."""
|
1130
|
+
|
1131
|
+
_value: SliderValueT | Sequence[SliderValueT] | None
|
1132
|
+
|
1133
|
+
proto: SliderProto = field(repr=False)
|
1134
|
+
label: str
|
1135
|
+
data_type: SliderProto.DataType.ValueType
|
1136
|
+
min: SliderValueT
|
1137
|
+
max: SliderValueT
|
1138
|
+
step: SliderStep
|
1139
|
+
help: str
|
1140
|
+
form_id: str
|
1141
|
+
|
1142
|
+
def __init__(self, proto: SliderProto, root: ElementTree):
|
1143
|
+
super().__init__(proto, root)
|
1144
|
+
self.type = "slider"
|
1145
|
+
|
1146
|
+
def set_value(
|
1147
|
+
self, v: SliderValueT | Sequence[SliderValueT]
|
1148
|
+
) -> Slider[SliderValueT]:
|
1149
|
+
"""Set the (single) value of the slider."""
|
1150
|
+
self._value = v
|
1151
|
+
return self
|
1152
|
+
|
1153
|
+
@property
|
1154
|
+
def _widget_state(self) -> WidgetState:
|
1155
|
+
data_type = self.proto.data_type
|
1156
|
+
serde = SliderSerde([], data_type, True, None)
|
1157
|
+
v = serde.serialize(self.value)
|
1158
|
+
|
1159
|
+
ws = WidgetState()
|
1160
|
+
ws.id = self.id
|
1161
|
+
ws.double_array_value.data[:] = v
|
1162
|
+
return ws
|
1163
|
+
|
1164
|
+
@property
|
1165
|
+
def value(self) -> SliderValueT | Sequence[SliderValueT]:
|
1166
|
+
"""The currently selected value or range. (Any or Sequence of Any)"""
|
1167
|
+
if self._value is not None:
|
1168
|
+
return self._value
|
1169
|
+
else:
|
1170
|
+
state = self.root.session_state
|
1171
|
+
assert state
|
1172
|
+
# Awkward to do this with `cast`
|
1173
|
+
return state[self.id] # type: ignore
|
1174
|
+
|
1175
|
+
def set_range(
|
1176
|
+
self, lower: SliderValueT, upper: SliderValueT
|
1177
|
+
) -> Slider[SliderValueT]:
|
1178
|
+
"""Set the ranged value of the slider."""
|
1179
|
+
return self.set_value([lower, upper])
|
1180
|
+
|
1181
|
+
|
1182
|
+
@dataclass(repr=False)
|
1183
|
+
class Table(Element):
|
1184
|
+
proto: ArrowProto = field(repr=False)
|
1185
|
+
|
1186
|
+
def __init__(self, proto: ArrowProto, root: ElementTree):
|
1187
|
+
self.key = None
|
1188
|
+
self.proto = proto
|
1189
|
+
self.root = root
|
1190
|
+
self.type = "arrow_table"
|
1191
|
+
|
1192
|
+
@property
|
1193
|
+
def value(self) -> PandasDataframe:
|
1194
|
+
return dataframe_util.convert_arrow_bytes_to_pandas_df(self.proto.data)
|
1195
|
+
|
1196
|
+
|
1197
|
+
@dataclass(repr=False)
|
1198
|
+
class Text(Element):
|
1199
|
+
proto: TextProto = field(repr=False)
|
1200
|
+
|
1201
|
+
key: None = None
|
1202
|
+
|
1203
|
+
def __init__(self, proto: TextProto, root: ElementTree):
|
1204
|
+
self.proto = proto
|
1205
|
+
self.root = root
|
1206
|
+
self.type = "text"
|
1207
|
+
|
1208
|
+
@property
|
1209
|
+
def value(self) -> str:
|
1210
|
+
"""The value of the element. (str)"""
|
1211
|
+
return self.proto.body
|
1212
|
+
|
1213
|
+
|
1214
|
+
@dataclass(repr=False)
|
1215
|
+
class TextArea(Widget):
|
1216
|
+
"""A representation of ``st.text_area``."""
|
1217
|
+
|
1218
|
+
_value: str | None | InitialValue
|
1219
|
+
|
1220
|
+
proto: TextAreaProto = field(repr=False)
|
1221
|
+
label: str
|
1222
|
+
max_chars: int
|
1223
|
+
placeholder: str
|
1224
|
+
help: str
|
1225
|
+
form_id: str
|
1226
|
+
|
1227
|
+
def __init__(self, proto: TextAreaProto, root: ElementTree):
|
1228
|
+
super().__init__(proto, root)
|
1229
|
+
self._value = InitialValue()
|
1230
|
+
self.type = "text_area"
|
1231
|
+
|
1232
|
+
def set_value(self, v: str | None) -> TextArea:
|
1233
|
+
"""Set the value of the widget."""
|
1234
|
+
self._value = v
|
1235
|
+
return self
|
1236
|
+
|
1237
|
+
@property
|
1238
|
+
def _widget_state(self) -> WidgetState:
|
1239
|
+
ws = WidgetState()
|
1240
|
+
ws.id = self.id
|
1241
|
+
if self.value is not None:
|
1242
|
+
ws.string_value = self.value
|
1243
|
+
return ws
|
1244
|
+
|
1245
|
+
@property
|
1246
|
+
def value(self) -> str | None:
|
1247
|
+
"""The current value of the widget. (str)"""
|
1248
|
+
if not isinstance(self._value, InitialValue):
|
1249
|
+
return self._value
|
1250
|
+
else:
|
1251
|
+
state = self.root.session_state
|
1252
|
+
assert state
|
1253
|
+
# Awkward to do this with `cast`
|
1254
|
+
return state[self.id] # type: ignore
|
1255
|
+
|
1256
|
+
def input(self, v: str) -> TextArea:
|
1257
|
+
"""
|
1258
|
+
Set the value of the widget only if the value does not exceed the\
|
1259
|
+
maximum allowed characters.
|
1260
|
+
"""
|
1261
|
+
# TODO: should input be setting or appending?
|
1262
|
+
if self.max_chars and len(v) > self.max_chars:
|
1263
|
+
return self
|
1264
|
+
return self.set_value(v)
|
1265
|
+
|
1266
|
+
|
1267
|
+
@dataclass(repr=False)
|
1268
|
+
class TextInput(Widget):
|
1269
|
+
"""A representation of ``st.text_input``."""
|
1270
|
+
|
1271
|
+
_value: str | None | InitialValue
|
1272
|
+
proto: TextInputProto = field(repr=False)
|
1273
|
+
label: str
|
1274
|
+
max_chars: int
|
1275
|
+
autocomplete: str
|
1276
|
+
placeholder: str
|
1277
|
+
help: str
|
1278
|
+
form_id: str
|
1279
|
+
|
1280
|
+
def __init__(self, proto: TextInputProto, root: ElementTree):
|
1281
|
+
super().__init__(proto, root)
|
1282
|
+
self._value = InitialValue()
|
1283
|
+
self.type = "text_input"
|
1284
|
+
|
1285
|
+
def set_value(self, v: str | None) -> TextInput:
|
1286
|
+
"""Set the value of the widget."""
|
1287
|
+
self._value = v
|
1288
|
+
return self
|
1289
|
+
|
1290
|
+
@property
|
1291
|
+
def _widget_state(self) -> WidgetState:
|
1292
|
+
ws = WidgetState()
|
1293
|
+
ws.id = self.id
|
1294
|
+
if self.value is not None:
|
1295
|
+
ws.string_value = self.value
|
1296
|
+
return ws
|
1297
|
+
|
1298
|
+
@property
|
1299
|
+
def value(self) -> str | None:
|
1300
|
+
"""The current value of the widget. (str)"""
|
1301
|
+
if not isinstance(self._value, InitialValue):
|
1302
|
+
return self._value
|
1303
|
+
else:
|
1304
|
+
state = self.root.session_state
|
1305
|
+
assert state
|
1306
|
+
# Awkward to do this with `cast`
|
1307
|
+
return state[self.id] # type: ignore
|
1308
|
+
|
1309
|
+
def input(self, v: str) -> TextInput:
|
1310
|
+
"""
|
1311
|
+
Set the value of the widget only if the value does not exceed the\
|
1312
|
+
maximum allowed characters.
|
1313
|
+
"""
|
1314
|
+
# TODO: should input be setting or appending?
|
1315
|
+
if self.max_chars and len(v) > self.max_chars:
|
1316
|
+
return self
|
1317
|
+
return self.set_value(v)
|
1318
|
+
|
1319
|
+
|
1320
|
+
TimeValue: TypeAlias = Union[time, datetime]
|
1321
|
+
|
1322
|
+
|
1323
|
+
@dataclass(repr=False)
|
1324
|
+
class TimeInput(Widget):
|
1325
|
+
"""A representation of ``st.time_input``."""
|
1326
|
+
|
1327
|
+
_value: TimeValue | None | InitialValue
|
1328
|
+
proto: TimeInputProto = field(repr=False)
|
1329
|
+
label: str
|
1330
|
+
step: int
|
1331
|
+
help: str
|
1332
|
+
form_id: str
|
1333
|
+
|
1334
|
+
def __init__(self, proto: TimeInputProto, root: ElementTree):
|
1335
|
+
super().__init__(proto, root)
|
1336
|
+
self._value = InitialValue()
|
1337
|
+
self.type = "time_input"
|
1338
|
+
|
1339
|
+
def set_value(self, v: TimeValue | None) -> TimeInput:
|
1340
|
+
"""Set the value of the widget."""
|
1341
|
+
self._value = v
|
1342
|
+
return self
|
1343
|
+
|
1344
|
+
@property
|
1345
|
+
def _widget_state(self) -> WidgetState:
|
1346
|
+
ws = WidgetState()
|
1347
|
+
ws.id = self.id
|
1348
|
+
|
1349
|
+
serde = TimeInputSerde(None)
|
1350
|
+
serialized_value = serde.serialize(self.value)
|
1351
|
+
if serialized_value is not None:
|
1352
|
+
ws.string_value = serialized_value
|
1353
|
+
return ws
|
1354
|
+
|
1355
|
+
@property
|
1356
|
+
def value(self) -> time | None:
|
1357
|
+
"""The current value of the widget. (time)"""
|
1358
|
+
if not isinstance(self._value, InitialValue):
|
1359
|
+
v = self._value
|
1360
|
+
v = v.time() if isinstance(v, datetime) else v
|
1361
|
+
return v
|
1362
|
+
else:
|
1363
|
+
state = self.root.session_state
|
1364
|
+
assert state
|
1365
|
+
return state[self.id] # type: ignore
|
1366
|
+
|
1367
|
+
def increment(self) -> TimeInput:
|
1368
|
+
"""Select the next available time."""
|
1369
|
+
if self.value is None:
|
1370
|
+
return self
|
1371
|
+
dt = datetime.combine(date.today(), self.value) + timedelta(seconds=self.step)
|
1372
|
+
return self.set_value(dt.time())
|
1373
|
+
|
1374
|
+
def decrement(self) -> TimeInput:
|
1375
|
+
"""Select the previous available time."""
|
1376
|
+
if self.value is None:
|
1377
|
+
return self
|
1378
|
+
dt = datetime.combine(date.today(), self.value) - timedelta(seconds=self.step)
|
1379
|
+
return self.set_value(dt.time())
|
1380
|
+
|
1381
|
+
|
1382
|
+
@dataclass(repr=False)
|
1383
|
+
class Toast(Element):
|
1384
|
+
proto: ToastProto = field(repr=False)
|
1385
|
+
icon: str
|
1386
|
+
|
1387
|
+
def __init__(self, proto: ToastProto, root: ElementTree):
|
1388
|
+
self.proto = proto
|
1389
|
+
self.key = None
|
1390
|
+
self.root = root
|
1391
|
+
self.type = "toast"
|
1392
|
+
|
1393
|
+
@property
|
1394
|
+
def value(self) -> str:
|
1395
|
+
return self.proto.body
|
1396
|
+
|
1397
|
+
|
1398
|
+
@dataclass(repr=False)
|
1399
|
+
class Toggle(Widget):
|
1400
|
+
"""A representation of ``st.toggle``."""
|
1401
|
+
|
1402
|
+
_value: bool | None
|
1403
|
+
|
1404
|
+
proto: CheckboxProto = field(repr=False)
|
1405
|
+
label: str
|
1406
|
+
help: str
|
1407
|
+
form_id: str
|
1408
|
+
|
1409
|
+
def __init__(self, proto: CheckboxProto, root: ElementTree):
|
1410
|
+
super().__init__(proto, root)
|
1411
|
+
self._value = None
|
1412
|
+
self.type = "toggle"
|
1413
|
+
|
1414
|
+
@property
|
1415
|
+
def _widget_state(self) -> WidgetState:
|
1416
|
+
ws = WidgetState()
|
1417
|
+
ws.id = self.id
|
1418
|
+
ws.bool_value = self.value
|
1419
|
+
return ws
|
1420
|
+
|
1421
|
+
@property
|
1422
|
+
def value(self) -> bool:
|
1423
|
+
"""The current value of the widget. (bool)"""
|
1424
|
+
if self._value is not None:
|
1425
|
+
return self._value
|
1426
|
+
else:
|
1427
|
+
state = self.root.session_state
|
1428
|
+
assert state
|
1429
|
+
return cast(bool, state[self.id])
|
1430
|
+
|
1431
|
+
def set_value(self, v: bool) -> Toggle:
|
1432
|
+
"""Set the value of the widget."""
|
1433
|
+
self._value = v
|
1434
|
+
return self
|
1435
|
+
|
1436
|
+
|
1437
|
+
@dataclass(repr=False)
|
1438
|
+
class Block:
|
1439
|
+
"""A container of other elements.
|
1440
|
+
|
1441
|
+
Elements within a Block can be inspected and interacted with. This follows
|
1442
|
+
the same syntax as inspecting and interacting within an ``AppTest`` object.
|
1443
|
+
|
1444
|
+
For all container classes, parameters of the original element can be
|
1445
|
+
obtained as properties. For example, ``ChatMessage.avatar`` and
|
1446
|
+
``Tab.label``.
|
1447
|
+
"""
|
1448
|
+
|
1449
|
+
type: str
|
1450
|
+
children: dict[int, Node]
|
1451
|
+
proto: Any = field(repr=False)
|
1452
|
+
root: ElementTree = field(repr=False)
|
1453
|
+
|
1454
|
+
def __init__(
|
1455
|
+
self,
|
1456
|
+
proto: BlockProto | None,
|
1457
|
+
root: ElementTree,
|
1458
|
+
):
|
1459
|
+
self.children = {}
|
1460
|
+
self.proto = proto
|
1461
|
+
if proto:
|
1462
|
+
ty = proto.WhichOneof("type")
|
1463
|
+
if ty is not None:
|
1464
|
+
self.type = ty
|
1465
|
+
else:
|
1466
|
+
# `st.container` has no sub-message
|
1467
|
+
self.type = "container"
|
1468
|
+
else:
|
1469
|
+
self.type = "unknown"
|
1470
|
+
self.root = root
|
1471
|
+
|
1472
|
+
def __len__(self) -> int:
|
1473
|
+
return len(self.children)
|
1474
|
+
|
1475
|
+
def __iter__(self):
|
1476
|
+
yield self
|
1477
|
+
for child_idx in self.children:
|
1478
|
+
yield from self.children[child_idx]
|
1479
|
+
|
1480
|
+
def __getitem__(self, k: int) -> Node:
|
1481
|
+
return self.children[k]
|
1482
|
+
|
1483
|
+
@property
|
1484
|
+
def key(self) -> str | None:
|
1485
|
+
return None
|
1486
|
+
|
1487
|
+
# We could implement these using __getattr__ but that would have
|
1488
|
+
# much worse type information.
|
1489
|
+
@property
|
1490
|
+
def button(self) -> WidgetList[Button]:
|
1491
|
+
return WidgetList(self.get("button")) # type: ignore
|
1492
|
+
|
1493
|
+
@property
|
1494
|
+
def button_group(self) -> WidgetList[ButtonGroup[Any]]:
|
1495
|
+
return WidgetList(self.get("button_group")) # type: ignore
|
1496
|
+
|
1497
|
+
@property
|
1498
|
+
def caption(self) -> ElementList[Caption]:
|
1499
|
+
return ElementList(self.get("caption")) # type: ignore
|
1500
|
+
|
1501
|
+
@property
|
1502
|
+
def chat_input(self) -> WidgetList[ChatInput]:
|
1503
|
+
return WidgetList(self.get("chat_input")) # type: ignore
|
1504
|
+
|
1505
|
+
@property
|
1506
|
+
def chat_message(self) -> Sequence[ChatMessage]:
|
1507
|
+
return self.get("chat_message") # type: ignore
|
1508
|
+
|
1509
|
+
@property
|
1510
|
+
def checkbox(self) -> WidgetList[Checkbox]:
|
1511
|
+
return WidgetList(self.get("checkbox")) # type: ignore
|
1512
|
+
|
1513
|
+
@property
|
1514
|
+
def code(self) -> ElementList[Code]:
|
1515
|
+
return ElementList(self.get("code")) # type: ignore
|
1516
|
+
|
1517
|
+
@property
|
1518
|
+
def color_picker(self) -> WidgetList[ColorPicker]:
|
1519
|
+
return WidgetList(self.get("color_picker")) # type: ignore
|
1520
|
+
|
1521
|
+
@property
|
1522
|
+
def columns(self) -> Sequence[Column]:
|
1523
|
+
return self.get("column") # type: ignore
|
1524
|
+
|
1525
|
+
@property
|
1526
|
+
def dataframe(self) -> ElementList[Dataframe]:
|
1527
|
+
return ElementList(self.get("arrow_data_frame")) # type: ignore
|
1528
|
+
|
1529
|
+
@property
|
1530
|
+
def date_input(self) -> WidgetList[DateInput]:
|
1531
|
+
return WidgetList(self.get("date_input")) # type: ignore
|
1532
|
+
|
1533
|
+
@property
|
1534
|
+
def divider(self) -> ElementList[Divider]:
|
1535
|
+
return ElementList(self.get("divider")) # type: ignore
|
1536
|
+
|
1537
|
+
@property
|
1538
|
+
def error(self) -> ElementList[Error]:
|
1539
|
+
return ElementList(self.get("error")) # type: ignore
|
1540
|
+
|
1541
|
+
@property
|
1542
|
+
def exception(self) -> ElementList[Exception]:
|
1543
|
+
return ElementList(self.get("exception")) # type: ignore
|
1544
|
+
|
1545
|
+
@property
|
1546
|
+
def expander(self) -> Sequence[Expander]:
|
1547
|
+
return self.get("expander") # type: ignore
|
1548
|
+
|
1549
|
+
@property
|
1550
|
+
def header(self) -> ElementList[Header]:
|
1551
|
+
return ElementList(self.get("header")) # type: ignore
|
1552
|
+
|
1553
|
+
@property
|
1554
|
+
def info(self) -> ElementList[Info]:
|
1555
|
+
return ElementList(self.get("info")) # type: ignore
|
1556
|
+
|
1557
|
+
@property
|
1558
|
+
def json(self) -> ElementList[Json]:
|
1559
|
+
return ElementList(self.get("json")) # type: ignore
|
1560
|
+
|
1561
|
+
@property
|
1562
|
+
def latex(self) -> ElementList[Latex]:
|
1563
|
+
return ElementList(self.get("latex")) # type: ignore
|
1564
|
+
|
1565
|
+
@property
|
1566
|
+
def markdown(self) -> ElementList[Markdown]:
|
1567
|
+
return ElementList(self.get("markdown")) # type: ignore
|
1568
|
+
|
1569
|
+
@property
|
1570
|
+
def metric(self) -> ElementList[Metric]:
|
1571
|
+
return ElementList(self.get("metric")) # type: ignore
|
1572
|
+
|
1573
|
+
@property
|
1574
|
+
def multiselect(self) -> WidgetList[Multiselect[Any]]:
|
1575
|
+
return WidgetList(self.get("multiselect")) # type: ignore
|
1576
|
+
|
1577
|
+
@property
|
1578
|
+
def number_input(self) -> WidgetList[NumberInput]:
|
1579
|
+
return WidgetList(self.get("number_input")) # type: ignore
|
1580
|
+
|
1581
|
+
@property
|
1582
|
+
def radio(self) -> WidgetList[Radio[Any]]:
|
1583
|
+
return WidgetList(self.get("radio")) # type: ignore
|
1584
|
+
|
1585
|
+
@property
|
1586
|
+
def select_slider(self) -> WidgetList[SelectSlider[Any]]:
|
1587
|
+
return WidgetList(self.get("select_slider")) # type: ignore
|
1588
|
+
|
1589
|
+
@property
|
1590
|
+
def selectbox(self) -> WidgetList[Selectbox[Any]]:
|
1591
|
+
return WidgetList(self.get("selectbox")) # type: ignore
|
1592
|
+
|
1593
|
+
@property
|
1594
|
+
def slider(self) -> WidgetList[Slider[Any]]:
|
1595
|
+
return WidgetList(self.get("slider")) # type: ignore
|
1596
|
+
|
1597
|
+
@property
|
1598
|
+
def status(self) -> Sequence[Status]:
|
1599
|
+
return self.get("status") # type: ignore
|
1600
|
+
|
1601
|
+
@property
|
1602
|
+
def subheader(self) -> ElementList[Subheader]:
|
1603
|
+
return ElementList(self.get("subheader")) # type: ignore
|
1604
|
+
|
1605
|
+
@property
|
1606
|
+
def success(self) -> ElementList[Success]:
|
1607
|
+
return ElementList(self.get("success")) # type: ignore
|
1608
|
+
|
1609
|
+
@property
|
1610
|
+
def table(self) -> ElementList[Table]:
|
1611
|
+
return ElementList(self.get("arrow_table")) # type: ignore
|
1612
|
+
|
1613
|
+
@property
|
1614
|
+
def tabs(self) -> Sequence[Tab]:
|
1615
|
+
return self.get("tab") # type: ignore
|
1616
|
+
|
1617
|
+
@property
|
1618
|
+
def text(self) -> ElementList[Text]:
|
1619
|
+
return ElementList(self.get("text")) # type: ignore
|
1620
|
+
|
1621
|
+
@property
|
1622
|
+
def text_area(self) -> WidgetList[TextArea]:
|
1623
|
+
return WidgetList(self.get("text_area")) # type: ignore
|
1624
|
+
|
1625
|
+
@property
|
1626
|
+
def text_input(self) -> WidgetList[TextInput]:
|
1627
|
+
return WidgetList(self.get("text_input")) # type: ignore
|
1628
|
+
|
1629
|
+
@property
|
1630
|
+
def time_input(self) -> WidgetList[TimeInput]:
|
1631
|
+
return WidgetList(self.get("time_input")) # type: ignore
|
1632
|
+
|
1633
|
+
@property
|
1634
|
+
def title(self) -> ElementList[Title]:
|
1635
|
+
return ElementList(self.get("title")) # type: ignore
|
1636
|
+
|
1637
|
+
@property
|
1638
|
+
def toast(self) -> ElementList[Toast]:
|
1639
|
+
return ElementList(self.get("toast")) # type: ignore
|
1640
|
+
|
1641
|
+
@property
|
1642
|
+
def toggle(self) -> WidgetList[Toggle]:
|
1643
|
+
return WidgetList(self.get("toggle")) # type: ignore
|
1644
|
+
|
1645
|
+
@property
|
1646
|
+
def warning(self) -> ElementList[Warning]:
|
1647
|
+
return ElementList(self.get("warning")) # type: ignore
|
1648
|
+
|
1649
|
+
def get(self, element_type: str) -> Sequence[Node]:
|
1650
|
+
return [e for e in self if e.type == element_type]
|
1651
|
+
|
1652
|
+
def run(self, *, timeout: float | None = None) -> AppTest:
|
1653
|
+
"""Run the script with updated widget values.
|
1654
|
+
|
1655
|
+
Parameters
|
1656
|
+
----------
|
1657
|
+
timeout
|
1658
|
+
The maximum number of seconds to run the script. None means
|
1659
|
+
use the AppTest's default.
|
1660
|
+
"""
|
1661
|
+
return self.root.run(timeout=timeout)
|
1662
|
+
|
1663
|
+
def __repr__(self):
|
1664
|
+
return repr_(self)
|
1665
|
+
|
1666
|
+
|
1667
|
+
def repr_(self) -> str:
|
1668
|
+
"""A custom repr similar to `streamlit.util.repr_` but that shows tree
|
1669
|
+
structure using indentation.
|
1670
|
+
"""
|
1671
|
+
classname = self.__class__.__name__
|
1672
|
+
|
1673
|
+
defaults: list[Any] = [None, "", False, [], set(), {}]
|
1674
|
+
|
1675
|
+
if is_dataclass(self):
|
1676
|
+
fields_vals = (
|
1677
|
+
(f.name, getattr(self, f.name))
|
1678
|
+
for f in fields(self)
|
1679
|
+
if f.repr
|
1680
|
+
and getattr(self, f.name) != f.default
|
1681
|
+
and getattr(self, f.name) not in defaults
|
1682
|
+
)
|
1683
|
+
else:
|
1684
|
+
fields_vals = ((f, v) for (f, v) in self.__dict__.items() if v not in defaults)
|
1685
|
+
|
1686
|
+
reprs = []
|
1687
|
+
for field_name, value in fields_vals:
|
1688
|
+
if isinstance(value, dict):
|
1689
|
+
line = f"{field_name}={format_dict(value)}"
|
1690
|
+
else:
|
1691
|
+
line = f"{field_name}={value!r}"
|
1692
|
+
reprs.append(line)
|
1693
|
+
|
1694
|
+
reprs[0] = "\n" + reprs[0]
|
1695
|
+
field_reprs = ",\n".join(reprs)
|
1696
|
+
|
1697
|
+
field_reprs = textwrap.indent(field_reprs, " " * 4)
|
1698
|
+
return f"{classname}({field_reprs}\n)"
|
1699
|
+
|
1700
|
+
|
1701
|
+
def format_dict(d: dict[Any, Any]):
|
1702
|
+
lines = []
|
1703
|
+
for k, v in d.items():
|
1704
|
+
line = f"{k}: {v!r}"
|
1705
|
+
lines.append(line)
|
1706
|
+
r = ",\n".join(lines)
|
1707
|
+
r = textwrap.indent(r, " " * 4)
|
1708
|
+
r = f"{{\n{r}\n}}"
|
1709
|
+
return r
|
1710
|
+
|
1711
|
+
|
1712
|
+
@dataclass(repr=False)
|
1713
|
+
class SpecialBlock(Block):
|
1714
|
+
"""Base class for the sidebar and main body containers."""
|
1715
|
+
|
1716
|
+
def __init__(
|
1717
|
+
self,
|
1718
|
+
proto: BlockProto | None,
|
1719
|
+
root: ElementTree,
|
1720
|
+
type: str | None = None,
|
1721
|
+
):
|
1722
|
+
self.children = {}
|
1723
|
+
self.proto = proto
|
1724
|
+
if type:
|
1725
|
+
self.type = type
|
1726
|
+
elif proto and proto.WhichOneof("type"):
|
1727
|
+
ty = proto.WhichOneof("type")
|
1728
|
+
assert ty is not None
|
1729
|
+
self.type = ty
|
1730
|
+
else:
|
1731
|
+
self.type = "unknown"
|
1732
|
+
self.root = root
|
1733
|
+
|
1734
|
+
|
1735
|
+
@dataclass(repr=False)
|
1736
|
+
class ChatMessage(Block):
|
1737
|
+
"""A representation of ``st.chat_message``."""
|
1738
|
+
|
1739
|
+
type: str = field(repr=False)
|
1740
|
+
proto: BlockProto.ChatMessage = field(repr=False)
|
1741
|
+
name: str
|
1742
|
+
avatar: str
|
1743
|
+
|
1744
|
+
def __init__(
|
1745
|
+
self,
|
1746
|
+
proto: BlockProto.ChatMessage,
|
1747
|
+
root: ElementTree,
|
1748
|
+
):
|
1749
|
+
self.children = {}
|
1750
|
+
self.proto = proto
|
1751
|
+
self.root = root
|
1752
|
+
self.type = "chat_message"
|
1753
|
+
self.name = proto.name
|
1754
|
+
self.avatar = proto.avatar
|
1755
|
+
|
1756
|
+
|
1757
|
+
@dataclass(repr=False)
|
1758
|
+
class Column(Block):
|
1759
|
+
"""A representation of a column within ``st.columns``."""
|
1760
|
+
|
1761
|
+
type: str = field(repr=False)
|
1762
|
+
proto: BlockProto.Column = field(repr=False)
|
1763
|
+
weight: float
|
1764
|
+
gap: str
|
1765
|
+
|
1766
|
+
def __init__(
|
1767
|
+
self,
|
1768
|
+
proto: BlockProto.Column,
|
1769
|
+
root: ElementTree,
|
1770
|
+
):
|
1771
|
+
self.children = {}
|
1772
|
+
self.proto = proto
|
1773
|
+
self.root = root
|
1774
|
+
self.type = "column"
|
1775
|
+
self.weight = proto.weight
|
1776
|
+
self.gap = proto.gap
|
1777
|
+
|
1778
|
+
|
1779
|
+
@dataclass(repr=False)
|
1780
|
+
class Expander(Block):
|
1781
|
+
type: str = field(repr=False)
|
1782
|
+
proto: BlockProto.Expandable = field(repr=False)
|
1783
|
+
icon: str
|
1784
|
+
label: str
|
1785
|
+
|
1786
|
+
def __init__(self, proto: BlockProto.Expandable, root: ElementTree):
|
1787
|
+
self.children = {}
|
1788
|
+
self.proto = proto
|
1789
|
+
self.root = root
|
1790
|
+
# The internal name is "expandable" but the public API uses "expander"
|
1791
|
+
# so the naming of the class and type follows the public name.
|
1792
|
+
self.type = "expander"
|
1793
|
+
self.icon = proto.icon
|
1794
|
+
self.label = proto.label
|
1795
|
+
|
1796
|
+
|
1797
|
+
@dataclass(repr=False)
|
1798
|
+
class Status(Block):
|
1799
|
+
type: str = field(repr=False)
|
1800
|
+
proto: BlockProto.Expandable = field(repr=False)
|
1801
|
+
icon: str
|
1802
|
+
label: str
|
1803
|
+
|
1804
|
+
def __init__(self, proto: BlockProto.Expandable, root: ElementTree):
|
1805
|
+
self.children = {}
|
1806
|
+
self.proto = proto
|
1807
|
+
self.root = root
|
1808
|
+
self.type = "status"
|
1809
|
+
self.icon = proto.icon
|
1810
|
+
self.label = proto.label
|
1811
|
+
|
1812
|
+
@property
|
1813
|
+
def state(self):
|
1814
|
+
if self.icon == "spinner":
|
1815
|
+
return "running"
|
1816
|
+
elif self.icon == ":material/check:":
|
1817
|
+
return "complete"
|
1818
|
+
elif self.icon == ":material/error:":
|
1819
|
+
return "error"
|
1820
|
+
else:
|
1821
|
+
raise ValueError("Unknown Status state")
|
1822
|
+
|
1823
|
+
|
1824
|
+
@dataclass(repr=False)
|
1825
|
+
class Tab(Block):
|
1826
|
+
"""A representation of tab within ``st.tabs``."""
|
1827
|
+
|
1828
|
+
type: str = field(repr=False)
|
1829
|
+
proto: BlockProto.Tab = field(repr=False)
|
1830
|
+
label: str
|
1831
|
+
|
1832
|
+
def __init__(
|
1833
|
+
self,
|
1834
|
+
proto: BlockProto.Tab,
|
1835
|
+
root: ElementTree,
|
1836
|
+
):
|
1837
|
+
self.children = {}
|
1838
|
+
self.proto = proto
|
1839
|
+
self.root = root
|
1840
|
+
self.type = "tab"
|
1841
|
+
self.label = proto.label
|
1842
|
+
|
1843
|
+
|
1844
|
+
Node: TypeAlias = Union[Element, Block]
|
1845
|
+
|
1846
|
+
|
1847
|
+
def get_widget_state(node: Node) -> WidgetState | None:
|
1848
|
+
if isinstance(node, Widget):
|
1849
|
+
return node._widget_state
|
1850
|
+
else:
|
1851
|
+
return None
|
1852
|
+
|
1853
|
+
|
1854
|
+
@dataclass(repr=False)
|
1855
|
+
class ElementTree(Block):
|
1856
|
+
"""A tree of the elements produced by running a streamlit script.
|
1857
|
+
|
1858
|
+
Elements can be queried in three ways:
|
1859
|
+
- By element type, using `.foo` properties to get a list of all of that element,
|
1860
|
+
in the order they appear in the app
|
1861
|
+
- By user key, for widgets, by calling the above list with a key: `.foo(key='bar')`
|
1862
|
+
- Positionally, using list indexing syntax (`[...]`) to access a child of a
|
1863
|
+
block element. Not recommended because the exact tree structure can be surprising.
|
1864
|
+
|
1865
|
+
Element queries made on a block container will return only the elements
|
1866
|
+
descending from that block.
|
1867
|
+
|
1868
|
+
Returned elements have methods for accessing whatever attributes are relevant.
|
1869
|
+
For very simple elements this may be only its value, while complex elements
|
1870
|
+
like widgets have many.
|
1871
|
+
|
1872
|
+
Widgets provide a fluent API for faking frontend interaction and rerunning
|
1873
|
+
the script with the new widget values. All widgets provide a low level `set_value`
|
1874
|
+
method, along with higher level methods specific to that type of widget.
|
1875
|
+
After an interaction, calling `.run()` will update the AppTest with the
|
1876
|
+
results of that script run.
|
1877
|
+
"""
|
1878
|
+
|
1879
|
+
_runner: AppTest | None = field(repr=False, default=None)
|
1880
|
+
|
1881
|
+
def __init__(self):
|
1882
|
+
self.children = {}
|
1883
|
+
self.root = self
|
1884
|
+
self.type = "root"
|
1885
|
+
|
1886
|
+
@property
|
1887
|
+
def main(self) -> Block:
|
1888
|
+
m = self[0]
|
1889
|
+
assert isinstance(m, Block)
|
1890
|
+
return m
|
1891
|
+
|
1892
|
+
@property
|
1893
|
+
def sidebar(self) -> Block:
|
1894
|
+
s = self[1]
|
1895
|
+
assert isinstance(s, Block)
|
1896
|
+
return s
|
1897
|
+
|
1898
|
+
@property
|
1899
|
+
def session_state(self) -> SafeSessionState:
|
1900
|
+
assert self._runner is not None
|
1901
|
+
return self._runner.session_state
|
1902
|
+
|
1903
|
+
def get_widget_states(self) -> WidgetStates:
|
1904
|
+
ws = WidgetStates()
|
1905
|
+
for node in self:
|
1906
|
+
w = get_widget_state(node)
|
1907
|
+
if w is not None:
|
1908
|
+
ws.widgets.append(w)
|
1909
|
+
|
1910
|
+
return ws
|
1911
|
+
|
1912
|
+
def run(self, *, timeout: float | None = None) -> AppTest:
|
1913
|
+
"""Run the script with updated widget values.
|
1914
|
+
|
1915
|
+
Parameters
|
1916
|
+
----------
|
1917
|
+
timeout
|
1918
|
+
The maximum number of seconds to run the script. None means
|
1919
|
+
use the AppTest's default.
|
1920
|
+
"""
|
1921
|
+
assert self._runner is not None
|
1922
|
+
|
1923
|
+
widget_states = self.get_widget_states()
|
1924
|
+
return self._runner._run(widget_states, timeout=timeout)
|
1925
|
+
|
1926
|
+
def __repr__(self):
|
1927
|
+
return format_dict(self.children)
|
1928
|
+
|
1929
|
+
|
1930
|
+
def parse_tree_from_messages(messages: list[ForwardMsg]) -> ElementTree:
|
1931
|
+
"""Transform a list of `ForwardMsg` into a tree matching the implicit
|
1932
|
+
tree structure of blocks and elements in a streamlit app.
|
1933
|
+
|
1934
|
+
Returns the root of the tree, which acts as the entrypoint for the query
|
1935
|
+
and interaction API.
|
1936
|
+
"""
|
1937
|
+
root = ElementTree()
|
1938
|
+
root.children = {
|
1939
|
+
0: SpecialBlock(type="main", root=root, proto=None),
|
1940
|
+
1: SpecialBlock(type="sidebar", root=root, proto=None),
|
1941
|
+
2: SpecialBlock(type="event", root=root, proto=None),
|
1942
|
+
}
|
1943
|
+
|
1944
|
+
for msg in messages:
|
1945
|
+
if not msg.HasField("delta"):
|
1946
|
+
continue
|
1947
|
+
delta_path = msg.metadata.delta_path
|
1948
|
+
delta = msg.delta
|
1949
|
+
if delta.WhichOneof("type") == "new_element":
|
1950
|
+
elt = delta.new_element
|
1951
|
+
ty = elt.WhichOneof("type")
|
1952
|
+
new_node: Node
|
1953
|
+
if ty == "alert":
|
1954
|
+
format = elt.alert.format
|
1955
|
+
if format == AlertProto.Format.ERROR:
|
1956
|
+
new_node = Error(elt.alert, root=root)
|
1957
|
+
elif format == AlertProto.Format.INFO:
|
1958
|
+
new_node = Info(elt.alert, root=root)
|
1959
|
+
elif format == AlertProto.Format.SUCCESS:
|
1960
|
+
new_node = Success(elt.alert, root=root)
|
1961
|
+
elif format == AlertProto.Format.WARNING:
|
1962
|
+
new_node = Warning(elt.alert, root=root)
|
1963
|
+
else:
|
1964
|
+
raise ValueError(
|
1965
|
+
f"Unknown alert type with format {elt.alert.format}"
|
1966
|
+
)
|
1967
|
+
elif ty == "arrow_data_frame":
|
1968
|
+
new_node = Dataframe(elt.arrow_data_frame, root=root)
|
1969
|
+
elif ty == "arrow_table":
|
1970
|
+
new_node = Table(elt.arrow_table, root=root)
|
1971
|
+
elif ty == "button":
|
1972
|
+
new_node = Button(elt.button, root=root)
|
1973
|
+
elif ty == "button_group":
|
1974
|
+
new_node = ButtonGroup(elt.button_group, root=root)
|
1975
|
+
elif ty == "chat_input":
|
1976
|
+
new_node = ChatInput(elt.chat_input, root=root)
|
1977
|
+
elif ty == "checkbox":
|
1978
|
+
style = elt.checkbox.type
|
1979
|
+
if style == CheckboxProto.StyleType.TOGGLE:
|
1980
|
+
new_node = Toggle(elt.checkbox, root=root)
|
1981
|
+
else:
|
1982
|
+
new_node = Checkbox(elt.checkbox, root=root)
|
1983
|
+
elif ty == "code":
|
1984
|
+
new_node = Code(elt.code, root=root)
|
1985
|
+
elif ty == "color_picker":
|
1986
|
+
new_node = ColorPicker(elt.color_picker, root=root)
|
1987
|
+
elif ty == "date_input":
|
1988
|
+
new_node = DateInput(elt.date_input, root=root)
|
1989
|
+
elif ty == "exception":
|
1990
|
+
new_node = Exception(elt.exception, root=root)
|
1991
|
+
elif ty == "heading":
|
1992
|
+
if elt.heading.tag == HeadingProtoTag.TITLE_TAG.value:
|
1993
|
+
new_node = Title(elt.heading, root=root)
|
1994
|
+
elif elt.heading.tag == HeadingProtoTag.HEADER_TAG.value:
|
1995
|
+
new_node = Header(elt.heading, root=root)
|
1996
|
+
elif elt.heading.tag == HeadingProtoTag.SUBHEADER_TAG.value:
|
1997
|
+
new_node = Subheader(elt.heading, root=root)
|
1998
|
+
else:
|
1999
|
+
raise ValueError(f"Unknown heading type with tag {elt.heading.tag}")
|
2000
|
+
elif ty == "json":
|
2001
|
+
new_node = Json(elt.json, root=root)
|
2002
|
+
elif ty == "markdown":
|
2003
|
+
if elt.markdown.element_type == MarkdownProto.Type.NATIVE:
|
2004
|
+
new_node = Markdown(elt.markdown, root=root)
|
2005
|
+
elif elt.markdown.element_type == MarkdownProto.Type.CAPTION:
|
2006
|
+
new_node = Caption(elt.markdown, root=root)
|
2007
|
+
elif elt.markdown.element_type == MarkdownProto.Type.LATEX:
|
2008
|
+
new_node = Latex(elt.markdown, root=root)
|
2009
|
+
elif elt.markdown.element_type == MarkdownProto.Type.DIVIDER:
|
2010
|
+
new_node = Divider(elt.markdown, root=root)
|
2011
|
+
else:
|
2012
|
+
raise ValueError(
|
2013
|
+
f"Unknown markdown type {elt.markdown.element_type}"
|
2014
|
+
)
|
2015
|
+
elif ty == "metric":
|
2016
|
+
new_node = Metric(elt.metric, root=root)
|
2017
|
+
elif ty == "multiselect":
|
2018
|
+
new_node = Multiselect(elt.multiselect, root=root)
|
2019
|
+
elif ty == "number_input":
|
2020
|
+
new_node = NumberInput(elt.number_input, root=root)
|
2021
|
+
elif ty == "radio":
|
2022
|
+
new_node = Radio(elt.radio, root=root)
|
2023
|
+
elif ty == "selectbox":
|
2024
|
+
new_node = Selectbox(elt.selectbox, root=root)
|
2025
|
+
elif ty == "slider":
|
2026
|
+
if elt.slider.type == SliderProto.Type.SLIDER:
|
2027
|
+
new_node = Slider(elt.slider, root=root)
|
2028
|
+
elif elt.slider.type == SliderProto.Type.SELECT_SLIDER:
|
2029
|
+
new_node = SelectSlider(elt.slider, root=root)
|
2030
|
+
else:
|
2031
|
+
raise ValueError(f"Slider with unknown type {elt.slider}")
|
2032
|
+
elif ty == "text":
|
2033
|
+
new_node = Text(elt.text, root=root)
|
2034
|
+
elif ty == "text_area":
|
2035
|
+
new_node = TextArea(elt.text_area, root=root)
|
2036
|
+
elif ty == "text_input":
|
2037
|
+
new_node = TextInput(elt.text_input, root=root)
|
2038
|
+
elif ty == "time_input":
|
2039
|
+
new_node = TimeInput(elt.time_input, root=root)
|
2040
|
+
elif ty == "toast":
|
2041
|
+
new_node = Toast(elt.toast, root=root)
|
2042
|
+
else:
|
2043
|
+
new_node = UnknownElement(elt, root=root)
|
2044
|
+
elif delta.WhichOneof("type") == "add_block":
|
2045
|
+
block = delta.add_block
|
2046
|
+
bty = block.WhichOneof("type")
|
2047
|
+
if bty == "chat_message":
|
2048
|
+
new_node = ChatMessage(block.chat_message, root=root)
|
2049
|
+
elif bty == "column":
|
2050
|
+
new_node = Column(block.column, root=root)
|
2051
|
+
elif bty == "expandable":
|
2052
|
+
if block.expandable.icon:
|
2053
|
+
new_node = Status(block.expandable, root=root)
|
2054
|
+
else:
|
2055
|
+
new_node = Expander(block.expandable, root=root)
|
2056
|
+
elif bty == "tab":
|
2057
|
+
new_node = Tab(block.tab, root=root)
|
2058
|
+
else:
|
2059
|
+
new_node = Block(proto=block, root=root)
|
2060
|
+
else:
|
2061
|
+
# add_rows
|
2062
|
+
continue
|
2063
|
+
|
2064
|
+
current_node: Block = root
|
2065
|
+
# Every node up to the end is a Block
|
2066
|
+
for idx in delta_path[:-1]:
|
2067
|
+
children = current_node.children
|
2068
|
+
child = children.get(idx)
|
2069
|
+
if child is None:
|
2070
|
+
child = Block(proto=None, root=root)
|
2071
|
+
children[idx] = child
|
2072
|
+
assert isinstance(child, Block)
|
2073
|
+
current_node = child
|
2074
|
+
|
2075
|
+
# Handle a block when we already have a placeholder for that location
|
2076
|
+
if isinstance(new_node, Block):
|
2077
|
+
placeholder_block = current_node.children.get(delta_path[-1])
|
2078
|
+
if placeholder_block is not None:
|
2079
|
+
new_node.children = placeholder_block.children
|
2080
|
+
|
2081
|
+
current_node.children[delta_path[-1]] = new_node
|
2082
|
+
|
2083
|
+
return root
|