streamlit-nightly 1.43.2.dev20250307__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (563) hide show
  1. streamlit/__init__.py +306 -0
  2. streamlit/__main__.py +20 -0
  3. streamlit/auth_util.py +218 -0
  4. streamlit/cli_util.py +105 -0
  5. streamlit/column_config.py +56 -0
  6. streamlit/commands/__init__.py +13 -0
  7. streamlit/commands/echo.py +126 -0
  8. streamlit/commands/execution_control.py +238 -0
  9. streamlit/commands/experimental_query_params.py +169 -0
  10. streamlit/commands/logo.py +189 -0
  11. streamlit/commands/navigation.py +385 -0
  12. streamlit/commands/page_config.py +311 -0
  13. streamlit/components/__init__.py +13 -0
  14. streamlit/components/lib/__init__.py +13 -0
  15. streamlit/components/lib/local_component_registry.py +84 -0
  16. streamlit/components/types/__init__.py +13 -0
  17. streamlit/components/types/base_component_registry.py +99 -0
  18. streamlit/components/types/base_custom_component.py +150 -0
  19. streamlit/components/v1/__init__.py +31 -0
  20. streamlit/components/v1/component_arrow.py +141 -0
  21. streamlit/components/v1/component_registry.py +130 -0
  22. streamlit/components/v1/components.py +38 -0
  23. streamlit/components/v1/custom_component.py +243 -0
  24. streamlit/config.py +1513 -0
  25. streamlit/config_option.py +311 -0
  26. streamlit/config_util.py +177 -0
  27. streamlit/connections/__init__.py +28 -0
  28. streamlit/connections/base_connection.py +174 -0
  29. streamlit/connections/snowflake_connection.py +562 -0
  30. streamlit/connections/snowpark_connection.py +213 -0
  31. streamlit/connections/sql_connection.py +425 -0
  32. streamlit/connections/util.py +97 -0
  33. streamlit/cursor.py +210 -0
  34. streamlit/dataframe_util.py +1416 -0
  35. streamlit/delta_generator.py +602 -0
  36. streamlit/delta_generator_singletons.py +204 -0
  37. streamlit/deprecation_util.py +209 -0
  38. streamlit/development.py +21 -0
  39. streamlit/elements/__init__.py +13 -0
  40. streamlit/elements/alert.py +234 -0
  41. streamlit/elements/arrow.py +962 -0
  42. streamlit/elements/balloons.py +47 -0
  43. streamlit/elements/bokeh_chart.py +133 -0
  44. streamlit/elements/code.py +114 -0
  45. streamlit/elements/deck_gl_json_chart.py +546 -0
  46. streamlit/elements/dialog_decorator.py +267 -0
  47. streamlit/elements/doc_string.py +558 -0
  48. streamlit/elements/empty.py +130 -0
  49. streamlit/elements/exception.py +331 -0
  50. streamlit/elements/form.py +354 -0
  51. streamlit/elements/graphviz_chart.py +150 -0
  52. streamlit/elements/heading.py +302 -0
  53. streamlit/elements/html.py +105 -0
  54. streamlit/elements/iframe.py +191 -0
  55. streamlit/elements/image.py +196 -0
  56. streamlit/elements/json.py +139 -0
  57. streamlit/elements/layouts.py +879 -0
  58. streamlit/elements/lib/__init__.py +13 -0
  59. streamlit/elements/lib/built_in_chart_utils.py +1157 -0
  60. streamlit/elements/lib/color_util.py +263 -0
  61. streamlit/elements/lib/column_config_utils.py +542 -0
  62. streamlit/elements/lib/column_types.py +2188 -0
  63. streamlit/elements/lib/dialog.py +147 -0
  64. streamlit/elements/lib/dicttools.py +154 -0
  65. streamlit/elements/lib/event_utils.py +37 -0
  66. streamlit/elements/lib/file_uploader_utils.py +66 -0
  67. streamlit/elements/lib/form_utils.py +77 -0
  68. streamlit/elements/lib/image_utils.py +441 -0
  69. streamlit/elements/lib/js_number.py +105 -0
  70. streamlit/elements/lib/mutable_status_container.py +183 -0
  71. streamlit/elements/lib/options_selector_utils.py +250 -0
  72. streamlit/elements/lib/pandas_styler_utils.py +274 -0
  73. streamlit/elements/lib/policies.py +194 -0
  74. streamlit/elements/lib/streamlit_plotly_theme.py +207 -0
  75. streamlit/elements/lib/subtitle_utils.py +176 -0
  76. streamlit/elements/lib/utils.py +250 -0
  77. streamlit/elements/map.py +508 -0
  78. streamlit/elements/markdown.py +277 -0
  79. streamlit/elements/media.py +793 -0
  80. streamlit/elements/metric.py +301 -0
  81. streamlit/elements/plotly_chart.py +546 -0
  82. streamlit/elements/progress.py +156 -0
  83. streamlit/elements/pyplot.py +194 -0
  84. streamlit/elements/snow.py +47 -0
  85. streamlit/elements/spinner.py +113 -0
  86. streamlit/elements/text.py +76 -0
  87. streamlit/elements/toast.py +98 -0
  88. streamlit/elements/vega_charts.py +1984 -0
  89. streamlit/elements/widgets/__init__.py +13 -0
  90. streamlit/elements/widgets/audio_input.py +310 -0
  91. streamlit/elements/widgets/button.py +1123 -0
  92. streamlit/elements/widgets/button_group.py +1008 -0
  93. streamlit/elements/widgets/camera_input.py +263 -0
  94. streamlit/elements/widgets/chat.py +647 -0
  95. streamlit/elements/widgets/checkbox.py +352 -0
  96. streamlit/elements/widgets/color_picker.py +265 -0
  97. streamlit/elements/widgets/data_editor.py +983 -0
  98. streamlit/elements/widgets/file_uploader.py +486 -0
  99. streamlit/elements/widgets/multiselect.py +338 -0
  100. streamlit/elements/widgets/number_input.py +545 -0
  101. streamlit/elements/widgets/radio.py +407 -0
  102. streamlit/elements/widgets/select_slider.py +437 -0
  103. streamlit/elements/widgets/selectbox.py +366 -0
  104. streamlit/elements/widgets/slider.py +880 -0
  105. streamlit/elements/widgets/text_widgets.py +628 -0
  106. streamlit/elements/widgets/time_widgets.py +970 -0
  107. streamlit/elements/write.py +574 -0
  108. streamlit/emojis.py +34 -0
  109. streamlit/env_util.py +61 -0
  110. streamlit/error_util.py +105 -0
  111. streamlit/errors.py +452 -0
  112. streamlit/external/__init__.py +13 -0
  113. streamlit/external/langchain/__init__.py +23 -0
  114. streamlit/external/langchain/streamlit_callback_handler.py +406 -0
  115. streamlit/file_util.py +247 -0
  116. streamlit/git_util.py +173 -0
  117. streamlit/hello/__init__.py +13 -0
  118. streamlit/hello/animation_demo.py +82 -0
  119. streamlit/hello/dataframe_demo.py +71 -0
  120. streamlit/hello/hello.py +37 -0
  121. streamlit/hello/mapping_demo.py +114 -0
  122. streamlit/hello/plotting_demo.py +55 -0
  123. streamlit/hello/streamlit_app.py +55 -0
  124. streamlit/hello/utils.py +28 -0
  125. streamlit/logger.py +130 -0
  126. streamlit/material_icon_names.py +25 -0
  127. streamlit/navigation/__init__.py +13 -0
  128. streamlit/navigation/page.py +302 -0
  129. streamlit/net_util.py +125 -0
  130. streamlit/platform.py +33 -0
  131. streamlit/proto/Alert_pb2.py +29 -0
  132. streamlit/proto/Alert_pb2.pyi +90 -0
  133. streamlit/proto/AppPage_pb2.py +27 -0
  134. streamlit/proto/AppPage_pb2.pyi +64 -0
  135. streamlit/proto/ArrowNamedDataSet_pb2.py +28 -0
  136. streamlit/proto/ArrowNamedDataSet_pb2.pyi +57 -0
  137. streamlit/proto/ArrowVegaLiteChart_pb2.py +29 -0
  138. streamlit/proto/ArrowVegaLiteChart_pb2.pyi +84 -0
  139. streamlit/proto/Arrow_pb2.py +33 -0
  140. streamlit/proto/Arrow_pb2.pyi +188 -0
  141. streamlit/proto/AudioInput_pb2.py +28 -0
  142. streamlit/proto/AudioInput_pb2.pyi +58 -0
  143. streamlit/proto/Audio_pb2.py +27 -0
  144. streamlit/proto/Audio_pb2.pyi +58 -0
  145. streamlit/proto/AuthRedirect_pb2.py +27 -0
  146. streamlit/proto/AuthRedirect_pb2.pyi +41 -0
  147. streamlit/proto/AutoRerun_pb2.py +27 -0
  148. streamlit/proto/AutoRerun_pb2.pyi +45 -0
  149. streamlit/proto/BackMsg_pb2.py +29 -0
  150. streamlit/proto/BackMsg_pb2.pyi +105 -0
  151. streamlit/proto/Balloons_pb2.py +27 -0
  152. streamlit/proto/Balloons_pb2.pyi +43 -0
  153. streamlit/proto/Block_pb2.py +53 -0
  154. streamlit/proto/Block_pb2.pyi +322 -0
  155. streamlit/proto/BokehChart_pb2.py +27 -0
  156. streamlit/proto/BokehChart_pb2.pyi +49 -0
  157. streamlit/proto/ButtonGroup_pb2.py +36 -0
  158. streamlit/proto/ButtonGroup_pb2.pyi +169 -0
  159. streamlit/proto/Button_pb2.py +27 -0
  160. streamlit/proto/Button_pb2.pyi +71 -0
  161. streamlit/proto/CameraInput_pb2.py +28 -0
  162. streamlit/proto/CameraInput_pb2.pyi +58 -0
  163. streamlit/proto/ChatInput_pb2.py +31 -0
  164. streamlit/proto/ChatInput_pb2.pyi +111 -0
  165. streamlit/proto/Checkbox_pb2.py +30 -0
  166. streamlit/proto/Checkbox_pb2.pyi +90 -0
  167. streamlit/proto/ClientState_pb2.py +30 -0
  168. streamlit/proto/ClientState_pb2.pyi +90 -0
  169. streamlit/proto/Code_pb2.py +27 -0
  170. streamlit/proto/Code_pb2.pyi +55 -0
  171. streamlit/proto/ColorPicker_pb2.py +28 -0
  172. streamlit/proto/ColorPicker_pb2.pyi +67 -0
  173. streamlit/proto/Common_pb2.py +51 -0
  174. streamlit/proto/Common_pb2.pyi +293 -0
  175. streamlit/proto/Components_pb2.py +35 -0
  176. streamlit/proto/Components_pb2.pyi +172 -0
  177. streamlit/proto/DataFrame_pb2.py +56 -0
  178. streamlit/proto/DataFrame_pb2.pyi +397 -0
  179. streamlit/proto/DateInput_pb2.py +28 -0
  180. streamlit/proto/DateInput_pb2.pyi +83 -0
  181. streamlit/proto/DeckGlJsonChart_pb2.py +29 -0
  182. streamlit/proto/DeckGlJsonChart_pb2.pyi +102 -0
  183. streamlit/proto/Delta_pb2.py +31 -0
  184. streamlit/proto/Delta_pb2.pyi +74 -0
  185. streamlit/proto/DocString_pb2.py +29 -0
  186. streamlit/proto/DocString_pb2.pyi +93 -0
  187. streamlit/proto/DownloadButton_pb2.py +27 -0
  188. streamlit/proto/DownloadButton_pb2.pyi +70 -0
  189. streamlit/proto/Element_pb2.py +78 -0
  190. streamlit/proto/Element_pb2.pyi +312 -0
  191. streamlit/proto/Empty_pb2.py +27 -0
  192. streamlit/proto/Empty_pb2.pyi +36 -0
  193. streamlit/proto/Exception_pb2.py +27 -0
  194. streamlit/proto/Exception_pb2.pyi +72 -0
  195. streamlit/proto/Favicon_pb2.py +27 -0
  196. streamlit/proto/Favicon_pb2.pyi +40 -0
  197. streamlit/proto/FileUploader_pb2.py +28 -0
  198. streamlit/proto/FileUploader_pb2.pyi +78 -0
  199. streamlit/proto/ForwardMsg_pb2.py +53 -0
  200. streamlit/proto/ForwardMsg_pb2.pyi +293 -0
  201. streamlit/proto/GitInfo_pb2.py +29 -0
  202. streamlit/proto/GitInfo_pb2.pyi +83 -0
  203. streamlit/proto/GraphVizChart_pb2.py +27 -0
  204. streamlit/proto/GraphVizChart_pb2.pyi +53 -0
  205. streamlit/proto/Heading_pb2.py +27 -0
  206. streamlit/proto/Heading_pb2.pyi +56 -0
  207. streamlit/proto/Html_pb2.py +27 -0
  208. streamlit/proto/Html_pb2.pyi +42 -0
  209. streamlit/proto/IFrame_pb2.py +27 -0
  210. streamlit/proto/IFrame_pb2.pyi +59 -0
  211. streamlit/proto/Image_pb2.py +29 -0
  212. streamlit/proto/Image_pb2.pyi +84 -0
  213. streamlit/proto/Json_pb2.py +27 -0
  214. streamlit/proto/Json_pb2.pyi +53 -0
  215. streamlit/proto/LabelVisibilityMessage_pb2.py +29 -0
  216. streamlit/proto/LabelVisibilityMessage_pb2.pyi +68 -0
  217. streamlit/proto/LinkButton_pb2.py +27 -0
  218. streamlit/proto/LinkButton_pb2.pyi +58 -0
  219. streamlit/proto/Logo_pb2.py +27 -0
  220. streamlit/proto/Logo_pb2.pyi +51 -0
  221. streamlit/proto/Markdown_pb2.py +29 -0
  222. streamlit/proto/Markdown_pb2.pyi +86 -0
  223. streamlit/proto/Metric_pb2.py +32 -0
  224. streamlit/proto/Metric_pb2.pyi +101 -0
  225. streamlit/proto/MetricsEvent_pb2.py +30 -0
  226. streamlit/proto/MetricsEvent_pb2.pyi +200 -0
  227. streamlit/proto/MultiSelect_pb2.py +28 -0
  228. streamlit/proto/MultiSelect_pb2.pyi +81 -0
  229. streamlit/proto/NamedDataSet_pb2.py +28 -0
  230. streamlit/proto/NamedDataSet_pb2.pyi +59 -0
  231. streamlit/proto/Navigation_pb2.py +30 -0
  232. streamlit/proto/Navigation_pb2.pyi +84 -0
  233. streamlit/proto/NewSession_pb2.py +51 -0
  234. streamlit/proto/NewSession_pb2.pyi +481 -0
  235. streamlit/proto/NumberInput_pb2.py +30 -0
  236. streamlit/proto/NumberInput_pb2.pyi +121 -0
  237. streamlit/proto/PageConfig_pb2.py +33 -0
  238. streamlit/proto/PageConfig_pb2.pyi +126 -0
  239. streamlit/proto/PageInfo_pb2.py +27 -0
  240. streamlit/proto/PageInfo_pb2.pyi +43 -0
  241. streamlit/proto/PageLink_pb2.py +27 -0
  242. streamlit/proto/PageLink_pb2.pyi +63 -0
  243. streamlit/proto/PageNotFound_pb2.py +27 -0
  244. streamlit/proto/PageNotFound_pb2.pyi +42 -0
  245. streamlit/proto/PageProfile_pb2.py +31 -0
  246. streamlit/proto/PageProfile_pb2.pyi +127 -0
  247. streamlit/proto/PagesChanged_pb2.py +28 -0
  248. streamlit/proto/PagesChanged_pb2.pyi +48 -0
  249. streamlit/proto/ParentMessage_pb2.py +27 -0
  250. streamlit/proto/ParentMessage_pb2.pyi +46 -0
  251. streamlit/proto/PlotlyChart_pb2.py +31 -0
  252. streamlit/proto/PlotlyChart_pb2.pyi +131 -0
  253. streamlit/proto/Progress_pb2.py +27 -0
  254. streamlit/proto/Progress_pb2.pyi +43 -0
  255. streamlit/proto/Radio_pb2.py +28 -0
  256. streamlit/proto/Radio_pb2.pyi +84 -0
  257. streamlit/proto/RootContainer_pb2.py +27 -0
  258. streamlit/proto/RootContainer_pb2.pyi +56 -0
  259. streamlit/proto/Selectbox_pb2.py +28 -0
  260. streamlit/proto/Selectbox_pb2.pyi +80 -0
  261. streamlit/proto/SessionEvent_pb2.py +28 -0
  262. streamlit/proto/SessionEvent_pb2.pyi +62 -0
  263. streamlit/proto/SessionStatus_pb2.py +27 -0
  264. streamlit/proto/SessionStatus_pb2.pyi +57 -0
  265. streamlit/proto/Skeleton_pb2.py +29 -0
  266. streamlit/proto/Skeleton_pb2.pyi +71 -0
  267. streamlit/proto/Slider_pb2.py +32 -0
  268. streamlit/proto/Slider_pb2.pyi +142 -0
  269. streamlit/proto/Snow_pb2.py +27 -0
  270. streamlit/proto/Snow_pb2.pyi +43 -0
  271. streamlit/proto/Spinner_pb2.py +27 -0
  272. streamlit/proto/Spinner_pb2.pyi +49 -0
  273. streamlit/proto/TextArea_pb2.py +28 -0
  274. streamlit/proto/TextArea_pb2.pyi +80 -0
  275. streamlit/proto/TextInput_pb2.py +30 -0
  276. streamlit/proto/TextInput_pb2.pyi +107 -0
  277. streamlit/proto/Text_pb2.py +27 -0
  278. streamlit/proto/Text_pb2.pyi +46 -0
  279. streamlit/proto/TimeInput_pb2.py +28 -0
  280. streamlit/proto/TimeInput_pb2.pyi +74 -0
  281. streamlit/proto/Toast_pb2.py +27 -0
  282. streamlit/proto/Toast_pb2.pyi +45 -0
  283. streamlit/proto/VegaLiteChart_pb2.py +29 -0
  284. streamlit/proto/VegaLiteChart_pb2.pyi +71 -0
  285. streamlit/proto/Video_pb2.py +31 -0
  286. streamlit/proto/Video_pb2.pyi +117 -0
  287. streamlit/proto/WidgetStates_pb2.py +31 -0
  288. streamlit/proto/WidgetStates_pb2.pyi +126 -0
  289. streamlit/proto/__init__.py +15 -0
  290. streamlit/proto/openmetrics_data_model_pb2.py +60 -0
  291. streamlit/proto/openmetrics_data_model_pb2.pyi +522 -0
  292. streamlit/py.typed +0 -0
  293. streamlit/runtime/__init__.py +50 -0
  294. streamlit/runtime/app_session.py +982 -0
  295. streamlit/runtime/caching/__init__.py +98 -0
  296. streamlit/runtime/caching/cache_data_api.py +665 -0
  297. streamlit/runtime/caching/cache_errors.py +142 -0
  298. streamlit/runtime/caching/cache_resource_api.py +527 -0
  299. streamlit/runtime/caching/cache_type.py +33 -0
  300. streamlit/runtime/caching/cache_utils.py +523 -0
  301. streamlit/runtime/caching/cached_message_replay.py +290 -0
  302. streamlit/runtime/caching/hashing.py +637 -0
  303. streamlit/runtime/caching/legacy_cache_api.py +169 -0
  304. streamlit/runtime/caching/storage/__init__.py +29 -0
  305. streamlit/runtime/caching/storage/cache_storage_protocol.py +239 -0
  306. streamlit/runtime/caching/storage/dummy_cache_storage.py +60 -0
  307. streamlit/runtime/caching/storage/in_memory_cache_storage_wrapper.py +145 -0
  308. streamlit/runtime/caching/storage/local_disk_cache_storage.py +223 -0
  309. streamlit/runtime/connection_factory.py +436 -0
  310. streamlit/runtime/context.py +280 -0
  311. streamlit/runtime/credentials.py +364 -0
  312. streamlit/runtime/forward_msg_cache.py +296 -0
  313. streamlit/runtime/forward_msg_queue.py +240 -0
  314. streamlit/runtime/fragment.py +477 -0
  315. streamlit/runtime/media_file_manager.py +234 -0
  316. streamlit/runtime/media_file_storage.py +143 -0
  317. streamlit/runtime/memory_media_file_storage.py +181 -0
  318. streamlit/runtime/memory_session_storage.py +77 -0
  319. streamlit/runtime/memory_uploaded_file_manager.py +138 -0
  320. streamlit/runtime/metrics_util.py +486 -0
  321. streamlit/runtime/pages_manager.py +165 -0
  322. streamlit/runtime/runtime.py +792 -0
  323. streamlit/runtime/runtime_util.py +106 -0
  324. streamlit/runtime/script_data.py +46 -0
  325. streamlit/runtime/scriptrunner/__init__.py +38 -0
  326. streamlit/runtime/scriptrunner/exec_code.py +159 -0
  327. streamlit/runtime/scriptrunner/magic.py +273 -0
  328. streamlit/runtime/scriptrunner/magic_funcs.py +32 -0
  329. streamlit/runtime/scriptrunner/script_cache.py +89 -0
  330. streamlit/runtime/scriptrunner/script_runner.py +756 -0
  331. streamlit/runtime/scriptrunner_utils/__init__.py +19 -0
  332. streamlit/runtime/scriptrunner_utils/exceptions.py +48 -0
  333. streamlit/runtime/scriptrunner_utils/script_requests.py +307 -0
  334. streamlit/runtime/scriptrunner_utils/script_run_context.py +287 -0
  335. streamlit/runtime/secrets.py +534 -0
  336. streamlit/runtime/session_manager.py +394 -0
  337. streamlit/runtime/state/__init__.py +41 -0
  338. streamlit/runtime/state/common.py +191 -0
  339. streamlit/runtime/state/query_params.py +205 -0
  340. streamlit/runtime/state/query_params_proxy.py +218 -0
  341. streamlit/runtime/state/safe_session_state.py +138 -0
  342. streamlit/runtime/state/session_state.py +772 -0
  343. streamlit/runtime/state/session_state_proxy.py +153 -0
  344. streamlit/runtime/state/widgets.py +135 -0
  345. streamlit/runtime/stats.py +109 -0
  346. streamlit/runtime/uploaded_file_manager.py +148 -0
  347. streamlit/runtime/websocket_session_manager.py +167 -0
  348. streamlit/source_util.py +98 -0
  349. streamlit/static/favicon.png +0 -0
  350. streamlit/static/index.html +61 -0
  351. streamlit/static/static/css/index.Bmkmz40k.css +1 -0
  352. streamlit/static/static/css/index.DpJG_94W.css +1 -0
  353. streamlit/static/static/css/index.DzuxGC_t.css +1 -0
  354. streamlit/static/static/js/FileDownload.esm.Bp9m5jrx.js +1 -0
  355. streamlit/static/static/js/FileHelper.D_3pbilj.js +5 -0
  356. streamlit/static/static/js/FormClearHelper.Ct2rwLXo.js +1 -0
  357. streamlit/static/static/js/Hooks.BKdzj5MJ.js +1 -0
  358. streamlit/static/static/js/InputInstructions.DB3QGNJP.js +1 -0
  359. streamlit/static/static/js/ProgressBar.D40A5xc2.js +2 -0
  360. streamlit/static/static/js/RenderInPortalIfExists.DLUCooTN.js +1 -0
  361. streamlit/static/static/js/Toolbar.BiGGIQun.js +1 -0
  362. streamlit/static/static/js/UploadFileInfo.C-jY39rj.js +1 -0
  363. streamlit/static/static/js/base-input.CQBQT24M.js +4 -0
  364. streamlit/static/static/js/checkbox.Buj8gd_M.js +9 -0
  365. streamlit/static/static/js/createDownloadLinkElement.DZMwyjvU.js +1 -0
  366. streamlit/static/static/js/createSuper.CesK3I23.js +1 -0
  367. streamlit/static/static/js/data-grid-overlay-editor.B69OOFM4.js +1 -0
  368. streamlit/static/static/js/downloader.BZQhlBNT.js +1 -0
  369. streamlit/static/static/js/es6.D9Zhqujy.js +2 -0
  370. streamlit/static/static/js/iframeResizer.contentWindow.CAzcBpCC.js +1 -0
  371. streamlit/static/static/js/index.08vcOOvb.js +1 -0
  372. streamlit/static/static/js/index.0uqKfJUS.js +1 -0
  373. streamlit/static/static/js/index.B02M5u69.js +203 -0
  374. streamlit/static/static/js/index.B7mcZKMx.js +1 -0
  375. streamlit/static/static/js/index.BAQDHFA_.js +1 -0
  376. streamlit/static/static/js/index.BI60cMVr.js +2 -0
  377. streamlit/static/static/js/index.BLug2inK.js +1 -0
  378. streamlit/static/static/js/index.BM6TMY8g.js +2 -0
  379. streamlit/static/static/js/index.BZ9p1t7G.js +1 -0
  380. streamlit/static/static/js/index.BZqa87a1.js +2 -0
  381. streamlit/static/static/js/index.BcsRUzZZ.js +1 -0
  382. streamlit/static/static/js/index.BgVMiY_P.js +197 -0
  383. streamlit/static/static/js/index.BtuGy7By.js +6 -0
  384. streamlit/static/static/js/index.BuDuBmrs.js +1 -0
  385. streamlit/static/static/js/index.BvXU2oKV.js +1 -0
  386. streamlit/static/static/js/index.BxcwPacT.js +73 -0
  387. streamlit/static/static/js/index.CWX8KB81.js +1 -0
  388. streamlit/static/static/js/index.CXzZTo_q.js +1 -0
  389. streamlit/static/static/js/index.CcRWp_KL.js +1 -0
  390. streamlit/static/static/js/index.Cd-_xe55.js +3 -0
  391. streamlit/static/static/js/index.CdG2PXln.js +4537 -0
  392. streamlit/static/static/js/index.CjXvXmcP.js +1 -0
  393. streamlit/static/static/js/index.D1HZENZx.js +776 -0
  394. streamlit/static/static/js/index.D21Efo64.js +1617 -0
  395. streamlit/static/static/js/index.D9WgGVBx.js +7 -0
  396. streamlit/static/static/js/index.DEcsHtvb.js +12 -0
  397. streamlit/static/static/js/index.DFeMfr_K.js +1 -0
  398. streamlit/static/static/js/index.DHFBoItz.js +1 -0
  399. streamlit/static/static/js/index.D_PrBKnJ.js +3 -0
  400. streamlit/static/static/js/index.DmuRkekN.js +3855 -0
  401. streamlit/static/static/js/index.Do6eY8sf.js +1 -0
  402. streamlit/static/static/js/index.Dz3lP2P-.js +1 -0
  403. streamlit/static/static/js/index.Dz_UqF-s.js +1 -0
  404. streamlit/static/static/js/index.GkSUsPhJ.js +1 -0
  405. streamlit/static/static/js/index.H1U1IC_d.js +3 -0
  406. streamlit/static/static/js/index.g6p_4DPr.js +1 -0
  407. streamlit/static/static/js/index.g9x_GKss.js +1 -0
  408. streamlit/static/static/js/index.zo9jm08y.js +1 -0
  409. streamlit/static/static/js/input.DnaFglHq.js +2 -0
  410. streamlit/static/static/js/inputUtils.CQWz5UKz.js +1 -0
  411. streamlit/static/static/js/memory.Crb9x4-F.js +1 -0
  412. streamlit/static/static/js/mergeWith.ouAz0sK3.js +1 -0
  413. streamlit/static/static/js/number-overlay-editor._UaN-O48.js +9 -0
  414. streamlit/static/static/js/possibleConstructorReturn.CtGjGFHz.js +1 -0
  415. streamlit/static/static/js/sandbox.CBybYOhV.js +1 -0
  416. streamlit/static/static/js/sprintf.D7DtBTRn.js +1 -0
  417. streamlit/static/static/js/textarea.Cb_uJt5U.js +2 -0
  418. streamlit/static/static/js/threshold.DjX0wlsa.js +1 -0
  419. streamlit/static/static/js/timepicker.DKT7pfoF.js +4 -0
  420. streamlit/static/static/js/timer.CAwTRJ_g.js +1 -0
  421. streamlit/static/static/js/toConsumableArray.05Ikp13-.js +3 -0
  422. streamlit/static/static/js/uniqueId.D2FMWUEI.js +1 -0
  423. streamlit/static/static/js/useBasicWidgetState.urnZLANY.js +1 -0
  424. streamlit/static/static/js/useOnInputChange.BOKIIdJ1.js +1 -0
  425. streamlit/static/static/js/value.CgPGBV_l.js +1 -0
  426. streamlit/static/static/js/withFullScreenWrapper.C_N8J0Xx.js +1 -0
  427. streamlit/static/static/media/KaTeX_AMS-Regular.BQhdFMY1.woff2 +0 -0
  428. streamlit/static/static/media/KaTeX_AMS-Regular.DMm9YOAa.woff +0 -0
  429. streamlit/static/static/media/KaTeX_AMS-Regular.DRggAlZN.ttf +0 -0
  430. streamlit/static/static/media/KaTeX_Caligraphic-Bold.ATXxdsX0.ttf +0 -0
  431. streamlit/static/static/media/KaTeX_Caligraphic-Bold.BEiXGLvX.woff +0 -0
  432. streamlit/static/static/media/KaTeX_Caligraphic-Bold.Dq_IR9rO.woff2 +0 -0
  433. streamlit/static/static/media/KaTeX_Caligraphic-Regular.CTRA-rTL.woff +0 -0
  434. streamlit/static/static/media/KaTeX_Caligraphic-Regular.Di6jR-x-.woff2 +0 -0
  435. streamlit/static/static/media/KaTeX_Caligraphic-Regular.wX97UBjC.ttf +0 -0
  436. streamlit/static/static/media/KaTeX_Fraktur-Bold.BdnERNNW.ttf +0 -0
  437. streamlit/static/static/media/KaTeX_Fraktur-Bold.BsDP51OF.woff +0 -0
  438. streamlit/static/static/media/KaTeX_Fraktur-Bold.CL6g_b3V.woff2 +0 -0
  439. streamlit/static/static/media/KaTeX_Fraktur-Regular.CB_wures.ttf +0 -0
  440. streamlit/static/static/media/KaTeX_Fraktur-Regular.CTYiF6lA.woff2 +0 -0
  441. streamlit/static/static/media/KaTeX_Fraktur-Regular.Dxdc4cR9.woff +0 -0
  442. streamlit/static/static/media/KaTeX_Main-Bold.Cx986IdX.woff2 +0 -0
  443. streamlit/static/static/media/KaTeX_Main-Bold.Jm3AIy58.woff +0 -0
  444. streamlit/static/static/media/KaTeX_Main-Bold.waoOVXN0.ttf +0 -0
  445. streamlit/static/static/media/KaTeX_Main-BoldItalic.DxDJ3AOS.woff2 +0 -0
  446. streamlit/static/static/media/KaTeX_Main-BoldItalic.DzxPMmG6.ttf +0 -0
  447. streamlit/static/static/media/KaTeX_Main-BoldItalic.SpSLRI95.woff +0 -0
  448. streamlit/static/static/media/KaTeX_Main-Italic.3WenGoN9.ttf +0 -0
  449. streamlit/static/static/media/KaTeX_Main-Italic.BMLOBm91.woff +0 -0
  450. streamlit/static/static/media/KaTeX_Main-Italic.NWA7e6Wa.woff2 +0 -0
  451. streamlit/static/static/media/KaTeX_Main-Regular.B22Nviop.woff2 +0 -0
  452. streamlit/static/static/media/KaTeX_Main-Regular.Dr94JaBh.woff +0 -0
  453. streamlit/static/static/media/KaTeX_Main-Regular.ypZvNtVU.ttf +0 -0
  454. streamlit/static/static/media/KaTeX_Math-BoldItalic.B3XSjfu4.ttf +0 -0
  455. streamlit/static/static/media/KaTeX_Math-BoldItalic.CZnvNsCZ.woff2 +0 -0
  456. streamlit/static/static/media/KaTeX_Math-BoldItalic.iY-2wyZ7.woff +0 -0
  457. streamlit/static/static/media/KaTeX_Math-Italic.DA0__PXp.woff +0 -0
  458. streamlit/static/static/media/KaTeX_Math-Italic.flOr_0UB.ttf +0 -0
  459. streamlit/static/static/media/KaTeX_Math-Italic.t53AETM-.woff2 +0 -0
  460. streamlit/static/static/media/KaTeX_SansSerif-Bold.CFMepnvq.ttf +0 -0
  461. streamlit/static/static/media/KaTeX_SansSerif-Bold.D1sUS0GD.woff2 +0 -0
  462. streamlit/static/static/media/KaTeX_SansSerif-Bold.DbIhKOiC.woff +0 -0
  463. streamlit/static/static/media/KaTeX_SansSerif-Italic.C3H0VqGB.woff2 +0 -0
  464. streamlit/static/static/media/KaTeX_SansSerif-Italic.DN2j7dab.woff +0 -0
  465. streamlit/static/static/media/KaTeX_SansSerif-Italic.YYjJ1zSn.ttf +0 -0
  466. streamlit/static/static/media/KaTeX_SansSerif-Regular.BNo7hRIc.ttf +0 -0
  467. streamlit/static/static/media/KaTeX_SansSerif-Regular.CS6fqUqJ.woff +0 -0
  468. streamlit/static/static/media/KaTeX_SansSerif-Regular.DDBCnlJ7.woff2 +0 -0
  469. streamlit/static/static/media/KaTeX_Script-Regular.C5JkGWo-.ttf +0 -0
  470. streamlit/static/static/media/KaTeX_Script-Regular.D3wIWfF6.woff2 +0 -0
  471. streamlit/static/static/media/KaTeX_Script-Regular.D5yQViql.woff +0 -0
  472. streamlit/static/static/media/KaTeX_Size1-Regular.C195tn64.woff +0 -0
  473. streamlit/static/static/media/KaTeX_Size1-Regular.Dbsnue_I.ttf +0 -0
  474. streamlit/static/static/media/KaTeX_Size1-Regular.mCD8mA8B.woff2 +0 -0
  475. streamlit/static/static/media/KaTeX_Size2-Regular.B7gKUWhC.ttf +0 -0
  476. streamlit/static/static/media/KaTeX_Size2-Regular.Dy4dx90m.woff2 +0 -0
  477. streamlit/static/static/media/KaTeX_Size2-Regular.oD1tc_U0.woff +0 -0
  478. streamlit/static/static/media/KaTeX_Size3-Regular.CTq5MqoE.woff +0 -0
  479. streamlit/static/static/media/KaTeX_Size3-Regular.DgpXs0kz.ttf +0 -0
  480. streamlit/static/static/media/KaTeX_Size4-Regular.BF-4gkZK.woff +0 -0
  481. streamlit/static/static/media/KaTeX_Size4-Regular.DWFBv043.ttf +0 -0
  482. streamlit/static/static/media/KaTeX_Size4-Regular.Dl5lxZxV.woff2 +0 -0
  483. streamlit/static/static/media/KaTeX_Typewriter-Regular.C0xS9mPB.woff +0 -0
  484. streamlit/static/static/media/KaTeX_Typewriter-Regular.CO6r4hn1.woff2 +0 -0
  485. streamlit/static/static/media/KaTeX_Typewriter-Regular.D3Ib7_Hf.ttf +0 -0
  486. streamlit/static/static/media/MaterialSymbols-Rounded.DcZbplWk.woff2 +0 -0
  487. streamlit/static/static/media/SourceCodePro-Bold.CFEfr7-q.woff2 +0 -0
  488. streamlit/static/static/media/SourceCodePro-BoldItalic.C-LkFXxa.woff2 +0 -0
  489. streamlit/static/static/media/SourceCodePro-Italic.CxFOx7N-.woff2 +0 -0
  490. streamlit/static/static/media/SourceCodePro-Regular.CBOlD63d.woff2 +0 -0
  491. streamlit/static/static/media/SourceCodePro-SemiBold.CFHwW3Wd.woff2 +0 -0
  492. streamlit/static/static/media/SourceCodePro-SemiBoldItalic.Cg2yRu82.woff2 +0 -0
  493. streamlit/static/static/media/SourceSansPro-Bold.-6c9oR8J.woff2 +0 -0
  494. streamlit/static/static/media/SourceSansPro-BoldItalic.DmM_grLY.woff2 +0 -0
  495. streamlit/static/static/media/SourceSansPro-Italic.I1ipWe7Q.woff2 +0 -0
  496. streamlit/static/static/media/SourceSansPro-Regular.DZLUzqI4.woff2 +0 -0
  497. streamlit/static/static/media/SourceSansPro-SemiBold.sKQIyTMz.woff2 +0 -0
  498. streamlit/static/static/media/SourceSansPro-SemiBoldItalic.C0wP0icr.woff2 +0 -0
  499. streamlit/static/static/media/SourceSerifPro-Bold.8TUnKj4x.woff2 +0 -0
  500. streamlit/static/static/media/SourceSerifPro-BoldItalic.CBVO7Ve7.woff2 +0 -0
  501. streamlit/static/static/media/SourceSerifPro-Italic.DkFgL2HZ.woff2 +0 -0
  502. streamlit/static/static/media/SourceSerifPro-Regular.CNJNET2S.woff2 +0 -0
  503. streamlit/static/static/media/SourceSerifPro-SemiBold.CHyh9GC5.woff2 +0 -0
  504. streamlit/static/static/media/SourceSerifPro-SemiBoldItalic.CBtz8sWN.woff2 +0 -0
  505. streamlit/static/static/media/balloon-0.Czj7AKwE.png +0 -0
  506. streamlit/static/static/media/balloon-1.CNvFFrND.png +0 -0
  507. streamlit/static/static/media/balloon-2.DTvC6B1t.png +0 -0
  508. streamlit/static/static/media/balloon-3.CgSk4tbL.png +0 -0
  509. streamlit/static/static/media/balloon-4.mbtFrzxf.png +0 -0
  510. streamlit/static/static/media/balloon-5.CSwkUfRA.png +0 -0
  511. streamlit/static/static/media/fireworks.B4d-_KUe.gif +0 -0
  512. streamlit/static/static/media/flake-0.DgWaVvm5.png +0 -0
  513. streamlit/static/static/media/flake-1.B2r5AHMK.png +0 -0
  514. streamlit/static/static/media/flake-2.BnWSExPC.png +0 -0
  515. streamlit/static/static/media/snowflake.JU2jBHL8.svg +11 -0
  516. streamlit/string_util.py +203 -0
  517. streamlit/temporary_directory.py +56 -0
  518. streamlit/testing/__init__.py +13 -0
  519. streamlit/testing/v1/__init__.py +17 -0
  520. streamlit/testing/v1/app_test.py +1050 -0
  521. streamlit/testing/v1/element_tree.py +2083 -0
  522. streamlit/testing/v1/local_script_runner.py +180 -0
  523. streamlit/testing/v1/util.py +53 -0
  524. streamlit/time_util.py +75 -0
  525. streamlit/type_util.py +460 -0
  526. streamlit/url_util.py +122 -0
  527. streamlit/user_info.py +519 -0
  528. streamlit/util.py +72 -0
  529. streamlit/vendor/__init__.py +0 -0
  530. streamlit/vendor/pympler/__init__.py +0 -0
  531. streamlit/vendor/pympler/asizeof.py +2869 -0
  532. streamlit/version.py +18 -0
  533. streamlit/watcher/__init__.py +28 -0
  534. streamlit/watcher/event_based_path_watcher.py +406 -0
  535. streamlit/watcher/folder_black_list.py +82 -0
  536. streamlit/watcher/local_sources_watcher.py +233 -0
  537. streamlit/watcher/path_watcher.py +185 -0
  538. streamlit/watcher/polling_path_watcher.py +124 -0
  539. streamlit/watcher/util.py +207 -0
  540. streamlit/web/__init__.py +13 -0
  541. streamlit/web/bootstrap.py +353 -0
  542. streamlit/web/cache_storage_manager_config.py +38 -0
  543. streamlit/web/cli.py +369 -0
  544. streamlit/web/server/__init__.py +26 -0
  545. streamlit/web/server/app_static_file_handler.py +93 -0
  546. streamlit/web/server/authlib_tornado_integration.py +60 -0
  547. streamlit/web/server/browser_websocket_handler.py +246 -0
  548. streamlit/web/server/component_request_handler.py +116 -0
  549. streamlit/web/server/media_file_handler.py +141 -0
  550. streamlit/web/server/oauth_authlib_routes.py +176 -0
  551. streamlit/web/server/oidc_mixin.py +108 -0
  552. streamlit/web/server/routes.py +295 -0
  553. streamlit/web/server/server.py +479 -0
  554. streamlit/web/server/server_util.py +161 -0
  555. streamlit/web/server/stats_request_handler.py +95 -0
  556. streamlit/web/server/upload_file_request_handler.py +137 -0
  557. streamlit/web/server/websocket_headers.py +56 -0
  558. streamlit_nightly-1.43.2.dev20250307.data/scripts/streamlit.cmd +16 -0
  559. streamlit_nightly-1.43.2.dev20250307.dist-info/METADATA +207 -0
  560. streamlit_nightly-1.43.2.dev20250307.dist-info/RECORD +563 -0
  561. streamlit_nightly-1.43.2.dev20250307.dist-info/WHEEL +5 -0
  562. streamlit_nightly-1.43.2.dev20250307.dist-info/entry_points.txt +2 -0
  563. streamlit_nightly-1.43.2.dev20250307.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2083 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ from __future__ import annotations
15
+
16
+ import textwrap
17
+ from abc import ABC, abstractmethod
18
+ from collections.abc import Sequence
19
+ from dataclasses import dataclass, field, fields, is_dataclass
20
+ from datetime import date, datetime, time, timedelta
21
+ from typing import (
22
+ TYPE_CHECKING,
23
+ Any,
24
+ Callable,
25
+ Generic,
26
+ TypeVar,
27
+ Union,
28
+ cast,
29
+ overload,
30
+ )
31
+
32
+ from typing_extensions import TypeAlias
33
+
34
+ from streamlit import dataframe_util, util
35
+ from streamlit.elements.heading import HeadingProtoTag
36
+ from streamlit.elements.widgets.select_slider import SelectSliderSerde
37
+ from streamlit.elements.widgets.slider import (
38
+ SliderSerde,
39
+ SliderStep,
40
+ SliderValueT,
41
+ )
42
+ from streamlit.elements.widgets.time_widgets import (
43
+ DateInputSerde,
44
+ DateWidgetReturn,
45
+ TimeInputSerde,
46
+ _parse_date_value,
47
+ )
48
+ from streamlit.proto.Alert_pb2 import Alert as AlertProto
49
+ from streamlit.proto.Checkbox_pb2 import Checkbox as CheckboxProto
50
+ from streamlit.proto.Markdown_pb2 import Markdown as MarkdownProto
51
+ from streamlit.proto.Slider_pb2 import Slider as SliderProto
52
+ from streamlit.proto.WidgetStates_pb2 import WidgetState, WidgetStates
53
+ from streamlit.runtime.state.common import TESTING_KEY, user_key_from_element_id
54
+
55
+ if TYPE_CHECKING:
56
+ from pandas import DataFrame as PandasDataframe
57
+
58
+ from streamlit.proto.Arrow_pb2 import Arrow as ArrowProto
59
+ from streamlit.proto.Block_pb2 import Block as BlockProto
60
+ from streamlit.proto.Button_pb2 import Button as ButtonProto
61
+ from streamlit.proto.ButtonGroup_pb2 import ButtonGroup as ButtonGroupProto
62
+ from streamlit.proto.ChatInput_pb2 import ChatInput as ChatInputProto
63
+ from streamlit.proto.Code_pb2 import Code as CodeProto
64
+ from streamlit.proto.ColorPicker_pb2 import ColorPicker as ColorPickerProto
65
+ from streamlit.proto.DateInput_pb2 import DateInput as DateInputProto
66
+ from streamlit.proto.Element_pb2 import Element as ElementProto
67
+ from streamlit.proto.Exception_pb2 import Exception as ExceptionProto
68
+ from streamlit.proto.ForwardMsg_pb2 import ForwardMsg
69
+ from streamlit.proto.Heading_pb2 import Heading as HeadingProto
70
+ from streamlit.proto.Json_pb2 import Json as JsonProto
71
+ from streamlit.proto.Metric_pb2 import Metric as MetricProto
72
+ from streamlit.proto.MultiSelect_pb2 import MultiSelect as MultiSelectProto
73
+ from streamlit.proto.NumberInput_pb2 import NumberInput as NumberInputProto
74
+ from streamlit.proto.Radio_pb2 import Radio as RadioProto
75
+ from streamlit.proto.Selectbox_pb2 import Selectbox as SelectboxProto
76
+ from streamlit.proto.Text_pb2 import Text as TextProto
77
+ from streamlit.proto.TextArea_pb2 import TextArea as TextAreaProto
78
+ from streamlit.proto.TextInput_pb2 import TextInput as TextInputProto
79
+ from streamlit.proto.TimeInput_pb2 import TimeInput as TimeInputProto
80
+ from streamlit.proto.Toast_pb2 import Toast as ToastProto
81
+ from streamlit.runtime.state.safe_session_state import SafeSessionState
82
+ from streamlit.testing.v1.app_test import AppTest
83
+
84
+ T = TypeVar("T")
85
+
86
+
87
+ @dataclass
88
+ class InitialValue:
89
+ """This class is used to represent the initial value of a widget."""
90
+
91
+ pass
92
+
93
+
94
+ # TODO: This class serves as a fallback option for elements that have not
95
+ # been implemented yet, as well as providing implementations of some
96
+ # trivial methods. It may have significantly reduced scope once all elements
97
+ # have been implemented.
98
+ # This class will not be sufficient implementation for most elements.
99
+ # Widgets need their own classes to translate interactions into the appropriate
100
+ # WidgetState and provide higher level interaction interfaces, and other elements
101
+ # have enough variation in how to get their values that most will need their
102
+ # own classes too.
103
+ @dataclass
104
+ class Element(ABC):
105
+ """
106
+ Element base class for testing.
107
+
108
+ This class's methods and attributes are universal for all elements
109
+ implemented in testing. For example, ``Caption``, ``Code``, ``Text``, and
110
+ ``Title`` inherit from ``Element``. All widget classes also
111
+ inherit from Element, but have additional methods specific to each
112
+ widget type. See the ``AppTest`` class for the full list of supported
113
+ elements.
114
+
115
+ For all element classes, parameters of the original element can be obtained
116
+ as properties. For example, ``Button.label``, ``Caption.help``, and
117
+ ``Toast.icon``.
118
+
119
+ """
120
+
121
+ type: str = field(repr=False)
122
+ proto: Any = field(repr=False)
123
+ root: ElementTree = field(repr=False)
124
+ key: str | None
125
+
126
+ @abstractmethod
127
+ def __init__(self, proto: ElementProto, root: ElementTree): ...
128
+
129
+ def __iter__(self):
130
+ yield self
131
+
132
+ @property
133
+ @abstractmethod
134
+ def value(self) -> Any:
135
+ """The value or contents of the element."""
136
+ ...
137
+
138
+ def __getattr__(self, name: str) -> Any:
139
+ """Fallback attempt to get an attribute from the proto"""
140
+ return getattr(self.proto, name)
141
+
142
+ def run(self, *, timeout: float | None = None) -> AppTest:
143
+ """Run the ``AppTest`` script which contains the element.
144
+
145
+ Parameters
146
+ ----------
147
+ timeout
148
+ The maximum number of seconds to run the script. None means
149
+ use the AppTest's default.
150
+ """
151
+ return self.root.run(timeout=timeout)
152
+
153
+ def __repr__(self):
154
+ return util.repr_(self)
155
+
156
+
157
+ @dataclass(repr=False)
158
+ class UnknownElement(Element):
159
+ def __init__(self, proto: ElementProto, root: ElementTree):
160
+ ty = proto.WhichOneof("type")
161
+ assert ty is not None
162
+ self.proto = getattr(proto, ty)
163
+ self.root = root
164
+ self.type = ty
165
+ self.key = None
166
+
167
+ @property
168
+ def value(self) -> Any:
169
+ try:
170
+ state = self.root.session_state
171
+ assert state is not None
172
+ return state[self.proto.id]
173
+ except ValueError:
174
+ # No id field, not a widget
175
+ return self.proto.value
176
+
177
+
178
+ @dataclass(repr=False)
179
+ class Widget(Element, ABC):
180
+ """Widget base class for testing."""
181
+
182
+ id: str = field(repr=False)
183
+ disabled: bool
184
+ key: str | None
185
+ _value: Any
186
+
187
+ def __init__(self, proto: Any, root: ElementTree):
188
+ self.proto = proto
189
+ self.root = root
190
+ self.key = user_key_from_element_id(self.id)
191
+ self._value = None
192
+
193
+ def set_value(self, v: Any):
194
+ """Set the value of the widget."""
195
+ self._value = v
196
+ return self
197
+
198
+ @property
199
+ @abstractmethod
200
+ def _widget_state(self) -> WidgetState: ...
201
+
202
+
203
+ El_co = TypeVar("El_co", bound=Element, covariant=True)
204
+
205
+
206
+ class ElementList(Generic[El_co]):
207
+ def __init__(self, els: Sequence[El_co]):
208
+ self._list: Sequence[El_co] = els
209
+
210
+ def __len__(self) -> int:
211
+ return len(self._list)
212
+
213
+ @property
214
+ def len(self) -> int:
215
+ return len(self)
216
+
217
+ @overload
218
+ def __getitem__(self, idx: int) -> El_co: ...
219
+
220
+ @overload
221
+ def __getitem__(self, idx: slice) -> ElementList[El_co]: ...
222
+
223
+ def __getitem__(self, idx: int | slice) -> El_co | ElementList[El_co]:
224
+ if isinstance(idx, slice):
225
+ return ElementList(self._list[idx])
226
+ else:
227
+ return self._list[idx]
228
+
229
+ def __iter__(self):
230
+ yield from self._list
231
+
232
+ def __repr__(self):
233
+ return util.repr_(self)
234
+
235
+ def __eq__(self, other: ElementList[El_co] | object) -> bool:
236
+ if isinstance(other, ElementList):
237
+ return self._list == other._list
238
+ else:
239
+ return self._list == other
240
+
241
+ @property
242
+ def values(self) -> Sequence[Any]:
243
+ return [e.value for e in self]
244
+
245
+
246
+ W_co = TypeVar("W_co", bound=Widget, covariant=True)
247
+
248
+
249
+ class WidgetList(ElementList[W_co], Generic[W_co]):
250
+ def __call__(self, key: str) -> W_co:
251
+ for e in self._list:
252
+ if e.key == key:
253
+ return e
254
+
255
+ raise KeyError(key)
256
+
257
+
258
+ @dataclass(repr=False)
259
+ class AlertBase(Element):
260
+ proto: AlertProto = field(repr=False)
261
+ icon: str
262
+
263
+ def __init__(self, proto: AlertProto, root: ElementTree):
264
+ self.proto = proto
265
+ self.key = None
266
+ self.root = root
267
+
268
+ @property
269
+ def value(self) -> str:
270
+ return self.proto.body
271
+
272
+
273
+ @dataclass(repr=False)
274
+ class Error(AlertBase):
275
+ def __init__(self, proto: AlertProto, root: ElementTree):
276
+ super().__init__(proto, root)
277
+ self.type = "error"
278
+
279
+
280
+ @dataclass(repr=False)
281
+ class Warning(AlertBase):
282
+ def __init__(self, proto: AlertProto, root: ElementTree):
283
+ super().__init__(proto, root)
284
+ self.type = "warning"
285
+
286
+
287
+ @dataclass(repr=False)
288
+ class Info(AlertBase):
289
+ def __init__(self, proto: AlertProto, root: ElementTree):
290
+ super().__init__(proto, root)
291
+ self.type = "info"
292
+
293
+
294
+ @dataclass(repr=False)
295
+ class Success(AlertBase):
296
+ def __init__(self, proto: AlertProto, root: ElementTree):
297
+ super().__init__(proto, root)
298
+ self.type = "success"
299
+
300
+
301
+ @dataclass(repr=False)
302
+ class Button(Widget):
303
+ """A representation of ``st.button`` and ``st.form_submit_button``."""
304
+
305
+ _value: bool
306
+
307
+ proto: ButtonProto = field(repr=False)
308
+ label: str
309
+ help: str
310
+ form_id: str
311
+
312
+ def __init__(self, proto: ButtonProto, root: ElementTree):
313
+ super().__init__(proto, root)
314
+ self._value = False
315
+ self.type = "button"
316
+
317
+ @property
318
+ def _widget_state(self) -> WidgetState:
319
+ ws = WidgetState()
320
+ ws.id = self.id
321
+ ws.trigger_value = self._value
322
+ return ws
323
+
324
+ @property
325
+ def value(self) -> bool:
326
+ """The value of the button. (bool)"""
327
+ if self._value:
328
+ return self._value
329
+ else:
330
+ state = self.root.session_state
331
+ assert state
332
+ return cast(bool, state[TESTING_KEY][self.id])
333
+
334
+ def set_value(self, v: bool) -> Button:
335
+ """Set the value of the button."""
336
+ self._value = v
337
+ return self
338
+
339
+ def click(self) -> Button:
340
+ """Set the value of the button to True."""
341
+ return self.set_value(True)
342
+
343
+
344
+ @dataclass(repr=False)
345
+ class ChatInput(Widget):
346
+ """A representation of ``st.chat_input``."""
347
+
348
+ _value: str | None
349
+ proto: ChatInputProto = field(repr=False)
350
+ placeholder: str
351
+
352
+ def __init__(self, proto: ChatInputProto, root: ElementTree):
353
+ super().__init__(proto, root)
354
+ self.type = "chat_input"
355
+
356
+ def set_value(self, v: str | None) -> ChatInput:
357
+ """Set the value of the widget."""
358
+ self._value = v
359
+ return self
360
+
361
+ @property
362
+ def _widget_state(self) -> WidgetState:
363
+ ws = WidgetState()
364
+ ws.id = self.id
365
+ if self._value is not None:
366
+ ws.string_trigger_value.data = self._value
367
+ return ws
368
+
369
+ @property
370
+ def value(self) -> str | None:
371
+ """The value of the widget. (str)"""
372
+ if self._value:
373
+ return self._value
374
+ else:
375
+ state = self.root.session_state
376
+ assert state
377
+ return state[TESTING_KEY][self.id] # type: ignore
378
+
379
+
380
+ @dataclass(repr=False)
381
+ class Checkbox(Widget):
382
+ """A representation of ``st.checkbox``."""
383
+
384
+ _value: bool | None
385
+
386
+ proto: CheckboxProto = field(repr=False)
387
+ label: str
388
+ help: str
389
+ form_id: str
390
+
391
+ def __init__(self, proto: CheckboxProto, root: ElementTree):
392
+ super().__init__(proto, root)
393
+ self.type = "checkbox"
394
+
395
+ @property
396
+ def _widget_state(self) -> WidgetState:
397
+ ws = WidgetState()
398
+ ws.id = self.id
399
+ ws.bool_value = self.value
400
+ return ws
401
+
402
+ @property
403
+ def value(self) -> bool:
404
+ """The value of the widget. (bool)"""
405
+ if self._value is not None:
406
+ return self._value
407
+ else:
408
+ state = self.root.session_state
409
+ assert state
410
+ return cast(bool, state[self.id])
411
+
412
+ def set_value(self, v: bool) -> Checkbox:
413
+ """Set the value of the widget."""
414
+ self._value = v
415
+ return self
416
+
417
+ def check(self) -> Checkbox:
418
+ """Set the value of the widget to True."""
419
+ return self.set_value(True)
420
+
421
+ def uncheck(self) -> Checkbox:
422
+ """Set the value of the widget to False."""
423
+ return self.set_value(False)
424
+
425
+
426
+ @dataclass(repr=False)
427
+ class Code(Element):
428
+ """A representation of ``st.code``."""
429
+
430
+ proto: CodeProto = field(repr=False)
431
+
432
+ language: str
433
+ show_line_numbers: bool
434
+ key: None
435
+
436
+ def __init__(self, proto: CodeProto, root: ElementTree):
437
+ self.proto = proto
438
+ self.key = None
439
+ self.root = root
440
+ self.type = "code"
441
+
442
+ @property
443
+ def value(self) -> str:
444
+ """The value of the element. (str)"""
445
+ return self.proto.code_text
446
+
447
+
448
+ @dataclass(repr=False)
449
+ class ColorPicker(Widget):
450
+ """A representation of ``st.color_picker``."""
451
+
452
+ _value: str | None
453
+ label: str
454
+ help: str
455
+ form_id: str
456
+
457
+ proto: ColorPickerProto = field(repr=False)
458
+
459
+ def __init__(self, proto: ColorPickerProto, root: ElementTree):
460
+ super().__init__(proto, root)
461
+ self.type = "color_picker"
462
+
463
+ @property
464
+ def value(self) -> str:
465
+ """The currently selected value as a hex string. (str)"""
466
+ if self._value is not None:
467
+ return self._value
468
+ else:
469
+ state = self.root.session_state
470
+ assert state
471
+ return cast(str, state[self.id])
472
+
473
+ @property
474
+ def _widget_state(self) -> WidgetState:
475
+ """Protobuf message representing the state of the widget, including
476
+ any interactions that have happened.
477
+ Should be the same as the frontend would produce for those interactions.
478
+ """
479
+ ws = WidgetState()
480
+ ws.id = self.id
481
+ ws.string_value = self.value
482
+ return ws
483
+
484
+ def set_value(self, v: str) -> ColorPicker:
485
+ """Set the value of the widget as a hex string."""
486
+ self._value = v
487
+ return self
488
+
489
+ def pick(self, v: str) -> ColorPicker:
490
+ """Set the value of the widget as a hex string. May omit the "#" prefix."""
491
+ if not v.startswith("#"):
492
+ v = f"#{v}"
493
+ return self.set_value(v)
494
+
495
+
496
+ @dataclass(repr=False)
497
+ class Dataframe(Element):
498
+ proto: ArrowProto = field(repr=False)
499
+
500
+ def __init__(self, proto: ArrowProto, root: ElementTree):
501
+ self.key = None
502
+ self.proto = proto
503
+ self.root = root
504
+ self.type = "arrow_data_frame"
505
+
506
+ @property
507
+ def value(self) -> PandasDataframe:
508
+ return dataframe_util.convert_arrow_bytes_to_pandas_df(self.proto.data)
509
+
510
+
511
+ SingleDateValue: TypeAlias = Union[date, datetime]
512
+ DateValue: TypeAlias = Union[SingleDateValue, Sequence[SingleDateValue], None]
513
+
514
+
515
+ @dataclass(repr=False)
516
+ class DateInput(Widget):
517
+ """A representation of ``st.date_input``."""
518
+
519
+ _value: DateValue | None | InitialValue
520
+ proto: DateInputProto = field(repr=False)
521
+ label: str
522
+ min: date
523
+ max: date
524
+ is_range: bool
525
+ help: str
526
+ form_id: str
527
+
528
+ def __init__(self, proto: DateInputProto, root: ElementTree):
529
+ super().__init__(proto, root)
530
+ self._value = InitialValue()
531
+ self.type = "date_input"
532
+ self.min = datetime.strptime(proto.min, "%Y/%m/%d").date()
533
+ self.max = datetime.strptime(proto.max, "%Y/%m/%d").date()
534
+
535
+ def set_value(self, v: DateValue) -> DateInput:
536
+ """Set the value of the widget."""
537
+ self._value = v
538
+ return self
539
+
540
+ @property
541
+ def _widget_state(self) -> WidgetState:
542
+ ws = WidgetState()
543
+ ws.id = self.id
544
+
545
+ serde = DateInputSerde(None) # type: ignore
546
+ ws.string_array_value.data[:] = serde.serialize(self.value)
547
+ return ws
548
+
549
+ @property
550
+ def value(self) -> DateWidgetReturn:
551
+ """The value of the widget. (date or Tuple of date)"""
552
+ if not isinstance(self._value, InitialValue):
553
+ parsed, _ = _parse_date_value(self._value)
554
+ return tuple(parsed) if parsed is not None else None # type: ignore
555
+ else:
556
+ state = self.root.session_state
557
+ assert state
558
+ return state[self.id] # type: ignore
559
+
560
+
561
+ @dataclass(repr=False)
562
+ class Exception(Element):
563
+ message: str
564
+ is_markdown: bool
565
+ stack_trace: list[str]
566
+ is_warning: bool
567
+
568
+ def __init__(self, proto: ExceptionProto, root: ElementTree):
569
+ self.key = None
570
+ self.root = root
571
+ self.proto = proto
572
+ self.type = "exception"
573
+
574
+ self.is_markdown = proto.message_is_markdown
575
+ self.stack_trace = list(proto.stack_trace)
576
+
577
+ @property
578
+ def value(self) -> str:
579
+ return self.message
580
+
581
+
582
+ @dataclass(repr=False)
583
+ class HeadingBase(Element, ABC):
584
+ proto: HeadingProto = field(repr=False)
585
+
586
+ tag: str
587
+ anchor: str | None
588
+ hide_anchor: bool
589
+ key: None
590
+
591
+ def __init__(self, proto: HeadingProto, root: ElementTree, type_: str):
592
+ self.proto = proto
593
+ self.key = None
594
+ self.root = root
595
+ self.type = type_
596
+
597
+ @property
598
+ def value(self) -> str:
599
+ return self.proto.body
600
+
601
+
602
+ @dataclass(repr=False)
603
+ class Header(HeadingBase):
604
+ def __init__(self, proto: HeadingProto, root: ElementTree):
605
+ super().__init__(proto, root, "header")
606
+
607
+
608
+ @dataclass(repr=False)
609
+ class Subheader(HeadingBase):
610
+ def __init__(self, proto: HeadingProto, root: ElementTree):
611
+ super().__init__(proto, root, "subheader")
612
+
613
+
614
+ @dataclass(repr=False)
615
+ class Title(HeadingBase):
616
+ def __init__(self, proto: HeadingProto, root: ElementTree):
617
+ super().__init__(proto, root, "title")
618
+
619
+
620
+ @dataclass(repr=False)
621
+ class Json(Element):
622
+ proto: JsonProto = field(repr=False)
623
+
624
+ expanded: bool
625
+
626
+ def __init__(self, proto: JsonProto, root: ElementTree):
627
+ self.proto = proto
628
+ self.key = None
629
+ self.root = root
630
+ self.type = "json"
631
+
632
+ @property
633
+ def value(self) -> str:
634
+ return self.proto.body
635
+
636
+
637
+ @dataclass(repr=False)
638
+ class Markdown(Element):
639
+ proto: MarkdownProto = field(repr=False)
640
+
641
+ is_caption: bool
642
+ allow_html: bool
643
+ key: None
644
+
645
+ def __init__(self, proto: MarkdownProto, root: ElementTree):
646
+ self.proto = proto
647
+ self.key = None
648
+ self.root = root
649
+ self.type = "markdown"
650
+
651
+ @property
652
+ def value(self) -> str:
653
+ return self.proto.body
654
+
655
+
656
+ @dataclass(repr=False)
657
+ class Caption(Markdown):
658
+ def __init__(self, proto: MarkdownProto, root: ElementTree):
659
+ super().__init__(proto, root)
660
+ self.type = "caption"
661
+
662
+
663
+ @dataclass(repr=False)
664
+ class Divider(Markdown):
665
+ def __init__(self, proto: MarkdownProto, root: ElementTree):
666
+ super().__init__(proto, root)
667
+ self.type = "divider"
668
+
669
+
670
+ @dataclass(repr=False)
671
+ class Latex(Markdown):
672
+ def __init__(self, proto: MarkdownProto, root: ElementTree):
673
+ super().__init__(proto, root)
674
+ self.type = "latex"
675
+
676
+
677
+ @dataclass(repr=False)
678
+ class Metric(Element):
679
+ proto: MetricProto
680
+ label: str
681
+ delta: str
682
+ color: str
683
+ help: str
684
+
685
+ def __init__(self, proto: MetricProto, root: ElementTree):
686
+ self.proto = proto
687
+ self.key = None
688
+ self.root = root
689
+ self.type = "metric"
690
+
691
+ @property
692
+ def value(self) -> str:
693
+ return self.proto.body
694
+
695
+
696
+ @dataclass(repr=False)
697
+ class ButtonGroup(Widget, Generic[T]):
698
+ """A representation of button_group that is used by ``st.feedback``."""
699
+
700
+ _value: list[T] | None
701
+
702
+ proto: ButtonGroupProto = field(repr=False)
703
+ options: list[ButtonGroupProto.Option]
704
+ form_id: str
705
+
706
+ def __init__(self, proto: ButtonGroupProto, root: ElementTree):
707
+ super().__init__(proto, root)
708
+ self.type = "button_group"
709
+ self.options = list(proto.options)
710
+
711
+ @property
712
+ def _widget_state(self) -> WidgetState:
713
+ """Protobuf message representing the state of the widget, including
714
+ any interactions that have happened.
715
+ Should be the same as the frontend would produce for those interactions.
716
+ """
717
+ ws = WidgetState()
718
+ ws.id = self.id
719
+ ws.int_array_value.data[:] = self.indices
720
+ return ws
721
+
722
+ @property
723
+ def value(self) -> list[T]:
724
+ """The currently selected values from the options. (list)"""
725
+ if self._value is not None:
726
+ return self._value
727
+ else:
728
+ state = self.root.session_state
729
+ assert state
730
+ return cast(list[T], state[self.id])
731
+
732
+ @property
733
+ def indices(self) -> Sequence[int]:
734
+ """The indices of the currently selected values from the options. (list)"""
735
+ return [self.options.index(self.format_func(v)) for v in self.value]
736
+
737
+ @property
738
+ def format_func(self) -> Callable[[Any], Any]:
739
+ """The widget's formatting function for displaying options. (callable)"""
740
+ ss = self.root.session_state
741
+ return cast(Callable[[Any], Any], ss[TESTING_KEY][self.id])
742
+
743
+ def set_value(self, v: list[T]) -> ButtonGroup[T]:
744
+ """Set the value of the multiselect widget. (list)"""
745
+
746
+ self._value = v
747
+ return self
748
+
749
+ def select(self, v: T) -> ButtonGroup[T]:
750
+ """
751
+ Add a selection to the widget. Do nothing if the value is already selected.\
752
+ If testing a multiselect widget with repeated options, use ``set_value``\
753
+ instead.
754
+ """
755
+ current = self.value
756
+ if v in current:
757
+ return self
758
+ else:
759
+ new = current.copy()
760
+ new.append(v)
761
+ self.set_value(new)
762
+ return self
763
+
764
+ def unselect(self, v: T) -> ButtonGroup[T]:
765
+ """
766
+ Remove a selection from the widget. Do nothing if the value is not\
767
+ already selected. If a value is selected multiple times, the first\
768
+ instance is removed.
769
+ """
770
+ current = self.value
771
+ if v not in current:
772
+ return self
773
+ else:
774
+ new = current.copy()
775
+ while v in new:
776
+ new.remove(v)
777
+ self.set_value(new)
778
+ return self
779
+
780
+
781
+ @dataclass(repr=False)
782
+ class Multiselect(Widget, Generic[T]):
783
+ """A representation of ``st.multiselect``."""
784
+
785
+ _value: list[T] | None
786
+
787
+ proto: MultiSelectProto = field(repr=False)
788
+ label: str
789
+ options: list[str]
790
+ max_selections: int
791
+ help: str
792
+ form_id: str
793
+
794
+ def __init__(self, proto: MultiSelectProto, root: ElementTree):
795
+ super().__init__(proto, root)
796
+ self.type = "multiselect"
797
+ self.options = list(proto.options)
798
+
799
+ @property
800
+ def _widget_state(self) -> WidgetState:
801
+ """Protobuf message representing the state of the widget, including
802
+ any interactions that have happened.
803
+ Should be the same as the frontend would produce for those interactions.
804
+ """
805
+ ws = WidgetState()
806
+ ws.id = self.id
807
+ ws.int_array_value.data[:] = self.indices
808
+ return ws
809
+
810
+ @property
811
+ def value(self) -> list[T]:
812
+ """The currently selected values from the options. (list)"""
813
+ if self._value is not None:
814
+ return self._value
815
+ else:
816
+ state = self.root.session_state
817
+ assert state
818
+ return cast(list[T], state[self.id])
819
+
820
+ @property
821
+ def indices(self) -> Sequence[int]:
822
+ """The indices of the currently selected values from the options. (list)"""
823
+ return [self.options.index(self.format_func(v)) for v in self.value]
824
+
825
+ @property
826
+ def format_func(self) -> Callable[[Any], Any]:
827
+ """The widget's formatting function for displaying options. (callable)"""
828
+ ss = self.root.session_state
829
+ return cast(Callable[[Any], Any], ss[TESTING_KEY][self.id])
830
+
831
+ def set_value(self, v: list[T]) -> Multiselect[T]:
832
+ """Set the value of the multiselect widget. (list)"""
833
+
834
+ self._value = v
835
+ return self
836
+
837
+ def select(self, v: T) -> Multiselect[T]:
838
+ """
839
+ Add a selection to the widget. Do nothing if the value is already selected.\
840
+ If testing a multiselect widget with repeated options, use ``set_value``\
841
+ instead.
842
+ """
843
+ current = self.value
844
+ if v in current:
845
+ return self
846
+ else:
847
+ new = current.copy()
848
+ new.append(v)
849
+ self.set_value(new)
850
+ return self
851
+
852
+ def unselect(self, v: T) -> Multiselect[T]:
853
+ """
854
+ Remove a selection from the widget. Do nothing if the value is not\
855
+ already selected. If a value is selected multiple times, the first\
856
+ instance is removed.
857
+ """
858
+ current = self.value
859
+ if v not in current:
860
+ return self
861
+ else:
862
+ new = current.copy()
863
+ while v in new:
864
+ new.remove(v)
865
+ self.set_value(new)
866
+ return self
867
+
868
+
869
+ Number = Union[int, float]
870
+
871
+
872
+ @dataclass(repr=False)
873
+ class NumberInput(Widget):
874
+ """A representation of ``st.number_input``."""
875
+
876
+ _value: Number | None | InitialValue
877
+ proto: NumberInputProto = field(repr=False)
878
+ label: str
879
+ min: Number | None
880
+ max: Number | None
881
+ step: Number
882
+ help: str
883
+ form_id: str
884
+
885
+ def __init__(self, proto: NumberInputProto, root: ElementTree):
886
+ super().__init__(proto, root)
887
+ self._value = InitialValue()
888
+ self.type = "number_input"
889
+ self.min = proto.min if proto.has_min else None
890
+ self.max = proto.max if proto.has_max else None
891
+
892
+ def set_value(self, v: Number | None) -> NumberInput:
893
+ """Set the value of the ``st.number_input`` widget."""
894
+ self._value = v
895
+ return self
896
+
897
+ @property
898
+ def _widget_state(self) -> WidgetState:
899
+ ws = WidgetState()
900
+ ws.id = self.id
901
+ if self.value is not None:
902
+ ws.double_value = self.value
903
+ return ws
904
+
905
+ @property
906
+ def value(self) -> Number | None:
907
+ """Get the current value of the ``st.number_input`` widget."""
908
+ if not isinstance(self._value, InitialValue):
909
+ return self._value
910
+ else:
911
+ state = self.root.session_state
912
+ assert state
913
+
914
+ # Awkward to do this with `cast`
915
+ return state[self.id] # type: ignore
916
+
917
+ def increment(self) -> NumberInput:
918
+ """Increment the ``st.number_input`` widget as if the user clicked "+"."""
919
+ if self.value is None:
920
+ return self
921
+
922
+ v = min(self.value + self.step, self.max or float("inf"))
923
+ return self.set_value(v)
924
+
925
+ def decrement(self) -> NumberInput:
926
+ """Decrement the ``st.number_input`` widget as if the user clicked "-"."""
927
+ if self.value is None:
928
+ return self
929
+
930
+ v = max(self.value - self.step, self.min or float("-inf"))
931
+ return self.set_value(v)
932
+
933
+
934
+ @dataclass(repr=False)
935
+ class Radio(Widget, Generic[T]):
936
+ """A representation of ``st.radio``."""
937
+
938
+ _value: T | None | InitialValue
939
+
940
+ proto: RadioProto = field(repr=False)
941
+ label: str
942
+ options: list[str]
943
+ horizontal: bool
944
+ help: str
945
+ form_id: str
946
+
947
+ def __init__(self, proto: RadioProto, root: ElementTree):
948
+ super().__init__(proto, root)
949
+ self._value = InitialValue()
950
+ self.type = "radio"
951
+ self.options = list(proto.options)
952
+
953
+ @property
954
+ def index(self) -> int | None:
955
+ """The index of the current selection. (int)"""
956
+ if self.value is None:
957
+ return None
958
+ return self.options.index(self.format_func(self.value))
959
+
960
+ @property
961
+ def value(self) -> T | None:
962
+ """The currently selected value from the options. (Any)"""
963
+ if not isinstance(self._value, InitialValue):
964
+ return self._value
965
+ else:
966
+ state = self.root.session_state
967
+ assert state
968
+ return cast(T, state[self.id])
969
+
970
+ @property
971
+ def format_func(self) -> Callable[[Any], Any]:
972
+ """The widget's formatting function for displaying options. (callable)"""
973
+ ss = self.root.session_state
974
+ return cast(Callable[[Any], Any], ss[TESTING_KEY][self.id])
975
+
976
+ def set_value(self, v: T | None) -> Radio[T]:
977
+ """Set the selection by value."""
978
+ self._value = v
979
+ return self
980
+
981
+ @property
982
+ def _widget_state(self) -> WidgetState:
983
+ """Protobuf message representing the state of the widget, including
984
+ any interactions that have happened.
985
+ Should be the same as the frontend would produce for those interactions.
986
+ """
987
+ ws = WidgetState()
988
+ ws.id = self.id
989
+ if self.index is not None:
990
+ ws.int_value = self.index
991
+ return ws
992
+
993
+
994
+ @dataclass(repr=False)
995
+ class Selectbox(Widget, Generic[T]):
996
+ """A representation of ``st.selectbox``."""
997
+
998
+ _value: T | None | InitialValue
999
+
1000
+ proto: SelectboxProto = field(repr=False)
1001
+ label: str
1002
+ options: list[str]
1003
+ help: str
1004
+ form_id: str
1005
+
1006
+ def __init__(self, proto: SelectboxProto, root: ElementTree):
1007
+ super().__init__(proto, root)
1008
+ self._value = InitialValue()
1009
+ self.type = "selectbox"
1010
+ self.options = list(proto.options)
1011
+
1012
+ @property
1013
+ def index(self) -> int | None:
1014
+ """The index of the current selection. (int)"""
1015
+ if self.value is None:
1016
+ return None
1017
+
1018
+ if len(self.options) == 0:
1019
+ return 0
1020
+ return self.options.index(self.format_func(self.value))
1021
+
1022
+ @property
1023
+ def value(self) -> T | None:
1024
+ """The currently selected value from the options. (Any)"""
1025
+ if not isinstance(self._value, InitialValue):
1026
+ return self._value
1027
+ else:
1028
+ state = self.root.session_state
1029
+ assert state
1030
+ return cast(T, state[self.id])
1031
+
1032
+ @property
1033
+ def format_func(self) -> Callable[[Any], Any]:
1034
+ """The widget's formatting function for displaying options. (callable)"""
1035
+ ss = self.root.session_state
1036
+ return cast(Callable[[Any], Any], ss[TESTING_KEY][self.id])
1037
+
1038
+ def set_value(self, v: T | None) -> Selectbox[T]:
1039
+ """Set the selection by value."""
1040
+ self._value = v
1041
+ return self
1042
+
1043
+ def select(self, v: T | None) -> Selectbox[T]:
1044
+ """Set the selection by value."""
1045
+ return self.set_value(v)
1046
+
1047
+ def select_index(self, index: int | None) -> Selectbox[T]:
1048
+ """Set the selection by index."""
1049
+ if index is None:
1050
+ return self.set_value(None)
1051
+ return self.set_value(cast(T, self.options[index]))
1052
+
1053
+ @property
1054
+ def _widget_state(self) -> WidgetState:
1055
+ """Protobuf message representing the state of the widget, including
1056
+ any interactions that have happened.
1057
+ Should be the same as the frontend would produce for those interactions.
1058
+ """
1059
+ ws = WidgetState()
1060
+ ws.id = self.id
1061
+ if self.index is not None:
1062
+ ws.int_value = self.index
1063
+ return ws
1064
+
1065
+
1066
+ @dataclass(repr=False)
1067
+ class SelectSlider(Widget, Generic[T]):
1068
+ """A representation of ``st.select_slider``."""
1069
+
1070
+ _value: T | Sequence[T] | None
1071
+
1072
+ proto: SliderProto = field(repr=False)
1073
+ label: str
1074
+ data_type: SliderProto.DataType.ValueType
1075
+ options: list[str]
1076
+ help: str
1077
+ form_id: str
1078
+
1079
+ def __init__(self, proto: SliderProto, root: ElementTree):
1080
+ super().__init__(proto, root)
1081
+ self.type = "select_slider"
1082
+ self.options = list(proto.options)
1083
+
1084
+ def set_value(self, v: T | Sequence[T]) -> SelectSlider[T]:
1085
+ """Set the (single) selection by value."""
1086
+ self._value = v
1087
+ return self
1088
+
1089
+ @property
1090
+ def _widget_state(self) -> WidgetState:
1091
+ serde = SelectSliderSerde(self.options, [], False)
1092
+ try:
1093
+ v = serde.serialize(self.format_func(self.value))
1094
+ except (ValueError, TypeError):
1095
+ try:
1096
+ v = serde.serialize([self.format_func(val) for val in self.value]) # type: ignore
1097
+ except: # noqa: E722
1098
+ raise ValueError(f"Could not find index for {self.value}")
1099
+
1100
+ ws = WidgetState()
1101
+ ws.id = self.id
1102
+ ws.double_array_value.data[:] = v
1103
+ return ws
1104
+
1105
+ @property
1106
+ def value(self) -> T | Sequence[T]:
1107
+ """The currently selected value or range. (Any or Sequence of Any)"""
1108
+ if self._value is not None:
1109
+ return self._value
1110
+ else:
1111
+ state = self.root.session_state
1112
+ assert state
1113
+ # Awkward to do this with `cast`
1114
+ return state[self.id] # type: ignore
1115
+
1116
+ @property
1117
+ def format_func(self) -> Callable[[Any], Any]:
1118
+ """The widget's formatting function for displaying options. (callable)"""
1119
+ ss = self.root.session_state
1120
+ return cast(Callable[[Any], Any], ss[TESTING_KEY][self.id])
1121
+
1122
+ def set_range(self, lower: T, upper: T) -> SelectSlider[T]:
1123
+ """Set the ranged selection by values."""
1124
+ return self.set_value([lower, upper])
1125
+
1126
+
1127
+ @dataclass(repr=False)
1128
+ class Slider(Widget, Generic[SliderValueT]):
1129
+ """A representation of ``st.slider``."""
1130
+
1131
+ _value: SliderValueT | Sequence[SliderValueT] | None
1132
+
1133
+ proto: SliderProto = field(repr=False)
1134
+ label: str
1135
+ data_type: SliderProto.DataType.ValueType
1136
+ min: SliderValueT
1137
+ max: SliderValueT
1138
+ step: SliderStep
1139
+ help: str
1140
+ form_id: str
1141
+
1142
+ def __init__(self, proto: SliderProto, root: ElementTree):
1143
+ super().__init__(proto, root)
1144
+ self.type = "slider"
1145
+
1146
+ def set_value(
1147
+ self, v: SliderValueT | Sequence[SliderValueT]
1148
+ ) -> Slider[SliderValueT]:
1149
+ """Set the (single) value of the slider."""
1150
+ self._value = v
1151
+ return self
1152
+
1153
+ @property
1154
+ def _widget_state(self) -> WidgetState:
1155
+ data_type = self.proto.data_type
1156
+ serde = SliderSerde([], data_type, True, None)
1157
+ v = serde.serialize(self.value)
1158
+
1159
+ ws = WidgetState()
1160
+ ws.id = self.id
1161
+ ws.double_array_value.data[:] = v
1162
+ return ws
1163
+
1164
+ @property
1165
+ def value(self) -> SliderValueT | Sequence[SliderValueT]:
1166
+ """The currently selected value or range. (Any or Sequence of Any)"""
1167
+ if self._value is not None:
1168
+ return self._value
1169
+ else:
1170
+ state = self.root.session_state
1171
+ assert state
1172
+ # Awkward to do this with `cast`
1173
+ return state[self.id] # type: ignore
1174
+
1175
+ def set_range(
1176
+ self, lower: SliderValueT, upper: SliderValueT
1177
+ ) -> Slider[SliderValueT]:
1178
+ """Set the ranged value of the slider."""
1179
+ return self.set_value([lower, upper])
1180
+
1181
+
1182
+ @dataclass(repr=False)
1183
+ class Table(Element):
1184
+ proto: ArrowProto = field(repr=False)
1185
+
1186
+ def __init__(self, proto: ArrowProto, root: ElementTree):
1187
+ self.key = None
1188
+ self.proto = proto
1189
+ self.root = root
1190
+ self.type = "arrow_table"
1191
+
1192
+ @property
1193
+ def value(self) -> PandasDataframe:
1194
+ return dataframe_util.convert_arrow_bytes_to_pandas_df(self.proto.data)
1195
+
1196
+
1197
+ @dataclass(repr=False)
1198
+ class Text(Element):
1199
+ proto: TextProto = field(repr=False)
1200
+
1201
+ key: None = None
1202
+
1203
+ def __init__(self, proto: TextProto, root: ElementTree):
1204
+ self.proto = proto
1205
+ self.root = root
1206
+ self.type = "text"
1207
+
1208
+ @property
1209
+ def value(self) -> str:
1210
+ """The value of the element. (str)"""
1211
+ return self.proto.body
1212
+
1213
+
1214
+ @dataclass(repr=False)
1215
+ class TextArea(Widget):
1216
+ """A representation of ``st.text_area``."""
1217
+
1218
+ _value: str | None | InitialValue
1219
+
1220
+ proto: TextAreaProto = field(repr=False)
1221
+ label: str
1222
+ max_chars: int
1223
+ placeholder: str
1224
+ help: str
1225
+ form_id: str
1226
+
1227
+ def __init__(self, proto: TextAreaProto, root: ElementTree):
1228
+ super().__init__(proto, root)
1229
+ self._value = InitialValue()
1230
+ self.type = "text_area"
1231
+
1232
+ def set_value(self, v: str | None) -> TextArea:
1233
+ """Set the value of the widget."""
1234
+ self._value = v
1235
+ return self
1236
+
1237
+ @property
1238
+ def _widget_state(self) -> WidgetState:
1239
+ ws = WidgetState()
1240
+ ws.id = self.id
1241
+ if self.value is not None:
1242
+ ws.string_value = self.value
1243
+ return ws
1244
+
1245
+ @property
1246
+ def value(self) -> str | None:
1247
+ """The current value of the widget. (str)"""
1248
+ if not isinstance(self._value, InitialValue):
1249
+ return self._value
1250
+ else:
1251
+ state = self.root.session_state
1252
+ assert state
1253
+ # Awkward to do this with `cast`
1254
+ return state[self.id] # type: ignore
1255
+
1256
+ def input(self, v: str) -> TextArea:
1257
+ """
1258
+ Set the value of the widget only if the value does not exceed the\
1259
+ maximum allowed characters.
1260
+ """
1261
+ # TODO: should input be setting or appending?
1262
+ if self.max_chars and len(v) > self.max_chars:
1263
+ return self
1264
+ return self.set_value(v)
1265
+
1266
+
1267
+ @dataclass(repr=False)
1268
+ class TextInput(Widget):
1269
+ """A representation of ``st.text_input``."""
1270
+
1271
+ _value: str | None | InitialValue
1272
+ proto: TextInputProto = field(repr=False)
1273
+ label: str
1274
+ max_chars: int
1275
+ autocomplete: str
1276
+ placeholder: str
1277
+ help: str
1278
+ form_id: str
1279
+
1280
+ def __init__(self, proto: TextInputProto, root: ElementTree):
1281
+ super().__init__(proto, root)
1282
+ self._value = InitialValue()
1283
+ self.type = "text_input"
1284
+
1285
+ def set_value(self, v: str | None) -> TextInput:
1286
+ """Set the value of the widget."""
1287
+ self._value = v
1288
+ return self
1289
+
1290
+ @property
1291
+ def _widget_state(self) -> WidgetState:
1292
+ ws = WidgetState()
1293
+ ws.id = self.id
1294
+ if self.value is not None:
1295
+ ws.string_value = self.value
1296
+ return ws
1297
+
1298
+ @property
1299
+ def value(self) -> str | None:
1300
+ """The current value of the widget. (str)"""
1301
+ if not isinstance(self._value, InitialValue):
1302
+ return self._value
1303
+ else:
1304
+ state = self.root.session_state
1305
+ assert state
1306
+ # Awkward to do this with `cast`
1307
+ return state[self.id] # type: ignore
1308
+
1309
+ def input(self, v: str) -> TextInput:
1310
+ """
1311
+ Set the value of the widget only if the value does not exceed the\
1312
+ maximum allowed characters.
1313
+ """
1314
+ # TODO: should input be setting or appending?
1315
+ if self.max_chars and len(v) > self.max_chars:
1316
+ return self
1317
+ return self.set_value(v)
1318
+
1319
+
1320
+ TimeValue: TypeAlias = Union[time, datetime]
1321
+
1322
+
1323
+ @dataclass(repr=False)
1324
+ class TimeInput(Widget):
1325
+ """A representation of ``st.time_input``."""
1326
+
1327
+ _value: TimeValue | None | InitialValue
1328
+ proto: TimeInputProto = field(repr=False)
1329
+ label: str
1330
+ step: int
1331
+ help: str
1332
+ form_id: str
1333
+
1334
+ def __init__(self, proto: TimeInputProto, root: ElementTree):
1335
+ super().__init__(proto, root)
1336
+ self._value = InitialValue()
1337
+ self.type = "time_input"
1338
+
1339
+ def set_value(self, v: TimeValue | None) -> TimeInput:
1340
+ """Set the value of the widget."""
1341
+ self._value = v
1342
+ return self
1343
+
1344
+ @property
1345
+ def _widget_state(self) -> WidgetState:
1346
+ ws = WidgetState()
1347
+ ws.id = self.id
1348
+
1349
+ serde = TimeInputSerde(None)
1350
+ serialized_value = serde.serialize(self.value)
1351
+ if serialized_value is not None:
1352
+ ws.string_value = serialized_value
1353
+ return ws
1354
+
1355
+ @property
1356
+ def value(self) -> time | None:
1357
+ """The current value of the widget. (time)"""
1358
+ if not isinstance(self._value, InitialValue):
1359
+ v = self._value
1360
+ v = v.time() if isinstance(v, datetime) else v
1361
+ return v
1362
+ else:
1363
+ state = self.root.session_state
1364
+ assert state
1365
+ return state[self.id] # type: ignore
1366
+
1367
+ def increment(self) -> TimeInput:
1368
+ """Select the next available time."""
1369
+ if self.value is None:
1370
+ return self
1371
+ dt = datetime.combine(date.today(), self.value) + timedelta(seconds=self.step)
1372
+ return self.set_value(dt.time())
1373
+
1374
+ def decrement(self) -> TimeInput:
1375
+ """Select the previous available time."""
1376
+ if self.value is None:
1377
+ return self
1378
+ dt = datetime.combine(date.today(), self.value) - timedelta(seconds=self.step)
1379
+ return self.set_value(dt.time())
1380
+
1381
+
1382
+ @dataclass(repr=False)
1383
+ class Toast(Element):
1384
+ proto: ToastProto = field(repr=False)
1385
+ icon: str
1386
+
1387
+ def __init__(self, proto: ToastProto, root: ElementTree):
1388
+ self.proto = proto
1389
+ self.key = None
1390
+ self.root = root
1391
+ self.type = "toast"
1392
+
1393
+ @property
1394
+ def value(self) -> str:
1395
+ return self.proto.body
1396
+
1397
+
1398
+ @dataclass(repr=False)
1399
+ class Toggle(Widget):
1400
+ """A representation of ``st.toggle``."""
1401
+
1402
+ _value: bool | None
1403
+
1404
+ proto: CheckboxProto = field(repr=False)
1405
+ label: str
1406
+ help: str
1407
+ form_id: str
1408
+
1409
+ def __init__(self, proto: CheckboxProto, root: ElementTree):
1410
+ super().__init__(proto, root)
1411
+ self._value = None
1412
+ self.type = "toggle"
1413
+
1414
+ @property
1415
+ def _widget_state(self) -> WidgetState:
1416
+ ws = WidgetState()
1417
+ ws.id = self.id
1418
+ ws.bool_value = self.value
1419
+ return ws
1420
+
1421
+ @property
1422
+ def value(self) -> bool:
1423
+ """The current value of the widget. (bool)"""
1424
+ if self._value is not None:
1425
+ return self._value
1426
+ else:
1427
+ state = self.root.session_state
1428
+ assert state
1429
+ return cast(bool, state[self.id])
1430
+
1431
+ def set_value(self, v: bool) -> Toggle:
1432
+ """Set the value of the widget."""
1433
+ self._value = v
1434
+ return self
1435
+
1436
+
1437
+ @dataclass(repr=False)
1438
+ class Block:
1439
+ """A container of other elements.
1440
+
1441
+ Elements within a Block can be inspected and interacted with. This follows
1442
+ the same syntax as inspecting and interacting within an ``AppTest`` object.
1443
+
1444
+ For all container classes, parameters of the original element can be
1445
+ obtained as properties. For example, ``ChatMessage.avatar`` and
1446
+ ``Tab.label``.
1447
+ """
1448
+
1449
+ type: str
1450
+ children: dict[int, Node]
1451
+ proto: Any = field(repr=False)
1452
+ root: ElementTree = field(repr=False)
1453
+
1454
+ def __init__(
1455
+ self,
1456
+ proto: BlockProto | None,
1457
+ root: ElementTree,
1458
+ ):
1459
+ self.children = {}
1460
+ self.proto = proto
1461
+ if proto:
1462
+ ty = proto.WhichOneof("type")
1463
+ if ty is not None:
1464
+ self.type = ty
1465
+ else:
1466
+ # `st.container` has no sub-message
1467
+ self.type = "container"
1468
+ else:
1469
+ self.type = "unknown"
1470
+ self.root = root
1471
+
1472
+ def __len__(self) -> int:
1473
+ return len(self.children)
1474
+
1475
+ def __iter__(self):
1476
+ yield self
1477
+ for child_idx in self.children:
1478
+ yield from self.children[child_idx]
1479
+
1480
+ def __getitem__(self, k: int) -> Node:
1481
+ return self.children[k]
1482
+
1483
+ @property
1484
+ def key(self) -> str | None:
1485
+ return None
1486
+
1487
+ # We could implement these using __getattr__ but that would have
1488
+ # much worse type information.
1489
+ @property
1490
+ def button(self) -> WidgetList[Button]:
1491
+ return WidgetList(self.get("button")) # type: ignore
1492
+
1493
+ @property
1494
+ def button_group(self) -> WidgetList[ButtonGroup[Any]]:
1495
+ return WidgetList(self.get("button_group")) # type: ignore
1496
+
1497
+ @property
1498
+ def caption(self) -> ElementList[Caption]:
1499
+ return ElementList(self.get("caption")) # type: ignore
1500
+
1501
+ @property
1502
+ def chat_input(self) -> WidgetList[ChatInput]:
1503
+ return WidgetList(self.get("chat_input")) # type: ignore
1504
+
1505
+ @property
1506
+ def chat_message(self) -> Sequence[ChatMessage]:
1507
+ return self.get("chat_message") # type: ignore
1508
+
1509
+ @property
1510
+ def checkbox(self) -> WidgetList[Checkbox]:
1511
+ return WidgetList(self.get("checkbox")) # type: ignore
1512
+
1513
+ @property
1514
+ def code(self) -> ElementList[Code]:
1515
+ return ElementList(self.get("code")) # type: ignore
1516
+
1517
+ @property
1518
+ def color_picker(self) -> WidgetList[ColorPicker]:
1519
+ return WidgetList(self.get("color_picker")) # type: ignore
1520
+
1521
+ @property
1522
+ def columns(self) -> Sequence[Column]:
1523
+ return self.get("column") # type: ignore
1524
+
1525
+ @property
1526
+ def dataframe(self) -> ElementList[Dataframe]:
1527
+ return ElementList(self.get("arrow_data_frame")) # type: ignore
1528
+
1529
+ @property
1530
+ def date_input(self) -> WidgetList[DateInput]:
1531
+ return WidgetList(self.get("date_input")) # type: ignore
1532
+
1533
+ @property
1534
+ def divider(self) -> ElementList[Divider]:
1535
+ return ElementList(self.get("divider")) # type: ignore
1536
+
1537
+ @property
1538
+ def error(self) -> ElementList[Error]:
1539
+ return ElementList(self.get("error")) # type: ignore
1540
+
1541
+ @property
1542
+ def exception(self) -> ElementList[Exception]:
1543
+ return ElementList(self.get("exception")) # type: ignore
1544
+
1545
+ @property
1546
+ def expander(self) -> Sequence[Expander]:
1547
+ return self.get("expander") # type: ignore
1548
+
1549
+ @property
1550
+ def header(self) -> ElementList[Header]:
1551
+ return ElementList(self.get("header")) # type: ignore
1552
+
1553
+ @property
1554
+ def info(self) -> ElementList[Info]:
1555
+ return ElementList(self.get("info")) # type: ignore
1556
+
1557
+ @property
1558
+ def json(self) -> ElementList[Json]:
1559
+ return ElementList(self.get("json")) # type: ignore
1560
+
1561
+ @property
1562
+ def latex(self) -> ElementList[Latex]:
1563
+ return ElementList(self.get("latex")) # type: ignore
1564
+
1565
+ @property
1566
+ def markdown(self) -> ElementList[Markdown]:
1567
+ return ElementList(self.get("markdown")) # type: ignore
1568
+
1569
+ @property
1570
+ def metric(self) -> ElementList[Metric]:
1571
+ return ElementList(self.get("metric")) # type: ignore
1572
+
1573
+ @property
1574
+ def multiselect(self) -> WidgetList[Multiselect[Any]]:
1575
+ return WidgetList(self.get("multiselect")) # type: ignore
1576
+
1577
+ @property
1578
+ def number_input(self) -> WidgetList[NumberInput]:
1579
+ return WidgetList(self.get("number_input")) # type: ignore
1580
+
1581
+ @property
1582
+ def radio(self) -> WidgetList[Radio[Any]]:
1583
+ return WidgetList(self.get("radio")) # type: ignore
1584
+
1585
+ @property
1586
+ def select_slider(self) -> WidgetList[SelectSlider[Any]]:
1587
+ return WidgetList(self.get("select_slider")) # type: ignore
1588
+
1589
+ @property
1590
+ def selectbox(self) -> WidgetList[Selectbox[Any]]:
1591
+ return WidgetList(self.get("selectbox")) # type: ignore
1592
+
1593
+ @property
1594
+ def slider(self) -> WidgetList[Slider[Any]]:
1595
+ return WidgetList(self.get("slider")) # type: ignore
1596
+
1597
+ @property
1598
+ def status(self) -> Sequence[Status]:
1599
+ return self.get("status") # type: ignore
1600
+
1601
+ @property
1602
+ def subheader(self) -> ElementList[Subheader]:
1603
+ return ElementList(self.get("subheader")) # type: ignore
1604
+
1605
+ @property
1606
+ def success(self) -> ElementList[Success]:
1607
+ return ElementList(self.get("success")) # type: ignore
1608
+
1609
+ @property
1610
+ def table(self) -> ElementList[Table]:
1611
+ return ElementList(self.get("arrow_table")) # type: ignore
1612
+
1613
+ @property
1614
+ def tabs(self) -> Sequence[Tab]:
1615
+ return self.get("tab") # type: ignore
1616
+
1617
+ @property
1618
+ def text(self) -> ElementList[Text]:
1619
+ return ElementList(self.get("text")) # type: ignore
1620
+
1621
+ @property
1622
+ def text_area(self) -> WidgetList[TextArea]:
1623
+ return WidgetList(self.get("text_area")) # type: ignore
1624
+
1625
+ @property
1626
+ def text_input(self) -> WidgetList[TextInput]:
1627
+ return WidgetList(self.get("text_input")) # type: ignore
1628
+
1629
+ @property
1630
+ def time_input(self) -> WidgetList[TimeInput]:
1631
+ return WidgetList(self.get("time_input")) # type: ignore
1632
+
1633
+ @property
1634
+ def title(self) -> ElementList[Title]:
1635
+ return ElementList(self.get("title")) # type: ignore
1636
+
1637
+ @property
1638
+ def toast(self) -> ElementList[Toast]:
1639
+ return ElementList(self.get("toast")) # type: ignore
1640
+
1641
+ @property
1642
+ def toggle(self) -> WidgetList[Toggle]:
1643
+ return WidgetList(self.get("toggle")) # type: ignore
1644
+
1645
+ @property
1646
+ def warning(self) -> ElementList[Warning]:
1647
+ return ElementList(self.get("warning")) # type: ignore
1648
+
1649
+ def get(self, element_type: str) -> Sequence[Node]:
1650
+ return [e for e in self if e.type == element_type]
1651
+
1652
+ def run(self, *, timeout: float | None = None) -> AppTest:
1653
+ """Run the script with updated widget values.
1654
+
1655
+ Parameters
1656
+ ----------
1657
+ timeout
1658
+ The maximum number of seconds to run the script. None means
1659
+ use the AppTest's default.
1660
+ """
1661
+ return self.root.run(timeout=timeout)
1662
+
1663
+ def __repr__(self):
1664
+ return repr_(self)
1665
+
1666
+
1667
+ def repr_(self) -> str:
1668
+ """A custom repr similar to `streamlit.util.repr_` but that shows tree
1669
+ structure using indentation.
1670
+ """
1671
+ classname = self.__class__.__name__
1672
+
1673
+ defaults: list[Any] = [None, "", False, [], set(), {}]
1674
+
1675
+ if is_dataclass(self):
1676
+ fields_vals = (
1677
+ (f.name, getattr(self, f.name))
1678
+ for f in fields(self)
1679
+ if f.repr
1680
+ and getattr(self, f.name) != f.default
1681
+ and getattr(self, f.name) not in defaults
1682
+ )
1683
+ else:
1684
+ fields_vals = ((f, v) for (f, v) in self.__dict__.items() if v not in defaults)
1685
+
1686
+ reprs = []
1687
+ for field_name, value in fields_vals:
1688
+ if isinstance(value, dict):
1689
+ line = f"{field_name}={format_dict(value)}"
1690
+ else:
1691
+ line = f"{field_name}={value!r}"
1692
+ reprs.append(line)
1693
+
1694
+ reprs[0] = "\n" + reprs[0]
1695
+ field_reprs = ",\n".join(reprs)
1696
+
1697
+ field_reprs = textwrap.indent(field_reprs, " " * 4)
1698
+ return f"{classname}({field_reprs}\n)"
1699
+
1700
+
1701
+ def format_dict(d: dict[Any, Any]):
1702
+ lines = []
1703
+ for k, v in d.items():
1704
+ line = f"{k}: {v!r}"
1705
+ lines.append(line)
1706
+ r = ",\n".join(lines)
1707
+ r = textwrap.indent(r, " " * 4)
1708
+ r = f"{{\n{r}\n}}"
1709
+ return r
1710
+
1711
+
1712
+ @dataclass(repr=False)
1713
+ class SpecialBlock(Block):
1714
+ """Base class for the sidebar and main body containers."""
1715
+
1716
+ def __init__(
1717
+ self,
1718
+ proto: BlockProto | None,
1719
+ root: ElementTree,
1720
+ type: str | None = None,
1721
+ ):
1722
+ self.children = {}
1723
+ self.proto = proto
1724
+ if type:
1725
+ self.type = type
1726
+ elif proto and proto.WhichOneof("type"):
1727
+ ty = proto.WhichOneof("type")
1728
+ assert ty is not None
1729
+ self.type = ty
1730
+ else:
1731
+ self.type = "unknown"
1732
+ self.root = root
1733
+
1734
+
1735
+ @dataclass(repr=False)
1736
+ class ChatMessage(Block):
1737
+ """A representation of ``st.chat_message``."""
1738
+
1739
+ type: str = field(repr=False)
1740
+ proto: BlockProto.ChatMessage = field(repr=False)
1741
+ name: str
1742
+ avatar: str
1743
+
1744
+ def __init__(
1745
+ self,
1746
+ proto: BlockProto.ChatMessage,
1747
+ root: ElementTree,
1748
+ ):
1749
+ self.children = {}
1750
+ self.proto = proto
1751
+ self.root = root
1752
+ self.type = "chat_message"
1753
+ self.name = proto.name
1754
+ self.avatar = proto.avatar
1755
+
1756
+
1757
+ @dataclass(repr=False)
1758
+ class Column(Block):
1759
+ """A representation of a column within ``st.columns``."""
1760
+
1761
+ type: str = field(repr=False)
1762
+ proto: BlockProto.Column = field(repr=False)
1763
+ weight: float
1764
+ gap: str
1765
+
1766
+ def __init__(
1767
+ self,
1768
+ proto: BlockProto.Column,
1769
+ root: ElementTree,
1770
+ ):
1771
+ self.children = {}
1772
+ self.proto = proto
1773
+ self.root = root
1774
+ self.type = "column"
1775
+ self.weight = proto.weight
1776
+ self.gap = proto.gap
1777
+
1778
+
1779
+ @dataclass(repr=False)
1780
+ class Expander(Block):
1781
+ type: str = field(repr=False)
1782
+ proto: BlockProto.Expandable = field(repr=False)
1783
+ icon: str
1784
+ label: str
1785
+
1786
+ def __init__(self, proto: BlockProto.Expandable, root: ElementTree):
1787
+ self.children = {}
1788
+ self.proto = proto
1789
+ self.root = root
1790
+ # The internal name is "expandable" but the public API uses "expander"
1791
+ # so the naming of the class and type follows the public name.
1792
+ self.type = "expander"
1793
+ self.icon = proto.icon
1794
+ self.label = proto.label
1795
+
1796
+
1797
+ @dataclass(repr=False)
1798
+ class Status(Block):
1799
+ type: str = field(repr=False)
1800
+ proto: BlockProto.Expandable = field(repr=False)
1801
+ icon: str
1802
+ label: str
1803
+
1804
+ def __init__(self, proto: BlockProto.Expandable, root: ElementTree):
1805
+ self.children = {}
1806
+ self.proto = proto
1807
+ self.root = root
1808
+ self.type = "status"
1809
+ self.icon = proto.icon
1810
+ self.label = proto.label
1811
+
1812
+ @property
1813
+ def state(self):
1814
+ if self.icon == "spinner":
1815
+ return "running"
1816
+ elif self.icon == ":material/check:":
1817
+ return "complete"
1818
+ elif self.icon == ":material/error:":
1819
+ return "error"
1820
+ else:
1821
+ raise ValueError("Unknown Status state")
1822
+
1823
+
1824
+ @dataclass(repr=False)
1825
+ class Tab(Block):
1826
+ """A representation of tab within ``st.tabs``."""
1827
+
1828
+ type: str = field(repr=False)
1829
+ proto: BlockProto.Tab = field(repr=False)
1830
+ label: str
1831
+
1832
+ def __init__(
1833
+ self,
1834
+ proto: BlockProto.Tab,
1835
+ root: ElementTree,
1836
+ ):
1837
+ self.children = {}
1838
+ self.proto = proto
1839
+ self.root = root
1840
+ self.type = "tab"
1841
+ self.label = proto.label
1842
+
1843
+
1844
+ Node: TypeAlias = Union[Element, Block]
1845
+
1846
+
1847
+ def get_widget_state(node: Node) -> WidgetState | None:
1848
+ if isinstance(node, Widget):
1849
+ return node._widget_state
1850
+ else:
1851
+ return None
1852
+
1853
+
1854
+ @dataclass(repr=False)
1855
+ class ElementTree(Block):
1856
+ """A tree of the elements produced by running a streamlit script.
1857
+
1858
+ Elements can be queried in three ways:
1859
+ - By element type, using `.foo` properties to get a list of all of that element,
1860
+ in the order they appear in the app
1861
+ - By user key, for widgets, by calling the above list with a key: `.foo(key='bar')`
1862
+ - Positionally, using list indexing syntax (`[...]`) to access a child of a
1863
+ block element. Not recommended because the exact tree structure can be surprising.
1864
+
1865
+ Element queries made on a block container will return only the elements
1866
+ descending from that block.
1867
+
1868
+ Returned elements have methods for accessing whatever attributes are relevant.
1869
+ For very simple elements this may be only its value, while complex elements
1870
+ like widgets have many.
1871
+
1872
+ Widgets provide a fluent API for faking frontend interaction and rerunning
1873
+ the script with the new widget values. All widgets provide a low level `set_value`
1874
+ method, along with higher level methods specific to that type of widget.
1875
+ After an interaction, calling `.run()` will update the AppTest with the
1876
+ results of that script run.
1877
+ """
1878
+
1879
+ _runner: AppTest | None = field(repr=False, default=None)
1880
+
1881
+ def __init__(self):
1882
+ self.children = {}
1883
+ self.root = self
1884
+ self.type = "root"
1885
+
1886
+ @property
1887
+ def main(self) -> Block:
1888
+ m = self[0]
1889
+ assert isinstance(m, Block)
1890
+ return m
1891
+
1892
+ @property
1893
+ def sidebar(self) -> Block:
1894
+ s = self[1]
1895
+ assert isinstance(s, Block)
1896
+ return s
1897
+
1898
+ @property
1899
+ def session_state(self) -> SafeSessionState:
1900
+ assert self._runner is not None
1901
+ return self._runner.session_state
1902
+
1903
+ def get_widget_states(self) -> WidgetStates:
1904
+ ws = WidgetStates()
1905
+ for node in self:
1906
+ w = get_widget_state(node)
1907
+ if w is not None:
1908
+ ws.widgets.append(w)
1909
+
1910
+ return ws
1911
+
1912
+ def run(self, *, timeout: float | None = None) -> AppTest:
1913
+ """Run the script with updated widget values.
1914
+
1915
+ Parameters
1916
+ ----------
1917
+ timeout
1918
+ The maximum number of seconds to run the script. None means
1919
+ use the AppTest's default.
1920
+ """
1921
+ assert self._runner is not None
1922
+
1923
+ widget_states = self.get_widget_states()
1924
+ return self._runner._run(widget_states, timeout=timeout)
1925
+
1926
+ def __repr__(self):
1927
+ return format_dict(self.children)
1928
+
1929
+
1930
+ def parse_tree_from_messages(messages: list[ForwardMsg]) -> ElementTree:
1931
+ """Transform a list of `ForwardMsg` into a tree matching the implicit
1932
+ tree structure of blocks and elements in a streamlit app.
1933
+
1934
+ Returns the root of the tree, which acts as the entrypoint for the query
1935
+ and interaction API.
1936
+ """
1937
+ root = ElementTree()
1938
+ root.children = {
1939
+ 0: SpecialBlock(type="main", root=root, proto=None),
1940
+ 1: SpecialBlock(type="sidebar", root=root, proto=None),
1941
+ 2: SpecialBlock(type="event", root=root, proto=None),
1942
+ }
1943
+
1944
+ for msg in messages:
1945
+ if not msg.HasField("delta"):
1946
+ continue
1947
+ delta_path = msg.metadata.delta_path
1948
+ delta = msg.delta
1949
+ if delta.WhichOneof("type") == "new_element":
1950
+ elt = delta.new_element
1951
+ ty = elt.WhichOneof("type")
1952
+ new_node: Node
1953
+ if ty == "alert":
1954
+ format = elt.alert.format
1955
+ if format == AlertProto.Format.ERROR:
1956
+ new_node = Error(elt.alert, root=root)
1957
+ elif format == AlertProto.Format.INFO:
1958
+ new_node = Info(elt.alert, root=root)
1959
+ elif format == AlertProto.Format.SUCCESS:
1960
+ new_node = Success(elt.alert, root=root)
1961
+ elif format == AlertProto.Format.WARNING:
1962
+ new_node = Warning(elt.alert, root=root)
1963
+ else:
1964
+ raise ValueError(
1965
+ f"Unknown alert type with format {elt.alert.format}"
1966
+ )
1967
+ elif ty == "arrow_data_frame":
1968
+ new_node = Dataframe(elt.arrow_data_frame, root=root)
1969
+ elif ty == "arrow_table":
1970
+ new_node = Table(elt.arrow_table, root=root)
1971
+ elif ty == "button":
1972
+ new_node = Button(elt.button, root=root)
1973
+ elif ty == "button_group":
1974
+ new_node = ButtonGroup(elt.button_group, root=root)
1975
+ elif ty == "chat_input":
1976
+ new_node = ChatInput(elt.chat_input, root=root)
1977
+ elif ty == "checkbox":
1978
+ style = elt.checkbox.type
1979
+ if style == CheckboxProto.StyleType.TOGGLE:
1980
+ new_node = Toggle(elt.checkbox, root=root)
1981
+ else:
1982
+ new_node = Checkbox(elt.checkbox, root=root)
1983
+ elif ty == "code":
1984
+ new_node = Code(elt.code, root=root)
1985
+ elif ty == "color_picker":
1986
+ new_node = ColorPicker(elt.color_picker, root=root)
1987
+ elif ty == "date_input":
1988
+ new_node = DateInput(elt.date_input, root=root)
1989
+ elif ty == "exception":
1990
+ new_node = Exception(elt.exception, root=root)
1991
+ elif ty == "heading":
1992
+ if elt.heading.tag == HeadingProtoTag.TITLE_TAG.value:
1993
+ new_node = Title(elt.heading, root=root)
1994
+ elif elt.heading.tag == HeadingProtoTag.HEADER_TAG.value:
1995
+ new_node = Header(elt.heading, root=root)
1996
+ elif elt.heading.tag == HeadingProtoTag.SUBHEADER_TAG.value:
1997
+ new_node = Subheader(elt.heading, root=root)
1998
+ else:
1999
+ raise ValueError(f"Unknown heading type with tag {elt.heading.tag}")
2000
+ elif ty == "json":
2001
+ new_node = Json(elt.json, root=root)
2002
+ elif ty == "markdown":
2003
+ if elt.markdown.element_type == MarkdownProto.Type.NATIVE:
2004
+ new_node = Markdown(elt.markdown, root=root)
2005
+ elif elt.markdown.element_type == MarkdownProto.Type.CAPTION:
2006
+ new_node = Caption(elt.markdown, root=root)
2007
+ elif elt.markdown.element_type == MarkdownProto.Type.LATEX:
2008
+ new_node = Latex(elt.markdown, root=root)
2009
+ elif elt.markdown.element_type == MarkdownProto.Type.DIVIDER:
2010
+ new_node = Divider(elt.markdown, root=root)
2011
+ else:
2012
+ raise ValueError(
2013
+ f"Unknown markdown type {elt.markdown.element_type}"
2014
+ )
2015
+ elif ty == "metric":
2016
+ new_node = Metric(elt.metric, root=root)
2017
+ elif ty == "multiselect":
2018
+ new_node = Multiselect(elt.multiselect, root=root)
2019
+ elif ty == "number_input":
2020
+ new_node = NumberInput(elt.number_input, root=root)
2021
+ elif ty == "radio":
2022
+ new_node = Radio(elt.radio, root=root)
2023
+ elif ty == "selectbox":
2024
+ new_node = Selectbox(elt.selectbox, root=root)
2025
+ elif ty == "slider":
2026
+ if elt.slider.type == SliderProto.Type.SLIDER:
2027
+ new_node = Slider(elt.slider, root=root)
2028
+ elif elt.slider.type == SliderProto.Type.SELECT_SLIDER:
2029
+ new_node = SelectSlider(elt.slider, root=root)
2030
+ else:
2031
+ raise ValueError(f"Slider with unknown type {elt.slider}")
2032
+ elif ty == "text":
2033
+ new_node = Text(elt.text, root=root)
2034
+ elif ty == "text_area":
2035
+ new_node = TextArea(elt.text_area, root=root)
2036
+ elif ty == "text_input":
2037
+ new_node = TextInput(elt.text_input, root=root)
2038
+ elif ty == "time_input":
2039
+ new_node = TimeInput(elt.time_input, root=root)
2040
+ elif ty == "toast":
2041
+ new_node = Toast(elt.toast, root=root)
2042
+ else:
2043
+ new_node = UnknownElement(elt, root=root)
2044
+ elif delta.WhichOneof("type") == "add_block":
2045
+ block = delta.add_block
2046
+ bty = block.WhichOneof("type")
2047
+ if bty == "chat_message":
2048
+ new_node = ChatMessage(block.chat_message, root=root)
2049
+ elif bty == "column":
2050
+ new_node = Column(block.column, root=root)
2051
+ elif bty == "expandable":
2052
+ if block.expandable.icon:
2053
+ new_node = Status(block.expandable, root=root)
2054
+ else:
2055
+ new_node = Expander(block.expandable, root=root)
2056
+ elif bty == "tab":
2057
+ new_node = Tab(block.tab, root=root)
2058
+ else:
2059
+ new_node = Block(proto=block, root=root)
2060
+ else:
2061
+ # add_rows
2062
+ continue
2063
+
2064
+ current_node: Block = root
2065
+ # Every node up to the end is a Block
2066
+ for idx in delta_path[:-1]:
2067
+ children = current_node.children
2068
+ child = children.get(idx)
2069
+ if child is None:
2070
+ child = Block(proto=None, root=root)
2071
+ children[idx] = child
2072
+ assert isinstance(child, Block)
2073
+ current_node = child
2074
+
2075
+ # Handle a block when we already have a placeholder for that location
2076
+ if isinstance(new_node, Block):
2077
+ placeholder_block = current_node.children.get(delta_path[-1])
2078
+ if placeholder_block is not None:
2079
+ new_node.children = placeholder_block.children
2080
+
2081
+ current_node.children[delta_path[-1]] = new_node
2082
+
2083
+ return root