streamlit 1.45.1__py3-none-any.whl → 1.46.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 (335) hide show
  1. streamlit/__init__.py +5 -1
  2. streamlit/auth_util.py +12 -12
  3. streamlit/cli_util.py +4 -3
  4. streamlit/column_config.py +11 -9
  5. streamlit/commands/echo.py +6 -4
  6. streamlit/commands/execution_control.py +33 -32
  7. streamlit/commands/experimental_query_params.py +2 -2
  8. streamlit/commands/logo.py +9 -4
  9. streamlit/commands/navigation.py +61 -18
  10. streamlit/commands/page_config.py +57 -47
  11. streamlit/components/types/base_custom_component.py +7 -7
  12. streamlit/components/v1/component_registry.py +7 -3
  13. streamlit/components/v1/components.py +1 -1
  14. streamlit/components/v1/custom_component.py +8 -8
  15. streamlit/config.py +289 -144
  16. streamlit/config_option.py +19 -15
  17. streamlit/config_util.py +29 -23
  18. streamlit/connections/__init__.py +2 -2
  19. streamlit/connections/base_connection.py +5 -5
  20. streamlit/connections/snowflake_connection.py +13 -11
  21. streamlit/connections/snowpark_connection.py +3 -3
  22. streamlit/connections/sql_connection.py +20 -18
  23. streamlit/connections/util.py +2 -2
  24. streamlit/cursor.py +6 -6
  25. streamlit/dataframe_util.py +52 -52
  26. streamlit/delta_generator.py +46 -48
  27. streamlit/delta_generator_singletons.py +3 -3
  28. streamlit/deprecation_util.py +6 -6
  29. streamlit/elements/alert.py +37 -29
  30. streamlit/elements/arrow.py +40 -22
  31. streamlit/elements/code.py +46 -13
  32. streamlit/elements/deck_gl_json_chart.py +38 -27
  33. streamlit/elements/dialog_decorator.py +3 -4
  34. streamlit/elements/doc_string.py +64 -58
  35. streamlit/elements/exception.py +23 -27
  36. streamlit/elements/form.py +41 -0
  37. streamlit/elements/graphviz_chart.py +1 -1
  38. streamlit/elements/heading.py +60 -9
  39. streamlit/elements/html.py +3 -4
  40. streamlit/elements/image.py +8 -9
  41. streamlit/elements/json.py +21 -2
  42. streamlit/elements/layouts.py +120 -31
  43. streamlit/elements/lib/built_in_chart_utils.py +96 -73
  44. streamlit/elements/lib/color_util.py +3 -3
  45. streamlit/elements/lib/column_config_utils.py +2 -4
  46. streamlit/elements/lib/column_types.py +14 -8
  47. streamlit/elements/lib/dialog.py +9 -5
  48. streamlit/elements/lib/image_utils.py +39 -40
  49. streamlit/elements/lib/js_number.py +4 -4
  50. streamlit/elements/lib/layout_utils.py +65 -1
  51. streamlit/elements/lib/mutable_status_container.py +14 -3
  52. streamlit/elements/lib/options_selector_utils.py +22 -12
  53. streamlit/elements/lib/pandas_styler_utils.py +25 -21
  54. streamlit/elements/lib/policies.py +6 -5
  55. streamlit/elements/lib/streamlit_plotly_theme.py +54 -53
  56. streamlit/elements/lib/subtitle_utils.py +6 -9
  57. streamlit/elements/lib/utils.py +20 -5
  58. streamlit/elements/map.py +32 -56
  59. streamlit/elements/markdown.py +101 -12
  60. streamlit/elements/media.py +78 -21
  61. streamlit/elements/metric.py +32 -16
  62. streamlit/elements/plotly_chart.py +15 -15
  63. streamlit/elements/progress.py +33 -15
  64. streamlit/elements/spinner.py +31 -6
  65. streamlit/elements/text.py +21 -1
  66. streamlit/elements/toast.py +1 -2
  67. streamlit/elements/vega_charts.py +54 -23
  68. streamlit/elements/widgets/audio_input.py +24 -7
  69. streamlit/elements/widgets/button.py +26 -19
  70. streamlit/elements/widgets/button_group.py +10 -15
  71. streamlit/elements/widgets/camera_input.py +27 -7
  72. streamlit/elements/widgets/chat.py +91 -38
  73. streamlit/elements/widgets/checkbox.py +45 -4
  74. streamlit/elements/widgets/color_picker.py +40 -17
  75. streamlit/elements/widgets/data_editor.py +76 -37
  76. streamlit/elements/widgets/file_uploader.py +42 -13
  77. streamlit/elements/widgets/multiselect.py +7 -10
  78. streamlit/elements/widgets/number_input.py +123 -47
  79. streamlit/elements/widgets/radio.py +59 -13
  80. streamlit/elements/widgets/select_slider.py +35 -30
  81. streamlit/elements/widgets/selectbox.py +56 -9
  82. streamlit/elements/widgets/slider.py +190 -99
  83. streamlit/elements/widgets/text_widgets.py +54 -8
  84. streamlit/elements/widgets/time_widgets.py +53 -14
  85. streamlit/elements/write.py +5 -8
  86. streamlit/env_util.py +2 -7
  87. streamlit/error_util.py +16 -9
  88. streamlit/errors.py +69 -48
  89. streamlit/external/langchain/streamlit_callback_handler.py +10 -5
  90. streamlit/file_util.py +27 -10
  91. streamlit/git_util.py +29 -24
  92. streamlit/hello/animation_demo.py +9 -9
  93. streamlit/hello/dataframe_demo.py +5 -5
  94. streamlit/hello/hello.py +1 -0
  95. streamlit/hello/mapping_demo.py +7 -8
  96. streamlit/hello/plotting_demo.py +3 -3
  97. streamlit/hello/streamlit_app.py +28 -26
  98. streamlit/hello/utils.py +2 -1
  99. streamlit/logger.py +10 -11
  100. streamlit/navigation/page.py +11 -8
  101. streamlit/proto/Audio_pb2.py +4 -3
  102. streamlit/proto/Audio_pb2.pyi +8 -1
  103. streamlit/proto/Block_pb2.py +38 -29
  104. streamlit/proto/Block_pb2.pyi +72 -4
  105. streamlit/proto/ClientState_pb2.py +4 -4
  106. streamlit/proto/ClientState_pb2.pyi +7 -2
  107. streamlit/proto/Code_pb2.py +4 -2
  108. streamlit/proto/Code_pb2.pyi +1 -0
  109. streamlit/proto/DataFrame_pb2.pyi +1 -1
  110. streamlit/proto/DeckGlJsonChart_pb2.pyi +1 -1
  111. streamlit/proto/Element_pb2.py +5 -3
  112. streamlit/proto/Element_pb2.pyi +20 -3
  113. streamlit/proto/GapSize_pb2.py +29 -0
  114. streamlit/proto/GapSize_pb2.pyi +70 -0
  115. streamlit/proto/HeightConfig_pb2.py +27 -0
  116. streamlit/proto/HeightConfig_pb2.pyi +48 -0
  117. streamlit/proto/NamedDataSet_pb2.pyi +1 -1
  118. streamlit/proto/Navigation_pb2.py +3 -3
  119. streamlit/proto/Navigation_pb2.pyi +4 -0
  120. streamlit/proto/NewSession_pb2.py +18 -16
  121. streamlit/proto/NewSession_pb2.pyi +29 -3
  122. streamlit/proto/PageConfig_pb2.py +7 -7
  123. streamlit/proto/PageConfig_pb2.pyi +21 -1
  124. streamlit/proto/Video_pb2.py +8 -7
  125. streamlit/proto/Video_pb2.pyi +8 -1
  126. streamlit/proto/WidthConfig_pb2.py +2 -2
  127. streamlit/proto/WidthConfig_pb2.pyi +15 -1
  128. streamlit/runtime/__init__.py +1 -1
  129. streamlit/runtime/app_session.py +53 -40
  130. streamlit/runtime/caching/__init__.py +9 -9
  131. streamlit/runtime/caching/cache_data_api.py +36 -30
  132. streamlit/runtime/caching/cache_errors.py +4 -4
  133. streamlit/runtime/caching/cache_resource_api.py +8 -8
  134. streamlit/runtime/caching/cache_utils.py +15 -14
  135. streamlit/runtime/caching/cached_message_replay.py +14 -8
  136. streamlit/runtime/caching/hashing.py +91 -97
  137. streamlit/runtime/caching/legacy_cache_api.py +2 -2
  138. streamlit/runtime/caching/storage/cache_storage_protocol.py +1 -1
  139. streamlit/runtime/caching/storage/dummy_cache_storage.py +1 -1
  140. streamlit/runtime/caching/storage/in_memory_cache_storage_wrapper.py +12 -14
  141. streamlit/runtime/caching/storage/local_disk_cache_storage.py +6 -6
  142. streamlit/runtime/connection_factory.py +36 -36
  143. streamlit/runtime/context.py +58 -9
  144. streamlit/runtime/credentials.py +29 -40
  145. streamlit/runtime/forward_msg_queue.py +11 -11
  146. streamlit/runtime/fragment.py +7 -7
  147. streamlit/runtime/media_file_manager.py +3 -4
  148. streamlit/runtime/memory_media_file_storage.py +6 -5
  149. streamlit/runtime/memory_uploaded_file_manager.py +2 -2
  150. streamlit/runtime/metrics_util.py +11 -12
  151. streamlit/runtime/pages_manager.py +4 -6
  152. streamlit/runtime/runtime.py +8 -6
  153. streamlit/runtime/runtime_util.py +7 -6
  154. streamlit/runtime/scriptrunner/__init__.py +4 -4
  155. streamlit/runtime/scriptrunner/exec_code.py +12 -5
  156. streamlit/runtime/scriptrunner/magic.py +16 -12
  157. streamlit/runtime/scriptrunner/script_cache.py +1 -1
  158. streamlit/runtime/scriptrunner/script_runner.py +53 -29
  159. streamlit/runtime/scriptrunner_utils/exceptions.py +1 -1
  160. streamlit/runtime/scriptrunner_utils/script_requests.py +7 -4
  161. streamlit/runtime/scriptrunner_utils/script_run_context.py +10 -23
  162. streamlit/runtime/secrets.py +40 -35
  163. streamlit/runtime/session_manager.py +2 -1
  164. streamlit/runtime/state/__init__.py +5 -5
  165. streamlit/runtime/state/common.py +2 -2
  166. streamlit/runtime/state/query_params.py +13 -15
  167. streamlit/runtime/state/query_params_proxy.py +17 -13
  168. streamlit/runtime/state/safe_session_state.py +2 -2
  169. streamlit/runtime/state/session_state.py +52 -34
  170. streamlit/runtime/stats.py +2 -2
  171. streamlit/runtime/uploaded_file_manager.py +1 -1
  172. streamlit/runtime/websocket_session_manager.py +10 -6
  173. streamlit/source_util.py +8 -6
  174. streamlit/static/index.html +3 -17
  175. streamlit/static/manifest.json +1180 -0
  176. streamlit/static/static/css/{index.DqDwtg6_.css → index.CJVRHjQZ.css} +1 -1
  177. streamlit/static/static/js/{ErrorOutline.esm.DU9IrB3M.js → ErrorOutline.esm.6PVAQvlT.js} +1 -1
  178. streamlit/static/static/js/{FileDownload.esm.P9rKwKo8.js → FileDownload.esm.BZQHC61b.js} +1 -1
  179. streamlit/static/static/js/{FileHelper.D7RMkx0e.js → FileHelper.Bn1VShMJ.js} +5 -5
  180. streamlit/static/static/js/{FormClearHelper.B67tgll0.js → FormClearHelper.CsFEiTNN.js} +1 -1
  181. streamlit/static/static/js/{Hooks.ncTJktu9.js → Hooks.DguOHQL1.js} +1 -1
  182. streamlit/static/static/js/{InputInstructions.D-Y8geDN.js → InputInstructions.CTYn2BJQ.js} +1 -1
  183. streamlit/static/static/js/{ProgressBar.B-kexwwD.js → ProgressBar.CPOGBKCi.js} +2 -2
  184. streamlit/static/static/js/{RenderInPortalIfExists.BgaoZgep.js → RenderInPortalIfExists.BYu_CZaF.js} +1 -1
  185. streamlit/static/static/js/Toolbar.gXKw7ANv.js +1 -0
  186. streamlit/static/static/js/UploadFileInfo.0DCkpDDf.js +6 -0
  187. streamlit/static/static/js/{base-input.BoAa1U94.js → base-input.DBYPj91R.js} +4 -4
  188. streamlit/static/static/js/{checkbox.Z6iSfe5F.js → checkbox.BUm2vnNv.js} +2 -2
  189. streamlit/static/static/js/{createSuper.B4oGDYRm.js → createSuper.KD4RuZ-W.js} +1 -1
  190. streamlit/static/static/js/{data-grid-overlay-editor.msYws2Ou.js → data-grid-overlay-editor.CUwpDfvI.js} +1 -1
  191. streamlit/static/static/js/{downloader.kc14n2Hv.js → downloader.CkDtclup.js} +1 -1
  192. streamlit/static/static/js/{es6.CxQz807-.js → es6.Dlcvh_r0.js} +2 -2
  193. streamlit/static/static/js/{iframeResizer.contentWindow.B19u0ONI.js → iframeResizer.contentWindow.DOXlFfve.js} +1 -1
  194. streamlit/static/static/js/{index.BGga-hcS.js → index.B0cuGMAB.js} +25 -25
  195. streamlit/static/static/js/index.BCWTclSV.js +73 -0
  196. streamlit/static/static/js/index.BJY_fap7.js +1 -0
  197. streamlit/static/static/js/index.BL3l6dnk.js +1 -0
  198. streamlit/static/static/js/{index.BFz9U2y0.js → index.BMZzRZjB.js} +1 -1
  199. streamlit/static/static/js/{index.-5ruC9At.js → index.BOzUTGDe.js} +1 -1
  200. streamlit/static/static/js/index.BYI5iO-o.js +1 -0
  201. streamlit/static/static/js/index.BYo0ywlm.js +783 -0
  202. streamlit/static/static/js/{index.CmTAF0dM.js → index.BYz9btsY.js} +1 -1
  203. streamlit/static/static/js/{index.BHGGDa8K.js → index.CCVzQz0Z.js} +2 -2
  204. streamlit/static/static/js/index.CD6FydK9.js +1 -0
  205. streamlit/static/static/js/index.CDYEqgC8.js +2 -0
  206. streamlit/static/static/js/{index.BRXmLIsC.js → index.CMP9c4xA.js} +1 -1
  207. streamlit/static/static/js/index.CN30QAPD.js +1 -0
  208. streamlit/static/static/js/{index.LaIasviC.js → index.CNqWQkTe.js} +1 -1
  209. streamlit/static/static/js/index.CaxS67Xz.js +1 -0
  210. streamlit/static/static/js/{index.xNQq3Ei5.js → index.CbsT4sGW.js} +1 -1
  211. streamlit/static/static/js/index.ChAVlxpQ.js +1 -0
  212. streamlit/static/static/js/{index.BqfdT8-Q.js → index.ClLMMmDd.js} +1 -1
  213. streamlit/static/static/js/{index.BHXxWdde.js → index.D-O9rQmV.js} +1 -1
  214. streamlit/static/static/js/{index.9V1KdxfP.js → index.D4k7VZZL.js} +1 -1
  215. streamlit/static/static/js/index.DLBi0Ar1.js +1 -0
  216. streamlit/static/static/js/index.DVq5XmJo.js +197 -0
  217. streamlit/static/static/js/{index.BoigZiu7.js → index.DZKmKXWw.js} +1 -1
  218. streamlit/static/static/js/index.DkaVx80F.js +1 -0
  219. streamlit/static/static/js/index.Dr968Klx.js +1 -0
  220. streamlit/static/static/js/{index.BpILzHf_.js → index.DtUYLn9j.js} +20 -20
  221. streamlit/static/static/js/index.DwjYSyhs.js +1 -0
  222. streamlit/static/static/js/index.DzrImxu4.js +1 -0
  223. streamlit/static/static/js/index.HyGsn4VM.js +1 -0
  224. streamlit/static/static/js/index.OwxC65od.js +12 -0
  225. streamlit/static/static/js/index.PZs7VZkC.js +1 -0
  226. streamlit/static/static/js/index.Voiqpj4q.js +1 -0
  227. streamlit/static/static/js/index.bSROvR-J.js +3 -0
  228. streamlit/static/static/js/index.oT9GD3l4.js +1 -0
  229. streamlit/static/static/js/{index.DeB9iKFW.js → index.qb-yAPH6.js} +255 -255
  230. streamlit/static/static/js/index.rJFy_Ygy.js +2 -0
  231. streamlit/static/static/js/{input.DsCfafm0.js → input.CwQtEnFN.js} +2 -2
  232. streamlit/static/static/js/{memory.nY_lMTtu.js → memory.C5XaFIjR.js} +1 -1
  233. streamlit/static/static/js/{mergeWith.B_7zmsM4.js → mergeWith.DzwwH6AG.js} +1 -1
  234. streamlit/static/static/js/{number-overlay-editor.CSeVhHRU.js → number-overlay-editor.Dx0XqCkD.js} +1 -1
  235. streamlit/static/static/js/{possibleConstructorReturn.nNhsvgRd.js → possibleConstructorReturn.CVfSu9Ws.js} +1 -1
  236. streamlit/static/static/js/{sandbox.Cgm3iuL6.js → sandbox.BT0gdMXk.js} +1 -1
  237. streamlit/static/static/js/{textarea.BR8rlyih.js → textarea.DNCbrtbM.js} +2 -2
  238. streamlit/static/static/js/{timepicker.w4XhAenH.js → timepicker.4UYJD9Ts.js} +1 -1
  239. streamlit/static/static/js/{toConsumableArray.CgkEPBwD.js → toConsumableArray.DUmnaVWV.js} +1 -1
  240. streamlit/static/static/js/{uniqueId.j-1rlNNH.js → uniqueId.DUvh-GL8.js} +1 -1
  241. streamlit/static/static/js/{useBasicWidgetState.zXY9CjFS.js → useBasicWidgetState.Cwd7-jJa.js} +1 -1
  242. streamlit/static/static/js/useOnInputChange.DvemQrOM.js +1 -0
  243. streamlit/static/static/js/{withFullScreenWrapper.Ov13692o.js → withFullScreenWrapper.CiQ10ByU.js} +1 -1
  244. streamlit/static/static/media/SourceCodeVF-Italic.ttf.Ba1oaZG1.woff2 +0 -0
  245. streamlit/static/static/media/SourceCodeVF-Upright.ttf.BjWn63N-.woff2 +0 -0
  246. streamlit/static/static/media/SourceSansVF-Italic.ttf.Bt9VkdQ3.woff2 +0 -0
  247. streamlit/static/static/media/SourceSansVF-Upright.ttf.BsWL4Kly.woff2 +0 -0
  248. streamlit/static/static/media/SourceSerifVariable-Italic.ttf.CVdzAtxO.woff2 +0 -0
  249. streamlit/static/static/media/SourceSerifVariable-Roman.ttf.mdpVL9bi.woff2 +0 -0
  250. streamlit/string_util.py +14 -19
  251. streamlit/temporary_directory.py +13 -4
  252. streamlit/testing/v1/app_test.py +15 -10
  253. streamlit/testing/v1/element_tree.py +157 -178
  254. streamlit/testing/v1/local_script_runner.py +11 -15
  255. streamlit/testing/v1/util.py +11 -4
  256. streamlit/type_util.py +8 -12
  257. streamlit/url_util.py +1 -1
  258. streamlit/user_info.py +6 -5
  259. streamlit/util.py +25 -1
  260. streamlit/vendor/pympler/asizeof.py +3 -2
  261. streamlit/watcher/event_based_path_watcher.py +15 -1
  262. streamlit/watcher/folder_black_list.py +2 -2
  263. streamlit/watcher/local_sources_watcher.py +64 -18
  264. streamlit/watcher/path_watcher.py +6 -10
  265. streamlit/watcher/polling_path_watcher.py +8 -7
  266. streamlit/watcher/util.py +7 -6
  267. streamlit/web/bootstrap.py +16 -14
  268. streamlit/web/cli.py +52 -45
  269. streamlit/web/server/__init__.py +7 -3
  270. streamlit/web/server/app_static_file_handler.py +1 -1
  271. streamlit/web/server/authlib_tornado_integration.py +9 -4
  272. streamlit/web/server/browser_websocket_handler.py +8 -2
  273. streamlit/web/server/component_request_handler.py +14 -10
  274. streamlit/web/server/media_file_handler.py +14 -7
  275. streamlit/web/server/oauth_authlib_routes.py +41 -9
  276. streamlit/web/server/oidc_mixin.py +35 -17
  277. streamlit/web/server/routes.py +32 -22
  278. streamlit/web/server/server.py +6 -18
  279. streamlit/web/server/server_util.py +19 -9
  280. streamlit/web/server/stats_request_handler.py +7 -5
  281. streamlit/web/server/upload_file_request_handler.py +22 -19
  282. streamlit/web/server/websocket_headers.py +1 -1
  283. {streamlit-1.45.1.dist-info → streamlit-1.46.0.dist-info}/METADATA +4 -4
  284. streamlit-1.46.0.dist-info/RECORD +559 -0
  285. {streamlit-1.45.1.dist-info → streamlit-1.46.0.dist-info}/WHEEL +1 -1
  286. streamlit/elements/lib/event_utils.py +0 -39
  287. streamlit/static/static/js/Toolbar.D9RUZv9G.js +0 -1
  288. streamlit/static/static/js/UploadFileInfo.C-jY39rj.js +0 -1
  289. streamlit/static/static/js/index.8jhZBWF2.js +0 -3
  290. streamlit/static/static/js/index.BCx3C6e_.js +0 -1
  291. streamlit/static/static/js/index.BRuTz_S4.js +0 -1
  292. streamlit/static/static/js/index.Bcru_ti-.js +0 -1
  293. streamlit/static/static/js/index.Bl1FMJRd.js +0 -1
  294. streamlit/static/static/js/index.C1z8KpLA.js +0 -779
  295. streamlit/static/static/js/index.C32I2PUe.js +0 -2
  296. streamlit/static/static/js/index.C5GnDRB7.js +0 -1
  297. streamlit/static/static/js/index.CG4qPaaW.js +0 -2
  298. streamlit/static/static/js/index.C_msmT1u.js +0 -1
  299. streamlit/static/static/js/index.CbeNTdd6.js +0 -1
  300. streamlit/static/static/js/index.CnGQVJcw.js +0 -12
  301. streamlit/static/static/js/index.CopVVq4l.js +0 -1
  302. streamlit/static/static/js/index.CtXupx4d.js +0 -197
  303. streamlit/static/static/js/index.DGmCchO7.js +0 -1
  304. streamlit/static/static/js/index.DH6zBk0e.js +0 -1
  305. streamlit/static/static/js/index.DHVlVWsm.js +0 -1
  306. streamlit/static/static/js/index.DRKIVBoi.js +0 -1
  307. streamlit/static/static/js/index.DUd-lFXx.js +0 -73
  308. streamlit/static/static/js/index.D_uRBA4B.js +0 -1
  309. streamlit/static/static/js/index.QHNfgPJd.js +0 -1
  310. streamlit/static/static/js/index.a-RJocYL.js +0 -1
  311. streamlit/static/static/js/index.cvz4B1gy.js +0 -1
  312. streamlit/static/static/js/index.t--hEgTQ.js +0 -6
  313. streamlit/static/static/js/useOnInputChange.z04u96A8.js +0 -1
  314. streamlit/static/static/media/SourceCodePro-Bold.CFEfr7-q.woff2 +0 -0
  315. streamlit/static/static/media/SourceCodePro-BoldItalic.C-LkFXxa.woff2 +0 -0
  316. streamlit/static/static/media/SourceCodePro-Italic.CxFOx7N-.woff2 +0 -0
  317. streamlit/static/static/media/SourceCodePro-Regular.CBOlD63d.woff2 +0 -0
  318. streamlit/static/static/media/SourceCodePro-SemiBold.CFHwW3Wd.woff2 +0 -0
  319. streamlit/static/static/media/SourceCodePro-SemiBoldItalic.Cg2yRu82.woff2 +0 -0
  320. streamlit/static/static/media/SourceSansPro-Bold.-6c9oR8J.woff2 +0 -0
  321. streamlit/static/static/media/SourceSansPro-BoldItalic.DmM_grLY.woff2 +0 -0
  322. streamlit/static/static/media/SourceSansPro-Italic.I1ipWe7Q.woff2 +0 -0
  323. streamlit/static/static/media/SourceSansPro-Regular.DZLUzqI4.woff2 +0 -0
  324. streamlit/static/static/media/SourceSansPro-SemiBold.sKQIyTMz.woff2 +0 -0
  325. streamlit/static/static/media/SourceSansPro-SemiBoldItalic.C0wP0icr.woff2 +0 -0
  326. streamlit/static/static/media/SourceSerifPro-Bold.8TUnKj4x.woff2 +0 -0
  327. streamlit/static/static/media/SourceSerifPro-BoldItalic.CBVO7Ve7.woff2 +0 -0
  328. streamlit/static/static/media/SourceSerifPro-Italic.DkFgL2HZ.woff2 +0 -0
  329. streamlit/static/static/media/SourceSerifPro-Regular.CNJNET2S.woff2 +0 -0
  330. streamlit/static/static/media/SourceSerifPro-SemiBold.CHyh9GC5.woff2 +0 -0
  331. streamlit/static/static/media/SourceSerifPro-SemiBoldItalic.CBtz8sWN.woff2 +0 -0
  332. streamlit-1.45.1.dist-info/RECORD +0 -568
  333. {streamlit-1.45.1.data → streamlit-1.46.0.data}/scripts/streamlit.cmd +0 -0
  334. {streamlit-1.45.1.dist-info → streamlit-1.46.0.dist-info}/entry_points.txt +0 -0
  335. {streamlit-1.45.1.dist-info → streamlit-1.46.0.dist-info}/top_level.txt +0 -0
@@ -43,12 +43,13 @@ class LocalScriptRunner(ScriptRunner):
43
43
  script_path: str,
44
44
  session_state: SafeSessionState,
45
45
  pages_manager: PagesManager,
46
- args=None,
47
- kwargs=None,
48
- ):
46
+ args: Any = None,
47
+ kwargs: Any = None,
48
+ ) -> None:
49
49
  """Initializes the ScriptRunner for the given script_path."""
50
50
 
51
- assert os.path.isfile(script_path), f"File not found at {script_path}"
51
+ if not os.path.isfile(script_path):
52
+ raise FileNotFoundError(f"File not found at {script_path}")
52
53
 
53
54
  self.forward_msg_queue = ForwardMsgQueue()
54
55
  self.script_path = script_path
@@ -73,13 +74,12 @@ class LocalScriptRunner(ScriptRunner):
73
74
  self.event_data: list[Any] = []
74
75
 
75
76
  def record_event(
76
- sender: ScriptRunner | None, event: ScriptRunnerEvent, **kwargs
77
+ sender: ScriptRunner | None, event: ScriptRunnerEvent, **kwargs: Any
77
78
  ) -> None:
78
79
  # Assert that we're not getting unexpected `sender` params
79
80
  # from ScriptRunner.on_event
80
- assert sender is None or sender == self, (
81
- "Unexpected ScriptRunnerEvent sender!"
82
- )
81
+ if sender is not None and sender != self:
82
+ raise RuntimeError("Unexpected ScriptRunnerEvent sender!")
83
83
 
84
84
  self.events.append(event)
85
85
  self.event_data.append(kwargs)
@@ -103,7 +103,7 @@ class LocalScriptRunner(ScriptRunner):
103
103
  def run(
104
104
  self,
105
105
  widget_state: WidgetStates | None = None,
106
- query_params=None,
106
+ query_params: dict[str, Any] | None = None,
107
107
  timeout: float = 3,
108
108
  page_hash: str = "",
109
109
  ) -> ElementTree:
@@ -127,14 +127,10 @@ class LocalScriptRunner(ScriptRunner):
127
127
  self.start()
128
128
  require_widgets_deltas(self, timeout)
129
129
 
130
- tree = parse_tree_from_messages(self.forward_msgs())
131
- return tree
130
+ return parse_tree_from_messages(self.forward_msgs())
132
131
 
133
132
  def script_stopped(self) -> bool:
134
- for e in self.events:
135
- if e == ScriptRunnerEvent.SHUTDOWN:
136
- return True
137
- return False
133
+ return any(e == ScriptRunnerEvent.SHUTDOWN for e in self.events)
138
134
 
139
135
  def _on_script_finished(
140
136
  self, ctx: ScriptRunContext, event: ScriptRunnerEvent, premature_stop: bool
@@ -15,13 +15,18 @@
15
15
  from __future__ import annotations
16
16
 
17
17
  from contextlib import contextmanager
18
- from typing import Any
18
+ from typing import TYPE_CHECKING, Any, Callable
19
19
 
20
20
  from streamlit import config
21
21
 
22
+ if TYPE_CHECKING:
23
+ from collections.abc import Generator
24
+
22
25
 
23
26
  @contextmanager
24
- def patch_config_options(config_overrides: dict[str, Any]):
27
+ def patch_config_options(
28
+ config_overrides: dict[str, Any],
29
+ ) -> Generator[None, None, None]:
25
30
  """A context manager that overrides config options. It can
26
31
  also be used as a function decorator.
27
32
 
@@ -43,10 +48,12 @@ def patch_config_options(config_overrides: dict[str, Any]):
43
48
  yield
44
49
 
45
50
 
46
- def build_mock_config_get_option(overrides_dict):
51
+ def build_mock_config_get_option(
52
+ overrides_dict: dict[str, Any],
53
+ ) -> Callable[[str], Any]:
47
54
  orig_get_option = config.get_option
48
55
 
49
- def mock_config_get_option(name):
56
+ def mock_config_get_option(name: str) -> Any:
50
57
  if name in overrides_dict:
51
58
  return overrides_dict[name]
52
59
  return orig_get_option(name)
streamlit/type_util.py CHANGED
@@ -49,7 +49,7 @@ from streamlit.errors import StreamlitAPIException
49
49
 
50
50
  if TYPE_CHECKING:
51
51
  import graphviz
52
- import sympy # type: ignore
52
+ import sympy
53
53
  from plotly.graph_objs import Figure
54
54
  from pydeck import Deck
55
55
 
@@ -119,8 +119,7 @@ def is_type(obj: object, fqn_type_pattern: str | re.Pattern[str]) -> bool:
119
119
  fqn_type = get_fqn_type(obj)
120
120
  if isinstance(fqn_type_pattern, str):
121
121
  return fqn_type_pattern == fqn_type
122
- else:
123
- return fqn_type_pattern.match(fqn_type) is not None
122
+ return fqn_type_pattern.match(fqn_type) is not None
124
123
 
125
124
 
126
125
  def _is_type_instance(obj: object, type_to_check: str) -> bool:
@@ -161,7 +160,7 @@ def to_bytes(obj: BytesLike) -> bytes:
161
160
  """
162
161
  if isinstance(obj, bytearray):
163
162
  return bytes(obj)
164
- elif isinstance(obj, bytes):
163
+ if isinstance(obj, bytes):
165
164
  return obj
166
165
 
167
166
  raise RuntimeError(f"{obj} is not convertible to bytes")
@@ -232,10 +231,10 @@ def is_graphviz_chart(
232
231
  ) -> TypeGuard[graphviz.Graph | graphviz.Digraph]:
233
232
  """True if input looks like a GraphViz chart."""
234
233
  return (
235
- # GraphViz < 0.18
234
+ # In GraphViz < 0.18
236
235
  is_type(obj, "graphviz.dot.Graph")
237
236
  or is_type(obj, "graphviz.dot.Digraph")
238
- # GraphViz >= 0.18
237
+ # In GraphViz >= 0.18
239
238
  or is_type(obj, "graphviz.graphs.Graph")
240
239
  or is_type(obj, "graphviz.graphs.Digraph")
241
240
  or is_type(obj, "graphviz.sources.Source")
@@ -263,16 +262,13 @@ def _is_probably_plotly_dict(obj: object) -> TypeGuard[dict[str, Any]]:
263
262
  if len(obj.keys()) == 0:
264
263
  return False
265
264
 
266
- if any(k not in ["config", "data", "frames", "layout"] for k in obj.keys()):
265
+ if any(k not in ["config", "data", "frames", "layout"] for k in obj):
267
266
  return False
268
267
 
269
268
  if any(_is_plotly_obj(v) for v in obj.values()):
270
269
  return True
271
270
 
272
- if any(_is_list_of_plotly_objs(v) for v in obj.values()):
273
- return True
274
-
275
- return False
271
+ return bool(any(_is_list_of_plotly_objs(v) for v in obj.values()))
276
272
 
277
273
 
278
274
  def is_delta_generator(obj: object) -> TypeGuard[DeltaGenerator]:
@@ -317,7 +313,7 @@ def is_pydeck(obj: object) -> TypeGuard[Deck]:
317
313
  return is_type(obj, "pydeck.bindings.deck.Deck")
318
314
 
319
315
 
320
- def is_pydantic_model(obj) -> bool:
316
+ def is_pydantic_model(obj: object) -> bool:
321
317
  """True if input looks like a Pydantic model instance."""
322
318
 
323
319
  if isinstance(obj, type):
streamlit/url_util.py CHANGED
@@ -91,7 +91,7 @@ def is_url(
91
91
 
92
92
  if result.scheme in ["http", "https"]:
93
93
  return bool(result.netloc)
94
- elif result.scheme in ["mailto", "data"]:
94
+ if result.scheme in ["mailto", "data"]:
95
95
  return bool(result.path)
96
96
 
97
97
  except ValueError:
streamlit/user_info.py CHANGED
@@ -17,6 +17,7 @@ from __future__ import annotations
17
17
  from collections.abc import Iterator, Mapping
18
18
  from typing import (
19
19
  TYPE_CHECKING,
20
+ Any,
20
21
  Final,
21
22
  NoReturn,
22
23
  Union,
@@ -358,7 +359,7 @@ def _get_user_info() -> UserInfo:
358
359
  ctx = _get_script_run_ctx()
359
360
  if ctx is None:
360
361
  _LOGGER.warning(
361
- "No script run context available. st.experimental_user will return an empty dictionary."
362
+ "No script run context available. st.user will return an empty dictionary."
362
363
  )
363
364
  return {}
364
365
 
@@ -486,13 +487,13 @@ class UserInfoProxy(Mapping[str, Union[str, bool, None]]):
486
487
  try:
487
488
  return _get_user_info()[key]
488
489
  except KeyError:
489
- raise KeyError(f'st.experimental_user has no key "{key}".')
490
+ raise KeyError(f'st.user has no key "{key}".')
490
491
 
491
492
  def __getattr__(self, key: str) -> str | bool | None:
492
493
  try:
493
494
  return _get_user_info()[key]
494
495
  except KeyError:
495
- raise AttributeError(f'st.experimental_user has no attribute "{key}".')
496
+ raise AttributeError(f'st.user has no attribute "{key}".')
496
497
 
497
498
  def __setattr__(self, name: str, value: str | None) -> NoReturn:
498
499
  raise StreamlitAPIException("st.user cannot be modified")
@@ -548,10 +549,10 @@ class DeprecatedUserInfoProxy(UserInfoProxy):
548
549
  Streamlit.
549
550
  """
550
551
 
551
- def __getattribute__(self, name: str):
552
+ def __getattribute__(self, name: str) -> Any:
552
553
  maybe_show_deprecated_user_warning()
553
554
  return super().__getattribute__(name)
554
555
 
555
- def __getitem__(self, key: str):
556
+ def __getitem__(self, key: str) -> Any:
556
557
  maybe_show_deprecated_user_warning()
557
558
  return super().__getitem__(key)
streamlit/util.py CHANGED
@@ -27,7 +27,7 @@ def memoize(func: Callable[..., Any]) -> Callable[..., Any]:
27
27
  result: list[Any] = []
28
28
 
29
29
  @functools.wraps(func)
30
- def wrapped_func():
30
+ def wrapped_func() -> Any:
31
31
  if not result:
32
32
  result.append(func())
33
33
  return result[0]
@@ -70,3 +70,27 @@ def calc_md5(s: bytes | str) -> str:
70
70
 
71
71
  h.update(b)
72
72
  return h.hexdigest()
73
+
74
+
75
+ class AttributeDictionary(dict[Any, Any]):
76
+ """
77
+ A dictionary subclass that supports attribute-style access.
78
+
79
+ This class extends the functionality of a standard dictionary to allow items
80
+ to be accessed via attribute-style dot notation in addition to the traditional
81
+ key-based access. If a dictionary item is accessed and is itself a dictionary,
82
+ it is automatically wrapped in another `AttributeDictionary`, enabling recursive
83
+ attribute-style access.
84
+ """
85
+
86
+ def __getattr__(self, key: str) -> Any:
87
+ try:
88
+ item = self.__getitem__(key)
89
+ return AttributeDictionary(item) if isinstance(item, dict) else item
90
+ except KeyError as err:
91
+ raise AttributeError(
92
+ f"'{type(self).__name__}' object has no attribute '{key}'"
93
+ ) from err
94
+
95
+ def __setattr__(self, name: str, value: Any) -> None:
96
+ self[name] = value
@@ -172,6 +172,7 @@ details.
172
172
 
173
173
  import sys
174
174
  import types as Types
175
+ from typing import Any
175
176
  import warnings
176
177
  import weakref as Weakref
177
178
  from inspect import isbuiltin, isclass, iscode, isframe, isfunction, ismethod, ismodule
@@ -192,7 +193,7 @@ _ignored_modules = {
192
193
  "types",
193
194
  Exception.__module__, # 'weakref'
194
195
  __name__,
195
- } # inluding this very module
196
+ } # including this very module
196
197
 
197
198
  # Sizes of some primitive C types
198
199
  # XXX len(pack(T, 0)) == Struct(T).size == calcsize(T)
@@ -2586,7 +2587,7 @@ def asized(*objs, **opts):
2586
2587
  return t
2587
2588
 
2588
2589
 
2589
- def asizeof(*objs, **opts):
2590
+ def asizeof(*objs: Any, **opts: Any) -> int:
2590
2591
  """Return the combined size (in bytes) of all objects passed
2591
2592
  as positional arguments.
2592
2593
 
@@ -243,7 +243,7 @@ class WatchedPath:
243
243
  *, # keyword-only arguments:
244
244
  glob_pattern: str | None = None,
245
245
  allow_nonexistent: bool = False,
246
- ):
246
+ ) -> None:
247
247
  self.md5 = md5
248
248
  self.modification_time = modification_time
249
249
 
@@ -359,7 +359,21 @@ class _FolderEventHandler(events.FileSystemEventHandler):
359
359
 
360
360
  abs_changed_path = os.path.abspath(changed_path)
361
361
 
362
+ # First check if the exact path is being watched
362
363
  changed_path_info = self._watched_paths.get(abs_changed_path, None)
364
+
365
+ # If the exact path isn't found, check if it's inside any watched directories
366
+ # This is necessary for the folder watching feature to detect changes to files
367
+ # within watched directories, not just the directories themselves
368
+ for path, info in self._watched_paths.items():
369
+ if (
370
+ os.path.isdir(path)
371
+ and os.path.commonpath([path, abs_changed_path]) == path
372
+ ):
373
+ changed_path_info = info
374
+ break
375
+
376
+ # If we still haven't found a matching path, ignore this event
363
377
  if changed_path_info is None:
364
378
  _LOGGER.debug(
365
379
  "Ignoring changed path %s.\nWatched_paths: %s",
@@ -46,7 +46,7 @@ class FolderBlackList:
46
46
 
47
47
  """
48
48
 
49
- def __init__(self, folder_blacklist):
49
+ def __init__(self, folder_blacklist: list[str]) -> None:
50
50
  """Constructor.
51
51
 
52
52
  Parameters
@@ -67,7 +67,7 @@ class FolderBlackList:
67
67
  def __repr__(self) -> str:
68
68
  return util.repr_(self)
69
69
 
70
- def is_blacklisted(self, filepath):
70
+ def is_blacklisted(self, filepath: str) -> bool:
71
71
  """Test if filepath is in the blacklist.
72
72
 
73
73
  Parameters
@@ -46,11 +46,12 @@ PathWatcher = None
46
46
 
47
47
 
48
48
  class LocalSourcesWatcher:
49
- def __init__(self, pages_manager: PagesManager):
49
+ def __init__(self, pages_manager: PagesManager) -> None:
50
50
  self._pages_manager = pages_manager
51
51
  self._main_script_path = os.path.abspath(self._pages_manager.main_script_path)
52
+ self._watch_folders = config.get_option("server.folderWatchList")
52
53
  self._script_folder = os.path.dirname(self._main_script_path)
53
- self._on_file_changed: list[Callable[[str], None]] = []
54
+ self._on_path_changed: list[Callable[[str], None]] = []
54
55
  self._is_closed = False
55
56
  self._cached_sys_modules: set[str] = set()
56
57
 
@@ -79,6 +80,21 @@ class LocalSourcesWatcher:
79
80
  module_name=None,
80
81
  )
81
82
 
83
+ # Add custom watch path if it exists
84
+
85
+ for watch_folder in self._watch_folders:
86
+ # check if it is folder
87
+ if not os.path.isdir(watch_folder):
88
+ _LOGGER.warning("Watch folder is not a directory: %s", watch_folder)
89
+ continue
90
+ _LOGGER.debug("Registering watch folder: %s", watch_folder)
91
+ if watch_folder not in self._watched_pages:
92
+ self._register_watcher(
93
+ watch_folder,
94
+ module_name=None,
95
+ is_directory=True,
96
+ )
97
+
82
98
  for old_page_path in old_page_paths:
83
99
  # Only remove pages that are no longer valid files
84
100
  if old_page_path not in new_pages_paths and not os.path.isfile(
@@ -90,11 +106,22 @@ class LocalSourcesWatcher:
90
106
  self._watched_pages = self._watched_pages.union(new_pages_paths)
91
107
 
92
108
  def register_file_change_callback(self, cb: Callable[[str], None]) -> None:
93
- self._on_file_changed.append(cb)
109
+ self._on_path_changed.append(cb)
94
110
 
95
- def on_file_changed(self, filepath):
111
+ def on_path_changed(self, filepath: str) -> None:
112
+ _LOGGER.debug("Path changed: %s", filepath)
96
113
  if filepath not in self._watched_modules:
97
- _LOGGER.error("Received event for non-watched file: %s", filepath)
114
+ # Check if this is a file in a watched directory
115
+ for watched_dir in self._watched_modules:
116
+ if (
117
+ os.path.isdir(watched_dir)
118
+ and os.path.commonpath([watched_dir, filepath]) == watched_dir
119
+ ):
120
+ _LOGGER.info("File changed in watched directory: %s", filepath)
121
+ for cb in self._on_path_changed:
122
+ cb(filepath)
123
+ return
124
+ _LOGGER.error("Received event for non-watched path: %s", filepath)
98
125
  return
99
126
 
100
127
  # Workaround:
@@ -113,17 +140,19 @@ class LocalSourcesWatcher:
113
140
  if wm.module_name is not None and wm.module_name in sys.modules:
114
141
  del sys.modules[wm.module_name]
115
142
 
116
- for cb in self._on_file_changed:
143
+ for cb in self._on_path_changed:
117
144
  cb(filepath)
118
145
 
119
- def close(self):
146
+ def close(self) -> None:
120
147
  for wm in self._watched_modules.values():
121
148
  wm.watcher.close()
122
149
  self._watched_modules = {}
123
150
  self._watched_pages = set()
124
151
  self._is_closed = True
125
152
 
126
- def _register_watcher(self, filepath, module_name):
153
+ def _register_watcher(
154
+ self, filepath: str, module_name: str | None, is_directory: bool = False
155
+ ) -> None:
127
156
  global PathWatcher # noqa: PLW0603
128
157
  if PathWatcher is None:
129
158
  PathWatcher = get_default_path_watcher_class()
@@ -132,10 +161,19 @@ class LocalSourcesWatcher:
132
161
  return
133
162
 
134
163
  try:
164
+ # Instead of using **kwargs, explicitly pass the named parameters
165
+ glob_pattern = "**/*" if is_directory else None
166
+
135
167
  wm = WatchedModule(
136
- watcher=PathWatcher(filepath, self.on_file_changed),
168
+ watcher=PathWatcher(
169
+ filepath,
170
+ self.on_path_changed,
171
+ glob_pattern=glob_pattern, # Pass as named parameter
172
+ allow_nonexistent=False,
173
+ ),
137
174
  module_name=module_name,
138
175
  )
176
+ self._watched_modules[filepath] = wm
139
177
  except PermissionError:
140
178
  # If you don't have permission to read this file, don't even add it
141
179
  # to watchers.
@@ -143,7 +181,7 @@ class LocalSourcesWatcher:
143
181
 
144
182
  self._watched_modules[filepath] = wm
145
183
 
146
- def _deregister_watcher(self, filepath):
184
+ def _deregister_watcher(self, filepath: str) -> None:
147
185
  if filepath not in self._watched_modules:
148
186
  return
149
187
 
@@ -154,17 +192,17 @@ class LocalSourcesWatcher:
154
192
  wm.watcher.close()
155
193
  del self._watched_modules[filepath]
156
194
 
157
- def _file_is_new(self, filepath):
195
+ def _file_is_new(self, filepath: str) -> bool:
158
196
  return filepath not in self._watched_modules
159
197
 
160
- def _file_should_be_watched(self, filepath):
198
+ def _file_should_be_watched(self, filepath: str) -> bool:
161
199
  # Using short circuiting for performance.
162
200
  return self._file_is_new(filepath) and (
163
201
  file_util.file_is_in_folder_glob(filepath, self._script_folder)
164
202
  or file_util.file_in_pythonpath(filepath)
165
203
  )
166
204
 
167
- def update_watched_modules(self):
205
+ def update_watched_modules(self) -> None:
168
206
  if self._is_closed:
169
207
  return
170
208
 
@@ -187,12 +225,12 @@ class LocalSourcesWatcher:
187
225
 
188
226
 
189
227
  def get_module_paths(module: ModuleType) -> set[str]:
190
- paths_extractors = [
228
+ paths_extractors: list[Callable[[ModuleType], list[str | None]]] = [
191
229
  # https://docs.python.org/3/reference/datamodel.html
192
230
  # __file__ is the pathname of the file from which the module was loaded
193
231
  # if it was loaded from a file.
194
232
  # The __file__ attribute may be missing for certain types of modules
195
- lambda m: [m.__file__],
233
+ lambda m: [m.__file__] if hasattr(m, "__file__") else [],
196
234
  # https://docs.python.org/3/reference/import.html#__spec__
197
235
  # The __spec__ attribute is set to the module spec that was used
198
236
  # when importing the module. one exception is __main__,
@@ -202,12 +240,20 @@ def get_module_paths(module: ModuleType) -> set[str]:
202
240
  # (or resource within a system) from which a module originates
203
241
  # ... It is up to the loader to decide on how to interpret
204
242
  # and use a module's origin, if at all.
205
- lambda m: [m.__spec__.origin],
243
+ lambda m: [m.__spec__.origin]
244
+ if hasattr(m, "__spec__") and m.__spec__ is not None
245
+ else [],
206
246
  # https://www.python.org/dev/peps/pep-0420/
207
247
  # Handling of "namespace packages" in which the __path__ attribute
208
248
  # is a _NamespacePath object with a _path attribute containing
209
249
  # the various paths of the package.
210
- lambda m: list(m.__path__._path),
250
+ lambda m: list(m.__path__._path)
251
+ if hasattr(m, "__path__")
252
+ # This check prevents issues with torch classes:
253
+ # https://github.com/streamlit/streamlit/issues/10992
254
+ and type(m.__path__).__name__ == "_NamespacePath"
255
+ and hasattr(m.__path__, "_path")
256
+ else [],
211
257
  ]
212
258
 
213
259
  all_paths = set()
@@ -220,7 +266,7 @@ def get_module_paths(module: ModuleType) -> set[str]:
220
266
  pass
221
267
  except Exception:
222
268
  _LOGGER.warning(
223
- f"Examining the path of {module.__name__} raised:", exc_info=True
269
+ "Examining the path of %s raised:", module.__name__, exc_info=True
224
270
  )
225
271
 
226
272
  all_paths.update(
@@ -38,7 +38,7 @@ class NoOpPathWatcher:
38
38
  *, # keyword-only arguments:
39
39
  glob_pattern: str | None = None,
40
40
  allow_nonexistent: bool = False,
41
- ):
41
+ ) -> None:
42
42
  pass
43
43
 
44
44
 
@@ -62,7 +62,7 @@ def _is_watchdog_available() -> bool:
62
62
  return False
63
63
 
64
64
 
65
- def report_watchdog_availability():
65
+ def report_watchdog_availability() -> None:
66
66
  if (
67
67
  config.get_option("server.fileWatcherType") not in ["poll", "none"]
68
68
  and not _is_watchdog_available()
@@ -70,15 +70,14 @@ def report_watchdog_availability():
70
70
  msg = "\n $ xcode-select --install" if env_util.IS_DARWIN else ""
71
71
 
72
72
  cli_util.print_to_cli(
73
- " %s" % "For better performance, install the Watchdog module:",
73
+ " For better performance, install the Watchdog module:",
74
74
  fg="blue",
75
75
  bold=True,
76
76
  )
77
77
  cli_util.print_to_cli(
78
- """%s
78
+ f"""{msg}
79
79
  $ pip install watchdog
80
80
  """
81
- % msg
82
81
  )
83
82
 
84
83
 
@@ -177,9 +176,6 @@ def get_path_watcher_class(watcher_type: str) -> PathWatcherType:
177
176
  from streamlit.watcher.event_based_path_watcher import EventBasedPathWatcher
178
177
 
179
178
  return EventBasedPathWatcher
180
- elif watcher_type == "auto":
181
- return PollingPathWatcher
182
- elif watcher_type == "poll":
179
+ if watcher_type in {"auto", "poll"}:
183
180
  return PollingPathWatcher
184
- else:
185
- return NoOpPathWatcher
181
+ return NoOpPathWatcher
@@ -18,6 +18,7 @@ from __future__ import annotations
18
18
 
19
19
  import time
20
20
  from concurrent.futures import ThreadPoolExecutor
21
+ from pathlib import Path
21
22
  from typing import Callable, Final
22
23
 
23
24
  from streamlit.logger import get_logger
@@ -59,7 +60,7 @@ class PollingPathWatcher:
59
60
  retains references to all active instances.)
60
61
  """
61
62
  # TODO(vdonato): Modernize this by switching to pathlib.
62
- self._path = path
63
+ self._path = Path(path) # Changed to pathlib.Path
63
64
  self._on_changed = on_changed
64
65
 
65
66
  self._glob_pattern = glob_pattern
@@ -68,10 +69,10 @@ class PollingPathWatcher:
68
69
  self._active = True
69
70
 
70
71
  self._modification_time = util.path_modification_time(
71
- self._path, self._allow_nonexistent
72
+ str(self._path), self._allow_nonexistent
72
73
  )
73
74
  self._md5 = util.calc_md5_with_blocking_retries(
74
- self._path,
75
+ str(self._path),
75
76
  glob_pattern=self._glob_pattern,
76
77
  allow_nonexistent=self._allow_nonexistent,
77
78
  )
@@ -81,7 +82,7 @@ class PollingPathWatcher:
81
82
  return repr_(self)
82
83
 
83
84
  def _schedule(self) -> None:
84
- def task():
85
+ def task() -> None:
85
86
  time.sleep(_POLLING_PERIOD_SECS)
86
87
  self._check_if_path_changed()
87
88
 
@@ -93,7 +94,7 @@ class PollingPathWatcher:
93
94
  return
94
95
 
95
96
  modification_time = util.path_modification_time(
96
- self._path, self._allow_nonexistent
97
+ str(self._path), self._allow_nonexistent
97
98
  )
98
99
  # We add modification_time != 0.0 check since on some file systems (s3fs/fuse)
99
100
  # modification_time is always 0.0 because of file system limitations.
@@ -104,7 +105,7 @@ class PollingPathWatcher:
104
105
  self._modification_time = modification_time
105
106
 
106
107
  md5 = util.calc_md5_with_blocking_retries(
107
- self._path,
108
+ str(self._path),
108
109
  glob_pattern=self._glob_pattern,
109
110
  allow_nonexistent=self._allow_nonexistent,
110
111
  )
@@ -115,7 +116,7 @@ class PollingPathWatcher:
115
116
  self._md5 = md5
116
117
 
117
118
  _LOGGER.debug("Change detected: %s", self._path)
118
- self._on_changed(self._path)
119
+ self._on_changed(str(self._path))
119
120
 
120
121
  self._schedule()
121
122
 
streamlit/watcher/util.py CHANGED
@@ -23,11 +23,14 @@ from __future__ import annotations
23
23
  import os
24
24
  import time
25
25
  from pathlib import Path
26
- from typing import Callable, TypeVar
26
+ from typing import TYPE_CHECKING, Callable, TypeVar
27
27
 
28
28
  from streamlit.errors import Error
29
29
  from streamlit.util import calc_md5
30
30
 
31
+ if TYPE_CHECKING:
32
+ from collections.abc import Generator
33
+
31
34
  # How many times to try to grab the MD5 hash.
32
35
  _MAX_RETRIES = 5
33
36
 
@@ -170,17 +173,15 @@ def _do_with_retries(
170
173
  for i in _retry_dance():
171
174
  try:
172
175
  return orig_fn()
173
- except exceptions:
176
+ except exceptions: # noqa: PERF203
174
177
  if i >= _MAX_RETRIES - 1:
175
178
  raise
176
- else:
177
- # Continue with loop to either retry or raise MaxRetriesError.
178
- pass
179
+ # Continue with loop to either retry or raise MaxRetriesError.
179
180
 
180
181
  raise MaxRetriesError(f"Unable to access file or folder: {path}")
181
182
 
182
183
 
183
- def _retry_dance():
184
+ def _retry_dance() -> Generator[int, None, None]:
184
185
  """Helper for writing a retry loop.
185
186
 
186
187
  This is useful to make sure all our retry loops work the same way. For example,