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.
Files changed (456) hide show
  1. streamlit/__init__.py +292 -0
  2. streamlit/__main__.py +20 -0
  3. streamlit/auth_util.py +583 -0
  4. streamlit/cli_util.py +107 -0
  5. streamlit/column_config.py +60 -0
  6. streamlit/commands/__init__.py +13 -0
  7. streamlit/commands/echo.py +128 -0
  8. streamlit/commands/execution_control.py +318 -0
  9. streamlit/commands/logo.py +250 -0
  10. streamlit/commands/navigation.py +430 -0
  11. streamlit/commands/page_config.py +335 -0
  12. streamlit/components/__init__.py +13 -0
  13. streamlit/components/lib/__init__.py +13 -0
  14. streamlit/components/lib/local_component_registry.py +84 -0
  15. streamlit/components/types/__init__.py +13 -0
  16. streamlit/components/types/base_component_registry.py +99 -0
  17. streamlit/components/types/base_custom_component.py +158 -0
  18. streamlit/components/v1/__init__.py +29 -0
  19. streamlit/components/v1/component_arrow.py +141 -0
  20. streamlit/components/v1/component_registry.py +147 -0
  21. streamlit/components/v1/components.py +38 -0
  22. streamlit/components/v1/custom_component.py +239 -0
  23. streamlit/components/v2/__init__.py +531 -0
  24. streamlit/components/v2/bidi_component/__init__.py +20 -0
  25. streamlit/components/v2/bidi_component/constants.py +29 -0
  26. streamlit/components/v2/bidi_component/main.py +537 -0
  27. streamlit/components/v2/bidi_component/serialization.py +272 -0
  28. streamlit/components/v2/bidi_component/state.py +92 -0
  29. streamlit/components/v2/component_definition_resolver.py +143 -0
  30. streamlit/components/v2/component_file_watcher.py +403 -0
  31. streamlit/components/v2/component_manager.py +439 -0
  32. streamlit/components/v2/component_manifest_handler.py +122 -0
  33. streamlit/components/v2/component_path_utils.py +233 -0
  34. streamlit/components/v2/component_registry.py +426 -0
  35. streamlit/components/v2/get_bidi_component_manager.py +51 -0
  36. streamlit/components/v2/manifest_scanner.py +620 -0
  37. streamlit/components/v2/presentation.py +198 -0
  38. streamlit/components/v2/types.py +317 -0
  39. streamlit/config.py +2977 -0
  40. streamlit/config_option.py +319 -0
  41. streamlit/config_util.py +887 -0
  42. streamlit/connections/__init__.py +32 -0
  43. streamlit/connections/base_connection.py +215 -0
  44. streamlit/connections/snowflake_connection.py +765 -0
  45. streamlit/connections/snowpark_connection.py +213 -0
  46. streamlit/connections/sql_connection.py +427 -0
  47. streamlit/connections/util.py +97 -0
  48. streamlit/cursor.py +314 -0
  49. streamlit/dataframe_util.py +1434 -0
  50. streamlit/delta_generator.py +696 -0
  51. streamlit/delta_generator_singletons.py +243 -0
  52. streamlit/deprecation_util.py +253 -0
  53. streamlit/development.py +21 -0
  54. streamlit/elements/__init__.py +13 -0
  55. streamlit/elements/alert.py +341 -0
  56. streamlit/elements/arrow.py +1065 -0
  57. streamlit/elements/balloons.py +47 -0
  58. streamlit/elements/bokeh_chart.py +75 -0
  59. streamlit/elements/code.py +154 -0
  60. streamlit/elements/deck_gl_json_chart.py +627 -0
  61. streamlit/elements/dialog_decorator.py +320 -0
  62. streamlit/elements/empty.py +130 -0
  63. streamlit/elements/exception.py +370 -0
  64. streamlit/elements/form.py +481 -0
  65. streamlit/elements/graphviz_chart.py +226 -0
  66. streamlit/elements/heading.py +407 -0
  67. streamlit/elements/help.py +565 -0
  68. streamlit/elements/html.py +189 -0
  69. streamlit/elements/iframe.py +245 -0
  70. streamlit/elements/image.py +221 -0
  71. streamlit/elements/json.py +171 -0
  72. streamlit/elements/layouts.py +1534 -0
  73. streamlit/elements/lib/__init__.py +13 -0
  74. streamlit/elements/lib/built_in_chart_utils.py +1316 -0
  75. streamlit/elements/lib/color_util.py +272 -0
  76. streamlit/elements/lib/column_config_utils.py +555 -0
  77. streamlit/elements/lib/column_types.py +2699 -0
  78. streamlit/elements/lib/dialog.py +212 -0
  79. streamlit/elements/lib/dicttools.py +152 -0
  80. streamlit/elements/lib/file_uploader_utils.py +91 -0
  81. streamlit/elements/lib/form_utils.py +77 -0
  82. streamlit/elements/lib/image_utils.py +447 -0
  83. streamlit/elements/lib/js_number.py +105 -0
  84. streamlit/elements/lib/layout_utils.py +325 -0
  85. streamlit/elements/lib/mutable_expander_container.py +73 -0
  86. streamlit/elements/lib/mutable_popover_container.py +73 -0
  87. streamlit/elements/lib/mutable_status_container.py +194 -0
  88. streamlit/elements/lib/mutable_tab_container.py +73 -0
  89. streamlit/elements/lib/options_selector_utils.py +480 -0
  90. streamlit/elements/lib/pandas_styler_utils.py +302 -0
  91. streamlit/elements/lib/policies.py +198 -0
  92. streamlit/elements/lib/shortcut_utils.py +152 -0
  93. streamlit/elements/lib/streamlit_plotly_theme.py +206 -0
  94. streamlit/elements/lib/subtitle_utils.py +175 -0
  95. streamlit/elements/lib/utils.py +274 -0
  96. streamlit/elements/map.py +526 -0
  97. streamlit/elements/markdown.py +529 -0
  98. streamlit/elements/media.py +843 -0
  99. streamlit/elements/metric.py +529 -0
  100. streamlit/elements/pdf.py +190 -0
  101. streamlit/elements/plotly_chart.py +779 -0
  102. streamlit/elements/progress.py +172 -0
  103. streamlit/elements/pyplot.py +240 -0
  104. streamlit/elements/snow.py +47 -0
  105. streamlit/elements/space.py +121 -0
  106. streamlit/elements/spinner.py +152 -0
  107. streamlit/elements/table.py +240 -0
  108. streamlit/elements/text.py +113 -0
  109. streamlit/elements/toast.py +175 -0
  110. streamlit/elements/vega_charts.py +2510 -0
  111. streamlit/elements/widgets/__init__.py +13 -0
  112. streamlit/elements/widgets/audio_input.py +334 -0
  113. streamlit/elements/widgets/button.py +1559 -0
  114. streamlit/elements/widgets/button_group.py +1061 -0
  115. streamlit/elements/widgets/camera_input.py +281 -0
  116. streamlit/elements/widgets/chat.py +1028 -0
  117. streamlit/elements/widgets/checkbox.py +420 -0
  118. streamlit/elements/widgets/color_picker.py +329 -0
  119. streamlit/elements/widgets/data_editor.py +1168 -0
  120. streamlit/elements/widgets/data_editor_plus.py +902 -0
  121. streamlit/elements/widgets/feedback.py +322 -0
  122. streamlit/elements/widgets/file_uploader.py +592 -0
  123. streamlit/elements/widgets/multiselect.py +654 -0
  124. streamlit/elements/widgets/number_input.py +719 -0
  125. streamlit/elements/widgets/radio.py +551 -0
  126. streamlit/elements/widgets/select_slider.py +568 -0
  127. streamlit/elements/widgets/selectbox.py +680 -0
  128. streamlit/elements/widgets/slider.py +1093 -0
  129. streamlit/elements/widgets/text_widgets.py +779 -0
  130. streamlit/elements/widgets/time_widgets.py +1712 -0
  131. streamlit/elements/write.py +585 -0
  132. streamlit/emojis.py +34 -0
  133. streamlit/env_util.py +56 -0
  134. streamlit/error_util.py +112 -0
  135. streamlit/errors.py +680 -0
  136. streamlit/external/__init__.py +13 -0
  137. streamlit/external/langchain/__init__.py +23 -0
  138. streamlit/external/langchain/streamlit_callback_handler.py +410 -0
  139. streamlit/file_util.py +266 -0
  140. streamlit/git_util.py +200 -0
  141. streamlit/hello/__init__.py +13 -0
  142. streamlit/hello/__pycache__/__init__.cpython-312.pyc +0 -0
  143. streamlit/hello/__pycache__/streamlit_app.cpython-312.pyc +0 -0
  144. streamlit/hello/animation_demo.py +82 -0
  145. streamlit/hello/dataframe_demo.py +71 -0
  146. streamlit/hello/hello.py +46 -0
  147. streamlit/hello/mapping_demo.py +113 -0
  148. streamlit/hello/plotting_demo.py +62 -0
  149. streamlit/hello/streamlit_app.py +57 -0
  150. streamlit/hello/utils.py +30 -0
  151. streamlit/logger.py +129 -0
  152. streamlit/material_icon_names.py +25 -0
  153. streamlit/navigation/__init__.py +13 -0
  154. streamlit/navigation/page.py +365 -0
  155. streamlit/net_util.py +125 -0
  156. streamlit/path_security.py +98 -0
  157. streamlit/platform.py +31 -0
  158. streamlit/proto/Alert_pb2.py +28 -0
  159. streamlit/proto/Alert_pb2.pyi +100 -0
  160. streamlit/proto/AppPage_pb2.py +25 -0
  161. streamlit/proto/AppPage_pb2.pyi +75 -0
  162. streamlit/proto/ArrowData_pb2.py +27 -0
  163. streamlit/proto/ArrowData_pb2.pyi +103 -0
  164. streamlit/proto/ArrowNamedDataSet_pb2.py +26 -0
  165. streamlit/proto/ArrowNamedDataSet_pb2.pyi +65 -0
  166. streamlit/proto/AudioInput_pb2.py +26 -0
  167. streamlit/proto/AudioInput_pb2.pyi +72 -0
  168. streamlit/proto/Audio_pb2.py +26 -0
  169. streamlit/proto/Audio_pb2.pyi +75 -0
  170. streamlit/proto/AuthRedirect_pb2.py +25 -0
  171. streamlit/proto/AuthRedirect_pb2.pyi +48 -0
  172. streamlit/proto/AutoRerun_pb2.py +25 -0
  173. streamlit/proto/AutoRerun_pb2.pyi +52 -0
  174. streamlit/proto/BackMsg_pb2.py +29 -0
  175. streamlit/proto/BackMsg_pb2.pyi +132 -0
  176. streamlit/proto/Balloons_pb2.py +25 -0
  177. streamlit/proto/Balloons_pb2.pyi +48 -0
  178. streamlit/proto/BidiComponent_pb2.py +32 -0
  179. streamlit/proto/BidiComponent_pb2.pyi +176 -0
  180. streamlit/proto/Block_pb2.py +62 -0
  181. streamlit/proto/Block_pb2.pyi +518 -0
  182. streamlit/proto/ButtonGroup_pb2.py +32 -0
  183. streamlit/proto/ButtonGroup_pb2.pyi +157 -0
  184. streamlit/proto/ButtonLikeIconPosition_pb2.py +25 -0
  185. streamlit/proto/ButtonLikeIconPosition_pb2.pyi +47 -0
  186. streamlit/proto/Button_pb2.py +26 -0
  187. streamlit/proto/Button_pb2.pyi +82 -0
  188. streamlit/proto/CameraInput_pb2.py +26 -0
  189. streamlit/proto/CameraInput_pb2.pyi +66 -0
  190. streamlit/proto/ChatInput_pb2.py +27 -0
  191. streamlit/proto/ChatInput_pb2.pyi +113 -0
  192. streamlit/proto/Checkbox_pb2.py +28 -0
  193. streamlit/proto/Checkbox_pb2.pyi +99 -0
  194. streamlit/proto/ClientState_pb2.py +28 -0
  195. streamlit/proto/ClientState_pb2.pyi +137 -0
  196. streamlit/proto/Code_pb2.py +25 -0
  197. streamlit/proto/Code_pb2.pyi +59 -0
  198. streamlit/proto/ColorPicker_pb2.py +26 -0
  199. streamlit/proto/ColorPicker_pb2.pyi +82 -0
  200. streamlit/proto/Common_pb2.py +49 -0
  201. streamlit/proto/Common_pb2.pyi +325 -0
  202. streamlit/proto/Components_pb2.py +33 -0
  203. streamlit/proto/Components_pb2.pyi +196 -0
  204. streamlit/proto/Dataframe_pb2.py +30 -0
  205. streamlit/proto/Dataframe_pb2.pyi +172 -0
  206. streamlit/proto/DateInput_pb2.py +26 -0
  207. streamlit/proto/DateInput_pb2.pyi +91 -0
  208. streamlit/proto/DateTimeInput_pb2.py +26 -0
  209. streamlit/proto/DateTimeInput_pb2.pyi +94 -0
  210. streamlit/proto/DeckGlJsonChart_pb2.py +27 -0
  211. streamlit/proto/DeckGlJsonChart_pb2.pyi +91 -0
  212. streamlit/proto/Delta_pb2.py +29 -0
  213. streamlit/proto/Delta_pb2.pyi +82 -0
  214. streamlit/proto/DownloadButton_pb2.py +26 -0
  215. streamlit/proto/DownloadButton_pb2.pyi +92 -0
  216. streamlit/proto/Element_pb2.py +81 -0
  217. streamlit/proto/Element_pb2.pyi +348 -0
  218. streamlit/proto/Empty_pb2.py +25 -0
  219. streamlit/proto/Empty_pb2.pyi +42 -0
  220. streamlit/proto/Exception_pb2.py +26 -0
  221. streamlit/proto/Exception_pb2.pyi +88 -0
  222. streamlit/proto/Favicon_pb2.py +25 -0
  223. streamlit/proto/Favicon_pb2.pyi +47 -0
  224. streamlit/proto/Feedback_pb2.py +27 -0
  225. streamlit/proto/Feedback_pb2.pyi +93 -0
  226. streamlit/proto/FileUploader_pb2.py +26 -0
  227. streamlit/proto/FileUploader_pb2.pyi +90 -0
  228. streamlit/proto/ForwardMsg_pb2.py +48 -0
  229. streamlit/proto/ForwardMsg_pb2.pyi +296 -0
  230. streamlit/proto/GapSize_pb2.py +27 -0
  231. streamlit/proto/GapSize_pb2.pyi +82 -0
  232. streamlit/proto/GitInfo_pb2.py +27 -0
  233. streamlit/proto/GitInfo_pb2.pyi +84 -0
  234. streamlit/proto/GraphVizChart_pb2.py +25 -0
  235. streamlit/proto/GraphVizChart_pb2.pyi +56 -0
  236. streamlit/proto/Heading_pb2.py +25 -0
  237. streamlit/proto/Heading_pb2.pyi +63 -0
  238. streamlit/proto/HeightConfig_pb2.py +25 -0
  239. streamlit/proto/HeightConfig_pb2.pyi +61 -0
  240. streamlit/proto/Help_pb2.py +27 -0
  241. streamlit/proto/Help_pb2.pyi +104 -0
  242. streamlit/proto/Html_pb2.py +25 -0
  243. streamlit/proto/Html_pb2.pyi +52 -0
  244. streamlit/proto/IFrame_pb2.py +25 -0
  245. streamlit/proto/IFrame_pb2.pyi +68 -0
  246. streamlit/proto/Image_pb2.py +27 -0
  247. streamlit/proto/Image_pb2.pyi +73 -0
  248. streamlit/proto/Json_pb2.py +25 -0
  249. streamlit/proto/Json_pb2.pyi +63 -0
  250. streamlit/proto/LabelVisibility_pb2.py +27 -0
  251. streamlit/proto/LabelVisibility_pb2.pyi +69 -0
  252. streamlit/proto/LinkButton_pb2.py +26 -0
  253. streamlit/proto/LinkButton_pb2.pyi +76 -0
  254. streamlit/proto/Logo_pb2.py +27 -0
  255. streamlit/proto/Logo_pb2.pyi +82 -0
  256. streamlit/proto/Markdown_pb2.py +27 -0
  257. streamlit/proto/Markdown_pb2.pyi +83 -0
  258. streamlit/proto/Metric_pb2.py +32 -0
  259. streamlit/proto/Metric_pb2.pyi +145 -0
  260. streamlit/proto/MetricsEvent_pb2.py +28 -0
  261. streamlit/proto/MetricsEvent_pb2.pyi +224 -0
  262. streamlit/proto/MultiSelect_pb2.py +26 -0
  263. streamlit/proto/MultiSelect_pb2.pyi +108 -0
  264. streamlit/proto/Navigation_pb2.py +28 -0
  265. streamlit/proto/Navigation_pb2.pyi +89 -0
  266. streamlit/proto/NewSession_pb2.py +47 -0
  267. streamlit/proto/NewSession_pb2.pyi +753 -0
  268. streamlit/proto/NumberInput_pb2.py +28 -0
  269. streamlit/proto/NumberInput_pb2.pyi +138 -0
  270. streamlit/proto/PageConfig_pb2.py +33 -0
  271. streamlit/proto/PageConfig_pb2.pyi +179 -0
  272. streamlit/proto/PageInfo_pb2.py +25 -0
  273. streamlit/proto/PageInfo_pb2.pyi +50 -0
  274. streamlit/proto/PageLink_pb2.py +26 -0
  275. streamlit/proto/PageLink_pb2.pyi +80 -0
  276. streamlit/proto/PageNotFound_pb2.py +25 -0
  277. streamlit/proto/PageNotFound_pb2.pyi +49 -0
  278. streamlit/proto/PageProfile_pb2.py +29 -0
  279. streamlit/proto/PageProfile_pb2.pyi +146 -0
  280. streamlit/proto/ParentMessage_pb2.py +25 -0
  281. streamlit/proto/ParentMessage_pb2.pyi +53 -0
  282. streamlit/proto/PlotlyChart_pb2.py +27 -0
  283. streamlit/proto/PlotlyChart_pb2.pyi +96 -0
  284. streamlit/proto/Progress_pb2.py +25 -0
  285. streamlit/proto/Progress_pb2.pyi +50 -0
  286. streamlit/proto/Radio_pb2.py +26 -0
  287. streamlit/proto/Radio_pb2.pyi +104 -0
  288. streamlit/proto/RootContainer_pb2.py +25 -0
  289. streamlit/proto/RootContainer_pb2.pyi +56 -0
  290. streamlit/proto/Selectbox_pb2.py +26 -0
  291. streamlit/proto/Selectbox_pb2.pyi +107 -0
  292. streamlit/proto/SessionEvent_pb2.py +26 -0
  293. streamlit/proto/SessionEvent_pb2.pyi +72 -0
  294. streamlit/proto/SessionStatus_pb2.py +25 -0
  295. streamlit/proto/SessionStatus_pb2.pyi +64 -0
  296. streamlit/proto/Skeleton_pb2.py +27 -0
  297. streamlit/proto/Skeleton_pb2.pyi +75 -0
  298. streamlit/proto/Slider_pb2.py +30 -0
  299. streamlit/proto/Slider_pb2.pyi +161 -0
  300. streamlit/proto/Snow_pb2.py +25 -0
  301. streamlit/proto/Snow_pb2.pyi +48 -0
  302. streamlit/proto/Space_pb2.py +25 -0
  303. streamlit/proto/Space_pb2.pyi +48 -0
  304. streamlit/proto/Spinner_pb2.py +25 -0
  305. streamlit/proto/Spinner_pb2.pyi +56 -0
  306. streamlit/proto/Table_pb2.py +28 -0
  307. streamlit/proto/Table_pb2.pyi +83 -0
  308. streamlit/proto/TextAlignmentConfig_pb2.py +27 -0
  309. streamlit/proto/TextAlignmentConfig_pb2.pyi +69 -0
  310. streamlit/proto/TextArea_pb2.py +26 -0
  311. streamlit/proto/TextArea_pb2.pyi +97 -0
  312. streamlit/proto/TextInput_pb2.py +28 -0
  313. streamlit/proto/TextInput_pb2.pyi +124 -0
  314. streamlit/proto/Text_pb2.py +25 -0
  315. streamlit/proto/Text_pb2.pyi +53 -0
  316. streamlit/proto/TimeInput_pb2.py +26 -0
  317. streamlit/proto/TimeInput_pb2.pyi +86 -0
  318. streamlit/proto/Toast_pb2.py +25 -0
  319. streamlit/proto/Toast_pb2.pyi +64 -0
  320. streamlit/proto/Transient_pb2.py +26 -0
  321. streamlit/proto/Transient_pb2.pyi +53 -0
  322. streamlit/proto/VegaLiteChart_pb2.py +27 -0
  323. streamlit/proto/VegaLiteChart_pb2.pyi +92 -0
  324. streamlit/proto/Video_pb2.py +30 -0
  325. streamlit/proto/Video_pb2.pyi +129 -0
  326. streamlit/proto/WidgetStates_pb2.py +31 -0
  327. streamlit/proto/WidgetStates_pb2.pyi +157 -0
  328. streamlit/proto/WidthConfig_pb2.py +25 -0
  329. streamlit/proto/WidthConfig_pb2.pyi +61 -0
  330. streamlit/proto/__init__.py +15 -0
  331. streamlit/proto/openmetrics_data_model_pb2.py +58 -0
  332. streamlit/proto/openmetrics_data_model_pb2.pyi +558 -0
  333. streamlit/py.typed +0 -0
  334. streamlit/runtime/__init__.py +50 -0
  335. streamlit/runtime/app_session.py +1302 -0
  336. streamlit/runtime/caching/__init__.py +118 -0
  337. streamlit/runtime/caching/cache_data_api.py +775 -0
  338. streamlit/runtime/caching/cache_errors.py +143 -0
  339. streamlit/runtime/caching/cache_resource_api.py +756 -0
  340. streamlit/runtime/caching/cache_type.py +33 -0
  341. streamlit/runtime/caching/cache_utils.py +601 -0
  342. streamlit/runtime/caching/cached_message_replay.py +290 -0
  343. streamlit/runtime/caching/hashing.py +655 -0
  344. streamlit/runtime/caching/legacy_cache_api.py +170 -0
  345. streamlit/runtime/caching/storage/__init__.py +29 -0
  346. streamlit/runtime/caching/storage/cache_storage_protocol.py +236 -0
  347. streamlit/runtime/caching/storage/dummy_cache_storage.py +60 -0
  348. streamlit/runtime/caching/storage/in_memory_cache_storage_wrapper.py +159 -0
  349. streamlit/runtime/caching/storage/local_disk_cache_storage.py +223 -0
  350. streamlit/runtime/caching/ttl_cleanup_cache.py +85 -0
  351. streamlit/runtime/connection_factory.py +498 -0
  352. streamlit/runtime/context.py +449 -0
  353. streamlit/runtime/context_util.py +49 -0
  354. streamlit/runtime/credentials.py +355 -0
  355. streamlit/runtime/download_data_util.py +53 -0
  356. streamlit/runtime/forward_msg_cache.py +101 -0
  357. streamlit/runtime/forward_msg_queue.py +263 -0
  358. streamlit/runtime/fragment.py +441 -0
  359. streamlit/runtime/media_file_manager.py +409 -0
  360. streamlit/runtime/media_file_storage.py +143 -0
  361. streamlit/runtime/memory_media_file_storage.py +201 -0
  362. streamlit/runtime/memory_session_storage.py +77 -0
  363. streamlit/runtime/memory_uploaded_file_manager.py +149 -0
  364. streamlit/runtime/metrics_util.py +612 -0
  365. streamlit/runtime/pages_manager.py +160 -0
  366. streamlit/runtime/runtime.py +775 -0
  367. streamlit/runtime/runtime_util.py +108 -0
  368. streamlit/runtime/script_data.py +46 -0
  369. streamlit/runtime/scriptrunner/__init__.py +38 -0
  370. streamlit/runtime/scriptrunner/exec_code.py +167 -0
  371. streamlit/runtime/scriptrunner/magic.py +277 -0
  372. streamlit/runtime/scriptrunner/magic_funcs.py +32 -0
  373. streamlit/runtime/scriptrunner/script_cache.py +89 -0
  374. streamlit/runtime/scriptrunner/script_runner.py +805 -0
  375. streamlit/runtime/scriptrunner_utils/__init__.py +19 -0
  376. streamlit/runtime/scriptrunner_utils/exceptions.py +44 -0
  377. streamlit/runtime/scriptrunner_utils/script_requests.py +311 -0
  378. streamlit/runtime/scriptrunner_utils/script_run_context.py +277 -0
  379. streamlit/runtime/secrets.py +533 -0
  380. streamlit/runtime/session_manager.py +430 -0
  381. streamlit/runtime/state/__init__.py +47 -0
  382. streamlit/runtime/state/common.py +256 -0
  383. streamlit/runtime/state/presentation.py +85 -0
  384. streamlit/runtime/state/query_params.py +767 -0
  385. streamlit/runtime/state/query_params_proxy.py +222 -0
  386. streamlit/runtime/state/safe_session_state.py +146 -0
  387. streamlit/runtime/state/session_state.py +1299 -0
  388. streamlit/runtime/state/session_state_proxy.py +153 -0
  389. streamlit/runtime/state/widgets.py +197 -0
  390. streamlit/runtime/stats.py +356 -0
  391. streamlit/runtime/theme_util.py +148 -0
  392. streamlit/runtime/uploaded_file_manager.py +152 -0
  393. streamlit/runtime/websocket_session_manager.py +301 -0
  394. streamlit/source_util.py +97 -0
  395. streamlit/starlette.py +34 -0
  396. streamlit/string_util.py +264 -0
  397. streamlit/temporary_directory.py +67 -0
  398. streamlit/testing/__init__.py +13 -0
  399. streamlit/testing/v1/__init__.py +17 -0
  400. streamlit/testing/v1/app_test.py +1089 -0
  401. streamlit/testing/v1/element_tree.py +2272 -0
  402. streamlit/testing/v1/local_script_runner.py +176 -0
  403. streamlit/testing/v1/util.py +61 -0
  404. streamlit/time_util.py +73 -0
  405. streamlit/type_util.py +480 -0
  406. streamlit/url_util.py +120 -0
  407. streamlit/user_info.py +698 -0
  408. streamlit/util.py +108 -0
  409. streamlit/vendor/__init__.py +0 -0
  410. streamlit/vendor/pympler/__init__.py +0 -0
  411. streamlit/vendor/pympler/asizeof.py +2870 -0
  412. streamlit/version.py +18 -0
  413. streamlit/watcher/__init__.py +28 -0
  414. streamlit/watcher/event_based_path_watcher.py +500 -0
  415. streamlit/watcher/folder_black_list.py +82 -0
  416. streamlit/watcher/local_sources_watcher.py +297 -0
  417. streamlit/watcher/path_watcher.py +244 -0
  418. streamlit/watcher/polling_path_watcher.py +138 -0
  419. streamlit/watcher/util.py +223 -0
  420. streamlit/web/__init__.py +13 -0
  421. streamlit/web/bootstrap.py +463 -0
  422. streamlit/web/cache_storage_manager_config.py +38 -0
  423. streamlit/web/cli.py +465 -0
  424. streamlit/web/server/__init__.py +30 -0
  425. streamlit/web/server/app_discovery.py +422 -0
  426. streamlit/web/server/app_static_file_handler.py +102 -0
  427. streamlit/web/server/authlib_tornado_integration.py +125 -0
  428. streamlit/web/server/bidi_component_request_handler.py +193 -0
  429. streamlit/web/server/browser_websocket_handler.py +341 -0
  430. streamlit/web/server/component_file_utils.py +105 -0
  431. streamlit/web/server/component_request_handler.py +107 -0
  432. streamlit/web/server/media_file_handler.py +148 -0
  433. streamlit/web/server/oauth_authlib_routes.py +314 -0
  434. streamlit/web/server/oidc_mixin.py +138 -0
  435. streamlit/web/server/routes.py +257 -0
  436. streamlit/web/server/server.py +555 -0
  437. streamlit/web/server/server_util.py +235 -0
  438. streamlit/web/server/starlette/__init__.py +23 -0
  439. streamlit/web/server/starlette/starlette_app.py +541 -0
  440. streamlit/web/server/starlette/starlette_app_utils.py +306 -0
  441. streamlit/web/server/starlette/starlette_auth_routes.py +584 -0
  442. streamlit/web/server/starlette/starlette_gzip_middleware.py +121 -0
  443. streamlit/web/server/starlette/starlette_path_security_middleware.py +97 -0
  444. streamlit/web/server/starlette/starlette_routes.py +884 -0
  445. streamlit/web/server/starlette/starlette_server.py +496 -0
  446. streamlit/web/server/starlette/starlette_server_config.py +55 -0
  447. streamlit/web/server/starlette/starlette_static_routes.py +181 -0
  448. streamlit/web/server/starlette/starlette_websocket.py +560 -0
  449. streamlit/web/server/stats_request_handler.py +116 -0
  450. streamlit/web/server/upload_file_request_handler.py +158 -0
  451. streamlit/web/server/websocket_headers.py +56 -0
  452. streamlit_data_editor_plus-0.1.0.dist-info/METADATA +76 -0
  453. streamlit_data_editor_plus-0.1.0.dist-info/RECORD +456 -0
  454. streamlit_data_editor_plus-0.1.0.dist-info/WHEEL +5 -0
  455. streamlit_data_editor_plus-0.1.0.dist-info/entry_points.txt +2 -0
  456. 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