streamlit-data-editor-plus 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (456) hide show
  1. streamlit/__init__.py +292 -0
  2. streamlit/__main__.py +20 -0
  3. streamlit/auth_util.py +583 -0
  4. streamlit/cli_util.py +107 -0
  5. streamlit/column_config.py +60 -0
  6. streamlit/commands/__init__.py +13 -0
  7. streamlit/commands/echo.py +128 -0
  8. streamlit/commands/execution_control.py +318 -0
  9. streamlit/commands/logo.py +250 -0
  10. streamlit/commands/navigation.py +430 -0
  11. streamlit/commands/page_config.py +335 -0
  12. streamlit/components/__init__.py +13 -0
  13. streamlit/components/lib/__init__.py +13 -0
  14. streamlit/components/lib/local_component_registry.py +84 -0
  15. streamlit/components/types/__init__.py +13 -0
  16. streamlit/components/types/base_component_registry.py +99 -0
  17. streamlit/components/types/base_custom_component.py +158 -0
  18. streamlit/components/v1/__init__.py +29 -0
  19. streamlit/components/v1/component_arrow.py +141 -0
  20. streamlit/components/v1/component_registry.py +147 -0
  21. streamlit/components/v1/components.py +38 -0
  22. streamlit/components/v1/custom_component.py +239 -0
  23. streamlit/components/v2/__init__.py +531 -0
  24. streamlit/components/v2/bidi_component/__init__.py +20 -0
  25. streamlit/components/v2/bidi_component/constants.py +29 -0
  26. streamlit/components/v2/bidi_component/main.py +537 -0
  27. streamlit/components/v2/bidi_component/serialization.py +272 -0
  28. streamlit/components/v2/bidi_component/state.py +92 -0
  29. streamlit/components/v2/component_definition_resolver.py +143 -0
  30. streamlit/components/v2/component_file_watcher.py +403 -0
  31. streamlit/components/v2/component_manager.py +439 -0
  32. streamlit/components/v2/component_manifest_handler.py +122 -0
  33. streamlit/components/v2/component_path_utils.py +233 -0
  34. streamlit/components/v2/component_registry.py +426 -0
  35. streamlit/components/v2/get_bidi_component_manager.py +51 -0
  36. streamlit/components/v2/manifest_scanner.py +620 -0
  37. streamlit/components/v2/presentation.py +198 -0
  38. streamlit/components/v2/types.py +317 -0
  39. streamlit/config.py +2977 -0
  40. streamlit/config_option.py +319 -0
  41. streamlit/config_util.py +887 -0
  42. streamlit/connections/__init__.py +32 -0
  43. streamlit/connections/base_connection.py +215 -0
  44. streamlit/connections/snowflake_connection.py +765 -0
  45. streamlit/connections/snowpark_connection.py +213 -0
  46. streamlit/connections/sql_connection.py +427 -0
  47. streamlit/connections/util.py +97 -0
  48. streamlit/cursor.py +314 -0
  49. streamlit/dataframe_util.py +1434 -0
  50. streamlit/delta_generator.py +696 -0
  51. streamlit/delta_generator_singletons.py +243 -0
  52. streamlit/deprecation_util.py +253 -0
  53. streamlit/development.py +21 -0
  54. streamlit/elements/__init__.py +13 -0
  55. streamlit/elements/alert.py +341 -0
  56. streamlit/elements/arrow.py +1065 -0
  57. streamlit/elements/balloons.py +47 -0
  58. streamlit/elements/bokeh_chart.py +75 -0
  59. streamlit/elements/code.py +154 -0
  60. streamlit/elements/deck_gl_json_chart.py +627 -0
  61. streamlit/elements/dialog_decorator.py +320 -0
  62. streamlit/elements/empty.py +130 -0
  63. streamlit/elements/exception.py +370 -0
  64. streamlit/elements/form.py +481 -0
  65. streamlit/elements/graphviz_chart.py +226 -0
  66. streamlit/elements/heading.py +407 -0
  67. streamlit/elements/help.py +565 -0
  68. streamlit/elements/html.py +189 -0
  69. streamlit/elements/iframe.py +245 -0
  70. streamlit/elements/image.py +221 -0
  71. streamlit/elements/json.py +171 -0
  72. streamlit/elements/layouts.py +1534 -0
  73. streamlit/elements/lib/__init__.py +13 -0
  74. streamlit/elements/lib/built_in_chart_utils.py +1316 -0
  75. streamlit/elements/lib/color_util.py +272 -0
  76. streamlit/elements/lib/column_config_utils.py +555 -0
  77. streamlit/elements/lib/column_types.py +2699 -0
  78. streamlit/elements/lib/dialog.py +212 -0
  79. streamlit/elements/lib/dicttools.py +152 -0
  80. streamlit/elements/lib/file_uploader_utils.py +91 -0
  81. streamlit/elements/lib/form_utils.py +77 -0
  82. streamlit/elements/lib/image_utils.py +447 -0
  83. streamlit/elements/lib/js_number.py +105 -0
  84. streamlit/elements/lib/layout_utils.py +325 -0
  85. streamlit/elements/lib/mutable_expander_container.py +73 -0
  86. streamlit/elements/lib/mutable_popover_container.py +73 -0
  87. streamlit/elements/lib/mutable_status_container.py +194 -0
  88. streamlit/elements/lib/mutable_tab_container.py +73 -0
  89. streamlit/elements/lib/options_selector_utils.py +480 -0
  90. streamlit/elements/lib/pandas_styler_utils.py +302 -0
  91. streamlit/elements/lib/policies.py +198 -0
  92. streamlit/elements/lib/shortcut_utils.py +152 -0
  93. streamlit/elements/lib/streamlit_plotly_theme.py +206 -0
  94. streamlit/elements/lib/subtitle_utils.py +175 -0
  95. streamlit/elements/lib/utils.py +274 -0
  96. streamlit/elements/map.py +526 -0
  97. streamlit/elements/markdown.py +529 -0
  98. streamlit/elements/media.py +843 -0
  99. streamlit/elements/metric.py +529 -0
  100. streamlit/elements/pdf.py +190 -0
  101. streamlit/elements/plotly_chart.py +779 -0
  102. streamlit/elements/progress.py +172 -0
  103. streamlit/elements/pyplot.py +240 -0
  104. streamlit/elements/snow.py +47 -0
  105. streamlit/elements/space.py +121 -0
  106. streamlit/elements/spinner.py +152 -0
  107. streamlit/elements/table.py +240 -0
  108. streamlit/elements/text.py +113 -0
  109. streamlit/elements/toast.py +175 -0
  110. streamlit/elements/vega_charts.py +2510 -0
  111. streamlit/elements/widgets/__init__.py +13 -0
  112. streamlit/elements/widgets/audio_input.py +334 -0
  113. streamlit/elements/widgets/button.py +1559 -0
  114. streamlit/elements/widgets/button_group.py +1061 -0
  115. streamlit/elements/widgets/camera_input.py +281 -0
  116. streamlit/elements/widgets/chat.py +1028 -0
  117. streamlit/elements/widgets/checkbox.py +420 -0
  118. streamlit/elements/widgets/color_picker.py +329 -0
  119. streamlit/elements/widgets/data_editor.py +1168 -0
  120. streamlit/elements/widgets/data_editor_plus.py +902 -0
  121. streamlit/elements/widgets/feedback.py +322 -0
  122. streamlit/elements/widgets/file_uploader.py +592 -0
  123. streamlit/elements/widgets/multiselect.py +654 -0
  124. streamlit/elements/widgets/number_input.py +719 -0
  125. streamlit/elements/widgets/radio.py +551 -0
  126. streamlit/elements/widgets/select_slider.py +568 -0
  127. streamlit/elements/widgets/selectbox.py +680 -0
  128. streamlit/elements/widgets/slider.py +1093 -0
  129. streamlit/elements/widgets/text_widgets.py +779 -0
  130. streamlit/elements/widgets/time_widgets.py +1712 -0
  131. streamlit/elements/write.py +585 -0
  132. streamlit/emojis.py +34 -0
  133. streamlit/env_util.py +56 -0
  134. streamlit/error_util.py +112 -0
  135. streamlit/errors.py +680 -0
  136. streamlit/external/__init__.py +13 -0
  137. streamlit/external/langchain/__init__.py +23 -0
  138. streamlit/external/langchain/streamlit_callback_handler.py +410 -0
  139. streamlit/file_util.py +266 -0
  140. streamlit/git_util.py +200 -0
  141. streamlit/hello/__init__.py +13 -0
  142. streamlit/hello/__pycache__/__init__.cpython-312.pyc +0 -0
  143. streamlit/hello/__pycache__/streamlit_app.cpython-312.pyc +0 -0
  144. streamlit/hello/animation_demo.py +82 -0
  145. streamlit/hello/dataframe_demo.py +71 -0
  146. streamlit/hello/hello.py +46 -0
  147. streamlit/hello/mapping_demo.py +113 -0
  148. streamlit/hello/plotting_demo.py +62 -0
  149. streamlit/hello/streamlit_app.py +57 -0
  150. streamlit/hello/utils.py +30 -0
  151. streamlit/logger.py +129 -0
  152. streamlit/material_icon_names.py +25 -0
  153. streamlit/navigation/__init__.py +13 -0
  154. streamlit/navigation/page.py +365 -0
  155. streamlit/net_util.py +125 -0
  156. streamlit/path_security.py +98 -0
  157. streamlit/platform.py +31 -0
  158. streamlit/proto/Alert_pb2.py +28 -0
  159. streamlit/proto/Alert_pb2.pyi +100 -0
  160. streamlit/proto/AppPage_pb2.py +25 -0
  161. streamlit/proto/AppPage_pb2.pyi +75 -0
  162. streamlit/proto/ArrowData_pb2.py +27 -0
  163. streamlit/proto/ArrowData_pb2.pyi +103 -0
  164. streamlit/proto/ArrowNamedDataSet_pb2.py +26 -0
  165. streamlit/proto/ArrowNamedDataSet_pb2.pyi +65 -0
  166. streamlit/proto/AudioInput_pb2.py +26 -0
  167. streamlit/proto/AudioInput_pb2.pyi +72 -0
  168. streamlit/proto/Audio_pb2.py +26 -0
  169. streamlit/proto/Audio_pb2.pyi +75 -0
  170. streamlit/proto/AuthRedirect_pb2.py +25 -0
  171. streamlit/proto/AuthRedirect_pb2.pyi +48 -0
  172. streamlit/proto/AutoRerun_pb2.py +25 -0
  173. streamlit/proto/AutoRerun_pb2.pyi +52 -0
  174. streamlit/proto/BackMsg_pb2.py +29 -0
  175. streamlit/proto/BackMsg_pb2.pyi +132 -0
  176. streamlit/proto/Balloons_pb2.py +25 -0
  177. streamlit/proto/Balloons_pb2.pyi +48 -0
  178. streamlit/proto/BidiComponent_pb2.py +32 -0
  179. streamlit/proto/BidiComponent_pb2.pyi +176 -0
  180. streamlit/proto/Block_pb2.py +62 -0
  181. streamlit/proto/Block_pb2.pyi +518 -0
  182. streamlit/proto/ButtonGroup_pb2.py +32 -0
  183. streamlit/proto/ButtonGroup_pb2.pyi +157 -0
  184. streamlit/proto/ButtonLikeIconPosition_pb2.py +25 -0
  185. streamlit/proto/ButtonLikeIconPosition_pb2.pyi +47 -0
  186. streamlit/proto/Button_pb2.py +26 -0
  187. streamlit/proto/Button_pb2.pyi +82 -0
  188. streamlit/proto/CameraInput_pb2.py +26 -0
  189. streamlit/proto/CameraInput_pb2.pyi +66 -0
  190. streamlit/proto/ChatInput_pb2.py +27 -0
  191. streamlit/proto/ChatInput_pb2.pyi +113 -0
  192. streamlit/proto/Checkbox_pb2.py +28 -0
  193. streamlit/proto/Checkbox_pb2.pyi +99 -0
  194. streamlit/proto/ClientState_pb2.py +28 -0
  195. streamlit/proto/ClientState_pb2.pyi +137 -0
  196. streamlit/proto/Code_pb2.py +25 -0
  197. streamlit/proto/Code_pb2.pyi +59 -0
  198. streamlit/proto/ColorPicker_pb2.py +26 -0
  199. streamlit/proto/ColorPicker_pb2.pyi +82 -0
  200. streamlit/proto/Common_pb2.py +49 -0
  201. streamlit/proto/Common_pb2.pyi +325 -0
  202. streamlit/proto/Components_pb2.py +33 -0
  203. streamlit/proto/Components_pb2.pyi +196 -0
  204. streamlit/proto/Dataframe_pb2.py +30 -0
  205. streamlit/proto/Dataframe_pb2.pyi +172 -0
  206. streamlit/proto/DateInput_pb2.py +26 -0
  207. streamlit/proto/DateInput_pb2.pyi +91 -0
  208. streamlit/proto/DateTimeInput_pb2.py +26 -0
  209. streamlit/proto/DateTimeInput_pb2.pyi +94 -0
  210. streamlit/proto/DeckGlJsonChart_pb2.py +27 -0
  211. streamlit/proto/DeckGlJsonChart_pb2.pyi +91 -0
  212. streamlit/proto/Delta_pb2.py +29 -0
  213. streamlit/proto/Delta_pb2.pyi +82 -0
  214. streamlit/proto/DownloadButton_pb2.py +26 -0
  215. streamlit/proto/DownloadButton_pb2.pyi +92 -0
  216. streamlit/proto/Element_pb2.py +81 -0
  217. streamlit/proto/Element_pb2.pyi +348 -0
  218. streamlit/proto/Empty_pb2.py +25 -0
  219. streamlit/proto/Empty_pb2.pyi +42 -0
  220. streamlit/proto/Exception_pb2.py +26 -0
  221. streamlit/proto/Exception_pb2.pyi +88 -0
  222. streamlit/proto/Favicon_pb2.py +25 -0
  223. streamlit/proto/Favicon_pb2.pyi +47 -0
  224. streamlit/proto/Feedback_pb2.py +27 -0
  225. streamlit/proto/Feedback_pb2.pyi +93 -0
  226. streamlit/proto/FileUploader_pb2.py +26 -0
  227. streamlit/proto/FileUploader_pb2.pyi +90 -0
  228. streamlit/proto/ForwardMsg_pb2.py +48 -0
  229. streamlit/proto/ForwardMsg_pb2.pyi +296 -0
  230. streamlit/proto/GapSize_pb2.py +27 -0
  231. streamlit/proto/GapSize_pb2.pyi +82 -0
  232. streamlit/proto/GitInfo_pb2.py +27 -0
  233. streamlit/proto/GitInfo_pb2.pyi +84 -0
  234. streamlit/proto/GraphVizChart_pb2.py +25 -0
  235. streamlit/proto/GraphVizChart_pb2.pyi +56 -0
  236. streamlit/proto/Heading_pb2.py +25 -0
  237. streamlit/proto/Heading_pb2.pyi +63 -0
  238. streamlit/proto/HeightConfig_pb2.py +25 -0
  239. streamlit/proto/HeightConfig_pb2.pyi +61 -0
  240. streamlit/proto/Help_pb2.py +27 -0
  241. streamlit/proto/Help_pb2.pyi +104 -0
  242. streamlit/proto/Html_pb2.py +25 -0
  243. streamlit/proto/Html_pb2.pyi +52 -0
  244. streamlit/proto/IFrame_pb2.py +25 -0
  245. streamlit/proto/IFrame_pb2.pyi +68 -0
  246. streamlit/proto/Image_pb2.py +27 -0
  247. streamlit/proto/Image_pb2.pyi +73 -0
  248. streamlit/proto/Json_pb2.py +25 -0
  249. streamlit/proto/Json_pb2.pyi +63 -0
  250. streamlit/proto/LabelVisibility_pb2.py +27 -0
  251. streamlit/proto/LabelVisibility_pb2.pyi +69 -0
  252. streamlit/proto/LinkButton_pb2.py +26 -0
  253. streamlit/proto/LinkButton_pb2.pyi +76 -0
  254. streamlit/proto/Logo_pb2.py +27 -0
  255. streamlit/proto/Logo_pb2.pyi +82 -0
  256. streamlit/proto/Markdown_pb2.py +27 -0
  257. streamlit/proto/Markdown_pb2.pyi +83 -0
  258. streamlit/proto/Metric_pb2.py +32 -0
  259. streamlit/proto/Metric_pb2.pyi +145 -0
  260. streamlit/proto/MetricsEvent_pb2.py +28 -0
  261. streamlit/proto/MetricsEvent_pb2.pyi +224 -0
  262. streamlit/proto/MultiSelect_pb2.py +26 -0
  263. streamlit/proto/MultiSelect_pb2.pyi +108 -0
  264. streamlit/proto/Navigation_pb2.py +28 -0
  265. streamlit/proto/Navigation_pb2.pyi +89 -0
  266. streamlit/proto/NewSession_pb2.py +47 -0
  267. streamlit/proto/NewSession_pb2.pyi +753 -0
  268. streamlit/proto/NumberInput_pb2.py +28 -0
  269. streamlit/proto/NumberInput_pb2.pyi +138 -0
  270. streamlit/proto/PageConfig_pb2.py +33 -0
  271. streamlit/proto/PageConfig_pb2.pyi +179 -0
  272. streamlit/proto/PageInfo_pb2.py +25 -0
  273. streamlit/proto/PageInfo_pb2.pyi +50 -0
  274. streamlit/proto/PageLink_pb2.py +26 -0
  275. streamlit/proto/PageLink_pb2.pyi +80 -0
  276. streamlit/proto/PageNotFound_pb2.py +25 -0
  277. streamlit/proto/PageNotFound_pb2.pyi +49 -0
  278. streamlit/proto/PageProfile_pb2.py +29 -0
  279. streamlit/proto/PageProfile_pb2.pyi +146 -0
  280. streamlit/proto/ParentMessage_pb2.py +25 -0
  281. streamlit/proto/ParentMessage_pb2.pyi +53 -0
  282. streamlit/proto/PlotlyChart_pb2.py +27 -0
  283. streamlit/proto/PlotlyChart_pb2.pyi +96 -0
  284. streamlit/proto/Progress_pb2.py +25 -0
  285. streamlit/proto/Progress_pb2.pyi +50 -0
  286. streamlit/proto/Radio_pb2.py +26 -0
  287. streamlit/proto/Radio_pb2.pyi +104 -0
  288. streamlit/proto/RootContainer_pb2.py +25 -0
  289. streamlit/proto/RootContainer_pb2.pyi +56 -0
  290. streamlit/proto/Selectbox_pb2.py +26 -0
  291. streamlit/proto/Selectbox_pb2.pyi +107 -0
  292. streamlit/proto/SessionEvent_pb2.py +26 -0
  293. streamlit/proto/SessionEvent_pb2.pyi +72 -0
  294. streamlit/proto/SessionStatus_pb2.py +25 -0
  295. streamlit/proto/SessionStatus_pb2.pyi +64 -0
  296. streamlit/proto/Skeleton_pb2.py +27 -0
  297. streamlit/proto/Skeleton_pb2.pyi +75 -0
  298. streamlit/proto/Slider_pb2.py +30 -0
  299. streamlit/proto/Slider_pb2.pyi +161 -0
  300. streamlit/proto/Snow_pb2.py +25 -0
  301. streamlit/proto/Snow_pb2.pyi +48 -0
  302. streamlit/proto/Space_pb2.py +25 -0
  303. streamlit/proto/Space_pb2.pyi +48 -0
  304. streamlit/proto/Spinner_pb2.py +25 -0
  305. streamlit/proto/Spinner_pb2.pyi +56 -0
  306. streamlit/proto/Table_pb2.py +28 -0
  307. streamlit/proto/Table_pb2.pyi +83 -0
  308. streamlit/proto/TextAlignmentConfig_pb2.py +27 -0
  309. streamlit/proto/TextAlignmentConfig_pb2.pyi +69 -0
  310. streamlit/proto/TextArea_pb2.py +26 -0
  311. streamlit/proto/TextArea_pb2.pyi +97 -0
  312. streamlit/proto/TextInput_pb2.py +28 -0
  313. streamlit/proto/TextInput_pb2.pyi +124 -0
  314. streamlit/proto/Text_pb2.py +25 -0
  315. streamlit/proto/Text_pb2.pyi +53 -0
  316. streamlit/proto/TimeInput_pb2.py +26 -0
  317. streamlit/proto/TimeInput_pb2.pyi +86 -0
  318. streamlit/proto/Toast_pb2.py +25 -0
  319. streamlit/proto/Toast_pb2.pyi +64 -0
  320. streamlit/proto/Transient_pb2.py +26 -0
  321. streamlit/proto/Transient_pb2.pyi +53 -0
  322. streamlit/proto/VegaLiteChart_pb2.py +27 -0
  323. streamlit/proto/VegaLiteChart_pb2.pyi +92 -0
  324. streamlit/proto/Video_pb2.py +30 -0
  325. streamlit/proto/Video_pb2.pyi +129 -0
  326. streamlit/proto/WidgetStates_pb2.py +31 -0
  327. streamlit/proto/WidgetStates_pb2.pyi +157 -0
  328. streamlit/proto/WidthConfig_pb2.py +25 -0
  329. streamlit/proto/WidthConfig_pb2.pyi +61 -0
  330. streamlit/proto/__init__.py +15 -0
  331. streamlit/proto/openmetrics_data_model_pb2.py +58 -0
  332. streamlit/proto/openmetrics_data_model_pb2.pyi +558 -0
  333. streamlit/py.typed +0 -0
  334. streamlit/runtime/__init__.py +50 -0
  335. streamlit/runtime/app_session.py +1302 -0
  336. streamlit/runtime/caching/__init__.py +118 -0
  337. streamlit/runtime/caching/cache_data_api.py +775 -0
  338. streamlit/runtime/caching/cache_errors.py +143 -0
  339. streamlit/runtime/caching/cache_resource_api.py +756 -0
  340. streamlit/runtime/caching/cache_type.py +33 -0
  341. streamlit/runtime/caching/cache_utils.py +601 -0
  342. streamlit/runtime/caching/cached_message_replay.py +290 -0
  343. streamlit/runtime/caching/hashing.py +655 -0
  344. streamlit/runtime/caching/legacy_cache_api.py +170 -0
  345. streamlit/runtime/caching/storage/__init__.py +29 -0
  346. streamlit/runtime/caching/storage/cache_storage_protocol.py +236 -0
  347. streamlit/runtime/caching/storage/dummy_cache_storage.py +60 -0
  348. streamlit/runtime/caching/storage/in_memory_cache_storage_wrapper.py +159 -0
  349. streamlit/runtime/caching/storage/local_disk_cache_storage.py +223 -0
  350. streamlit/runtime/caching/ttl_cleanup_cache.py +85 -0
  351. streamlit/runtime/connection_factory.py +498 -0
  352. streamlit/runtime/context.py +449 -0
  353. streamlit/runtime/context_util.py +49 -0
  354. streamlit/runtime/credentials.py +355 -0
  355. streamlit/runtime/download_data_util.py +53 -0
  356. streamlit/runtime/forward_msg_cache.py +101 -0
  357. streamlit/runtime/forward_msg_queue.py +263 -0
  358. streamlit/runtime/fragment.py +441 -0
  359. streamlit/runtime/media_file_manager.py +409 -0
  360. streamlit/runtime/media_file_storage.py +143 -0
  361. streamlit/runtime/memory_media_file_storage.py +201 -0
  362. streamlit/runtime/memory_session_storage.py +77 -0
  363. streamlit/runtime/memory_uploaded_file_manager.py +149 -0
  364. streamlit/runtime/metrics_util.py +612 -0
  365. streamlit/runtime/pages_manager.py +160 -0
  366. streamlit/runtime/runtime.py +775 -0
  367. streamlit/runtime/runtime_util.py +108 -0
  368. streamlit/runtime/script_data.py +46 -0
  369. streamlit/runtime/scriptrunner/__init__.py +38 -0
  370. streamlit/runtime/scriptrunner/exec_code.py +167 -0
  371. streamlit/runtime/scriptrunner/magic.py +277 -0
  372. streamlit/runtime/scriptrunner/magic_funcs.py +32 -0
  373. streamlit/runtime/scriptrunner/script_cache.py +89 -0
  374. streamlit/runtime/scriptrunner/script_runner.py +805 -0
  375. streamlit/runtime/scriptrunner_utils/__init__.py +19 -0
  376. streamlit/runtime/scriptrunner_utils/exceptions.py +44 -0
  377. streamlit/runtime/scriptrunner_utils/script_requests.py +311 -0
  378. streamlit/runtime/scriptrunner_utils/script_run_context.py +277 -0
  379. streamlit/runtime/secrets.py +533 -0
  380. streamlit/runtime/session_manager.py +430 -0
  381. streamlit/runtime/state/__init__.py +47 -0
  382. streamlit/runtime/state/common.py +256 -0
  383. streamlit/runtime/state/presentation.py +85 -0
  384. streamlit/runtime/state/query_params.py +767 -0
  385. streamlit/runtime/state/query_params_proxy.py +222 -0
  386. streamlit/runtime/state/safe_session_state.py +146 -0
  387. streamlit/runtime/state/session_state.py +1299 -0
  388. streamlit/runtime/state/session_state_proxy.py +153 -0
  389. streamlit/runtime/state/widgets.py +197 -0
  390. streamlit/runtime/stats.py +356 -0
  391. streamlit/runtime/theme_util.py +148 -0
  392. streamlit/runtime/uploaded_file_manager.py +152 -0
  393. streamlit/runtime/websocket_session_manager.py +301 -0
  394. streamlit/source_util.py +97 -0
  395. streamlit/starlette.py +34 -0
  396. streamlit/string_util.py +264 -0
  397. streamlit/temporary_directory.py +67 -0
  398. streamlit/testing/__init__.py +13 -0
  399. streamlit/testing/v1/__init__.py +17 -0
  400. streamlit/testing/v1/app_test.py +1089 -0
  401. streamlit/testing/v1/element_tree.py +2272 -0
  402. streamlit/testing/v1/local_script_runner.py +176 -0
  403. streamlit/testing/v1/util.py +61 -0
  404. streamlit/time_util.py +73 -0
  405. streamlit/type_util.py +480 -0
  406. streamlit/url_util.py +120 -0
  407. streamlit/user_info.py +698 -0
  408. streamlit/util.py +108 -0
  409. streamlit/vendor/__init__.py +0 -0
  410. streamlit/vendor/pympler/__init__.py +0 -0
  411. streamlit/vendor/pympler/asizeof.py +2870 -0
  412. streamlit/version.py +18 -0
  413. streamlit/watcher/__init__.py +28 -0
  414. streamlit/watcher/event_based_path_watcher.py +500 -0
  415. streamlit/watcher/folder_black_list.py +82 -0
  416. streamlit/watcher/local_sources_watcher.py +297 -0
  417. streamlit/watcher/path_watcher.py +244 -0
  418. streamlit/watcher/polling_path_watcher.py +138 -0
  419. streamlit/watcher/util.py +223 -0
  420. streamlit/web/__init__.py +13 -0
  421. streamlit/web/bootstrap.py +463 -0
  422. streamlit/web/cache_storage_manager_config.py +38 -0
  423. streamlit/web/cli.py +465 -0
  424. streamlit/web/server/__init__.py +30 -0
  425. streamlit/web/server/app_discovery.py +422 -0
  426. streamlit/web/server/app_static_file_handler.py +102 -0
  427. streamlit/web/server/authlib_tornado_integration.py +125 -0
  428. streamlit/web/server/bidi_component_request_handler.py +193 -0
  429. streamlit/web/server/browser_websocket_handler.py +341 -0
  430. streamlit/web/server/component_file_utils.py +105 -0
  431. streamlit/web/server/component_request_handler.py +107 -0
  432. streamlit/web/server/media_file_handler.py +148 -0
  433. streamlit/web/server/oauth_authlib_routes.py +314 -0
  434. streamlit/web/server/oidc_mixin.py +138 -0
  435. streamlit/web/server/routes.py +257 -0
  436. streamlit/web/server/server.py +555 -0
  437. streamlit/web/server/server_util.py +235 -0
  438. streamlit/web/server/starlette/__init__.py +23 -0
  439. streamlit/web/server/starlette/starlette_app.py +541 -0
  440. streamlit/web/server/starlette/starlette_app_utils.py +306 -0
  441. streamlit/web/server/starlette/starlette_auth_routes.py +584 -0
  442. streamlit/web/server/starlette/starlette_gzip_middleware.py +121 -0
  443. streamlit/web/server/starlette/starlette_path_security_middleware.py +97 -0
  444. streamlit/web/server/starlette/starlette_routes.py +884 -0
  445. streamlit/web/server/starlette/starlette_server.py +496 -0
  446. streamlit/web/server/starlette/starlette_server_config.py +55 -0
  447. streamlit/web/server/starlette/starlette_static_routes.py +181 -0
  448. streamlit/web/server/starlette/starlette_websocket.py +560 -0
  449. streamlit/web/server/stats_request_handler.py +116 -0
  450. streamlit/web/server/upload_file_request_handler.py +158 -0
  451. streamlit/web/server/websocket_headers.py +56 -0
  452. streamlit_data_editor_plus-0.1.0.dist-info/METADATA +76 -0
  453. streamlit_data_editor_plus-0.1.0.dist-info/RECORD +456 -0
  454. streamlit_data_editor_plus-0.1.0.dist-info/WHEEL +5 -0
  455. streamlit_data_editor_plus-0.1.0.dist-info/entry_points.txt +2 -0
  456. streamlit_data_editor_plus-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,297 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2026)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ import os
18
+ import sys
19
+ from typing import TYPE_CHECKING, Any, Final, NamedTuple
20
+
21
+ from streamlit import config, file_util
22
+ from streamlit.logger import get_logger
23
+ from streamlit.watcher.folder_black_list import FolderBlackList
24
+ from streamlit.watcher.path_watcher import (
25
+ NoOpPathWatcher,
26
+ PathWatcherType,
27
+ get_default_path_watcher_class,
28
+ )
29
+
30
+ if TYPE_CHECKING:
31
+ from collections.abc import Callable
32
+ from types import ModuleType
33
+
34
+ from streamlit.runtime.pages_manager import PagesManager
35
+
36
+ _LOGGER: Final = get_logger(__name__)
37
+
38
+
39
+ class WatchedModule(NamedTuple):
40
+ watcher: Any
41
+ module_name: str | None
42
+
43
+
44
+ # This needs to be initialized lazily to avoid calling config.get_option() and
45
+ # thus initializing config options when this file is first imported.
46
+ PathWatcher: PathWatcherType | None = None
47
+
48
+
49
+ class LocalSourcesWatcher:
50
+ """Watch local Python sources and pages to trigger app reruns.
51
+
52
+ Purpose
53
+ -------
54
+ This watcher powers Streamlit's core developer workflow: save a Python file
55
+ and the app reruns. It tracks Python modules, the main script directory, and
56
+ configured watch folders to notify the runtime when a relevant file changes.
57
+ """
58
+
59
+ def __init__(self, pages_manager: PagesManager) -> None:
60
+ self._pages_manager = pages_manager
61
+ self._main_script_path = os.path.realpath(self._pages_manager.main_script_path)
62
+ self._watch_folders = config.get_option("server.folderWatchList")
63
+ self._script_folder = os.path.dirname(self._main_script_path)
64
+ self._on_path_changed: list[Callable[[str], None]] = []
65
+ self._is_closed = False
66
+ self._cached_sys_modules: set[str] = set()
67
+
68
+ # Blacklist for folders that should not be watched
69
+ self._folder_black_list = FolderBlackList(
70
+ config.get_option("server.folderWatchBlacklist")
71
+ )
72
+
73
+ self._watched_modules: dict[str, WatchedModule] = {}
74
+ self._watched_pages: set[str] = set()
75
+
76
+ self.update_watched_pages()
77
+
78
+ def update_watched_pages(self) -> None:
79
+ old_page_paths = self._watched_pages.copy()
80
+ new_pages_paths: set[str] = set()
81
+
82
+ for page_info in self._pages_manager.get_pages().values():
83
+ if not page_info["script_path"]:
84
+ continue
85
+
86
+ page_path = os.path.realpath(page_info["script_path"])
87
+ new_pages_paths.add(page_path)
88
+ if page_path not in self._watched_pages:
89
+ self._register_watcher(
90
+ page_path,
91
+ module_name=None,
92
+ )
93
+
94
+ # Add custom watch path if it exists
95
+
96
+ for watch_folder in self._watch_folders:
97
+ # check if it is folder
98
+ if not os.path.isdir(watch_folder):
99
+ _LOGGER.warning("Watch folder is not a directory: %s", watch_folder)
100
+ continue
101
+ _LOGGER.debug("Registering watch folder: %s", watch_folder)
102
+ watch_folder_path = os.path.realpath(watch_folder)
103
+ if watch_folder_path not in self._watched_pages:
104
+ self._register_watcher(
105
+ watch_folder_path,
106
+ module_name=None,
107
+ is_directory=True,
108
+ )
109
+
110
+ for old_page_path in old_page_paths:
111
+ # Only remove pages that are no longer valid files
112
+ if old_page_path not in new_pages_paths and not os.path.isfile(
113
+ old_page_path
114
+ ):
115
+ self._deregister_watcher(old_page_path)
116
+ self._watched_pages.remove(old_page_path)
117
+
118
+ self._watched_pages = self._watched_pages.union(new_pages_paths)
119
+
120
+ def register_file_change_callback(self, cb: Callable[[str], None]) -> None:
121
+ self._on_path_changed.append(cb)
122
+
123
+ def on_path_changed(self, filepath: str) -> None:
124
+ _LOGGER.debug("Path changed: %s", filepath)
125
+
126
+ norm_filepath = os.path.realpath(filepath)
127
+ if norm_filepath not in self._watched_modules:
128
+ # Check if this is a file in a watched directory
129
+ for watched_path in self._watched_modules:
130
+ if (
131
+ os.path.isdir(watched_path)
132
+ and os.path.commonpath([watched_path, norm_filepath])
133
+ == watched_path
134
+ ):
135
+ _LOGGER.debug("File changed in watched directory: %s", filepath)
136
+ for cb in self._on_path_changed:
137
+ cb(filepath)
138
+ return
139
+ _LOGGER.error("Received event for non-watched path: %s", filepath)
140
+ return
141
+
142
+ # Workaround:
143
+ # Delete all watched modules so we can guarantee changes to the
144
+ # updated module are reflected on reload.
145
+ #
146
+ # In principle, for reloading a given module, we only need to unload
147
+ # the module itself and all of the modules which import it (directly
148
+ # or indirectly) such that when we exec the application code, the
149
+ # changes are reloaded and reflected in the running application.
150
+ #
151
+ # However, determining all import paths for a given loaded module is
152
+ # non-trivial, and so as a workaround we simply unload all watched
153
+ # modules.
154
+ for wm in self._watched_modules.values():
155
+ if wm.module_name is not None and wm.module_name in sys.modules:
156
+ del sys.modules[wm.module_name]
157
+
158
+ for cb in self._on_path_changed:
159
+ cb(filepath)
160
+
161
+ def close(self) -> None:
162
+ for wm in self._watched_modules.values():
163
+ wm.watcher.close()
164
+ self._watched_modules = {}
165
+ self._watched_pages = set()
166
+ self._is_closed = True
167
+
168
+ def _register_watcher(
169
+ self, filepath: str, module_name: str | None, is_directory: bool = False
170
+ ) -> None:
171
+ global PathWatcher # noqa: PLW0603
172
+ if PathWatcher is None:
173
+ PathWatcher = get_default_path_watcher_class()
174
+
175
+ if PathWatcher is NoOpPathWatcher:
176
+ return
177
+
178
+ try:
179
+ # Instead of using **kwargs, explicitly pass the named parameters
180
+ glob_pattern = "**/*" if is_directory else None
181
+
182
+ wm = WatchedModule(
183
+ watcher=PathWatcher(
184
+ filepath,
185
+ self.on_path_changed,
186
+ glob_pattern=glob_pattern, # Pass as named parameter
187
+ allow_nonexistent=False,
188
+ ),
189
+ module_name=module_name,
190
+ )
191
+ self._watched_modules[filepath] = wm
192
+ except Exception as ex:
193
+ # If we don't have permission to read this file, or if the file
194
+ # doesn't exist, don't even add it to watchers.
195
+ _LOGGER.warning("Failed to watch file %s: %s", filepath, exc_info=ex)
196
+ return
197
+
198
+ def _deregister_watcher(self, filepath: str) -> None:
199
+ if filepath not in self._watched_modules:
200
+ return
201
+
202
+ if filepath == self._main_script_path:
203
+ return
204
+
205
+ wm = self._watched_modules[filepath]
206
+ wm.watcher.close()
207
+ del self._watched_modules[filepath]
208
+
209
+ def _file_is_new(self, filepath: str) -> bool:
210
+ return filepath not in self._watched_modules
211
+
212
+ def _file_should_be_watched(self, filepath: str) -> bool:
213
+ # Using short circuiting for performance.
214
+ return self._file_is_new(filepath) and (
215
+ file_util.file_is_in_folder_glob(filepath, self._script_folder)
216
+ or file_util.file_in_pythonpath(filepath)
217
+ )
218
+
219
+ def update_watched_modules(self) -> None:
220
+ if self._is_closed:
221
+ return
222
+
223
+ if set(sys.modules) != self._cached_sys_modules:
224
+ modules_paths = {
225
+ name: self._exclude_blacklisted_paths(get_module_paths(module))
226
+ for name, module in dict(sys.modules).items()
227
+ }
228
+ self._cached_sys_modules = set(sys.modules)
229
+ self._register_necessary_watchers(modules_paths)
230
+
231
+ def _register_necessary_watchers(self, module_paths: dict[str, set[str]]) -> None:
232
+ for name, paths in module_paths.items():
233
+ for path in paths:
234
+ if self._file_should_be_watched(path):
235
+ self._register_watcher(os.path.realpath(path), name)
236
+
237
+ def _exclude_blacklisted_paths(self, paths: set[str]) -> set[str]:
238
+ return {p for p in paths if not self._folder_black_list.is_blacklisted(p)}
239
+
240
+
241
+ def get_module_paths(module: ModuleType) -> set[str]:
242
+ paths_extractors: list[Callable[[ModuleType], list[str | None]]] = [
243
+ # https://docs.python.org/3/reference/datamodel.html
244
+ # __file__ is the pathname of the file from which the module was loaded
245
+ # if it was loaded from a file.
246
+ # The __file__ attribute may be missing for certain types of modules
247
+ lambda m: [m.__file__] if hasattr(m, "__file__") else [],
248
+ # https://docs.python.org/3/reference/import.html#__spec__
249
+ # The __spec__ attribute is set to the module spec that was used
250
+ # when importing the module. one exception is __main__,
251
+ # where __spec__ is set to None in some cases.
252
+ # https://www.python.org/dev/peps/pep-0451/#id16
253
+ # "origin" in an import context means the system
254
+ # (or resource within a system) from which a module originates
255
+ # ... It is up to the loader to decide on how to interpret
256
+ # and use a module's origin, if at all.
257
+ lambda m: (
258
+ [m.__spec__.origin]
259
+ if hasattr(m, "__spec__") and m.__spec__ is not None
260
+ else []
261
+ ),
262
+ # https://www.python.org/dev/peps/pep-0420/
263
+ # Handling of "namespace packages" in which the __path__ attribute
264
+ # is a _NamespacePath object with a _path attribute containing
265
+ # the various paths of the package.
266
+ lambda m: (
267
+ list(m.__path__._path)
268
+ if hasattr(m, "__path__")
269
+ # This check prevents issues with torch classes:
270
+ # https://github.com/streamlit/streamlit/issues/10992
271
+ and type(m.__path__).__name__ == "_NamespacePath"
272
+ and hasattr(m.__path__, "_path")
273
+ else []
274
+ ),
275
+ ]
276
+
277
+ all_paths = set()
278
+ for extract_paths in paths_extractors:
279
+ potential_paths = []
280
+ try:
281
+ potential_paths = extract_paths(module)
282
+ except AttributeError:
283
+ # Some modules might not have __file__ or __spec__ attributes.
284
+ pass
285
+ except Exception:
286
+ _LOGGER.warning(
287
+ "Examining the path of %s raised:", module.__name__, exc_info=True
288
+ )
289
+
290
+ all_paths.update(
291
+ [os.path.realpath(str(p)) for p in potential_paths if _is_valid_path(p)]
292
+ )
293
+ return all_paths
294
+
295
+
296
+ def _is_valid_path(path: str | None) -> bool:
297
+ return isinstance(path, str) and (os.path.isfile(path) or os.path.isdir(path))
@@ -0,0 +1,244 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2026)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ import os
18
+ from typing import TYPE_CHECKING, TypeAlias
19
+
20
+ from streamlit import cli_util, config, env_util
21
+
22
+ if TYPE_CHECKING:
23
+ from collections.abc import Callable
24
+
25
+ from streamlit.watcher.event_based_path_watcher import EventBasedPathWatcher
26
+ from streamlit.watcher.polling_path_watcher import PollingPathWatcher
27
+
28
+
29
+ # local_sources_watcher.py caches the return value of
30
+ # get_default_path_watcher_class(), so it needs to differentiate between the
31
+ # cases where it:
32
+ # 1. has yet to call get_default_path_watcher_class()
33
+ # 2. has called get_default_path_watcher_class(), which returned that no
34
+ # path watcher should be installed.
35
+ # This forces us to define this stub class since the cached value equaling
36
+ # None corresponds to case 1 above.
37
+ class NoOpPathWatcher:
38
+ def __init__(
39
+ self,
40
+ _path_str: str,
41
+ _on_changed: Callable[[str], None],
42
+ *, # keyword-only arguments:
43
+ glob_pattern: str | None = None,
44
+ allow_nonexistent: bool = False,
45
+ ) -> None:
46
+ pass
47
+
48
+
49
+ # EventBasedPathWatcher will be a stub and have no functional
50
+ # implementation if its import failed (due to missing watchdog module),
51
+ # so we can't reference it directly in this type.
52
+ PathWatcherType: TypeAlias = (
53
+ type["EventBasedPathWatcher"] | type["PollingPathWatcher"] | type[NoOpPathWatcher]
54
+ )
55
+
56
+
57
+ def _is_watchdog_available() -> bool:
58
+ """Check if the watchdog module is installed."""
59
+ try:
60
+ import watchdog # noqa: F401
61
+
62
+ return True
63
+ except ImportError:
64
+ return False
65
+
66
+
67
+ def report_watchdog_availability() -> None:
68
+ if (
69
+ config.get_option("server.fileWatcherType") not in {"poll", "none"}
70
+ and not _is_watchdog_available()
71
+ ):
72
+ msg = "\n $ xcode-select --install" if env_util.IS_DARWIN else ""
73
+
74
+ cli_util.print_to_cli(
75
+ " For better performance, install the Watchdog module:",
76
+ fg="blue",
77
+ bold=True,
78
+ )
79
+ cli_util.print_to_cli(
80
+ f"""{msg}
81
+ $ pip install watchdog
82
+ """
83
+ )
84
+
85
+
86
+ def _watch_path(
87
+ path: str,
88
+ on_path_changed: Callable[[str], None],
89
+ watcher_type: str | None = None,
90
+ *, # keyword-only arguments:
91
+ glob_pattern: str | None = None,
92
+ allow_nonexistent: bool = False,
93
+ ) -> bool:
94
+ """Create a PathWatcher for the given path if we have a viable
95
+ PathWatcher class.
96
+
97
+ Parameters
98
+ ----------
99
+ path
100
+ Path to watch.
101
+ on_path_changed
102
+ Function that's called when the path changes.
103
+ watcher_type
104
+ Optional watcher_type string. If None, it will default to the
105
+ 'server.fileWatcherType` config option.
106
+ glob_pattern
107
+ Optional glob pattern to use when watching a directory. If set, only
108
+ files matching the pattern will be counted as being created/deleted
109
+ within the watched directory.
110
+ allow_nonexistent
111
+ If True, allow the file or directory at the given path to be
112
+ nonexistent.
113
+
114
+ Returns
115
+ -------
116
+ bool
117
+ True if the path is being watched, or False if we have no
118
+ PathWatcher class.
119
+ """
120
+ if watcher_type is None:
121
+ watcher_type = config.get_option("server.fileWatcherType")
122
+
123
+ watcher_class = get_path_watcher_class(watcher_type)
124
+ if watcher_class is NoOpPathWatcher:
125
+ return False
126
+
127
+ watcher_class(
128
+ path,
129
+ on_path_changed,
130
+ glob_pattern=glob_pattern,
131
+ allow_nonexistent=allow_nonexistent,
132
+ )
133
+ return True
134
+
135
+
136
+ def watch_file(
137
+ path: str,
138
+ on_file_changed: Callable[[str], None],
139
+ watcher_type: str | None = None,
140
+ *, # keyword-only arguments:
141
+ allow_nonexistent: bool = False,
142
+ ) -> bool:
143
+ """Watch a file for changes.
144
+
145
+ The callback is invoked when the file's content changes (detected via MD5).
146
+ If allow_nonexistent is True, the watcher will also detect when the file
147
+ is created.
148
+
149
+ Parameters
150
+ ----------
151
+ path
152
+ Path to the file to watch.
153
+ on_file_changed
154
+ Callback invoked with the file path when changes are detected.
155
+ watcher_type
156
+ Optional watcher type ('watchdog', 'poll', 'auto', or 'none').
157
+ allow_nonexistent
158
+ If True, watch for file creation even if the file doesn't exist yet.
159
+ Note: The file's parent directory must exist for watching to work.
160
+ If the parent directory doesn't exist, the watcher silently skips
161
+ watching (the file can't be created without its parent directory).
162
+
163
+ Returns
164
+ -------
165
+ bool
166
+ True if the watcher was successfully created.
167
+ """
168
+ return _watch_path(
169
+ path, on_file_changed, watcher_type, allow_nonexistent=allow_nonexistent
170
+ )
171
+
172
+
173
+ def watch_dir(
174
+ path: str,
175
+ on_dir_changed: Callable[[str], None],
176
+ watcher_type: str | None = None,
177
+ *, # keyword-only arguments:
178
+ glob_pattern: str | None = None,
179
+ allow_nonexistent: bool = False,
180
+ ) -> bool:
181
+ """Watch a directory for file changes.
182
+
183
+ The callback is invoked for any file activity within the directory,
184
+ including file creation, deletion, and content modifications. The callback
185
+ receives the path of the actual changed file (not the directory path).
186
+
187
+ Note: The glob_pattern parameter only affects the initial state detection
188
+ (which files are counted when determining if the directory changed). It does
189
+ NOT filter which file events trigger the callback - all file events in the
190
+ directory will invoke the callback regardless of glob_pattern.
191
+
192
+ Parameters
193
+ ----------
194
+ path
195
+ Path to the directory to watch.
196
+ on_dir_changed
197
+ Callback invoked with the changed file path when changes are detected.
198
+ watcher_type
199
+ Optional watcher type ('watchdog', 'poll', 'auto', or 'none').
200
+ glob_pattern
201
+ Glob pattern for initial state detection (e.g., "*.py"). Does not
202
+ filter runtime events.
203
+ allow_nonexistent
204
+ If True, watch for directory creation even if it doesn't exist yet.
205
+
206
+ Returns
207
+ -------
208
+ bool
209
+ True if the watcher was successfully created.
210
+ """
211
+ # Add a trailing slash to the path to ensure
212
+ # that its interpreted as a directory.
213
+ path = os.path.join(path, "")
214
+
215
+ return _watch_path(
216
+ path,
217
+ on_dir_changed,
218
+ watcher_type,
219
+ glob_pattern=glob_pattern,
220
+ allow_nonexistent=allow_nonexistent,
221
+ )
222
+
223
+
224
+ def get_default_path_watcher_class() -> PathWatcherType:
225
+ """Return the class to use for path changes notifications, based on the
226
+ server.fileWatcherType config option.
227
+ """
228
+ return get_path_watcher_class(config.get_option("server.fileWatcherType"))
229
+
230
+
231
+ def get_path_watcher_class(watcher_type: str) -> PathWatcherType:
232
+ """Return the PathWatcher class that corresponds to the given watcher_type
233
+ string. Acceptable values are 'auto', 'watchdog', 'poll' and 'none'.
234
+ """
235
+ if watcher_type in {"watchdog", "auto"} and _is_watchdog_available():
236
+ # Lazy-import this module to prevent unnecessary imports of the watchdog package.
237
+ from streamlit.watcher.event_based_path_watcher import EventBasedPathWatcher
238
+
239
+ return EventBasedPathWatcher
240
+ if watcher_type in {"auto", "poll"}:
241
+ from streamlit.watcher.polling_path_watcher import PollingPathWatcher
242
+
243
+ return PollingPathWatcher
244
+ return NoOpPathWatcher
@@ -0,0 +1,138 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2026)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """A class that watches a given path via polling."""
16
+
17
+ from __future__ import annotations
18
+
19
+ import time
20
+ from concurrent.futures import ThreadPoolExecutor
21
+ from pathlib import Path
22
+ from typing import TYPE_CHECKING, Final
23
+
24
+ from streamlit.errors import StreamlitMaxRetriesError
25
+ from streamlit.logger import get_logger
26
+ from streamlit.util import repr_
27
+ from streamlit.watcher import util
28
+
29
+ if TYPE_CHECKING:
30
+ from collections.abc import Callable
31
+
32
+ _LOGGER: Final = get_logger(__name__)
33
+
34
+ _MAX_WORKERS: Final = 4
35
+ _POLLING_PERIOD_SECS: Final = 0.2
36
+
37
+
38
+ class PollingPathWatcher:
39
+ """Watches a path on disk via a polling loop."""
40
+
41
+ _executor = ThreadPoolExecutor(max_workers=_MAX_WORKERS)
42
+
43
+ @staticmethod
44
+ def close_all() -> None:
45
+ """Close top-level watcher object.
46
+
47
+ This is a no-op, and exists for interface parity with
48
+ EventBasedPathWatcher.
49
+ """
50
+ _LOGGER.debug("Watcher closed")
51
+
52
+ def __init__(
53
+ self,
54
+ path: str,
55
+ on_changed: Callable[[str], None],
56
+ *, # keyword-only arguments:
57
+ glob_pattern: str | None = None,
58
+ allow_nonexistent: bool = False,
59
+ ) -> None:
60
+ """Constructor.
61
+
62
+ You do not need to retain a reference to a PollingPathWatcher to
63
+ prevent it from being garbage collected. (The global _executor object
64
+ retains references to all active instances.)
65
+ """
66
+ # TODO(vdonato): Modernize this by switching to pathlib.
67
+ self._path = Path(path) # Changed to pathlib.Path
68
+ self._on_changed = on_changed
69
+
70
+ self._glob_pattern = glob_pattern
71
+ self._allow_nonexistent = allow_nonexistent
72
+
73
+ self._active = True
74
+
75
+ self._modification_time = util.path_modification_time(
76
+ str(self._path), self._allow_nonexistent
77
+ )
78
+ self._md5 = util.calc_md5_with_blocking_retries(
79
+ str(self._path),
80
+ glob_pattern=self._glob_pattern,
81
+ allow_nonexistent=self._allow_nonexistent,
82
+ )
83
+ self._schedule()
84
+
85
+ def __repr__(self) -> str:
86
+ return repr_(self)
87
+
88
+ def _schedule(self) -> None:
89
+ def task() -> None:
90
+ time.sleep(_POLLING_PERIOD_SECS)
91
+ self._check_if_path_changed()
92
+
93
+ PollingPathWatcher._executor.submit(task)
94
+
95
+ def _check_if_path_changed(self) -> None:
96
+ if not self._active:
97
+ # Don't call self._schedule()
98
+ return
99
+
100
+ try:
101
+ modification_time = util.path_modification_time(
102
+ str(self._path), self._allow_nonexistent
103
+ )
104
+ # We add the modification_time > 0.0 check since on some file systems
105
+ # (s3fs/fuse), modification_time is always 0.0 because of file system
106
+ # limitations.
107
+ if modification_time > 0.0 and modification_time <= self._modification_time:
108
+ self._schedule()
109
+ return
110
+
111
+ self._modification_time = modification_time
112
+
113
+ md5 = util.calc_md5_with_blocking_retries(
114
+ str(self._path),
115
+ glob_pattern=self._glob_pattern,
116
+ allow_nonexistent=self._allow_nonexistent,
117
+ )
118
+ if md5 == self._md5:
119
+ self._schedule()
120
+ return
121
+ except StreamlitMaxRetriesError as ex:
122
+ _LOGGER.debug(
123
+ "Ignoring file change. Failed to calculate MD5 for path %s",
124
+ self._path,
125
+ exc_info=ex,
126
+ )
127
+ return
128
+
129
+ self._md5 = md5
130
+
131
+ _LOGGER.debug("Change detected: %s", self._path)
132
+ self._on_changed(str(self._path))
133
+
134
+ self._schedule()
135
+
136
+ def close(self) -> None:
137
+ """Stop watching the file system."""
138
+ self._active = False