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,792 @@
|
|
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 time
|
19
|
+
import traceback
|
20
|
+
from dataclasses import dataclass, field
|
21
|
+
from enum import Enum
|
22
|
+
from typing import TYPE_CHECKING, Final, NamedTuple
|
23
|
+
|
24
|
+
from streamlit import config
|
25
|
+
from streamlit.components.lib.local_component_registry import LocalComponentRegistry
|
26
|
+
from streamlit.logger import get_logger
|
27
|
+
from streamlit.proto.ForwardMsg_pb2 import ForwardMsg
|
28
|
+
from streamlit.runtime.app_session import AppSession
|
29
|
+
from streamlit.runtime.caching import (
|
30
|
+
get_data_cache_stats_provider,
|
31
|
+
get_resource_cache_stats_provider,
|
32
|
+
)
|
33
|
+
from streamlit.runtime.caching.storage.local_disk_cache_storage import (
|
34
|
+
LocalDiskCacheStorageManager,
|
35
|
+
)
|
36
|
+
from streamlit.runtime.forward_msg_cache import (
|
37
|
+
ForwardMsgCache,
|
38
|
+
create_reference_msg,
|
39
|
+
populate_hash_if_needed,
|
40
|
+
)
|
41
|
+
from streamlit.runtime.media_file_manager import MediaFileManager
|
42
|
+
from streamlit.runtime.memory_session_storage import MemorySessionStorage
|
43
|
+
from streamlit.runtime.runtime_util import is_cacheable_msg
|
44
|
+
from streamlit.runtime.script_data import ScriptData
|
45
|
+
from streamlit.runtime.scriptrunner.script_cache import ScriptCache
|
46
|
+
from streamlit.runtime.session_manager import (
|
47
|
+
ActiveSessionInfo,
|
48
|
+
SessionClient,
|
49
|
+
SessionClientDisconnectedError,
|
50
|
+
SessionManager,
|
51
|
+
SessionStorage,
|
52
|
+
)
|
53
|
+
from streamlit.runtime.state import (
|
54
|
+
SCRIPT_RUN_WITHOUT_ERRORS_KEY,
|
55
|
+
SessionStateStatProvider,
|
56
|
+
)
|
57
|
+
from streamlit.runtime.stats import StatsManager
|
58
|
+
from streamlit.runtime.websocket_session_manager import WebsocketSessionManager
|
59
|
+
|
60
|
+
if TYPE_CHECKING:
|
61
|
+
from collections.abc import Awaitable
|
62
|
+
|
63
|
+
from streamlit.components.types.base_component_registry import BaseComponentRegistry
|
64
|
+
from streamlit.proto.BackMsg_pb2 import BackMsg
|
65
|
+
from streamlit.runtime.caching.storage import CacheStorageManager
|
66
|
+
from streamlit.runtime.media_file_storage import MediaFileStorage
|
67
|
+
from streamlit.runtime.uploaded_file_manager import UploadedFileManager
|
68
|
+
|
69
|
+
# Wait for the script run result for 60s and if no result is available give up
|
70
|
+
SCRIPT_RUN_CHECK_TIMEOUT: Final = 60
|
71
|
+
|
72
|
+
_LOGGER: Final = get_logger(__name__)
|
73
|
+
|
74
|
+
|
75
|
+
class RuntimeStoppedError(Exception):
|
76
|
+
"""Raised by operations on a Runtime instance that is stopped."""
|
77
|
+
|
78
|
+
|
79
|
+
@dataclass(frozen=True)
|
80
|
+
class RuntimeConfig:
|
81
|
+
"""Config options for StreamlitRuntime."""
|
82
|
+
|
83
|
+
# The filesystem path of the Streamlit script to run.
|
84
|
+
script_path: str
|
85
|
+
|
86
|
+
# DEPRECATED: We need to keep this field around for compatibility reasons, but we no
|
87
|
+
# longer use this anywhere.
|
88
|
+
command_line: str | None
|
89
|
+
|
90
|
+
# The storage backend for Streamlit's MediaFileManager.
|
91
|
+
media_file_storage: MediaFileStorage
|
92
|
+
|
93
|
+
# The upload file manager
|
94
|
+
uploaded_file_manager: UploadedFileManager
|
95
|
+
|
96
|
+
# The cache storage backend for Streamlit's st.cache_data.
|
97
|
+
cache_storage_manager: CacheStorageManager = field(
|
98
|
+
default_factory=LocalDiskCacheStorageManager
|
99
|
+
)
|
100
|
+
|
101
|
+
# The ComponentRegistry instance to use.
|
102
|
+
component_registry: BaseComponentRegistry = field(
|
103
|
+
default_factory=LocalComponentRegistry
|
104
|
+
)
|
105
|
+
|
106
|
+
# The SessionManager class to be used.
|
107
|
+
session_manager_class: type[SessionManager] = WebsocketSessionManager
|
108
|
+
|
109
|
+
# The SessionStorage instance for the SessionManager to use.
|
110
|
+
session_storage: SessionStorage = field(default_factory=MemorySessionStorage)
|
111
|
+
|
112
|
+
# True if the command used to start Streamlit was `streamlit hello`.
|
113
|
+
is_hello: bool = False
|
114
|
+
|
115
|
+
# TODO(vdonato): Eventually add a new fragment_storage_class field enabling the code
|
116
|
+
# creating a new Streamlit Runtime to configure the FragmentStorage instances
|
117
|
+
# created by each new AppSession. We choose not to do this for now to avoid adding
|
118
|
+
# additional complexity to RuntimeConfig/SessionManager/etc when it's unlikely
|
119
|
+
# we'll have a custom implementation of this class anytime soon.
|
120
|
+
|
121
|
+
|
122
|
+
class RuntimeState(Enum):
|
123
|
+
INITIAL = "INITIAL"
|
124
|
+
NO_SESSIONS_CONNECTED = "NO_SESSIONS_CONNECTED"
|
125
|
+
ONE_OR_MORE_SESSIONS_CONNECTED = "ONE_OR_MORE_SESSIONS_CONNECTED"
|
126
|
+
STOPPING = "STOPPING"
|
127
|
+
STOPPED = "STOPPED"
|
128
|
+
|
129
|
+
|
130
|
+
class AsyncObjects(NamedTuple):
|
131
|
+
"""Container for all asyncio objects that Runtime manages.
|
132
|
+
These cannot be initialized until the Runtime's eventloop is assigned.
|
133
|
+
"""
|
134
|
+
|
135
|
+
# The eventloop that Runtime is running on.
|
136
|
+
eventloop: asyncio.AbstractEventLoop
|
137
|
+
|
138
|
+
# Set after Runtime.stop() is called. Never cleared.
|
139
|
+
must_stop: asyncio.Event
|
140
|
+
|
141
|
+
# Set when a client connects; cleared when we have no connected clients.
|
142
|
+
has_connection: asyncio.Event
|
143
|
+
|
144
|
+
# Set after a ForwardMsg is enqueued; cleared when we flush ForwardMsgs.
|
145
|
+
need_send_data: asyncio.Event
|
146
|
+
|
147
|
+
# Completed when the Runtime has started.
|
148
|
+
started: asyncio.Future[None]
|
149
|
+
|
150
|
+
# Completed when the Runtime has stopped.
|
151
|
+
stopped: asyncio.Future[None]
|
152
|
+
|
153
|
+
|
154
|
+
class Runtime:
|
155
|
+
_instance: Runtime | None = None
|
156
|
+
|
157
|
+
@classmethod
|
158
|
+
def instance(cls) -> Runtime:
|
159
|
+
"""Return the singleton Runtime instance. Raise an Error if the
|
160
|
+
Runtime hasn't been created yet.
|
161
|
+
"""
|
162
|
+
if cls._instance is None:
|
163
|
+
raise RuntimeError("Runtime hasn't been created!")
|
164
|
+
return cls._instance
|
165
|
+
|
166
|
+
@classmethod
|
167
|
+
def exists(cls) -> bool:
|
168
|
+
"""True if the singleton Runtime instance has been created.
|
169
|
+
|
170
|
+
When a Streamlit app is running in "raw mode" - that is, when the
|
171
|
+
app is run via `python app.py` instead of `streamlit run app.py` -
|
172
|
+
the Runtime will not exist, and various Streamlit functions need
|
173
|
+
to adapt.
|
174
|
+
"""
|
175
|
+
return cls._instance is not None
|
176
|
+
|
177
|
+
def __init__(self, config: RuntimeConfig):
|
178
|
+
"""Create a Runtime instance. It won't be started yet.
|
179
|
+
|
180
|
+
Runtime is *not* thread-safe. Its public methods are generally
|
181
|
+
safe to call only on the same thread that its event loop runs on.
|
182
|
+
|
183
|
+
Parameters
|
184
|
+
----------
|
185
|
+
config
|
186
|
+
Config options.
|
187
|
+
"""
|
188
|
+
if Runtime._instance is not None:
|
189
|
+
raise RuntimeError("Runtime instance already exists!")
|
190
|
+
Runtime._instance = self
|
191
|
+
|
192
|
+
# Will be created when we start.
|
193
|
+
self._async_objs: AsyncObjects | None = None
|
194
|
+
|
195
|
+
# The task that runs our main loop. We need to save a reference
|
196
|
+
# to it so that it doesn't get garbage collected while running.
|
197
|
+
self._loop_coroutine_task: asyncio.Task[None] | None = None
|
198
|
+
|
199
|
+
self._main_script_path = config.script_path
|
200
|
+
self._is_hello = config.is_hello
|
201
|
+
|
202
|
+
self._state = RuntimeState.INITIAL
|
203
|
+
|
204
|
+
# Initialize managers
|
205
|
+
self._component_registry = config.component_registry
|
206
|
+
self._message_cache = ForwardMsgCache()
|
207
|
+
self._uploaded_file_mgr = config.uploaded_file_manager
|
208
|
+
self._media_file_mgr = MediaFileManager(storage=config.media_file_storage)
|
209
|
+
self._cache_storage_manager = config.cache_storage_manager
|
210
|
+
self._script_cache = ScriptCache()
|
211
|
+
|
212
|
+
self._session_mgr = config.session_manager_class(
|
213
|
+
session_storage=config.session_storage,
|
214
|
+
uploaded_file_manager=self._uploaded_file_mgr,
|
215
|
+
script_cache=self._script_cache,
|
216
|
+
message_enqueued_callback=self._enqueued_some_message,
|
217
|
+
)
|
218
|
+
|
219
|
+
self._stats_mgr = StatsManager()
|
220
|
+
self._stats_mgr.register_provider(get_data_cache_stats_provider())
|
221
|
+
self._stats_mgr.register_provider(get_resource_cache_stats_provider())
|
222
|
+
self._stats_mgr.register_provider(self._message_cache)
|
223
|
+
self._stats_mgr.register_provider(self._uploaded_file_mgr)
|
224
|
+
self._stats_mgr.register_provider(SessionStateStatProvider(self._session_mgr))
|
225
|
+
|
226
|
+
@property
|
227
|
+
def state(self) -> RuntimeState:
|
228
|
+
return self._state
|
229
|
+
|
230
|
+
@property
|
231
|
+
def component_registry(self) -> BaseComponentRegistry:
|
232
|
+
return self._component_registry
|
233
|
+
|
234
|
+
@property
|
235
|
+
def message_cache(self) -> ForwardMsgCache:
|
236
|
+
return self._message_cache
|
237
|
+
|
238
|
+
@property
|
239
|
+
def uploaded_file_mgr(self) -> UploadedFileManager:
|
240
|
+
return self._uploaded_file_mgr
|
241
|
+
|
242
|
+
@property
|
243
|
+
def cache_storage_manager(self) -> CacheStorageManager:
|
244
|
+
return self._cache_storage_manager
|
245
|
+
|
246
|
+
@property
|
247
|
+
def media_file_mgr(self) -> MediaFileManager:
|
248
|
+
return self._media_file_mgr
|
249
|
+
|
250
|
+
@property
|
251
|
+
def stats_mgr(self) -> StatsManager:
|
252
|
+
return self._stats_mgr
|
253
|
+
|
254
|
+
@property
|
255
|
+
def stopped(self) -> Awaitable[None]:
|
256
|
+
"""A Future that completes when the Runtime's run loop has exited."""
|
257
|
+
return self._get_async_objs().stopped
|
258
|
+
|
259
|
+
# NOTE: A few Runtime methods listed as threadsafe (get_client and
|
260
|
+
# is_active_session) currently rely on the implementation detail that
|
261
|
+
# WebsocketSessionManager's get_active_session_info and is_active_session methods
|
262
|
+
# happen to be threadsafe. This may change with future SessionManager implementations,
|
263
|
+
# at which point we'll need to formalize our thread safety rules for each
|
264
|
+
# SessionManager method.
|
265
|
+
def get_client(self, session_id: str) -> SessionClient | None:
|
266
|
+
"""Get the SessionClient for the given session_id, or None
|
267
|
+
if no such session exists.
|
268
|
+
|
269
|
+
Notes
|
270
|
+
-----
|
271
|
+
Threading: SAFE. May be called on any thread.
|
272
|
+
"""
|
273
|
+
session_info = self._session_mgr.get_active_session_info(session_id)
|
274
|
+
if session_info is None:
|
275
|
+
return None
|
276
|
+
return session_info.client
|
277
|
+
|
278
|
+
def clear_user_info_for_session(self, session_id: str) -> None:
|
279
|
+
"""Clear the user_info for the given session_id.
|
280
|
+
|
281
|
+
Notes
|
282
|
+
-----
|
283
|
+
Threading: SAFE. May be called on any thread.
|
284
|
+
"""
|
285
|
+
session_info = self._session_mgr.get_session_info(session_id)
|
286
|
+
if session_info is not None:
|
287
|
+
session_info.session.clear_user_info()
|
288
|
+
|
289
|
+
async def start(self) -> None:
|
290
|
+
"""Start the runtime. This must be called only once, before
|
291
|
+
any other functions are called.
|
292
|
+
|
293
|
+
When this coroutine returns, Streamlit is ready to accept new sessions.
|
294
|
+
|
295
|
+
Notes
|
296
|
+
-----
|
297
|
+
Threading: UNSAFE. Must be called on the eventloop thread.
|
298
|
+
"""
|
299
|
+
|
300
|
+
# Create our AsyncObjects. We need to have a running eventloop to
|
301
|
+
# instantiate our various synchronization primitives.
|
302
|
+
async_objs = AsyncObjects(
|
303
|
+
eventloop=asyncio.get_running_loop(),
|
304
|
+
must_stop=asyncio.Event(),
|
305
|
+
has_connection=asyncio.Event(),
|
306
|
+
need_send_data=asyncio.Event(),
|
307
|
+
started=asyncio.Future(),
|
308
|
+
stopped=asyncio.Future(),
|
309
|
+
)
|
310
|
+
self._async_objs = async_objs
|
311
|
+
|
312
|
+
self._loop_coroutine_task = asyncio.create_task(
|
313
|
+
self._loop_coroutine(), name="Runtime.loop_coroutine"
|
314
|
+
)
|
315
|
+
|
316
|
+
await async_objs.started
|
317
|
+
|
318
|
+
def stop(self) -> None:
|
319
|
+
"""Request that Streamlit close all sessions and stop running.
|
320
|
+
Note that Streamlit won't stop running immediately.
|
321
|
+
|
322
|
+
Notes
|
323
|
+
-----
|
324
|
+
Threading: SAFE. May be called from any thread.
|
325
|
+
"""
|
326
|
+
|
327
|
+
async_objs = self._get_async_objs()
|
328
|
+
|
329
|
+
def stop_on_eventloop():
|
330
|
+
if self._state in (RuntimeState.STOPPING, RuntimeState.STOPPED):
|
331
|
+
return
|
332
|
+
|
333
|
+
_LOGGER.debug("Runtime stopping...")
|
334
|
+
self._set_state(RuntimeState.STOPPING)
|
335
|
+
async_objs.must_stop.set()
|
336
|
+
|
337
|
+
async_objs.eventloop.call_soon_threadsafe(stop_on_eventloop)
|
338
|
+
|
339
|
+
def is_active_session(self, session_id: str) -> bool:
|
340
|
+
"""True if the session_id belongs to an active session.
|
341
|
+
|
342
|
+
Notes
|
343
|
+
-----
|
344
|
+
Threading: SAFE. May be called on any thread.
|
345
|
+
"""
|
346
|
+
return self._session_mgr.is_active_session(session_id)
|
347
|
+
|
348
|
+
def connect_session(
|
349
|
+
self,
|
350
|
+
client: SessionClient,
|
351
|
+
user_info: dict[str, str | bool | None],
|
352
|
+
existing_session_id: str | None = None,
|
353
|
+
session_id_override: str | None = None,
|
354
|
+
) -> str:
|
355
|
+
"""Create a new session (or connect to an existing one) and return its unique ID.
|
356
|
+
|
357
|
+
Parameters
|
358
|
+
----------
|
359
|
+
client
|
360
|
+
A concrete SessionClient implementation for communicating with
|
361
|
+
the session's client.
|
362
|
+
user_info
|
363
|
+
A dict that contains information about the session's user. For now,
|
364
|
+
it only (optionally) contains the user's email address.
|
365
|
+
|
366
|
+
{
|
367
|
+
"email": "example@example.com"
|
368
|
+
}
|
369
|
+
existing_session_id
|
370
|
+
The ID of an existing session to reconnect to. If one is not provided, a new
|
371
|
+
session is created. Note that whether the Runtime's SessionManager supports
|
372
|
+
reconnecting to an existing session depends on the SessionManager that this
|
373
|
+
runtime is configured with.
|
374
|
+
session_id_override
|
375
|
+
The ID to assign to a new session being created with this method. Setting
|
376
|
+
this can be useful when the service that a Streamlit Runtime is running in
|
377
|
+
wants to tie the lifecycle of a Streamlit session to some other session-like
|
378
|
+
object that it manages. Only one of existing_session_id and
|
379
|
+
session_id_override should be set.
|
380
|
+
|
381
|
+
Returns
|
382
|
+
-------
|
383
|
+
str
|
384
|
+
The session's unique string ID.
|
385
|
+
|
386
|
+
Notes
|
387
|
+
-----
|
388
|
+
Threading: UNSAFE. Must be called on the eventloop thread.
|
389
|
+
"""
|
390
|
+
assert not (existing_session_id and session_id_override), (
|
391
|
+
"Only one of existing_session_id and session_id_override should be set!"
|
392
|
+
)
|
393
|
+
|
394
|
+
if self._state in (RuntimeState.STOPPING, RuntimeState.STOPPED):
|
395
|
+
raise RuntimeStoppedError(f"Can't connect_session (state={self._state})")
|
396
|
+
|
397
|
+
session_id = self._session_mgr.connect_session(
|
398
|
+
client=client,
|
399
|
+
script_data=ScriptData(self._main_script_path, self._is_hello),
|
400
|
+
user_info=user_info,
|
401
|
+
existing_session_id=existing_session_id,
|
402
|
+
session_id_override=session_id_override,
|
403
|
+
)
|
404
|
+
self._set_state(RuntimeState.ONE_OR_MORE_SESSIONS_CONNECTED)
|
405
|
+
self._get_async_objs().has_connection.set()
|
406
|
+
|
407
|
+
return session_id
|
408
|
+
|
409
|
+
def create_session(
|
410
|
+
self,
|
411
|
+
client: SessionClient,
|
412
|
+
user_info: dict[str, str | bool | None],
|
413
|
+
existing_session_id: str | None = None,
|
414
|
+
session_id_override: str | None = None,
|
415
|
+
) -> str:
|
416
|
+
"""Create a new session (or connect to an existing one) and return its unique ID.
|
417
|
+
|
418
|
+
Notes
|
419
|
+
-----
|
420
|
+
This method is simply an alias for connect_session added for backwards
|
421
|
+
compatibility.
|
422
|
+
"""
|
423
|
+
_LOGGER.warning("create_session is deprecated! Use connect_session instead.")
|
424
|
+
return self.connect_session(
|
425
|
+
client=client,
|
426
|
+
user_info=user_info,
|
427
|
+
existing_session_id=existing_session_id,
|
428
|
+
session_id_override=session_id_override,
|
429
|
+
)
|
430
|
+
|
431
|
+
def close_session(self, session_id: str) -> None:
|
432
|
+
"""Close and completely shut down a session.
|
433
|
+
|
434
|
+
This differs from disconnect_session in that it always completely shuts down a
|
435
|
+
session, permanently losing any associated state (session state, uploaded files,
|
436
|
+
etc.).
|
437
|
+
|
438
|
+
This function may be called multiple times for the same session,
|
439
|
+
which is not an error. (Subsequent calls just no-op.)
|
440
|
+
|
441
|
+
Parameters
|
442
|
+
----------
|
443
|
+
session_id
|
444
|
+
The session's unique ID.
|
445
|
+
|
446
|
+
Notes
|
447
|
+
-----
|
448
|
+
Threading: UNSAFE. Must be called on the eventloop thread.
|
449
|
+
"""
|
450
|
+
session_info = self._session_mgr.get_session_info(session_id)
|
451
|
+
if session_info:
|
452
|
+
self._message_cache.remove_refs_for_session(session_info.session)
|
453
|
+
self._session_mgr.close_session(session_id)
|
454
|
+
self._on_session_disconnected()
|
455
|
+
|
456
|
+
def disconnect_session(self, session_id: str) -> None:
|
457
|
+
"""Disconnect a session. It will stop producing ForwardMsgs.
|
458
|
+
|
459
|
+
Differs from close_session because disconnected sessions can be reconnected to
|
460
|
+
for a brief window (depending on the SessionManager/SessionStorage
|
461
|
+
implementations used by the runtime).
|
462
|
+
|
463
|
+
This function may be called multiple times for the same session,
|
464
|
+
which is not an error. (Subsequent calls just no-op.)
|
465
|
+
|
466
|
+
Parameters
|
467
|
+
----------
|
468
|
+
session_id
|
469
|
+
The session's unique ID.
|
470
|
+
|
471
|
+
Notes
|
472
|
+
-----
|
473
|
+
Threading: UNSAFE. Must be called on the eventloop thread.
|
474
|
+
"""
|
475
|
+
session_info = self._session_mgr.get_active_session_info(session_id)
|
476
|
+
if session_info:
|
477
|
+
# NOTE: Ideally, we'd like to keep ForwardMsgCache refs for a session around
|
478
|
+
# when a session is disconnected (and defer their cleanup until the session
|
479
|
+
# is garbage collected), but this would be difficult to do as the
|
480
|
+
# ForwardMsgCache is not thread safe, and we have no guarantee that the
|
481
|
+
# garbage collector will only run on the eventloop thread. Because of this,
|
482
|
+
# we clean up refs now and accept the risk that we're deleting cache entries
|
483
|
+
# that will be useful once the browser tab reconnects.
|
484
|
+
self._message_cache.remove_refs_for_session(session_info.session)
|
485
|
+
self._session_mgr.disconnect_session(session_id)
|
486
|
+
self._on_session_disconnected()
|
487
|
+
|
488
|
+
def handle_backmsg(self, session_id: str, msg: BackMsg) -> None:
|
489
|
+
"""Send a BackMsg to an active session.
|
490
|
+
|
491
|
+
Parameters
|
492
|
+
----------
|
493
|
+
session_id
|
494
|
+
The session's unique ID.
|
495
|
+
msg
|
496
|
+
The BackMsg to deliver to the session.
|
497
|
+
|
498
|
+
Notes
|
499
|
+
-----
|
500
|
+
Threading: UNSAFE. Must be called on the eventloop thread.
|
501
|
+
"""
|
502
|
+
if self._state in (RuntimeState.STOPPING, RuntimeState.STOPPED):
|
503
|
+
raise RuntimeStoppedError(f"Can't handle_backmsg (state={self._state})")
|
504
|
+
|
505
|
+
session_info = self._session_mgr.get_active_session_info(session_id)
|
506
|
+
if session_info is None:
|
507
|
+
_LOGGER.debug(
|
508
|
+
"Discarding BackMsg for disconnected session (id=%s)", session_id
|
509
|
+
)
|
510
|
+
return
|
511
|
+
|
512
|
+
session_info.session.handle_backmsg(msg)
|
513
|
+
|
514
|
+
def handle_backmsg_deserialization_exception(
|
515
|
+
self, session_id: str, exc: BaseException
|
516
|
+
) -> None:
|
517
|
+
"""Handle an Exception raised during deserialization of a BackMsg.
|
518
|
+
|
519
|
+
Parameters
|
520
|
+
----------
|
521
|
+
session_id
|
522
|
+
The session's unique ID.
|
523
|
+
exc
|
524
|
+
The Exception.
|
525
|
+
|
526
|
+
Notes
|
527
|
+
-----
|
528
|
+
Threading: UNSAFE. Must be called on the eventloop thread.
|
529
|
+
"""
|
530
|
+
if self._state in (RuntimeState.STOPPING, RuntimeState.STOPPED):
|
531
|
+
raise RuntimeStoppedError(
|
532
|
+
f"Can't handle_backmsg_deserialization_exception (state={self._state})"
|
533
|
+
)
|
534
|
+
|
535
|
+
session_info = self._session_mgr.get_active_session_info(session_id)
|
536
|
+
if session_info is None:
|
537
|
+
_LOGGER.debug(
|
538
|
+
"Discarding BackMsg Exception for disconnected session (id=%s)",
|
539
|
+
session_id,
|
540
|
+
)
|
541
|
+
return
|
542
|
+
|
543
|
+
session_info.session.handle_backmsg_exception(exc)
|
544
|
+
|
545
|
+
@property
|
546
|
+
async def is_ready_for_browser_connection(self) -> tuple[bool, str]:
|
547
|
+
if self._state not in (
|
548
|
+
RuntimeState.INITIAL,
|
549
|
+
RuntimeState.STOPPING,
|
550
|
+
RuntimeState.STOPPED,
|
551
|
+
):
|
552
|
+
return True, "ok"
|
553
|
+
|
554
|
+
return False, "unavailable"
|
555
|
+
|
556
|
+
async def does_script_run_without_error(self) -> tuple[bool, str]:
|
557
|
+
"""Load and execute the app's script to verify it runs without an error.
|
558
|
+
|
559
|
+
Returns
|
560
|
+
-------
|
561
|
+
(True, "ok") if the script completes without error, or (False, err_msg)
|
562
|
+
if the script raises an exception.
|
563
|
+
|
564
|
+
Notes
|
565
|
+
-----
|
566
|
+
Threading: UNSAFE. Must be called on the eventloop thread.
|
567
|
+
"""
|
568
|
+
# NOTE: We create an AppSession directly here instead of using the
|
569
|
+
# SessionManager intentionally. This isn't a "real" session and is only being
|
570
|
+
# used to test that the script runs without error.
|
571
|
+
session = AppSession(
|
572
|
+
script_data=ScriptData(self._main_script_path, self._is_hello),
|
573
|
+
uploaded_file_manager=self._uploaded_file_mgr,
|
574
|
+
script_cache=self._script_cache,
|
575
|
+
message_enqueued_callback=self._enqueued_some_message,
|
576
|
+
user_info={"email": "test@example.com"},
|
577
|
+
)
|
578
|
+
|
579
|
+
try:
|
580
|
+
session.request_rerun(None)
|
581
|
+
|
582
|
+
now = time.perf_counter()
|
583
|
+
while ( # noqa: ASYNC110
|
584
|
+
SCRIPT_RUN_WITHOUT_ERRORS_KEY not in session.session_state
|
585
|
+
and (time.perf_counter() - now) < SCRIPT_RUN_CHECK_TIMEOUT
|
586
|
+
):
|
587
|
+
await asyncio.sleep(0.1)
|
588
|
+
|
589
|
+
if SCRIPT_RUN_WITHOUT_ERRORS_KEY not in session.session_state:
|
590
|
+
return False, "timeout"
|
591
|
+
|
592
|
+
ok = session.session_state[SCRIPT_RUN_WITHOUT_ERRORS_KEY]
|
593
|
+
msg = "ok" if ok else "error"
|
594
|
+
|
595
|
+
return ok, msg
|
596
|
+
finally:
|
597
|
+
session.shutdown()
|
598
|
+
|
599
|
+
def _set_state(self, new_state: RuntimeState) -> None:
|
600
|
+
_LOGGER.debug("Runtime state: %s -> %s", self._state, new_state)
|
601
|
+
self._state = new_state
|
602
|
+
|
603
|
+
async def _loop_coroutine(self) -> None:
|
604
|
+
"""The main Runtime loop.
|
605
|
+
|
606
|
+
This function won't exit until `stop` is called.
|
607
|
+
|
608
|
+
Notes
|
609
|
+
-----
|
610
|
+
Threading: UNSAFE. Must be called on the eventloop thread.
|
611
|
+
"""
|
612
|
+
|
613
|
+
async_objs = self._get_async_objs()
|
614
|
+
|
615
|
+
try:
|
616
|
+
if self._state == RuntimeState.INITIAL:
|
617
|
+
self._set_state(RuntimeState.NO_SESSIONS_CONNECTED)
|
618
|
+
elif self._state == RuntimeState.ONE_OR_MORE_SESSIONS_CONNECTED:
|
619
|
+
pass
|
620
|
+
else:
|
621
|
+
raise RuntimeError(f"Bad Runtime state at start: {self._state}")
|
622
|
+
|
623
|
+
# Signal that we're started and ready to accept sessions
|
624
|
+
async_objs.started.set_result(None)
|
625
|
+
|
626
|
+
while not async_objs.must_stop.is_set():
|
627
|
+
if self._state == RuntimeState.NO_SESSIONS_CONNECTED: # type: ignore[comparison-overlap]
|
628
|
+
# mypy 1.4 incorrectly thinks this if-clause is unreachable,
|
629
|
+
# because it thinks self._state must be INITIAL | ONE_OR_MORE_SESSIONS_CONNECTED.
|
630
|
+
|
631
|
+
# Wait for new websocket connections (new sessions):
|
632
|
+
_, pending_tasks = await asyncio.wait( # type: ignore[unreachable]
|
633
|
+
(
|
634
|
+
asyncio.create_task(async_objs.must_stop.wait()),
|
635
|
+
asyncio.create_task(async_objs.has_connection.wait()),
|
636
|
+
),
|
637
|
+
return_when=asyncio.FIRST_COMPLETED,
|
638
|
+
)
|
639
|
+
# Clean up pending tasks to avoid memory leaks
|
640
|
+
for task in pending_tasks:
|
641
|
+
task.cancel()
|
642
|
+
elif self._state == RuntimeState.ONE_OR_MORE_SESSIONS_CONNECTED:
|
643
|
+
async_objs.need_send_data.clear()
|
644
|
+
|
645
|
+
for active_session_info in self._session_mgr.list_active_sessions():
|
646
|
+
msg_list = active_session_info.session.flush_browser_queue()
|
647
|
+
for msg in msg_list:
|
648
|
+
try:
|
649
|
+
self._send_message(active_session_info, msg)
|
650
|
+
except SessionClientDisconnectedError:
|
651
|
+
self._session_mgr.disconnect_session(
|
652
|
+
active_session_info.session.id
|
653
|
+
)
|
654
|
+
|
655
|
+
# Yield for a tick after sending a message.
|
656
|
+
await asyncio.sleep(0)
|
657
|
+
|
658
|
+
# Yield for a few milliseconds between session message
|
659
|
+
# flushing.
|
660
|
+
await asyncio.sleep(0.01)
|
661
|
+
else:
|
662
|
+
# Break out of the thread loop if we encounter any other state.
|
663
|
+
break
|
664
|
+
|
665
|
+
# Wait for new proto messages that need to be sent out:
|
666
|
+
_, pending_tasks = await asyncio.wait(
|
667
|
+
(
|
668
|
+
asyncio.create_task(async_objs.must_stop.wait()),
|
669
|
+
asyncio.create_task(async_objs.need_send_data.wait()),
|
670
|
+
),
|
671
|
+
return_when=asyncio.FIRST_COMPLETED,
|
672
|
+
)
|
673
|
+
# We need to cancel the pending tasks (the `must_stop` one in most situations).
|
674
|
+
# Otherwise, this would stack up one waiting task per loop
|
675
|
+
# (e.g. per forward message). These tasks cannot be garbage collected
|
676
|
+
# causing an increase in memory (-> memory leak).
|
677
|
+
for task in pending_tasks:
|
678
|
+
task.cancel()
|
679
|
+
|
680
|
+
# Shut down all AppSessions.
|
681
|
+
for session_info in self._session_mgr.list_sessions():
|
682
|
+
# NOTE: We want to fully shut down sessions when the runtime stops for
|
683
|
+
# now, but this may change in the future if/when our notion of a session
|
684
|
+
# is no longer so tightly coupled to a browser tab.
|
685
|
+
self._session_mgr.close_session(session_info.session.id)
|
686
|
+
|
687
|
+
self._set_state(RuntimeState.STOPPED)
|
688
|
+
async_objs.stopped.set_result(None)
|
689
|
+
|
690
|
+
except Exception as e:
|
691
|
+
async_objs.stopped.set_exception(e)
|
692
|
+
traceback.print_exc()
|
693
|
+
_LOGGER.info(
|
694
|
+
"""
|
695
|
+
Please report this bug at https://github.com/streamlit/streamlit/issues.
|
696
|
+
"""
|
697
|
+
)
|
698
|
+
|
699
|
+
def _send_message(self, session_info: ActiveSessionInfo, msg: ForwardMsg) -> None:
|
700
|
+
"""Send a message to a client.
|
701
|
+
|
702
|
+
If the client is likely to have already cached the message, we may
|
703
|
+
instead send a "reference" message that contains only the hash of the
|
704
|
+
message.
|
705
|
+
|
706
|
+
Parameters
|
707
|
+
----------
|
708
|
+
session_info : ActiveSessionInfo
|
709
|
+
The ActiveSessionInfo associated with websocket
|
710
|
+
msg : ForwardMsg
|
711
|
+
The message to send to the client
|
712
|
+
|
713
|
+
Notes
|
714
|
+
-----
|
715
|
+
Threading: UNSAFE. Must be called on the eventloop thread.
|
716
|
+
"""
|
717
|
+
msg.metadata.cacheable = is_cacheable_msg(msg)
|
718
|
+
msg_to_send = msg
|
719
|
+
if msg.metadata.cacheable:
|
720
|
+
populate_hash_if_needed(msg)
|
721
|
+
|
722
|
+
if self._message_cache.has_message_reference(
|
723
|
+
msg, session_info.session, session_info.script_run_count
|
724
|
+
):
|
725
|
+
# This session has probably cached this message. Send
|
726
|
+
# a reference instead.
|
727
|
+
_LOGGER.debug("Sending cached message ref (hash=%s)", msg.hash)
|
728
|
+
msg_to_send = create_reference_msg(msg)
|
729
|
+
|
730
|
+
# Cache the message so it can be referenced in the future.
|
731
|
+
# If the message is already cached, this will reset its
|
732
|
+
# age.
|
733
|
+
_LOGGER.debug("Caching message (hash=%s)", msg.hash)
|
734
|
+
self._message_cache.add_message(
|
735
|
+
msg, session_info.session, session_info.script_run_count
|
736
|
+
)
|
737
|
+
|
738
|
+
# If this was a `script_finished` message, we increment the
|
739
|
+
# script_run_count for this session, and update the cache
|
740
|
+
if msg.WhichOneof("type") == "script_finished" and (
|
741
|
+
msg.script_finished == ForwardMsg.FINISHED_SUCCESSFULLY
|
742
|
+
or (
|
743
|
+
config.get_option(
|
744
|
+
"global.includeFragmentRunsInForwardMessageCacheCount"
|
745
|
+
)
|
746
|
+
and msg.script_finished == ForwardMsg.FINISHED_FRAGMENT_RUN_SUCCESSFULLY
|
747
|
+
)
|
748
|
+
):
|
749
|
+
_LOGGER.debug(
|
750
|
+
"Script run finished successfully; "
|
751
|
+
"removing expired entries from MessageCache "
|
752
|
+
"(max_age=%s)",
|
753
|
+
config.get_option("global.maxCachedMessageAge"),
|
754
|
+
)
|
755
|
+
session_info.script_run_count += 1
|
756
|
+
self._message_cache.remove_expired_entries_for_session(
|
757
|
+
session_info.session, session_info.script_run_count
|
758
|
+
)
|
759
|
+
|
760
|
+
# Ship it off!
|
761
|
+
session_info.client.write_forward_msg(msg_to_send)
|
762
|
+
|
763
|
+
def _enqueued_some_message(self) -> None:
|
764
|
+
"""Callback called by AppSession after the AppSession has enqueued a
|
765
|
+
message. Sets the "needs_send_data" event, which causes our core
|
766
|
+
loop to wake up and flush client message queues.
|
767
|
+
|
768
|
+
Notes
|
769
|
+
-----
|
770
|
+
Threading: SAFE. May be called on any thread.
|
771
|
+
"""
|
772
|
+
async_objs = self._get_async_objs()
|
773
|
+
async_objs.eventloop.call_soon_threadsafe(async_objs.need_send_data.set)
|
774
|
+
|
775
|
+
def _get_async_objs(self) -> AsyncObjects:
|
776
|
+
"""Return our AsyncObjects instance. If the Runtime hasn't been
|
777
|
+
started, this will raise an error.
|
778
|
+
"""
|
779
|
+
if self._async_objs is None:
|
780
|
+
raise RuntimeError("Runtime hasn't started yet!")
|
781
|
+
return self._async_objs
|
782
|
+
|
783
|
+
def _on_session_disconnected(self) -> None:
|
784
|
+
"""Set the runtime state to NO_SESSIONS_CONNECTED if the last active
|
785
|
+
session was disconnected.
|
786
|
+
"""
|
787
|
+
if (
|
788
|
+
self._state == RuntimeState.ONE_OR_MORE_SESSIONS_CONNECTED
|
789
|
+
and self._session_mgr.num_active_sessions() == 0
|
790
|
+
):
|
791
|
+
self._get_async_objs().has_connection.clear()
|
792
|
+
self._set_state(RuntimeState.NO_SESSIONS_CONNECTED)
|