streamlit 1.45.1__py3-none-any.whl → 1.46.1__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 +5 -1
- streamlit/auth_util.py +12 -12
- streamlit/cli_util.py +4 -3
- streamlit/column_config.py +11 -9
- streamlit/commands/echo.py +6 -4
- streamlit/commands/execution_control.py +33 -32
- streamlit/commands/experimental_query_params.py +2 -2
- streamlit/commands/logo.py +9 -4
- streamlit/commands/navigation.py +61 -18
- streamlit/commands/page_config.py +57 -47
- streamlit/components/types/base_custom_component.py +7 -7
- streamlit/components/v1/component_registry.py +7 -3
- streamlit/components/v1/components.py +1 -1
- streamlit/components/v1/custom_component.py +8 -8
- streamlit/config.py +289 -144
- streamlit/config_option.py +19 -15
- streamlit/config_util.py +29 -23
- streamlit/connections/__init__.py +2 -2
- streamlit/connections/base_connection.py +5 -5
- streamlit/connections/snowflake_connection.py +13 -11
- streamlit/connections/snowpark_connection.py +3 -3
- streamlit/connections/sql_connection.py +20 -18
- streamlit/connections/util.py +2 -2
- streamlit/cursor.py +6 -6
- streamlit/dataframe_util.py +52 -52
- streamlit/delta_generator.py +46 -48
- streamlit/delta_generator_singletons.py +3 -3
- streamlit/deprecation_util.py +6 -6
- streamlit/elements/alert.py +37 -29
- streamlit/elements/arrow.py +40 -22
- streamlit/elements/code.py +46 -13
- streamlit/elements/deck_gl_json_chart.py +38 -27
- streamlit/elements/dialog_decorator.py +3 -4
- streamlit/elements/doc_string.py +64 -58
- streamlit/elements/exception.py +23 -27
- streamlit/elements/form.py +41 -0
- streamlit/elements/graphviz_chart.py +1 -1
- streamlit/elements/heading.py +60 -9
- streamlit/elements/html.py +3 -4
- streamlit/elements/image.py +8 -9
- streamlit/elements/json.py +21 -2
- streamlit/elements/layouts.py +120 -31
- streamlit/elements/lib/built_in_chart_utils.py +96 -73
- streamlit/elements/lib/color_util.py +3 -3
- streamlit/elements/lib/column_config_utils.py +2 -4
- streamlit/elements/lib/column_types.py +14 -8
- streamlit/elements/lib/dialog.py +9 -5
- streamlit/elements/lib/image_utils.py +39 -40
- streamlit/elements/lib/js_number.py +4 -4
- streamlit/elements/lib/layout_utils.py +65 -1
- streamlit/elements/lib/mutable_status_container.py +14 -3
- streamlit/elements/lib/options_selector_utils.py +22 -12
- streamlit/elements/lib/pandas_styler_utils.py +25 -21
- streamlit/elements/lib/policies.py +6 -5
- streamlit/elements/lib/streamlit_plotly_theme.py +54 -53
- streamlit/elements/lib/subtitle_utils.py +6 -9
- streamlit/elements/lib/utils.py +20 -5
- streamlit/elements/map.py +32 -56
- streamlit/elements/markdown.py +101 -12
- streamlit/elements/media.py +78 -21
- streamlit/elements/metric.py +32 -16
- streamlit/elements/plotly_chart.py +15 -15
- streamlit/elements/progress.py +33 -15
- streamlit/elements/spinner.py +31 -6
- streamlit/elements/text.py +21 -1
- streamlit/elements/toast.py +1 -2
- streamlit/elements/vega_charts.py +54 -23
- streamlit/elements/widgets/audio_input.py +24 -7
- streamlit/elements/widgets/button.py +26 -19
- streamlit/elements/widgets/button_group.py +10 -15
- streamlit/elements/widgets/camera_input.py +27 -7
- streamlit/elements/widgets/chat.py +91 -38
- streamlit/elements/widgets/checkbox.py +45 -4
- streamlit/elements/widgets/color_picker.py +40 -17
- streamlit/elements/widgets/data_editor.py +76 -37
- streamlit/elements/widgets/file_uploader.py +42 -13
- streamlit/elements/widgets/multiselect.py +7 -10
- streamlit/elements/widgets/number_input.py +123 -47
- streamlit/elements/widgets/radio.py +59 -13
- streamlit/elements/widgets/select_slider.py +35 -30
- streamlit/elements/widgets/selectbox.py +56 -9
- streamlit/elements/widgets/slider.py +190 -99
- streamlit/elements/widgets/text_widgets.py +54 -8
- streamlit/elements/widgets/time_widgets.py +53 -14
- streamlit/elements/write.py +5 -8
- streamlit/env_util.py +2 -7
- streamlit/error_util.py +16 -9
- streamlit/errors.py +69 -48
- streamlit/external/langchain/streamlit_callback_handler.py +10 -5
- streamlit/file_util.py +27 -10
- streamlit/git_util.py +29 -24
- streamlit/hello/animation_demo.py +9 -9
- streamlit/hello/dataframe_demo.py +5 -5
- streamlit/hello/hello.py +1 -0
- streamlit/hello/mapping_demo.py +7 -8
- streamlit/hello/plotting_demo.py +3 -3
- streamlit/hello/streamlit_app.py +28 -26
- streamlit/hello/utils.py +2 -1
- streamlit/logger.py +10 -11
- streamlit/navigation/page.py +11 -8
- streamlit/proto/Audio_pb2.py +4 -3
- streamlit/proto/Audio_pb2.pyi +8 -1
- streamlit/proto/Block_pb2.py +38 -29
- streamlit/proto/Block_pb2.pyi +72 -4
- streamlit/proto/ClientState_pb2.py +4 -4
- streamlit/proto/ClientState_pb2.pyi +7 -2
- streamlit/proto/Code_pb2.py +4 -2
- streamlit/proto/Code_pb2.pyi +1 -0
- streamlit/proto/DataFrame_pb2.pyi +1 -1
- streamlit/proto/DeckGlJsonChart_pb2.pyi +1 -1
- streamlit/proto/Element_pb2.py +5 -3
- streamlit/proto/Element_pb2.pyi +20 -3
- streamlit/proto/GapSize_pb2.py +29 -0
- streamlit/proto/GapSize_pb2.pyi +70 -0
- streamlit/proto/HeightConfig_pb2.py +27 -0
- streamlit/proto/HeightConfig_pb2.pyi +48 -0
- streamlit/proto/NamedDataSet_pb2.pyi +1 -1
- streamlit/proto/Navigation_pb2.py +3 -3
- streamlit/proto/Navigation_pb2.pyi +4 -0
- streamlit/proto/NewSession_pb2.py +18 -16
- streamlit/proto/NewSession_pb2.pyi +29 -3
- streamlit/proto/PageConfig_pb2.py +7 -7
- streamlit/proto/PageConfig_pb2.pyi +21 -1
- streamlit/proto/Video_pb2.py +8 -7
- streamlit/proto/Video_pb2.pyi +8 -1
- streamlit/proto/WidthConfig_pb2.py +2 -2
- streamlit/proto/WidthConfig_pb2.pyi +15 -1
- streamlit/runtime/__init__.py +1 -1
- streamlit/runtime/app_session.py +53 -40
- streamlit/runtime/caching/__init__.py +9 -9
- streamlit/runtime/caching/cache_data_api.py +36 -30
- streamlit/runtime/caching/cache_errors.py +4 -4
- streamlit/runtime/caching/cache_resource_api.py +8 -8
- streamlit/runtime/caching/cache_utils.py +15 -14
- streamlit/runtime/caching/cached_message_replay.py +14 -8
- streamlit/runtime/caching/hashing.py +91 -97
- streamlit/runtime/caching/legacy_cache_api.py +2 -2
- streamlit/runtime/caching/storage/cache_storage_protocol.py +1 -1
- streamlit/runtime/caching/storage/dummy_cache_storage.py +1 -1
- streamlit/runtime/caching/storage/in_memory_cache_storage_wrapper.py +12 -14
- streamlit/runtime/caching/storage/local_disk_cache_storage.py +6 -6
- streamlit/runtime/connection_factory.py +36 -36
- streamlit/runtime/context.py +58 -9
- streamlit/runtime/credentials.py +29 -40
- streamlit/runtime/forward_msg_queue.py +11 -11
- streamlit/runtime/fragment.py +7 -7
- streamlit/runtime/media_file_manager.py +3 -4
- streamlit/runtime/memory_media_file_storage.py +6 -5
- streamlit/runtime/memory_uploaded_file_manager.py +2 -2
- streamlit/runtime/metrics_util.py +11 -12
- streamlit/runtime/pages_manager.py +4 -6
- streamlit/runtime/runtime.py +8 -6
- streamlit/runtime/runtime_util.py +7 -6
- streamlit/runtime/scriptrunner/__init__.py +4 -4
- streamlit/runtime/scriptrunner/exec_code.py +12 -5
- streamlit/runtime/scriptrunner/magic.py +16 -12
- streamlit/runtime/scriptrunner/script_cache.py +1 -1
- streamlit/runtime/scriptrunner/script_runner.py +53 -29
- streamlit/runtime/scriptrunner_utils/exceptions.py +1 -1
- streamlit/runtime/scriptrunner_utils/script_requests.py +7 -4
- streamlit/runtime/scriptrunner_utils/script_run_context.py +10 -23
- streamlit/runtime/secrets.py +40 -35
- streamlit/runtime/session_manager.py +2 -1
- streamlit/runtime/state/__init__.py +5 -5
- streamlit/runtime/state/common.py +2 -2
- streamlit/runtime/state/query_params.py +13 -15
- streamlit/runtime/state/query_params_proxy.py +17 -13
- streamlit/runtime/state/safe_session_state.py +2 -2
- streamlit/runtime/state/session_state.py +52 -34
- streamlit/runtime/stats.py +2 -2
- streamlit/runtime/uploaded_file_manager.py +1 -1
- streamlit/runtime/websocket_session_manager.py +10 -6
- streamlit/source_util.py +8 -6
- streamlit/static/index.html +3 -17
- streamlit/static/manifest.json +1180 -0
- streamlit/static/static/css/{index.DqDwtg6_.css → index.CJVRHjQZ.css} +1 -1
- streamlit/static/static/js/{ErrorOutline.esm.DU9IrB3M.js → ErrorOutline.esm.DitPpe1Y.js} +1 -1
- streamlit/static/static/js/{FileDownload.esm.P9rKwKo8.js → FileDownload.esm.AI3watX9.js} +1 -1
- streamlit/static/static/js/{FileHelper.D7RMkx0e.js → FileHelper.kt7mhnu8.js} +5 -5
- streamlit/static/static/js/{FormClearHelper.B67tgll0.js → FormClearHelper.D1M9GM_c.js} +1 -1
- streamlit/static/static/js/{Hooks.ncTJktu9.js → Hooks.BGwHKeUc.js} +1 -1
- streamlit/static/static/js/{InputInstructions.D-Y8geDN.js → InputInstructions.DaZ89mzH.js} +1 -1
- streamlit/static/static/js/{ProgressBar.B-kexwwD.js → ProgressBar.C0zPMe-p.js} +2 -2
- streamlit/static/static/js/{RenderInPortalIfExists.BgaoZgep.js → RenderInPortalIfExists.Ox8gQvdz.js} +1 -1
- streamlit/static/static/js/Toolbar.KhlcEc0K.js +1 -0
- streamlit/static/static/js/UploadFileInfo.0DCkpDDf.js +6 -0
- streamlit/static/static/js/{base-input.BoAa1U94.js → base-input.BJ4qsfSq.js} +4 -4
- streamlit/static/static/js/{checkbox.Z6iSfe5F.js → checkbox.DSDh78Xz.js} +2 -2
- streamlit/static/static/js/{createSuper.B4oGDYRm.js → createSuper.wQ9SIXEJ.js} +1 -1
- streamlit/static/static/js/{data-grid-overlay-editor.msYws2Ou.js → data-grid-overlay-editor.DvbdPJ15.js} +1 -1
- streamlit/static/static/js/{downloader.kc14n2Hv.js → downloader.CD9rzih5.js} +1 -1
- streamlit/static/static/js/{es6.CxQz807-.js → es6.48Q9Qjgb.js} +2 -2
- streamlit/static/static/js/{iframeResizer.contentWindow.B19u0ONI.js → iframeResizer.contentWindow.CKdem3Bn.js} +1 -1
- streamlit/static/static/js/{index.LaIasviC.js → index.6md5Qhod.js} +1 -1
- streamlit/static/static/js/index.7hy6AeJ1.js +1 -0
- streamlit/static/static/js/index.B4CGJiBW.js +1 -0
- streamlit/static/static/js/index.B8oW0ZTD.js +1 -0
- streamlit/static/static/js/index.BU6RnlHI.js +73 -0
- streamlit/static/static/js/index.BUq9Wcf8.js +197 -0
- streamlit/static/static/js/{index.BFz9U2y0.js → index.BXXo-Yoj.js} +1 -1
- streamlit/static/static/js/index.Bae9H0OS.js +1 -0
- streamlit/static/static/js/{index.-5ruC9At.js → index.BhTl2Uyb.js} +1 -1
- streamlit/static/static/js/{index.BpILzHf_.js → index.BiSaCB1o.js} +20 -20
- streamlit/static/static/js/{index.xNQq3Ei5.js → index.BulSAJ9z.js} +1 -1
- streamlit/static/static/js/{index.9V1KdxfP.js → index.Bv-EuTKR.js} +1 -1
- streamlit/static/static/js/index.BvMLYCHi.js +1 -0
- streamlit/static/static/js/index.C1NIn1Y2.js +783 -0
- streamlit/static/static/js/index.CP-fthOJ.js +2 -0
- streamlit/static/static/js/{index.BoigZiu7.js → index.CS9guO3p.js} +1 -1
- streamlit/static/static/js/index.CYTBHth8.js +1 -0
- streamlit/static/static/js/{index.CmTAF0dM.js → index.CcJufcuD.js} +1 -1
- streamlit/static/static/js/index.CnENU1yn.js +1 -0
- streamlit/static/static/js/index.Cns13qBb.js +1 -0
- streamlit/static/static/js/index.Ct_xXq7w.js +1 -0
- streamlit/static/static/js/{index.BqfdT8-Q.js → index.CxGSemHL.js} +1 -1
- streamlit/static/static/js/index.D5S0ldVb.js +1 -0
- streamlit/static/static/js/index.D72B_ksb.js +2 -0
- streamlit/static/static/js/index.DI4yZ27M.js +1 -0
- streamlit/static/static/js/index.DN51vLxR.js +1 -0
- streamlit/static/static/js/index.DRtq5dka.js +1 -0
- streamlit/static/static/js/{index.BHXxWdde.js → index.DX-oiXlb.js} +1 -1
- streamlit/static/static/js/index.DlFE4_Aq.js +12 -0
- streamlit/static/static/js/{index.BHGGDa8K.js → index.J7BJwXOi.js} +2 -2
- streamlit/static/static/js/index.Jg38kJPP.js +1 -0
- streamlit/static/static/js/index.JhIO6abf.js +3 -0
- streamlit/static/static/js/{index.DeB9iKFW.js → index.NkRcWwc5.js} +255 -255
- streamlit/static/static/js/{index.BGga-hcS.js → index.prekPLrm.js} +25 -25
- streamlit/static/static/js/{index.BRXmLIsC.js → index.wyzngKUE.js} +1 -1
- streamlit/static/static/js/index.xW7mVdI8.js +1 -0
- streamlit/static/static/js/index.yk07dYGx.js +1 -0
- streamlit/static/static/js/{input.DsCfafm0.js → input.CxKZ5Wrc.js} +2 -2
- streamlit/static/static/js/{memory.nY_lMTtu.js → memory.DeZ9VUvl.js} +1 -1
- streamlit/static/static/js/{mergeWith.B_7zmsM4.js → mergeWith.CVkhrWUb.js} +1 -1
- streamlit/static/static/js/{number-overlay-editor.CSeVhHRU.js → number-overlay-editor.Bpkm3nTq.js} +1 -1
- streamlit/static/static/js/{possibleConstructorReturn.nNhsvgRd.js → possibleConstructorReturn.CIDCId52.js} +1 -1
- streamlit/static/static/js/{sandbox.Cgm3iuL6.js → sandbox.TrkMaokR.js} +1 -1
- streamlit/static/static/js/{textarea.BR8rlyih.js → textarea.QKjxR64N.js} +2 -2
- streamlit/static/static/js/{timepicker.w4XhAenH.js → timepicker.DJYmE1dK.js} +1 -1
- streamlit/static/static/js/{toConsumableArray.CgkEPBwD.js → toConsumableArray.BZoworE-.js} +1 -1
- streamlit/static/static/js/{uniqueId.j-1rlNNH.js → uniqueId.O0UbJ2Bu.js} +1 -1
- streamlit/static/static/js/{useBasicWidgetState.zXY9CjFS.js → useBasicWidgetState.Ci89jaH5.js} +1 -1
- streamlit/static/static/js/useOnInputChange.Cxh6ExEn.js +1 -0
- streamlit/static/static/js/{withFullScreenWrapper.Ov13692o.js → withFullScreenWrapper.iW37lS8Z.js} +1 -1
- streamlit/static/static/media/SourceCodeVF-Italic.ttf.Ba1oaZG1.woff2 +0 -0
- streamlit/static/static/media/SourceCodeVF-Upright.ttf.BjWn63N-.woff2 +0 -0
- streamlit/static/static/media/SourceSansVF-Italic.ttf.Bt9VkdQ3.woff2 +0 -0
- streamlit/static/static/media/SourceSansVF-Upright.ttf.BsWL4Kly.woff2 +0 -0
- streamlit/static/static/media/SourceSerifVariable-Italic.ttf.CVdzAtxO.woff2 +0 -0
- streamlit/static/static/media/SourceSerifVariable-Roman.ttf.mdpVL9bi.woff2 +0 -0
- streamlit/string_util.py +14 -19
- streamlit/temporary_directory.py +13 -4
- streamlit/testing/v1/app_test.py +15 -10
- streamlit/testing/v1/element_tree.py +157 -178
- streamlit/testing/v1/local_script_runner.py +11 -15
- streamlit/testing/v1/util.py +11 -4
- streamlit/type_util.py +8 -12
- streamlit/url_util.py +1 -1
- streamlit/user_info.py +6 -5
- streamlit/util.py +25 -1
- streamlit/vendor/pympler/asizeof.py +3 -2
- streamlit/watcher/event_based_path_watcher.py +21 -2
- streamlit/watcher/folder_black_list.py +2 -2
- streamlit/watcher/local_sources_watcher.py +64 -18
- streamlit/watcher/path_watcher.py +6 -10
- streamlit/watcher/polling_path_watcher.py +8 -7
- streamlit/watcher/util.py +7 -6
- streamlit/web/bootstrap.py +16 -14
- streamlit/web/cli.py +52 -45
- streamlit/web/server/__init__.py +7 -3
- streamlit/web/server/app_static_file_handler.py +1 -1
- streamlit/web/server/authlib_tornado_integration.py +9 -4
- streamlit/web/server/browser_websocket_handler.py +8 -2
- streamlit/web/server/component_request_handler.py +14 -10
- streamlit/web/server/media_file_handler.py +14 -7
- streamlit/web/server/oauth_authlib_routes.py +41 -9
- streamlit/web/server/oidc_mixin.py +35 -17
- streamlit/web/server/routes.py +32 -22
- streamlit/web/server/server.py +13 -24
- streamlit/web/server/server_util.py +43 -9
- streamlit/web/server/stats_request_handler.py +7 -5
- streamlit/web/server/upload_file_request_handler.py +22 -19
- streamlit/web/server/websocket_headers.py +1 -1
- {streamlit-1.45.1.dist-info → streamlit-1.46.1.dist-info}/METADATA +4 -4
- streamlit-1.46.1.dist-info/RECORD +559 -0
- {streamlit-1.45.1.dist-info → streamlit-1.46.1.dist-info}/WHEEL +1 -1
- streamlit/elements/lib/event_utils.py +0 -39
- streamlit/static/static/js/Toolbar.D9RUZv9G.js +0 -1
- streamlit/static/static/js/UploadFileInfo.C-jY39rj.js +0 -1
- streamlit/static/static/js/index.8jhZBWF2.js +0 -3
- streamlit/static/static/js/index.BCx3C6e_.js +0 -1
- streamlit/static/static/js/index.BRuTz_S4.js +0 -1
- streamlit/static/static/js/index.Bcru_ti-.js +0 -1
- streamlit/static/static/js/index.Bl1FMJRd.js +0 -1
- streamlit/static/static/js/index.C1z8KpLA.js +0 -779
- streamlit/static/static/js/index.C32I2PUe.js +0 -2
- streamlit/static/static/js/index.C5GnDRB7.js +0 -1
- streamlit/static/static/js/index.CG4qPaaW.js +0 -2
- streamlit/static/static/js/index.C_msmT1u.js +0 -1
- streamlit/static/static/js/index.CbeNTdd6.js +0 -1
- streamlit/static/static/js/index.CnGQVJcw.js +0 -12
- streamlit/static/static/js/index.CopVVq4l.js +0 -1
- streamlit/static/static/js/index.CtXupx4d.js +0 -197
- streamlit/static/static/js/index.DGmCchO7.js +0 -1
- streamlit/static/static/js/index.DH6zBk0e.js +0 -1
- streamlit/static/static/js/index.DHVlVWsm.js +0 -1
- streamlit/static/static/js/index.DRKIVBoi.js +0 -1
- streamlit/static/static/js/index.DUd-lFXx.js +0 -73
- streamlit/static/static/js/index.D_uRBA4B.js +0 -1
- streamlit/static/static/js/index.QHNfgPJd.js +0 -1
- streamlit/static/static/js/index.a-RJocYL.js +0 -1
- streamlit/static/static/js/index.cvz4B1gy.js +0 -1
- streamlit/static/static/js/index.t--hEgTQ.js +0 -6
- streamlit/static/static/js/useOnInputChange.z04u96A8.js +0 -1
- 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-1.45.1.dist-info/RECORD +0 -568
- {streamlit-1.45.1.data → streamlit-1.46.1.data}/scripts/streamlit.cmd +0 -0
- {streamlit-1.45.1.dist-info → streamlit-1.46.1.dist-info}/entry_points.txt +0 -0
- {streamlit-1.45.1.dist-info → streamlit-1.46.1.dist-info}/top_level.txt +0 -0
|
@@ -111,9 +111,9 @@ def _coalesce_widget_states(
|
|
|
111
111
|
"""
|
|
112
112
|
if not old_states and not new_states:
|
|
113
113
|
return None
|
|
114
|
-
|
|
114
|
+
if not old_states:
|
|
115
115
|
return new_states
|
|
116
|
-
|
|
116
|
+
if not new_states:
|
|
117
117
|
return old_states
|
|
118
118
|
|
|
119
119
|
states_by_id: dict[str, WidgetState] = {
|
|
@@ -161,7 +161,7 @@ class ScriptRequests:
|
|
|
161
161
|
ScriptRunner handles those requests.
|
|
162
162
|
"""
|
|
163
163
|
|
|
164
|
-
def __init__(self):
|
|
164
|
+
def __init__(self) -> None:
|
|
165
165
|
self._lock = threading.Lock()
|
|
166
166
|
self._state = ScriptRequestType.CONTINUE
|
|
167
167
|
self._rerun_data = RerunData()
|
|
@@ -284,7 +284,10 @@ class ScriptRequests:
|
|
|
284
284
|
self._state = ScriptRequestType.CONTINUE
|
|
285
285
|
return ScriptRequest(ScriptRequestType.RERUN, self._rerun_data)
|
|
286
286
|
|
|
287
|
-
|
|
287
|
+
if self._state != ScriptRequestType.STOP:
|
|
288
|
+
raise RuntimeError(
|
|
289
|
+
f"Unrecognized ScriptRunnerState: {self._state}. This should never happen."
|
|
290
|
+
)
|
|
288
291
|
return ScriptRequest(ScriptRequestType.STOP)
|
|
289
292
|
|
|
290
293
|
def on_scriptrunner_ready(self) -> ScriptRequest:
|
|
@@ -33,7 +33,6 @@ from typing_extensions import TypeAlias
|
|
|
33
33
|
from streamlit.errors import (
|
|
34
34
|
NoSessionContext,
|
|
35
35
|
StreamlitAPIException,
|
|
36
|
-
StreamlitSetPageConfigMustBeFirstCommandError,
|
|
37
36
|
)
|
|
38
37
|
from streamlit.logger import get_logger
|
|
39
38
|
from streamlit.runtime.forward_msg_cache import (
|
|
@@ -42,6 +41,7 @@ from streamlit.runtime.forward_msg_cache import (
|
|
|
42
41
|
)
|
|
43
42
|
|
|
44
43
|
if TYPE_CHECKING:
|
|
44
|
+
from collections.abc import Generator
|
|
45
45
|
from pathlib import Path
|
|
46
46
|
|
|
47
47
|
from streamlit.cursor import RunningCursor
|
|
@@ -97,7 +97,6 @@ class ScriptRunContext:
|
|
|
97
97
|
command_tracking_deactivated: bool = False
|
|
98
98
|
tracked_commands: list[Command] = field(default_factory=list)
|
|
99
99
|
tracked_commands_counter: Counter[str] = field(default_factory=collections.Counter)
|
|
100
|
-
_set_page_config_allowed: bool = True
|
|
101
100
|
_has_script_started: bool = False
|
|
102
101
|
widget_ids_this_run: set[str] = field(default_factory=set)
|
|
103
102
|
widget_user_keys_this_run: set[str] = field(default_factory=set)
|
|
@@ -107,6 +106,7 @@ class ScriptRunContext:
|
|
|
107
106
|
current_fragment_id: str | None = None
|
|
108
107
|
fragment_ids_this_run: list[str] | None = None
|
|
109
108
|
new_fragment_ids: set[str] = field(default_factory=set)
|
|
109
|
+
in_fragment_callback: bool = False
|
|
110
110
|
_active_script_hash: str = ""
|
|
111
111
|
# we allow only one dialog to be open at the same time
|
|
112
112
|
has_dialog_opened: bool = False
|
|
@@ -116,11 +116,11 @@ class ScriptRunContext:
|
|
|
116
116
|
_production_query_params_used = False
|
|
117
117
|
|
|
118
118
|
@property
|
|
119
|
-
def page_script_hash(self):
|
|
119
|
+
def page_script_hash(self) -> str:
|
|
120
120
|
return self.pages_manager.current_page_script_hash
|
|
121
121
|
|
|
122
122
|
@property
|
|
123
|
-
def active_script_hash(self):
|
|
123
|
+
def active_script_hash(self) -> str:
|
|
124
124
|
return self._active_script_hash
|
|
125
125
|
|
|
126
126
|
@property
|
|
@@ -128,7 +128,7 @@ class ScriptRunContext:
|
|
|
128
128
|
return self.pages_manager.main_script_parent
|
|
129
129
|
|
|
130
130
|
@contextlib.contextmanager
|
|
131
|
-
def run_with_active_hash(self, page_hash: str):
|
|
131
|
+
def run_with_active_hash(self, page_hash: str) -> Generator[None, None, None]:
|
|
132
132
|
original_page_hash = self._active_script_hash
|
|
133
133
|
self._active_script_hash = page_hash
|
|
134
134
|
try:
|
|
@@ -137,7 +137,7 @@ class ScriptRunContext:
|
|
|
137
137
|
# in the event of any exception, ensure we set the active hash back
|
|
138
138
|
self._active_script_hash = original_page_hash
|
|
139
139
|
|
|
140
|
-
def set_mpa_v2_page(self, page_script_hash: str):
|
|
140
|
+
def set_mpa_v2_page(self, page_script_hash: str) -> None:
|
|
141
141
|
self._active_script_hash = self.pages_manager.main_script_hash
|
|
142
142
|
self.pages_manager.set_current_page_script_hash(page_script_hash)
|
|
143
143
|
|
|
@@ -157,8 +157,6 @@ class ScriptRunContext:
|
|
|
157
157
|
self.context_info = context_info
|
|
158
158
|
self.pages_manager.set_current_page_script_hash(page_script_hash)
|
|
159
159
|
self._active_script_hash = self.pages_manager.main_script_hash
|
|
160
|
-
# Permit set_page_config when the ScriptRunContext is reused on a rerun
|
|
161
|
-
self._set_page_config_allowed = True
|
|
162
160
|
self._has_script_started = False
|
|
163
161
|
self.command_tracking_deactivated: bool = False
|
|
164
162
|
self.tracked_commands = []
|
|
@@ -188,17 +186,6 @@ class ScriptRunContext:
|
|
|
188
186
|
|
|
189
187
|
def enqueue(self, msg: ForwardMsg) -> None:
|
|
190
188
|
"""Enqueue a ForwardMsg for this context's session."""
|
|
191
|
-
if msg.HasField("page_config_changed") and not self._set_page_config_allowed:
|
|
192
|
-
raise StreamlitSetPageConfigMustBeFirstCommandError()
|
|
193
|
-
|
|
194
|
-
# We want to disallow set_page config if one of the following occurs:
|
|
195
|
-
# - set_page_config was called on this message
|
|
196
|
-
# - The script has already started and a different st call occurs (a delta)
|
|
197
|
-
if msg.HasField("page_config_changed") or (
|
|
198
|
-
msg.HasField("delta") and self._has_script_started
|
|
199
|
-
):
|
|
200
|
-
self._set_page_config_allowed = False
|
|
201
|
-
|
|
202
189
|
msg.metadata.active_script_hash = self.active_script_hash
|
|
203
190
|
|
|
204
191
|
# We populate the hash and cacheable field for all messages.
|
|
@@ -217,7 +204,7 @@ class ScriptRunContext:
|
|
|
217
204
|
# Pass the message up to our associated ScriptRunner.
|
|
218
205
|
self._enqueue(msg_to_send)
|
|
219
206
|
|
|
220
|
-
def ensure_single_query_api_used(self):
|
|
207
|
+
def ensure_single_query_api_used(self) -> None:
|
|
221
208
|
if self._experimental_query_params_used and self._production_query_params_used:
|
|
222
209
|
raise StreamlitAPIException(
|
|
223
210
|
"Using `st.query_params` together with either `st.experimental_get_query_params` "
|
|
@@ -225,11 +212,11 @@ class ScriptRunContext:
|
|
|
225
212
|
" convert your app to only use `st.query_params`"
|
|
226
213
|
)
|
|
227
214
|
|
|
228
|
-
def mark_experimental_query_params_used(self):
|
|
215
|
+
def mark_experimental_query_params_used(self) -> None:
|
|
229
216
|
self._experimental_query_params_used = True
|
|
230
217
|
self.ensure_single_query_api_used()
|
|
231
218
|
|
|
232
|
-
def mark_production_query_params_used(self):
|
|
219
|
+
def mark_production_query_params_used(self) -> None:
|
|
233
220
|
self._production_query_params_used = True
|
|
234
221
|
self.ensure_single_query_api_used()
|
|
235
222
|
|
|
@@ -239,7 +226,7 @@ SCRIPT_RUN_CONTEXT_ATTR_NAME: Final = "streamlit_script_run_ctx"
|
|
|
239
226
|
|
|
240
227
|
def add_script_run_ctx(
|
|
241
228
|
thread: threading.Thread | None = None, ctx: ScriptRunContext | None = None
|
|
242
|
-
):
|
|
229
|
+
) -> threading.Thread:
|
|
243
230
|
"""Adds the current ScriptRunContext to a newly-created thread.
|
|
244
231
|
|
|
245
232
|
This should be called from this thread's parent thread,
|
streamlit/runtime/secrets.py
CHANGED
|
@@ -27,9 +27,8 @@ from typing import (
|
|
|
27
27
|
|
|
28
28
|
from blinker import Signal
|
|
29
29
|
|
|
30
|
-
import streamlit as st
|
|
31
30
|
import streamlit.watcher.path_watcher
|
|
32
|
-
from streamlit import runtime
|
|
31
|
+
from streamlit import config, runtime
|
|
33
32
|
from streamlit.errors import StreamlitSecretNotFoundError
|
|
34
33
|
from streamlit.logger import get_logger
|
|
35
34
|
|
|
@@ -37,34 +36,40 @@ _LOGGER: Final = get_logger(__name__)
|
|
|
37
36
|
|
|
38
37
|
|
|
39
38
|
class SecretErrorMessages:
|
|
40
|
-
"""SecretErrorMessages stores all error messages we use for secrets to allow customization
|
|
39
|
+
"""SecretErrorMessages stores all error messages we use for secrets to allow customization
|
|
40
|
+
for different environments.
|
|
41
|
+
|
|
41
42
|
For example Streamlit Cloud can customize the message to be different than the open source.
|
|
42
43
|
|
|
43
44
|
For internal use, may change in future releases without notice.
|
|
44
45
|
"""
|
|
45
46
|
|
|
46
|
-
def __init__(self):
|
|
47
|
-
self.missing_attr_message = lambda attr_name: (
|
|
47
|
+
def __init__(self) -> None:
|
|
48
|
+
self.missing_attr_message: Callable[[str], str] = lambda attr_name: (
|
|
48
49
|
f'st.secrets has no attribute "{attr_name}". '
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
"Did you forget to add it to secrets.toml, mount it to secret directory, or the app settings "
|
|
51
|
+
"on Streamlit Cloud? More info: "
|
|
52
|
+
"https://docs.streamlit.io/deploy/streamlit-community-cloud/deploy-your-app/secrets-management"
|
|
51
53
|
)
|
|
52
|
-
self.missing_key_message = lambda key: (
|
|
54
|
+
self.missing_key_message: Callable[[str], str] = lambda key: (
|
|
53
55
|
f'st.secrets has no key "{key}". '
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
"Did you forget to add it to secrets.toml, mount it to secret directory, or the app settings "
|
|
57
|
+
"on Streamlit Cloud? More info: "
|
|
58
|
+
"https://docs.streamlit.io/deploy/streamlit-community-cloud/deploy-your-app/secrets-management"
|
|
56
59
|
)
|
|
57
|
-
self.no_secrets_found = lambda file_paths: (
|
|
60
|
+
self.no_secrets_found: Callable[[list[str]], str] = lambda file_paths: (
|
|
58
61
|
f"No secrets found. Valid paths for a secrets.toml file or secret directories are: {', '.join(file_paths)}"
|
|
59
62
|
)
|
|
60
|
-
self.error_parsing_file_at_path = (
|
|
63
|
+
self.error_parsing_file_at_path: Callable[[str, Exception], str] = (
|
|
61
64
|
lambda path, ex: f"Error parsing secrets file at {path}: {ex}"
|
|
62
65
|
)
|
|
63
|
-
self.subfolder_path_is_not_a_folder
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
self.subfolder_path_is_not_a_folder: Callable[[str], str] = (
|
|
67
|
+
lambda sub_folder_path: (
|
|
68
|
+
f"{sub_folder_path} is not a folder. "
|
|
69
|
+
"To use directory based secrets, mount every secret in a subfolder under the secret directory"
|
|
70
|
+
)
|
|
66
71
|
)
|
|
67
|
-
self.invalid_secret_path = lambda path: (
|
|
72
|
+
self.invalid_secret_path: Callable[[str], str] = lambda path: (
|
|
68
73
|
f"Invalid secrets path: {path}: path is not a .toml file or a directory"
|
|
69
74
|
)
|
|
70
75
|
|
|
@@ -144,15 +149,14 @@ class AttrDict(Mapping[str, Any]):
|
|
|
144
149
|
to provide dot access to nested secrets.
|
|
145
150
|
"""
|
|
146
151
|
|
|
147
|
-
def __init__(self, value):
|
|
152
|
+
def __init__(self, value: Mapping[str, Any]) -> None:
|
|
148
153
|
self.__dict__["__nested_secrets__"] = dict(value)
|
|
149
154
|
|
|
150
155
|
@staticmethod
|
|
151
|
-
def _maybe_wrap_in_attr_dict(value) -> Any:
|
|
156
|
+
def _maybe_wrap_in_attr_dict(value: Any) -> Any:
|
|
152
157
|
if not isinstance(value, Mapping):
|
|
153
158
|
return value
|
|
154
|
-
|
|
155
|
-
return AttrDict(value)
|
|
159
|
+
return AttrDict(value)
|
|
156
160
|
|
|
157
161
|
def __len__(self) -> int:
|
|
158
162
|
return len(self.__nested_secrets__)
|
|
@@ -174,13 +178,13 @@ class AttrDict(Mapping[str, Any]):
|
|
|
174
178
|
except KeyError:
|
|
175
179
|
raise AttributeError(_missing_attr_error_message(attr_name))
|
|
176
180
|
|
|
177
|
-
def __repr__(self):
|
|
181
|
+
def __repr__(self) -> str:
|
|
178
182
|
return repr(self.__nested_secrets__)
|
|
179
183
|
|
|
180
|
-
def __setitem__(self, key, value) -> NoReturn:
|
|
184
|
+
def __setitem__(self, key: str, value: Any) -> NoReturn:
|
|
181
185
|
raise TypeError("Secrets does not support item assignment.")
|
|
182
186
|
|
|
183
|
-
def __setattr__(self, key, value) -> NoReturn:
|
|
187
|
+
def __setattr__(self, key: str, value: Any) -> NoReturn:
|
|
184
188
|
raise TypeError("Secrets does not support attribute assignment.")
|
|
185
189
|
|
|
186
190
|
def to_dict(self) -> dict[str, Any]:
|
|
@@ -194,7 +198,7 @@ class Secrets(Mapping[str, Any]):
|
|
|
194
198
|
Safe to use from multiple threads.
|
|
195
199
|
"""
|
|
196
200
|
|
|
197
|
-
def __init__(self):
|
|
201
|
+
def __init__(self) -> None:
|
|
198
202
|
# Our secrets dict.
|
|
199
203
|
self._secrets: Mapping[str, Any] | None = None
|
|
200
204
|
self._lock = threading.RLock()
|
|
@@ -272,7 +276,8 @@ class Secrets(Mapping[str, Any]):
|
|
|
272
276
|
return secrets, found_secrets_file
|
|
273
277
|
|
|
274
278
|
def _parse_directory(self, path: str) -> tuple[Mapping[str, Any], bool]:
|
|
275
|
-
"""Parse a directory for secrets. Directory style can be used to support Kubernetes secrets that are
|
|
279
|
+
"""Parse a directory for secrets. Directory style can be used to support Kubernetes secrets that are
|
|
280
|
+
mounted to folders.
|
|
276
281
|
|
|
277
282
|
Example structure:
|
|
278
283
|
- top_level_secret_folder
|
|
@@ -310,7 +315,7 @@ class Secrets(Mapping[str, Any]):
|
|
|
310
315
|
|
|
311
316
|
if len(sub_secrets) == 1:
|
|
312
317
|
# if there's just one file, collapse it so it's directly under `dirname`
|
|
313
|
-
secrets[dirname] = sub_secrets[
|
|
318
|
+
secrets[dirname] = sub_secrets[next(iter(sub_secrets.keys()))]
|
|
314
319
|
else:
|
|
315
320
|
secrets[dirname] = sub_secrets
|
|
316
321
|
|
|
@@ -356,7 +361,7 @@ class Secrets(Mapping[str, Any]):
|
|
|
356
361
|
|
|
357
362
|
secrets = {}
|
|
358
363
|
|
|
359
|
-
file_paths =
|
|
364
|
+
file_paths = config.get_option("secrets.files")
|
|
360
365
|
found_secrets_file = False
|
|
361
366
|
for path in file_paths:
|
|
362
367
|
path_secrets, found_secrets_file_in_path = self._parse_file_path(path)
|
|
@@ -380,7 +385,9 @@ class Secrets(Mapping[str, Any]):
|
|
|
380
385
|
return self._secrets
|
|
381
386
|
|
|
382
387
|
def to_dict(self) -> dict[str, Any]:
|
|
383
|
-
"""Converts the secrets store into a nested dictionary, where nested AttrDict objects are
|
|
388
|
+
"""Converts the secrets store into a nested dictionary, where nested AttrDict objects are
|
|
389
|
+
also converted into dictionaries.
|
|
390
|
+
"""
|
|
384
391
|
secrets = self._parse()
|
|
385
392
|
return _convert_to_dict(secrets)
|
|
386
393
|
|
|
@@ -407,7 +414,7 @@ class Secrets(Mapping[str, Any]):
|
|
|
407
414
|
if self._file_watchers_installed:
|
|
408
415
|
return
|
|
409
416
|
|
|
410
|
-
file_paths =
|
|
417
|
+
file_paths = config.get_option("secrets.files")
|
|
411
418
|
for path in file_paths:
|
|
412
419
|
try:
|
|
413
420
|
if path.endswith(".toml"):
|
|
@@ -422,7 +429,7 @@ class Secrets(Mapping[str, Any]):
|
|
|
422
429
|
self._on_secrets_changed,
|
|
423
430
|
watcher_type="poll",
|
|
424
431
|
)
|
|
425
|
-
except FileNotFoundError:
|
|
432
|
+
except FileNotFoundError: # noqa: PERF203
|
|
426
433
|
# A user may only have one secrets.toml file defined, so we'd expect
|
|
427
434
|
# FileNotFoundErrors to be raised when attempting to install a
|
|
428
435
|
# watcher on the nonexistent ones.
|
|
@@ -432,7 +439,7 @@ class Secrets(Mapping[str, Any]):
|
|
|
432
439
|
# failed to avoid repeatedly trying to install it.
|
|
433
440
|
self._file_watchers_installed = True
|
|
434
441
|
|
|
435
|
-
def _on_secrets_changed(self, changed_file_path) -> None:
|
|
442
|
+
def _on_secrets_changed(self, changed_file_path: str) -> None:
|
|
436
443
|
with self._lock:
|
|
437
444
|
_LOGGER.debug("Secret path %s changed, reloading", changed_file_path)
|
|
438
445
|
self._reset()
|
|
@@ -452,8 +459,7 @@ class Secrets(Mapping[str, Any]):
|
|
|
452
459
|
value = self._parse()[key]
|
|
453
460
|
if not isinstance(value, Mapping):
|
|
454
461
|
return value
|
|
455
|
-
|
|
456
|
-
return AttrDict(value)
|
|
462
|
+
return AttrDict(value)
|
|
457
463
|
# We add FileNotFoundError since __getattr__ is expected to only raise
|
|
458
464
|
# AttributeError. Without handling FileNotFoundError, unittests.mocks
|
|
459
465
|
# fails during mock creation on Python3.9
|
|
@@ -470,8 +476,7 @@ class Secrets(Mapping[str, Any]):
|
|
|
470
476
|
value = self._parse()[key]
|
|
471
477
|
if not isinstance(value, Mapping):
|
|
472
478
|
return value
|
|
473
|
-
|
|
474
|
-
return AttrDict(value)
|
|
479
|
+
return AttrDict(value)
|
|
475
480
|
except KeyError:
|
|
476
481
|
raise KeyError(_missing_key_error_message(key))
|
|
477
482
|
|
|
@@ -72,7 +72,8 @@ class SessionInfo:
|
|
|
72
72
|
return self.client is not None
|
|
73
73
|
|
|
74
74
|
def to_active(self) -> ActiveSessionInfo:
|
|
75
|
-
|
|
75
|
+
if not self.is_active():
|
|
76
|
+
raise RuntimeError("A SessionInfo with no client cannot be active!")
|
|
76
77
|
|
|
77
78
|
# NOTE: The cast here (rather than copying this SessionInfo's fields into a new
|
|
78
79
|
# ActiveSessionInfo) is important as the Runtime expects to be able to mutate
|
|
@@ -27,15 +27,15 @@ from streamlit.runtime.state.session_state_proxy import (
|
|
|
27
27
|
from streamlit.runtime.state.widgets import register_widget
|
|
28
28
|
|
|
29
29
|
__all__ = [
|
|
30
|
-
"
|
|
31
|
-
"WidgetCallback",
|
|
32
|
-
"WidgetKwargs",
|
|
30
|
+
"SCRIPT_RUN_WITHOUT_ERRORS_KEY",
|
|
33
31
|
"QueryParamsProxy",
|
|
34
32
|
"SafeSessionState",
|
|
35
|
-
"SCRIPT_RUN_WITHOUT_ERRORS_KEY",
|
|
36
33
|
"SessionState",
|
|
37
|
-
"SessionStateStatProvider",
|
|
38
34
|
"SessionStateProxy",
|
|
35
|
+
"SessionStateStatProvider",
|
|
36
|
+
"WidgetArgs",
|
|
37
|
+
"WidgetCallback",
|
|
38
|
+
"WidgetKwargs",
|
|
39
39
|
"get_session_state",
|
|
40
40
|
"register_widget",
|
|
41
41
|
]
|
|
@@ -51,7 +51,7 @@ WidgetCallback: TypeAlias = Callable[..., None]
|
|
|
51
51
|
# WidgetState proto, and returns a regular python value. A serializer
|
|
52
52
|
# receives a regular python value, and returns something suitable for
|
|
53
53
|
# a value field on WidgetState proto. They should be inverses.
|
|
54
|
-
WidgetDeserializer: TypeAlias = Callable[[Any
|
|
54
|
+
WidgetDeserializer: TypeAlias = Callable[[Any], T]
|
|
55
55
|
WidgetSerializer: TypeAlias = Callable[[T], Any]
|
|
56
56
|
|
|
57
57
|
# The array value field names are part of the larger set of possible value
|
|
@@ -156,7 +156,7 @@ class RegisterWidgetResult(Generic[T_co]):
|
|
|
156
156
|
"""The canonical way to construct a RegisterWidgetResult in cases
|
|
157
157
|
where the true widget value could not be determined.
|
|
158
158
|
"""
|
|
159
|
-
return cls(value=deserializer(None
|
|
159
|
+
return cls(value=deserializer(None), value_changed=False)
|
|
160
160
|
|
|
161
161
|
|
|
162
162
|
def user_key_from_element_id(element_id: str) -> str | None:
|
|
@@ -47,9 +47,7 @@ class QueryParams(MutableMapping[str, str]):
|
|
|
47
47
|
self._ensure_single_query_api_used()
|
|
48
48
|
|
|
49
49
|
return iter(
|
|
50
|
-
key
|
|
51
|
-
for key in self._query_params.keys()
|
|
52
|
-
if key not in EMBED_QUERY_PARAMS_KEYS
|
|
50
|
+
key for key in self._query_params if key not in EMBED_QUERY_PARAMS_KEYS
|
|
53
51
|
)
|
|
54
52
|
|
|
55
53
|
def __getitem__(self, key: str) -> str:
|
|
@@ -58,17 +56,17 @@ class QueryParams(MutableMapping[str, str]):
|
|
|
58
56
|
If the key is not present, raise KeyError.
|
|
59
57
|
"""
|
|
60
58
|
self._ensure_single_query_api_used()
|
|
59
|
+
if key in EMBED_QUERY_PARAMS_KEYS:
|
|
60
|
+
raise KeyError(missing_key_error_message(key))
|
|
61
|
+
|
|
61
62
|
try:
|
|
62
|
-
if key in EMBED_QUERY_PARAMS_KEYS:
|
|
63
|
-
raise KeyError(missing_key_error_message(key))
|
|
64
63
|
value = self._query_params[key]
|
|
65
64
|
if isinstance(value, list):
|
|
66
65
|
if len(value) == 0:
|
|
67
66
|
return ""
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return value[-1]
|
|
67
|
+
# Return the last value to mimic Tornado's behavior
|
|
68
|
+
# https://www.tornadoweb.org/en/stable/web.html#tornado.web.RequestHandler.get_query_argument
|
|
69
|
+
return value[-1]
|
|
72
70
|
return value
|
|
73
71
|
except KeyError:
|
|
74
72
|
raise KeyError(missing_key_error_message(key))
|
|
@@ -97,9 +95,9 @@ class QueryParams(MutableMapping[str, str]):
|
|
|
97
95
|
|
|
98
96
|
def __delitem__(self, key: str) -> None:
|
|
99
97
|
self._ensure_single_query_api_used()
|
|
98
|
+
if key in EMBED_QUERY_PARAMS_KEYS:
|
|
99
|
+
raise KeyError(missing_key_error_message(key))
|
|
100
100
|
try:
|
|
101
|
-
if key in EMBED_QUERY_PARAMS_KEYS:
|
|
102
|
-
raise KeyError(missing_key_error_message(key))
|
|
103
101
|
del self._query_params[key]
|
|
104
102
|
self._send_query_param_msg()
|
|
105
103
|
except KeyError:
|
|
@@ -111,12 +109,12 @@ class QueryParams(MutableMapping[str, str]):
|
|
|
111
109
|
| SupportsKeysAndGetItem[str, str | Iterable[str]] = (),
|
|
112
110
|
/,
|
|
113
111
|
**kwds: str,
|
|
114
|
-
):
|
|
112
|
+
) -> None:
|
|
115
113
|
# This overrides the `update` provided by MutableMapping
|
|
116
114
|
# to ensure only one one ForwardMsg is sent.
|
|
117
115
|
self._ensure_single_query_api_used()
|
|
118
116
|
if hasattr(other, "keys") and hasattr(other, "__getitem__"):
|
|
119
|
-
for key in other.keys():
|
|
117
|
+
for key in other.keys(): # noqa: SIM118
|
|
120
118
|
self.__set_item_internal(key, other[key])
|
|
121
119
|
else:
|
|
122
120
|
for key, value in other:
|
|
@@ -173,7 +171,7 @@ class QueryParams(MutableMapping[str, str]):
|
|
|
173
171
|
self,
|
|
174
172
|
_dict: Iterable[tuple[str, str | Iterable[str]]]
|
|
175
173
|
| SupportsKeysAndGetItem[str, str | Iterable[str]],
|
|
176
|
-
):
|
|
174
|
+
) -> None:
|
|
177
175
|
self._ensure_single_query_api_used()
|
|
178
176
|
old_value = self._query_params.copy()
|
|
179
177
|
self.clear_with_no_forward_msg(preserve_embed=True)
|
|
@@ -194,7 +192,7 @@ class QueryParams(MutableMapping[str, str]):
|
|
|
194
192
|
if key in EMBED_QUERY_PARAMS_KEYS and preserve_embed
|
|
195
193
|
}
|
|
196
194
|
|
|
197
|
-
def _ensure_single_query_api_used(self):
|
|
195
|
+
def _ensure_single_query_api_used(self) -> None:
|
|
198
196
|
ctx = get_script_run_ctx()
|
|
199
197
|
if ctx is None:
|
|
200
198
|
return
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
17
|
from collections.abc import Iterable, Iterator, MutableMapping
|
|
18
|
-
from typing import TYPE_CHECKING, overload
|
|
18
|
+
from typing import TYPE_CHECKING, Any, overload
|
|
19
19
|
|
|
20
20
|
from streamlit.runtime.metrics_util import gather_metrics
|
|
21
21
|
from streamlit.runtime.state.session_state_proxy import get_session_state
|
|
@@ -55,7 +55,7 @@ class QueryParamsProxy(MutableMapping[str, str]):
|
|
|
55
55
|
del qp[key]
|
|
56
56
|
|
|
57
57
|
@gather_metrics("query_params.set_item")
|
|
58
|
-
def __setitem__(self, key: str, value:
|
|
58
|
+
def __setitem__(self, key: str, value: Any) -> None:
|
|
59
59
|
with get_session_state().query_params() as qp:
|
|
60
60
|
qp[key] = value
|
|
61
61
|
|
|
@@ -76,18 +76,18 @@ class QueryParamsProxy(MutableMapping[str, str]):
|
|
|
76
76
|
|
|
77
77
|
@overload
|
|
78
78
|
def update(
|
|
79
|
-
self,
|
|
79
|
+
self, params: SupportsKeysAndGetItem[str, str | Iterable[str]], /, **kwds: str
|
|
80
80
|
) -> None: ...
|
|
81
81
|
|
|
82
82
|
@overload
|
|
83
83
|
def update(
|
|
84
|
-
self,
|
|
84
|
+
self, params: Iterable[tuple[str, str | Iterable[str]]], /, **kwds: str
|
|
85
85
|
) -> None: ...
|
|
86
86
|
|
|
87
87
|
@overload
|
|
88
88
|
def update(self, **kwds: str | Iterable[str]) -> None: ...
|
|
89
89
|
|
|
90
|
-
def update(self,
|
|
90
|
+
def update(self, params=(), /, **kwds) -> None: # type: ignore
|
|
91
91
|
"""
|
|
92
92
|
Update one or more values in query_params at once from a dictionary or
|
|
93
93
|
dictionary-like object.
|
|
@@ -102,10 +102,10 @@ class QueryParamsProxy(MutableMapping[str, str]):
|
|
|
102
102
|
Additional key/value pairs to update passed as keyword arguments.
|
|
103
103
|
"""
|
|
104
104
|
with get_session_state().query_params() as qp:
|
|
105
|
-
qp.update(
|
|
105
|
+
qp.update(params, **kwds)
|
|
106
106
|
|
|
107
107
|
@gather_metrics("query_params.set_attr")
|
|
108
|
-
def __setattr__(self, key: str, value:
|
|
108
|
+
def __setattr__(self, key: str, value: Any) -> None:
|
|
109
109
|
with get_session_state().query_params() as qp:
|
|
110
110
|
qp[key] = value
|
|
111
111
|
|
|
@@ -159,23 +159,25 @@ class QueryParamsProxy(MutableMapping[str, str]):
|
|
|
159
159
|
Returns
|
|
160
160
|
-------
|
|
161
161
|
Dict[str,str]
|
|
162
|
-
A dictionary of the current query
|
|
162
|
+
A dictionary of the current query parameters in the app's URL.
|
|
163
163
|
"""
|
|
164
164
|
with get_session_state().query_params() as qp:
|
|
165
165
|
return qp.to_dict()
|
|
166
166
|
|
|
167
167
|
@overload
|
|
168
|
-
def from_dict(
|
|
169
|
-
self, keys_and_values: Iterable[tuple[str, str | Iterable[str]]]
|
|
170
|
-
) -> None: ...
|
|
168
|
+
def from_dict(self, params: Iterable[tuple[str, str | Iterable[str]]]) -> None: ...
|
|
171
169
|
|
|
172
170
|
@overload
|
|
173
171
|
def from_dict(
|
|
174
|
-
self,
|
|
172
|
+
self, params: SupportsKeysAndGetItem[str, str | Iterable[str]]
|
|
175
173
|
) -> None: ...
|
|
176
174
|
|
|
177
175
|
@gather_metrics("query_params.from_dict")
|
|
178
|
-
def from_dict(
|
|
176
|
+
def from_dict(
|
|
177
|
+
self,
|
|
178
|
+
params: SupportsKeysAndGetItem[str, str | Iterable[str]]
|
|
179
|
+
| Iterable[tuple[str, str | Iterable[str]]],
|
|
180
|
+
) -> None:
|
|
179
181
|
"""
|
|
180
182
|
Set all of the query parameters from a dictionary or dictionary-like object.
|
|
181
183
|
|
|
@@ -211,8 +213,10 @@ class QueryParamsProxy(MutableMapping[str, str]):
|
|
|
211
213
|
|
|
212
214
|
@staticmethod
|
|
213
215
|
def missing_key_error_message(key: str) -> str:
|
|
216
|
+
"""Returns a formatted error message for missing keys."""
|
|
214
217
|
return f'st.query_params has no key "{key}".'
|
|
215
218
|
|
|
216
219
|
@staticmethod
|
|
217
220
|
def missing_attr_error_message(key: str) -> str:
|
|
221
|
+
"""Returns a formatted error message for missing attributes."""
|
|
218
222
|
return f'st.query_params has no attribute "{key}".'
|
|
@@ -42,7 +42,7 @@ class SafeSessionState:
|
|
|
42
42
|
_lock: threading.RLock
|
|
43
43
|
_yield_callback: Callable[[], None]
|
|
44
44
|
|
|
45
|
-
def __init__(self, state: SessionState, yield_callback: Callable[[], None]):
|
|
45
|
+
def __init__(self, state: SessionState, yield_callback: Callable[[], None]) -> None:
|
|
46
46
|
# Fields must be set using the object's setattr method to avoid
|
|
47
47
|
# infinite recursion from trying to look up the fields we're setting.
|
|
48
48
|
object.__setattr__(self, "_state", state)
|
|
@@ -125,7 +125,7 @@ class SafeSessionState:
|
|
|
125
125
|
except KeyError:
|
|
126
126
|
raise AttributeError(f"{key} not found in session_state.")
|
|
127
127
|
|
|
128
|
-
def __repr__(self):
|
|
128
|
+
def __repr__(self) -> str:
|
|
129
129
|
"""Presents itself as a simple dict of the underlying SessionState instance."""
|
|
130
130
|
kv = ((k, self._state[k]) for k in self._state._keys())
|
|
131
131
|
s = ", ".join(f"{k}: {v!r}" for k, v in kv)
|