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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (563) hide show
  1. streamlit/__init__.py +306 -0
  2. streamlit/__main__.py +20 -0
  3. streamlit/auth_util.py +218 -0
  4. streamlit/cli_util.py +105 -0
  5. streamlit/column_config.py +56 -0
  6. streamlit/commands/__init__.py +13 -0
  7. streamlit/commands/echo.py +126 -0
  8. streamlit/commands/execution_control.py +238 -0
  9. streamlit/commands/experimental_query_params.py +169 -0
  10. streamlit/commands/logo.py +189 -0
  11. streamlit/commands/navigation.py +385 -0
  12. streamlit/commands/page_config.py +311 -0
  13. streamlit/components/__init__.py +13 -0
  14. streamlit/components/lib/__init__.py +13 -0
  15. streamlit/components/lib/local_component_registry.py +84 -0
  16. streamlit/components/types/__init__.py +13 -0
  17. streamlit/components/types/base_component_registry.py +99 -0
  18. streamlit/components/types/base_custom_component.py +150 -0
  19. streamlit/components/v1/__init__.py +31 -0
  20. streamlit/components/v1/component_arrow.py +141 -0
  21. streamlit/components/v1/component_registry.py +130 -0
  22. streamlit/components/v1/components.py +38 -0
  23. streamlit/components/v1/custom_component.py +243 -0
  24. streamlit/config.py +1513 -0
  25. streamlit/config_option.py +311 -0
  26. streamlit/config_util.py +177 -0
  27. streamlit/connections/__init__.py +28 -0
  28. streamlit/connections/base_connection.py +174 -0
  29. streamlit/connections/snowflake_connection.py +562 -0
  30. streamlit/connections/snowpark_connection.py +213 -0
  31. streamlit/connections/sql_connection.py +425 -0
  32. streamlit/connections/util.py +97 -0
  33. streamlit/cursor.py +210 -0
  34. streamlit/dataframe_util.py +1416 -0
  35. streamlit/delta_generator.py +602 -0
  36. streamlit/delta_generator_singletons.py +204 -0
  37. streamlit/deprecation_util.py +209 -0
  38. streamlit/development.py +21 -0
  39. streamlit/elements/__init__.py +13 -0
  40. streamlit/elements/alert.py +234 -0
  41. streamlit/elements/arrow.py +962 -0
  42. streamlit/elements/balloons.py +47 -0
  43. streamlit/elements/bokeh_chart.py +133 -0
  44. streamlit/elements/code.py +114 -0
  45. streamlit/elements/deck_gl_json_chart.py +546 -0
  46. streamlit/elements/dialog_decorator.py +267 -0
  47. streamlit/elements/doc_string.py +558 -0
  48. streamlit/elements/empty.py +130 -0
  49. streamlit/elements/exception.py +331 -0
  50. streamlit/elements/form.py +354 -0
  51. streamlit/elements/graphviz_chart.py +150 -0
  52. streamlit/elements/heading.py +302 -0
  53. streamlit/elements/html.py +105 -0
  54. streamlit/elements/iframe.py +191 -0
  55. streamlit/elements/image.py +196 -0
  56. streamlit/elements/json.py +139 -0
  57. streamlit/elements/layouts.py +879 -0
  58. streamlit/elements/lib/__init__.py +13 -0
  59. streamlit/elements/lib/built_in_chart_utils.py +1157 -0
  60. streamlit/elements/lib/color_util.py +263 -0
  61. streamlit/elements/lib/column_config_utils.py +542 -0
  62. streamlit/elements/lib/column_types.py +2188 -0
  63. streamlit/elements/lib/dialog.py +147 -0
  64. streamlit/elements/lib/dicttools.py +154 -0
  65. streamlit/elements/lib/event_utils.py +37 -0
  66. streamlit/elements/lib/file_uploader_utils.py +66 -0
  67. streamlit/elements/lib/form_utils.py +77 -0
  68. streamlit/elements/lib/image_utils.py +441 -0
  69. streamlit/elements/lib/js_number.py +105 -0
  70. streamlit/elements/lib/mutable_status_container.py +183 -0
  71. streamlit/elements/lib/options_selector_utils.py +250 -0
  72. streamlit/elements/lib/pandas_styler_utils.py +274 -0
  73. streamlit/elements/lib/policies.py +194 -0
  74. streamlit/elements/lib/streamlit_plotly_theme.py +207 -0
  75. streamlit/elements/lib/subtitle_utils.py +176 -0
  76. streamlit/elements/lib/utils.py +250 -0
  77. streamlit/elements/map.py +508 -0
  78. streamlit/elements/markdown.py +277 -0
  79. streamlit/elements/media.py +793 -0
  80. streamlit/elements/metric.py +301 -0
  81. streamlit/elements/plotly_chart.py +546 -0
  82. streamlit/elements/progress.py +156 -0
  83. streamlit/elements/pyplot.py +194 -0
  84. streamlit/elements/snow.py +47 -0
  85. streamlit/elements/spinner.py +113 -0
  86. streamlit/elements/text.py +76 -0
  87. streamlit/elements/toast.py +98 -0
  88. streamlit/elements/vega_charts.py +1984 -0
  89. streamlit/elements/widgets/__init__.py +13 -0
  90. streamlit/elements/widgets/audio_input.py +310 -0
  91. streamlit/elements/widgets/button.py +1123 -0
  92. streamlit/elements/widgets/button_group.py +1008 -0
  93. streamlit/elements/widgets/camera_input.py +263 -0
  94. streamlit/elements/widgets/chat.py +647 -0
  95. streamlit/elements/widgets/checkbox.py +352 -0
  96. streamlit/elements/widgets/color_picker.py +265 -0
  97. streamlit/elements/widgets/data_editor.py +983 -0
  98. streamlit/elements/widgets/file_uploader.py +486 -0
  99. streamlit/elements/widgets/multiselect.py +338 -0
  100. streamlit/elements/widgets/number_input.py +545 -0
  101. streamlit/elements/widgets/radio.py +407 -0
  102. streamlit/elements/widgets/select_slider.py +437 -0
  103. streamlit/elements/widgets/selectbox.py +366 -0
  104. streamlit/elements/widgets/slider.py +880 -0
  105. streamlit/elements/widgets/text_widgets.py +628 -0
  106. streamlit/elements/widgets/time_widgets.py +970 -0
  107. streamlit/elements/write.py +574 -0
  108. streamlit/emojis.py +34 -0
  109. streamlit/env_util.py +61 -0
  110. streamlit/error_util.py +105 -0
  111. streamlit/errors.py +452 -0
  112. streamlit/external/__init__.py +13 -0
  113. streamlit/external/langchain/__init__.py +23 -0
  114. streamlit/external/langchain/streamlit_callback_handler.py +406 -0
  115. streamlit/file_util.py +247 -0
  116. streamlit/git_util.py +173 -0
  117. streamlit/hello/__init__.py +13 -0
  118. streamlit/hello/animation_demo.py +82 -0
  119. streamlit/hello/dataframe_demo.py +71 -0
  120. streamlit/hello/hello.py +37 -0
  121. streamlit/hello/mapping_demo.py +114 -0
  122. streamlit/hello/plotting_demo.py +55 -0
  123. streamlit/hello/streamlit_app.py +55 -0
  124. streamlit/hello/utils.py +28 -0
  125. streamlit/logger.py +130 -0
  126. streamlit/material_icon_names.py +25 -0
  127. streamlit/navigation/__init__.py +13 -0
  128. streamlit/navigation/page.py +302 -0
  129. streamlit/net_util.py +125 -0
  130. streamlit/platform.py +33 -0
  131. streamlit/proto/Alert_pb2.py +29 -0
  132. streamlit/proto/Alert_pb2.pyi +90 -0
  133. streamlit/proto/AppPage_pb2.py +27 -0
  134. streamlit/proto/AppPage_pb2.pyi +64 -0
  135. streamlit/proto/ArrowNamedDataSet_pb2.py +28 -0
  136. streamlit/proto/ArrowNamedDataSet_pb2.pyi +57 -0
  137. streamlit/proto/ArrowVegaLiteChart_pb2.py +29 -0
  138. streamlit/proto/ArrowVegaLiteChart_pb2.pyi +84 -0
  139. streamlit/proto/Arrow_pb2.py +33 -0
  140. streamlit/proto/Arrow_pb2.pyi +188 -0
  141. streamlit/proto/AudioInput_pb2.py +28 -0
  142. streamlit/proto/AudioInput_pb2.pyi +58 -0
  143. streamlit/proto/Audio_pb2.py +27 -0
  144. streamlit/proto/Audio_pb2.pyi +58 -0
  145. streamlit/proto/AuthRedirect_pb2.py +27 -0
  146. streamlit/proto/AuthRedirect_pb2.pyi +41 -0
  147. streamlit/proto/AutoRerun_pb2.py +27 -0
  148. streamlit/proto/AutoRerun_pb2.pyi +45 -0
  149. streamlit/proto/BackMsg_pb2.py +29 -0
  150. streamlit/proto/BackMsg_pb2.pyi +105 -0
  151. streamlit/proto/Balloons_pb2.py +27 -0
  152. streamlit/proto/Balloons_pb2.pyi +43 -0
  153. streamlit/proto/Block_pb2.py +53 -0
  154. streamlit/proto/Block_pb2.pyi +322 -0
  155. streamlit/proto/BokehChart_pb2.py +27 -0
  156. streamlit/proto/BokehChart_pb2.pyi +49 -0
  157. streamlit/proto/ButtonGroup_pb2.py +36 -0
  158. streamlit/proto/ButtonGroup_pb2.pyi +169 -0
  159. streamlit/proto/Button_pb2.py +27 -0
  160. streamlit/proto/Button_pb2.pyi +71 -0
  161. streamlit/proto/CameraInput_pb2.py +28 -0
  162. streamlit/proto/CameraInput_pb2.pyi +58 -0
  163. streamlit/proto/ChatInput_pb2.py +31 -0
  164. streamlit/proto/ChatInput_pb2.pyi +111 -0
  165. streamlit/proto/Checkbox_pb2.py +30 -0
  166. streamlit/proto/Checkbox_pb2.pyi +90 -0
  167. streamlit/proto/ClientState_pb2.py +30 -0
  168. streamlit/proto/ClientState_pb2.pyi +90 -0
  169. streamlit/proto/Code_pb2.py +27 -0
  170. streamlit/proto/Code_pb2.pyi +55 -0
  171. streamlit/proto/ColorPicker_pb2.py +28 -0
  172. streamlit/proto/ColorPicker_pb2.pyi +67 -0
  173. streamlit/proto/Common_pb2.py +51 -0
  174. streamlit/proto/Common_pb2.pyi +293 -0
  175. streamlit/proto/Components_pb2.py +35 -0
  176. streamlit/proto/Components_pb2.pyi +172 -0
  177. streamlit/proto/DataFrame_pb2.py +56 -0
  178. streamlit/proto/DataFrame_pb2.pyi +397 -0
  179. streamlit/proto/DateInput_pb2.py +28 -0
  180. streamlit/proto/DateInput_pb2.pyi +83 -0
  181. streamlit/proto/DeckGlJsonChart_pb2.py +29 -0
  182. streamlit/proto/DeckGlJsonChart_pb2.pyi +102 -0
  183. streamlit/proto/Delta_pb2.py +31 -0
  184. streamlit/proto/Delta_pb2.pyi +74 -0
  185. streamlit/proto/DocString_pb2.py +29 -0
  186. streamlit/proto/DocString_pb2.pyi +93 -0
  187. streamlit/proto/DownloadButton_pb2.py +27 -0
  188. streamlit/proto/DownloadButton_pb2.pyi +70 -0
  189. streamlit/proto/Element_pb2.py +78 -0
  190. streamlit/proto/Element_pb2.pyi +312 -0
  191. streamlit/proto/Empty_pb2.py +27 -0
  192. streamlit/proto/Empty_pb2.pyi +36 -0
  193. streamlit/proto/Exception_pb2.py +27 -0
  194. streamlit/proto/Exception_pb2.pyi +72 -0
  195. streamlit/proto/Favicon_pb2.py +27 -0
  196. streamlit/proto/Favicon_pb2.pyi +40 -0
  197. streamlit/proto/FileUploader_pb2.py +28 -0
  198. streamlit/proto/FileUploader_pb2.pyi +78 -0
  199. streamlit/proto/ForwardMsg_pb2.py +53 -0
  200. streamlit/proto/ForwardMsg_pb2.pyi +293 -0
  201. streamlit/proto/GitInfo_pb2.py +29 -0
  202. streamlit/proto/GitInfo_pb2.pyi +83 -0
  203. streamlit/proto/GraphVizChart_pb2.py +27 -0
  204. streamlit/proto/GraphVizChart_pb2.pyi +53 -0
  205. streamlit/proto/Heading_pb2.py +27 -0
  206. streamlit/proto/Heading_pb2.pyi +56 -0
  207. streamlit/proto/Html_pb2.py +27 -0
  208. streamlit/proto/Html_pb2.pyi +42 -0
  209. streamlit/proto/IFrame_pb2.py +27 -0
  210. streamlit/proto/IFrame_pb2.pyi +59 -0
  211. streamlit/proto/Image_pb2.py +29 -0
  212. streamlit/proto/Image_pb2.pyi +84 -0
  213. streamlit/proto/Json_pb2.py +27 -0
  214. streamlit/proto/Json_pb2.pyi +53 -0
  215. streamlit/proto/LabelVisibilityMessage_pb2.py +29 -0
  216. streamlit/proto/LabelVisibilityMessage_pb2.pyi +68 -0
  217. streamlit/proto/LinkButton_pb2.py +27 -0
  218. streamlit/proto/LinkButton_pb2.pyi +58 -0
  219. streamlit/proto/Logo_pb2.py +27 -0
  220. streamlit/proto/Logo_pb2.pyi +51 -0
  221. streamlit/proto/Markdown_pb2.py +29 -0
  222. streamlit/proto/Markdown_pb2.pyi +86 -0
  223. streamlit/proto/Metric_pb2.py +32 -0
  224. streamlit/proto/Metric_pb2.pyi +101 -0
  225. streamlit/proto/MetricsEvent_pb2.py +30 -0
  226. streamlit/proto/MetricsEvent_pb2.pyi +200 -0
  227. streamlit/proto/MultiSelect_pb2.py +28 -0
  228. streamlit/proto/MultiSelect_pb2.pyi +81 -0
  229. streamlit/proto/NamedDataSet_pb2.py +28 -0
  230. streamlit/proto/NamedDataSet_pb2.pyi +59 -0
  231. streamlit/proto/Navigation_pb2.py +30 -0
  232. streamlit/proto/Navigation_pb2.pyi +84 -0
  233. streamlit/proto/NewSession_pb2.py +51 -0
  234. streamlit/proto/NewSession_pb2.pyi +481 -0
  235. streamlit/proto/NumberInput_pb2.py +30 -0
  236. streamlit/proto/NumberInput_pb2.pyi +121 -0
  237. streamlit/proto/PageConfig_pb2.py +33 -0
  238. streamlit/proto/PageConfig_pb2.pyi +126 -0
  239. streamlit/proto/PageInfo_pb2.py +27 -0
  240. streamlit/proto/PageInfo_pb2.pyi +43 -0
  241. streamlit/proto/PageLink_pb2.py +27 -0
  242. streamlit/proto/PageLink_pb2.pyi +63 -0
  243. streamlit/proto/PageNotFound_pb2.py +27 -0
  244. streamlit/proto/PageNotFound_pb2.pyi +42 -0
  245. streamlit/proto/PageProfile_pb2.py +31 -0
  246. streamlit/proto/PageProfile_pb2.pyi +127 -0
  247. streamlit/proto/PagesChanged_pb2.py +28 -0
  248. streamlit/proto/PagesChanged_pb2.pyi +48 -0
  249. streamlit/proto/ParentMessage_pb2.py +27 -0
  250. streamlit/proto/ParentMessage_pb2.pyi +46 -0
  251. streamlit/proto/PlotlyChart_pb2.py +31 -0
  252. streamlit/proto/PlotlyChart_pb2.pyi +131 -0
  253. streamlit/proto/Progress_pb2.py +27 -0
  254. streamlit/proto/Progress_pb2.pyi +43 -0
  255. streamlit/proto/Radio_pb2.py +28 -0
  256. streamlit/proto/Radio_pb2.pyi +84 -0
  257. streamlit/proto/RootContainer_pb2.py +27 -0
  258. streamlit/proto/RootContainer_pb2.pyi +56 -0
  259. streamlit/proto/Selectbox_pb2.py +28 -0
  260. streamlit/proto/Selectbox_pb2.pyi +80 -0
  261. streamlit/proto/SessionEvent_pb2.py +28 -0
  262. streamlit/proto/SessionEvent_pb2.pyi +62 -0
  263. streamlit/proto/SessionStatus_pb2.py +27 -0
  264. streamlit/proto/SessionStatus_pb2.pyi +57 -0
  265. streamlit/proto/Skeleton_pb2.py +29 -0
  266. streamlit/proto/Skeleton_pb2.pyi +71 -0
  267. streamlit/proto/Slider_pb2.py +32 -0
  268. streamlit/proto/Slider_pb2.pyi +142 -0
  269. streamlit/proto/Snow_pb2.py +27 -0
  270. streamlit/proto/Snow_pb2.pyi +43 -0
  271. streamlit/proto/Spinner_pb2.py +27 -0
  272. streamlit/proto/Spinner_pb2.pyi +49 -0
  273. streamlit/proto/TextArea_pb2.py +28 -0
  274. streamlit/proto/TextArea_pb2.pyi +80 -0
  275. streamlit/proto/TextInput_pb2.py +30 -0
  276. streamlit/proto/TextInput_pb2.pyi +107 -0
  277. streamlit/proto/Text_pb2.py +27 -0
  278. streamlit/proto/Text_pb2.pyi +46 -0
  279. streamlit/proto/TimeInput_pb2.py +28 -0
  280. streamlit/proto/TimeInput_pb2.pyi +74 -0
  281. streamlit/proto/Toast_pb2.py +27 -0
  282. streamlit/proto/Toast_pb2.pyi +45 -0
  283. streamlit/proto/VegaLiteChart_pb2.py +29 -0
  284. streamlit/proto/VegaLiteChart_pb2.pyi +71 -0
  285. streamlit/proto/Video_pb2.py +31 -0
  286. streamlit/proto/Video_pb2.pyi +117 -0
  287. streamlit/proto/WidgetStates_pb2.py +31 -0
  288. streamlit/proto/WidgetStates_pb2.pyi +126 -0
  289. streamlit/proto/__init__.py +15 -0
  290. streamlit/proto/openmetrics_data_model_pb2.py +60 -0
  291. streamlit/proto/openmetrics_data_model_pb2.pyi +522 -0
  292. streamlit/py.typed +0 -0
  293. streamlit/runtime/__init__.py +50 -0
  294. streamlit/runtime/app_session.py +982 -0
  295. streamlit/runtime/caching/__init__.py +98 -0
  296. streamlit/runtime/caching/cache_data_api.py +665 -0
  297. streamlit/runtime/caching/cache_errors.py +142 -0
  298. streamlit/runtime/caching/cache_resource_api.py +527 -0
  299. streamlit/runtime/caching/cache_type.py +33 -0
  300. streamlit/runtime/caching/cache_utils.py +523 -0
  301. streamlit/runtime/caching/cached_message_replay.py +290 -0
  302. streamlit/runtime/caching/hashing.py +637 -0
  303. streamlit/runtime/caching/legacy_cache_api.py +169 -0
  304. streamlit/runtime/caching/storage/__init__.py +29 -0
  305. streamlit/runtime/caching/storage/cache_storage_protocol.py +239 -0
  306. streamlit/runtime/caching/storage/dummy_cache_storage.py +60 -0
  307. streamlit/runtime/caching/storage/in_memory_cache_storage_wrapper.py +145 -0
  308. streamlit/runtime/caching/storage/local_disk_cache_storage.py +223 -0
  309. streamlit/runtime/connection_factory.py +436 -0
  310. streamlit/runtime/context.py +280 -0
  311. streamlit/runtime/credentials.py +364 -0
  312. streamlit/runtime/forward_msg_cache.py +296 -0
  313. streamlit/runtime/forward_msg_queue.py +240 -0
  314. streamlit/runtime/fragment.py +477 -0
  315. streamlit/runtime/media_file_manager.py +234 -0
  316. streamlit/runtime/media_file_storage.py +143 -0
  317. streamlit/runtime/memory_media_file_storage.py +181 -0
  318. streamlit/runtime/memory_session_storage.py +77 -0
  319. streamlit/runtime/memory_uploaded_file_manager.py +138 -0
  320. streamlit/runtime/metrics_util.py +486 -0
  321. streamlit/runtime/pages_manager.py +165 -0
  322. streamlit/runtime/runtime.py +792 -0
  323. streamlit/runtime/runtime_util.py +106 -0
  324. streamlit/runtime/script_data.py +46 -0
  325. streamlit/runtime/scriptrunner/__init__.py +38 -0
  326. streamlit/runtime/scriptrunner/exec_code.py +159 -0
  327. streamlit/runtime/scriptrunner/magic.py +273 -0
  328. streamlit/runtime/scriptrunner/magic_funcs.py +32 -0
  329. streamlit/runtime/scriptrunner/script_cache.py +89 -0
  330. streamlit/runtime/scriptrunner/script_runner.py +756 -0
  331. streamlit/runtime/scriptrunner_utils/__init__.py +19 -0
  332. streamlit/runtime/scriptrunner_utils/exceptions.py +48 -0
  333. streamlit/runtime/scriptrunner_utils/script_requests.py +307 -0
  334. streamlit/runtime/scriptrunner_utils/script_run_context.py +287 -0
  335. streamlit/runtime/secrets.py +534 -0
  336. streamlit/runtime/session_manager.py +394 -0
  337. streamlit/runtime/state/__init__.py +41 -0
  338. streamlit/runtime/state/common.py +191 -0
  339. streamlit/runtime/state/query_params.py +205 -0
  340. streamlit/runtime/state/query_params_proxy.py +218 -0
  341. streamlit/runtime/state/safe_session_state.py +138 -0
  342. streamlit/runtime/state/session_state.py +772 -0
  343. streamlit/runtime/state/session_state_proxy.py +153 -0
  344. streamlit/runtime/state/widgets.py +135 -0
  345. streamlit/runtime/stats.py +109 -0
  346. streamlit/runtime/uploaded_file_manager.py +148 -0
  347. streamlit/runtime/websocket_session_manager.py +167 -0
  348. streamlit/source_util.py +98 -0
  349. streamlit/static/favicon.png +0 -0
  350. streamlit/static/index.html +61 -0
  351. streamlit/static/static/css/index.Bmkmz40k.css +1 -0
  352. streamlit/static/static/css/index.DpJG_94W.css +1 -0
  353. streamlit/static/static/css/index.DzuxGC_t.css +1 -0
  354. streamlit/static/static/js/FileDownload.esm.Bp9m5jrx.js +1 -0
  355. streamlit/static/static/js/FileHelper.D_3pbilj.js +5 -0
  356. streamlit/static/static/js/FormClearHelper.Ct2rwLXo.js +1 -0
  357. streamlit/static/static/js/Hooks.BKdzj5MJ.js +1 -0
  358. streamlit/static/static/js/InputInstructions.DB3QGNJP.js +1 -0
  359. streamlit/static/static/js/ProgressBar.D40A5xc2.js +2 -0
  360. streamlit/static/static/js/RenderInPortalIfExists.DLUCooTN.js +1 -0
  361. streamlit/static/static/js/Toolbar.BiGGIQun.js +1 -0
  362. streamlit/static/static/js/UploadFileInfo.C-jY39rj.js +1 -0
  363. streamlit/static/static/js/base-input.CQBQT24M.js +4 -0
  364. streamlit/static/static/js/checkbox.Buj8gd_M.js +9 -0
  365. streamlit/static/static/js/createDownloadLinkElement.DZMwyjvU.js +1 -0
  366. streamlit/static/static/js/createSuper.CesK3I23.js +1 -0
  367. streamlit/static/static/js/data-grid-overlay-editor.B69OOFM4.js +1 -0
  368. streamlit/static/static/js/downloader.BZQhlBNT.js +1 -0
  369. streamlit/static/static/js/es6.D9Zhqujy.js +2 -0
  370. streamlit/static/static/js/iframeResizer.contentWindow.CAzcBpCC.js +1 -0
  371. streamlit/static/static/js/index.08vcOOvb.js +1 -0
  372. streamlit/static/static/js/index.0uqKfJUS.js +1 -0
  373. streamlit/static/static/js/index.B02M5u69.js +203 -0
  374. streamlit/static/static/js/index.B7mcZKMx.js +1 -0
  375. streamlit/static/static/js/index.BAQDHFA_.js +1 -0
  376. streamlit/static/static/js/index.BI60cMVr.js +2 -0
  377. streamlit/static/static/js/index.BLug2inK.js +1 -0
  378. streamlit/static/static/js/index.BM6TMY8g.js +2 -0
  379. streamlit/static/static/js/index.BZ9p1t7G.js +1 -0
  380. streamlit/static/static/js/index.BZqa87a1.js +2 -0
  381. streamlit/static/static/js/index.BcsRUzZZ.js +1 -0
  382. streamlit/static/static/js/index.BgVMiY_P.js +197 -0
  383. streamlit/static/static/js/index.BtuGy7By.js +6 -0
  384. streamlit/static/static/js/index.BuDuBmrs.js +1 -0
  385. streamlit/static/static/js/index.BvXU2oKV.js +1 -0
  386. streamlit/static/static/js/index.BxcwPacT.js +73 -0
  387. streamlit/static/static/js/index.CWX8KB81.js +1 -0
  388. streamlit/static/static/js/index.CXzZTo_q.js +1 -0
  389. streamlit/static/static/js/index.CcRWp_KL.js +1 -0
  390. streamlit/static/static/js/index.Cd-_xe55.js +3 -0
  391. streamlit/static/static/js/index.CdG2PXln.js +4537 -0
  392. streamlit/static/static/js/index.CjXvXmcP.js +1 -0
  393. streamlit/static/static/js/index.D1HZENZx.js +776 -0
  394. streamlit/static/static/js/index.D21Efo64.js +1617 -0
  395. streamlit/static/static/js/index.D9WgGVBx.js +7 -0
  396. streamlit/static/static/js/index.DEcsHtvb.js +12 -0
  397. streamlit/static/static/js/index.DFeMfr_K.js +1 -0
  398. streamlit/static/static/js/index.DHFBoItz.js +1 -0
  399. streamlit/static/static/js/index.D_PrBKnJ.js +3 -0
  400. streamlit/static/static/js/index.DmuRkekN.js +3855 -0
  401. streamlit/static/static/js/index.Do6eY8sf.js +1 -0
  402. streamlit/static/static/js/index.Dz3lP2P-.js +1 -0
  403. streamlit/static/static/js/index.Dz_UqF-s.js +1 -0
  404. streamlit/static/static/js/index.GkSUsPhJ.js +1 -0
  405. streamlit/static/static/js/index.H1U1IC_d.js +3 -0
  406. streamlit/static/static/js/index.g6p_4DPr.js +1 -0
  407. streamlit/static/static/js/index.g9x_GKss.js +1 -0
  408. streamlit/static/static/js/index.zo9jm08y.js +1 -0
  409. streamlit/static/static/js/input.DnaFglHq.js +2 -0
  410. streamlit/static/static/js/inputUtils.CQWz5UKz.js +1 -0
  411. streamlit/static/static/js/memory.Crb9x4-F.js +1 -0
  412. streamlit/static/static/js/mergeWith.ouAz0sK3.js +1 -0
  413. streamlit/static/static/js/number-overlay-editor._UaN-O48.js +9 -0
  414. streamlit/static/static/js/possibleConstructorReturn.CtGjGFHz.js +1 -0
  415. streamlit/static/static/js/sandbox.CBybYOhV.js +1 -0
  416. streamlit/static/static/js/sprintf.D7DtBTRn.js +1 -0
  417. streamlit/static/static/js/textarea.Cb_uJt5U.js +2 -0
  418. streamlit/static/static/js/threshold.DjX0wlsa.js +1 -0
  419. streamlit/static/static/js/timepicker.DKT7pfoF.js +4 -0
  420. streamlit/static/static/js/timer.CAwTRJ_g.js +1 -0
  421. streamlit/static/static/js/toConsumableArray.05Ikp13-.js +3 -0
  422. streamlit/static/static/js/uniqueId.D2FMWUEI.js +1 -0
  423. streamlit/static/static/js/useBasicWidgetState.urnZLANY.js +1 -0
  424. streamlit/static/static/js/useOnInputChange.BOKIIdJ1.js +1 -0
  425. streamlit/static/static/js/value.CgPGBV_l.js +1 -0
  426. streamlit/static/static/js/withFullScreenWrapper.C_N8J0Xx.js +1 -0
  427. streamlit/static/static/media/KaTeX_AMS-Regular.BQhdFMY1.woff2 +0 -0
  428. streamlit/static/static/media/KaTeX_AMS-Regular.DMm9YOAa.woff +0 -0
  429. streamlit/static/static/media/KaTeX_AMS-Regular.DRggAlZN.ttf +0 -0
  430. streamlit/static/static/media/KaTeX_Caligraphic-Bold.ATXxdsX0.ttf +0 -0
  431. streamlit/static/static/media/KaTeX_Caligraphic-Bold.BEiXGLvX.woff +0 -0
  432. streamlit/static/static/media/KaTeX_Caligraphic-Bold.Dq_IR9rO.woff2 +0 -0
  433. streamlit/static/static/media/KaTeX_Caligraphic-Regular.CTRA-rTL.woff +0 -0
  434. streamlit/static/static/media/KaTeX_Caligraphic-Regular.Di6jR-x-.woff2 +0 -0
  435. streamlit/static/static/media/KaTeX_Caligraphic-Regular.wX97UBjC.ttf +0 -0
  436. streamlit/static/static/media/KaTeX_Fraktur-Bold.BdnERNNW.ttf +0 -0
  437. streamlit/static/static/media/KaTeX_Fraktur-Bold.BsDP51OF.woff +0 -0
  438. streamlit/static/static/media/KaTeX_Fraktur-Bold.CL6g_b3V.woff2 +0 -0
  439. streamlit/static/static/media/KaTeX_Fraktur-Regular.CB_wures.ttf +0 -0
  440. streamlit/static/static/media/KaTeX_Fraktur-Regular.CTYiF6lA.woff2 +0 -0
  441. streamlit/static/static/media/KaTeX_Fraktur-Regular.Dxdc4cR9.woff +0 -0
  442. streamlit/static/static/media/KaTeX_Main-Bold.Cx986IdX.woff2 +0 -0
  443. streamlit/static/static/media/KaTeX_Main-Bold.Jm3AIy58.woff +0 -0
  444. streamlit/static/static/media/KaTeX_Main-Bold.waoOVXN0.ttf +0 -0
  445. streamlit/static/static/media/KaTeX_Main-BoldItalic.DxDJ3AOS.woff2 +0 -0
  446. streamlit/static/static/media/KaTeX_Main-BoldItalic.DzxPMmG6.ttf +0 -0
  447. streamlit/static/static/media/KaTeX_Main-BoldItalic.SpSLRI95.woff +0 -0
  448. streamlit/static/static/media/KaTeX_Main-Italic.3WenGoN9.ttf +0 -0
  449. streamlit/static/static/media/KaTeX_Main-Italic.BMLOBm91.woff +0 -0
  450. streamlit/static/static/media/KaTeX_Main-Italic.NWA7e6Wa.woff2 +0 -0
  451. streamlit/static/static/media/KaTeX_Main-Regular.B22Nviop.woff2 +0 -0
  452. streamlit/static/static/media/KaTeX_Main-Regular.Dr94JaBh.woff +0 -0
  453. streamlit/static/static/media/KaTeX_Main-Regular.ypZvNtVU.ttf +0 -0
  454. streamlit/static/static/media/KaTeX_Math-BoldItalic.B3XSjfu4.ttf +0 -0
  455. streamlit/static/static/media/KaTeX_Math-BoldItalic.CZnvNsCZ.woff2 +0 -0
  456. streamlit/static/static/media/KaTeX_Math-BoldItalic.iY-2wyZ7.woff +0 -0
  457. streamlit/static/static/media/KaTeX_Math-Italic.DA0__PXp.woff +0 -0
  458. streamlit/static/static/media/KaTeX_Math-Italic.flOr_0UB.ttf +0 -0
  459. streamlit/static/static/media/KaTeX_Math-Italic.t53AETM-.woff2 +0 -0
  460. streamlit/static/static/media/KaTeX_SansSerif-Bold.CFMepnvq.ttf +0 -0
  461. streamlit/static/static/media/KaTeX_SansSerif-Bold.D1sUS0GD.woff2 +0 -0
  462. streamlit/static/static/media/KaTeX_SansSerif-Bold.DbIhKOiC.woff +0 -0
  463. streamlit/static/static/media/KaTeX_SansSerif-Italic.C3H0VqGB.woff2 +0 -0
  464. streamlit/static/static/media/KaTeX_SansSerif-Italic.DN2j7dab.woff +0 -0
  465. streamlit/static/static/media/KaTeX_SansSerif-Italic.YYjJ1zSn.ttf +0 -0
  466. streamlit/static/static/media/KaTeX_SansSerif-Regular.BNo7hRIc.ttf +0 -0
  467. streamlit/static/static/media/KaTeX_SansSerif-Regular.CS6fqUqJ.woff +0 -0
  468. streamlit/static/static/media/KaTeX_SansSerif-Regular.DDBCnlJ7.woff2 +0 -0
  469. streamlit/static/static/media/KaTeX_Script-Regular.C5JkGWo-.ttf +0 -0
  470. streamlit/static/static/media/KaTeX_Script-Regular.D3wIWfF6.woff2 +0 -0
  471. streamlit/static/static/media/KaTeX_Script-Regular.D5yQViql.woff +0 -0
  472. streamlit/static/static/media/KaTeX_Size1-Regular.C195tn64.woff +0 -0
  473. streamlit/static/static/media/KaTeX_Size1-Regular.Dbsnue_I.ttf +0 -0
  474. streamlit/static/static/media/KaTeX_Size1-Regular.mCD8mA8B.woff2 +0 -0
  475. streamlit/static/static/media/KaTeX_Size2-Regular.B7gKUWhC.ttf +0 -0
  476. streamlit/static/static/media/KaTeX_Size2-Regular.Dy4dx90m.woff2 +0 -0
  477. streamlit/static/static/media/KaTeX_Size2-Regular.oD1tc_U0.woff +0 -0
  478. streamlit/static/static/media/KaTeX_Size3-Regular.CTq5MqoE.woff +0 -0
  479. streamlit/static/static/media/KaTeX_Size3-Regular.DgpXs0kz.ttf +0 -0
  480. streamlit/static/static/media/KaTeX_Size4-Regular.BF-4gkZK.woff +0 -0
  481. streamlit/static/static/media/KaTeX_Size4-Regular.DWFBv043.ttf +0 -0
  482. streamlit/static/static/media/KaTeX_Size4-Regular.Dl5lxZxV.woff2 +0 -0
  483. streamlit/static/static/media/KaTeX_Typewriter-Regular.C0xS9mPB.woff +0 -0
  484. streamlit/static/static/media/KaTeX_Typewriter-Regular.CO6r4hn1.woff2 +0 -0
  485. streamlit/static/static/media/KaTeX_Typewriter-Regular.D3Ib7_Hf.ttf +0 -0
  486. streamlit/static/static/media/MaterialSymbols-Rounded.DcZbplWk.woff2 +0 -0
  487. streamlit/static/static/media/SourceCodePro-Bold.CFEfr7-q.woff2 +0 -0
  488. streamlit/static/static/media/SourceCodePro-BoldItalic.C-LkFXxa.woff2 +0 -0
  489. streamlit/static/static/media/SourceCodePro-Italic.CxFOx7N-.woff2 +0 -0
  490. streamlit/static/static/media/SourceCodePro-Regular.CBOlD63d.woff2 +0 -0
  491. streamlit/static/static/media/SourceCodePro-SemiBold.CFHwW3Wd.woff2 +0 -0
  492. streamlit/static/static/media/SourceCodePro-SemiBoldItalic.Cg2yRu82.woff2 +0 -0
  493. streamlit/static/static/media/SourceSansPro-Bold.-6c9oR8J.woff2 +0 -0
  494. streamlit/static/static/media/SourceSansPro-BoldItalic.DmM_grLY.woff2 +0 -0
  495. streamlit/static/static/media/SourceSansPro-Italic.I1ipWe7Q.woff2 +0 -0
  496. streamlit/static/static/media/SourceSansPro-Regular.DZLUzqI4.woff2 +0 -0
  497. streamlit/static/static/media/SourceSansPro-SemiBold.sKQIyTMz.woff2 +0 -0
  498. streamlit/static/static/media/SourceSansPro-SemiBoldItalic.C0wP0icr.woff2 +0 -0
  499. streamlit/static/static/media/SourceSerifPro-Bold.8TUnKj4x.woff2 +0 -0
  500. streamlit/static/static/media/SourceSerifPro-BoldItalic.CBVO7Ve7.woff2 +0 -0
  501. streamlit/static/static/media/SourceSerifPro-Italic.DkFgL2HZ.woff2 +0 -0
  502. streamlit/static/static/media/SourceSerifPro-Regular.CNJNET2S.woff2 +0 -0
  503. streamlit/static/static/media/SourceSerifPro-SemiBold.CHyh9GC5.woff2 +0 -0
  504. streamlit/static/static/media/SourceSerifPro-SemiBoldItalic.CBtz8sWN.woff2 +0 -0
  505. streamlit/static/static/media/balloon-0.Czj7AKwE.png +0 -0
  506. streamlit/static/static/media/balloon-1.CNvFFrND.png +0 -0
  507. streamlit/static/static/media/balloon-2.DTvC6B1t.png +0 -0
  508. streamlit/static/static/media/balloon-3.CgSk4tbL.png +0 -0
  509. streamlit/static/static/media/balloon-4.mbtFrzxf.png +0 -0
  510. streamlit/static/static/media/balloon-5.CSwkUfRA.png +0 -0
  511. streamlit/static/static/media/fireworks.B4d-_KUe.gif +0 -0
  512. streamlit/static/static/media/flake-0.DgWaVvm5.png +0 -0
  513. streamlit/static/static/media/flake-1.B2r5AHMK.png +0 -0
  514. streamlit/static/static/media/flake-2.BnWSExPC.png +0 -0
  515. streamlit/static/static/media/snowflake.JU2jBHL8.svg +11 -0
  516. streamlit/string_util.py +203 -0
  517. streamlit/temporary_directory.py +56 -0
  518. streamlit/testing/__init__.py +13 -0
  519. streamlit/testing/v1/__init__.py +17 -0
  520. streamlit/testing/v1/app_test.py +1050 -0
  521. streamlit/testing/v1/element_tree.py +2083 -0
  522. streamlit/testing/v1/local_script_runner.py +180 -0
  523. streamlit/testing/v1/util.py +53 -0
  524. streamlit/time_util.py +75 -0
  525. streamlit/type_util.py +460 -0
  526. streamlit/url_util.py +122 -0
  527. streamlit/user_info.py +519 -0
  528. streamlit/util.py +72 -0
  529. streamlit/vendor/__init__.py +0 -0
  530. streamlit/vendor/pympler/__init__.py +0 -0
  531. streamlit/vendor/pympler/asizeof.py +2869 -0
  532. streamlit/version.py +18 -0
  533. streamlit/watcher/__init__.py +28 -0
  534. streamlit/watcher/event_based_path_watcher.py +406 -0
  535. streamlit/watcher/folder_black_list.py +82 -0
  536. streamlit/watcher/local_sources_watcher.py +233 -0
  537. streamlit/watcher/path_watcher.py +185 -0
  538. streamlit/watcher/polling_path_watcher.py +124 -0
  539. streamlit/watcher/util.py +207 -0
  540. streamlit/web/__init__.py +13 -0
  541. streamlit/web/bootstrap.py +353 -0
  542. streamlit/web/cache_storage_manager_config.py +38 -0
  543. streamlit/web/cli.py +369 -0
  544. streamlit/web/server/__init__.py +26 -0
  545. streamlit/web/server/app_static_file_handler.py +93 -0
  546. streamlit/web/server/authlib_tornado_integration.py +60 -0
  547. streamlit/web/server/browser_websocket_handler.py +246 -0
  548. streamlit/web/server/component_request_handler.py +116 -0
  549. streamlit/web/server/media_file_handler.py +141 -0
  550. streamlit/web/server/oauth_authlib_routes.py +176 -0
  551. streamlit/web/server/oidc_mixin.py +108 -0
  552. streamlit/web/server/routes.py +295 -0
  553. streamlit/web/server/server.py +479 -0
  554. streamlit/web/server/server_util.py +161 -0
  555. streamlit/web/server/stats_request_handler.py +95 -0
  556. streamlit/web/server/upload_file_request_handler.py +137 -0
  557. streamlit/web/server/websocket_headers.py +56 -0
  558. streamlit_nightly-1.43.2.dev20250307.data/scripts/streamlit.cmd +16 -0
  559. streamlit_nightly-1.43.2.dev20250307.dist-info/METADATA +207 -0
  560. streamlit_nightly-1.43.2.dev20250307.dist-info/RECORD +563 -0
  561. streamlit_nightly-1.43.2.dev20250307.dist-info/WHEEL +5 -0
  562. streamlit_nightly-1.43.2.dev20250307.dist-info/entry_points.txt +2 -0
  563. streamlit_nightly-1.43.2.dev20250307.dist-info/top_level.txt +1 -0
@@ -0,0 +1,982 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ import asyncio
18
+ import json
19
+ import sys
20
+ import uuid
21
+ from enum import Enum
22
+ from typing import TYPE_CHECKING, Callable, Final
23
+
24
+ from google.protobuf.json_format import ParseDict
25
+
26
+ import streamlit.elements.exception as exception_utils
27
+ from streamlit import config, runtime
28
+ from streamlit.logger import get_logger
29
+ from streamlit.proto.ClientState_pb2 import ClientState
30
+ from streamlit.proto.Common_pb2 import FileURLs, FileURLsRequest
31
+ from streamlit.proto.ForwardMsg_pb2 import ForwardMsg
32
+ from streamlit.proto.GitInfo_pb2 import GitInfo
33
+ from streamlit.proto.NewSession_pb2 import (
34
+ Config,
35
+ CustomThemeConfig,
36
+ FontFace,
37
+ NewSession,
38
+ UserInfo,
39
+ )
40
+ from streamlit.runtime import caching
41
+ from streamlit.runtime.forward_msg_queue import ForwardMsgQueue
42
+ from streamlit.runtime.fragment import FragmentStorage, MemoryFragmentStorage
43
+ from streamlit.runtime.metrics_util import Installation
44
+ from streamlit.runtime.pages_manager import PagesManager
45
+ from streamlit.runtime.scriptrunner import RerunData, ScriptRunner, ScriptRunnerEvent
46
+ from streamlit.runtime.secrets import secrets_singleton
47
+ from streamlit.string_util import to_snake_case
48
+ from streamlit.version import STREAMLIT_VERSION_STRING
49
+ from streamlit.watcher import LocalSourcesWatcher
50
+
51
+ if TYPE_CHECKING:
52
+ from streamlit.proto.BackMsg_pb2 import BackMsg
53
+ from streamlit.runtime.script_data import ScriptData
54
+ from streamlit.runtime.scriptrunner.script_cache import ScriptCache
55
+ from streamlit.runtime.state import SessionState
56
+ from streamlit.runtime.uploaded_file_manager import UploadedFileManager
57
+ from streamlit.source_util import PageHash, PageInfo
58
+
59
+ _LOGGER: Final = get_logger(__name__)
60
+
61
+
62
+ class AppSessionState(Enum):
63
+ APP_NOT_RUNNING = "APP_NOT_RUNNING"
64
+ APP_IS_RUNNING = "APP_IS_RUNNING"
65
+ SHUTDOWN_REQUESTED = "SHUTDOWN_REQUESTED"
66
+
67
+
68
+ def _generate_scriptrun_id() -> str:
69
+ """Randomly generate a unique ID for a script execution."""
70
+ return str(uuid.uuid4())
71
+
72
+
73
+ class AppSession:
74
+ """
75
+ Contains session data for a single "user" of an active app
76
+ (that is, a connected browser tab).
77
+
78
+ Each AppSession has its own ScriptData, root DeltaGenerator, ScriptRunner,
79
+ and widget state.
80
+
81
+ An AppSession is attached to each thread involved in running its script.
82
+
83
+ """
84
+
85
+ def __init__(
86
+ self,
87
+ script_data: ScriptData,
88
+ uploaded_file_manager: UploadedFileManager,
89
+ script_cache: ScriptCache,
90
+ message_enqueued_callback: Callable[[], None] | None,
91
+ user_info: dict[str, str | bool | None],
92
+ session_id_override: str | None = None,
93
+ ) -> None:
94
+ """Initialize the AppSession.
95
+
96
+ Parameters
97
+ ----------
98
+ script_data
99
+ Object storing parameters related to running a script
100
+
101
+ uploaded_file_manager
102
+ Used to manage files uploaded by users via the Streamlit web client.
103
+
104
+ script_cache
105
+ The app's ScriptCache instance. Stores cached user scripts. ScriptRunner
106
+ uses the ScriptCache to avoid having to reload user scripts from disk
107
+ on each rerun.
108
+
109
+ message_enqueued_callback
110
+ After enqueuing a message, this callable notification will be invoked.
111
+
112
+ user_info
113
+ A dict that contains information about the current user. For now,
114
+ it only contains the user's email address.
115
+
116
+ {
117
+ "email": "example@example.com"
118
+ }
119
+
120
+ Information about the current user is optionally provided when a
121
+ websocket connection is initialized via the "X-Streamlit-User" header.
122
+
123
+ session_id_override
124
+ The ID to assign to this session. Setting this can be useful when the
125
+ service that a Streamlit Runtime is running in wants to tie the lifecycle of
126
+ a Streamlit session to some other session-like object that it manages.
127
+ """
128
+
129
+ # Each AppSession has a unique string ID.
130
+ self.id = session_id_override or str(uuid.uuid4())
131
+
132
+ self._event_loop = asyncio.get_running_loop()
133
+ self._script_data = script_data
134
+ self._uploaded_file_mgr = uploaded_file_manager
135
+ self._script_cache = script_cache
136
+ self._pages_manager = PagesManager(
137
+ script_data.main_script_path, self._script_cache
138
+ )
139
+
140
+ # The browser queue contains messages that haven't yet been
141
+ # delivered to the browser. Periodically, the server flushes
142
+ # this queue and delivers its contents to the browser.
143
+ self._browser_queue = ForwardMsgQueue()
144
+ self._message_enqueued_callback = message_enqueued_callback
145
+
146
+ self._state = AppSessionState.APP_NOT_RUNNING
147
+
148
+ # Need to remember the client state here because when a script reruns
149
+ # due to the source code changing we need to pass in the previous client state.
150
+ self._client_state = ClientState()
151
+
152
+ self._local_sources_watcher: LocalSourcesWatcher | None = None
153
+ self._stop_config_listener: Callable[[], bool] | None = None
154
+ self._stop_pages_listener: Callable[[], None] | None = None
155
+
156
+ if config.get_option("server.fileWatcherType") != "none":
157
+ self.register_file_watchers()
158
+
159
+ self._run_on_save = config.get_option("server.runOnSave")
160
+
161
+ self._scriptrunner: ScriptRunner | None = None
162
+
163
+ # This needs to be lazily imported to avoid a dependency cycle.
164
+ from streamlit.runtime.state import SessionState
165
+
166
+ self._session_state = SessionState()
167
+ self._user_info = user_info
168
+
169
+ self._debug_last_backmsg_id: str | None = None
170
+
171
+ self._fragment_storage: FragmentStorage = MemoryFragmentStorage()
172
+
173
+ _LOGGER.debug("AppSession initialized (id=%s)", self.id)
174
+
175
+ def __del__(self) -> None:
176
+ """Ensure that we call shutdown() when an AppSession is garbage collected."""
177
+ self.shutdown()
178
+
179
+ def register_file_watchers(self) -> None:
180
+ """Register handlers to be called when various files are changed.
181
+
182
+ Files that we watch include:
183
+ - source files that already exist (for edits)
184
+ - `.py` files in the the main script's `pages/` directory (for file additions
185
+ and deletions)
186
+ - project and user-level config.toml files
187
+ - the project-level secrets.toml files
188
+
189
+ This method is called automatically on AppSession construction, but it may be
190
+ called again in the case when a session is disconnected and is being reconnect
191
+ to.
192
+ """
193
+ if self._local_sources_watcher is None:
194
+ self._local_sources_watcher = LocalSourcesWatcher(self._pages_manager)
195
+
196
+ self._local_sources_watcher.register_file_change_callback(
197
+ self._on_source_file_changed
198
+ )
199
+ self._stop_config_listener = config.on_config_parsed(
200
+ self._on_source_file_changed, force_connect=True
201
+ )
202
+ secrets_singleton.file_change_listener.connect(self._on_secrets_file_changed)
203
+
204
+ def disconnect_file_watchers(self) -> None:
205
+ """Disconnect the file watcher handlers registered by register_file_watchers."""
206
+ if self._local_sources_watcher is not None:
207
+ self._local_sources_watcher.close()
208
+ if self._stop_config_listener is not None:
209
+ self._stop_config_listener()
210
+ if self._stop_pages_listener is not None:
211
+ self._stop_pages_listener()
212
+
213
+ secrets_singleton.file_change_listener.disconnect(self._on_secrets_file_changed)
214
+
215
+ self._local_sources_watcher = None
216
+ self._stop_config_listener = None
217
+ self._stop_pages_listener = None
218
+
219
+ def flush_browser_queue(self) -> list[ForwardMsg]:
220
+ """Clear the forward message queue and return the messages it contained.
221
+
222
+ The Server calls this periodically to deliver new messages
223
+ to the browser connected to this app.
224
+
225
+ Returns
226
+ -------
227
+ list[ForwardMsg]
228
+ The messages that were removed from the queue and should
229
+ be delivered to the browser.
230
+
231
+ """
232
+ return self._browser_queue.flush()
233
+
234
+ def shutdown(self) -> None:
235
+ """Shut down the AppSession.
236
+
237
+ It's an error to use a AppSession after it's been shut down.
238
+
239
+ """
240
+ if self._state != AppSessionState.SHUTDOWN_REQUESTED:
241
+ _LOGGER.debug("Shutting down (id=%s)", self.id)
242
+ # Clear any unused session files in upload file manager and media
243
+ # file manager
244
+ self._uploaded_file_mgr.remove_session_files(self.id)
245
+
246
+ if runtime.exists():
247
+ rt = runtime.get_instance()
248
+ rt.media_file_mgr.clear_session_refs(self.id)
249
+ rt.media_file_mgr.remove_orphaned_files()
250
+
251
+ # Shut down the ScriptRunner, if one is active.
252
+ # self._state must not be set to SHUTDOWN_REQUESTED until
253
+ # *after* this is called.
254
+ self.request_script_stop()
255
+
256
+ self._state = AppSessionState.SHUTDOWN_REQUESTED
257
+
258
+ # Disconnect all file watchers if we haven't already, although we will have
259
+ # generally already done so by the time we get here.
260
+ self.disconnect_file_watchers()
261
+
262
+ def _enqueue_forward_msg(self, msg: ForwardMsg) -> None:
263
+ """Enqueue a new ForwardMsg to our browser queue.
264
+
265
+ This can be called on both the main thread and a ScriptRunner
266
+ run thread.
267
+
268
+ Parameters
269
+ ----------
270
+ msg : ForwardMsg
271
+ The message to enqueue
272
+
273
+ """
274
+
275
+ if self._debug_last_backmsg_id:
276
+ msg.debug_last_backmsg_id = self._debug_last_backmsg_id
277
+
278
+ self._browser_queue.enqueue(msg)
279
+ if self._message_enqueued_callback:
280
+ self._message_enqueued_callback()
281
+
282
+ def handle_backmsg(self, msg: BackMsg) -> None:
283
+ """Process a BackMsg."""
284
+ try:
285
+ msg_type = msg.WhichOneof("type")
286
+ if msg_type == "rerun_script":
287
+ if msg.debug_last_backmsg_id:
288
+ self._debug_last_backmsg_id = msg.debug_last_backmsg_id
289
+
290
+ self._handle_rerun_script_request(msg.rerun_script)
291
+ elif msg_type == "load_git_info":
292
+ self._handle_git_information_request()
293
+ elif msg_type == "clear_cache":
294
+ self._handle_clear_cache_request()
295
+ elif msg_type == "app_heartbeat":
296
+ self._handle_app_heartbeat_request()
297
+ elif msg_type == "set_run_on_save":
298
+ self._handle_set_run_on_save_request(msg.set_run_on_save)
299
+ elif msg_type == "stop_script":
300
+ self._handle_stop_script_request()
301
+ elif msg_type == "file_urls_request":
302
+ self._handle_file_urls_request(msg.file_urls_request)
303
+ else:
304
+ _LOGGER.warning('No handler for "%s"', msg_type)
305
+
306
+ except Exception as ex:
307
+ _LOGGER.exception("Error processing back message")
308
+ self.handle_backmsg_exception(ex)
309
+
310
+ def handle_backmsg_exception(self, e: BaseException) -> None:
311
+ """Handle an Exception raised while processing a BackMsg from the browser."""
312
+ # This does a few things:
313
+ # 1) Clears the current app in the browser.
314
+ # 2) Marks the current app as "stopped" in the browser.
315
+ # 3) HACK: Resets any script params that may have been broken (e.g. the
316
+ # command-line when rerunning with wrong argv[0])
317
+
318
+ self._on_scriptrunner_event(
319
+ self._scriptrunner, ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS
320
+ )
321
+ self._on_scriptrunner_event(
322
+ self._scriptrunner,
323
+ ScriptRunnerEvent.SCRIPT_STARTED,
324
+ page_script_hash="",
325
+ )
326
+ self._on_scriptrunner_event(
327
+ self._scriptrunner, ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS
328
+ )
329
+
330
+ # Send an Exception message to the frontend.
331
+ # Because _on_scriptrunner_event does its work in an eventloop callback,
332
+ # this exception ForwardMsg *must* also be enqueued in a callback,
333
+ # so that it will be enqueued *after* the various ForwardMsgs that
334
+ # _on_scriptrunner_event sends.
335
+ self._event_loop.call_soon_threadsafe(
336
+ lambda: self._enqueue_forward_msg(self._create_exception_message(e))
337
+ )
338
+
339
+ def request_rerun(self, client_state: ClientState | None) -> None:
340
+ """Signal that we're interested in running the script.
341
+
342
+ If the script is not already running, it will be started immediately.
343
+ Otherwise, a rerun will be requested.
344
+
345
+ Parameters
346
+ ----------
347
+ client_state : streamlit.proto.ClientState_pb2.ClientState | None
348
+ The ClientState protobuf to run the script with, or None
349
+ to use previous client state.
350
+
351
+ """
352
+
353
+ if self._state == AppSessionState.SHUTDOWN_REQUESTED:
354
+ _LOGGER.warning("Discarding rerun request after shutdown")
355
+ return
356
+
357
+ if client_state:
358
+ fragment_id = client_state.fragment_id
359
+
360
+ # Early check whether this fragment still exists in the fragment storage or
361
+ # might have been removed by a full app run. This is not merely a
362
+ # performance optimization, but also fixes following potential situation:
363
+ # A fragment run might create a new ScriptRunner when the current
364
+ # ScriptRunner is in state STOPPED (in this case, the 'success' variable
365
+ # below is false and the new ScriptRunner is created). This will lead to all
366
+ # events that were not sent / received from the previous script runner to be
367
+ # ignored in _handle_scriptrunner_event_on_event_loop, because the
368
+ # _script_runner changed. When the full app rerun ScriptRunner is done
369
+ # (STOPPED) but its events are not processed before the new ScriptRunner is
370
+ # created, its finished message is not sent to the frontend and no
371
+ # full-app-run cleanup is happening. This scenario can be triggered by the
372
+ # example app described in
373
+ # https://github.com/streamlit/streamlit/issues/9921, where the dialog
374
+ # sometimes stays open.
375
+ if fragment_id and not self._fragment_storage.contains(fragment_id):
376
+ _LOGGER.info(
377
+ f"The fragment with id {fragment_id} does not exist anymore - "
378
+ "it might have been removed during a preceding full-app rerun."
379
+ )
380
+ return
381
+
382
+ if client_state.HasField("context_info"):
383
+ self._client_state.context_info.CopyFrom(client_state.context_info)
384
+
385
+ rerun_data = RerunData(
386
+ client_state.query_string,
387
+ client_state.widget_states,
388
+ client_state.page_script_hash,
389
+ client_state.page_name,
390
+ fragment_id=fragment_id if fragment_id else None,
391
+ is_auto_rerun=client_state.is_auto_rerun,
392
+ context_info=client_state.context_info,
393
+ )
394
+ else:
395
+ rerun_data = RerunData()
396
+
397
+ if self._scriptrunner is not None:
398
+ if (
399
+ bool(config.get_option("runner.fastReruns"))
400
+ and not rerun_data.fragment_id
401
+ ):
402
+ # If fastReruns is enabled and this is *not* a rerun of a fragment,
403
+ # we don't send rerun requests to our existing ScriptRunner. Instead, we
404
+ # tell it to shut down. We'll then spin up a new ScriptRunner, below, to
405
+ # handle the rerun immediately.
406
+ self._scriptrunner.request_stop()
407
+ self._scriptrunner = None
408
+ else:
409
+ # Either fastReruns is not enabled or this RERUN request is a request to
410
+ # run a fragment. We send our current ScriptRunner a rerun request, and
411
+ # if it's accepted, we're done.
412
+ success = self._scriptrunner.request_rerun(rerun_data)
413
+ if success:
414
+ return
415
+
416
+ # If we are here, then either we have no ScriptRunner, or our
417
+ # current ScriptRunner is shutting down and cannot handle a rerun
418
+ # request - so we'll create and start a new ScriptRunner.
419
+ self._create_scriptrunner(rerun_data)
420
+
421
+ def request_script_stop(self) -> None:
422
+ """Request that the scriptrunner stop execution.
423
+
424
+ Does nothing if no scriptrunner exists.
425
+ """
426
+ if self._scriptrunner is not None:
427
+ self._scriptrunner.request_stop()
428
+
429
+ def clear_user_info(self) -> None:
430
+ """Clear the user info for this session."""
431
+ self._user_info.clear()
432
+
433
+ def _create_scriptrunner(self, initial_rerun_data: RerunData) -> None:
434
+ """Create and run a new ScriptRunner with the given RerunData."""
435
+ self._scriptrunner = ScriptRunner(
436
+ session_id=self.id,
437
+ main_script_path=self._script_data.main_script_path,
438
+ session_state=self._session_state,
439
+ uploaded_file_mgr=self._uploaded_file_mgr,
440
+ script_cache=self._script_cache,
441
+ initial_rerun_data=initial_rerun_data,
442
+ user_info=self._user_info,
443
+ fragment_storage=self._fragment_storage,
444
+ pages_manager=self._pages_manager,
445
+ )
446
+ self._scriptrunner.on_event.connect(self._on_scriptrunner_event)
447
+ self._scriptrunner.start()
448
+
449
+ @property
450
+ def session_state(self) -> SessionState:
451
+ return self._session_state
452
+
453
+ def _should_rerun_on_file_change(self, filepath: str) -> bool:
454
+ pages = self._pages_manager.get_pages()
455
+
456
+ changed_page_script_hash = next(
457
+ filter(lambda k: pages[k]["script_path"] == filepath, pages),
458
+ None,
459
+ )
460
+
461
+ if changed_page_script_hash is not None:
462
+ current_page_script_hash = self._client_state.page_script_hash
463
+ return changed_page_script_hash == current_page_script_hash
464
+
465
+ return True
466
+
467
+ def _on_source_file_changed(self, filepath: str | None = None) -> None:
468
+ """One of our source files changed. Clear the cache and schedule a rerun if
469
+ appropriate.
470
+ """
471
+ self._script_cache.clear()
472
+
473
+ if filepath is not None and not self._should_rerun_on_file_change(filepath):
474
+ return
475
+
476
+ if self._run_on_save:
477
+ self.request_rerun(self._client_state)
478
+ else:
479
+ self._enqueue_forward_msg(self._create_file_change_message())
480
+
481
+ def _on_secrets_file_changed(self, _) -> None:
482
+ """Called when `secrets.file_change_listener` emits a Signal."""
483
+
484
+ # NOTE: At the time of writing, this function only calls
485
+ # `_on_source_file_changed`. The reason behind creating this function instead of
486
+ # just passing `_on_source_file_changed` to `connect` / `disconnect` directly is
487
+ # that every function that is passed to `connect` / `disconnect` must have at
488
+ # least one argument for `sender` (in this case we don't really care about it,
489
+ # thus `_`), and introducing an unnecessary argument to
490
+ # `_on_source_file_changed` just for this purpose sounded finicky.
491
+ self._on_source_file_changed()
492
+
493
+ def _clear_queue(self, fragment_ids_this_run: list[str] | None = None) -> None:
494
+ self._browser_queue.clear(
495
+ retain_lifecycle_msgs=True, fragment_ids_this_run=fragment_ids_this_run
496
+ )
497
+
498
+ def _on_scriptrunner_event(
499
+ self,
500
+ sender: ScriptRunner | None,
501
+ event: ScriptRunnerEvent,
502
+ forward_msg: ForwardMsg | None = None,
503
+ exception: BaseException | None = None,
504
+ client_state: ClientState | None = None,
505
+ page_script_hash: str | None = None,
506
+ fragment_ids_this_run: list[str] | None = None,
507
+ pages: dict[PageHash, PageInfo] | None = None,
508
+ ) -> None:
509
+ """Called when our ScriptRunner emits an event.
510
+
511
+ This is generally called from the sender ScriptRunner's script thread.
512
+ We forward the event on to _handle_scriptrunner_event_on_event_loop,
513
+ which will be called on the main thread.
514
+ """
515
+ self._event_loop.call_soon_threadsafe(
516
+ lambda: self._handle_scriptrunner_event_on_event_loop(
517
+ sender,
518
+ event,
519
+ forward_msg,
520
+ exception,
521
+ client_state,
522
+ page_script_hash,
523
+ fragment_ids_this_run,
524
+ pages,
525
+ )
526
+ )
527
+
528
+ def _handle_scriptrunner_event_on_event_loop(
529
+ self,
530
+ sender: ScriptRunner | None,
531
+ event: ScriptRunnerEvent,
532
+ forward_msg: ForwardMsg | None = None,
533
+ exception: BaseException | None = None,
534
+ client_state: ClientState | None = None,
535
+ page_script_hash: str | None = None,
536
+ fragment_ids_this_run: list[str] | None = None,
537
+ pages: dict[PageHash, PageInfo] | None = None,
538
+ ) -> None:
539
+ """Handle a ScriptRunner event.
540
+
541
+ This function must only be called on our eventloop thread.
542
+
543
+ Parameters
544
+ ----------
545
+ sender : ScriptRunner | None
546
+ The ScriptRunner that emitted the event. (This may be set to
547
+ None when called from `handle_backmsg_exception`, if no
548
+ ScriptRunner was active when the backmsg exception was raised.)
549
+
550
+ event : ScriptRunnerEvent
551
+ The event type.
552
+
553
+ forward_msg : ForwardMsg | None
554
+ The ForwardMsg to send to the frontend. Set only for the
555
+ ENQUEUE_FORWARD_MSG event.
556
+
557
+ exception : BaseException | None
558
+ An exception thrown during compilation. Set only for the
559
+ SCRIPT_STOPPED_WITH_COMPILE_ERROR event.
560
+
561
+ client_state : streamlit.proto.ClientState_pb2.ClientState | None
562
+ The ScriptRunner's final ClientState. Set only for the
563
+ SHUTDOWN event.
564
+
565
+ page_script_hash : str | None
566
+ A hash of the script path corresponding to the page currently being
567
+ run. Set only for the SCRIPT_STARTED event.
568
+
569
+ fragment_ids_this_run : list[str] | None
570
+ The fragment IDs of the fragments being executed in this script run. Only
571
+ set for the SCRIPT_STARTED event. If this value is falsy, this script run
572
+ must be for the full script.
573
+
574
+ clear_forward_msg_queue : bool
575
+ If set (the default), clears the queue of forward messages to be sent to the
576
+ browser. Set only for the SCRIPT_STARTED event.
577
+ """
578
+
579
+ assert self._event_loop == asyncio.get_running_loop(), (
580
+ "This function must only be called on the eventloop thread the AppSession was created on."
581
+ )
582
+
583
+ if sender is not self._scriptrunner:
584
+ # This event was sent by a non-current ScriptRunner; ignore it.
585
+ # This can happen after sppinng up a new ScriptRunner (to handle a
586
+ # rerun request, for example) while another ScriptRunner is still
587
+ # shutting down. The shutting-down ScriptRunner may still
588
+ # emit events.
589
+ _LOGGER.debug("Ignoring event from non-current ScriptRunner: %s", event)
590
+ return
591
+
592
+ prev_state = self._state
593
+
594
+ if event == ScriptRunnerEvent.SCRIPT_STARTED:
595
+ if self._state != AppSessionState.SHUTDOWN_REQUESTED:
596
+ self._state = AppSessionState.APP_IS_RUNNING
597
+ assert page_script_hash is not None, (
598
+ "page_script_hash must be set for the SCRIPT_STARTED event"
599
+ )
600
+
601
+ # Update the client state with the new page_script_hash if
602
+ # necessary. This handles an edge case where a script is never
603
+ # finishes (eg. by calling st.rerun()), but the page has changed
604
+ # via st.navigation()
605
+ if page_script_hash != self._client_state.page_script_hash:
606
+ self._client_state.page_script_hash = page_script_hash
607
+
608
+ self._clear_queue(fragment_ids_this_run)
609
+
610
+ self._enqueue_forward_msg(
611
+ self._create_new_session_message(
612
+ page_script_hash, fragment_ids_this_run, pages
613
+ )
614
+ )
615
+
616
+ elif (
617
+ event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS
618
+ or event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_COMPILE_ERROR
619
+ or event == ScriptRunnerEvent.FRAGMENT_STOPPED_WITH_SUCCESS
620
+ ):
621
+ if self._state != AppSessionState.SHUTDOWN_REQUESTED:
622
+ self._state = AppSessionState.APP_NOT_RUNNING
623
+
624
+ if event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS:
625
+ status = ForwardMsg.FINISHED_SUCCESSFULLY
626
+ elif event == ScriptRunnerEvent.FRAGMENT_STOPPED_WITH_SUCCESS:
627
+ status = ForwardMsg.FINISHED_FRAGMENT_RUN_SUCCESSFULLY
628
+ else:
629
+ status = ForwardMsg.FINISHED_WITH_COMPILE_ERROR
630
+
631
+ self._enqueue_forward_msg(self._create_script_finished_message(status))
632
+ self._debug_last_backmsg_id = None
633
+
634
+ if (
635
+ event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS
636
+ or event == ScriptRunnerEvent.FRAGMENT_STOPPED_WITH_SUCCESS
637
+ ):
638
+ # The script completed successfully: update our
639
+ # LocalSourcesWatcher to account for any source code changes
640
+ # that change which modules should be watched.
641
+ if self._local_sources_watcher:
642
+ self._local_sources_watcher.update_watched_modules()
643
+ self._local_sources_watcher.update_watched_pages()
644
+ else:
645
+ # The script didn't complete successfully: send the exception
646
+ # to the frontend.
647
+ assert exception is not None, (
648
+ "exception must be set for the SCRIPT_STOPPED_WITH_COMPILE_ERROR event"
649
+ )
650
+ msg = ForwardMsg()
651
+ exception_utils.marshall(
652
+ msg.session_event.script_compilation_exception, exception
653
+ )
654
+ self._enqueue_forward_msg(msg)
655
+
656
+ elif event == ScriptRunnerEvent.SCRIPT_STOPPED_FOR_RERUN:
657
+ self._state = AppSessionState.APP_NOT_RUNNING
658
+ self._enqueue_forward_msg(
659
+ self._create_script_finished_message(
660
+ ForwardMsg.FINISHED_EARLY_FOR_RERUN
661
+ )
662
+ )
663
+ if self._local_sources_watcher:
664
+ self._local_sources_watcher.update_watched_modules()
665
+
666
+ elif event == ScriptRunnerEvent.SHUTDOWN:
667
+ assert client_state is not None, (
668
+ "client_state must be set for the SHUTDOWN event"
669
+ )
670
+
671
+ if self._state == AppSessionState.SHUTDOWN_REQUESTED:
672
+ # Only clear media files if the script is done running AND the
673
+ # session is actually shutting down.
674
+ runtime.get_instance().media_file_mgr.clear_session_refs(self.id)
675
+
676
+ self._client_state = client_state
677
+ self._scriptrunner = None
678
+
679
+ elif event == ScriptRunnerEvent.ENQUEUE_FORWARD_MSG:
680
+ assert forward_msg is not None, (
681
+ "null forward_msg in ENQUEUE_FORWARD_MSG event"
682
+ )
683
+ self._enqueue_forward_msg(forward_msg)
684
+
685
+ # Send a message if our run state changed
686
+ app_was_running = prev_state == AppSessionState.APP_IS_RUNNING
687
+ app_is_running = self._state == AppSessionState.APP_IS_RUNNING
688
+ if app_is_running != app_was_running:
689
+ self._enqueue_forward_msg(self._create_session_status_changed_message())
690
+
691
+ def _create_session_status_changed_message(self) -> ForwardMsg:
692
+ """Create and return a session_status_changed ForwardMsg."""
693
+ msg = ForwardMsg()
694
+ msg.session_status_changed.run_on_save = self._run_on_save
695
+ msg.session_status_changed.script_is_running = (
696
+ self._state == AppSessionState.APP_IS_RUNNING
697
+ )
698
+ return msg
699
+
700
+ def _create_file_change_message(self) -> ForwardMsg:
701
+ """Create and return a 'script_changed_on_disk' ForwardMsg."""
702
+ msg = ForwardMsg()
703
+ msg.session_event.script_changed_on_disk = True
704
+ return msg
705
+
706
+ def _create_new_session_message(
707
+ self,
708
+ page_script_hash: str,
709
+ fragment_ids_this_run: list[str] | None = None,
710
+ pages: dict[PageHash, PageInfo] | None = None,
711
+ ) -> ForwardMsg:
712
+ """Create and return a new_session ForwardMsg."""
713
+ msg = ForwardMsg()
714
+
715
+ msg.new_session.script_run_id = _generate_scriptrun_id()
716
+ msg.new_session.name = self._script_data.name
717
+ msg.new_session.main_script_path = self._pages_manager.main_script_path
718
+ msg.new_session.main_script_hash = self._pages_manager.main_script_hash
719
+ msg.new_session.page_script_hash = page_script_hash
720
+
721
+ if fragment_ids_this_run:
722
+ msg.new_session.fragment_ids_this_run.extend(fragment_ids_this_run)
723
+
724
+ self._populate_app_pages(
725
+ msg.new_session, pages or self._pages_manager.get_pages()
726
+ )
727
+ _populate_config_msg(msg.new_session.config)
728
+ _populate_theme_msg(msg.new_session.custom_theme)
729
+
730
+ # Immutable session data. We send this every time a new session is
731
+ # started, to avoid having to track whether the client has already
732
+ # received it. It does not change from run to run; it's up to the
733
+ # to perform one-time initialization only once.
734
+ imsg = msg.new_session.initialize
735
+
736
+ _populate_user_info_msg(imsg.user_info)
737
+
738
+ imsg.environment_info.streamlit_version = STREAMLIT_VERSION_STRING
739
+ imsg.environment_info.python_version = ".".join(map(str, sys.version_info))
740
+
741
+ imsg.session_status.run_on_save = self._run_on_save
742
+ imsg.session_status.script_is_running = (
743
+ self._state == AppSessionState.APP_IS_RUNNING
744
+ )
745
+
746
+ imsg.is_hello = self._script_data.is_hello
747
+ imsg.session_id = self.id
748
+
749
+ return msg
750
+
751
+ def _create_script_finished_message(
752
+ self, status: ForwardMsg.ScriptFinishedStatus.ValueType
753
+ ) -> ForwardMsg:
754
+ """Create and return a script_finished ForwardMsg."""
755
+ msg = ForwardMsg()
756
+ msg.script_finished = status
757
+ return msg
758
+
759
+ def _create_exception_message(self, e: BaseException) -> ForwardMsg:
760
+ """Create and return an Exception ForwardMsg."""
761
+ msg = ForwardMsg()
762
+ exception_utils.marshall(msg.delta.new_element.exception, e)
763
+ return msg
764
+
765
+ def _handle_git_information_request(self) -> None:
766
+ msg = ForwardMsg()
767
+
768
+ try:
769
+ from streamlit.git_util import GitRepo
770
+
771
+ repo = GitRepo(self._script_data.main_script_path)
772
+
773
+ repo_info = repo.get_repo_info()
774
+ if repo_info is None:
775
+ return
776
+
777
+ repository_name, branch, module = repo_info
778
+
779
+ repository_name = repository_name.removesuffix(".git")
780
+
781
+ msg.git_info_changed.repository = repository_name
782
+ msg.git_info_changed.branch = branch
783
+ msg.git_info_changed.module = module
784
+
785
+ msg.git_info_changed.untracked_files[:] = repo.untracked_files
786
+ msg.git_info_changed.uncommitted_files[:] = repo.uncommitted_files
787
+
788
+ if repo.is_head_detached:
789
+ msg.git_info_changed.state = GitInfo.GitStates.HEAD_DETACHED
790
+ elif len(repo.ahead_commits) > 0:
791
+ msg.git_info_changed.state = GitInfo.GitStates.AHEAD_OF_REMOTE
792
+ else:
793
+ msg.git_info_changed.state = GitInfo.GitStates.DEFAULT
794
+
795
+ self._enqueue_forward_msg(msg)
796
+ except Exception as ex:
797
+ # Users may never even install Git in the first place, so this
798
+ # error requires no action. It can be useful for debugging.
799
+ _LOGGER.debug("Obtaining Git information produced an error", exc_info=ex)
800
+
801
+ def _handle_rerun_script_request(
802
+ self, client_state: ClientState | None = None
803
+ ) -> None:
804
+ """Tell the ScriptRunner to re-run its script.
805
+
806
+ Parameters
807
+ ----------
808
+ client_state : streamlit.proto.ClientState_pb2.ClientState | None
809
+ The ClientState protobuf to run the script with, or None
810
+ to use previous client state.
811
+
812
+ """
813
+ self.request_rerun(client_state)
814
+
815
+ def _handle_stop_script_request(self) -> None:
816
+ """Tell the ScriptRunner to stop running its script."""
817
+ self.request_script_stop()
818
+
819
+ def _handle_clear_cache_request(self) -> None:
820
+ """Clear this app's cache.
821
+
822
+ Because this cache is global, it will be cleared for all users.
823
+
824
+ """
825
+ caching.cache_data.clear()
826
+ caching.cache_resource.clear()
827
+ self._session_state.clear()
828
+
829
+ def _handle_app_heartbeat_request(self) -> None:
830
+ """Handle an incoming app heartbeat.
831
+
832
+ The heartbeat indicates the frontend is active and keeps the
833
+ websocket from going idle and disconnecting.
834
+
835
+ The actual handler here is a noop
836
+
837
+ """
838
+ pass
839
+
840
+ def _handle_set_run_on_save_request(self, new_value: bool) -> None:
841
+ """Change our run_on_save flag to the given value.
842
+
843
+ The browser will be notified of the change.
844
+
845
+ Parameters
846
+ ----------
847
+ new_value : bool
848
+ New run_on_save value
849
+
850
+ """
851
+ self._run_on_save = new_value
852
+ self._enqueue_forward_msg(self._create_session_status_changed_message())
853
+
854
+ def _handle_file_urls_request(self, file_urls_request: FileURLsRequest) -> None:
855
+ """Handle a file_urls_request BackMsg sent by the client."""
856
+ msg = ForwardMsg()
857
+ msg.file_urls_response.response_id = file_urls_request.request_id
858
+
859
+ upload_url_infos = self._uploaded_file_mgr.get_upload_urls(
860
+ self.id, file_urls_request.file_names
861
+ )
862
+
863
+ for upload_url_info in upload_url_infos:
864
+ msg.file_urls_response.file_urls.append(
865
+ FileURLs(
866
+ file_id=upload_url_info.file_id,
867
+ upload_url=upload_url_info.upload_url,
868
+ delete_url=upload_url_info.delete_url,
869
+ )
870
+ )
871
+
872
+ self._enqueue_forward_msg(msg)
873
+
874
+ def _populate_app_pages(
875
+ self, msg: NewSession, pages: dict[PageHash, PageInfo]
876
+ ) -> None:
877
+ for page_script_hash, page_info in pages.items():
878
+ page_proto = msg.app_pages.add()
879
+
880
+ page_proto.page_script_hash = page_script_hash
881
+ page_proto.page_name = page_info["page_name"].replace("_", " ")
882
+ page_proto.url_pathname = page_info["page_name"]
883
+ page_proto.icon = page_info["icon"]
884
+
885
+
886
+ # Config.ToolbarMode.ValueType does not exist at runtime (only in the pyi stubs), so
887
+ # we need to use quotes.
888
+ # This field will be available at runtime as of protobuf 3.20.1, but
889
+ # we are using an older version.
890
+ # For details, see: https://github.com/protocolbuffers/protobuf/issues/8175
891
+ def _get_toolbar_mode() -> Config.ToolbarMode.ValueType:
892
+ config_key = "client.toolbarMode"
893
+ config_value = config.get_option(config_key)
894
+ enum_value: Config.ToolbarMode.ValueType | None = getattr(
895
+ Config.ToolbarMode, config_value.upper()
896
+ )
897
+ if enum_value is None:
898
+ allowed_values = ", ".join(k.lower() for k in Config.ToolbarMode.keys())
899
+ raise ValueError(
900
+ f"Config {config_key!r} expects to have one of "
901
+ f"the following values: {allowed_values}. "
902
+ f"Current value: {config_value}"
903
+ )
904
+ return enum_value
905
+
906
+
907
+ def _populate_config_msg(msg: Config) -> None:
908
+ msg.gather_usage_stats = config.get_option("browser.gatherUsageStats")
909
+ msg.max_cached_message_age = config.get_option("global.maxCachedMessageAge")
910
+ msg.allow_run_on_save = config.get_option("server.allowRunOnSave")
911
+ msg.hide_top_bar = config.get_option("ui.hideTopBar")
912
+ if config.get_option("client.showSidebarNavigation") is False:
913
+ msg.hide_sidebar_nav = True
914
+ msg.toolbar_mode = _get_toolbar_mode()
915
+
916
+
917
+ def _populate_theme_msg(msg: CustomThemeConfig) -> None:
918
+ theme_opts = config.get_options_for_section("theme")
919
+
920
+ if not any(theme_opts.values()):
921
+ return
922
+
923
+ for option_name, option_val in theme_opts.items():
924
+ # We need to ignore some config options here that need special handling
925
+ # and cannot directly be set on the protobuf.
926
+ if option_name not in {"base", "font", "fontFaces"} and option_val is not None:
927
+ setattr(msg, to_snake_case(option_name), option_val)
928
+
929
+ # NOTE: If unset, base and font will default to the protobuf enum zero
930
+ # values, which are BaseTheme.LIGHT and FontFamily.SANS_SERIF,
931
+ # respectively. This is why we both don't handle the cases explicitly and
932
+ # also only log a warning when receiving invalid base/font options.
933
+ base_map = {
934
+ "light": msg.BaseTheme.LIGHT,
935
+ "dark": msg.BaseTheme.DARK,
936
+ }
937
+ base = theme_opts["base"]
938
+ if base is not None:
939
+ if base not in base_map:
940
+ _LOGGER.warning(
941
+ f'"{base}" is an invalid value for theme.base.'
942
+ f" Allowed values include {list(base_map.keys())}."
943
+ ' Setting theme.base to "light".'
944
+ )
945
+ else:
946
+ msg.base = base_map[base]
947
+
948
+ # Since the font field uses the deprecated enum, we need to put the font
949
+ # config into the body_font field instead:
950
+ body_font = theme_opts["font"]
951
+ if body_font:
952
+ msg.body_font = body_font
953
+
954
+ font_faces = theme_opts["fontFaces"]
955
+ # If fontFaces was configured via config.toml, it's already a parsed list of
956
+ # dictionaries. However, if it was provided via env variable or via CLI arg,
957
+ # it's a json string that still needs to be parsed.
958
+ if isinstance(font_faces, str):
959
+ try:
960
+ font_faces = json.loads(font_faces)
961
+ except Exception as e:
962
+ _LOGGER.warning(
963
+ "Failed to parse the theme.fontFaces config option with json.loads: "
964
+ f"{font_faces}.",
965
+ exc_info=e,
966
+ )
967
+ font_faces = None
968
+
969
+ if font_faces is not None:
970
+ for font_face in font_faces:
971
+ try:
972
+ msg.font_faces.append(ParseDict(font_face, FontFace()))
973
+ except Exception as e:
974
+ _LOGGER.warning(
975
+ f"Failed to parse the theme.fontFaces config option: {font_face}.",
976
+ exc_info=e,
977
+ )
978
+
979
+
980
+ def _populate_user_info_msg(msg: UserInfo) -> None:
981
+ msg.installation_id = Installation.instance().installation_id
982
+ msg.installation_id_v3 = Installation.instance().installation_id_v3