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,982 @@
|
|
1
|
+
# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
from __future__ import annotations
|
16
|
+
|
17
|
+
import asyncio
|
18
|
+
import json
|
19
|
+
import sys
|
20
|
+
import uuid
|
21
|
+
from enum import Enum
|
22
|
+
from typing import TYPE_CHECKING, Callable, Final
|
23
|
+
|
24
|
+
from google.protobuf.json_format import ParseDict
|
25
|
+
|
26
|
+
import streamlit.elements.exception as exception_utils
|
27
|
+
from streamlit import config, runtime
|
28
|
+
from streamlit.logger import get_logger
|
29
|
+
from streamlit.proto.ClientState_pb2 import ClientState
|
30
|
+
from streamlit.proto.Common_pb2 import FileURLs, FileURLsRequest
|
31
|
+
from streamlit.proto.ForwardMsg_pb2 import ForwardMsg
|
32
|
+
from streamlit.proto.GitInfo_pb2 import GitInfo
|
33
|
+
from streamlit.proto.NewSession_pb2 import (
|
34
|
+
Config,
|
35
|
+
CustomThemeConfig,
|
36
|
+
FontFace,
|
37
|
+
NewSession,
|
38
|
+
UserInfo,
|
39
|
+
)
|
40
|
+
from streamlit.runtime import caching
|
41
|
+
from streamlit.runtime.forward_msg_queue import ForwardMsgQueue
|
42
|
+
from streamlit.runtime.fragment import FragmentStorage, MemoryFragmentStorage
|
43
|
+
from streamlit.runtime.metrics_util import Installation
|
44
|
+
from streamlit.runtime.pages_manager import PagesManager
|
45
|
+
from streamlit.runtime.scriptrunner import RerunData, ScriptRunner, ScriptRunnerEvent
|
46
|
+
from streamlit.runtime.secrets import secrets_singleton
|
47
|
+
from streamlit.string_util import to_snake_case
|
48
|
+
from streamlit.version import STREAMLIT_VERSION_STRING
|
49
|
+
from streamlit.watcher import LocalSourcesWatcher
|
50
|
+
|
51
|
+
if TYPE_CHECKING:
|
52
|
+
from streamlit.proto.BackMsg_pb2 import BackMsg
|
53
|
+
from streamlit.runtime.script_data import ScriptData
|
54
|
+
from streamlit.runtime.scriptrunner.script_cache import ScriptCache
|
55
|
+
from streamlit.runtime.state import SessionState
|
56
|
+
from streamlit.runtime.uploaded_file_manager import UploadedFileManager
|
57
|
+
from streamlit.source_util import PageHash, PageInfo
|
58
|
+
|
59
|
+
_LOGGER: Final = get_logger(__name__)
|
60
|
+
|
61
|
+
|
62
|
+
class AppSessionState(Enum):
|
63
|
+
APP_NOT_RUNNING = "APP_NOT_RUNNING"
|
64
|
+
APP_IS_RUNNING = "APP_IS_RUNNING"
|
65
|
+
SHUTDOWN_REQUESTED = "SHUTDOWN_REQUESTED"
|
66
|
+
|
67
|
+
|
68
|
+
def _generate_scriptrun_id() -> str:
|
69
|
+
"""Randomly generate a unique ID for a script execution."""
|
70
|
+
return str(uuid.uuid4())
|
71
|
+
|
72
|
+
|
73
|
+
class AppSession:
|
74
|
+
"""
|
75
|
+
Contains session data for a single "user" of an active app
|
76
|
+
(that is, a connected browser tab).
|
77
|
+
|
78
|
+
Each AppSession has its own ScriptData, root DeltaGenerator, ScriptRunner,
|
79
|
+
and widget state.
|
80
|
+
|
81
|
+
An AppSession is attached to each thread involved in running its script.
|
82
|
+
|
83
|
+
"""
|
84
|
+
|
85
|
+
def __init__(
|
86
|
+
self,
|
87
|
+
script_data: ScriptData,
|
88
|
+
uploaded_file_manager: UploadedFileManager,
|
89
|
+
script_cache: ScriptCache,
|
90
|
+
message_enqueued_callback: Callable[[], None] | None,
|
91
|
+
user_info: dict[str, str | bool | None],
|
92
|
+
session_id_override: str | None = None,
|
93
|
+
) -> None:
|
94
|
+
"""Initialize the AppSession.
|
95
|
+
|
96
|
+
Parameters
|
97
|
+
----------
|
98
|
+
script_data
|
99
|
+
Object storing parameters related to running a script
|
100
|
+
|
101
|
+
uploaded_file_manager
|
102
|
+
Used to manage files uploaded by users via the Streamlit web client.
|
103
|
+
|
104
|
+
script_cache
|
105
|
+
The app's ScriptCache instance. Stores cached user scripts. ScriptRunner
|
106
|
+
uses the ScriptCache to avoid having to reload user scripts from disk
|
107
|
+
on each rerun.
|
108
|
+
|
109
|
+
message_enqueued_callback
|
110
|
+
After enqueuing a message, this callable notification will be invoked.
|
111
|
+
|
112
|
+
user_info
|
113
|
+
A dict that contains information about the current user. For now,
|
114
|
+
it only contains the user's email address.
|
115
|
+
|
116
|
+
{
|
117
|
+
"email": "example@example.com"
|
118
|
+
}
|
119
|
+
|
120
|
+
Information about the current user is optionally provided when a
|
121
|
+
websocket connection is initialized via the "X-Streamlit-User" header.
|
122
|
+
|
123
|
+
session_id_override
|
124
|
+
The ID to assign to this session. Setting this can be useful when the
|
125
|
+
service that a Streamlit Runtime is running in wants to tie the lifecycle of
|
126
|
+
a Streamlit session to some other session-like object that it manages.
|
127
|
+
"""
|
128
|
+
|
129
|
+
# Each AppSession has a unique string ID.
|
130
|
+
self.id = session_id_override or str(uuid.uuid4())
|
131
|
+
|
132
|
+
self._event_loop = asyncio.get_running_loop()
|
133
|
+
self._script_data = script_data
|
134
|
+
self._uploaded_file_mgr = uploaded_file_manager
|
135
|
+
self._script_cache = script_cache
|
136
|
+
self._pages_manager = PagesManager(
|
137
|
+
script_data.main_script_path, self._script_cache
|
138
|
+
)
|
139
|
+
|
140
|
+
# The browser queue contains messages that haven't yet been
|
141
|
+
# delivered to the browser. Periodically, the server flushes
|
142
|
+
# this queue and delivers its contents to the browser.
|
143
|
+
self._browser_queue = ForwardMsgQueue()
|
144
|
+
self._message_enqueued_callback = message_enqueued_callback
|
145
|
+
|
146
|
+
self._state = AppSessionState.APP_NOT_RUNNING
|
147
|
+
|
148
|
+
# Need to remember the client state here because when a script reruns
|
149
|
+
# due to the source code changing we need to pass in the previous client state.
|
150
|
+
self._client_state = ClientState()
|
151
|
+
|
152
|
+
self._local_sources_watcher: LocalSourcesWatcher | None = None
|
153
|
+
self._stop_config_listener: Callable[[], bool] | None = None
|
154
|
+
self._stop_pages_listener: Callable[[], None] | None = None
|
155
|
+
|
156
|
+
if config.get_option("server.fileWatcherType") != "none":
|
157
|
+
self.register_file_watchers()
|
158
|
+
|
159
|
+
self._run_on_save = config.get_option("server.runOnSave")
|
160
|
+
|
161
|
+
self._scriptrunner: ScriptRunner | None = None
|
162
|
+
|
163
|
+
# This needs to be lazily imported to avoid a dependency cycle.
|
164
|
+
from streamlit.runtime.state import SessionState
|
165
|
+
|
166
|
+
self._session_state = SessionState()
|
167
|
+
self._user_info = user_info
|
168
|
+
|
169
|
+
self._debug_last_backmsg_id: str | None = None
|
170
|
+
|
171
|
+
self._fragment_storage: FragmentStorage = MemoryFragmentStorage()
|
172
|
+
|
173
|
+
_LOGGER.debug("AppSession initialized (id=%s)", self.id)
|
174
|
+
|
175
|
+
def __del__(self) -> None:
|
176
|
+
"""Ensure that we call shutdown() when an AppSession is garbage collected."""
|
177
|
+
self.shutdown()
|
178
|
+
|
179
|
+
def register_file_watchers(self) -> None:
|
180
|
+
"""Register handlers to be called when various files are changed.
|
181
|
+
|
182
|
+
Files that we watch include:
|
183
|
+
- source files that already exist (for edits)
|
184
|
+
- `.py` files in the the main script's `pages/` directory (for file additions
|
185
|
+
and deletions)
|
186
|
+
- project and user-level config.toml files
|
187
|
+
- the project-level secrets.toml files
|
188
|
+
|
189
|
+
This method is called automatically on AppSession construction, but it may be
|
190
|
+
called again in the case when a session is disconnected and is being reconnect
|
191
|
+
to.
|
192
|
+
"""
|
193
|
+
if self._local_sources_watcher is None:
|
194
|
+
self._local_sources_watcher = LocalSourcesWatcher(self._pages_manager)
|
195
|
+
|
196
|
+
self._local_sources_watcher.register_file_change_callback(
|
197
|
+
self._on_source_file_changed
|
198
|
+
)
|
199
|
+
self._stop_config_listener = config.on_config_parsed(
|
200
|
+
self._on_source_file_changed, force_connect=True
|
201
|
+
)
|
202
|
+
secrets_singleton.file_change_listener.connect(self._on_secrets_file_changed)
|
203
|
+
|
204
|
+
def disconnect_file_watchers(self) -> None:
|
205
|
+
"""Disconnect the file watcher handlers registered by register_file_watchers."""
|
206
|
+
if self._local_sources_watcher is not None:
|
207
|
+
self._local_sources_watcher.close()
|
208
|
+
if self._stop_config_listener is not None:
|
209
|
+
self._stop_config_listener()
|
210
|
+
if self._stop_pages_listener is not None:
|
211
|
+
self._stop_pages_listener()
|
212
|
+
|
213
|
+
secrets_singleton.file_change_listener.disconnect(self._on_secrets_file_changed)
|
214
|
+
|
215
|
+
self._local_sources_watcher = None
|
216
|
+
self._stop_config_listener = None
|
217
|
+
self._stop_pages_listener = None
|
218
|
+
|
219
|
+
def flush_browser_queue(self) -> list[ForwardMsg]:
|
220
|
+
"""Clear the forward message queue and return the messages it contained.
|
221
|
+
|
222
|
+
The Server calls this periodically to deliver new messages
|
223
|
+
to the browser connected to this app.
|
224
|
+
|
225
|
+
Returns
|
226
|
+
-------
|
227
|
+
list[ForwardMsg]
|
228
|
+
The messages that were removed from the queue and should
|
229
|
+
be delivered to the browser.
|
230
|
+
|
231
|
+
"""
|
232
|
+
return self._browser_queue.flush()
|
233
|
+
|
234
|
+
def shutdown(self) -> None:
|
235
|
+
"""Shut down the AppSession.
|
236
|
+
|
237
|
+
It's an error to use a AppSession after it's been shut down.
|
238
|
+
|
239
|
+
"""
|
240
|
+
if self._state != AppSessionState.SHUTDOWN_REQUESTED:
|
241
|
+
_LOGGER.debug("Shutting down (id=%s)", self.id)
|
242
|
+
# Clear any unused session files in upload file manager and media
|
243
|
+
# file manager
|
244
|
+
self._uploaded_file_mgr.remove_session_files(self.id)
|
245
|
+
|
246
|
+
if runtime.exists():
|
247
|
+
rt = runtime.get_instance()
|
248
|
+
rt.media_file_mgr.clear_session_refs(self.id)
|
249
|
+
rt.media_file_mgr.remove_orphaned_files()
|
250
|
+
|
251
|
+
# Shut down the ScriptRunner, if one is active.
|
252
|
+
# self._state must not be set to SHUTDOWN_REQUESTED until
|
253
|
+
# *after* this is called.
|
254
|
+
self.request_script_stop()
|
255
|
+
|
256
|
+
self._state = AppSessionState.SHUTDOWN_REQUESTED
|
257
|
+
|
258
|
+
# Disconnect all file watchers if we haven't already, although we will have
|
259
|
+
# generally already done so by the time we get here.
|
260
|
+
self.disconnect_file_watchers()
|
261
|
+
|
262
|
+
def _enqueue_forward_msg(self, msg: ForwardMsg) -> None:
|
263
|
+
"""Enqueue a new ForwardMsg to our browser queue.
|
264
|
+
|
265
|
+
This can be called on both the main thread and a ScriptRunner
|
266
|
+
run thread.
|
267
|
+
|
268
|
+
Parameters
|
269
|
+
----------
|
270
|
+
msg : ForwardMsg
|
271
|
+
The message to enqueue
|
272
|
+
|
273
|
+
"""
|
274
|
+
|
275
|
+
if self._debug_last_backmsg_id:
|
276
|
+
msg.debug_last_backmsg_id = self._debug_last_backmsg_id
|
277
|
+
|
278
|
+
self._browser_queue.enqueue(msg)
|
279
|
+
if self._message_enqueued_callback:
|
280
|
+
self._message_enqueued_callback()
|
281
|
+
|
282
|
+
def handle_backmsg(self, msg: BackMsg) -> None:
|
283
|
+
"""Process a BackMsg."""
|
284
|
+
try:
|
285
|
+
msg_type = msg.WhichOneof("type")
|
286
|
+
if msg_type == "rerun_script":
|
287
|
+
if msg.debug_last_backmsg_id:
|
288
|
+
self._debug_last_backmsg_id = msg.debug_last_backmsg_id
|
289
|
+
|
290
|
+
self._handle_rerun_script_request(msg.rerun_script)
|
291
|
+
elif msg_type == "load_git_info":
|
292
|
+
self._handle_git_information_request()
|
293
|
+
elif msg_type == "clear_cache":
|
294
|
+
self._handle_clear_cache_request()
|
295
|
+
elif msg_type == "app_heartbeat":
|
296
|
+
self._handle_app_heartbeat_request()
|
297
|
+
elif msg_type == "set_run_on_save":
|
298
|
+
self._handle_set_run_on_save_request(msg.set_run_on_save)
|
299
|
+
elif msg_type == "stop_script":
|
300
|
+
self._handle_stop_script_request()
|
301
|
+
elif msg_type == "file_urls_request":
|
302
|
+
self._handle_file_urls_request(msg.file_urls_request)
|
303
|
+
else:
|
304
|
+
_LOGGER.warning('No handler for "%s"', msg_type)
|
305
|
+
|
306
|
+
except Exception as ex:
|
307
|
+
_LOGGER.exception("Error processing back message")
|
308
|
+
self.handle_backmsg_exception(ex)
|
309
|
+
|
310
|
+
def handle_backmsg_exception(self, e: BaseException) -> None:
|
311
|
+
"""Handle an Exception raised while processing a BackMsg from the browser."""
|
312
|
+
# This does a few things:
|
313
|
+
# 1) Clears the current app in the browser.
|
314
|
+
# 2) Marks the current app as "stopped" in the browser.
|
315
|
+
# 3) HACK: Resets any script params that may have been broken (e.g. the
|
316
|
+
# command-line when rerunning with wrong argv[0])
|
317
|
+
|
318
|
+
self._on_scriptrunner_event(
|
319
|
+
self._scriptrunner, ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS
|
320
|
+
)
|
321
|
+
self._on_scriptrunner_event(
|
322
|
+
self._scriptrunner,
|
323
|
+
ScriptRunnerEvent.SCRIPT_STARTED,
|
324
|
+
page_script_hash="",
|
325
|
+
)
|
326
|
+
self._on_scriptrunner_event(
|
327
|
+
self._scriptrunner, ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS
|
328
|
+
)
|
329
|
+
|
330
|
+
# Send an Exception message to the frontend.
|
331
|
+
# Because _on_scriptrunner_event does its work in an eventloop callback,
|
332
|
+
# this exception ForwardMsg *must* also be enqueued in a callback,
|
333
|
+
# so that it will be enqueued *after* the various ForwardMsgs that
|
334
|
+
# _on_scriptrunner_event sends.
|
335
|
+
self._event_loop.call_soon_threadsafe(
|
336
|
+
lambda: self._enqueue_forward_msg(self._create_exception_message(e))
|
337
|
+
)
|
338
|
+
|
339
|
+
def request_rerun(self, client_state: ClientState | None) -> None:
|
340
|
+
"""Signal that we're interested in running the script.
|
341
|
+
|
342
|
+
If the script is not already running, it will be started immediately.
|
343
|
+
Otherwise, a rerun will be requested.
|
344
|
+
|
345
|
+
Parameters
|
346
|
+
----------
|
347
|
+
client_state : streamlit.proto.ClientState_pb2.ClientState | None
|
348
|
+
The ClientState protobuf to run the script with, or None
|
349
|
+
to use previous client state.
|
350
|
+
|
351
|
+
"""
|
352
|
+
|
353
|
+
if self._state == AppSessionState.SHUTDOWN_REQUESTED:
|
354
|
+
_LOGGER.warning("Discarding rerun request after shutdown")
|
355
|
+
return
|
356
|
+
|
357
|
+
if client_state:
|
358
|
+
fragment_id = client_state.fragment_id
|
359
|
+
|
360
|
+
# Early check whether this fragment still exists in the fragment storage or
|
361
|
+
# might have been removed by a full app run. This is not merely a
|
362
|
+
# performance optimization, but also fixes following potential situation:
|
363
|
+
# A fragment run might create a new ScriptRunner when the current
|
364
|
+
# ScriptRunner is in state STOPPED (in this case, the 'success' variable
|
365
|
+
# below is false and the new ScriptRunner is created). This will lead to all
|
366
|
+
# events that were not sent / received from the previous script runner to be
|
367
|
+
# ignored in _handle_scriptrunner_event_on_event_loop, because the
|
368
|
+
# _script_runner changed. When the full app rerun ScriptRunner is done
|
369
|
+
# (STOPPED) but its events are not processed before the new ScriptRunner is
|
370
|
+
# created, its finished message is not sent to the frontend and no
|
371
|
+
# full-app-run cleanup is happening. This scenario can be triggered by the
|
372
|
+
# example app described in
|
373
|
+
# https://github.com/streamlit/streamlit/issues/9921, where the dialog
|
374
|
+
# sometimes stays open.
|
375
|
+
if fragment_id and not self._fragment_storage.contains(fragment_id):
|
376
|
+
_LOGGER.info(
|
377
|
+
f"The fragment with id {fragment_id} does not exist anymore - "
|
378
|
+
"it might have been removed during a preceding full-app rerun."
|
379
|
+
)
|
380
|
+
return
|
381
|
+
|
382
|
+
if client_state.HasField("context_info"):
|
383
|
+
self._client_state.context_info.CopyFrom(client_state.context_info)
|
384
|
+
|
385
|
+
rerun_data = RerunData(
|
386
|
+
client_state.query_string,
|
387
|
+
client_state.widget_states,
|
388
|
+
client_state.page_script_hash,
|
389
|
+
client_state.page_name,
|
390
|
+
fragment_id=fragment_id if fragment_id else None,
|
391
|
+
is_auto_rerun=client_state.is_auto_rerun,
|
392
|
+
context_info=client_state.context_info,
|
393
|
+
)
|
394
|
+
else:
|
395
|
+
rerun_data = RerunData()
|
396
|
+
|
397
|
+
if self._scriptrunner is not None:
|
398
|
+
if (
|
399
|
+
bool(config.get_option("runner.fastReruns"))
|
400
|
+
and not rerun_data.fragment_id
|
401
|
+
):
|
402
|
+
# If fastReruns is enabled and this is *not* a rerun of a fragment,
|
403
|
+
# we don't send rerun requests to our existing ScriptRunner. Instead, we
|
404
|
+
# tell it to shut down. We'll then spin up a new ScriptRunner, below, to
|
405
|
+
# handle the rerun immediately.
|
406
|
+
self._scriptrunner.request_stop()
|
407
|
+
self._scriptrunner = None
|
408
|
+
else:
|
409
|
+
# Either fastReruns is not enabled or this RERUN request is a request to
|
410
|
+
# run a fragment. We send our current ScriptRunner a rerun request, and
|
411
|
+
# if it's accepted, we're done.
|
412
|
+
success = self._scriptrunner.request_rerun(rerun_data)
|
413
|
+
if success:
|
414
|
+
return
|
415
|
+
|
416
|
+
# If we are here, then either we have no ScriptRunner, or our
|
417
|
+
# current ScriptRunner is shutting down and cannot handle a rerun
|
418
|
+
# request - so we'll create and start a new ScriptRunner.
|
419
|
+
self._create_scriptrunner(rerun_data)
|
420
|
+
|
421
|
+
def request_script_stop(self) -> None:
|
422
|
+
"""Request that the scriptrunner stop execution.
|
423
|
+
|
424
|
+
Does nothing if no scriptrunner exists.
|
425
|
+
"""
|
426
|
+
if self._scriptrunner is not None:
|
427
|
+
self._scriptrunner.request_stop()
|
428
|
+
|
429
|
+
def clear_user_info(self) -> None:
|
430
|
+
"""Clear the user info for this session."""
|
431
|
+
self._user_info.clear()
|
432
|
+
|
433
|
+
def _create_scriptrunner(self, initial_rerun_data: RerunData) -> None:
|
434
|
+
"""Create and run a new ScriptRunner with the given RerunData."""
|
435
|
+
self._scriptrunner = ScriptRunner(
|
436
|
+
session_id=self.id,
|
437
|
+
main_script_path=self._script_data.main_script_path,
|
438
|
+
session_state=self._session_state,
|
439
|
+
uploaded_file_mgr=self._uploaded_file_mgr,
|
440
|
+
script_cache=self._script_cache,
|
441
|
+
initial_rerun_data=initial_rerun_data,
|
442
|
+
user_info=self._user_info,
|
443
|
+
fragment_storage=self._fragment_storage,
|
444
|
+
pages_manager=self._pages_manager,
|
445
|
+
)
|
446
|
+
self._scriptrunner.on_event.connect(self._on_scriptrunner_event)
|
447
|
+
self._scriptrunner.start()
|
448
|
+
|
449
|
+
@property
|
450
|
+
def session_state(self) -> SessionState:
|
451
|
+
return self._session_state
|
452
|
+
|
453
|
+
def _should_rerun_on_file_change(self, filepath: str) -> bool:
|
454
|
+
pages = self._pages_manager.get_pages()
|
455
|
+
|
456
|
+
changed_page_script_hash = next(
|
457
|
+
filter(lambda k: pages[k]["script_path"] == filepath, pages),
|
458
|
+
None,
|
459
|
+
)
|
460
|
+
|
461
|
+
if changed_page_script_hash is not None:
|
462
|
+
current_page_script_hash = self._client_state.page_script_hash
|
463
|
+
return changed_page_script_hash == current_page_script_hash
|
464
|
+
|
465
|
+
return True
|
466
|
+
|
467
|
+
def _on_source_file_changed(self, filepath: str | None = None) -> None:
|
468
|
+
"""One of our source files changed. Clear the cache and schedule a rerun if
|
469
|
+
appropriate.
|
470
|
+
"""
|
471
|
+
self._script_cache.clear()
|
472
|
+
|
473
|
+
if filepath is not None and not self._should_rerun_on_file_change(filepath):
|
474
|
+
return
|
475
|
+
|
476
|
+
if self._run_on_save:
|
477
|
+
self.request_rerun(self._client_state)
|
478
|
+
else:
|
479
|
+
self._enqueue_forward_msg(self._create_file_change_message())
|
480
|
+
|
481
|
+
def _on_secrets_file_changed(self, _) -> None:
|
482
|
+
"""Called when `secrets.file_change_listener` emits a Signal."""
|
483
|
+
|
484
|
+
# NOTE: At the time of writing, this function only calls
|
485
|
+
# `_on_source_file_changed`. The reason behind creating this function instead of
|
486
|
+
# just passing `_on_source_file_changed` to `connect` / `disconnect` directly is
|
487
|
+
# that every function that is passed to `connect` / `disconnect` must have at
|
488
|
+
# least one argument for `sender` (in this case we don't really care about it,
|
489
|
+
# thus `_`), and introducing an unnecessary argument to
|
490
|
+
# `_on_source_file_changed` just for this purpose sounded finicky.
|
491
|
+
self._on_source_file_changed()
|
492
|
+
|
493
|
+
def _clear_queue(self, fragment_ids_this_run: list[str] | None = None) -> None:
|
494
|
+
self._browser_queue.clear(
|
495
|
+
retain_lifecycle_msgs=True, fragment_ids_this_run=fragment_ids_this_run
|
496
|
+
)
|
497
|
+
|
498
|
+
def _on_scriptrunner_event(
|
499
|
+
self,
|
500
|
+
sender: ScriptRunner | None,
|
501
|
+
event: ScriptRunnerEvent,
|
502
|
+
forward_msg: ForwardMsg | None = None,
|
503
|
+
exception: BaseException | None = None,
|
504
|
+
client_state: ClientState | None = None,
|
505
|
+
page_script_hash: str | None = None,
|
506
|
+
fragment_ids_this_run: list[str] | None = None,
|
507
|
+
pages: dict[PageHash, PageInfo] | None = None,
|
508
|
+
) -> None:
|
509
|
+
"""Called when our ScriptRunner emits an event.
|
510
|
+
|
511
|
+
This is generally called from the sender ScriptRunner's script thread.
|
512
|
+
We forward the event on to _handle_scriptrunner_event_on_event_loop,
|
513
|
+
which will be called on the main thread.
|
514
|
+
"""
|
515
|
+
self._event_loop.call_soon_threadsafe(
|
516
|
+
lambda: self._handle_scriptrunner_event_on_event_loop(
|
517
|
+
sender,
|
518
|
+
event,
|
519
|
+
forward_msg,
|
520
|
+
exception,
|
521
|
+
client_state,
|
522
|
+
page_script_hash,
|
523
|
+
fragment_ids_this_run,
|
524
|
+
pages,
|
525
|
+
)
|
526
|
+
)
|
527
|
+
|
528
|
+
def _handle_scriptrunner_event_on_event_loop(
|
529
|
+
self,
|
530
|
+
sender: ScriptRunner | None,
|
531
|
+
event: ScriptRunnerEvent,
|
532
|
+
forward_msg: ForwardMsg | None = None,
|
533
|
+
exception: BaseException | None = None,
|
534
|
+
client_state: ClientState | None = None,
|
535
|
+
page_script_hash: str | None = None,
|
536
|
+
fragment_ids_this_run: list[str] | None = None,
|
537
|
+
pages: dict[PageHash, PageInfo] | None = None,
|
538
|
+
) -> None:
|
539
|
+
"""Handle a ScriptRunner event.
|
540
|
+
|
541
|
+
This function must only be called on our eventloop thread.
|
542
|
+
|
543
|
+
Parameters
|
544
|
+
----------
|
545
|
+
sender : ScriptRunner | None
|
546
|
+
The ScriptRunner that emitted the event. (This may be set to
|
547
|
+
None when called from `handle_backmsg_exception`, if no
|
548
|
+
ScriptRunner was active when the backmsg exception was raised.)
|
549
|
+
|
550
|
+
event : ScriptRunnerEvent
|
551
|
+
The event type.
|
552
|
+
|
553
|
+
forward_msg : ForwardMsg | None
|
554
|
+
The ForwardMsg to send to the frontend. Set only for the
|
555
|
+
ENQUEUE_FORWARD_MSG event.
|
556
|
+
|
557
|
+
exception : BaseException | None
|
558
|
+
An exception thrown during compilation. Set only for the
|
559
|
+
SCRIPT_STOPPED_WITH_COMPILE_ERROR event.
|
560
|
+
|
561
|
+
client_state : streamlit.proto.ClientState_pb2.ClientState | None
|
562
|
+
The ScriptRunner's final ClientState. Set only for the
|
563
|
+
SHUTDOWN event.
|
564
|
+
|
565
|
+
page_script_hash : str | None
|
566
|
+
A hash of the script path corresponding to the page currently being
|
567
|
+
run. Set only for the SCRIPT_STARTED event.
|
568
|
+
|
569
|
+
fragment_ids_this_run : list[str] | None
|
570
|
+
The fragment IDs of the fragments being executed in this script run. Only
|
571
|
+
set for the SCRIPT_STARTED event. If this value is falsy, this script run
|
572
|
+
must be for the full script.
|
573
|
+
|
574
|
+
clear_forward_msg_queue : bool
|
575
|
+
If set (the default), clears the queue of forward messages to be sent to the
|
576
|
+
browser. Set only for the SCRIPT_STARTED event.
|
577
|
+
"""
|
578
|
+
|
579
|
+
assert self._event_loop == asyncio.get_running_loop(), (
|
580
|
+
"This function must only be called on the eventloop thread the AppSession was created on."
|
581
|
+
)
|
582
|
+
|
583
|
+
if sender is not self._scriptrunner:
|
584
|
+
# This event was sent by a non-current ScriptRunner; ignore it.
|
585
|
+
# This can happen after sppinng up a new ScriptRunner (to handle a
|
586
|
+
# rerun request, for example) while another ScriptRunner is still
|
587
|
+
# shutting down. The shutting-down ScriptRunner may still
|
588
|
+
# emit events.
|
589
|
+
_LOGGER.debug("Ignoring event from non-current ScriptRunner: %s", event)
|
590
|
+
return
|
591
|
+
|
592
|
+
prev_state = self._state
|
593
|
+
|
594
|
+
if event == ScriptRunnerEvent.SCRIPT_STARTED:
|
595
|
+
if self._state != AppSessionState.SHUTDOWN_REQUESTED:
|
596
|
+
self._state = AppSessionState.APP_IS_RUNNING
|
597
|
+
assert page_script_hash is not None, (
|
598
|
+
"page_script_hash must be set for the SCRIPT_STARTED event"
|
599
|
+
)
|
600
|
+
|
601
|
+
# Update the client state with the new page_script_hash if
|
602
|
+
# necessary. This handles an edge case where a script is never
|
603
|
+
# finishes (eg. by calling st.rerun()), but the page has changed
|
604
|
+
# via st.navigation()
|
605
|
+
if page_script_hash != self._client_state.page_script_hash:
|
606
|
+
self._client_state.page_script_hash = page_script_hash
|
607
|
+
|
608
|
+
self._clear_queue(fragment_ids_this_run)
|
609
|
+
|
610
|
+
self._enqueue_forward_msg(
|
611
|
+
self._create_new_session_message(
|
612
|
+
page_script_hash, fragment_ids_this_run, pages
|
613
|
+
)
|
614
|
+
)
|
615
|
+
|
616
|
+
elif (
|
617
|
+
event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS
|
618
|
+
or event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_COMPILE_ERROR
|
619
|
+
or event == ScriptRunnerEvent.FRAGMENT_STOPPED_WITH_SUCCESS
|
620
|
+
):
|
621
|
+
if self._state != AppSessionState.SHUTDOWN_REQUESTED:
|
622
|
+
self._state = AppSessionState.APP_NOT_RUNNING
|
623
|
+
|
624
|
+
if event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS:
|
625
|
+
status = ForwardMsg.FINISHED_SUCCESSFULLY
|
626
|
+
elif event == ScriptRunnerEvent.FRAGMENT_STOPPED_WITH_SUCCESS:
|
627
|
+
status = ForwardMsg.FINISHED_FRAGMENT_RUN_SUCCESSFULLY
|
628
|
+
else:
|
629
|
+
status = ForwardMsg.FINISHED_WITH_COMPILE_ERROR
|
630
|
+
|
631
|
+
self._enqueue_forward_msg(self._create_script_finished_message(status))
|
632
|
+
self._debug_last_backmsg_id = None
|
633
|
+
|
634
|
+
if (
|
635
|
+
event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS
|
636
|
+
or event == ScriptRunnerEvent.FRAGMENT_STOPPED_WITH_SUCCESS
|
637
|
+
):
|
638
|
+
# The script completed successfully: update our
|
639
|
+
# LocalSourcesWatcher to account for any source code changes
|
640
|
+
# that change which modules should be watched.
|
641
|
+
if self._local_sources_watcher:
|
642
|
+
self._local_sources_watcher.update_watched_modules()
|
643
|
+
self._local_sources_watcher.update_watched_pages()
|
644
|
+
else:
|
645
|
+
# The script didn't complete successfully: send the exception
|
646
|
+
# to the frontend.
|
647
|
+
assert exception is not None, (
|
648
|
+
"exception must be set for the SCRIPT_STOPPED_WITH_COMPILE_ERROR event"
|
649
|
+
)
|
650
|
+
msg = ForwardMsg()
|
651
|
+
exception_utils.marshall(
|
652
|
+
msg.session_event.script_compilation_exception, exception
|
653
|
+
)
|
654
|
+
self._enqueue_forward_msg(msg)
|
655
|
+
|
656
|
+
elif event == ScriptRunnerEvent.SCRIPT_STOPPED_FOR_RERUN:
|
657
|
+
self._state = AppSessionState.APP_NOT_RUNNING
|
658
|
+
self._enqueue_forward_msg(
|
659
|
+
self._create_script_finished_message(
|
660
|
+
ForwardMsg.FINISHED_EARLY_FOR_RERUN
|
661
|
+
)
|
662
|
+
)
|
663
|
+
if self._local_sources_watcher:
|
664
|
+
self._local_sources_watcher.update_watched_modules()
|
665
|
+
|
666
|
+
elif event == ScriptRunnerEvent.SHUTDOWN:
|
667
|
+
assert client_state is not None, (
|
668
|
+
"client_state must be set for the SHUTDOWN event"
|
669
|
+
)
|
670
|
+
|
671
|
+
if self._state == AppSessionState.SHUTDOWN_REQUESTED:
|
672
|
+
# Only clear media files if the script is done running AND the
|
673
|
+
# session is actually shutting down.
|
674
|
+
runtime.get_instance().media_file_mgr.clear_session_refs(self.id)
|
675
|
+
|
676
|
+
self._client_state = client_state
|
677
|
+
self._scriptrunner = None
|
678
|
+
|
679
|
+
elif event == ScriptRunnerEvent.ENQUEUE_FORWARD_MSG:
|
680
|
+
assert forward_msg is not None, (
|
681
|
+
"null forward_msg in ENQUEUE_FORWARD_MSG event"
|
682
|
+
)
|
683
|
+
self._enqueue_forward_msg(forward_msg)
|
684
|
+
|
685
|
+
# Send a message if our run state changed
|
686
|
+
app_was_running = prev_state == AppSessionState.APP_IS_RUNNING
|
687
|
+
app_is_running = self._state == AppSessionState.APP_IS_RUNNING
|
688
|
+
if app_is_running != app_was_running:
|
689
|
+
self._enqueue_forward_msg(self._create_session_status_changed_message())
|
690
|
+
|
691
|
+
def _create_session_status_changed_message(self) -> ForwardMsg:
|
692
|
+
"""Create and return a session_status_changed ForwardMsg."""
|
693
|
+
msg = ForwardMsg()
|
694
|
+
msg.session_status_changed.run_on_save = self._run_on_save
|
695
|
+
msg.session_status_changed.script_is_running = (
|
696
|
+
self._state == AppSessionState.APP_IS_RUNNING
|
697
|
+
)
|
698
|
+
return msg
|
699
|
+
|
700
|
+
def _create_file_change_message(self) -> ForwardMsg:
|
701
|
+
"""Create and return a 'script_changed_on_disk' ForwardMsg."""
|
702
|
+
msg = ForwardMsg()
|
703
|
+
msg.session_event.script_changed_on_disk = True
|
704
|
+
return msg
|
705
|
+
|
706
|
+
def _create_new_session_message(
|
707
|
+
self,
|
708
|
+
page_script_hash: str,
|
709
|
+
fragment_ids_this_run: list[str] | None = None,
|
710
|
+
pages: dict[PageHash, PageInfo] | None = None,
|
711
|
+
) -> ForwardMsg:
|
712
|
+
"""Create and return a new_session ForwardMsg."""
|
713
|
+
msg = ForwardMsg()
|
714
|
+
|
715
|
+
msg.new_session.script_run_id = _generate_scriptrun_id()
|
716
|
+
msg.new_session.name = self._script_data.name
|
717
|
+
msg.new_session.main_script_path = self._pages_manager.main_script_path
|
718
|
+
msg.new_session.main_script_hash = self._pages_manager.main_script_hash
|
719
|
+
msg.new_session.page_script_hash = page_script_hash
|
720
|
+
|
721
|
+
if fragment_ids_this_run:
|
722
|
+
msg.new_session.fragment_ids_this_run.extend(fragment_ids_this_run)
|
723
|
+
|
724
|
+
self._populate_app_pages(
|
725
|
+
msg.new_session, pages or self._pages_manager.get_pages()
|
726
|
+
)
|
727
|
+
_populate_config_msg(msg.new_session.config)
|
728
|
+
_populate_theme_msg(msg.new_session.custom_theme)
|
729
|
+
|
730
|
+
# Immutable session data. We send this every time a new session is
|
731
|
+
# started, to avoid having to track whether the client has already
|
732
|
+
# received it. It does not change from run to run; it's up to the
|
733
|
+
# to perform one-time initialization only once.
|
734
|
+
imsg = msg.new_session.initialize
|
735
|
+
|
736
|
+
_populate_user_info_msg(imsg.user_info)
|
737
|
+
|
738
|
+
imsg.environment_info.streamlit_version = STREAMLIT_VERSION_STRING
|
739
|
+
imsg.environment_info.python_version = ".".join(map(str, sys.version_info))
|
740
|
+
|
741
|
+
imsg.session_status.run_on_save = self._run_on_save
|
742
|
+
imsg.session_status.script_is_running = (
|
743
|
+
self._state == AppSessionState.APP_IS_RUNNING
|
744
|
+
)
|
745
|
+
|
746
|
+
imsg.is_hello = self._script_data.is_hello
|
747
|
+
imsg.session_id = self.id
|
748
|
+
|
749
|
+
return msg
|
750
|
+
|
751
|
+
def _create_script_finished_message(
|
752
|
+
self, status: ForwardMsg.ScriptFinishedStatus.ValueType
|
753
|
+
) -> ForwardMsg:
|
754
|
+
"""Create and return a script_finished ForwardMsg."""
|
755
|
+
msg = ForwardMsg()
|
756
|
+
msg.script_finished = status
|
757
|
+
return msg
|
758
|
+
|
759
|
+
def _create_exception_message(self, e: BaseException) -> ForwardMsg:
|
760
|
+
"""Create and return an Exception ForwardMsg."""
|
761
|
+
msg = ForwardMsg()
|
762
|
+
exception_utils.marshall(msg.delta.new_element.exception, e)
|
763
|
+
return msg
|
764
|
+
|
765
|
+
def _handle_git_information_request(self) -> None:
|
766
|
+
msg = ForwardMsg()
|
767
|
+
|
768
|
+
try:
|
769
|
+
from streamlit.git_util import GitRepo
|
770
|
+
|
771
|
+
repo = GitRepo(self._script_data.main_script_path)
|
772
|
+
|
773
|
+
repo_info = repo.get_repo_info()
|
774
|
+
if repo_info is None:
|
775
|
+
return
|
776
|
+
|
777
|
+
repository_name, branch, module = repo_info
|
778
|
+
|
779
|
+
repository_name = repository_name.removesuffix(".git")
|
780
|
+
|
781
|
+
msg.git_info_changed.repository = repository_name
|
782
|
+
msg.git_info_changed.branch = branch
|
783
|
+
msg.git_info_changed.module = module
|
784
|
+
|
785
|
+
msg.git_info_changed.untracked_files[:] = repo.untracked_files
|
786
|
+
msg.git_info_changed.uncommitted_files[:] = repo.uncommitted_files
|
787
|
+
|
788
|
+
if repo.is_head_detached:
|
789
|
+
msg.git_info_changed.state = GitInfo.GitStates.HEAD_DETACHED
|
790
|
+
elif len(repo.ahead_commits) > 0:
|
791
|
+
msg.git_info_changed.state = GitInfo.GitStates.AHEAD_OF_REMOTE
|
792
|
+
else:
|
793
|
+
msg.git_info_changed.state = GitInfo.GitStates.DEFAULT
|
794
|
+
|
795
|
+
self._enqueue_forward_msg(msg)
|
796
|
+
except Exception as ex:
|
797
|
+
# Users may never even install Git in the first place, so this
|
798
|
+
# error requires no action. It can be useful for debugging.
|
799
|
+
_LOGGER.debug("Obtaining Git information produced an error", exc_info=ex)
|
800
|
+
|
801
|
+
def _handle_rerun_script_request(
|
802
|
+
self, client_state: ClientState | None = None
|
803
|
+
) -> None:
|
804
|
+
"""Tell the ScriptRunner to re-run its script.
|
805
|
+
|
806
|
+
Parameters
|
807
|
+
----------
|
808
|
+
client_state : streamlit.proto.ClientState_pb2.ClientState | None
|
809
|
+
The ClientState protobuf to run the script with, or None
|
810
|
+
to use previous client state.
|
811
|
+
|
812
|
+
"""
|
813
|
+
self.request_rerun(client_state)
|
814
|
+
|
815
|
+
def _handle_stop_script_request(self) -> None:
|
816
|
+
"""Tell the ScriptRunner to stop running its script."""
|
817
|
+
self.request_script_stop()
|
818
|
+
|
819
|
+
def _handle_clear_cache_request(self) -> None:
|
820
|
+
"""Clear this app's cache.
|
821
|
+
|
822
|
+
Because this cache is global, it will be cleared for all users.
|
823
|
+
|
824
|
+
"""
|
825
|
+
caching.cache_data.clear()
|
826
|
+
caching.cache_resource.clear()
|
827
|
+
self._session_state.clear()
|
828
|
+
|
829
|
+
def _handle_app_heartbeat_request(self) -> None:
|
830
|
+
"""Handle an incoming app heartbeat.
|
831
|
+
|
832
|
+
The heartbeat indicates the frontend is active and keeps the
|
833
|
+
websocket from going idle and disconnecting.
|
834
|
+
|
835
|
+
The actual handler here is a noop
|
836
|
+
|
837
|
+
"""
|
838
|
+
pass
|
839
|
+
|
840
|
+
def _handle_set_run_on_save_request(self, new_value: bool) -> None:
|
841
|
+
"""Change our run_on_save flag to the given value.
|
842
|
+
|
843
|
+
The browser will be notified of the change.
|
844
|
+
|
845
|
+
Parameters
|
846
|
+
----------
|
847
|
+
new_value : bool
|
848
|
+
New run_on_save value
|
849
|
+
|
850
|
+
"""
|
851
|
+
self._run_on_save = new_value
|
852
|
+
self._enqueue_forward_msg(self._create_session_status_changed_message())
|
853
|
+
|
854
|
+
def _handle_file_urls_request(self, file_urls_request: FileURLsRequest) -> None:
|
855
|
+
"""Handle a file_urls_request BackMsg sent by the client."""
|
856
|
+
msg = ForwardMsg()
|
857
|
+
msg.file_urls_response.response_id = file_urls_request.request_id
|
858
|
+
|
859
|
+
upload_url_infos = self._uploaded_file_mgr.get_upload_urls(
|
860
|
+
self.id, file_urls_request.file_names
|
861
|
+
)
|
862
|
+
|
863
|
+
for upload_url_info in upload_url_infos:
|
864
|
+
msg.file_urls_response.file_urls.append(
|
865
|
+
FileURLs(
|
866
|
+
file_id=upload_url_info.file_id,
|
867
|
+
upload_url=upload_url_info.upload_url,
|
868
|
+
delete_url=upload_url_info.delete_url,
|
869
|
+
)
|
870
|
+
)
|
871
|
+
|
872
|
+
self._enqueue_forward_msg(msg)
|
873
|
+
|
874
|
+
def _populate_app_pages(
|
875
|
+
self, msg: NewSession, pages: dict[PageHash, PageInfo]
|
876
|
+
) -> None:
|
877
|
+
for page_script_hash, page_info in pages.items():
|
878
|
+
page_proto = msg.app_pages.add()
|
879
|
+
|
880
|
+
page_proto.page_script_hash = page_script_hash
|
881
|
+
page_proto.page_name = page_info["page_name"].replace("_", " ")
|
882
|
+
page_proto.url_pathname = page_info["page_name"]
|
883
|
+
page_proto.icon = page_info["icon"]
|
884
|
+
|
885
|
+
|
886
|
+
# Config.ToolbarMode.ValueType does not exist at runtime (only in the pyi stubs), so
|
887
|
+
# we need to use quotes.
|
888
|
+
# This field will be available at runtime as of protobuf 3.20.1, but
|
889
|
+
# we are using an older version.
|
890
|
+
# For details, see: https://github.com/protocolbuffers/protobuf/issues/8175
|
891
|
+
def _get_toolbar_mode() -> Config.ToolbarMode.ValueType:
|
892
|
+
config_key = "client.toolbarMode"
|
893
|
+
config_value = config.get_option(config_key)
|
894
|
+
enum_value: Config.ToolbarMode.ValueType | None = getattr(
|
895
|
+
Config.ToolbarMode, config_value.upper()
|
896
|
+
)
|
897
|
+
if enum_value is None:
|
898
|
+
allowed_values = ", ".join(k.lower() for k in Config.ToolbarMode.keys())
|
899
|
+
raise ValueError(
|
900
|
+
f"Config {config_key!r} expects to have one of "
|
901
|
+
f"the following values: {allowed_values}. "
|
902
|
+
f"Current value: {config_value}"
|
903
|
+
)
|
904
|
+
return enum_value
|
905
|
+
|
906
|
+
|
907
|
+
def _populate_config_msg(msg: Config) -> None:
|
908
|
+
msg.gather_usage_stats = config.get_option("browser.gatherUsageStats")
|
909
|
+
msg.max_cached_message_age = config.get_option("global.maxCachedMessageAge")
|
910
|
+
msg.allow_run_on_save = config.get_option("server.allowRunOnSave")
|
911
|
+
msg.hide_top_bar = config.get_option("ui.hideTopBar")
|
912
|
+
if config.get_option("client.showSidebarNavigation") is False:
|
913
|
+
msg.hide_sidebar_nav = True
|
914
|
+
msg.toolbar_mode = _get_toolbar_mode()
|
915
|
+
|
916
|
+
|
917
|
+
def _populate_theme_msg(msg: CustomThemeConfig) -> None:
|
918
|
+
theme_opts = config.get_options_for_section("theme")
|
919
|
+
|
920
|
+
if not any(theme_opts.values()):
|
921
|
+
return
|
922
|
+
|
923
|
+
for option_name, option_val in theme_opts.items():
|
924
|
+
# We need to ignore some config options here that need special handling
|
925
|
+
# and cannot directly be set on the protobuf.
|
926
|
+
if option_name not in {"base", "font", "fontFaces"} and option_val is not None:
|
927
|
+
setattr(msg, to_snake_case(option_name), option_val)
|
928
|
+
|
929
|
+
# NOTE: If unset, base and font will default to the protobuf enum zero
|
930
|
+
# values, which are BaseTheme.LIGHT and FontFamily.SANS_SERIF,
|
931
|
+
# respectively. This is why we both don't handle the cases explicitly and
|
932
|
+
# also only log a warning when receiving invalid base/font options.
|
933
|
+
base_map = {
|
934
|
+
"light": msg.BaseTheme.LIGHT,
|
935
|
+
"dark": msg.BaseTheme.DARK,
|
936
|
+
}
|
937
|
+
base = theme_opts["base"]
|
938
|
+
if base is not None:
|
939
|
+
if base not in base_map:
|
940
|
+
_LOGGER.warning(
|
941
|
+
f'"{base}" is an invalid value for theme.base.'
|
942
|
+
f" Allowed values include {list(base_map.keys())}."
|
943
|
+
' Setting theme.base to "light".'
|
944
|
+
)
|
945
|
+
else:
|
946
|
+
msg.base = base_map[base]
|
947
|
+
|
948
|
+
# Since the font field uses the deprecated enum, we need to put the font
|
949
|
+
# config into the body_font field instead:
|
950
|
+
body_font = theme_opts["font"]
|
951
|
+
if body_font:
|
952
|
+
msg.body_font = body_font
|
953
|
+
|
954
|
+
font_faces = theme_opts["fontFaces"]
|
955
|
+
# If fontFaces was configured via config.toml, it's already a parsed list of
|
956
|
+
# dictionaries. However, if it was provided via env variable or via CLI arg,
|
957
|
+
# it's a json string that still needs to be parsed.
|
958
|
+
if isinstance(font_faces, str):
|
959
|
+
try:
|
960
|
+
font_faces = json.loads(font_faces)
|
961
|
+
except Exception as e:
|
962
|
+
_LOGGER.warning(
|
963
|
+
"Failed to parse the theme.fontFaces config option with json.loads: "
|
964
|
+
f"{font_faces}.",
|
965
|
+
exc_info=e,
|
966
|
+
)
|
967
|
+
font_faces = None
|
968
|
+
|
969
|
+
if font_faces is not None:
|
970
|
+
for font_face in font_faces:
|
971
|
+
try:
|
972
|
+
msg.font_faces.append(ParseDict(font_face, FontFace()))
|
973
|
+
except Exception as e:
|
974
|
+
_LOGGER.warning(
|
975
|
+
f"Failed to parse the theme.fontFaces config option: {font_face}.",
|
976
|
+
exc_info=e,
|
977
|
+
)
|
978
|
+
|
979
|
+
|
980
|
+
def _populate_user_info_msg(msg: UserInfo) -> None:
|
981
|
+
msg.installation_id = Installation.instance().installation_id
|
982
|
+
msg.installation_id_v3 = Installation.instance().installation_id_v3
|