streamlit-data-editor-plus 0.1.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 +292 -0
- streamlit/__main__.py +20 -0
- streamlit/auth_util.py +583 -0
- streamlit/cli_util.py +107 -0
- streamlit/column_config.py +60 -0
- streamlit/commands/__init__.py +13 -0
- streamlit/commands/echo.py +128 -0
- streamlit/commands/execution_control.py +318 -0
- streamlit/commands/logo.py +250 -0
- streamlit/commands/navigation.py +430 -0
- streamlit/commands/page_config.py +335 -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 +158 -0
- streamlit/components/v1/__init__.py +29 -0
- streamlit/components/v1/component_arrow.py +141 -0
- streamlit/components/v1/component_registry.py +147 -0
- streamlit/components/v1/components.py +38 -0
- streamlit/components/v1/custom_component.py +239 -0
- streamlit/components/v2/__init__.py +531 -0
- streamlit/components/v2/bidi_component/__init__.py +20 -0
- streamlit/components/v2/bidi_component/constants.py +29 -0
- streamlit/components/v2/bidi_component/main.py +537 -0
- streamlit/components/v2/bidi_component/serialization.py +272 -0
- streamlit/components/v2/bidi_component/state.py +92 -0
- streamlit/components/v2/component_definition_resolver.py +143 -0
- streamlit/components/v2/component_file_watcher.py +403 -0
- streamlit/components/v2/component_manager.py +439 -0
- streamlit/components/v2/component_manifest_handler.py +122 -0
- streamlit/components/v2/component_path_utils.py +233 -0
- streamlit/components/v2/component_registry.py +426 -0
- streamlit/components/v2/get_bidi_component_manager.py +51 -0
- streamlit/components/v2/manifest_scanner.py +620 -0
- streamlit/components/v2/presentation.py +198 -0
- streamlit/components/v2/types.py +317 -0
- streamlit/config.py +2977 -0
- streamlit/config_option.py +319 -0
- streamlit/config_util.py +887 -0
- streamlit/connections/__init__.py +32 -0
- streamlit/connections/base_connection.py +215 -0
- streamlit/connections/snowflake_connection.py +765 -0
- streamlit/connections/snowpark_connection.py +213 -0
- streamlit/connections/sql_connection.py +427 -0
- streamlit/connections/util.py +97 -0
- streamlit/cursor.py +314 -0
- streamlit/dataframe_util.py +1434 -0
- streamlit/delta_generator.py +696 -0
- streamlit/delta_generator_singletons.py +243 -0
- streamlit/deprecation_util.py +253 -0
- streamlit/development.py +21 -0
- streamlit/elements/__init__.py +13 -0
- streamlit/elements/alert.py +341 -0
- streamlit/elements/arrow.py +1065 -0
- streamlit/elements/balloons.py +47 -0
- streamlit/elements/bokeh_chart.py +75 -0
- streamlit/elements/code.py +154 -0
- streamlit/elements/deck_gl_json_chart.py +627 -0
- streamlit/elements/dialog_decorator.py +320 -0
- streamlit/elements/empty.py +130 -0
- streamlit/elements/exception.py +370 -0
- streamlit/elements/form.py +481 -0
- streamlit/elements/graphviz_chart.py +226 -0
- streamlit/elements/heading.py +407 -0
- streamlit/elements/help.py +565 -0
- streamlit/elements/html.py +189 -0
- streamlit/elements/iframe.py +245 -0
- streamlit/elements/image.py +221 -0
- streamlit/elements/json.py +171 -0
- streamlit/elements/layouts.py +1534 -0
- streamlit/elements/lib/__init__.py +13 -0
- streamlit/elements/lib/built_in_chart_utils.py +1316 -0
- streamlit/elements/lib/color_util.py +272 -0
- streamlit/elements/lib/column_config_utils.py +555 -0
- streamlit/elements/lib/column_types.py +2699 -0
- streamlit/elements/lib/dialog.py +212 -0
- streamlit/elements/lib/dicttools.py +152 -0
- streamlit/elements/lib/file_uploader_utils.py +91 -0
- streamlit/elements/lib/form_utils.py +77 -0
- streamlit/elements/lib/image_utils.py +447 -0
- streamlit/elements/lib/js_number.py +105 -0
- streamlit/elements/lib/layout_utils.py +325 -0
- streamlit/elements/lib/mutable_expander_container.py +73 -0
- streamlit/elements/lib/mutable_popover_container.py +73 -0
- streamlit/elements/lib/mutable_status_container.py +194 -0
- streamlit/elements/lib/mutable_tab_container.py +73 -0
- streamlit/elements/lib/options_selector_utils.py +480 -0
- streamlit/elements/lib/pandas_styler_utils.py +302 -0
- streamlit/elements/lib/policies.py +198 -0
- streamlit/elements/lib/shortcut_utils.py +152 -0
- streamlit/elements/lib/streamlit_plotly_theme.py +206 -0
- streamlit/elements/lib/subtitle_utils.py +175 -0
- streamlit/elements/lib/utils.py +274 -0
- streamlit/elements/map.py +526 -0
- streamlit/elements/markdown.py +529 -0
- streamlit/elements/media.py +843 -0
- streamlit/elements/metric.py +529 -0
- streamlit/elements/pdf.py +190 -0
- streamlit/elements/plotly_chart.py +779 -0
- streamlit/elements/progress.py +172 -0
- streamlit/elements/pyplot.py +240 -0
- streamlit/elements/snow.py +47 -0
- streamlit/elements/space.py +121 -0
- streamlit/elements/spinner.py +152 -0
- streamlit/elements/table.py +240 -0
- streamlit/elements/text.py +113 -0
- streamlit/elements/toast.py +175 -0
- streamlit/elements/vega_charts.py +2510 -0
- streamlit/elements/widgets/__init__.py +13 -0
- streamlit/elements/widgets/audio_input.py +334 -0
- streamlit/elements/widgets/button.py +1559 -0
- streamlit/elements/widgets/button_group.py +1061 -0
- streamlit/elements/widgets/camera_input.py +281 -0
- streamlit/elements/widgets/chat.py +1028 -0
- streamlit/elements/widgets/checkbox.py +420 -0
- streamlit/elements/widgets/color_picker.py +329 -0
- streamlit/elements/widgets/data_editor.py +1168 -0
- streamlit/elements/widgets/data_editor_plus.py +902 -0
- streamlit/elements/widgets/feedback.py +322 -0
- streamlit/elements/widgets/file_uploader.py +592 -0
- streamlit/elements/widgets/multiselect.py +654 -0
- streamlit/elements/widgets/number_input.py +719 -0
- streamlit/elements/widgets/radio.py +551 -0
- streamlit/elements/widgets/select_slider.py +568 -0
- streamlit/elements/widgets/selectbox.py +680 -0
- streamlit/elements/widgets/slider.py +1093 -0
- streamlit/elements/widgets/text_widgets.py +779 -0
- streamlit/elements/widgets/time_widgets.py +1712 -0
- streamlit/elements/write.py +585 -0
- streamlit/emojis.py +34 -0
- streamlit/env_util.py +56 -0
- streamlit/error_util.py +112 -0
- streamlit/errors.py +680 -0
- streamlit/external/__init__.py +13 -0
- streamlit/external/langchain/__init__.py +23 -0
- streamlit/external/langchain/streamlit_callback_handler.py +410 -0
- streamlit/file_util.py +266 -0
- streamlit/git_util.py +200 -0
- streamlit/hello/__init__.py +13 -0
- streamlit/hello/__pycache__/__init__.cpython-312.pyc +0 -0
- streamlit/hello/__pycache__/streamlit_app.cpython-312.pyc +0 -0
- streamlit/hello/animation_demo.py +82 -0
- streamlit/hello/dataframe_demo.py +71 -0
- streamlit/hello/hello.py +46 -0
- streamlit/hello/mapping_demo.py +113 -0
- streamlit/hello/plotting_demo.py +62 -0
- streamlit/hello/streamlit_app.py +57 -0
- streamlit/hello/utils.py +30 -0
- streamlit/logger.py +129 -0
- streamlit/material_icon_names.py +25 -0
- streamlit/navigation/__init__.py +13 -0
- streamlit/navigation/page.py +365 -0
- streamlit/net_util.py +125 -0
- streamlit/path_security.py +98 -0
- streamlit/platform.py +31 -0
- streamlit/proto/Alert_pb2.py +28 -0
- streamlit/proto/Alert_pb2.pyi +100 -0
- streamlit/proto/AppPage_pb2.py +25 -0
- streamlit/proto/AppPage_pb2.pyi +75 -0
- streamlit/proto/ArrowData_pb2.py +27 -0
- streamlit/proto/ArrowData_pb2.pyi +103 -0
- streamlit/proto/ArrowNamedDataSet_pb2.py +26 -0
- streamlit/proto/ArrowNamedDataSet_pb2.pyi +65 -0
- streamlit/proto/AudioInput_pb2.py +26 -0
- streamlit/proto/AudioInput_pb2.pyi +72 -0
- streamlit/proto/Audio_pb2.py +26 -0
- streamlit/proto/Audio_pb2.pyi +75 -0
- streamlit/proto/AuthRedirect_pb2.py +25 -0
- streamlit/proto/AuthRedirect_pb2.pyi +48 -0
- streamlit/proto/AutoRerun_pb2.py +25 -0
- streamlit/proto/AutoRerun_pb2.pyi +52 -0
- streamlit/proto/BackMsg_pb2.py +29 -0
- streamlit/proto/BackMsg_pb2.pyi +132 -0
- streamlit/proto/Balloons_pb2.py +25 -0
- streamlit/proto/Balloons_pb2.pyi +48 -0
- streamlit/proto/BidiComponent_pb2.py +32 -0
- streamlit/proto/BidiComponent_pb2.pyi +176 -0
- streamlit/proto/Block_pb2.py +62 -0
- streamlit/proto/Block_pb2.pyi +518 -0
- streamlit/proto/ButtonGroup_pb2.py +32 -0
- streamlit/proto/ButtonGroup_pb2.pyi +157 -0
- streamlit/proto/ButtonLikeIconPosition_pb2.py +25 -0
- streamlit/proto/ButtonLikeIconPosition_pb2.pyi +47 -0
- streamlit/proto/Button_pb2.py +26 -0
- streamlit/proto/Button_pb2.pyi +82 -0
- streamlit/proto/CameraInput_pb2.py +26 -0
- streamlit/proto/CameraInput_pb2.pyi +66 -0
- streamlit/proto/ChatInput_pb2.py +27 -0
- streamlit/proto/ChatInput_pb2.pyi +113 -0
- streamlit/proto/Checkbox_pb2.py +28 -0
- streamlit/proto/Checkbox_pb2.pyi +99 -0
- streamlit/proto/ClientState_pb2.py +28 -0
- streamlit/proto/ClientState_pb2.pyi +137 -0
- streamlit/proto/Code_pb2.py +25 -0
- streamlit/proto/Code_pb2.pyi +59 -0
- streamlit/proto/ColorPicker_pb2.py +26 -0
- streamlit/proto/ColorPicker_pb2.pyi +82 -0
- streamlit/proto/Common_pb2.py +49 -0
- streamlit/proto/Common_pb2.pyi +325 -0
- streamlit/proto/Components_pb2.py +33 -0
- streamlit/proto/Components_pb2.pyi +196 -0
- streamlit/proto/Dataframe_pb2.py +30 -0
- streamlit/proto/Dataframe_pb2.pyi +172 -0
- streamlit/proto/DateInput_pb2.py +26 -0
- streamlit/proto/DateInput_pb2.pyi +91 -0
- streamlit/proto/DateTimeInput_pb2.py +26 -0
- streamlit/proto/DateTimeInput_pb2.pyi +94 -0
- streamlit/proto/DeckGlJsonChart_pb2.py +27 -0
- streamlit/proto/DeckGlJsonChart_pb2.pyi +91 -0
- streamlit/proto/Delta_pb2.py +29 -0
- streamlit/proto/Delta_pb2.pyi +82 -0
- streamlit/proto/DownloadButton_pb2.py +26 -0
- streamlit/proto/DownloadButton_pb2.pyi +92 -0
- streamlit/proto/Element_pb2.py +81 -0
- streamlit/proto/Element_pb2.pyi +348 -0
- streamlit/proto/Empty_pb2.py +25 -0
- streamlit/proto/Empty_pb2.pyi +42 -0
- streamlit/proto/Exception_pb2.py +26 -0
- streamlit/proto/Exception_pb2.pyi +88 -0
- streamlit/proto/Favicon_pb2.py +25 -0
- streamlit/proto/Favicon_pb2.pyi +47 -0
- streamlit/proto/Feedback_pb2.py +27 -0
- streamlit/proto/Feedback_pb2.pyi +93 -0
- streamlit/proto/FileUploader_pb2.py +26 -0
- streamlit/proto/FileUploader_pb2.pyi +90 -0
- streamlit/proto/ForwardMsg_pb2.py +48 -0
- streamlit/proto/ForwardMsg_pb2.pyi +296 -0
- streamlit/proto/GapSize_pb2.py +27 -0
- streamlit/proto/GapSize_pb2.pyi +82 -0
- streamlit/proto/GitInfo_pb2.py +27 -0
- streamlit/proto/GitInfo_pb2.pyi +84 -0
- streamlit/proto/GraphVizChart_pb2.py +25 -0
- streamlit/proto/GraphVizChart_pb2.pyi +56 -0
- streamlit/proto/Heading_pb2.py +25 -0
- streamlit/proto/Heading_pb2.pyi +63 -0
- streamlit/proto/HeightConfig_pb2.py +25 -0
- streamlit/proto/HeightConfig_pb2.pyi +61 -0
- streamlit/proto/Help_pb2.py +27 -0
- streamlit/proto/Help_pb2.pyi +104 -0
- streamlit/proto/Html_pb2.py +25 -0
- streamlit/proto/Html_pb2.pyi +52 -0
- streamlit/proto/IFrame_pb2.py +25 -0
- streamlit/proto/IFrame_pb2.pyi +68 -0
- streamlit/proto/Image_pb2.py +27 -0
- streamlit/proto/Image_pb2.pyi +73 -0
- streamlit/proto/Json_pb2.py +25 -0
- streamlit/proto/Json_pb2.pyi +63 -0
- streamlit/proto/LabelVisibility_pb2.py +27 -0
- streamlit/proto/LabelVisibility_pb2.pyi +69 -0
- streamlit/proto/LinkButton_pb2.py +26 -0
- streamlit/proto/LinkButton_pb2.pyi +76 -0
- streamlit/proto/Logo_pb2.py +27 -0
- streamlit/proto/Logo_pb2.pyi +82 -0
- streamlit/proto/Markdown_pb2.py +27 -0
- streamlit/proto/Markdown_pb2.pyi +83 -0
- streamlit/proto/Metric_pb2.py +32 -0
- streamlit/proto/Metric_pb2.pyi +145 -0
- streamlit/proto/MetricsEvent_pb2.py +28 -0
- streamlit/proto/MetricsEvent_pb2.pyi +224 -0
- streamlit/proto/MultiSelect_pb2.py +26 -0
- streamlit/proto/MultiSelect_pb2.pyi +108 -0
- streamlit/proto/Navigation_pb2.py +28 -0
- streamlit/proto/Navigation_pb2.pyi +89 -0
- streamlit/proto/NewSession_pb2.py +47 -0
- streamlit/proto/NewSession_pb2.pyi +753 -0
- streamlit/proto/NumberInput_pb2.py +28 -0
- streamlit/proto/NumberInput_pb2.pyi +138 -0
- streamlit/proto/PageConfig_pb2.py +33 -0
- streamlit/proto/PageConfig_pb2.pyi +179 -0
- streamlit/proto/PageInfo_pb2.py +25 -0
- streamlit/proto/PageInfo_pb2.pyi +50 -0
- streamlit/proto/PageLink_pb2.py +26 -0
- streamlit/proto/PageLink_pb2.pyi +80 -0
- streamlit/proto/PageNotFound_pb2.py +25 -0
- streamlit/proto/PageNotFound_pb2.pyi +49 -0
- streamlit/proto/PageProfile_pb2.py +29 -0
- streamlit/proto/PageProfile_pb2.pyi +146 -0
- streamlit/proto/ParentMessage_pb2.py +25 -0
- streamlit/proto/ParentMessage_pb2.pyi +53 -0
- streamlit/proto/PlotlyChart_pb2.py +27 -0
- streamlit/proto/PlotlyChart_pb2.pyi +96 -0
- streamlit/proto/Progress_pb2.py +25 -0
- streamlit/proto/Progress_pb2.pyi +50 -0
- streamlit/proto/Radio_pb2.py +26 -0
- streamlit/proto/Radio_pb2.pyi +104 -0
- streamlit/proto/RootContainer_pb2.py +25 -0
- streamlit/proto/RootContainer_pb2.pyi +56 -0
- streamlit/proto/Selectbox_pb2.py +26 -0
- streamlit/proto/Selectbox_pb2.pyi +107 -0
- streamlit/proto/SessionEvent_pb2.py +26 -0
- streamlit/proto/SessionEvent_pb2.pyi +72 -0
- streamlit/proto/SessionStatus_pb2.py +25 -0
- streamlit/proto/SessionStatus_pb2.pyi +64 -0
- streamlit/proto/Skeleton_pb2.py +27 -0
- streamlit/proto/Skeleton_pb2.pyi +75 -0
- streamlit/proto/Slider_pb2.py +30 -0
- streamlit/proto/Slider_pb2.pyi +161 -0
- streamlit/proto/Snow_pb2.py +25 -0
- streamlit/proto/Snow_pb2.pyi +48 -0
- streamlit/proto/Space_pb2.py +25 -0
- streamlit/proto/Space_pb2.pyi +48 -0
- streamlit/proto/Spinner_pb2.py +25 -0
- streamlit/proto/Spinner_pb2.pyi +56 -0
- streamlit/proto/Table_pb2.py +28 -0
- streamlit/proto/Table_pb2.pyi +83 -0
- streamlit/proto/TextAlignmentConfig_pb2.py +27 -0
- streamlit/proto/TextAlignmentConfig_pb2.pyi +69 -0
- streamlit/proto/TextArea_pb2.py +26 -0
- streamlit/proto/TextArea_pb2.pyi +97 -0
- streamlit/proto/TextInput_pb2.py +28 -0
- streamlit/proto/TextInput_pb2.pyi +124 -0
- streamlit/proto/Text_pb2.py +25 -0
- streamlit/proto/Text_pb2.pyi +53 -0
- streamlit/proto/TimeInput_pb2.py +26 -0
- streamlit/proto/TimeInput_pb2.pyi +86 -0
- streamlit/proto/Toast_pb2.py +25 -0
- streamlit/proto/Toast_pb2.pyi +64 -0
- streamlit/proto/Transient_pb2.py +26 -0
- streamlit/proto/Transient_pb2.pyi +53 -0
- streamlit/proto/VegaLiteChart_pb2.py +27 -0
- streamlit/proto/VegaLiteChart_pb2.pyi +92 -0
- streamlit/proto/Video_pb2.py +30 -0
- streamlit/proto/Video_pb2.pyi +129 -0
- streamlit/proto/WidgetStates_pb2.py +31 -0
- streamlit/proto/WidgetStates_pb2.pyi +157 -0
- streamlit/proto/WidthConfig_pb2.py +25 -0
- streamlit/proto/WidthConfig_pb2.pyi +61 -0
- streamlit/proto/__init__.py +15 -0
- streamlit/proto/openmetrics_data_model_pb2.py +58 -0
- streamlit/proto/openmetrics_data_model_pb2.pyi +558 -0
- streamlit/py.typed +0 -0
- streamlit/runtime/__init__.py +50 -0
- streamlit/runtime/app_session.py +1302 -0
- streamlit/runtime/caching/__init__.py +118 -0
- streamlit/runtime/caching/cache_data_api.py +775 -0
- streamlit/runtime/caching/cache_errors.py +143 -0
- streamlit/runtime/caching/cache_resource_api.py +756 -0
- streamlit/runtime/caching/cache_type.py +33 -0
- streamlit/runtime/caching/cache_utils.py +601 -0
- streamlit/runtime/caching/cached_message_replay.py +290 -0
- streamlit/runtime/caching/hashing.py +655 -0
- streamlit/runtime/caching/legacy_cache_api.py +170 -0
- streamlit/runtime/caching/storage/__init__.py +29 -0
- streamlit/runtime/caching/storage/cache_storage_protocol.py +236 -0
- streamlit/runtime/caching/storage/dummy_cache_storage.py +60 -0
- streamlit/runtime/caching/storage/in_memory_cache_storage_wrapper.py +159 -0
- streamlit/runtime/caching/storage/local_disk_cache_storage.py +223 -0
- streamlit/runtime/caching/ttl_cleanup_cache.py +85 -0
- streamlit/runtime/connection_factory.py +498 -0
- streamlit/runtime/context.py +449 -0
- streamlit/runtime/context_util.py +49 -0
- streamlit/runtime/credentials.py +355 -0
- streamlit/runtime/download_data_util.py +53 -0
- streamlit/runtime/forward_msg_cache.py +101 -0
- streamlit/runtime/forward_msg_queue.py +263 -0
- streamlit/runtime/fragment.py +441 -0
- streamlit/runtime/media_file_manager.py +409 -0
- streamlit/runtime/media_file_storage.py +143 -0
- streamlit/runtime/memory_media_file_storage.py +201 -0
- streamlit/runtime/memory_session_storage.py +77 -0
- streamlit/runtime/memory_uploaded_file_manager.py +149 -0
- streamlit/runtime/metrics_util.py +612 -0
- streamlit/runtime/pages_manager.py +160 -0
- streamlit/runtime/runtime.py +775 -0
- streamlit/runtime/runtime_util.py +108 -0
- streamlit/runtime/script_data.py +46 -0
- streamlit/runtime/scriptrunner/__init__.py +38 -0
- streamlit/runtime/scriptrunner/exec_code.py +167 -0
- streamlit/runtime/scriptrunner/magic.py +277 -0
- streamlit/runtime/scriptrunner/magic_funcs.py +32 -0
- streamlit/runtime/scriptrunner/script_cache.py +89 -0
- streamlit/runtime/scriptrunner/script_runner.py +805 -0
- streamlit/runtime/scriptrunner_utils/__init__.py +19 -0
- streamlit/runtime/scriptrunner_utils/exceptions.py +44 -0
- streamlit/runtime/scriptrunner_utils/script_requests.py +311 -0
- streamlit/runtime/scriptrunner_utils/script_run_context.py +277 -0
- streamlit/runtime/secrets.py +533 -0
- streamlit/runtime/session_manager.py +430 -0
- streamlit/runtime/state/__init__.py +47 -0
- streamlit/runtime/state/common.py +256 -0
- streamlit/runtime/state/presentation.py +85 -0
- streamlit/runtime/state/query_params.py +767 -0
- streamlit/runtime/state/query_params_proxy.py +222 -0
- streamlit/runtime/state/safe_session_state.py +146 -0
- streamlit/runtime/state/session_state.py +1299 -0
- streamlit/runtime/state/session_state_proxy.py +153 -0
- streamlit/runtime/state/widgets.py +197 -0
- streamlit/runtime/stats.py +356 -0
- streamlit/runtime/theme_util.py +148 -0
- streamlit/runtime/uploaded_file_manager.py +152 -0
- streamlit/runtime/websocket_session_manager.py +301 -0
- streamlit/source_util.py +97 -0
- streamlit/starlette.py +34 -0
- streamlit/string_util.py +264 -0
- streamlit/temporary_directory.py +67 -0
- streamlit/testing/__init__.py +13 -0
- streamlit/testing/v1/__init__.py +17 -0
- streamlit/testing/v1/app_test.py +1089 -0
- streamlit/testing/v1/element_tree.py +2272 -0
- streamlit/testing/v1/local_script_runner.py +176 -0
- streamlit/testing/v1/util.py +61 -0
- streamlit/time_util.py +73 -0
- streamlit/type_util.py +480 -0
- streamlit/url_util.py +120 -0
- streamlit/user_info.py +698 -0
- streamlit/util.py +108 -0
- streamlit/vendor/__init__.py +0 -0
- streamlit/vendor/pympler/__init__.py +0 -0
- streamlit/vendor/pympler/asizeof.py +2870 -0
- streamlit/version.py +18 -0
- streamlit/watcher/__init__.py +28 -0
- streamlit/watcher/event_based_path_watcher.py +500 -0
- streamlit/watcher/folder_black_list.py +82 -0
- streamlit/watcher/local_sources_watcher.py +297 -0
- streamlit/watcher/path_watcher.py +244 -0
- streamlit/watcher/polling_path_watcher.py +138 -0
- streamlit/watcher/util.py +223 -0
- streamlit/web/__init__.py +13 -0
- streamlit/web/bootstrap.py +463 -0
- streamlit/web/cache_storage_manager_config.py +38 -0
- streamlit/web/cli.py +465 -0
- streamlit/web/server/__init__.py +30 -0
- streamlit/web/server/app_discovery.py +422 -0
- streamlit/web/server/app_static_file_handler.py +102 -0
- streamlit/web/server/authlib_tornado_integration.py +125 -0
- streamlit/web/server/bidi_component_request_handler.py +193 -0
- streamlit/web/server/browser_websocket_handler.py +341 -0
- streamlit/web/server/component_file_utils.py +105 -0
- streamlit/web/server/component_request_handler.py +107 -0
- streamlit/web/server/media_file_handler.py +148 -0
- streamlit/web/server/oauth_authlib_routes.py +314 -0
- streamlit/web/server/oidc_mixin.py +138 -0
- streamlit/web/server/routes.py +257 -0
- streamlit/web/server/server.py +555 -0
- streamlit/web/server/server_util.py +235 -0
- streamlit/web/server/starlette/__init__.py +23 -0
- streamlit/web/server/starlette/starlette_app.py +541 -0
- streamlit/web/server/starlette/starlette_app_utils.py +306 -0
- streamlit/web/server/starlette/starlette_auth_routes.py +584 -0
- streamlit/web/server/starlette/starlette_gzip_middleware.py +121 -0
- streamlit/web/server/starlette/starlette_path_security_middleware.py +97 -0
- streamlit/web/server/starlette/starlette_routes.py +884 -0
- streamlit/web/server/starlette/starlette_server.py +496 -0
- streamlit/web/server/starlette/starlette_server_config.py +55 -0
- streamlit/web/server/starlette/starlette_static_routes.py +181 -0
- streamlit/web/server/starlette/starlette_websocket.py +560 -0
- streamlit/web/server/stats_request_handler.py +116 -0
- streamlit/web/server/upload_file_request_handler.py +158 -0
- streamlit/web/server/websocket_headers.py +56 -0
- streamlit_data_editor_plus-0.1.0.dist-info/METADATA +76 -0
- streamlit_data_editor_plus-0.1.0.dist-info/RECORD +456 -0
- streamlit_data_editor_plus-0.1.0.dist-info/WHEEL +5 -0
- streamlit_data_editor_plus-0.1.0.dist-info/entry_points.txt +2 -0
- streamlit_data_editor_plus-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2026)
|
|
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, Literal
|
|
18
|
+
|
|
19
|
+
from typing_extensions import Self
|
|
20
|
+
|
|
21
|
+
from streamlit.delta_generator import DeltaGenerator
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from types import TracebackType
|
|
25
|
+
|
|
26
|
+
from streamlit.cursor import Cursor
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class TabContainer(DeltaGenerator):
|
|
30
|
+
"""DeltaGenerator subclass returned for each tab in ``st.tabs``.
|
|
31
|
+
|
|
32
|
+
Provides the ``.open`` property for checking whether this tab is active
|
|
33
|
+
when ``on_change`` is used.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
root_container: int | None,
|
|
39
|
+
cursor: Cursor | None,
|
|
40
|
+
parent: DeltaGenerator | None,
|
|
41
|
+
block_type: str | None,
|
|
42
|
+
) -> None:
|
|
43
|
+
super().__init__(root_container, cursor, parent, block_type)
|
|
44
|
+
self._open: bool | None = None
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def open(self) -> bool | None:
|
|
48
|
+
"""Whether this tab is the currently active tab.
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
bool or None
|
|
53
|
+
``True`` if this tab is active, ``False`` if inactive, or ``None``
|
|
54
|
+
if state tracking is not enabled (``on_change`` was not set or
|
|
55
|
+
set to ``"ignore"``).
|
|
56
|
+
"""
|
|
57
|
+
return self._open
|
|
58
|
+
|
|
59
|
+
@open.setter # noqa: A003
|
|
60
|
+
def open(self, value: bool | None) -> None:
|
|
61
|
+
self._open = value
|
|
62
|
+
|
|
63
|
+
def __enter__(self) -> Self: # type: ignore[override]
|
|
64
|
+
super().__enter__()
|
|
65
|
+
return self
|
|
66
|
+
|
|
67
|
+
def __exit__(
|
|
68
|
+
self,
|
|
69
|
+
exc_type: type[BaseException] | None,
|
|
70
|
+
exc_val: BaseException | None,
|
|
71
|
+
exc_tb: TracebackType | None,
|
|
72
|
+
) -> Literal[False]:
|
|
73
|
+
return super().__exit__(exc_type, exc_val, exc_tb)
|
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2026)
|
|
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 enum import Enum, EnumMeta
|
|
18
|
+
from typing import TYPE_CHECKING, Any, Final, TypeVar, overload
|
|
19
|
+
|
|
20
|
+
from streamlit import config, logger
|
|
21
|
+
from streamlit.dataframe_util import OptionSequence, convert_anything_to_list
|
|
22
|
+
from streamlit.errors import StreamlitAPIException
|
|
23
|
+
from streamlit.runtime.state import get_session_state
|
|
24
|
+
from streamlit.runtime.state.common import RegisterWidgetResult
|
|
25
|
+
from streamlit.type_util import (
|
|
26
|
+
check_python_comparable,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
from collections.abc import Callable, Iterable, Sequence
|
|
31
|
+
|
|
32
|
+
_LOGGER: Final = logger.get_logger(__name__)
|
|
33
|
+
|
|
34
|
+
_FLOAT_EQUALITY_EPSILON: Final[float] = 0.000000000005
|
|
35
|
+
_Value = TypeVar("_Value")
|
|
36
|
+
T = TypeVar("T")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def index_(iterable: Iterable[_Value], x: _Value) -> int:
|
|
40
|
+
"""Return zero-based index of the first item whose value is equal to x.
|
|
41
|
+
Raises a ValueError if there is no such item.
|
|
42
|
+
|
|
43
|
+
We need a custom implementation instead of the built-in list .index() to
|
|
44
|
+
be compatible with NumPy array and Pandas Series.
|
|
45
|
+
|
|
46
|
+
Parameters
|
|
47
|
+
----------
|
|
48
|
+
iterable : list, tuple, numpy.ndarray, pandas.Series
|
|
49
|
+
x : Any
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
int
|
|
54
|
+
"""
|
|
55
|
+
for i, value in enumerate(iterable):
|
|
56
|
+
if x == value:
|
|
57
|
+
return i
|
|
58
|
+
if (
|
|
59
|
+
isinstance(value, float)
|
|
60
|
+
and isinstance(x, float)
|
|
61
|
+
and abs(x - value) < _FLOAT_EQUALITY_EPSILON
|
|
62
|
+
):
|
|
63
|
+
return i
|
|
64
|
+
raise ValueError(f"{x} is not in iterable")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def check_and_convert_to_indices(
|
|
68
|
+
opt: Sequence[Any], default_values: Sequence[Any] | Any | None
|
|
69
|
+
) -> list[int] | None:
|
|
70
|
+
"""Perform validation checks and return indices based on the default values."""
|
|
71
|
+
if default_values is None:
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
default_values = convert_anything_to_list(default_values)
|
|
75
|
+
|
|
76
|
+
for value in default_values:
|
|
77
|
+
if value not in opt:
|
|
78
|
+
raise StreamlitAPIException(
|
|
79
|
+
f"The default value '{value}' is not part of the options. "
|
|
80
|
+
"Please make sure that every default values also exists in the options."
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return [opt.index(value) for value in default_values]
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def convert_to_sequence_and_check_comparable(options: OptionSequence[T]) -> Sequence[T]:
|
|
87
|
+
indexable_options = convert_anything_to_list(options)
|
|
88
|
+
check_python_comparable(indexable_options)
|
|
89
|
+
return indexable_options
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def get_default_indices(
|
|
93
|
+
indexable_options: Sequence[T], default: Sequence[Any] | Any | None = None
|
|
94
|
+
) -> list[int]:
|
|
95
|
+
default_indices = check_and_convert_to_indices(indexable_options, default)
|
|
96
|
+
return default_indices if default_indices is not None else []
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
E1 = TypeVar("E1", bound=Enum)
|
|
100
|
+
E2 = TypeVar("E2", bound=Enum)
|
|
101
|
+
|
|
102
|
+
_ALLOWED_ENUM_COERCION_CONFIG_SETTINGS = ("off", "nameOnly", "nameAndValue")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _coerce_enum(from_enum_value: E1, to_enum_class: type[E2]) -> E1 | E2:
|
|
106
|
+
"""Attempt to coerce an Enum value to another EnumMeta.
|
|
107
|
+
|
|
108
|
+
An Enum value of EnumMeta E1 is considered coercible to EnumType E2
|
|
109
|
+
if the EnumMeta __qualname__ match and the names of their members
|
|
110
|
+
match as well. (This is configurable in streamlist configs)
|
|
111
|
+
"""
|
|
112
|
+
if not isinstance(from_enum_value, Enum):
|
|
113
|
+
raise ValueError( # noqa: TRY004
|
|
114
|
+
f"Expected an Enum in the first argument. Got {type(from_enum_value)}"
|
|
115
|
+
)
|
|
116
|
+
if not isinstance(to_enum_class, EnumMeta):
|
|
117
|
+
raise ValueError( # noqa: TRY004
|
|
118
|
+
f"Expected an EnumMeta/Type in the second argument. Got {type(to_enum_class)}"
|
|
119
|
+
)
|
|
120
|
+
if isinstance(from_enum_value, to_enum_class):
|
|
121
|
+
return from_enum_value # Enum is already a member, no coersion necessary
|
|
122
|
+
|
|
123
|
+
coercion_type = config.get_option("runner.enumCoercion")
|
|
124
|
+
if coercion_type not in _ALLOWED_ENUM_COERCION_CONFIG_SETTINGS:
|
|
125
|
+
raise StreamlitAPIException(
|
|
126
|
+
"Invalid value for config option runner.enumCoercion. "
|
|
127
|
+
f"Expected one of {_ALLOWED_ENUM_COERCION_CONFIG_SETTINGS}, "
|
|
128
|
+
f"but got '{coercion_type}'."
|
|
129
|
+
)
|
|
130
|
+
if coercion_type == "off":
|
|
131
|
+
return from_enum_value # do not attempt to coerce
|
|
132
|
+
|
|
133
|
+
# We now know this is an Enum AND the user has configured coercion enabled.
|
|
134
|
+
# Check if we do NOT meet the required conditions and log a failure message
|
|
135
|
+
# if that is the case.
|
|
136
|
+
from_enum_class = from_enum_value.__class__
|
|
137
|
+
if (
|
|
138
|
+
from_enum_class.__qualname__ != to_enum_class.__qualname__
|
|
139
|
+
or (
|
|
140
|
+
coercion_type == "nameOnly"
|
|
141
|
+
and set(to_enum_class._member_names_) != set(from_enum_class._member_names_)
|
|
142
|
+
)
|
|
143
|
+
or (
|
|
144
|
+
coercion_type == "nameAndValue"
|
|
145
|
+
and set(to_enum_class._value2member_map_)
|
|
146
|
+
!= set(from_enum_class._value2member_map_)
|
|
147
|
+
)
|
|
148
|
+
):
|
|
149
|
+
_LOGGER.debug("Failed to coerce %s to class %s", from_enum_value, to_enum_class)
|
|
150
|
+
return from_enum_value # do not attempt to coerce
|
|
151
|
+
|
|
152
|
+
# At this point we think the Enum is coercible, and we know
|
|
153
|
+
# E1 and E2 have the same member names. We convert from E1 to E2 using _name_
|
|
154
|
+
# (since user Enum subclasses can override the .name property in 3.11)
|
|
155
|
+
_LOGGER.debug("Coerced %s to class %s", from_enum_value, to_enum_class)
|
|
156
|
+
return to_enum_class[from_enum_value._name_]
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _extract_common_class_from_iter(iterable: Iterable[Any]) -> Any:
|
|
160
|
+
"""Return the common class of all elements in a iterable if they share one.
|
|
161
|
+
Otherwise, return None.
|
|
162
|
+
"""
|
|
163
|
+
try:
|
|
164
|
+
inner_iter = iter(iterable)
|
|
165
|
+
first_class = type(next(inner_iter))
|
|
166
|
+
except StopIteration:
|
|
167
|
+
return None
|
|
168
|
+
if all(type(item) is first_class for item in inner_iter):
|
|
169
|
+
return first_class
|
|
170
|
+
return None
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@overload
|
|
174
|
+
def maybe_coerce_enum(
|
|
175
|
+
register_widget_result: RegisterWidgetResult[Enum],
|
|
176
|
+
options: type[Enum],
|
|
177
|
+
opt_sequence: Sequence[Any],
|
|
178
|
+
) -> RegisterWidgetResult[Enum]: ...
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@overload
|
|
182
|
+
def maybe_coerce_enum(
|
|
183
|
+
register_widget_result: RegisterWidgetResult[T],
|
|
184
|
+
options: OptionSequence[T],
|
|
185
|
+
opt_sequence: Sequence[T],
|
|
186
|
+
) -> RegisterWidgetResult[T]: ...
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def maybe_coerce_enum(
|
|
190
|
+
register_widget_result: RegisterWidgetResult[Any],
|
|
191
|
+
options: OptionSequence[Any],
|
|
192
|
+
opt_sequence: Sequence[Any],
|
|
193
|
+
) -> RegisterWidgetResult[Any]:
|
|
194
|
+
"""Maybe Coerce a RegisterWidgetResult with an Enum member value to
|
|
195
|
+
RegisterWidgetResult[option] if option is an EnumType, otherwise just return
|
|
196
|
+
the original RegisterWidgetResult.
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
# If the value is not a Enum, return early
|
|
200
|
+
if not isinstance(register_widget_result.value, Enum):
|
|
201
|
+
return register_widget_result
|
|
202
|
+
|
|
203
|
+
coerce_class: EnumMeta | None
|
|
204
|
+
if isinstance(options, EnumMeta):
|
|
205
|
+
coerce_class = options
|
|
206
|
+
else:
|
|
207
|
+
coerce_class = _extract_common_class_from_iter(opt_sequence)
|
|
208
|
+
if coerce_class is None:
|
|
209
|
+
return register_widget_result
|
|
210
|
+
|
|
211
|
+
return RegisterWidgetResult(
|
|
212
|
+
_coerce_enum(register_widget_result.value, coerce_class),
|
|
213
|
+
register_widget_result.value_changed,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
# slightly ugly typing because TypeVars with Generic Bounds are not supported
|
|
218
|
+
# (https://github.com/python/typing/issues/548)
|
|
219
|
+
@overload
|
|
220
|
+
def maybe_coerce_enum_sequence(
|
|
221
|
+
register_widget_result: RegisterWidgetResult[list[T] | list[T | str]],
|
|
222
|
+
options: OptionSequence[T],
|
|
223
|
+
opt_sequence: Sequence[T],
|
|
224
|
+
) -> RegisterWidgetResult[list[T] | list[T | str]]: ...
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
@overload
|
|
228
|
+
def maybe_coerce_enum_sequence(
|
|
229
|
+
register_widget_result: RegisterWidgetResult[tuple[T, T]],
|
|
230
|
+
options: OptionSequence[T],
|
|
231
|
+
opt_sequence: Sequence[T],
|
|
232
|
+
) -> RegisterWidgetResult[tuple[T, T]]: ...
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def maybe_coerce_enum_sequence(
|
|
236
|
+
register_widget_result: RegisterWidgetResult[list[Any] | tuple[Any, ...]],
|
|
237
|
+
options: OptionSequence[Any],
|
|
238
|
+
opt_sequence: Sequence[Any],
|
|
239
|
+
) -> RegisterWidgetResult[list[Any] | tuple[Any, ...]]:
|
|
240
|
+
"""Maybe Coerce a RegisterWidgetResult with a sequence of Enum members as value
|
|
241
|
+
to RegisterWidgetResult[Sequence[option]] if option is an EnumType, otherwise just
|
|
242
|
+
return the original RegisterWidgetResult.
|
|
243
|
+
"""
|
|
244
|
+
|
|
245
|
+
# If not all widget values are Enums, return early
|
|
246
|
+
if not all(isinstance(val, Enum) for val in register_widget_result.value):
|
|
247
|
+
return register_widget_result
|
|
248
|
+
|
|
249
|
+
# Extract the class to coerce
|
|
250
|
+
coerce_class: EnumMeta | None
|
|
251
|
+
if isinstance(options, EnumMeta):
|
|
252
|
+
coerce_class = options
|
|
253
|
+
else:
|
|
254
|
+
coerce_class = _extract_common_class_from_iter(opt_sequence)
|
|
255
|
+
if coerce_class is None:
|
|
256
|
+
return register_widget_result
|
|
257
|
+
|
|
258
|
+
# Return a new RegisterWidgetResult with the coerced enum values sequence
|
|
259
|
+
return RegisterWidgetResult(
|
|
260
|
+
type(register_widget_result.value)(
|
|
261
|
+
_coerce_enum(val, coerce_class) for val in register_widget_result.value
|
|
262
|
+
),
|
|
263
|
+
register_widget_result.value_changed,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def create_mappings(
|
|
268
|
+
options: Sequence[T], format_func: Callable[[T], str] = str
|
|
269
|
+
) -> tuple[list[str], dict[str, int]]:
|
|
270
|
+
"""Iterates through the options and formats them using the format_func.
|
|
271
|
+
|
|
272
|
+
Returns a tuple of the formatted options and a mapping of the formatted options to
|
|
273
|
+
the original options.
|
|
274
|
+
"""
|
|
275
|
+
formatted_option_to_option_mapping: dict[str, int] = {}
|
|
276
|
+
formatted_options: list[str] = []
|
|
277
|
+
for index, option in enumerate(options):
|
|
278
|
+
formatted_option = format_func(option)
|
|
279
|
+
formatted_options.append(formatted_option)
|
|
280
|
+
# If formatted labels are duplicated, the last one wins. We keep this
|
|
281
|
+
# behavior to mirror radio/selectbox/multiselect, but it makes selection
|
|
282
|
+
# ambiguous for string-based widgets.
|
|
283
|
+
# TODO: Consider raising a StreamlitAPIException on duplicate labels.
|
|
284
|
+
formatted_option_to_option_mapping[formatted_option] = index
|
|
285
|
+
|
|
286
|
+
return (
|
|
287
|
+
formatted_options,
|
|
288
|
+
formatted_option_to_option_mapping,
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def validate_and_sync_value_with_options(
|
|
293
|
+
current_value: T | None,
|
|
294
|
+
opt: Sequence[T],
|
|
295
|
+
default_index: int | None,
|
|
296
|
+
key: str | int | None,
|
|
297
|
+
format_func: Callable[[Any], str] = str,
|
|
298
|
+
) -> tuple[T | None, bool]:
|
|
299
|
+
"""Validate current value against options, resetting session state if invalid.
|
|
300
|
+
|
|
301
|
+
This function has a side-effect: if the value is not found in the options
|
|
302
|
+
and a key is provided, it will update session state with the new value.
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
Parameters
|
|
306
|
+
----------
|
|
307
|
+
current_value
|
|
308
|
+
The current widget value to validate.
|
|
309
|
+
opt
|
|
310
|
+
The sequence of valid options.
|
|
311
|
+
default_index
|
|
312
|
+
The default index to reset to if value is invalid.
|
|
313
|
+
key
|
|
314
|
+
The widget key for session state updates.
|
|
315
|
+
format_func
|
|
316
|
+
Function to format options for comparison. Used to compare values by their
|
|
317
|
+
string representation instead of using == directly. This is necessary because
|
|
318
|
+
widget values are deepcopied, and for custom classes without __eq__, the
|
|
319
|
+
deepcopied instances would fail identity comparison.
|
|
320
|
+
|
|
321
|
+
Returns
|
|
322
|
+
-------
|
|
323
|
+
tuple[T | None, bool]
|
|
324
|
+
A tuple of (validated_value, value_was_reset).
|
|
325
|
+
"""
|
|
326
|
+
if current_value is None:
|
|
327
|
+
return current_value, False
|
|
328
|
+
|
|
329
|
+
# Use format_func comparison for all values. This correctly handles:
|
|
330
|
+
# - Custom objects without __eq__ (deepcopied instances)
|
|
331
|
+
# - Enum values (already from current class due to serde deserialization)
|
|
332
|
+
formatted_options_set = {format_func(o) for o in opt}
|
|
333
|
+
try:
|
|
334
|
+
formatted_value = format_func(current_value)
|
|
335
|
+
if formatted_value in formatted_options_set:
|
|
336
|
+
return current_value, False
|
|
337
|
+
except Exception: # noqa: S110
|
|
338
|
+
pass # format_func failed - value is invalid, fall through to reset
|
|
339
|
+
|
|
340
|
+
# Value not in options - reset to default
|
|
341
|
+
if default_index is not None and len(opt) > 0:
|
|
342
|
+
new_value: T | None = opt[default_index]
|
|
343
|
+
else:
|
|
344
|
+
new_value = None
|
|
345
|
+
|
|
346
|
+
if key is not None:
|
|
347
|
+
# Update session_state so subsequent accesses in this run
|
|
348
|
+
# return the corrected value. Use reset_state_value to avoid
|
|
349
|
+
# the "cannot be modified after widget instantiated" error.
|
|
350
|
+
get_session_state().reset_state_value(str(key), new_value)
|
|
351
|
+
|
|
352
|
+
return new_value, True
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def validate_and_sync_multiselect_value_with_options(
|
|
356
|
+
current_values: list[T] | list[T | str],
|
|
357
|
+
opt: Sequence[T],
|
|
358
|
+
key: str | int | None,
|
|
359
|
+
format_func: Callable[[Any], str] = str,
|
|
360
|
+
) -> tuple[list[T] | list[T | str], bool]:
|
|
361
|
+
"""Validate multiselect values against options, syncing session state if needed.
|
|
362
|
+
|
|
363
|
+
This function has a side-effect: if any values are filtered out and a key
|
|
364
|
+
is provided, it will update session state with the filtered list.
|
|
365
|
+
|
|
366
|
+
Unlike selectbox which resets to a default when the value is invalid,
|
|
367
|
+
multiselect filters out invalid values and keeps the valid ones.
|
|
368
|
+
|
|
369
|
+
Parameters
|
|
370
|
+
----------
|
|
371
|
+
current_values
|
|
372
|
+
The current list of selected values to validate.
|
|
373
|
+
opt
|
|
374
|
+
The sequence of valid options.
|
|
375
|
+
key
|
|
376
|
+
The widget key for session state updates.
|
|
377
|
+
format_func
|
|
378
|
+
Function to format options for comparison. Used to compare values by their
|
|
379
|
+
string representation instead of using == directly. This is necessary because
|
|
380
|
+
widget values are deepcopied, and for custom classes without __eq__, the
|
|
381
|
+
deepcopied instances would fail identity comparison.
|
|
382
|
+
|
|
383
|
+
Returns
|
|
384
|
+
-------
|
|
385
|
+
tuple[list[T] | list[T | str], bool]
|
|
386
|
+
A tuple of (validated_values, values_were_filtered).
|
|
387
|
+
"""
|
|
388
|
+
if not current_values:
|
|
389
|
+
return current_values, False
|
|
390
|
+
|
|
391
|
+
# Create a set of formatted options for O(1) lookup.
|
|
392
|
+
# We use format_func to compare values by their string representation
|
|
393
|
+
# instead of using == directly. This is necessary because widget values
|
|
394
|
+
# are deepcopied, and for custom classes without __eq__, the deepcopied
|
|
395
|
+
# instances would fail identity comparison.
|
|
396
|
+
formatted_options_set = {format_func(o) for o in opt}
|
|
397
|
+
|
|
398
|
+
valid_values: list[T | str] = []
|
|
399
|
+
for value in current_values:
|
|
400
|
+
try:
|
|
401
|
+
formatted_value = format_func(value)
|
|
402
|
+
except Exception: # noqa: S112
|
|
403
|
+
# format_func failed on this value (e.g., a string value from a previous
|
|
404
|
+
# session when format_func expects an object with specific attributes).
|
|
405
|
+
# In this case, the value is definitely not valid since the current options
|
|
406
|
+
# can be formatted successfully.
|
|
407
|
+
continue
|
|
408
|
+
|
|
409
|
+
if formatted_value in formatted_options_set:
|
|
410
|
+
valid_values.append(value)
|
|
411
|
+
|
|
412
|
+
if len(valid_values) == len(current_values):
|
|
413
|
+
return current_values, False
|
|
414
|
+
|
|
415
|
+
if key is not None:
|
|
416
|
+
get_session_state().reset_state_value(str(key), valid_values)
|
|
417
|
+
|
|
418
|
+
return valid_values, True
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def validate_and_sync_range_value_with_options(
|
|
422
|
+
current_value: tuple[T, T],
|
|
423
|
+
opt: Sequence[T],
|
|
424
|
+
default_indices: list[int],
|
|
425
|
+
key: str | int | None,
|
|
426
|
+
format_func: Callable[[Any], str] = str,
|
|
427
|
+
) -> tuple[tuple[T, T], bool]:
|
|
428
|
+
"""Validate a range value (tuple of two values) against options.
|
|
429
|
+
|
|
430
|
+
If either value in the range is not found in options, the entire range is
|
|
431
|
+
reset to the default. This function has a side-effect: if the values are
|
|
432
|
+
invalid and a key is provided, it will update session state with the new value.
|
|
433
|
+
|
|
434
|
+
Parameters
|
|
435
|
+
----------
|
|
436
|
+
current_value
|
|
437
|
+
The current range value (tuple of two values) to validate.
|
|
438
|
+
opt
|
|
439
|
+
The sequence of valid options.
|
|
440
|
+
default_indices
|
|
441
|
+
The default indices to reset to if value is invalid. Should contain
|
|
442
|
+
at least one index; if only one index is provided, the second default
|
|
443
|
+
will be the last option.
|
|
444
|
+
key
|
|
445
|
+
The widget key for session state updates.
|
|
446
|
+
format_func
|
|
447
|
+
Function to format options for comparison. Used to compare values by their
|
|
448
|
+
string representation instead of using == directly.
|
|
449
|
+
|
|
450
|
+
Returns
|
|
451
|
+
-------
|
|
452
|
+
tuple[tuple[T, T], bool]
|
|
453
|
+
A tuple of (validated_value, value_was_reset).
|
|
454
|
+
"""
|
|
455
|
+
if len(opt) == 0:
|
|
456
|
+
return current_value, False
|
|
457
|
+
|
|
458
|
+
formatted_options_set = {format_func(o) for o in opt}
|
|
459
|
+
|
|
460
|
+
def is_valid(val: Any) -> bool:
|
|
461
|
+
"""Check if a value exists in options via format_func comparison."""
|
|
462
|
+
try:
|
|
463
|
+
return format_func(val) in formatted_options_set
|
|
464
|
+
except Exception:
|
|
465
|
+
return False
|
|
466
|
+
|
|
467
|
+
def get_default_range() -> tuple[T, T]:
|
|
468
|
+
"""Get the default range value."""
|
|
469
|
+
end_idx = default_indices[1] if len(default_indices) > 1 else len(opt) - 1
|
|
470
|
+
return (opt[default_indices[0]], opt[end_idx])
|
|
471
|
+
|
|
472
|
+
# Validate both values in the range.
|
|
473
|
+
if is_valid(current_value[0]) and is_valid(current_value[1]):
|
|
474
|
+
return current_value, False
|
|
475
|
+
|
|
476
|
+
# Either value is invalid - reset entire range.
|
|
477
|
+
new_value = get_default_range()
|
|
478
|
+
if key is not None:
|
|
479
|
+
get_session_state().reset_state_value(str(key), new_value)
|
|
480
|
+
return new_value, True
|