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,234 @@
|
|
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
|
+
"""Provides global MediaFileManager object as `media_file_manager`."""
|
16
|
+
|
17
|
+
from __future__ import annotations
|
18
|
+
|
19
|
+
import collections
|
20
|
+
import threading
|
21
|
+
from typing import Final
|
22
|
+
|
23
|
+
from streamlit.logger import get_logger
|
24
|
+
from streamlit.runtime.media_file_storage import MediaFileKind, MediaFileStorage
|
25
|
+
|
26
|
+
_LOGGER: Final = get_logger(__name__)
|
27
|
+
|
28
|
+
|
29
|
+
def _get_session_id() -> str:
|
30
|
+
"""Get the active AppSession's session_id."""
|
31
|
+
from streamlit.runtime.scriptrunner_utils.script_run_context import (
|
32
|
+
get_script_run_ctx,
|
33
|
+
)
|
34
|
+
|
35
|
+
ctx = get_script_run_ctx()
|
36
|
+
if ctx is None:
|
37
|
+
# This is only None when running "python myscript.py" rather than
|
38
|
+
# "streamlit run myscript.py". In which case the session ID doesn't
|
39
|
+
# matter and can just be a constant, as there's only ever "session".
|
40
|
+
return "dontcare"
|
41
|
+
else:
|
42
|
+
return ctx.session_id
|
43
|
+
|
44
|
+
|
45
|
+
class MediaFileMetadata:
|
46
|
+
"""Metadata that the MediaFileManager needs for each file it manages."""
|
47
|
+
|
48
|
+
def __init__(self, kind: MediaFileKind = MediaFileKind.MEDIA):
|
49
|
+
self._kind = kind
|
50
|
+
self._is_marked_for_delete = False
|
51
|
+
|
52
|
+
@property
|
53
|
+
def kind(self) -> MediaFileKind:
|
54
|
+
return self._kind
|
55
|
+
|
56
|
+
@property
|
57
|
+
def is_marked_for_delete(self) -> bool:
|
58
|
+
return self._is_marked_for_delete
|
59
|
+
|
60
|
+
def mark_for_delete(self) -> None:
|
61
|
+
self._is_marked_for_delete = True
|
62
|
+
|
63
|
+
|
64
|
+
class MediaFileManager:
|
65
|
+
"""In-memory file manager for MediaFile objects.
|
66
|
+
|
67
|
+
This keeps track of:
|
68
|
+
- Which files exist, and what their IDs are. This is important so we can
|
69
|
+
serve files by ID -- that's the whole point of this class!
|
70
|
+
- Which files are being used by which AppSession (by ID). This is
|
71
|
+
important so we can remove files from memory when no more sessions need
|
72
|
+
them.
|
73
|
+
- The exact location in the app where each file is being used (i.e. the
|
74
|
+
file's "coordinates"). This is is important so we can mark a file as "not
|
75
|
+
being used by a certain session" if it gets replaced by another file at
|
76
|
+
the same coordinates. For example, when doing an animation where the same
|
77
|
+
image is constantly replace with new frames. (This doesn't solve the case
|
78
|
+
where the file's coordinates keep changing for some reason, though! e.g.
|
79
|
+
if new elements keep being prepended to the app. Unlikely to happen, but
|
80
|
+
we should address it at some point.)
|
81
|
+
"""
|
82
|
+
|
83
|
+
def __init__(self, storage: MediaFileStorage):
|
84
|
+
self._storage = storage
|
85
|
+
|
86
|
+
# Dict of [file_id -> MediaFileMetadata]
|
87
|
+
self._file_metadata: dict[str, MediaFileMetadata] = {}
|
88
|
+
|
89
|
+
# Dict[session ID][coordinates] -> file_id.
|
90
|
+
self._files_by_session_and_coord: dict[str, dict[str, str]] = (
|
91
|
+
collections.defaultdict(dict)
|
92
|
+
)
|
93
|
+
|
94
|
+
# MediaFileManager is used from multiple threads, so all operations
|
95
|
+
# need to be protected with a Lock. (This is not an RLock, which
|
96
|
+
# means taking it multiple times from the same thread will deadlock.)
|
97
|
+
self._lock = threading.Lock()
|
98
|
+
|
99
|
+
def _get_inactive_file_ids(self) -> set[str]:
|
100
|
+
"""Compute the set of files that are stored in the manager, but are
|
101
|
+
not referenced by any active session. These are files that can be
|
102
|
+
safely deleted.
|
103
|
+
|
104
|
+
Thread safety: callers must hold `self._lock`.
|
105
|
+
"""
|
106
|
+
# Get the set of all our file IDs.
|
107
|
+
file_ids = set(self._file_metadata.keys())
|
108
|
+
|
109
|
+
# Subtract all IDs that are in use by each session
|
110
|
+
for session_file_ids_by_coord in self._files_by_session_and_coord.values():
|
111
|
+
file_ids.difference_update(session_file_ids_by_coord.values())
|
112
|
+
|
113
|
+
return file_ids
|
114
|
+
|
115
|
+
def remove_orphaned_files(self) -> None:
|
116
|
+
"""Remove all files that are no longer referenced by any active session.
|
117
|
+
|
118
|
+
Safe to call from any thread.
|
119
|
+
"""
|
120
|
+
_LOGGER.debug("Removing orphaned files...")
|
121
|
+
|
122
|
+
with self._lock:
|
123
|
+
for file_id in self._get_inactive_file_ids():
|
124
|
+
file = self._file_metadata[file_id]
|
125
|
+
if file.kind == MediaFileKind.MEDIA:
|
126
|
+
self._delete_file(file_id)
|
127
|
+
elif file.kind == MediaFileKind.DOWNLOADABLE:
|
128
|
+
if file.is_marked_for_delete:
|
129
|
+
self._delete_file(file_id)
|
130
|
+
else:
|
131
|
+
file.mark_for_delete()
|
132
|
+
|
133
|
+
def _delete_file(self, file_id: str) -> None:
|
134
|
+
"""Delete the given file from storage, and remove its metadata from
|
135
|
+
self._files_by_id.
|
136
|
+
|
137
|
+
Thread safety: callers must hold `self._lock`.
|
138
|
+
"""
|
139
|
+
_LOGGER.debug("Deleting File: %s", file_id)
|
140
|
+
self._storage.delete_file(file_id)
|
141
|
+
del self._file_metadata[file_id]
|
142
|
+
|
143
|
+
def clear_session_refs(self, session_id: str | None = None) -> None:
|
144
|
+
"""Remove the given session's file references.
|
145
|
+
|
146
|
+
(This does not remove any files from the manager - you must call
|
147
|
+
`remove_orphaned_files` for that.)
|
148
|
+
|
149
|
+
Should be called whenever ScriptRunner completes and when a session ends.
|
150
|
+
|
151
|
+
Safe to call from any thread.
|
152
|
+
"""
|
153
|
+
if session_id is None:
|
154
|
+
session_id = _get_session_id()
|
155
|
+
|
156
|
+
_LOGGER.debug("Disconnecting files for session with ID %s", session_id)
|
157
|
+
|
158
|
+
with self._lock:
|
159
|
+
if session_id in self._files_by_session_and_coord:
|
160
|
+
del self._files_by_session_and_coord[session_id]
|
161
|
+
|
162
|
+
_LOGGER.debug(
|
163
|
+
"Sessions still active: %r", self._files_by_session_and_coord.keys()
|
164
|
+
)
|
165
|
+
|
166
|
+
_LOGGER.debug(
|
167
|
+
"Files: %s; Sessions with files: %s",
|
168
|
+
len(self._file_metadata),
|
169
|
+
len(self._files_by_session_and_coord),
|
170
|
+
)
|
171
|
+
|
172
|
+
def add(
|
173
|
+
self,
|
174
|
+
path_or_data: bytes | str,
|
175
|
+
mimetype: str,
|
176
|
+
coordinates: str,
|
177
|
+
file_name: str | None = None,
|
178
|
+
is_for_static_download: bool = False,
|
179
|
+
) -> str:
|
180
|
+
"""Add a new MediaFile with the given parameters and return its URL.
|
181
|
+
|
182
|
+
If an identical file already exists, return the existing URL
|
183
|
+
and registers the current session as a user.
|
184
|
+
|
185
|
+
Safe to call from any thread.
|
186
|
+
|
187
|
+
Parameters
|
188
|
+
----------
|
189
|
+
path_or_data : bytes or str
|
190
|
+
If bytes: the media file's raw data. If str: the name of a file
|
191
|
+
to load from disk.
|
192
|
+
mimetype : str
|
193
|
+
The mime type for the file. E.g. "audio/mpeg".
|
194
|
+
This string will be used in the "Content-Type" header when the file
|
195
|
+
is served over HTTP.
|
196
|
+
coordinates : str
|
197
|
+
Unique string identifying an element's location.
|
198
|
+
Prevents memory leak of "forgotten" file IDs when element media
|
199
|
+
is being replaced-in-place (e.g. an st.image stream).
|
200
|
+
coordinates should be of the form: "1.(3.-14).5"
|
201
|
+
file_name : str or None
|
202
|
+
Optional file_name. Used to set the filename in the response header.
|
203
|
+
is_for_static_download: bool
|
204
|
+
Indicate that data stored for downloading as a file,
|
205
|
+
not as a media for rendering at page. [default: False]
|
206
|
+
|
207
|
+
Returns
|
208
|
+
-------
|
209
|
+
str
|
210
|
+
The url that the frontend can use to fetch the media.
|
211
|
+
|
212
|
+
Raises
|
213
|
+
------
|
214
|
+
If a filename is passed, any Exception raised when trying to read the
|
215
|
+
file will be re-raised.
|
216
|
+
"""
|
217
|
+
|
218
|
+
session_id = _get_session_id()
|
219
|
+
|
220
|
+
with self._lock:
|
221
|
+
kind = (
|
222
|
+
MediaFileKind.DOWNLOADABLE
|
223
|
+
if is_for_static_download
|
224
|
+
else MediaFileKind.MEDIA
|
225
|
+
)
|
226
|
+
file_id = self._storage.load_and_get_id(
|
227
|
+
path_or_data, mimetype, kind, file_name
|
228
|
+
)
|
229
|
+
metadata = MediaFileMetadata(kind=kind)
|
230
|
+
|
231
|
+
self._file_metadata[file_id] = metadata
|
232
|
+
self._files_by_session_and_coord[session_id][coordinates] = file_id
|
233
|
+
|
234
|
+
return self._storage.get_url(file_id)
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
from __future__ import annotations
|
16
|
+
|
17
|
+
from abc import abstractmethod
|
18
|
+
from enum import Enum
|
19
|
+
from typing import Protocol
|
20
|
+
|
21
|
+
|
22
|
+
class MediaFileKind(Enum):
|
23
|
+
# st.image, st.video, st.audio files
|
24
|
+
MEDIA = "media"
|
25
|
+
|
26
|
+
# st.download_button files
|
27
|
+
DOWNLOADABLE = "downloadable"
|
28
|
+
|
29
|
+
|
30
|
+
class MediaFileStorageError(Exception):
|
31
|
+
"""Exception class for errors raised by MediaFileStorage.
|
32
|
+
|
33
|
+
When running in "development mode", the full text of these errors
|
34
|
+
is displayed in the frontend, so errors should be human-readable
|
35
|
+
(and actionable).
|
36
|
+
|
37
|
+
When running in "release mode", errors are redacted on the
|
38
|
+
frontend; we instead show a generic "Something went wrong!" message.
|
39
|
+
"""
|
40
|
+
|
41
|
+
|
42
|
+
class MediaFileStorage(Protocol):
|
43
|
+
@abstractmethod
|
44
|
+
def load_and_get_id(
|
45
|
+
self,
|
46
|
+
path_or_data: str | bytes,
|
47
|
+
mimetype: str,
|
48
|
+
kind: MediaFileKind,
|
49
|
+
filename: str | None = None,
|
50
|
+
) -> str:
|
51
|
+
"""Load the given file path or bytes into the manager and return
|
52
|
+
an ID that uniquely identifies it.
|
53
|
+
|
54
|
+
It's an error to pass a URL to this function. (Media stored at
|
55
|
+
external URLs can be served directly to the Streamlit frontend;
|
56
|
+
there's no need to store this data in MediaFileStorage.)
|
57
|
+
|
58
|
+
Parameters
|
59
|
+
----------
|
60
|
+
path_or_data
|
61
|
+
A path to a file, or the file's raw data as bytes.
|
62
|
+
|
63
|
+
mimetype
|
64
|
+
The media's mimetype. Used to set the Content-Type header when
|
65
|
+
serving the media over HTTP.
|
66
|
+
|
67
|
+
kind
|
68
|
+
The kind of file this is: either MEDIA, or DOWNLOADABLE.
|
69
|
+
|
70
|
+
filename : str or None
|
71
|
+
Optional filename. Used to set the filename in the response header.
|
72
|
+
|
73
|
+
Returns
|
74
|
+
-------
|
75
|
+
str
|
76
|
+
The unique ID of the media file.
|
77
|
+
|
78
|
+
Raises
|
79
|
+
------
|
80
|
+
MediaFileStorageError
|
81
|
+
Raised if the media can't be loaded (for example, if a file
|
82
|
+
path is invalid).
|
83
|
+
|
84
|
+
"""
|
85
|
+
raise NotImplementedError
|
86
|
+
|
87
|
+
@abstractmethod
|
88
|
+
def get_url(self, file_id: str) -> str:
|
89
|
+
"""Return a URL for a file in the manager.
|
90
|
+
|
91
|
+
Parameters
|
92
|
+
----------
|
93
|
+
file_id
|
94
|
+
The file's ID, returned from load_media_and_get_id().
|
95
|
+
|
96
|
+
Returns
|
97
|
+
-------
|
98
|
+
str
|
99
|
+
A URL that the frontend can load the file from. Because this
|
100
|
+
URL may expire, it should not be cached!
|
101
|
+
|
102
|
+
Raises
|
103
|
+
------
|
104
|
+
MediaFileStorageError
|
105
|
+
Raised if the manager doesn't contain an object with the given ID.
|
106
|
+
|
107
|
+
"""
|
108
|
+
raise NotImplementedError
|
109
|
+
|
110
|
+
@abstractmethod
|
111
|
+
def delete_file(self, file_id: str) -> None:
|
112
|
+
"""Delete a file from the manager.
|
113
|
+
|
114
|
+
This should be called when a given file is no longer referenced
|
115
|
+
by any connected client, so that the MediaFileStorage can free its
|
116
|
+
resources.
|
117
|
+
|
118
|
+
Calling `delete_file` on a file_id that doesn't exist is allowed,
|
119
|
+
and is a no-op. (This means that multiple `delete_file` calls with
|
120
|
+
the same file_id is not an error.)
|
121
|
+
|
122
|
+
Note: implementations can choose to ignore `delete_file` calls -
|
123
|
+
this function is a *suggestion*, not a *command*. Callers should
|
124
|
+
not rely on file deletion happening immediately (or at all).
|
125
|
+
|
126
|
+
Parameters
|
127
|
+
----------
|
128
|
+
file_id
|
129
|
+
The file's ID, returned from load_media_and_get_id().
|
130
|
+
|
131
|
+
Returns
|
132
|
+
-------
|
133
|
+
None
|
134
|
+
|
135
|
+
Raises
|
136
|
+
------
|
137
|
+
MediaFileStorageError
|
138
|
+
Raised if file deletion fails for any reason. Note that these
|
139
|
+
failures will generally not be shown on the frontend (file
|
140
|
+
deletion usually occurs on session disconnect).
|
141
|
+
|
142
|
+
"""
|
143
|
+
raise NotImplementedError
|
@@ -0,0 +1,181 @@
|
|
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
|
+
"""MediaFileStorage implementation that stores files in memory."""
|
16
|
+
|
17
|
+
from __future__ import annotations
|
18
|
+
|
19
|
+
import contextlib
|
20
|
+
import hashlib
|
21
|
+
import mimetypes
|
22
|
+
import os.path
|
23
|
+
from typing import Final, NamedTuple
|
24
|
+
|
25
|
+
from streamlit.logger import get_logger
|
26
|
+
from streamlit.runtime.media_file_storage import (
|
27
|
+
MediaFileKind,
|
28
|
+
MediaFileStorage,
|
29
|
+
MediaFileStorageError,
|
30
|
+
)
|
31
|
+
from streamlit.runtime.stats import CacheStat, CacheStatsProvider, group_stats
|
32
|
+
|
33
|
+
_LOGGER: Final = get_logger(__name__)
|
34
|
+
|
35
|
+
# Mimetype -> filename extension map for the `get_extension_for_mimetype`
|
36
|
+
# function. We use Python's `mimetypes.guess_extension` for most mimetypes,
|
37
|
+
# but (as of Python 3.9) `mimetypes.guess_extension("audio/wav")` returns None,
|
38
|
+
# so we handle it ourselves.
|
39
|
+
PREFERRED_MIMETYPE_EXTENSION_MAP: Final = {
|
40
|
+
"audio/wav": ".wav",
|
41
|
+
"text/vtt": ".vtt",
|
42
|
+
}
|
43
|
+
|
44
|
+
|
45
|
+
def _calculate_file_id(data: bytes, mimetype: str, filename: str | None = None) -> str:
|
46
|
+
"""Hash data, mimetype, and an optional filename to generate a stable file ID.
|
47
|
+
|
48
|
+
Parameters
|
49
|
+
----------
|
50
|
+
data
|
51
|
+
Content of in-memory file in bytes. Other types will throw TypeError.
|
52
|
+
mimetype
|
53
|
+
Any string. Will be converted to bytes and used to compute a hash.
|
54
|
+
filename
|
55
|
+
Any string. Will be converted to bytes and used to compute a hash.
|
56
|
+
"""
|
57
|
+
filehash = hashlib.new("sha224", usedforsecurity=False)
|
58
|
+
filehash.update(data)
|
59
|
+
filehash.update(bytes(mimetype.encode()))
|
60
|
+
|
61
|
+
if filename is not None:
|
62
|
+
filehash.update(bytes(filename.encode()))
|
63
|
+
|
64
|
+
return filehash.hexdigest()
|
65
|
+
|
66
|
+
|
67
|
+
def get_extension_for_mimetype(mimetype: str) -> str:
|
68
|
+
if mimetype in PREFERRED_MIMETYPE_EXTENSION_MAP:
|
69
|
+
return PREFERRED_MIMETYPE_EXTENSION_MAP[mimetype]
|
70
|
+
|
71
|
+
extension = mimetypes.guess_extension(mimetype, strict=False)
|
72
|
+
if extension is None:
|
73
|
+
return ""
|
74
|
+
|
75
|
+
return extension
|
76
|
+
|
77
|
+
|
78
|
+
class MemoryFile(NamedTuple):
|
79
|
+
"""A MediaFile stored in memory."""
|
80
|
+
|
81
|
+
content: bytes
|
82
|
+
mimetype: str
|
83
|
+
kind: MediaFileKind
|
84
|
+
filename: str | None
|
85
|
+
|
86
|
+
@property
|
87
|
+
def content_size(self) -> int:
|
88
|
+
return len(self.content)
|
89
|
+
|
90
|
+
|
91
|
+
class MemoryMediaFileStorage(MediaFileStorage, CacheStatsProvider):
|
92
|
+
def __init__(self, media_endpoint: str):
|
93
|
+
"""Create a new MemoryMediaFileStorage instance
|
94
|
+
|
95
|
+
Parameters
|
96
|
+
----------
|
97
|
+
media_endpoint
|
98
|
+
The name of the local endpoint that media is served from.
|
99
|
+
This endpoint should start with a forward-slash (e.g. "/media").
|
100
|
+
"""
|
101
|
+
self._files_by_id: dict[str, MemoryFile] = {}
|
102
|
+
self._media_endpoint = media_endpoint
|
103
|
+
|
104
|
+
def load_and_get_id(
|
105
|
+
self,
|
106
|
+
path_or_data: str | bytes,
|
107
|
+
mimetype: str,
|
108
|
+
kind: MediaFileKind,
|
109
|
+
filename: str | None = None,
|
110
|
+
) -> str:
|
111
|
+
"""Add a file to the manager and return its ID."""
|
112
|
+
file_data: bytes
|
113
|
+
if isinstance(path_or_data, str):
|
114
|
+
file_data = self._read_file(path_or_data)
|
115
|
+
else:
|
116
|
+
file_data = path_or_data
|
117
|
+
|
118
|
+
# Because our file_ids are stable, if we already have a file with the
|
119
|
+
# given ID, we don't need to create a new one.
|
120
|
+
file_id = _calculate_file_id(file_data, mimetype, filename)
|
121
|
+
if file_id not in self._files_by_id:
|
122
|
+
_LOGGER.debug("Adding media file %s", file_id)
|
123
|
+
media_file = MemoryFile(
|
124
|
+
content=file_data, mimetype=mimetype, kind=kind, filename=filename
|
125
|
+
)
|
126
|
+
self._files_by_id[file_id] = media_file
|
127
|
+
|
128
|
+
return file_id
|
129
|
+
|
130
|
+
def get_file(self, filename: str) -> MemoryFile:
|
131
|
+
"""Return the MemoryFile with the given filename. Filenames are of the
|
132
|
+
form "file_id.extension". (Note that this is *not* the optional
|
133
|
+
user-specified filename for download files.)
|
134
|
+
|
135
|
+
Raises a MediaFileStorageError if no such file exists.
|
136
|
+
"""
|
137
|
+
file_id = os.path.splitext(filename)[0]
|
138
|
+
try:
|
139
|
+
return self._files_by_id[file_id]
|
140
|
+
except KeyError as e:
|
141
|
+
raise MediaFileStorageError(
|
142
|
+
f"Bad filename '{filename}'. (No media file with id '{file_id}')"
|
143
|
+
) from e
|
144
|
+
|
145
|
+
def get_url(self, file_id: str) -> str:
|
146
|
+
"""Get a URL for a given media file. Raise a MediaFileStorageError if
|
147
|
+
no such file exists.
|
148
|
+
"""
|
149
|
+
media_file = self.get_file(file_id)
|
150
|
+
extension = get_extension_for_mimetype(media_file.mimetype)
|
151
|
+
return f"{self._media_endpoint}/{file_id}{extension}"
|
152
|
+
|
153
|
+
def delete_file(self, file_id: str) -> None:
|
154
|
+
"""Delete the file with the given ID."""
|
155
|
+
# We swallow KeyErrors here - it's not an error to delete a file
|
156
|
+
# that doesn't exist.
|
157
|
+
with contextlib.suppress(KeyError):
|
158
|
+
del self._files_by_id[file_id]
|
159
|
+
|
160
|
+
def _read_file(self, filename: str) -> bytes:
|
161
|
+
"""Read a file into memory. Raise MediaFileStorageError if we can't."""
|
162
|
+
try:
|
163
|
+
with open(filename, "rb") as f:
|
164
|
+
return f.read()
|
165
|
+
except Exception as ex:
|
166
|
+
raise MediaFileStorageError(f"Error opening '{filename}'") from ex
|
167
|
+
|
168
|
+
def get_stats(self) -> list[CacheStat]:
|
169
|
+
# We operate on a copy of our dict, to avoid race conditions
|
170
|
+
# with other threads that may be manipulating the cache.
|
171
|
+
files_by_id = self._files_by_id.copy()
|
172
|
+
|
173
|
+
stats: list[CacheStat] = [
|
174
|
+
CacheStat(
|
175
|
+
category_name="st_memory_media_file_storage",
|
176
|
+
cache_name="",
|
177
|
+
byte_length=len(file.content),
|
178
|
+
)
|
179
|
+
for _, file in files_by_id.items()
|
180
|
+
]
|
181
|
+
return group_stats(stats)
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
from __future__ import annotations
|
16
|
+
|
17
|
+
from typing import TYPE_CHECKING
|
18
|
+
|
19
|
+
from cachetools import TTLCache
|
20
|
+
|
21
|
+
from streamlit.runtime.session_manager import SessionInfo, SessionStorage
|
22
|
+
|
23
|
+
if TYPE_CHECKING:
|
24
|
+
from collections.abc import MutableMapping
|
25
|
+
|
26
|
+
|
27
|
+
class MemorySessionStorage(SessionStorage):
|
28
|
+
"""A SessionStorage that stores sessions in memory.
|
29
|
+
|
30
|
+
At most maxsize sessions are stored with a TTL of ttl seconds. This class is really
|
31
|
+
just a thin wrapper around cachetools.TTLCache that complies with the SessionStorage
|
32
|
+
protocol.
|
33
|
+
"""
|
34
|
+
|
35
|
+
# NOTE: The defaults for maxsize and ttl are chosen arbitrarily for now. These
|
36
|
+
# numbers are reasonable as the main problems we're trying to solve at the moment are
|
37
|
+
# caused by transient disconnects that are usually just short network blips. In the
|
38
|
+
# future, we may want to increase both to support use cases such as saving state for
|
39
|
+
# much longer periods of time. For example, we may want session state to persist if
|
40
|
+
# a user closes their laptop lid and comes back to an app hours later.
|
41
|
+
def __init__(
|
42
|
+
self,
|
43
|
+
maxsize: int = 128,
|
44
|
+
ttl_seconds: int = 2 * 60, # 2 minutes
|
45
|
+
) -> None:
|
46
|
+
"""Instantiate a new MemorySessionStorage.
|
47
|
+
|
48
|
+
Parameters
|
49
|
+
----------
|
50
|
+
maxsize
|
51
|
+
The maximum number of sessions we allow to be stored in this
|
52
|
+
MemorySessionStorage. If an entry needs to be removed because we have
|
53
|
+
exceeded this number, either
|
54
|
+
- an expired entry is removed, or
|
55
|
+
- the least recently used entry is removed (if no entries have expired).
|
56
|
+
|
57
|
+
ttl_seconds
|
58
|
+
The time in seconds for an entry added to a MemorySessionStorage to live.
|
59
|
+
After this amount of time has passed for a given entry, it becomes
|
60
|
+
inaccessible and will be removed eventually.
|
61
|
+
"""
|
62
|
+
|
63
|
+
self._cache: MutableMapping[str, SessionInfo] = TTLCache(
|
64
|
+
maxsize=maxsize, ttl=ttl_seconds
|
65
|
+
)
|
66
|
+
|
67
|
+
def get(self, session_id: str) -> SessionInfo | None:
|
68
|
+
return self._cache.get(session_id, None)
|
69
|
+
|
70
|
+
def save(self, session_info: SessionInfo) -> None:
|
71
|
+
self._cache[session_info.session.id] = session_info
|
72
|
+
|
73
|
+
def delete(self, session_id: str) -> None:
|
74
|
+
del self._cache[session_id]
|
75
|
+
|
76
|
+
def list(self) -> list[SessionInfo]:
|
77
|
+
return list(self._cache.values())
|