streamlit 1.51.0__py3-none-any.whl → 1.52.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (314) hide show
  1. streamlit/__init__.py +1 -0
  2. streamlit/commands/execution_control.py +89 -14
  3. streamlit/components/v1/component_arrow.py +7 -7
  4. streamlit/components/v2/__init__.py +59 -3
  5. streamlit/components/v2/bidi_component/main.py +161 -13
  6. streamlit/components/v2/bidi_component/serialization.py +13 -6
  7. streamlit/components/v2/component_manager.py +11 -3
  8. streamlit/components/v2/component_registry.py +18 -1
  9. streamlit/components/v2/types.py +2 -2
  10. streamlit/connections/snowflake_connection.py +1 -1
  11. streamlit/connections/snowpark_connection.py +1 -1
  12. streamlit/dataframe_util.py +18 -18
  13. streamlit/delta_generator.py +7 -0
  14. streamlit/delta_generator_singletons.py +8 -14
  15. streamlit/elements/alert.py +16 -0
  16. streamlit/elements/arrow.py +36 -6
  17. streamlit/elements/bokeh_chart.py +10 -78
  18. streamlit/elements/code.py +2 -2
  19. streamlit/elements/deck_gl_json_chart.py +1 -1
  20. streamlit/elements/exception.py +1 -1
  21. streamlit/elements/form.py +27 -0
  22. streamlit/elements/heading.py +60 -5
  23. streamlit/elements/html.py +13 -2
  24. streamlit/elements/image.py +1 -1
  25. streamlit/elements/layouts.py +28 -22
  26. streamlit/elements/lib/built_in_chart_utils.py +49 -16
  27. streamlit/elements/lib/color_util.py +1 -1
  28. streamlit/elements/lib/column_config_utils.py +6 -5
  29. streamlit/elements/lib/layout_utils.py +50 -0
  30. streamlit/elements/lib/pandas_styler_utils.py +17 -9
  31. streamlit/elements/lib/shortcut_utils.py +152 -0
  32. streamlit/elements/markdown.py +50 -3
  33. streamlit/elements/metric.py +31 -1
  34. streamlit/elements/plotly_chart.py +75 -6
  35. streamlit/elements/spinner.py +1 -1
  36. streamlit/elements/text.py +20 -3
  37. streamlit/elements/toast.py +2 -0
  38. streamlit/elements/vega_charts.py +17 -1
  39. streamlit/elements/widgets/audio_input.py +8 -7
  40. streamlit/elements/widgets/button.py +279 -40
  41. streamlit/elements/widgets/button_group.py +27 -2
  42. streamlit/elements/widgets/camera_input.py +1 -1
  43. streamlit/elements/widgets/chat.py +300 -42
  44. streamlit/elements/widgets/color_picker.py +7 -0
  45. streamlit/elements/widgets/data_editor.py +68 -28
  46. streamlit/elements/widgets/file_uploader.py +4 -1
  47. streamlit/elements/widgets/number_input.py +2 -0
  48. streamlit/elements/widgets/text_widgets.py +2 -0
  49. streamlit/elements/widgets/time_widgets.py +581 -9
  50. streamlit/errors.py +22 -0
  51. streamlit/git_util.py +1 -1
  52. streamlit/navigation/page.py +7 -0
  53. streamlit/net_util.py +2 -2
  54. streamlit/proto/Alert_pb2.pyi +3 -3
  55. streamlit/proto/AppPage_pb2.pyi +7 -1
  56. streamlit/proto/ArrowData_pb2.pyi +7 -1
  57. streamlit/proto/ArrowNamedDataSet_pb2.pyi +7 -1
  58. streamlit/proto/ArrowVegaLiteChart_pb2.pyi +7 -1
  59. streamlit/proto/Arrow_pb2.py +10 -10
  60. streamlit/proto/Arrow_pb2.pyi +19 -12
  61. streamlit/proto/AudioInput_pb2.pyi +7 -1
  62. streamlit/proto/Audio_pb2.pyi +7 -1
  63. streamlit/proto/AuthRedirect_pb2.pyi +7 -1
  64. streamlit/proto/AutoRerun_pb2.pyi +7 -1
  65. streamlit/proto/BackMsg_pb2.py +4 -2
  66. streamlit/proto/BackMsg_pb2.pyi +34 -4
  67. streamlit/proto/Balloons_pb2.pyi +7 -1
  68. streamlit/proto/BidiComponent_pb2.pyi +10 -4
  69. streamlit/proto/Block_pb2.pyi +35 -35
  70. streamlit/proto/BokehChart_pb2.pyi +7 -1
  71. streamlit/proto/ButtonGroup_pb2.pyi +9 -9
  72. streamlit/proto/Button_pb2.py +2 -2
  73. streamlit/proto/Button_pb2.pyi +11 -2
  74. streamlit/proto/CameraInput_pb2.pyi +7 -1
  75. streamlit/proto/ChatInput_pb2.py +6 -6
  76. streamlit/proto/ChatInput_pb2.pyi +18 -6
  77. streamlit/proto/Checkbox_pb2.pyi +3 -3
  78. streamlit/proto/ClientState_pb2.pyi +10 -4
  79. streamlit/proto/Code_pb2.pyi +7 -1
  80. streamlit/proto/ColorPicker_pb2.pyi +7 -1
  81. streamlit/proto/Common_pb2.py +3 -3
  82. streamlit/proto/Common_pb2.pyi +35 -23
  83. streamlit/proto/Components_pb2.pyi +19 -13
  84. streamlit/proto/DataFrame_pb2.pyi +55 -49
  85. streamlit/proto/DateInput_pb2.pyi +7 -1
  86. streamlit/proto/DateTimeInput_pb2.py +28 -0
  87. streamlit/proto/DateTimeInput_pb2.pyi +92 -0
  88. streamlit/proto/DeckGlJsonChart_pb2.pyi +3 -3
  89. streamlit/proto/Delta_pb2.pyi +7 -1
  90. streamlit/proto/DocString_pb2.pyi +10 -4
  91. streamlit/proto/DownloadButton_pb2.py +2 -2
  92. streamlit/proto/DownloadButton_pb2.pyi +16 -2
  93. streamlit/proto/Element_pb2.py +5 -3
  94. streamlit/proto/Element_pb2.pyi +23 -5
  95. streamlit/proto/Empty_pb2.pyi +7 -1
  96. streamlit/proto/Exception_pb2.pyi +7 -1
  97. streamlit/proto/Favicon_pb2.pyi +7 -1
  98. streamlit/proto/FileUploader_pb2.pyi +7 -1
  99. streamlit/proto/ForwardMsg_pb2.py +12 -10
  100. streamlit/proto/ForwardMsg_pb2.pyi +42 -15
  101. streamlit/proto/GapSize_pb2.pyi +4 -4
  102. streamlit/proto/GitInfo_pb2.pyi +3 -3
  103. streamlit/proto/GraphVizChart_pb2.pyi +7 -1
  104. streamlit/proto/Heading_pb2.pyi +7 -1
  105. streamlit/proto/HeightConfig_pb2.pyi +7 -1
  106. streamlit/proto/Html_pb2.py +2 -2
  107. streamlit/proto/Html_pb2.pyi +11 -2
  108. streamlit/proto/IFrame_pb2.pyi +7 -1
  109. streamlit/proto/Image_pb2.pyi +10 -4
  110. streamlit/proto/Json_pb2.pyi +7 -1
  111. streamlit/proto/LabelVisibilityMessage_pb2.pyi +3 -3
  112. streamlit/proto/LinkButton_pb2.py +2 -2
  113. streamlit/proto/LinkButton_pb2.pyi +15 -2
  114. streamlit/proto/Logo_pb2.pyi +7 -1
  115. streamlit/proto/Markdown_pb2.pyi +3 -3
  116. streamlit/proto/Metric_pb2.pyi +7 -7
  117. streamlit/proto/MetricsEvent_pb2.pyi +10 -4
  118. streamlit/proto/MultiSelect_pb2.pyi +7 -1
  119. streamlit/proto/NamedDataSet_pb2.pyi +7 -1
  120. streamlit/proto/Navigation_pb2.pyi +3 -3
  121. streamlit/proto/NewSession_pb2.pyi +40 -40
  122. streamlit/proto/NumberInput_pb2.pyi +3 -3
  123. streamlit/proto/PageConfig_pb2.pyi +7 -7
  124. streamlit/proto/PageInfo_pb2.pyi +7 -1
  125. streamlit/proto/PageLink_pb2.py +2 -2
  126. streamlit/proto/PageLink_pb2.pyi +11 -2
  127. streamlit/proto/PageNotFound_pb2.pyi +7 -1
  128. streamlit/proto/PageProfile_pb2.pyi +13 -7
  129. streamlit/proto/PagesChanged_pb2.pyi +7 -1
  130. streamlit/proto/ParentMessage_pb2.pyi +7 -1
  131. streamlit/proto/PlotlyChart_pb2.pyi +6 -6
  132. streamlit/proto/Progress_pb2.pyi +7 -1
  133. streamlit/proto/Radio_pb2.pyi +7 -1
  134. streamlit/proto/RootContainer_pb2.pyi +1 -1
  135. streamlit/proto/Selectbox_pb2.pyi +7 -1
  136. streamlit/proto/SessionEvent_pb2.pyi +7 -1
  137. streamlit/proto/SessionStatus_pb2.pyi +7 -1
  138. streamlit/proto/Skeleton_pb2.pyi +3 -3
  139. streamlit/proto/Slider_pb2.pyi +5 -5
  140. streamlit/proto/Snow_pb2.pyi +7 -1
  141. streamlit/proto/Space_pb2.pyi +7 -1
  142. streamlit/proto/Spinner_pb2.pyi +7 -1
  143. streamlit/proto/TextAlignmentConfig_pb2.py +29 -0
  144. streamlit/proto/TextAlignmentConfig_pb2.pyi +68 -0
  145. streamlit/proto/TextArea_pb2.pyi +7 -1
  146. streamlit/proto/TextInput_pb2.pyi +3 -3
  147. streamlit/proto/Text_pb2.pyi +7 -1
  148. streamlit/proto/TimeInput_pb2.pyi +7 -1
  149. streamlit/proto/Toast_pb2.pyi +7 -1
  150. streamlit/proto/VegaLiteChart_pb2.pyi +7 -1
  151. streamlit/proto/Video_pb2.pyi +6 -6
  152. streamlit/proto/WidgetStates_pb2.pyi +10 -4
  153. streamlit/proto/WidthConfig_pb2.pyi +7 -1
  154. streamlit/proto/openmetrics_data_model_pb2.pyi +52 -52
  155. streamlit/runtime/app_session.py +38 -1
  156. streamlit/runtime/caching/cache_data_api.py +1 -1
  157. streamlit/runtime/caching/cache_resource_api.py +2 -2
  158. streamlit/runtime/caching/cache_utils.py +1 -1
  159. streamlit/runtime/caching/hashing.py +1 -1
  160. streamlit/runtime/download_data_util.py +53 -0
  161. streamlit/runtime/forward_msg_queue.py +1 -0
  162. streamlit/runtime/media_file_manager.py +178 -2
  163. streamlit/runtime/metrics_util.py +87 -3
  164. streamlit/runtime/scriptrunner/script_runner.py +3 -1
  165. streamlit/runtime/state/query_params.py +80 -29
  166. streamlit/runtime/state/session_state.py +2 -2
  167. streamlit/static/index.html +1 -1
  168. streamlit/static/manifest.json +530 -229
  169. streamlit/static/static/js/{ErrorOutline.esm.YoJdlW1p.js → ErrorOutline.esm.ZJDbmVTx.js} +1 -1
  170. streamlit/static/static/js/{FileDownload.esm.Ddx8VEYy.js → FileDownload.esm.Dx0vI3vH.js} +1 -1
  171. streamlit/static/static/js/{FileHelper.90EtOmj9.js → FileHelper.B7Ero7qQ.js} +3 -3
  172. streamlit/static/static/js/{FormClearHelper.BB1Km6eP.js → FormClearHelper.CG2XN1_g.js} +1 -1
  173. streamlit/static/static/js/IFrameUtil.DefezniK.js +1 -0
  174. streamlit/static/static/js/InputInstructions.Cj5-1zf6.js +1 -0
  175. streamlit/static/static/js/Particles.BfWfv0Aw.js +1 -0
  176. streamlit/static/static/js/{ProgressBar.DLY8H6nE.js → ProgressBar.CGQ8OgfO.js} +2 -2
  177. streamlit/static/static/js/StreamlitSyntaxHighlighter.DTKLpwhl.js +20 -0
  178. streamlit/static/static/js/{Toolbar.D8nHCkuz.js → Toolbar.B2qFUmd9.js} +1 -1
  179. streamlit/static/static/js/_arrayIncludes.B19Iyn2B.js +1 -0
  180. streamlit/static/static/js/_baseIndexOf.BTknn6Gb.js +1 -0
  181. streamlit/static/static/js/{base-input.CJGiNqed.js → base-input.o9tL8MDP.js} +4 -4
  182. streamlit/static/static/js/{checkbox.Cpdd482O.js → checkbox.0BeV1IBL.js} +1 -1
  183. streamlit/static/static/js/{createSuper.CuQIogbW.js → createSuper.RBO59fEm.js} +1 -1
  184. streamlit/static/static/js/data-grid-overlay-editor.CiTkUy0t.js +1 -0
  185. streamlit/static/static/js/{downloader.CN0K7xlu.js → downloader.DwNZg3Mw.js} +1 -1
  186. streamlit/static/static/js/embed.XT9xNd3F.js +195 -0
  187. streamlit/static/static/js/{es6.BJcsVXQ0.js → es6.x9KsYQg-.js} +2 -2
  188. streamlit/static/static/js/{iframeResizer.contentWindow.XzUvQqcZ.js → iframeResizer.contentWindow.ZVXpMPi0.js} +1 -1
  189. streamlit/static/static/js/index.5VPOamri.js +1 -0
  190. streamlit/static/static/js/index.8HslT92O.js +14 -0
  191. streamlit/static/static/js/index.AnXMIBz3.js +7 -0
  192. streamlit/static/static/js/index.B0yp3bM1.js +6 -0
  193. streamlit/static/static/js/index.B1fRb5wF.js +1 -0
  194. streamlit/static/static/js/index.B527JZdO.js +3 -0
  195. streamlit/static/static/js/index.BHgV-yW4.js +1 -0
  196. streamlit/static/static/js/index.BQr-XwGV.js +1 -0
  197. streamlit/static/static/js/index.BTtmaLDB.js +1 -0
  198. streamlit/static/static/js/index.BWB_91TA.js +1 -0
  199. streamlit/static/static/js/index.BfEKaEmw.js +1 -0
  200. streamlit/static/static/js/index.BfXjTO8b.js +1 -0
  201. streamlit/static/static/js/index.Bjy4NRu9.js +3 -0
  202. streamlit/static/static/js/index.Bu5JWpT_.js +1 -0
  203. streamlit/static/static/js/index.BuCx76ZV.js +1 -0
  204. streamlit/static/static/js/index.BxjzhVUb.js +2 -0
  205. streamlit/static/static/js/index.By55VdPY.js +1 -0
  206. streamlit/static/static/js/index.CF5MxTbK.js +1 -0
  207. streamlit/static/static/js/index.CLmq_z9K.js +1 -0
  208. streamlit/static/static/js/index.CNH4rdSz.js +1 -0
  209. streamlit/static/static/js/{index.D3GPA5k4.js → index.CTgm_-jO.js} +9 -40
  210. streamlit/static/static/js/index.C_rK-Swb.js +188 -0
  211. streamlit/static/static/js/index.CjozwSzS.js +1 -0
  212. streamlit/static/static/js/{index.DOFlg3dS.js → index.CkGVt6-G.js} +1 -1
  213. streamlit/static/static/js/index.CuvXOyER.js +2 -0
  214. streamlit/static/static/js/{index.B_dWA3vd.js → index.CyUHWoCC.js} +2 -2
  215. streamlit/static/static/js/index.CyroQtI4.js +2 -0
  216. streamlit/static/static/js/index.D6HmkoDm.js +263 -0
  217. streamlit/static/static/js/index.DAqCNvsO.js +1 -0
  218. streamlit/static/static/js/index.DB_w_CZQ.js +1 -0
  219. streamlit/static/static/js/index.DBalctjj.js +2 -0
  220. streamlit/static/static/js/index.DK0RFJUG.js +11 -0
  221. streamlit/static/static/js/index.DMxc2XFp.js +151 -0
  222. streamlit/static/static/js/index.DO5utP74.js +2 -0
  223. streamlit/static/static/js/index.DS7lf09n.js +1 -0
  224. streamlit/static/static/js/index.DWexTVLY.js +1 -0
  225. streamlit/static/static/js/index.DXxnU5ej.js +1 -0
  226. streamlit/static/static/js/index.DcU3uDvB.js +2 -0
  227. streamlit/static/static/js/index.DlltaH7J.js +1 -0
  228. streamlit/static/static/js/index.DpNTZz82.js +27 -0
  229. streamlit/static/static/js/index.Dr9HIhQw.js +1 -0
  230. streamlit/static/static/js/index.DsgAU5lc.js +1 -0
  231. streamlit/static/static/js/{index.DPUXkcQL.js → index.KfXqjDYy.js} +1 -1
  232. streamlit/static/static/js/index.PaidgjCs.js +1 -0
  233. streamlit/static/static/js/index.RJZuWCGA.js +1 -0
  234. streamlit/static/static/js/{index.CxIUUfab.js → index.hbeqcRTn.js} +53 -122
  235. streamlit/static/static/js/index.q5hIQwAY.js +1 -0
  236. streamlit/static/static/js/index.rORSX6IW.js +1 -0
  237. streamlit/static/static/js/index.uSX757_v.js +1 -0
  238. streamlit/static/static/js/index.x_QRaLMd.js +1 -0
  239. streamlit/static/static/js/{input.D4MN_FzN.js → input.D5oh9-aB.js} +2 -2
  240. streamlit/static/static/js/main.q9oGOg0H.js +13 -0
  241. streamlit/static/static/js/{memory.DrZjtdGT.js → memory.5kCSFUJS.js} +1 -1
  242. streamlit/static/static/js/moment.C3j7ZXd7.js +4 -0
  243. streamlit/static/static/js/number-overlay-editor.Cn_LsK8N.js +9 -0
  244. streamlit/static/static/js/pandasStylerUtils.BqhXt51_.js +1 -0
  245. streamlit/static/static/js/{possibleConstructorReturn.exeeJQEP.js → possibleConstructorReturn.DD9NK1Z8.js} +1 -1
  246. streamlit/static/static/js/{sandbox.ClO3IuUr.js → sandbox.DACSyz29.js} +1 -1
  247. streamlit/static/static/js/styled-components.C3R090At.js +1 -0
  248. streamlit/static/static/js/threshold.Q1mXg5rX.js +1 -0
  249. streamlit/static/static/js/throttle.B0GR3Iyz.js +1 -0
  250. streamlit/static/static/js/{timepicker.DAhu-vcF.js → timepicker.BdhzPxrv.js} +1 -1
  251. streamlit/static/static/js/timer.C2hYhUse.js +1 -0
  252. streamlit/static/static/js/{toConsumableArray.DNbljYEC.js → toConsumableArray.Db2pdqM2.js} +1 -1
  253. streamlit/static/static/js/uniqueId.CtqIr-Yh.js +1 -0
  254. streamlit/static/static/js/urls.BwSlolu9.js +1 -0
  255. streamlit/static/static/js/{useBasicWidgetState.D6sOH6oI.js → useBasicWidgetState.Bfp6TnSw.js} +1 -1
  256. streamlit/static/static/js/useIntlLocale.hRV75Xgj.js +12 -0
  257. streamlit/static/static/js/{useTextInputAutoExpand.4u3_GcuN.js → useTextInputAutoExpand.QepX7n8Y.js} +1 -1
  258. streamlit/static/static/js/useUpdateUiValue.DHx8TzX6.js +1 -0
  259. streamlit/static/static/js/useWaveformController.WxVzpzEX.js +1 -0
  260. streamlit/static/static/js/value.B4vHRSi7.js +1 -0
  261. streamlit/static/static/js/withCalculatedWidth.DcKeRSWJ.js +1 -0
  262. streamlit/static/static/js/withFullScreenWrapper.CrHddARq.js +1 -0
  263. streamlit/string_util.py +8 -1
  264. streamlit/testing/v1/app_test.py +15 -0
  265. streamlit/testing/v1/element_tree.py +62 -0
  266. streamlit/web/bootstrap.py +24 -0
  267. streamlit/web/server/oauth_authlib_routes.py +5 -2
  268. streamlit/web/server/upload_file_request_handler.py +16 -0
  269. {streamlit-1.51.0.dist-info → streamlit-1.52.0.dist-info}/METADATA +9 -5
  270. {streamlit-1.51.0.dist-info → streamlit-1.52.0.dist-info}/RECORD +274 -239
  271. streamlit/static/static/js/InputInstructions.jhH15PqV.js +0 -1
  272. streamlit/static/static/js/Particles.DUsputn1.js +0 -1
  273. streamlit/static/static/js/data-grid-overlay-editor.2Ufgxc6y.js +0 -1
  274. streamlit/static/static/js/index.B1ZQh4P1.js +0 -1
  275. streamlit/static/static/js/index.BKstZk0M.js +0 -27
  276. streamlit/static/static/js/index.BMcFsUee.js +0 -1
  277. streamlit/static/static/js/index.BR-IdcTb.js +0 -2
  278. streamlit/static/static/js/index.BgnZEMVh.js +0 -1
  279. streamlit/static/static/js/index.BohqXifI.js +0 -1
  280. streamlit/static/static/js/index.Br5nxKNj.js +0 -2
  281. streamlit/static/static/js/index.BrIKVbNc.js +0 -3
  282. streamlit/static/static/js/index.BtWUPzle.js +0 -1
  283. streamlit/static/static/js/index.C0RLraek.js +0 -1
  284. streamlit/static/static/js/index.CAIjskgG.js +0 -1
  285. streamlit/static/static/js/index.CAj-7vWz.js +0 -949
  286. streamlit/static/static/js/index.CMtEit2O.js +0 -1
  287. streamlit/static/static/js/index.CkRlykEE.js +0 -12
  288. streamlit/static/static/js/index.CmN3FXfI.js +0 -1617
  289. streamlit/static/static/js/index.CwbFI1_-.js +0 -1
  290. streamlit/static/static/js/index.D2KPNy7e.js +0 -1
  291. streamlit/static/static/js/index.DGAh7DMq.js +0 -1
  292. streamlit/static/static/js/index.DKb_NvmG.js +0 -197
  293. streamlit/static/static/js/index.DMqgUYKq.js +0 -1
  294. streamlit/static/static/js/index.DX1xY89g.js +0 -1
  295. streamlit/static/static/js/index.DYATBCsq.js +0 -2
  296. streamlit/static/static/js/index.DaSmGJ76.js +0 -3
  297. streamlit/static/static/js/index.Dd7bMeLP.js +0 -1
  298. streamlit/static/static/js/index.DjmmgI5U.js +0 -1
  299. streamlit/static/static/js/index.Dq56CyM2.js +0 -1
  300. streamlit/static/static/js/index.DuiXaS5_.js +0 -7
  301. streamlit/static/static/js/index.DvFidMLe.js +0 -2
  302. streamlit/static/static/js/index.DwkhC5Pc.js +0 -1
  303. streamlit/static/static/js/index.Q-3sFn1v.js +0 -1
  304. streamlit/static/static/js/index.QJ5QO9sJ.js +0 -1
  305. streamlit/static/static/js/index.VwTaeety.js +0 -1
  306. streamlit/static/static/js/index.YOqQbeX8.js +0 -1
  307. streamlit/static/static/js/number-overlay-editor.DRwAw1In.js +0 -9
  308. streamlit/static/static/js/uniqueId.oG4Gvj1v.js +0 -1
  309. streamlit/static/static/js/useUpdateUiValue.F2R3eTeR.js +0 -1
  310. streamlit/static/static/js/withFullScreenWrapper.zothJIsI.js +0 -1
  311. {streamlit-1.51.0.data → streamlit-1.52.0.data}/scripts/streamlit.cmd +0 -0
  312. {streamlit-1.51.0.dist-info → streamlit-1.52.0.dist-info}/WHEEL +0 -0
  313. {streamlit-1.51.0.dist-info → streamlit-1.52.0.dist-info}/entry_points.txt +0 -0
  314. {streamlit-1.51.0.dist-info → streamlit-1.52.0.dist-info}/top_level.txt +0 -0
@@ -18,14 +18,33 @@ from __future__ import annotations
18
18
 
19
19
  import collections
20
20
  import threading
21
- from typing import Final
21
+ import uuid
22
+ from typing import TYPE_CHECKING, BinaryIO, Final, TextIO, TypedDict, cast
22
23
 
23
24
  from streamlit.logger import get_logger
24
- from streamlit.runtime.media_file_storage import MediaFileKind, MediaFileStorage
25
+ from streamlit.runtime.download_data_util import convert_data_to_bytes_and_infer_mime
26
+ from streamlit.runtime.media_file_storage import (
27
+ MediaFileKind,
28
+ MediaFileStorage,
29
+ MediaFileStorageError,
30
+ )
31
+
32
+ if TYPE_CHECKING:
33
+ import io
34
+ from collections.abc import Callable
25
35
 
26
36
  _LOGGER: Final = get_logger(__name__)
27
37
 
28
38
 
39
+ class DeferredCallableEntry(TypedDict):
40
+ """Typed metadata for deferred download callables."""
41
+
42
+ callable: Callable[[], bytes | str | BinaryIO | TextIO | io.RawIOBase]
43
+ mimetype: str | None
44
+ filename: str | None
45
+ coordinates: str
46
+
47
+
29
48
  def _get_session_id() -> str:
30
49
  """Get the active AppSession's session_id."""
31
50
  from streamlit.runtime.scriptrunner_utils.script_run_context import (
@@ -90,6 +109,10 @@ class MediaFileManager:
90
109
  collections.defaultdict(dict)
91
110
  )
92
111
 
112
+ # Dict of [file_id -> deferred callable metadata]
113
+ # Used for deferred download button execution
114
+ self._deferred_callables: dict[str, DeferredCallableEntry] = {}
115
+
93
116
  # MediaFileManager is used from multiple threads, so all operations
94
117
  # need to be protected with a Lock. (This is not an RLock, which
95
118
  # means taking it multiple times from the same thread will deadlock.)
@@ -129,6 +152,31 @@ class MediaFileManager:
129
152
  else:
130
153
  file.mark_for_delete()
131
154
 
155
+ # Clean up orphaned deferred callables
156
+ self._remove_orphaned_deferred_callables()
157
+
158
+ def _remove_orphaned_deferred_callables(self) -> None:
159
+ """Remove deferred callables that are not referenced by any active session.
160
+
161
+ Thread safety: callers must hold `self._lock`.
162
+ """
163
+ _LOGGER.debug("Removing orphaned deferred callables...")
164
+
165
+ # Get all file_ids currently referenced by any session
166
+ active_file_ids = set[str]()
167
+ for session_file_ids_by_coord in self._files_by_session_and_coord.values():
168
+ active_file_ids.update(session_file_ids_by_coord.values())
169
+
170
+ # Remove deferred callables that are no longer referenced
171
+ deferred_ids_to_remove = [
172
+ file_id
173
+ for file_id in self._deferred_callables
174
+ if file_id not in active_file_ids
175
+ ]
176
+ for file_id in deferred_ids_to_remove:
177
+ _LOGGER.debug("Removing deferred callable: %s", file_id)
178
+ del self._deferred_callables[file_id]
179
+
132
180
  def _delete_file(self, file_id: str) -> None:
133
181
  """Delete the given file from storage, and remove its metadata from
134
182
  self._files_by_id.
@@ -158,6 +206,10 @@ class MediaFileManager:
158
206
  if session_id in self._files_by_session_and_coord:
159
207
  del self._files_by_session_and_coord[session_id]
160
208
 
209
+ # Don't immediately delete deferred callables here to avoid race conditions.
210
+ # They will be cleaned up by remove_orphaned_deferred_callables() which
211
+ # only removes callables that are not referenced by ANY session.
212
+
161
213
  _LOGGER.debug(
162
214
  "Sessions still active: %r", self._files_by_session_and_coord.keys()
163
215
  )
@@ -231,3 +283,127 @@ class MediaFileManager:
231
283
  self._files_by_session_and_coord[session_id][coordinates] = file_id
232
284
 
233
285
  return self._storage.get_url(file_id)
286
+
287
+ def add_deferred(
288
+ self,
289
+ data_callable: Callable[[], bytes | str | BinaryIO | TextIO | io.RawIOBase],
290
+ mimetype: str | None,
291
+ coordinates: str,
292
+ file_name: str | None = None,
293
+ ) -> str:
294
+ """Register a callable for deferred execution. Returns placeholder file_id.
295
+
296
+ The callable will be executed later when execute_deferred() is called,
297
+ typically when the user clicks a download button.
298
+
299
+ Safe to call from any thread.
300
+
301
+ Parameters
302
+ ----------
303
+ data_callable : Callable[[], bytes | str | BinaryIO | TextIO | io.RawIOBase]
304
+ A callable that returns the file data when invoked.
305
+ mimetype : str or None
306
+ The mime type for the file. E.g. "text/csv".
307
+ If None, the mimetype will be inferred from the data type when
308
+ execute_deferred() is called.
309
+ coordinates : str
310
+ Unique string identifying an element's location.
311
+ file_name : str or None
312
+ Optional file_name. Used to set the filename in the response header.
313
+
314
+ Returns
315
+ -------
316
+ str
317
+ A placeholder file_id that can be used to execute the callable later.
318
+ """
319
+ session_id = _get_session_id()
320
+
321
+ with self._lock:
322
+ # Generate a unique placeholder ID for this deferred callable
323
+ # Expected: a new placeholder ID is created on every script rerun.
324
+ file_id = uuid.uuid4().hex
325
+
326
+ # Store the callable with its metadata
327
+ self._deferred_callables[file_id] = cast(
328
+ "DeferredCallableEntry",
329
+ {
330
+ "callable": data_callable,
331
+ "mimetype": mimetype,
332
+ "filename": file_name,
333
+ "coordinates": coordinates,
334
+ },
335
+ )
336
+
337
+ # Track this deferred file by session and coordinate
338
+ self._files_by_session_and_coord[session_id][coordinates] = file_id
339
+
340
+ return file_id
341
+
342
+ def execute_deferred(self, file_id: str) -> str:
343
+ """Execute a deferred callable and return the URL to the generated file.
344
+
345
+ This method retrieves the callable registered with add_deferred(),
346
+ executes it, stores the result, and returns a URL to access it.
347
+
348
+ Safe to call from any thread.
349
+
350
+ Parameters
351
+ ----------
352
+ file_id : str
353
+ The placeholder file_id returned by add_deferred().
354
+
355
+ Returns
356
+ -------
357
+ str
358
+ The URL that can be used to download the generated file.
359
+
360
+ Raises
361
+ ------
362
+ MediaFileStorageError
363
+ If the file_id is not found or if the callable execution fails.
364
+ """
365
+ # Retrieve deferred callable metadata while holding lock
366
+ with self._lock:
367
+ if file_id not in self._deferred_callables:
368
+ raise MediaFileStorageError(f"Deferred file {file_id} not found")
369
+
370
+ deferred = self._deferred_callables[file_id]
371
+
372
+ # Execute callable outside lock to avoid blocking other operations
373
+ try:
374
+ data = deferred["callable"]()
375
+ except Exception as e:
376
+ raise MediaFileStorageError(f"Callable execution failed: {e}") from e
377
+
378
+ # Convert data to bytes and infer mimetype if needed
379
+ data_as_bytes, inferred_mime_type = convert_data_to_bytes_and_infer_mime(
380
+ data,
381
+ unsupported_error=MediaFileStorageError(
382
+ f"Callable returned unsupported type: {type(data)}"
383
+ ),
384
+ )
385
+
386
+ # Use provided mimetype if available, otherwise use inferred mimetype
387
+ mime_type: str = deferred["mimetype"] or inferred_mime_type
388
+
389
+ # Store the generated data and get the actual file_id
390
+ with self._lock:
391
+ actual_file_id = self._storage.load_and_get_id(
392
+ data_as_bytes,
393
+ mime_type,
394
+ MediaFileKind.DOWNLOADABLE,
395
+ deferred["filename"],
396
+ )
397
+
398
+ # Create metadata for the actual file
399
+ metadata = MediaFileMetadata(kind=MediaFileKind.DOWNLOADABLE)
400
+ self._file_metadata[actual_file_id] = metadata
401
+
402
+ # Keep the deferred callable so users can download multiple times
403
+ # It will be cleaned up when clear_session_refs() is called on rerun
404
+
405
+ # We leave actual_file_id unmapped so repeat clicks rerun the callable.
406
+ # Cleanup prunes the stored file once no session references it.
407
+
408
+ # Return the URL to access the file
409
+ return self._storage.get_url(actual_file_id)
@@ -78,6 +78,7 @@ _ATTRIBUTIONS_TO_CHECK: Final = [
78
78
  "duckdb",
79
79
  "opensearchpy",
80
80
  "supabase",
81
+ "databricks",
81
82
  # Dataframe Libraries:
82
83
  "polars",
83
84
  "dask",
@@ -92,6 +93,7 @@ _ATTRIBUTIONS_TO_CHECK: Final = [
92
93
  "tables",
93
94
  "zarr",
94
95
  "datasets",
96
+ "daft",
95
97
  # ML & LLM Tools:
96
98
  "mistralai",
97
99
  "openai",
@@ -131,11 +133,62 @@ _ATTRIBUTIONS_TO_CHECK: Final = [
131
133
  "lightgbm",
132
134
  "catboost",
133
135
  "sklearn",
136
+ "pydantic_ai",
137
+ "datachain",
138
+ "docling",
139
+ "litserve",
140
+ "crawl4ai",
141
+ "baml_client",
142
+ "browser_use",
143
+ "crewai",
144
+ "unsloth",
145
+ "langgraph",
146
+ "dspy",
147
+ "ultralytics",
148
+ "instructor",
149
+ "ragas",
150
+ "swarm",
151
+ "faster_whisper",
152
+ "memori",
153
+ "autogen_agentchat",
154
+ "xai_sdk",
155
+ "agno",
156
+ "langfuse",
157
+ "smolagents",
158
+ "ollama",
159
+ "groq",
160
+ "together",
161
+ "ai21",
162
+ "marvin",
163
+ "outlines",
164
+ "guardrails",
165
+ "promptflow",
166
+ "semantic_router",
167
+ "mem0",
168
+ "aisuite",
169
+ "mlflow",
170
+ "optuna",
171
+ "keras",
172
+ "jax",
173
+ "shap",
174
+ "evidently",
175
+ "great_expectations",
176
+ "bentoml",
177
+ "modal",
178
+ "sagemaker",
179
+ "vertexai",
180
+ "tiktoken",
181
+ "sentence_transformers",
182
+ "spacy",
183
+ "nltk",
184
+ "onnxruntime",
185
+ "llama_api_client",
134
186
  # Workflow Tools:
135
187
  "prefect",
136
188
  "luigi",
137
189
  "airflow",
138
190
  "dagster",
191
+ "celery",
139
192
  # Vector Stores:
140
193
  "pgvector",
141
194
  "faiss",
@@ -148,11 +201,40 @@ _ATTRIBUTIONS_TO_CHECK: Final = [
148
201
  "lancedb",
149
202
  # Others:
150
203
  "snowflake",
204
+ "pydantic",
205
+ "fastapi",
206
+ "starlette",
207
+ "playwright",
208
+ "folium",
209
+ "geopandas",
210
+ "httpx",
211
+ "pyecharts",
212
+ "fastplotlib",
213
+ "pygfx",
214
+ "highcharts_core",
215
+ # Optional streamlit dependencies:
216
+ "seaborn",
217
+ "graphviz",
218
+ "matplotlib",
219
+ "uvloop",
220
+ "orjson",
221
+ "rich",
151
222
  "streamlit_extras",
152
223
  "streamlit_pydantic",
153
- "pydantic",
224
+ "pygwalker",
225
+ "plotly",
226
+ "bokeh",
154
227
  "plost",
155
228
  "authlib",
229
+ # Document Processing:
230
+ "pypdf",
231
+ "pdfplumber",
232
+ "docx",
233
+ "openpyxl",
234
+ "xlsxwriter",
235
+ # Image/Vision:
236
+ "cv2",
237
+ "mediapipe",
156
238
  ]
157
239
 
158
240
  _ETC_MACHINE_ID_PATH = "/etc/machine-id"
@@ -331,8 +413,10 @@ def _get_command_telemetry(
331
413
  ):
332
414
  name = f"component:{self_arg.name}"
333
415
 
334
- if name == "_bidi_component" and len(args) > 0 and isinstance(args[0], str):
335
- component_name = args[0]
416
+ if name == "_bidi_component" and len(args) > 1 and isinstance(args[1], str):
417
+ # Bound DeltaGenerator methods always receive `self` as args[0], so args[1]
418
+ # is the user-supplied component name.
419
+ component_name = args[1]
336
420
  name = f"component_v2:{component_name}"
337
421
 
338
422
  return Command(name=name, args=arguments)
@@ -529,7 +529,9 @@ class ScriptRunner:
529
529
  widget_ids = {w.id for w in rerun_data.widget_states.widgets}
530
530
  self._session_state.on_script_finished(widget_ids)
531
531
 
532
- fragment_ids_this_run = list(rerun_data.fragment_id_queue)
532
+ fragment_ids_this_run: list[str] | None = (
533
+ rerun_data.fragment_id_queue or None
534
+ )
533
535
 
534
536
  ctx.reset(
535
537
  query_string=rerun_data.query_string,
@@ -14,18 +14,21 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- from collections.abc import Iterable, Iterator, MutableMapping
17
+ from collections.abc import Iterable, Iterator, Mapping, MutableMapping
18
18
  from dataclasses import dataclass, field
19
19
  from typing import TYPE_CHECKING, Final, cast
20
20
  from urllib import parse
21
21
 
22
- from streamlit.errors import StreamlitAPIException
22
+ from streamlit.errors import StreamlitAPIException, StreamlitQueryParamDictValueError
23
23
  from streamlit.proto.ForwardMsg_pb2 import ForwardMsg
24
24
  from streamlit.runtime.scriptrunner_utils.script_run_context import get_script_run_ctx
25
25
 
26
26
  if TYPE_CHECKING:
27
27
  from _typeshed import SupportsKeysAndGetItem
28
28
 
29
+ QueryParamValue = str | Iterable[str]
30
+ QueryParamsInput = Mapping[str, QueryParamValue] | Iterable[tuple[str, QueryParamValue]]
31
+
29
32
 
30
33
  EMBED_QUERY_PARAM: Final[str] = "embed"
31
34
  EMBED_OPTIONS_QUERY_PARAM: Final[str] = "embed_options"
@@ -47,7 +50,9 @@ class QueryParams(MutableMapping[str, str]):
47
50
  self._ensure_single_query_api_used()
48
51
 
49
52
  return iter(
50
- key for key in self._query_params if key not in EMBED_QUERY_PARAMS_KEYS
53
+ key
54
+ for key in self._query_params
55
+ if key.lower() not in EMBED_QUERY_PARAMS_KEYS
51
56
  )
52
57
 
53
58
  def __getitem__(self, key: str) -> str:
@@ -56,7 +61,7 @@ class QueryParams(MutableMapping[str, str]):
56
61
  If the key is not present, raise KeyError.
57
62
  """
58
63
  self._ensure_single_query_api_used()
59
- if key in EMBED_QUERY_PARAMS_KEYS:
64
+ if key.lower() in EMBED_QUERY_PARAMS_KEYS:
60
65
  raise KeyError(missing_key_error_message(key))
61
66
 
62
67
  try:
@@ -73,29 +78,15 @@ class QueryParams(MutableMapping[str, str]):
73
78
 
74
79
  def __setitem__(self, key: str, value: str | Iterable[str]) -> None:
75
80
  self._ensure_single_query_api_used()
76
- self.__set_item_internal(key, value)
81
+ self._set_item_internal(key, value)
77
82
  self._send_query_param_msg()
78
83
 
79
- def __set_item_internal(self, key: str, value: str | Iterable[str]) -> None:
80
- if isinstance(value, dict):
81
- raise StreamlitAPIException(
82
- f"You cannot set a query params key `{key}` to a dictionary."
83
- )
84
-
85
- if key in EMBED_QUERY_PARAMS_KEYS:
86
- raise StreamlitAPIException(
87
- "Query param embed and embed_options (case-insensitive) cannot be set programmatically."
88
- )
89
- # Type checking users should handle the string serialization themselves
90
- # We will accept any type for the list and serialize to str just in case
91
- if isinstance(value, Iterable) and not isinstance(value, str):
92
- self._query_params[key] = [str(item) for item in value]
93
- else:
94
- self._query_params[key] = str(value)
84
+ def _set_item_internal(self, key: str, value: str | Iterable[str]) -> None:
85
+ _set_item_in_dict(self._query_params, key, value)
95
86
 
96
87
  def __delitem__(self, key: str) -> None:
97
88
  self._ensure_single_query_api_used()
98
- if key in EMBED_QUERY_PARAMS_KEYS:
89
+ if key.lower() in EMBED_QUERY_PARAMS_KEYS:
99
90
  raise KeyError(missing_key_error_message(key))
100
91
  try:
101
92
  del self._query_params[key]
@@ -116,17 +107,17 @@ class QueryParams(MutableMapping[str, str]):
116
107
  if hasattr(other, "keys") and hasattr(other, "__getitem__"):
117
108
  other = cast("SupportsKeysAndGetItem[str, str | Iterable[str]]", other)
118
109
  for key in other.keys(): # noqa: SIM118
119
- self.__set_item_internal(key, other[key])
110
+ self._set_item_internal(key, other[key])
120
111
  else:
121
112
  for key, value in other:
122
- self.__set_item_internal(key, value)
113
+ self._set_item_internal(key, value)
123
114
  for key, value in kwds.items():
124
- self.__set_item_internal(key, value)
115
+ self._set_item_internal(key, value)
125
116
  self._send_query_param_msg()
126
117
 
127
118
  def get_all(self, key: str) -> list[str]:
128
119
  self._ensure_single_query_api_used()
129
- if key not in self._query_params or key in EMBED_QUERY_PARAMS_KEYS:
120
+ if key not in self._query_params or key.lower() in EMBED_QUERY_PARAMS_KEYS:
130
121
  return []
131
122
  value = self._query_params[key]
132
123
  return value if isinstance(value, list) else [value]
@@ -134,7 +125,11 @@ class QueryParams(MutableMapping[str, str]):
134
125
  def __len__(self) -> int:
135
126
  self._ensure_single_query_api_used()
136
127
  return len(
137
- {key for key in self._query_params if key not in EMBED_QUERY_PARAMS_KEYS}
128
+ {
129
+ key
130
+ for key in self._query_params
131
+ if key.lower() not in EMBED_QUERY_PARAMS_KEYS
132
+ }
138
133
  )
139
134
 
140
135
  def __str__(self) -> str:
@@ -165,7 +160,7 @@ class QueryParams(MutableMapping[str, str]):
165
160
  return {
166
161
  key: self[key]
167
162
  for key in self._query_params
168
- if key not in EMBED_QUERY_PARAMS_KEYS
163
+ if key.lower() not in EMBED_QUERY_PARAMS_KEYS
169
164
  }
170
165
 
171
166
  def from_dict(
@@ -190,7 +185,7 @@ class QueryParams(MutableMapping[str, str]):
190
185
  self._query_params = {
191
186
  key: value
192
187
  for key, value in self._query_params.items()
193
- if key in EMBED_QUERY_PARAMS_KEYS and preserve_embed
188
+ if key.lower() in EMBED_QUERY_PARAMS_KEYS and preserve_embed
194
189
  }
195
190
 
196
191
  def _ensure_single_query_api_used(self) -> None:
@@ -202,3 +197,59 @@ class QueryParams(MutableMapping[str, str]):
202
197
 
203
198
  def missing_key_error_message(key: str) -> str:
204
199
  return f'st.query_params has no key "{key}".'
200
+
201
+
202
+ def _set_item_in_dict(
203
+ target_dict: dict[str, list[str] | str], key: str, value: str | Iterable[str]
204
+ ) -> None:
205
+ """Set an item in a dictionary."""
206
+ if isinstance(value, dict):
207
+ raise StreamlitQueryParamDictValueError(key)
208
+
209
+ if key.lower() in EMBED_QUERY_PARAMS_KEYS:
210
+ raise StreamlitAPIException(
211
+ "Query param embed and embed_options (case-insensitive) cannot be set programmatically."
212
+ )
213
+ # Type checking users should handle the string serialization themselves
214
+ # We will accept any type for the list and serialize to str just in case
215
+ if isinstance(value, Iterable) and not isinstance(value, str):
216
+ target_dict[key] = [str(item) for item in value]
217
+ else:
218
+ target_dict[key] = str(value)
219
+
220
+
221
+ def process_query_params(
222
+ query_params: Iterable[tuple[str, str | Iterable[str]]]
223
+ | SupportsKeysAndGetItem[str, str | Iterable[str]],
224
+ ) -> str:
225
+ """Convert query params into a URL-encoded query string."""
226
+ processed_params: dict[str, list[str] | str] = {}
227
+
228
+ if hasattr(query_params, "keys") and hasattr(query_params, "__getitem__"):
229
+ query_params = cast(
230
+ "SupportsKeysAndGetItem[str, str | Iterable[str]]", query_params
231
+ )
232
+ for key in query_params.keys(): # noqa: SIM118
233
+ value = query_params[key]
234
+ _set_item_in_dict(processed_params, key, value)
235
+ else:
236
+ for key, value in query_params:
237
+ if key in processed_params:
238
+ # If the key already exists, we need to accumulate the values.
239
+ if isinstance(value, dict):
240
+ raise StreamlitQueryParamDictValueError(key)
241
+
242
+ current_val = processed_params[key]
243
+ if not isinstance(current_val, list):
244
+ current_val = [current_val]
245
+
246
+ if isinstance(value, Iterable) and not isinstance(value, str):
247
+ current_val.extend([str(item) for item in value])
248
+ else:
249
+ current_val.append(str(value))
250
+
251
+ processed_params[key] = current_val
252
+ else:
253
+ _set_item_in_dict(processed_params, key, value)
254
+
255
+ return parse.urlencode(processed_params, doseq=True)
@@ -16,7 +16,7 @@ from __future__ import annotations
16
16
 
17
17
  import json
18
18
  import pickle
19
- from collections.abc import Iterator, KeysView, MutableMapping
19
+ from collections.abc import Iterator, KeysView, Mapping, MutableMapping
20
20
  from copy import deepcopy
21
21
  from dataclasses import dataclass, field, replace
22
22
  from typing import (
@@ -726,7 +726,7 @@ class SessionState:
726
726
 
727
727
  for payload in payloads:
728
728
  if isinstance(payload, dict):
729
- event_name = payload.get("event")
729
+ event_name = cast("Mapping[str, object]", payload).get("event")
730
730
  if isinstance(event_name, str) and metadata.callbacks:
731
731
  cb = metadata.callbacks.get(event_name)
732
732
  if cb is not None:
@@ -37,7 +37,7 @@
37
37
  <script>
38
38
  window.prerenderReady = false
39
39
  </script>
40
- <script type="module" crossorigin src="./static/js/index.CAj-7vWz.js"></script>
40
+ <script type="module" crossorigin src="./static/js/index.DMxc2XFp.js"></script>
41
41
  <link rel="stylesheet" crossorigin href="./static/css/index.BpABIXK9.css">
42
42
  </head>
43
43
  <body>