streamlit 1.53.0__py3-none-any.whl → 1.54.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 (311) hide show
  1. streamlit/__init__.py +1 -31
  2. streamlit/auth_util.py +91 -2
  3. streamlit/cli_util.py +3 -2
  4. streamlit/commands/echo.py +2 -2
  5. streamlit/commands/execution_control.py +1 -1
  6. streamlit/commands/logo.py +76 -24
  7. streamlit/commands/navigation.py +1 -1
  8. streamlit/components/types/base_custom_component.py +0 -2
  9. streamlit/components/v1/custom_component.py +0 -2
  10. streamlit/components/v2/bidi_component/main.py +2 -2
  11. streamlit/components/v2/component_path_utils.py +17 -29
  12. streamlit/components/v2/manifest_scanner.py +8 -3
  13. streamlit/components/v2/presentation.py +1 -1
  14. streamlit/config.py +57 -13
  15. streamlit/config_util.py +5 -5
  16. streamlit/connections/snowflake_connection.py +6 -3
  17. streamlit/dataframe_util.py +10 -10
  18. streamlit/deprecation_util.py +19 -1
  19. streamlit/elements/arrow.py +18 -8
  20. streamlit/elements/deck_gl_json_chart.py +6 -2
  21. streamlit/elements/exception.py +4 -2
  22. streamlit/elements/form.py +1 -1
  23. streamlit/elements/layouts.py +1 -1
  24. streamlit/elements/lib/built_in_chart_utils.py +36 -13
  25. streamlit/elements/lib/color_util.py +21 -2
  26. streamlit/elements/lib/column_config_utils.py +9 -7
  27. streamlit/elements/lib/dialog.py +1 -1
  28. streamlit/elements/lib/image_utils.py +5 -5
  29. streamlit/elements/lib/layout_utils.py +1 -1
  30. streamlit/elements/lib/options_selector_utils.py +112 -18
  31. streamlit/elements/lib/policies.py +1 -1
  32. streamlit/elements/lib/streamlit_plotly_theme.py +9 -11
  33. streamlit/elements/lib/utils.py +1 -1
  34. streamlit/elements/map.py +6 -6
  35. streamlit/elements/plotly_chart.py +2 -2
  36. streamlit/elements/toast.py +1 -1
  37. streamlit/elements/vega_charts.py +30 -7
  38. streamlit/elements/widgets/button.py +3 -3
  39. streamlit/elements/widgets/button_group.py +3 -3
  40. streamlit/elements/widgets/chat.py +1 -1
  41. streamlit/elements/widgets/data_editor.py +6 -6
  42. streamlit/elements/widgets/multiselect.py +32 -8
  43. streamlit/elements/widgets/number_input.py +1 -1
  44. streamlit/elements/widgets/radio.py +91 -31
  45. streamlit/elements/widgets/select_slider.py +123 -37
  46. streamlit/elements/widgets/selectbox.py +38 -16
  47. streamlit/elements/widgets/slider.py +5 -5
  48. streamlit/elements/widgets/time_widgets.py +150 -18
  49. streamlit/elements/write.py +2 -3
  50. streamlit/env_util.py +1 -1
  51. streamlit/errors.py +2 -14
  52. streamlit/external/langchain/streamlit_callback_handler.py +1 -1
  53. streamlit/hello/dataframe_demo.py +1 -1
  54. streamlit/hello/plotting_demo.py +19 -12
  55. streamlit/path_security.py +98 -0
  56. streamlit/proto/Alert_pb2.py +2 -3
  57. streamlit/proto/AppPage_pb2.py +2 -3
  58. streamlit/proto/ArrowData_pb2.py +2 -3
  59. streamlit/proto/ArrowNamedDataSet_pb2.py +2 -3
  60. streamlit/proto/ArrowVegaLiteChart_pb2.py +2 -3
  61. streamlit/proto/Arrow_pb2.py +2 -3
  62. streamlit/proto/AudioInput_pb2.py +2 -3
  63. streamlit/proto/Audio_pb2.py +2 -3
  64. streamlit/proto/AuthRedirect_pb2.py +2 -3
  65. streamlit/proto/AutoRerun_pb2.py +2 -3
  66. streamlit/proto/BackMsg_pb2.py +2 -3
  67. streamlit/proto/Balloons_pb2.py +2 -3
  68. streamlit/proto/BidiComponent_pb2.py +2 -3
  69. streamlit/proto/Block_pb2.py +2 -3
  70. streamlit/proto/BokehChart_pb2.py +2 -3
  71. streamlit/proto/ButtonGroup_pb2.py +2 -3
  72. streamlit/proto/ButtonLikeIconPosition_pb2.py +2 -3
  73. streamlit/proto/Button_pb2.py +2 -3
  74. streamlit/proto/CameraInput_pb2.py +2 -3
  75. streamlit/proto/ChatInput_pb2.py +2 -3
  76. streamlit/proto/Checkbox_pb2.py +2 -3
  77. streamlit/proto/ClientState_pb2.py +2 -3
  78. streamlit/proto/Code_pb2.py +2 -3
  79. streamlit/proto/ColorPicker_pb2.py +2 -3
  80. streamlit/proto/Common_pb2.py +2 -3
  81. streamlit/proto/Components_pb2.py +2 -3
  82. streamlit/proto/DataFrame_pb2.py +2 -3
  83. streamlit/proto/DateInput_pb2.py +2 -3
  84. streamlit/proto/DateTimeInput_pb2.py +2 -3
  85. streamlit/proto/DeckGlJsonChart_pb2.py +2 -3
  86. streamlit/proto/Delta_pb2.py +2 -3
  87. streamlit/proto/DocString_pb2.py +2 -3
  88. streamlit/proto/DownloadButton_pb2.py +2 -3
  89. streamlit/proto/Element_pb2.py +2 -3
  90. streamlit/proto/Empty_pb2.py +2 -3
  91. streamlit/proto/Exception_pb2.py +2 -3
  92. streamlit/proto/Favicon_pb2.py +2 -3
  93. streamlit/proto/FileUploader_pb2.py +2 -3
  94. streamlit/proto/ForwardMsg_pb2.py +2 -3
  95. streamlit/proto/GapSize_pb2.py +2 -3
  96. streamlit/proto/GitInfo_pb2.py +2 -3
  97. streamlit/proto/GraphVizChart_pb2.py +2 -3
  98. streamlit/proto/Heading_pb2.py +2 -3
  99. streamlit/proto/HeightConfig_pb2.py +2 -3
  100. streamlit/proto/Html_pb2.py +2 -3
  101. streamlit/proto/IFrame_pb2.py +2 -3
  102. streamlit/proto/Image_pb2.py +2 -3
  103. streamlit/proto/Json_pb2.py +2 -3
  104. streamlit/proto/LabelVisibilityMessage_pb2.py +2 -3
  105. streamlit/proto/LinkButton_pb2.py +2 -3
  106. streamlit/proto/Logo_pb2.py +6 -5
  107. streamlit/proto/Logo_pb2.pyi +25 -1
  108. streamlit/proto/Markdown_pb2.py +2 -3
  109. streamlit/proto/Metric_pb2.py +2 -3
  110. streamlit/proto/MetricsEvent_pb2.py +2 -3
  111. streamlit/proto/MultiSelect_pb2.py +2 -3
  112. streamlit/proto/NamedDataSet_pb2.py +2 -3
  113. streamlit/proto/Navigation_pb2.py +2 -3
  114. streamlit/proto/NewSession_pb2.py +25 -24
  115. streamlit/proto/NewSession_pb2.pyi +28 -2
  116. streamlit/proto/NumberInput_pb2.py +2 -3
  117. streamlit/proto/PageConfig_pb2.py +2 -3
  118. streamlit/proto/PageInfo_pb2.py +2 -3
  119. streamlit/proto/PageLink_pb2.py +2 -3
  120. streamlit/proto/PageNotFound_pb2.py +2 -3
  121. streamlit/proto/PageProfile_pb2.py +2 -3
  122. streamlit/proto/PagesChanged_pb2.py +2 -3
  123. streamlit/proto/ParentMessage_pb2.py +2 -3
  124. streamlit/proto/PlotlyChart_pb2.py +2 -3
  125. streamlit/proto/Progress_pb2.py +2 -3
  126. streamlit/proto/Radio_pb2.py +5 -4
  127. streamlit/proto/Radio_pb2.pyi +20 -3
  128. streamlit/proto/RootContainer_pb2.py +2 -3
  129. streamlit/proto/Selectbox_pb2.py +2 -3
  130. streamlit/proto/SessionEvent_pb2.py +2 -3
  131. streamlit/proto/SessionStatus_pb2.py +2 -3
  132. streamlit/proto/Skeleton_pb2.py +2 -3
  133. streamlit/proto/Slider_pb2.py +7 -8
  134. streamlit/proto/Slider_pb2.pyi +9 -1
  135. streamlit/proto/Snow_pb2.py +2 -3
  136. streamlit/proto/Space_pb2.py +2 -3
  137. streamlit/proto/Spinner_pb2.py +2 -3
  138. streamlit/proto/TextAlignmentConfig_pb2.py +2 -3
  139. streamlit/proto/TextArea_pb2.py +2 -3
  140. streamlit/proto/TextInput_pb2.py +2 -3
  141. streamlit/proto/Text_pb2.py +2 -3
  142. streamlit/proto/TimeInput_pb2.py +2 -3
  143. streamlit/proto/Toast_pb2.py +2 -3
  144. streamlit/proto/Transient_pb2.py +2 -3
  145. streamlit/proto/VegaLiteChart_pb2.py +2 -3
  146. streamlit/proto/Video_pb2.py +2 -3
  147. streamlit/proto/WidgetStates_pb2.py +2 -3
  148. streamlit/proto/WidthConfig_pb2.py +2 -3
  149. streamlit/proto/openmetrics_data_model_pb2.py +2 -3
  150. streamlit/runtime/app_session.py +106 -60
  151. streamlit/runtime/caching/cache_data_api.py +3 -3
  152. streamlit/runtime/caching/cache_errors.py +0 -2
  153. streamlit/runtime/caching/cache_resource_api.py +1 -1
  154. streamlit/runtime/caching/cache_utils.py +2 -2
  155. streamlit/runtime/caching/hashing.py +1 -3
  156. streamlit/runtime/caching/storage/cache_storage_protocol.py +0 -3
  157. streamlit/runtime/connection_factory.py +1 -1
  158. streamlit/runtime/credentials.py +2 -2
  159. streamlit/runtime/metrics_util.py +3 -3
  160. streamlit/runtime/runtime.py +6 -6
  161. streamlit/runtime/scriptrunner/script_runner.py +17 -0
  162. streamlit/runtime/scriptrunner_utils/exceptions.py +0 -4
  163. streamlit/runtime/scriptrunner_utils/script_run_context.py +13 -31
  164. streamlit/runtime/secrets.py +3 -4
  165. streamlit/runtime/state/__init__.py +7 -1
  166. streamlit/runtime/state/common.py +13 -0
  167. streamlit/runtime/state/query_params.py +493 -24
  168. streamlit/runtime/state/session_state.py +179 -4
  169. streamlit/runtime/state/widgets.py +26 -1
  170. streamlit/runtime/stats.py +1 -10
  171. streamlit/static/index.html +1 -1
  172. streamlit/static/manifest.json +304 -304
  173. streamlit/static/static/js/{ErrorOutline.esm.Cxoit62D.js → ErrorOutline.esm.BWk6F-Tz.js} +1 -1
  174. streamlit/static/static/js/{FileDownload.esm.Cym2KVOR.js → FileDownload.esm.AllYUuOW.js} +1 -1
  175. streamlit/static/static/js/{FileHelper.C47VLeXF.js → FileHelper.BvVTNdmy.js} +1 -1
  176. streamlit/static/static/js/{FormClearHelper.CUrwwEeX.js → FormClearHelper.C__r5Llk.js} +1 -1
  177. streamlit/static/static/js/{InputInstructions.DyVOE42q.js → InputInstructions.DOtkdOMV.js} +1 -1
  178. streamlit/static/static/js/Particles.DCsqQZlE.js +1 -0
  179. streamlit/static/static/js/{ProgressBar.qKdiDYyx.js → ProgressBar.DLCRvt4m.js} +2 -2
  180. streamlit/static/static/js/{StreamlitSyntaxHighlighter.DUPp9dS3.js → StreamlitSyntaxHighlighter.CYFWoZHb.js} +1 -1
  181. streamlit/static/static/js/{TableChart.esm.C_g2CvCE.js → TableChart.esm.D6ydHcIm.js} +1 -1
  182. streamlit/static/static/js/Toolbar.BHDNzWBx.js +1 -0
  183. streamlit/static/static/js/{WidgetLabelHelpIconInline.Dy4yV6I2.js → WidgetLabelHelpIconInline.DEXBrVlc.js} +1 -1
  184. streamlit/static/static/js/{base-input.DQAb60v0.js → base-input.TSQjctlq.js} +4 -4
  185. streamlit/static/static/js/{checkbox.C0HE0ojW.js → checkbox.BKgfzJZV.js} +1 -1
  186. streamlit/static/static/js/{createDownloadLinkElement.DBMfH8_e.js → createDownloadLinkElement.CG7nr2a4.js} +1 -1
  187. streamlit/static/static/js/{data-grid-overlay-editor.CSZWem5Q.js → data-grid-overlay-editor.ChXO__lP.js} +1 -1
  188. streamlit/static/static/js/{downloader.Bp8c7mYD.js → downloader.DJ3R_zWA.js} +1 -1
  189. streamlit/static/static/js/embed.u3PPfLkw.js +193 -0
  190. streamlit/static/static/js/{es6.j7akTCaI.js → es6.C5Mfy8nd.js} +2 -2
  191. streamlit/static/static/js/{formatNumber.CfuUiEpF.js → formatNumber.CMRgW9EJ.js} +1 -1
  192. streamlit/static/static/js/{iconPosition.BVSTKfGd.js → iconPosition.B4EEXI3E.js} +1 -1
  193. streamlit/static/static/js/{iframeResizer.contentWindow.BZ3lugzo.js → iframeResizer.contentWindow.WSvOiTW0.js} +1 -1
  194. streamlit/static/static/js/index.-FOBV3nz.js +1 -0
  195. streamlit/static/static/js/{index.D0tXFTaW.js → index.-NF8OSF5.js} +1 -1
  196. streamlit/static/static/js/{index.Dk0CU4R6.js → index.4cBg8kn5.js} +1 -1
  197. streamlit/static/static/js/{index.DtZTtufl.js → index.B0pzzCsH.js} +1 -1
  198. streamlit/static/static/js/{index.DSaE74nc.js → index.BID6ND5j.js} +2 -2
  199. streamlit/static/static/js/index.BMp5bGjh.js +1 -0
  200. streamlit/static/static/js/{index.CAMxgVFm.js → index.BQcmlvas.js} +1 -1
  201. streamlit/static/static/js/{index.C0F0G-wg.js → index.BRcmclgI.js} +1 -1
  202. streamlit/static/static/js/index.BaUZR4IG.js +1 -0
  203. streamlit/static/static/js/{index.Cow0Hs9V.js → index.BbMJj4PN.js} +1 -1
  204. streamlit/static/static/js/{index.iboGgrkh.js → index.BdCTJtq3.js} +2 -2
  205. streamlit/static/static/js/index.BdETLMuI.js +1 -0
  206. streamlit/static/static/js/index.BnKMWhs1.js +1 -0
  207. streamlit/static/static/js/index.Br1kXwQW.js +2 -0
  208. streamlit/static/static/js/{index.B2fTHpId.js → index.Bt2olRE4.js} +1 -1
  209. streamlit/static/static/js/{index.DBIRzFM7.js → index.Bxwsv5T8.js} +1 -1
  210. streamlit/static/static/js/index.C4KskYz6.js +1 -0
  211. streamlit/static/static/js/{index.BgCYNmov.js → index.C6bmbXk0.js} +1 -1
  212. streamlit/static/static/js/{index.7S_sCSRx.js → index.CEfKfbta.js} +1 -1
  213. streamlit/static/static/js/index.CIuaA8q0.js +2 -0
  214. streamlit/static/static/js/{index.CWAvu1Qu.js → index.CV1sObFX.js} +1 -1
  215. streamlit/static/static/js/{index.C9QftD-S.js → index.CbR6dgaV.js} +1 -1
  216. streamlit/static/static/js/index.Cq6szKqJ.js +1 -0
  217. streamlit/static/static/js/index.CyouXqCz.js +1 -0
  218. streamlit/static/static/js/{index.BMFt07G_.js → index.D1NUgMFI.js} +1 -1
  219. streamlit/static/static/js/{index.Tq2okoAU.js → index.D7SWG4Om.js} +1 -1
  220. streamlit/static/static/js/{index.DgJeIFb5.js → index.DAYPEwLI.js} +1 -1
  221. streamlit/static/static/js/index.DKS75Vfg.js +11 -0
  222. streamlit/static/static/js/{index.FfR9SXQv.js → index.DOXrMIxB.js} +1 -1
  223. streamlit/static/static/js/{index.BiVJWMS-.js → index.DOzYX8yS.js} +3 -3
  224. streamlit/static/static/js/{index.nEa8y_He.js → index.DRFMYcC4.js} +4 -4
  225. streamlit/static/static/js/{index.dgs1TGpP.js → index.Divl5FCY.js} +1 -1
  226. streamlit/static/static/js/{index.95DldRtG.js → index.DjAJ_CUa.js} +1 -1
  227. streamlit/static/static/js/{index.Z0mB4zBp.js → index.Dncue2pm.js} +33 -33
  228. streamlit/static/static/js/{index.DFT9nVK6.js → index.Drusyo5m.js} +48 -48
  229. streamlit/static/static/js/{index.1PD6f3vh.js → index.DuUyDGnP.js} +1 -1
  230. streamlit/static/static/js/{index.DpU0Bc2F.js → index.DvgT2rB2.js} +223 -223
  231. streamlit/static/static/js/{index.Bukztsaz.js → index.DzutABu5.js} +2 -2
  232. streamlit/static/static/js/index.Dzw2iPzi.js +3 -0
  233. streamlit/static/static/js/{index.DYkkO_of.js → index.FsTmxLbT.js} +1 -1
  234. streamlit/static/static/js/{index.CTQ8QcOV.js → index.OIwPqGYN.js} +1 -1
  235. streamlit/static/static/js/{index.NtSfVVJe.js → index.RXLN7YFT.js} +2 -2
  236. streamlit/static/static/js/{index.BU3d_gp1.js → index.YYb2u0jk.js} +2 -2
  237. streamlit/static/static/js/{index.BXfSsjdq.js → index.h8ejt-W3.js} +1 -1
  238. streamlit/static/static/js/{index.gPUFpUqs.js → index.lFMCi9am.js} +1 -1
  239. streamlit/static/static/js/{index.BDA5l7b9.js → index.pOgf4cEj.js} +1 -1
  240. streamlit/static/static/js/index.s_E0s7LB.js +188 -0
  241. streamlit/static/static/js/{index.DysJZEAt.js → index.xLCbzoqj.js} +1 -1
  242. streamlit/static/static/js/{input.Pz8Lwzsi.js → input.BLG7kWaj.js} +2 -2
  243. streamlit/static/static/js/{main.BeiYkHRo.js → main.D_CmqChN.js} +1 -1
  244. streamlit/static/static/js/{memory.Dyx_JBbb.js → memory.T8u9KqIQ.js} +1 -1
  245. streamlit/static/static/js/{number-overlay-editor.NLIdF6b9.js → number-overlay-editor.BKBSXkAM.js} +2 -2
  246. streamlit/static/static/js/{pandasStylerUtils.DsNlDEqS.js → pandasStylerUtils.B4tLYMwS.js} +1 -1
  247. streamlit/static/static/js/{sandbox.bER7qtR1.js → sandbox.jRlkcPem.js} +1 -1
  248. streamlit/static/static/js/{styled-components.DcoFBb7G.js → styled-components.D2QhNwzd.js} +1 -1
  249. streamlit/static/static/js/{throttle.DOaQWO4U.js → throttle.Cyw_V0Dq.js} +1 -1
  250. streamlit/static/static/js/{timepicker.RjHB2IT4.js → timepicker.PzyuDDWl.js} +1 -1
  251. streamlit/static/static/js/{toConsumableArray.DFAIugL0.js → toConsumableArray.gE9fMkLj.js} +1 -1
  252. streamlit/static/static/js/uniqueId.B1GeHnT1.js +1 -0
  253. streamlit/static/static/js/{useBasicWidgetState.CTtyymrp.js → useBasicWidgetState.DFklfao0.js} +1 -1
  254. streamlit/static/static/js/{useIntlLocale.DG5haQGX.js → useIntlLocale.C3tUGWTU.js} +8 -8
  255. streamlit/static/static/js/{useTextInputAutoExpand.Cnfcep1Z.js → useTextInputAutoExpand.D9nU_y-e.js} +1 -1
  256. streamlit/static/static/js/useUpdateUiValue.ClTdrkJN.js +1 -0
  257. streamlit/static/static/js/{useWaveformController.DozaayUB.js → useWaveformController.lzTbjMW2.js} +1 -1
  258. streamlit/static/static/js/{withCalculatedWidth.SNNFFxhJ.js → withCalculatedWidth.Dxs9I5Oe.js} +1 -1
  259. streamlit/static/static/js/{withFullScreenWrapper.Dl2f8_gt.js → withFullScreenWrapper.DfpAcJxf.js} +1 -1
  260. streamlit/string_util.py +2 -2
  261. streamlit/testing/v1/app_test.py +1 -1
  262. streamlit/testing/v1/element_tree.py +33 -20
  263. streamlit/type_util.py +2 -2
  264. streamlit/url_util.py +2 -2
  265. streamlit/user_info.py +2 -41
  266. streamlit/util.py +1 -1
  267. streamlit/watcher/event_based_path_watcher.py +37 -7
  268. streamlit/watcher/path_watcher.py +61 -2
  269. streamlit/watcher/util.py +26 -10
  270. streamlit/web/bootstrap.py +16 -4
  271. streamlit/web/cli.py +1 -4
  272. streamlit/web/server/app_discovery.py +2 -1
  273. streamlit/web/server/app_static_file_handler.py +9 -0
  274. streamlit/web/server/bidi_component_request_handler.py +4 -4
  275. streamlit/web/server/component_file_utils.py +14 -6
  276. streamlit/web/server/component_request_handler.py +2 -2
  277. streamlit/web/server/oauth_authlib_routes.py +14 -42
  278. streamlit/web/server/server.py +1 -1
  279. streamlit/web/server/server_util.py +23 -1
  280. streamlit/web/server/starlette/starlette_app.py +7 -1
  281. streamlit/web/server/starlette/starlette_auth_routes.py +94 -16
  282. streamlit/web/server/starlette/starlette_path_security_middleware.py +97 -0
  283. streamlit/web/server/starlette/starlette_routes.py +16 -9
  284. streamlit/web/server/starlette/starlette_server.py +2 -2
  285. streamlit/web/server/starlette/starlette_static_routes.py +14 -4
  286. streamlit/web/server/stats_request_handler.py +1 -3
  287. {streamlit-1.53.0.dist-info → streamlit-1.54.0.dist-info}/METADATA +10 -25
  288. {streamlit-1.53.0.dist-info → streamlit-1.54.0.dist-info}/RECORD +291 -291
  289. {streamlit-1.53.0.dist-info → streamlit-1.54.0.dist-info}/WHEEL +1 -1
  290. streamlit/commands/experimental_query_params.py +0 -169
  291. streamlit/static/static/js/Particles.D5ZUTvE6.js +0 -1
  292. streamlit/static/static/js/Toolbar.BbO8bxwz.js +0 -1
  293. streamlit/static/static/js/embed.DQBlGL9Q.js +0 -195
  294. streamlit/static/static/js/index.5CsPRetw.js +0 -1
  295. streamlit/static/static/js/index.BGgra9Bb.js +0 -188
  296. streamlit/static/static/js/index.BGzJYcHz.js +0 -1
  297. streamlit/static/static/js/index.BNpEDrb2.js +0 -1
  298. streamlit/static/static/js/index.Bk5wGJXh.js +0 -1
  299. streamlit/static/static/js/index.By8GIgDH.js +0 -1
  300. streamlit/static/static/js/index.C8VoW8Ph.js +0 -1
  301. streamlit/static/static/js/index.CZzy-Gct.js +0 -1
  302. streamlit/static/static/js/index.CeFdbzfR.js +0 -11
  303. streamlit/static/static/js/index.CkmNfvPD.js +0 -1
  304. streamlit/static/static/js/index.CsmTnJl4.js +0 -3
  305. streamlit/static/static/js/index.DZGCJu4I.js +0 -2
  306. streamlit/static/static/js/index.svncz-Ad.js +0 -2
  307. streamlit/static/static/js/uniqueId.DEvFPH9n.js +0 -1
  308. streamlit/static/static/js/useUpdateUiValue.BWnXwmrp.js +0 -1
  309. streamlit-1.53.0.data/scripts/streamlit.cmd +0 -16
  310. {streamlit-1.53.0.dist-info → streamlit-1.54.0.dist-info}/entry_points.txt +0 -0
  311. {streamlit-1.53.0.dist-info → streamlit-1.54.0.dist-info}/top_level.txt +0 -0
@@ -14,7 +14,6 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- from dataclasses import dataclass
18
17
  from textwrap import dedent
19
18
  from typing import TYPE_CHECKING, Any, Generic, TypeVar, cast, overload
20
19
 
@@ -27,7 +26,11 @@ from streamlit.elements.lib.layout_utils import (
27
26
  Width,
28
27
  validate_width,
29
28
  )
30
- from streamlit.elements.lib.options_selector_utils import index_, maybe_coerce_enum
29
+ from streamlit.elements.lib.options_selector_utils import (
30
+ create_mappings,
31
+ maybe_coerce_enum,
32
+ validate_and_sync_value_with_options,
33
+ )
31
34
  from streamlit.elements.lib.policies import (
32
35
  check_widget_policies,
33
36
  maybe_raise_label_warnings,
@@ -63,27 +66,70 @@ if TYPE_CHECKING:
63
66
  T = TypeVar("T")
64
67
 
65
68
 
66
- @dataclass
67
69
  class RadioSerde(Generic[T]):
70
+ """Serializer/deserializer for Radio widget values.
71
+
72
+ Uses string-based values (formatted option strings) for robust handling
73
+ of dynamic option changes, similar to SelectboxSerde.
74
+ """
75
+
68
76
  options: Sequence[T]
69
- index: int | None
77
+ formatted_options: list[str]
78
+ formatted_option_to_option_index: dict[str, int]
79
+ default_option_index: int | None
80
+ format_func: Callable[[Any], str]
70
81
 
71
- def serialize(self, v: object) -> int | None:
82
+ def __init__(
83
+ self,
84
+ options: Sequence[T],
85
+ *,
86
+ formatted_options: list[str],
87
+ formatted_option_to_option_index: dict[str, int],
88
+ default_option_index: int | None = None,
89
+ format_func: Callable[[Any], str] = str,
90
+ ) -> None:
91
+ self.options = options
92
+ self.formatted_options = formatted_options
93
+ self.formatted_option_to_option_index = formatted_option_to_option_index
94
+ self.default_option_index = default_option_index
95
+ self.format_func = format_func
96
+
97
+ def serialize(self, v: T | str | None) -> str | None:
72
98
  if v is None:
73
99
  return None
100
+ if len(self.options) == 0:
101
+ return None
74
102
 
75
- return 0 if len(self.options) == 0 else index_(self.options, v)
103
+ # Use format_func to find the formatted option instead of using
104
+ # index_(self.options, v) which relies on == comparison. This is necessary
105
+ # because widget values are deepcopied, and for custom classes without
106
+ # __eq__, the deepcopied instances would fail identity comparison.
107
+ try:
108
+ formatted_value = self.format_func(v)
109
+ except Exception:
110
+ # format_func failed (e.g., v is a string but format_func expects
111
+ # an object with specific attributes). Treat v as a raw string.
112
+ return cast("str", v)
113
+
114
+ if formatted_value in self.formatted_option_to_option_index:
115
+ return formatted_value
116
+ # Value not found in options - return as raw string
117
+ return cast("str", v)
118
+
119
+ def deserialize(self, ui_value: str | None) -> T | str | None:
120
+ # If no options, there's no valid value - return None
121
+ if len(self.options) == 0:
122
+ return None
76
123
 
77
- def deserialize(self, ui_value: int | None) -> T | None:
78
- idx = ui_value if ui_value is not None else self.index
124
+ if ui_value is None:
125
+ return (
126
+ self.options[self.default_option_index]
127
+ if self.default_option_index is not None
128
+ else None
129
+ )
79
130
 
80
- return (
81
- self.options[idx]
82
- if idx is not None
83
- and len(self.options) > 0
84
- and self.options[idx] is not None
85
- else None
86
- )
131
+ option_index = self.formatted_option_to_option_index.get(ui_value)
132
+ return self.options[option_index] if option_index is not None else ui_value
87
133
 
88
134
 
89
135
  class RadioMixin:
@@ -368,18 +414,17 @@ class RadioMixin:
368
414
  opt = convert_anything_to_list(options)
369
415
  check_python_comparable(opt)
370
416
 
417
+ formatted_options, formatted_option_to_option_index = create_mappings(
418
+ opt, format_func
419
+ )
420
+
371
421
  element_id = compute_and_register_element_id(
372
422
  "radio",
373
423
  user_key=key,
374
- # Treat provided key as the main widget identity. Only include the
375
- # following parameters in the identity computation since they can
376
- # invalidate the current selection mapping.
377
- # Changes to format_func also invalidate the current selection,
378
- # but this is already handled via the `options` parameter below:
379
- key_as_main_identity={"options"},
424
+ key_as_main_identity=True,
380
425
  dg=self.dg,
381
426
  label=label,
382
- options=[str(format_func(option)) for option in opt],
427
+ options=formatted_options,
383
428
  index=index,
384
429
  help=help,
385
430
  horizontal=horizontal,
@@ -415,7 +460,7 @@ class RadioMixin:
415
460
  radio_proto.label = label
416
461
  if index is not None:
417
462
  radio_proto.default = index
418
- radio_proto.options[:] = [str(format_func(option)) for option in opt]
463
+ radio_proto.options[:] = formatted_options
419
464
  radio_proto.form_id = current_form_id(self.dg)
420
465
  radio_proto.horizontal = horizontal
421
466
  radio_proto.disabled = disabled
@@ -429,7 +474,13 @@ class RadioMixin:
429
474
  if help is not None:
430
475
  radio_proto.help = dedent(help)
431
476
 
432
- serde = RadioSerde(opt, index)
477
+ serde = RadioSerde(
478
+ opt,
479
+ formatted_options=formatted_options,
480
+ formatted_option_to_option_index=formatted_option_to_option_index,
481
+ default_option_index=index,
482
+ format_func=format_func,
483
+ )
433
484
 
434
485
  widget_state = register_widget(
435
486
  radio_proto.id,
@@ -439,21 +490,30 @@ class RadioMixin:
439
490
  deserializer=serde.deserialize,
440
491
  serializer=serde.serialize,
441
492
  ctx=ctx,
442
- value_type="int_value",
493
+ value_type="string_value",
443
494
  )
444
495
  widget_state = maybe_coerce_enum(widget_state, options, opt)
445
496
 
446
- if widget_state.value_changed:
447
- if widget_state.value is not None:
448
- serialized_value = serde.serialize(widget_state.value)
449
- if serialized_value is not None:
450
- radio_proto.value = serialized_value
497
+ # Validate the current value against the new options.
498
+ # If the value is no longer valid (not in options), reset to default.
499
+ # This handles the case where options change dynamically and the
500
+ # previously selected value is no longer available.
501
+ # Cast to T | None since radio doesn't support accept_new_options,
502
+ # so string values that aren't in options will be reset to default.
503
+ current_value, value_needs_reset = validate_and_sync_value_with_options(
504
+ cast("T | None", widget_state.value), opt, index, key
505
+ )
506
+
507
+ if value_needs_reset or widget_state.value_changed:
508
+ serialized_value = serde.serialize(current_value)
509
+ if serialized_value is not None:
510
+ radio_proto.raw_value = serialized_value
451
511
  radio_proto.set_value = True
452
512
 
453
513
  if ctx:
454
514
  save_for_app_testing(ctx, element_id, format_func)
455
515
  self.dg._enqueue("radio", radio_proto, layout_config=layout_config)
456
- return widget_state.value
516
+ return current_value
457
517
 
458
518
  @property
459
519
  def dg(self) -> DeltaGenerator:
@@ -14,7 +14,6 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- from dataclasses import dataclass
18
17
  from textwrap import dedent
19
18
  from typing import (
20
19
  TYPE_CHECKING,
@@ -30,9 +29,12 @@ from streamlit.dataframe_util import OptionSequence, convert_anything_to_list
30
29
  from streamlit.elements.lib.form_utils import current_form_id
31
30
  from streamlit.elements.lib.layout_utils import LayoutConfig, validate_width
32
31
  from streamlit.elements.lib.options_selector_utils import (
32
+ create_mappings,
33
33
  index_,
34
34
  maybe_coerce_enum,
35
35
  maybe_coerce_enum_sequence,
36
+ validate_and_sync_range_value_with_options,
37
+ validate_and_sync_value_with_options,
36
38
  )
37
39
  from streamlit.elements.lib.policies import (
38
40
  check_widget_policies,
@@ -72,37 +74,79 @@ def _is_range_value(value: T | Sequence[T]) -> TypeGuard[Sequence[T]]:
72
74
  return isinstance(value, (list, tuple))
73
75
 
74
76
 
75
- @dataclass
76
77
  class SelectSliderSerde(Generic[T]):
77
- options: Sequence[T]
78
- value: list[int]
79
- is_range_value: bool
78
+ """Serializer/deserializer for select_slider widget values.
80
79
 
81
- def serialize(self, v: object) -> list[int]:
82
- return self._as_index_list(v)
80
+ Uses formatted option strings for robust handling of dynamic option changes.
81
+ """
83
82
 
84
- def deserialize(self, ui_value: list[int] | None) -> T | tuple[T, T]:
85
- if not ui_value:
86
- # Widget has not been used; fallback to the original value,
87
- ui_value = self.value
83
+ def __init__(
84
+ self,
85
+ options: Sequence[T],
86
+ *,
87
+ formatted_option_to_index: dict[str, int],
88
+ default_indices: list[int],
89
+ format_func: Callable[[Any], str] = str,
90
+ ) -> None:
91
+ self.options = options
92
+ self.formatted_option_to_index = formatted_option_to_index
93
+ self.default_indices = default_indices
94
+ self.format_func = format_func
95
+
96
+ def _get_default(self, is_range: bool) -> T | tuple[T, T]:
97
+ """Return the default value based on default_indices."""
98
+ if is_range or len(self.default_indices) >= 2:
99
+ end_idx = (
100
+ self.default_indices[1]
101
+ if len(self.default_indices) > 1
102
+ else len(self.options) - 1
103
+ )
104
+ return (self.options[self.default_indices[0]], self.options[end_idx])
105
+ return self.options[self.default_indices[0]]
88
106
 
89
- # The widget always returns floats, so convert to ints before indexing
90
- return_value: tuple[T, T] = cast(
91
- "tuple[T, T]",
92
- tuple(self.options[int(x)] for x in ui_value),
93
- )
107
+ def serialize(self, v: T | tuple[T, T] | list[T]) -> list[str]:
108
+ """Convert option value(s) to formatted string list."""
109
+ # Check if v is a single option (handles options that are tuples/lists)
110
+ try:
111
+ formatted = self.format_func(v)
112
+ if formatted in self.formatted_option_to_index:
113
+ return [formatted]
114
+ except Exception: # noqa: S110
115
+ pass
94
116
 
95
- # If the original value was a list/tuple, so will be the output (and vice versa)
96
- return return_value if self.is_range_value else return_value[0]
117
+ # Handle as range/sequence
118
+ if isinstance(v, (tuple, list)):
119
+ return [self.format_func(x) for x in v]
97
120
 
98
- def _as_index_list(self, v: Any) -> list[int]:
99
- if _is_range_value(v):
100
- slider_value = [index_(self.options, val) for val in v]
101
- start, end = slider_value
102
- if start > end:
103
- slider_value = [end, start]
104
- return slider_value
105
- return [index_(self.options, v)]
121
+ return [self.format_func(v)]
122
+
123
+ def deserialize(self, ui_value: list[str] | None) -> T | tuple[T, T]:
124
+ """Convert formatted string list back to option value(s)."""
125
+ is_range = ui_value is not None and len(ui_value) >= 2
126
+
127
+ if not ui_value:
128
+ return self._get_default(is_range=len(self.default_indices) >= 2)
129
+
130
+ # Look up each string value
131
+ results: list[tuple[int, T]] = []
132
+ for i, s in enumerate(ui_value):
133
+ idx = self.formatted_option_to_index.get(s)
134
+ if idx is not None and idx < len(self.options):
135
+ results.append((idx, self.options[idx]))
136
+ else:
137
+ # Fallback to default for this position
138
+ default_idx = self.default_indices[
139
+ min(i, len(self.default_indices) - 1)
140
+ ]
141
+ results.append((default_idx, self.options[default_idx]))
142
+
143
+ if is_range and len(results) >= 2:
144
+ # Ensure start <= end by returning deserialized range value in ascending order
145
+ if results[0][0] > results[1][0]:
146
+ return (results[1][1], results[0][1])
147
+ return (results[0][1], results[1][1])
148
+
149
+ return results[0][1]
106
150
 
107
151
 
108
152
  class SelectSliderMixin:
@@ -378,16 +422,18 @@ class SelectSliderMixin:
378
422
  # Convert element to index of the elements
379
423
  slider_value = as_index_list(value)
380
424
 
425
+ # Create formatted options and mapping for string-based storage
426
+ formatted_options, formatted_option_to_option_index = create_mappings(
427
+ opt, format_func
428
+ )
429
+
381
430
  element_id = compute_and_register_element_id(
382
431
  "select_slider",
383
432
  user_key=key,
384
- # Treat the provided key as the main identity; only include
385
- # changes to the options (and implicitly their formatting) in the
386
- # identity computation as those can invalidate the current value.
387
- key_as_main_identity={"options", "format_func"},
433
+ key_as_main_identity=True,
388
434
  dg=self.dg,
389
435
  label=label,
390
- options=[str(format_func(option)) for option in opt],
436
+ options=formatted_options,
391
437
  value=slider_value,
392
438
  help=help,
393
439
  width=width,
@@ -403,7 +449,7 @@ class SelectSliderMixin:
403
449
  slider_proto.max = len(opt) - 1
404
450
  slider_proto.step = 1 # default for index changes
405
451
  slider_proto.data_type = SliderProto.INT
406
- slider_proto.options[:] = [str(format_func(option)) for option in opt]
452
+ slider_proto.options[:] = formatted_options
407
453
  slider_proto.form_id = current_form_id(self.dg)
408
454
  slider_proto.disabled = disabled
409
455
  slider_proto.label_visibility.value = get_label_visibility_proto_value(
@@ -415,7 +461,12 @@ class SelectSliderMixin:
415
461
  validate_width(width)
416
462
  layout_config = LayoutConfig(width=width)
417
463
 
418
- serde = SelectSliderSerde(opt, slider_value, _is_range_value(value))
464
+ serde = SelectSliderSerde(
465
+ opt,
466
+ formatted_option_to_index=formatted_option_to_option_index,
467
+ default_indices=slider_value,
468
+ format_func=format_func,
469
+ )
419
470
 
420
471
  widget_state = register_widget(
421
472
  slider_proto.id,
@@ -425,7 +476,7 @@ class SelectSliderMixin:
425
476
  deserializer=serde.deserialize,
426
477
  serializer=serde.serialize,
427
478
  ctx=ctx,
428
- value_type="double_array_value",
479
+ value_type="string_array_value",
429
480
  )
430
481
  if isinstance(widget_state.value, tuple):
431
482
  widget_state = maybe_coerce_enum_sequence(
@@ -434,15 +485,50 @@ class SelectSliderMixin:
434
485
  else:
435
486
  widget_state = maybe_coerce_enum(widget_state, options, opt)
436
487
 
437
- if widget_state.value_changed:
438
- slider_proto.value[:] = serde.serialize(widget_state.value)
488
+ # Validate the current value against the new options.
489
+ # If the value is no longer valid (not in options), reset to default.
490
+ # This handles the case where options change dynamically and the
491
+ # previously selected value is no longer available.
492
+ # Determine if we're dealing with a range value based on the actual
493
+ # widget state value, not just the value parameter (range can come from
494
+ # session state even when value param is None).
495
+ actual_is_range = isinstance(widget_state.value, tuple)
496
+ if actual_is_range:
497
+ # Range value: validate using range-specific function.
498
+ range_value = cast("tuple[T, T]", widget_state.value)
499
+ validated_range, value_needs_reset = (
500
+ validate_and_sync_range_value_with_options(
501
+ range_value,
502
+ opt,
503
+ slider_value,
504
+ key,
505
+ format_func,
506
+ )
507
+ )
508
+ current_value: T | tuple[T, T] = validated_range
509
+ else:
510
+ # Single value: use the standard validation function.
511
+ validated_single, value_needs_reset = validate_and_sync_value_with_options(
512
+ widget_state.value,
513
+ opt,
514
+ slider_value[0],
515
+ key,
516
+ format_func,
517
+ )
518
+ # validated_single is guaranteed to be T (not None) because
519
+ # deserialize() always returns a default value, never None.
520
+ current_value = cast("T", validated_single)
521
+
522
+ if value_needs_reset or widget_state.value_changed:
523
+ serialized_value = serde.serialize(current_value)
524
+ slider_proto.raw_value[:] = serialized_value
439
525
  slider_proto.set_value = True
440
526
 
441
527
  if ctx:
442
528
  save_for_app_testing(ctx, element_id, format_func)
443
529
 
444
530
  self.dg._enqueue("slider", slider_proto, layout_config=layout_config)
445
- return widget_state.value
531
+ return current_value
446
532
 
447
533
  @property
448
534
  def dg(self) -> DeltaGenerator:
@@ -35,7 +35,6 @@ from streamlit.elements.lib.layout_utils import (
35
35
  )
36
36
  from streamlit.elements.lib.options_selector_utils import (
37
37
  create_mappings,
38
- index_,
39
38
  maybe_coerce_enum,
40
39
  validate_and_sync_value_with_options,
41
40
  )
@@ -79,6 +78,7 @@ class SelectboxSerde(Generic[T]):
79
78
  formatted_options: list[str]
80
79
  formatted_option_to_option_index: dict[str, int]
81
80
  default_option_index: int | None
81
+ format_func: Callable[[Any], str]
82
82
 
83
83
  def __init__(
84
84
  self,
@@ -87,6 +87,7 @@ class SelectboxSerde(Generic[T]):
87
87
  formatted_options: list[str],
88
88
  formatted_option_to_option_index: dict[str, int],
89
89
  default_option_index: int | None = None,
90
+ format_func: Callable[[Any], str] = str,
90
91
  ) -> None:
91
92
  """Initialize the SelectboxSerde.
92
93
 
@@ -108,33 +109,53 @@ class SelectboxSerde(Generic[T]):
108
109
  default_option_index : int or None, optional
109
110
  The index of the default option to use when no selection is made.
110
111
  If None, no default option is selected.
112
+ format_func : Callable[[Any], str], optional
113
+ Function to format options for comparison. Used to compare values by their
114
+ string representation instead of using == directly. This is necessary because
115
+ widget values are deepcopied, and for custom classes without __eq__, the
116
+ deepcopied instances would fail identity comparison.
111
117
  """
112
118
 
113
119
  self.options = options
114
120
  self.formatted_options = formatted_options
115
121
  self.formatted_option_to_option_index = formatted_option_to_option_index
116
122
  self.default_option_index = default_option_index
123
+ self.format_func = format_func
117
124
 
118
125
  def serialize(self, v: T | str | None) -> str | None:
119
126
  if v is None:
120
127
  return None
121
- if len(self.options) == 0:
122
- return ""
123
-
124
- # we don't check for isinstance(v, str) because this could lead to wrong
125
- # results if v is a string that is part of the options itself as it would
126
- # skip formatting in that case
128
+ # Note: We don't short-circuit for empty options here because
129
+ # accept_new_options=True allows user-entered values even with no options.
130
+ # The normal flow below handles this correctly.
131
+
132
+ # Use format_func to find the formatted option instead of using
133
+ # index_(self.options, v) which relies on == comparison. This is necessary
134
+ # because widget values are deepcopied, and for custom classes without
135
+ # __eq__, the deepcopied instances would fail identity comparison.
127
136
  try:
128
- option_index = index_(self.options, v)
129
- return self.formatted_options[option_index]
130
- except ValueError:
131
- # we know that v is a string, otherwise it would have been found in the
132
- # options
133
- return cast("str", v)
137
+ formatted_value = self.format_func(v)
138
+ except Exception:
139
+ # format_func failed (e.g., v is a string but format_func expects
140
+ # an object with specific attributes). Use str(v) to ensure we return
141
+ # a proper string, not the original object. This handles both cases:
142
+ # - v is already a string -> str(v) returns it unchanged
143
+ # - v is a custom object -> str(v) gives its string representation
144
+ return str(v)
145
+
146
+ if formatted_value in self.formatted_option_to_option_index:
147
+ return formatted_value
148
+ # Value not found in options - return the formatted string (not the original
149
+ # object) to maintain type consistency since serialize() must return str|None
150
+ return formatted_value
134
151
 
135
152
  def deserialize(self, ui_value: str | None) -> T | str | None:
136
- # check if the option is pointing to a generic option type T,
137
- # otherwise return the option itself
153
+ # Note: We don't short-circuit for empty options here because
154
+ # accept_new_options=True allows user-entered values even with no options.
155
+ # The normal flow below handles this: ui_value not in options -> return ui_value.
156
+
157
+ # Check if the option is pointing to a generic option type T,
158
+ # otherwise return the option itself.
138
159
  if ui_value is None:
139
160
  return (
140
161
  self.options[self.default_option_index]
@@ -583,6 +604,7 @@ class SelectboxMixin:
583
604
  formatted_options=formatted_options,
584
605
  formatted_option_to_option_index=formatted_option_to_option_index,
585
606
  default_option_index=index,
607
+ format_func=format_func,
586
608
  )
587
609
  widget_state = register_widget(
588
610
  selectbox_proto.id,
@@ -605,7 +627,7 @@ class SelectboxMixin:
605
627
  # This handles the case where options change dynamically and the
606
628
  # previously selected value is no longer available.
607
629
  current_value, value_needs_reset = validate_and_sync_value_with_options(
608
- widget_state.value, opt, index, key
630
+ widget_state.value, opt, index, key, format_func
609
631
  )
610
632
 
611
633
  if value_needs_reset or widget_state.value_changed:
@@ -739,7 +739,7 @@ class SliderMixin:
739
739
 
740
740
  # Ensure that the value is either a single value or a range of values.
741
741
  single_value = isinstance(value, tuple(SUPPORTED_TYPES.keys()))
742
- range_value = isinstance(value, (list, tuple)) and len(value) in (0, 1, 2)
742
+ range_value = isinstance(value, (list, tuple)) and len(value) in {0, 1, 2}
743
743
  if not single_value and not range_value:
744
744
  raise StreamlitAPIException(
745
745
  "Slider value should either be an int/float/datetime or a list/tuple of "
@@ -778,7 +778,7 @@ class SliderMixin:
778
778
 
779
779
  datetime_min = time.min.replace(tzinfo=prepared_value[0].tzinfo)
780
780
  datetime_max = time.max.replace(tzinfo=prepared_value[0].tzinfo)
781
- if data_type in (SliderProto.DATETIME, SliderProto.DATE):
781
+ if data_type in {SliderProto.DATETIME, SliderProto.DATE}:
782
782
  prepared_value = cast("Sequence[datetime]", prepared_value)
783
783
 
784
784
  datetime_min = prepared_value[0] - timedelta(days=14)
@@ -823,10 +823,10 @@ class SliderMixin:
823
823
  max_value = defaults[data_type]["max_value"]
824
824
  if step is None:
825
825
  step = defaults[data_type]["step"]
826
- if data_type in (
826
+ if data_type in {
827
827
  SliderProto.DATETIME,
828
828
  SliderProto.DATE,
829
- ) and max_value - min_value < timedelta(days=1):
829
+ } and max_value - min_value < timedelta(days=1):
830
830
  step = timedelta(minutes=15)
831
831
  if format is None:
832
832
  format = cast("str", defaults[data_type]["format"]) # noqa: A001
@@ -945,7 +945,7 @@ class SliderMixin:
945
945
  # Restore times/datetimes to original timezone (dates are always naive)
946
946
  orig_tz = (
947
947
  prepared_value[0].tzinfo
948
- if data_type in (SliderProto.TIME, SliderProto.DATETIME)
948
+ if data_type in {SliderProto.TIME, SliderProto.DATETIME}
949
949
  else None
950
950
  )
951
951