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,775 @@
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 asyncio
18
+ import sys
19
+ import time
20
+ import traceback
21
+ from dataclasses import dataclass, field
22
+ from enum import Enum
23
+ from typing import TYPE_CHECKING, Final, NamedTuple
24
+
25
+ from streamlit.components.lib.local_component_registry import LocalComponentRegistry
26
+ from streamlit.components.v2.component_manager import BidiComponentManager
27
+ from streamlit.logger import get_logger
28
+ from streamlit.proto.ForwardMsg_pb2 import ForwardMsg
29
+ from streamlit.runtime.app_session import AppSession
30
+ from streamlit.runtime.caching import (
31
+ get_data_cache_stats_provider,
32
+ get_resource_cache_stats_provider,
33
+ )
34
+ from streamlit.runtime.caching.storage.local_disk_cache_storage import (
35
+ LocalDiskCacheStorageManager,
36
+ )
37
+ from streamlit.runtime.media_file_manager import MediaFileManager
38
+ from streamlit.runtime.memory_session_storage import MemorySessionStorage
39
+ from streamlit.runtime.script_data import ScriptData
40
+ from streamlit.runtime.scriptrunner.script_cache import ScriptCache
41
+ from streamlit.runtime.session_manager import (
42
+ ActiveSessionInfo,
43
+ SessionClient,
44
+ SessionClientDisconnectedError,
45
+ SessionManager,
46
+ SessionStorage,
47
+ )
48
+ from streamlit.runtime.state import (
49
+ SCRIPT_RUN_WITHOUT_ERRORS_KEY,
50
+ SessionStateStatProvider,
51
+ )
52
+ from streamlit.runtime.stats import (
53
+ StatsManager,
54
+ StatsProvider,
55
+ )
56
+ from streamlit.runtime.websocket_session_manager import WebsocketSessionManager
57
+
58
+ if TYPE_CHECKING:
59
+ from collections.abc import Awaitable
60
+
61
+ from streamlit.components.types.base_component_registry import BaseComponentRegistry
62
+ from streamlit.proto.BackMsg_pb2 import BackMsg
63
+ from streamlit.runtime.caching.storage import CacheStorageManager
64
+ from streamlit.runtime.media_file_storage import MediaFileStorage
65
+ from streamlit.runtime.scriptrunner_utils.script_run_context import UserInfoType
66
+ from streamlit.runtime.uploaded_file_manager import UploadedFileManager
67
+
68
+ # Wait for the script run result for 60s and if no result is available give up
69
+ SCRIPT_RUN_CHECK_TIMEOUT: Final = 60
70
+
71
+ # On Windows, periodically check for signals when blocked in asyncio.wait()
72
+ # This ensures Ctrl+C can be processed even when no sessions are connected
73
+ _SIGNAL_CHECK_INTERVAL: Final = 0.5 if sys.platform == "win32" else None
74
+
75
+ _LOGGER: Final = get_logger(__name__)
76
+
77
+
78
+ class RuntimeStoppedError(Exception):
79
+ """Raised by operations on a Runtime instance that is stopped."""
80
+
81
+
82
+ @dataclass(frozen=True)
83
+ class RuntimeConfig:
84
+ """Config options for StreamlitRuntime."""
85
+
86
+ # The filesystem path of the Streamlit script to run.
87
+ script_path: str
88
+
89
+ # The storage backend for Streamlit's MediaFileManager.
90
+ media_file_storage: MediaFileStorage
91
+
92
+ # The upload file manager
93
+ uploaded_file_manager: UploadedFileManager
94
+
95
+ # The cache storage backend for Streamlit's st.cache_data.
96
+ cache_storage_manager: CacheStorageManager = field(
97
+ default_factory=LocalDiskCacheStorageManager
98
+ )
99
+
100
+ # The ComponentRegistry instance to use.
101
+ component_registry: BaseComponentRegistry = field(
102
+ default_factory=LocalComponentRegistry
103
+ )
104
+
105
+ # The BidiComponentManager instance to use for v2 components.
106
+ bidi_component_registry: BidiComponentManager = field(
107
+ default_factory=BidiComponentManager
108
+ )
109
+
110
+ # The SessionManager class to be used.
111
+ session_manager_class: type[SessionManager] = WebsocketSessionManager
112
+
113
+ # The SessionStorage instance for the SessionManager to use.
114
+ session_storage: SessionStorage = field(default_factory=MemorySessionStorage)
115
+
116
+ # True if the command used to start Streamlit was `streamlit hello`.
117
+ is_hello: bool = False
118
+
119
+ # TODO(vdonato): Eventually add a new fragment_storage_class field enabling the code
120
+ # creating a new Streamlit Runtime to configure the FragmentStorage instances
121
+ # created by each new AppSession. We choose not to do this for now to avoid adding
122
+ # additional complexity to RuntimeConfig/SessionManager/etc when it's unlikely
123
+ # we'll have a custom implementation of this class anytime soon.
124
+
125
+
126
+ class RuntimeState(Enum):
127
+ INITIAL = "INITIAL"
128
+ NO_SESSIONS_CONNECTED = "NO_SESSIONS_CONNECTED"
129
+ ONE_OR_MORE_SESSIONS_CONNECTED = "ONE_OR_MORE_SESSIONS_CONNECTED"
130
+ STOPPING = "STOPPING"
131
+ STOPPED = "STOPPED"
132
+
133
+
134
+ class AsyncObjects(NamedTuple):
135
+ """Container for all asyncio objects that Runtime manages.
136
+ These cannot be initialized until the Runtime's eventloop is assigned.
137
+ """
138
+
139
+ # The eventloop that Runtime is running on.
140
+ eventloop: asyncio.AbstractEventLoop
141
+
142
+ # Set after Runtime.stop() is called. Never cleared.
143
+ must_stop: asyncio.Event
144
+
145
+ # Set when a client connects; cleared when we have no connected clients.
146
+ has_connection: asyncio.Event
147
+
148
+ # Set after a ForwardMsg is enqueued; cleared when we flush ForwardMsgs.
149
+ need_send_data: asyncio.Event
150
+
151
+ # Completed when the Runtime has started.
152
+ started: asyncio.Future[None]
153
+
154
+ # Completed when the Runtime has stopped.
155
+ stopped: asyncio.Future[None]
156
+
157
+
158
+ class Runtime:
159
+ _instance: Runtime | None = None
160
+
161
+ @classmethod
162
+ def instance(cls) -> Runtime:
163
+ """Return the singleton Runtime instance. Raise an Error if the
164
+ Runtime hasn't been created yet.
165
+ """
166
+ if cls._instance is None:
167
+ raise RuntimeError("Runtime hasn't been created!")
168
+ return cls._instance
169
+
170
+ @classmethod
171
+ def exists(cls) -> bool:
172
+ """True if the singleton Runtime instance has been created.
173
+
174
+ When a Streamlit app is running in "raw mode" - that is, when the
175
+ app is run via `python app.py` instead of `streamlit run app.py` -
176
+ the Runtime will not exist, and various Streamlit functions need
177
+ to adapt.
178
+ """
179
+ return cls._instance is not None
180
+
181
+ def __init__(self, config: RuntimeConfig) -> None:
182
+ """Create a Runtime instance. It won't be started yet.
183
+
184
+ Runtime is *not* thread-safe. Its public methods are generally
185
+ safe to call only on the same thread that its event loop runs on.
186
+
187
+ Parameters
188
+ ----------
189
+ config
190
+ Config options.
191
+ """
192
+ if Runtime._instance is not None:
193
+ raise RuntimeError("Runtime instance already exists!")
194
+ Runtime._instance = self
195
+
196
+ # Will be created when we start.
197
+ self._async_objs: AsyncObjects | None = None
198
+
199
+ # The task that runs our main loop. We need to save a reference
200
+ # to it so that it doesn't get garbage collected while running.
201
+ self._loop_coroutine_task: asyncio.Task[None] | None = None
202
+
203
+ self._main_script_path = config.script_path
204
+ self._is_hello = config.is_hello
205
+
206
+ self._state = RuntimeState.INITIAL
207
+
208
+ # Initialize managers
209
+ self._component_registry = config.component_registry
210
+ self._bidi_component_registry = config.bidi_component_registry
211
+ self._uploaded_file_mgr = config.uploaded_file_manager
212
+ self._media_file_mgr = MediaFileManager(storage=config.media_file_storage)
213
+ self._cache_storage_manager = config.cache_storage_manager
214
+ self._script_cache = ScriptCache()
215
+
216
+ # Discover and register components for CCv2 from installed packages
217
+ self._bidi_component_registry.discover_and_register_components()
218
+
219
+ self._session_mgr = config.session_manager_class(
220
+ session_storage=config.session_storage,
221
+ uploaded_file_manager=self._uploaded_file_mgr,
222
+ script_cache=self._script_cache,
223
+ message_enqueued_callback=self._enqueued_some_message,
224
+ )
225
+
226
+ self._stats_mgr = StatsManager()
227
+ self._stats_mgr.register_provider(get_data_cache_stats_provider())
228
+ self._stats_mgr.register_provider(get_resource_cache_stats_provider())
229
+ if self._uploaded_file_mgr is not None:
230
+ self._stats_mgr.register_provider(self._uploaded_file_mgr)
231
+ # Register media file storage for stats if it implements StatsProvider
232
+ if isinstance(config.media_file_storage, StatsProvider):
233
+ self._stats_mgr.register_provider(config.media_file_storage)
234
+ self._stats_mgr.register_provider(SessionStateStatProvider(self._session_mgr))
235
+
236
+ # Register session manager for session event metrics if it implements StatsProvider
237
+ if isinstance(self._session_mgr, StatsProvider):
238
+ self._stats_mgr.register_provider(self._session_mgr)
239
+
240
+ @property
241
+ def state(self) -> RuntimeState:
242
+ return self._state
243
+
244
+ @property
245
+ def component_registry(self) -> BaseComponentRegistry:
246
+ return self._component_registry
247
+
248
+ @property
249
+ def bidi_component_registry(self) -> BidiComponentManager:
250
+ return self._bidi_component_registry
251
+
252
+ @property
253
+ def uploaded_file_mgr(self) -> UploadedFileManager:
254
+ return self._uploaded_file_mgr
255
+
256
+ @property
257
+ def cache_storage_manager(self) -> CacheStorageManager:
258
+ return self._cache_storage_manager
259
+
260
+ @property
261
+ def media_file_mgr(self) -> MediaFileManager:
262
+ return self._media_file_mgr
263
+
264
+ @property
265
+ def stats_mgr(self) -> StatsManager:
266
+ return self._stats_mgr
267
+
268
+ @property
269
+ def stopped(self) -> Awaitable[None]:
270
+ """A Future that completes when the Runtime's run loop has exited."""
271
+ return self._get_async_objs().stopped
272
+
273
+ # NOTE: A few Runtime methods listed as threadsafe (get_client and
274
+ # is_active_session) currently rely on the implementation detail that
275
+ # WebsocketSessionManager's get_active_session_info and is_active_session methods
276
+ # happen to be threadsafe. This may change with future SessionManager implementations,
277
+ # at which point we'll need to formalize our thread safety rules for each
278
+ # SessionManager method.
279
+ def get_client(self, session_id: str) -> SessionClient | None:
280
+ """Get the SessionClient for the given session_id, or None
281
+ if no such session exists.
282
+
283
+ Notes
284
+ -----
285
+ Threading: SAFE. May be called on any thread.
286
+ """
287
+ session_info = self._session_mgr.get_active_session_info(session_id)
288
+ if session_info is None:
289
+ return None
290
+ return session_info.client
291
+
292
+ def clear_user_info_for_session(self, session_id: str) -> None:
293
+ """Clear the user_info for the given session_id.
294
+
295
+ Notes
296
+ -----
297
+ Threading: SAFE. May be called on any thread.
298
+ """
299
+ session_info = self._session_mgr.get_session_info(session_id)
300
+ if session_info is not None:
301
+ session_info.session.clear_user_info()
302
+
303
+ async def start(self) -> None:
304
+ """Start the runtime. This must be called only once, before
305
+ any other functions are called.
306
+
307
+ When this coroutine returns, Streamlit is ready to accept new sessions.
308
+
309
+ Notes
310
+ -----
311
+ Threading: UNSAFE. Must be called on the eventloop thread.
312
+ """
313
+
314
+ # Create our AsyncObjects. We need to have a running eventloop to
315
+ # instantiate our various synchronization primitives.
316
+ async_objs = AsyncObjects(
317
+ eventloop=asyncio.get_running_loop(),
318
+ must_stop=asyncio.Event(),
319
+ has_connection=asyncio.Event(),
320
+ need_send_data=asyncio.Event(),
321
+ started=asyncio.Future(),
322
+ stopped=asyncio.Future(),
323
+ )
324
+ self._async_objs = async_objs
325
+
326
+ self._loop_coroutine_task = asyncio.create_task(
327
+ self._loop_coroutine(), name="Runtime.loop_coroutine"
328
+ )
329
+
330
+ await async_objs.started
331
+
332
+ def stop(self) -> None:
333
+ """Request that Streamlit close all sessions and stop running.
334
+ Note that Streamlit won't stop running immediately.
335
+
336
+ Notes
337
+ -----
338
+ Threading: SAFE. May be called from any thread.
339
+ """
340
+
341
+ async_objs = self._get_async_objs()
342
+
343
+ def stop_on_eventloop() -> None:
344
+ if self._state in {RuntimeState.STOPPING, RuntimeState.STOPPED}:
345
+ return
346
+
347
+ _LOGGER.debug("Runtime stopping...")
348
+ self._set_state(RuntimeState.STOPPING)
349
+ async_objs.must_stop.set()
350
+
351
+ async_objs.eventloop.call_soon_threadsafe(stop_on_eventloop)
352
+
353
+ def is_active_session(self, session_id: str) -> bool:
354
+ """True if the session_id belongs to an active session.
355
+
356
+ Notes
357
+ -----
358
+ Threading: SAFE. May be called on any thread.
359
+ """
360
+ return self._session_mgr.is_active_session(session_id)
361
+
362
+ def connect_session(
363
+ self,
364
+ client: SessionClient,
365
+ user_info: UserInfoType,
366
+ existing_session_id: str | None = None,
367
+ session_id_override: str | None = None,
368
+ ) -> str:
369
+ """Create a new session (or connect to an existing one) and return its unique ID.
370
+
371
+ Parameters
372
+ ----------
373
+ client
374
+ A concrete SessionClient implementation for communicating with
375
+ the session's client.
376
+ user_info
377
+ A dict that contains information about the session's user. For now,
378
+ it only (optionally) contains the user's email address.
379
+
380
+ {
381
+ "email": "example@example.com"
382
+ }
383
+ existing_session_id
384
+ The ID of an existing session to reconnect to. If one is not provided, a new
385
+ session is created. Note that whether the Runtime's SessionManager supports
386
+ reconnecting to an existing session depends on the SessionManager that this
387
+ runtime is configured with.
388
+ session_id_override
389
+ The ID to assign to a new session being created with this method. Setting
390
+ this can be useful when the service that a Streamlit Runtime is running in
391
+ wants to tie the lifecycle of a Streamlit session to some other session-like
392
+ object that it manages. Only one of existing_session_id and
393
+ session_id_override should be set.
394
+
395
+ Returns
396
+ -------
397
+ str
398
+ The session's unique string ID.
399
+
400
+ Notes
401
+ -----
402
+ Threading: UNSAFE. Must be called on the eventloop thread.
403
+ """
404
+ if existing_session_id and session_id_override:
405
+ raise RuntimeError(
406
+ "Only one of existing_session_id and session_id_override should be set. "
407
+ "This should never happen."
408
+ )
409
+
410
+ if self._state in {RuntimeState.STOPPING, RuntimeState.STOPPED}:
411
+ raise RuntimeStoppedError(f"Can't connect_session (state={self._state})")
412
+
413
+ session_id = self._session_mgr.connect_session(
414
+ client=client,
415
+ script_data=ScriptData(self._main_script_path, self._is_hello),
416
+ user_info=user_info,
417
+ existing_session_id=existing_session_id,
418
+ session_id_override=session_id_override,
419
+ )
420
+ self._set_state(RuntimeState.ONE_OR_MORE_SESSIONS_CONNECTED)
421
+ self._get_async_objs().has_connection.set()
422
+
423
+ return session_id
424
+
425
+ def create_session(
426
+ self,
427
+ client: SessionClient,
428
+ user_info: UserInfoType,
429
+ existing_session_id: str | None = None,
430
+ session_id_override: str | None = None,
431
+ ) -> str:
432
+ """Create a new session (or connect to an existing one) and return its unique ID.
433
+
434
+ Notes
435
+ -----
436
+ This method is simply an alias for connect_session added for backwards
437
+ compatibility.
438
+ """
439
+ _LOGGER.warning("create_session is deprecated! Use connect_session instead.")
440
+ return self.connect_session(
441
+ client=client,
442
+ user_info=user_info,
443
+ existing_session_id=existing_session_id,
444
+ session_id_override=session_id_override,
445
+ )
446
+
447
+ def close_session(self, session_id: str) -> None:
448
+ """Close and completely shut down a session.
449
+
450
+ This differs from disconnect_session in that it always completely shuts down a
451
+ session, permanently losing any associated state (session state, uploaded files,
452
+ etc.).
453
+
454
+ This function may be called multiple times for the same session,
455
+ which is not an error. (Subsequent calls just no-op.)
456
+
457
+ Parameters
458
+ ----------
459
+ session_id
460
+ The session's unique ID.
461
+
462
+ Notes
463
+ -----
464
+ Threading: UNSAFE. Must be called on the eventloop thread.
465
+ """
466
+ session_info = self._session_mgr.get_session_info(session_id)
467
+ if session_info:
468
+ self._session_mgr.close_session(session_id)
469
+ self._on_session_disconnected()
470
+
471
+ def disconnect_session(self, session_id: str) -> None:
472
+ """Disconnect a session. It will stop producing ForwardMsgs.
473
+
474
+ Differs from close_session because disconnected sessions can be reconnected to
475
+ for a brief window (depending on the SessionManager/SessionStorage
476
+ implementations used by the runtime).
477
+
478
+ This function may be called multiple times for the same session,
479
+ which is not an error. (Subsequent calls just no-op.)
480
+
481
+ Parameters
482
+ ----------
483
+ session_id
484
+ The session's unique ID.
485
+
486
+ Notes
487
+ -----
488
+ Threading: UNSAFE. Must be called on the eventloop thread.
489
+ """
490
+ session_info = self._session_mgr.get_active_session_info(session_id)
491
+ if session_info:
492
+ self._session_mgr.disconnect_session(session_id)
493
+ self._on_session_disconnected()
494
+
495
+ def handle_backmsg(self, session_id: str, msg: BackMsg) -> None:
496
+ """Send a BackMsg to an active session.
497
+
498
+ Parameters
499
+ ----------
500
+ session_id
501
+ The session's unique ID.
502
+ msg
503
+ The BackMsg to deliver to the session.
504
+
505
+ Notes
506
+ -----
507
+ Threading: UNSAFE. Must be called on the eventloop thread.
508
+ """
509
+ if self._state in {RuntimeState.STOPPING, RuntimeState.STOPPED}:
510
+ raise RuntimeStoppedError(f"Can't handle_backmsg (state={self._state})")
511
+
512
+ session_info = self._session_mgr.get_active_session_info(session_id)
513
+ if session_info is None:
514
+ _LOGGER.debug(
515
+ "Discarding BackMsg for disconnected session (id=%s)", session_id
516
+ )
517
+ return
518
+
519
+ session_info.session.handle_backmsg(msg)
520
+
521
+ def handle_backmsg_deserialization_exception(
522
+ self, session_id: str, exc: BaseException
523
+ ) -> None:
524
+ """Handle an Exception raised during deserialization of a BackMsg.
525
+
526
+ Parameters
527
+ ----------
528
+ session_id
529
+ The session's unique ID.
530
+ exc
531
+ The Exception.
532
+
533
+ Notes
534
+ -----
535
+ Threading: UNSAFE. Must be called on the eventloop thread.
536
+ """
537
+ if self._state in {RuntimeState.STOPPING, RuntimeState.STOPPED}:
538
+ raise RuntimeStoppedError(
539
+ f"Can't handle_backmsg_deserialization_exception (state={self._state})"
540
+ )
541
+
542
+ session_info = self._session_mgr.get_active_session_info(session_id)
543
+ if session_info is None:
544
+ _LOGGER.debug(
545
+ "Discarding BackMsg Exception for disconnected session (id=%s)",
546
+ session_id,
547
+ )
548
+ return
549
+
550
+ session_info.session.handle_backmsg_exception(exc)
551
+
552
+ @property
553
+ async def is_ready_for_browser_connection(self) -> tuple[bool, str]:
554
+ if self._state not in {
555
+ RuntimeState.INITIAL,
556
+ RuntimeState.STOPPING,
557
+ RuntimeState.STOPPED,
558
+ }:
559
+ return True, "ok"
560
+
561
+ return False, "unavailable"
562
+
563
+ async def does_script_run_without_error(self) -> tuple[bool, str]:
564
+ """Load and execute the app's script to verify it runs without an error.
565
+
566
+ Returns
567
+ -------
568
+ (True, "ok") if the script completes without error, or (False, err_msg)
569
+ if the script raises an exception.
570
+
571
+ Notes
572
+ -----
573
+ Threading: UNSAFE. Must be called on the eventloop thread.
574
+ """
575
+ # NOTE: We create an AppSession directly here instead of using the
576
+ # SessionManager intentionally. This isn't a "real" session and is only being
577
+ # used to test that the script runs without error.
578
+ session = AppSession(
579
+ script_data=ScriptData(self._main_script_path, self._is_hello),
580
+ uploaded_file_manager=self._uploaded_file_mgr,
581
+ script_cache=self._script_cache,
582
+ message_enqueued_callback=self._enqueued_some_message,
583
+ user_info={"email": "test@example.com"},
584
+ )
585
+
586
+ try:
587
+ session.request_rerun(None)
588
+
589
+ now = time.perf_counter()
590
+ while ( # noqa: ASYNC110
591
+ SCRIPT_RUN_WITHOUT_ERRORS_KEY not in session.session_state
592
+ and (time.perf_counter() - now) < SCRIPT_RUN_CHECK_TIMEOUT
593
+ ):
594
+ await asyncio.sleep(0.1)
595
+
596
+ if SCRIPT_RUN_WITHOUT_ERRORS_KEY not in session.session_state:
597
+ return False, "timeout"
598
+
599
+ ok = session.session_state[SCRIPT_RUN_WITHOUT_ERRORS_KEY]
600
+ msg = "ok" if ok else "error"
601
+
602
+ return ok, msg
603
+ finally:
604
+ session.shutdown()
605
+
606
+ def _set_state(self, new_state: RuntimeState) -> None:
607
+ _LOGGER.debug("Runtime state: %s -> %s", self._state, new_state)
608
+ self._state = new_state
609
+
610
+ async def _loop_coroutine(self) -> None:
611
+ """The main Runtime loop.
612
+
613
+ This function won't exit until `stop` is called.
614
+
615
+ Notes
616
+ -----
617
+ Threading: UNSAFE. Must be called on the eventloop thread.
618
+ """
619
+
620
+ async_objs = self._get_async_objs()
621
+
622
+ try:
623
+ if self._state == RuntimeState.INITIAL:
624
+ self._set_state(RuntimeState.NO_SESSIONS_CONNECTED)
625
+ elif self._state == RuntimeState.ONE_OR_MORE_SESSIONS_CONNECTED:
626
+ pass
627
+ else:
628
+ raise RuntimeError(f"Bad Runtime state at start: {self._state}") # noqa: TRY301
629
+
630
+ # Signal that we're started and ready to accept sessions
631
+ async_objs.started.set_result(None)
632
+
633
+ while not async_objs.must_stop.is_set():
634
+ if self._state == RuntimeState.NO_SESSIONS_CONNECTED: # type: ignore[comparison-overlap]
635
+ # mypy 1.4 incorrectly thinks this if-clause is unreachable,
636
+ # because it thinks self._state must be INITIAL | ONE_OR_MORE_SESSIONS_CONNECTED.
637
+
638
+ # Wait for new websocket connections (new sessions):
639
+ done_tasks, pending_tasks = await asyncio.wait( # type: ignore[unreachable]
640
+ (
641
+ asyncio.create_task(async_objs.must_stop.wait()),
642
+ asyncio.create_task(async_objs.has_connection.wait()),
643
+ ),
644
+ return_when=asyncio.FIRST_COMPLETED,
645
+ # On Windows, use a timeout to ensure signal handlers can be processed
646
+ timeout=_SIGNAL_CHECK_INTERVAL,
647
+ )
648
+ # Clean up pending tasks to avoid memory leaks
649
+ for task in pending_tasks:
650
+ task.cancel()
651
+
652
+ # If we timed out (Windows only), continue the loop to check must_stop
653
+ if not done_tasks and _SIGNAL_CHECK_INTERVAL is not None:
654
+ continue
655
+ elif self._state == RuntimeState.ONE_OR_MORE_SESSIONS_CONNECTED:
656
+ async_objs.need_send_data.clear()
657
+
658
+ for active_session_info in self._session_mgr.list_active_sessions():
659
+ msg_list = active_session_info.session.flush_browser_queue()
660
+ for msg in msg_list:
661
+ try:
662
+ self._send_message(active_session_info, msg)
663
+ except SessionClientDisconnectedError:
664
+ self._session_mgr.disconnect_session(
665
+ active_session_info.session.id
666
+ )
667
+
668
+ # Yield for a tick after sending a message.
669
+ await asyncio.sleep(0)
670
+
671
+ # Yield for a few milliseconds between session message
672
+ # flushing.
673
+ await asyncio.sleep(0.01)
674
+ else:
675
+ # Break out of the thread loop if we encounter any other state.
676
+ break
677
+
678
+ # Wait for new proto messages that need to be sent out:
679
+ done_tasks, pending_tasks = await asyncio.wait(
680
+ (
681
+ asyncio.create_task(async_objs.must_stop.wait()),
682
+ asyncio.create_task(async_objs.need_send_data.wait()),
683
+ ),
684
+ return_when=asyncio.FIRST_COMPLETED,
685
+ # On Windows, use a timeout to ensure signal handlers can be processed
686
+ timeout=_SIGNAL_CHECK_INTERVAL,
687
+ )
688
+ # We need to cancel the pending tasks (the `must_stop` one in most situations).
689
+ # Otherwise, this would stack up one waiting task per loop
690
+ # (e.g. per forward message). These tasks cannot be garbage collected
691
+ # causing an increase in memory (-> memory leak).
692
+ for task in pending_tasks:
693
+ task.cancel()
694
+
695
+ # If we timed out (Windows only), continue to check must_stop
696
+ if not done_tasks and _SIGNAL_CHECK_INTERVAL is not None:
697
+ continue
698
+
699
+ # Shut down all AppSessions.
700
+ for session_info in self._session_mgr.list_sessions():
701
+ # NOTE: We want to fully shut down sessions when the runtime stops for
702
+ # now, but this may change in the future if/when our notion of a session
703
+ # is no longer so tightly coupled to a browser tab.
704
+ self._session_mgr.close_session(session_info.session.id)
705
+
706
+ self._set_state(RuntimeState.STOPPED)
707
+ async_objs.stopped.set_result(None)
708
+
709
+ except Exception as e:
710
+ async_objs.stopped.set_exception(e)
711
+ traceback.print_exc()
712
+ _LOGGER.info(
713
+ """
714
+ Please report this bug at https://github.com/streamlit/streamlit/issues.
715
+ """
716
+ )
717
+
718
+ def _send_message(self, session_info: ActiveSessionInfo, msg: ForwardMsg) -> None:
719
+ """Send a message to a client.
720
+ If the client is likely to have already cached the message, we may
721
+ instead send a "reference" message that contains only the hash of the
722
+ message.
723
+
724
+ Parameters
725
+ ----------
726
+ session_info : ActiveSessionInfo
727
+ The ActiveSessionInfo associated with websocket
728
+ msg : ForwardMsg
729
+ The message to send to the client
730
+
731
+ Notes
732
+ -----
733
+ Threading: UNSAFE. Must be called on the eventloop thread.
734
+ """
735
+
736
+ # If this was a `script_finished` message, we increment the
737
+ # script_run_count for this session
738
+ if msg.WhichOneof("type") == "script_finished" and (
739
+ msg.script_finished == ForwardMsg.FINISHED_SUCCESSFULLY
740
+ ):
741
+ session_info.script_run_count += 1
742
+
743
+ # Ship it off!
744
+ session_info.client.write_forward_msg(msg)
745
+
746
+ def _enqueued_some_message(self) -> None:
747
+ """Callback called by AppSession after the AppSession has enqueued a
748
+ message. Sets the "needs_send_data" event, which causes our core
749
+ loop to wake up and flush client message queues.
750
+
751
+ Notes
752
+ -----
753
+ Threading: SAFE. May be called on any thread.
754
+ """
755
+ async_objs = self._get_async_objs()
756
+ async_objs.eventloop.call_soon_threadsafe(async_objs.need_send_data.set)
757
+
758
+ def _get_async_objs(self) -> AsyncObjects:
759
+ """Return our AsyncObjects instance. If the Runtime hasn't been
760
+ started, this will raise an error.
761
+ """
762
+ if self._async_objs is None:
763
+ raise RuntimeError("Runtime hasn't started yet!")
764
+ return self._async_objs
765
+
766
+ def _on_session_disconnected(self) -> None:
767
+ """Set the runtime state to NO_SESSIONS_CONNECTED if the last active
768
+ session was disconnected.
769
+ """
770
+ if (
771
+ self._state == RuntimeState.ONE_OR_MORE_SESSIONS_CONNECTED
772
+ and self._session_mgr.num_active_sessions() == 0
773
+ ):
774
+ self._get_async_objs().has_connection.clear()
775
+ self._set_state(RuntimeState.NO_SESSIONS_CONNECTED)