streamlit 1.45.0__py3-none-any.whl → 1.46.0__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/file_uploader_utils.py +7 -2
- 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 +55 -34
- 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.DyIfDYvY.js → ErrorOutline.esm.6PVAQvlT.js} +1 -1
- streamlit/static/static/js/{FileDownload.esm.kF1FCxeJ.js → FileDownload.esm.BZQHC61b.js} +1 -1
- streamlit/static/static/js/{FileHelper.DKt6tIeO.js → FileHelper.Bn1VShMJ.js} +5 -5
- streamlit/static/static/js/{FormClearHelper.DpJR9YCu.js → FormClearHelper.CsFEiTNN.js} +1 -1
- streamlit/static/static/js/{Hooks.BT6PF2Zi.js → Hooks.DguOHQL1.js} +1 -1
- streamlit/static/static/js/{InputInstructions.BmnD4oa3.js → InputInstructions.CTYn2BJQ.js} +1 -1
- streamlit/static/static/js/{ProgressBar.Ch7VNdkM.js → ProgressBar.CPOGBKCi.js} +2 -2
- streamlit/static/static/js/{RenderInPortalIfExists.43tDswzK.js → RenderInPortalIfExists.BYu_CZaF.js} +1 -1
- streamlit/static/static/js/Toolbar.gXKw7ANv.js +1 -0
- streamlit/static/static/js/UploadFileInfo.0DCkpDDf.js +6 -0
- streamlit/static/static/js/{base-input.BjeC3XFX.js → base-input.DBYPj91R.js} +4 -4
- streamlit/static/static/js/{checkbox.DIVN0GOS.js → checkbox.BUm2vnNv.js} +2 -2
- streamlit/static/static/js/{createSuper.CKyBiJe0.js → createSuper.KD4RuZ-W.js} +1 -1
- streamlit/static/static/js/{data-grid-overlay-editor.CXpaXRk5.js → data-grid-overlay-editor.CUwpDfvI.js} +1 -1
- streamlit/static/static/js/{downloader.B-uWAyLB.js → downloader.CkDtclup.js} +1 -1
- streamlit/static/static/js/{es6._eGNfJ2i.js → es6.Dlcvh_r0.js} +2 -2
- streamlit/static/static/js/{iframeResizer.contentWindow.CebQfV1Q.js → iframeResizer.contentWindow.DOXlFfve.js} +1 -1
- streamlit/static/static/js/{index.EbMzSayc.js → index.B0cuGMAB.js} +25 -25
- streamlit/static/static/js/index.BCWTclSV.js +73 -0
- streamlit/static/static/js/index.BJY_fap7.js +1 -0
- streamlit/static/static/js/index.BL3l6dnk.js +1 -0
- streamlit/static/static/js/{index.CpV1hnf8.js → index.BMZzRZjB.js} +1 -1
- streamlit/static/static/js/{index.l6QfBDTC.js → index.BOzUTGDe.js} +1 -1
- streamlit/static/static/js/index.BYI5iO-o.js +1 -0
- streamlit/static/static/js/index.BYo0ywlm.js +783 -0
- streamlit/static/static/js/{index.CuUJHsRK.js → index.BYz9btsY.js} +1 -1
- streamlit/static/static/js/{index.C_nMqHLH.js → index.CCVzQz0Z.js} +2 -2
- streamlit/static/static/js/index.CD6FydK9.js +1 -0
- streamlit/static/static/js/index.CDYEqgC8.js +2 -0
- streamlit/static/static/js/{index.BAdBHmJD.js → index.CMP9c4xA.js} +1 -1
- streamlit/static/static/js/index.CN30QAPD.js +1 -0
- streamlit/static/static/js/{index.CtTgot1Z.js → index.CNqWQkTe.js} +1 -1
- streamlit/static/static/js/index.CaxS67Xz.js +1 -0
- streamlit/static/static/js/{index.HsXxdgGd.js → index.CbsT4sGW.js} +1 -1
- streamlit/static/static/js/index.ChAVlxpQ.js +1 -0
- streamlit/static/static/js/{index.BaYSBSaz.js → index.ClLMMmDd.js} +1 -1
- streamlit/static/static/js/{index.CxxktCLw.js → index.D-O9rQmV.js} +1 -1
- streamlit/static/static/js/{index.Coc8OVG7.js → index.D4k7VZZL.js} +1 -1
- streamlit/static/static/js/index.DLBi0Ar1.js +1 -0
- streamlit/static/static/js/index.DVq5XmJo.js +197 -0
- streamlit/static/static/js/{index.ClX0ambk.js → index.DZKmKXWw.js} +1 -1
- streamlit/static/static/js/index.DkaVx80F.js +1 -0
- streamlit/static/static/js/index.Dr968Klx.js +1 -0
- streamlit/static/static/js/{index.BaozEIL-.js → index.DtUYLn9j.js} +20 -20
- streamlit/static/static/js/index.DwjYSyhs.js +1 -0
- streamlit/static/static/js/index.DzrImxu4.js +1 -0
- streamlit/static/static/js/index.HyGsn4VM.js +1 -0
- streamlit/static/static/js/index.OwxC65od.js +12 -0
- streamlit/static/static/js/index.PZs7VZkC.js +1 -0
- streamlit/static/static/js/index.Voiqpj4q.js +1 -0
- streamlit/static/static/js/index.bSROvR-J.js +3 -0
- streamlit/static/static/js/index.oT9GD3l4.js +1 -0
- streamlit/static/static/js/{index.schmj9D9.js → index.qb-yAPH6.js} +255 -255
- streamlit/static/static/js/index.rJFy_Ygy.js +2 -0
- streamlit/static/static/js/{input.VQEe_bZy.js → input.CwQtEnFN.js} +2 -2
- streamlit/static/static/js/{memory.DGVHab07.js → memory.C5XaFIjR.js} +1 -1
- streamlit/static/static/js/{mergeWith.-RIuUGoA.js → mergeWith.DzwwH6AG.js} +1 -1
- streamlit/static/static/js/{number-overlay-editor.wzFLIbEE.js → number-overlay-editor.Dx0XqCkD.js} +1 -1
- streamlit/static/static/js/{possibleConstructorReturn.GIObu6rf.js → possibleConstructorReturn.CVfSu9Ws.js} +1 -1
- streamlit/static/static/js/{sandbox.DpjSeqe2.js → sandbox.BT0gdMXk.js} +1 -1
- streamlit/static/static/js/{textarea.Dz0J9LZe.js → textarea.DNCbrtbM.js} +2 -2
- streamlit/static/static/js/{timepicker.BLgJZnzX.js → timepicker.4UYJD9Ts.js} +1 -1
- streamlit/static/static/js/{toConsumableArray.0OZlxz7U.js → toConsumableArray.DUmnaVWV.js} +1 -1
- streamlit/static/static/js/{uniqueId.DbzplC8D.js → uniqueId.DUvh-GL8.js} +1 -1
- streamlit/static/static/js/{useBasicWidgetState.COJZng1S.js → useBasicWidgetState.Cwd7-jJa.js} +1 -1
- streamlit/static/static/js/useOnInputChange.DvemQrOM.js +1 -0
- streamlit/static/static/js/{withFullScreenWrapper.CzWvNbvi.js → withFullScreenWrapper.CiQ10ByU.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 +15 -1
- 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 +6 -18
- streamlit/web/server/server_util.py +19 -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.0.dist-info → streamlit-1.46.0.dist-info}/METADATA +4 -4
- streamlit-1.46.0.dist-info/RECORD +559 -0
- {streamlit-1.45.0.dist-info → streamlit-1.46.0.dist-info}/WHEEL +1 -1
- streamlit/elements/lib/event_utils.py +0 -39
- streamlit/static/static/js/Toolbar.HTGsjfCP.js +0 -1
- streamlit/static/static/js/UploadFileInfo.C-jY39rj.js +0 -1
- streamlit/static/static/js/index.B5TWFN5r.js +0 -1
- streamlit/static/static/js/index.BI9-p_-s.js +0 -3
- streamlit/static/static/js/index.BYHnDZYn.js +0 -1
- streamlit/static/static/js/index.B_M97aPz.js +0 -1
- streamlit/static/static/js/index.BdN5swP0.js +0 -1
- streamlit/static/static/js/index.BfCAp_Sj.js +0 -1
- streamlit/static/static/js/index.BqDl3eRM.js +0 -779
- streamlit/static/static/js/index.C0VtYb3T.js +0 -2
- streamlit/static/static/js/index.C1qW_Owy.js +0 -1
- streamlit/static/static/js/index.C4AcBARa.js +0 -1
- streamlit/static/static/js/index.CCOiJRk1.js +0 -1
- streamlit/static/static/js/index.COvpza5W.js +0 -1
- streamlit/static/static/js/index.C_9qGjbK.js +0 -2
- streamlit/static/static/js/index.CqISBfsc.js +0 -197
- streamlit/static/static/js/index.D1WOs2Ce.js +0 -1
- streamlit/static/static/js/index.DQljs-9e.js +0 -1
- streamlit/static/static/js/index.DZqX4P_2.js +0 -1
- streamlit/static/static/js/index.DajIfBOb.js +0 -1
- streamlit/static/static/js/index.DuOXre0H.js +0 -1
- streamlit/static/static/js/index.EWD98YhP.js +0 -1
- streamlit/static/static/js/index.LaZloCTl.js +0 -73
- streamlit/static/static/js/index.S_1klBoy.js +0 -1
- streamlit/static/static/js/index.b0Gf958T.js +0 -12
- streamlit/static/static/js/index.t--hEgTQ.js +0 -6
- streamlit/static/static/js/useOnInputChange.CgOwAHyw.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.0.dist-info/RECORD +0 -568
- {streamlit-1.45.0.data → streamlit-1.46.0.data}/scripts/streamlit.cmd +0 -0
- {streamlit-1.45.0.dist-info → streamlit-1.46.0.dist-info}/entry_points.txt +0 -0
- {streamlit-1.45.0.dist-info → streamlit-1.46.0.dist-info}/top_level.txt +0 -0
streamlit/__init__.py
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
# isort: skip_file
|
|
16
|
+
# ruff: noqa: E402, PLC0414, A001
|
|
16
17
|
|
|
17
18
|
"""Streamlit.
|
|
18
19
|
|
|
@@ -286,7 +287,10 @@ experimental_dialog = _experimental_dialog_decorator
|
|
|
286
287
|
experimental_fragment = _experimental_fragment
|
|
287
288
|
experimental_user = _DeprecatedUserInfoProxy()
|
|
288
289
|
|
|
289
|
-
_EXPERIMENTAL_QUERY_PARAMS_DEPRECATE_MSG =
|
|
290
|
+
_EXPERIMENTAL_QUERY_PARAMS_DEPRECATE_MSG = (
|
|
291
|
+
"Refer to our [docs page](https://docs.streamlit.io/develop/api-reference/caching-and-state/st.query_params) "
|
|
292
|
+
"for more information."
|
|
293
|
+
)
|
|
290
294
|
|
|
291
295
|
experimental_get_query_params = _deprecate_func_name(
|
|
292
296
|
_get_query_params,
|
streamlit/auth_util.py
CHANGED
|
@@ -32,28 +32,28 @@ if TYPE_CHECKING:
|
|
|
32
32
|
class AuthCache:
|
|
33
33
|
"""Simple cache implementation for storing info required for Authlib."""
|
|
34
34
|
|
|
35
|
-
def __init__(self):
|
|
36
|
-
self.cache = {}
|
|
35
|
+
def __init__(self) -> None:
|
|
36
|
+
self.cache: dict[str, Any] = {}
|
|
37
37
|
|
|
38
|
-
def get(self, key):
|
|
38
|
+
def get(self, key: str) -> Any:
|
|
39
39
|
return self.cache.get(key)
|
|
40
40
|
|
|
41
41
|
# for set method, we are follow the same signature used in Authlib
|
|
42
42
|
# the expires_in is not used in our case
|
|
43
|
-
def set(self, key, value, expires_in):
|
|
43
|
+
def set(self, key: str, value: Any, expires_in: int | None = None) -> None: # noqa: ARG002
|
|
44
44
|
self.cache[key] = value
|
|
45
45
|
|
|
46
|
-
def get_dict(self):
|
|
46
|
+
def get_dict(self) -> dict[str, Any]:
|
|
47
47
|
return self.cache
|
|
48
48
|
|
|
49
|
-
def delete(self, key):
|
|
49
|
+
def delete(self, key: str) -> None:
|
|
50
50
|
self.cache.pop(key, None)
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
def is_authlib_installed() -> bool:
|
|
54
54
|
"""Check if Authlib is installed."""
|
|
55
55
|
try:
|
|
56
|
-
import authlib
|
|
56
|
+
import authlib
|
|
57
57
|
|
|
58
58
|
authlib_version = authlib.__version__
|
|
59
59
|
authlib_version_tuple = tuple(map(int, authlib_version.split(".")))
|
|
@@ -87,7 +87,7 @@ def get_secrets_auth_section() -> AttrDict:
|
|
|
87
87
|
def encode_provider_token(provider: str) -> str:
|
|
88
88
|
"""Returns a signed JWT token with the provider and expiration time."""
|
|
89
89
|
try:
|
|
90
|
-
from authlib.jose import jwt
|
|
90
|
+
from authlib.jose import jwt
|
|
91
91
|
except ImportError:
|
|
92
92
|
raise StreamlitAuthError(
|
|
93
93
|
"""To use authentication features, you need to install Authlib>=1.3.2, e.g. via `pip install Authlib`."""
|
|
@@ -116,7 +116,7 @@ def decode_provider_token(provider_token: str) -> ProviderTokenPayload:
|
|
|
116
116
|
# the 'exp' (and it is not expired), and 'provider' field exists.
|
|
117
117
|
claim_options = {"exp": {"essential": True}, "provider": {"essential": True}}
|
|
118
118
|
try:
|
|
119
|
-
payload: JWTClaims = jwt.decode(
|
|
119
|
+
payload: JWTClaims = jwt.decode( # type: ignore[no-untyped-call]
|
|
120
120
|
provider_token, get_signing_secret(), claims_options=claim_options
|
|
121
121
|
)
|
|
122
122
|
payload.validate()
|
|
@@ -126,7 +126,7 @@ def decode_provider_token(provider_token: str) -> ProviderTokenPayload:
|
|
|
126
126
|
return cast("ProviderTokenPayload", payload)
|
|
127
127
|
|
|
128
128
|
|
|
129
|
-
def generate_default_provider_section(auth_section) -> dict[str, Any]:
|
|
129
|
+
def generate_default_provider_section(auth_section: AttrDict) -> dict[str, Any]:
|
|
130
130
|
"""Generate a default provider section for the 'auth' section of secrets.toml."""
|
|
131
131
|
default_provider_section = {}
|
|
132
132
|
if auth_section.get("client_id"):
|
|
@@ -138,8 +138,8 @@ def generate_default_provider_section(auth_section) -> dict[str, Any]:
|
|
|
138
138
|
"server_metadata_url"
|
|
139
139
|
)
|
|
140
140
|
if auth_section.get("client_kwargs"):
|
|
141
|
-
default_provider_section["client_kwargs"] =
|
|
142
|
-
"client_kwargs"
|
|
141
|
+
default_provider_section["client_kwargs"] = cast(
|
|
142
|
+
"AttrDict", auth_section.get("client_kwargs", AttrDict({}))
|
|
143
143
|
).to_dict()
|
|
144
144
|
return default_provider_section
|
|
145
145
|
|
streamlit/cli_util.py
CHANGED
|
@@ -18,11 +18,12 @@ from __future__ import annotations
|
|
|
18
18
|
|
|
19
19
|
import os
|
|
20
20
|
import subprocess
|
|
21
|
+
from typing import Any
|
|
21
22
|
|
|
22
23
|
from streamlit import env_util, errors
|
|
23
24
|
|
|
24
25
|
|
|
25
|
-
def print_to_cli(message: str, **kwargs) -> None:
|
|
26
|
+
def print_to_cli(message: str, **kwargs: Any) -> None:
|
|
26
27
|
"""Print a message to the terminal using click if available, else print
|
|
27
28
|
using the built-in print function.
|
|
28
29
|
|
|
@@ -36,7 +37,7 @@ def print_to_cli(message: str, **kwargs) -> None:
|
|
|
36
37
|
print(message, flush=True) # noqa: T201
|
|
37
38
|
|
|
38
39
|
|
|
39
|
-
def style_for_cli(message: str, **kwargs) -> str:
|
|
40
|
+
def style_for_cli(message: str, **kwargs: Any) -> str:
|
|
40
41
|
"""Style a message using click if available, else return the message
|
|
41
42
|
unchanged.
|
|
42
43
|
|
|
@@ -60,7 +61,7 @@ def _open_browser_with_webbrowser(url: str) -> None:
|
|
|
60
61
|
def _open_browser_with_command(command: str, url: str) -> None:
|
|
61
62
|
cmd_line = [command, url]
|
|
62
63
|
with open(os.devnull, "w") as devnull:
|
|
63
|
-
subprocess.Popen(cmd_line, stdout=devnull, stderr=subprocess.STDOUT)
|
|
64
|
+
subprocess.Popen(cmd_line, stdout=devnull, stderr=subprocess.STDOUT) # noqa: S603
|
|
64
65
|
|
|
65
66
|
|
|
66
67
|
def open_browser(url: str) -> None:
|
streamlit/column_config.py
CHANGED
|
@@ -12,27 +12,29 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
"""Column types that can be configured via the ``column_config`` parameter of
|
|
15
|
+
"""Column types that can be configured via the ``column_config`` parameter of
|
|
16
|
+
``st.dataframe`` and ``st.data_editor``.
|
|
17
|
+
"""
|
|
16
18
|
|
|
17
19
|
from __future__ import annotations
|
|
18
20
|
|
|
19
21
|
__all__ = [
|
|
20
22
|
"AreaChartColumn",
|
|
21
|
-
"Column",
|
|
22
|
-
"TextColumn",
|
|
23
|
-
"NumberColumn",
|
|
24
23
|
"BarChartColumn",
|
|
25
24
|
"CheckboxColumn",
|
|
25
|
+
"Column",
|
|
26
|
+
"DateColumn",
|
|
26
27
|
"DatetimeColumn",
|
|
27
28
|
"ImageColumn",
|
|
28
|
-
"
|
|
29
|
-
"ProgressColumn",
|
|
30
|
-
"LinkColumn",
|
|
29
|
+
"JsonColumn",
|
|
31
30
|
"LineChartColumn",
|
|
31
|
+
"LinkColumn",
|
|
32
32
|
"ListColumn",
|
|
33
|
-
"
|
|
33
|
+
"NumberColumn",
|
|
34
|
+
"ProgressColumn",
|
|
35
|
+
"SelectboxColumn",
|
|
36
|
+
"TextColumn",
|
|
34
37
|
"TimeColumn",
|
|
35
|
-
"JsonColumn",
|
|
36
38
|
]
|
|
37
39
|
|
|
38
40
|
|
streamlit/commands/echo.py
CHANGED
|
@@ -19,12 +19,12 @@ import contextlib
|
|
|
19
19
|
import re
|
|
20
20
|
import textwrap
|
|
21
21
|
import traceback
|
|
22
|
-
from typing import TYPE_CHECKING, Any
|
|
22
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
23
23
|
|
|
24
24
|
from streamlit.runtime.metrics_util import gather_metrics
|
|
25
25
|
|
|
26
26
|
if TYPE_CHECKING:
|
|
27
|
-
from collections.abc import Iterable
|
|
27
|
+
from collections.abc import Generator, Iterable
|
|
28
28
|
|
|
29
29
|
_SPACES_RE = re.compile("\\s*")
|
|
30
30
|
_EMPTY_LINE_RE = re.compile("\\s*\n")
|
|
@@ -32,7 +32,9 @@ _EMPTY_LINE_RE = re.compile("\\s*\n")
|
|
|
32
32
|
|
|
33
33
|
@gather_metrics("echo")
|
|
34
34
|
@contextlib.contextmanager
|
|
35
|
-
def echo(
|
|
35
|
+
def echo(
|
|
36
|
+
code_location: Literal["above", "below"] = "above",
|
|
37
|
+
) -> Generator[None, None, None]:
|
|
36
38
|
"""Use in a `with` block to draw some code on the app, then execute it.
|
|
37
39
|
|
|
38
40
|
Parameters
|
|
@@ -99,7 +101,7 @@ def echo(code_location="above"):
|
|
|
99
101
|
show_code(code_string, "python")
|
|
100
102
|
|
|
101
103
|
except FileNotFoundError as err:
|
|
102
|
-
show_warning("Unable to display code.
|
|
104
|
+
show_warning(f"Unable to display code. {err}")
|
|
103
105
|
|
|
104
106
|
|
|
105
107
|
def _get_initial_indent(lines: Iterable[str]) -> int:
|
|
@@ -64,41 +64,39 @@ def _new_fragment_id_queue(
|
|
|
64
64
|
if scope == "app":
|
|
65
65
|
return []
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
67
|
+
# > scope == "fragment"
|
|
68
|
+
curr_queue = ctx.fragment_ids_this_run
|
|
69
|
+
|
|
70
|
+
# If st.rerun(scope="fragment") is called during a full script run, we raise an
|
|
71
|
+
# exception. This occurs, of course, if st.rerun(scope="fragment") is called
|
|
72
|
+
# outside of a fragment, but it somewhat surprisingly occurs if it gets called
|
|
73
|
+
# from within a fragment during a run of the full script. While this behavior may
|
|
74
|
+
# be surprising, it seems somewhat reasonable given that the correct behavior of
|
|
75
|
+
# calling st.rerun(scope="fragment") in this situation is unclear to me:
|
|
76
|
+
# * Rerunning just the fragment immediately may cause weirdness down the line
|
|
77
|
+
# as any part of the script that occurs after the fragment will not be
|
|
78
|
+
# executed.
|
|
79
|
+
# * Waiting until the full script run completes before rerunning the fragment
|
|
80
|
+
# seems odd (even if we normally do this before running a fragment not
|
|
81
|
+
# triggered by st.rerun()) because it defers the execution of st.rerun().
|
|
82
|
+
# * Rerunning the full app feels incorrect as we're seemingly ignoring the
|
|
83
|
+
# `scope` argument.
|
|
84
|
+
# With these issues and given that it seems pretty unnatural to have a
|
|
85
|
+
# fragment-scoped rerun happen during a full script run to begin with, it seems
|
|
86
|
+
# reasonable to just disallow this completely for now.
|
|
87
|
+
if not curr_queue:
|
|
88
|
+
raise StreamlitAPIException(
|
|
89
|
+
'scope="fragment" can only be specified from `@st.fragment`-decorated '
|
|
90
|
+
"functions during fragment reruns."
|
|
91
|
+
)
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
)
|
|
97
|
-
), (
|
|
93
|
+
new_queue = list(dropwhile(lambda x: x != ctx.current_fragment_id, curr_queue))
|
|
94
|
+
if not new_queue:
|
|
95
|
+
raise RuntimeError(
|
|
98
96
|
"Could not find current_fragment_id in fragment_id_queue. This should never happen."
|
|
99
97
|
)
|
|
100
98
|
|
|
101
|
-
|
|
99
|
+
return new_queue
|
|
102
100
|
|
|
103
101
|
|
|
104
102
|
@gather_metrics("rerun")
|
|
@@ -222,7 +220,9 @@ def switch_page(page: str | Path | StreamlitPage) -> NoReturn: # type: ignore[m
|
|
|
222
220
|
|
|
223
221
|
if len(matched_pages) == 0:
|
|
224
222
|
raise StreamlitAPIException(
|
|
225
|
-
f"Could not find page: `{page}`. Must be the file path relative to the main script,
|
|
223
|
+
f"Could not find page: `{page}`. Must be the file path relative to the main script, "
|
|
224
|
+
f"from the directory: `{os.path.basename(main_script_directory)}`. Only the main app file "
|
|
225
|
+
"and files in the `pages/` directory are supported."
|
|
226
226
|
)
|
|
227
227
|
|
|
228
228
|
page_script_hash = matched_pages[0]["page_script_hash"]
|
|
@@ -236,6 +236,7 @@ def switch_page(page: str | Path | StreamlitPage) -> NoReturn: # type: ignore[m
|
|
|
236
236
|
query_string=ctx.query_string,
|
|
237
237
|
page_script_hash=page_script_hash,
|
|
238
238
|
cached_message_hashes=ctx.cached_message_hashes,
|
|
239
|
+
context_info=ctx.context_info,
|
|
239
240
|
)
|
|
240
241
|
)
|
|
241
242
|
# Force a yield point so the runner can do the rerun
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
|
-
import urllib.parse as parse
|
|
18
17
|
from typing import Any
|
|
18
|
+
from urllib import parse
|
|
19
19
|
|
|
20
20
|
from streamlit.errors import StreamlitAPIException
|
|
21
21
|
from streamlit.proto.ForwardMsg_pb2 import ForwardMsg
|
|
@@ -123,7 +123,7 @@ def _extract_key_query_params(
|
|
|
123
123
|
item.lower()
|
|
124
124
|
for sublist in [
|
|
125
125
|
[value.lower() for value in query_params[key]]
|
|
126
|
-
for key in query_params
|
|
126
|
+
for key in query_params
|
|
127
127
|
if key.lower() == param_key and query_params.get(key)
|
|
128
128
|
]
|
|
129
129
|
for item in sublist
|
streamlit/commands/logo.py
CHANGED
|
@@ -26,8 +26,12 @@ from streamlit.runtime.metrics_util import gather_metrics
|
|
|
26
26
|
from streamlit.runtime.scriptrunner_utils.script_run_context import get_script_run_ctx
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
def _invalid_logo_text(field_name: str):
|
|
30
|
-
return
|
|
29
|
+
def _invalid_logo_text(field_name: str) -> str:
|
|
30
|
+
return (
|
|
31
|
+
f"The {field_name} passed to st.logo is invalid - See "
|
|
32
|
+
"[documentation](https://docs.streamlit.io/develop/api-reference/media/st.logo) "
|
|
33
|
+
"for more information on valid types"
|
|
34
|
+
)
|
|
31
35
|
|
|
32
36
|
|
|
33
37
|
@gather_metrics("logo")
|
|
@@ -154,7 +158,8 @@ def logo(
|
|
|
154
158
|
fwd_msg.logo.link = link
|
|
155
159
|
else:
|
|
156
160
|
raise StreamlitAPIException(
|
|
157
|
-
f"Invalid link: {link} - the link param supports external links only and must
|
|
161
|
+
f"Invalid link: {link} - the link param supports external links only and must "
|
|
162
|
+
f"start with either http:// or https://."
|
|
158
163
|
)
|
|
159
164
|
|
|
160
165
|
if icon_image:
|
|
@@ -171,7 +176,7 @@ def logo(
|
|
|
171
176
|
except Exception as ex:
|
|
172
177
|
raise StreamlitAPIException(_invalid_logo_text("icon_image")) from ex
|
|
173
178
|
|
|
174
|
-
def validate_size(size):
|
|
179
|
+
def validate_size(size: str) -> str:
|
|
175
180
|
if isinstance(size, str):
|
|
176
181
|
image_size = size.lower()
|
|
177
182
|
valid_sizes = ["small", "medium", "large"]
|
streamlit/commands/navigation.py
CHANGED
|
@@ -68,13 +68,12 @@ def pages_from_nav_sections(
|
|
|
68
68
|
) -> list[StreamlitPage]:
|
|
69
69
|
page_list = []
|
|
70
70
|
for pages in nav_sections.values():
|
|
71
|
-
|
|
72
|
-
page_list.append(page)
|
|
71
|
+
page_list.extend(pages.copy())
|
|
73
72
|
|
|
74
73
|
return page_list
|
|
75
74
|
|
|
76
75
|
|
|
77
|
-
def send_page_not_found(ctx: ScriptRunContext):
|
|
76
|
+
def send_page_not_found(ctx: ScriptRunContext) -> None:
|
|
78
77
|
msg = ForwardMsg()
|
|
79
78
|
msg.page_not_found.page_name = ""
|
|
80
79
|
ctx.enqueue(msg)
|
|
@@ -84,7 +83,7 @@ def send_page_not_found(ctx: ScriptRunContext):
|
|
|
84
83
|
def navigation(
|
|
85
84
|
pages: Sequence[PageType] | Mapping[SectionHeader, Sequence[PageType]],
|
|
86
85
|
*,
|
|
87
|
-
position: Literal["sidebar", "hidden"] = "sidebar",
|
|
86
|
+
position: Literal["sidebar", "hidden", "top"] = "sidebar",
|
|
88
87
|
expanded: bool = False,
|
|
89
88
|
) -> StreamlitPage:
|
|
90
89
|
"""
|
|
@@ -121,7 +120,8 @@ def navigation(
|
|
|
121
120
|
To create labeled sections or page groupings within the navigation
|
|
122
121
|
menu, ``pages`` must be a dictionary. Each key is the label of a
|
|
123
122
|
section and each value is the list of page-like objects for
|
|
124
|
-
that section.
|
|
123
|
+
that section. If you use ``position="top"``, each grouping will be a
|
|
124
|
+
collapsible item in the navigation menu.
|
|
125
125
|
|
|
126
126
|
When you use a string or path as a page-like object, they are
|
|
127
127
|
internally passed to ``st.Page`` and converted to ``StreamlitPage``
|
|
@@ -129,10 +129,11 @@ def navigation(
|
|
|
129
129
|
path inferred from its path or filename. To customize these attributes
|
|
130
130
|
for your page, initialize your page with ``st.Page``.
|
|
131
131
|
|
|
132
|
-
position : "sidebar" or "hidden"
|
|
132
|
+
position : "sidebar", "top", or "hidden"
|
|
133
133
|
The position of the navigation menu. If this is ``"sidebar"``
|
|
134
134
|
(default), the navigation widget appears at the top of the sidebar. If
|
|
135
|
-
this is ``"
|
|
135
|
+
this is ``"top"``, the navigation appears in the top header of the app.
|
|
136
|
+
If this is ``"hidden"``, the navigation widget is not displayed.
|
|
136
137
|
|
|
137
138
|
If there is only one page in ``pages``, the navigation will be hidden
|
|
138
139
|
for any value of ``position``.
|
|
@@ -148,6 +149,8 @@ def navigation(
|
|
|
148
149
|
``expanded=False`` on a rerun, the menu will stay expanded and a
|
|
149
150
|
collapse button will be displayed.
|
|
150
151
|
|
|
152
|
+
The parameter is only used when ``position="sidebar"``.
|
|
153
|
+
|
|
151
154
|
Returns
|
|
152
155
|
-------
|
|
153
156
|
StreamlitPage
|
|
@@ -226,7 +229,38 @@ def navigation(
|
|
|
226
229
|
https://doc-navigation-example-2.streamlit.app/
|
|
227
230
|
height: 300px
|
|
228
231
|
|
|
229
|
-
|
|
232
|
+
|
|
233
|
+
**Example 3: Use top navigation**
|
|
234
|
+
|
|
235
|
+
You can use the ``position`` parameter to place the navigation at the top
|
|
236
|
+
of the app. This is useful for apps with a lot of pages because it allows
|
|
237
|
+
you to create collapsible sections for each group of pages. The following
|
|
238
|
+
example uses the same directory structure as Example 2 and shows how to
|
|
239
|
+
create a top navigation menu.
|
|
240
|
+
|
|
241
|
+
``streamlit_app.py``:
|
|
242
|
+
|
|
243
|
+
>>> import streamlit as st
|
|
244
|
+
>>>
|
|
245
|
+
>>> pages = {
|
|
246
|
+
... "Your account": [
|
|
247
|
+
... st.Page("create_account.py", title="Create your account"),
|
|
248
|
+
... st.Page("manage_account.py", title="Manage your account"),
|
|
249
|
+
... ],
|
|
250
|
+
... "Resources": [
|
|
251
|
+
... st.Page("learn.py", title="Learn about us"),
|
|
252
|
+
... st.Page("trial.py", title="Try it out"),
|
|
253
|
+
... ],
|
|
254
|
+
... }
|
|
255
|
+
>>>
|
|
256
|
+
>>> pg = st.navigation(pages, position="top")
|
|
257
|
+
>>> pg.run()
|
|
258
|
+
|
|
259
|
+
.. output::
|
|
260
|
+
https://doc-navigation-top.streamlit.app/
|
|
261
|
+
height: 300px
|
|
262
|
+
|
|
263
|
+
**Example 4: Stateful widgets across multiple pages**
|
|
230
264
|
|
|
231
265
|
Call widget functions in your entrypoint file when you want a widget to be
|
|
232
266
|
stateful across pages. Assign keys to your common widgets and access their
|
|
@@ -257,6 +291,13 @@ def navigation(
|
|
|
257
291
|
.. _st.Page: https://docs.streamlit.io/develop/api-reference/navigation/st.page
|
|
258
292
|
|
|
259
293
|
"""
|
|
294
|
+
# Validate position parameter
|
|
295
|
+
if not isinstance(position, str) or position not in ["sidebar", "hidden", "top"]:
|
|
296
|
+
raise StreamlitAPIException(
|
|
297
|
+
f'Invalid position "{position}". '
|
|
298
|
+
'The position parameter must be one of "sidebar", "hidden", or "top".'
|
|
299
|
+
)
|
|
300
|
+
|
|
260
301
|
# Disable the use of the pages feature (ie disregard v1 behavior of Multipage Apps)
|
|
261
302
|
PagesManager.uses_pages_directory = False
|
|
262
303
|
|
|
@@ -266,7 +307,7 @@ def navigation(
|
|
|
266
307
|
def _navigation(
|
|
267
308
|
pages: Sequence[PageType] | Mapping[SectionHeader, Sequence[PageType]],
|
|
268
309
|
*,
|
|
269
|
-
position: Literal["sidebar", "hidden"],
|
|
310
|
+
position: Literal["sidebar", "hidden", "top"],
|
|
270
311
|
expanded: bool,
|
|
271
312
|
) -> StreamlitPage:
|
|
272
313
|
if isinstance(pages, Sequence):
|
|
@@ -312,14 +353,11 @@ def _navigation(
|
|
|
312
353
|
# Build the pagehash-to-pageinfo mapping.
|
|
313
354
|
for section_header in nav_sections:
|
|
314
355
|
for page in nav_sections[section_header]:
|
|
315
|
-
if isinstance(page._page, Path)
|
|
316
|
-
script_path = str(page._page)
|
|
317
|
-
else:
|
|
318
|
-
script_path = ""
|
|
356
|
+
script_path = str(page._page) if isinstance(page._page, Path) else ""
|
|
319
357
|
|
|
320
358
|
script_hash = page._script_hash
|
|
321
359
|
if script_hash in pagehash_to_pageinfo:
|
|
322
|
-
# The page script hash is
|
|
360
|
+
# The page script hash is solely based on the url path
|
|
323
361
|
# So duplicate page script hashes are due to duplicate url paths
|
|
324
362
|
raise StreamlitAPIException(
|
|
325
363
|
f"Multiple Pages specified with URL pathname {page.url_path}. "
|
|
@@ -336,12 +374,17 @@ def _navigation(
|
|
|
336
374
|
}
|
|
337
375
|
|
|
338
376
|
msg = ForwardMsg()
|
|
377
|
+
# Handle position logic correctly
|
|
339
378
|
if position == "hidden":
|
|
340
379
|
msg.navigation.position = NavigationProto.Position.HIDDEN
|
|
341
|
-
elif
|
|
342
|
-
msg.navigation.position = NavigationProto.Position.
|
|
343
|
-
|
|
344
|
-
|
|
380
|
+
elif position == "top":
|
|
381
|
+
msg.navigation.position = NavigationProto.Position.TOP
|
|
382
|
+
elif position == "sidebar":
|
|
383
|
+
# Only apply config override if position is sidebar
|
|
384
|
+
if config.get_option("client.showSidebarNavigation") is False:
|
|
385
|
+
msg.navigation.position = NavigationProto.Position.HIDDEN
|
|
386
|
+
else:
|
|
387
|
+
msg.navigation.position = NavigationProto.Position.SIDEBAR
|
|
345
388
|
|
|
346
389
|
msg.navigation.expanded = expanded
|
|
347
390
|
msg.navigation.sections[:] = nav_sections.keys()
|