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
streamlit/auth_util.py ADDED
@@ -0,0 +1,583 @@
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 json
18
+ import re
19
+ from collections.abc import Callable, Mapping
20
+ from datetime import datetime, timedelta, timezone
21
+ from typing import TYPE_CHECKING, Any, Final, TypedDict, cast
22
+ from urllib.parse import urlencode, urlparse
23
+
24
+ from streamlit import config
25
+ from streamlit.errors import StreamlitAuthError
26
+ from streamlit.logger import get_logger
27
+ from streamlit.runtime.secrets import AttrDict, secrets_singleton
28
+
29
+ _LOGGER: Final = get_logger(__name__)
30
+
31
+ if TYPE_CHECKING:
32
+
33
+ class ProviderTokenPayload(TypedDict):
34
+ provider: str
35
+ exp: int
36
+
37
+
38
+ MAX_COOKIE_BYTES: Final = 4096
39
+ # Cookie attributes added by Tornado: "; Path=/; HttpOnly"
40
+ COOKIE_ATTRIBUTES: Final = "; Path=/; HttpOnly"
41
+ COOKIE_ATTR_SIZE: Final = len(COOKIE_ATTRIBUTES)
42
+ # Safety buffer for signing overhead to account for edge cases, rounding, and potential
43
+ # variations in signing implementations (e.g., longer timestamps after year 2286)
44
+ SIGNING_OVERHEAD_SAFETY_BUFFER: Final = 50
45
+ # Base64 encoding of 1 byte = 4 bytes, so overhead = total - 4
46
+ SINGLE_BYTE_BASE64_SIZE: Final = 4
47
+
48
+
49
+ class AuthCache:
50
+ """Simple cache implementation for storing info required for Authlib."""
51
+
52
+ def __init__(self) -> None:
53
+ self.cache: dict[str, Any] = {}
54
+
55
+ def get(self, key: str) -> Any:
56
+ return self.cache.get(key)
57
+
58
+ # for set method, we are follow the same signature used in Authlib
59
+ # the expires_in is not used in our case
60
+ def set(self, key: str, value: Any, expires_in: int | None = None) -> None: # noqa: ARG002
61
+ self.cache[key] = value
62
+
63
+ def get_dict(self) -> dict[str, Any]:
64
+ return self.cache
65
+
66
+ def delete(self, key: str) -> None:
67
+ self.cache.pop(key, None)
68
+
69
+
70
+ def is_authlib_installed() -> bool:
71
+ """Check if Authlib is installed."""
72
+ try:
73
+ import authlib
74
+
75
+ authlib_version = authlib.__version__
76
+ authlib_version_tuple = tuple(map(int, authlib_version.split(".")))
77
+
78
+ if authlib_version_tuple < (1, 3, 2):
79
+ return False
80
+ except (ImportError, ModuleNotFoundError):
81
+ return False
82
+ return True
83
+
84
+
85
+ def get_signing_secret() -> str:
86
+ """Get the cookie signing secret from the configuration or secrets.toml."""
87
+ signing_secret: str = config.get_option("server.cookieSecret")
88
+ if secrets_singleton.load_if_toml_exists():
89
+ auth_section = secrets_singleton.get("auth")
90
+ if auth_section:
91
+ signing_secret = auth_section.get("cookie_secret", signing_secret)
92
+ return signing_secret
93
+
94
+
95
+ def get_secrets_auth_section() -> AttrDict:
96
+ """Get the 'auth' section of the secrets.toml."""
97
+ auth_section = AttrDict({})
98
+ if secrets_singleton.load_if_toml_exists():
99
+ auth_section = cast("AttrDict", secrets_singleton.get("auth", AttrDict({})))
100
+
101
+ return auth_section
102
+
103
+
104
+ def get_expose_tokens_config() -> list[str]:
105
+ """Get the expose_tokens configuration from secrets.toml.
106
+
107
+ Returns a list of token types to expose. Accepts both string and list formats:
108
+ - expose_tokens = "id" -> ["id"]
109
+ - expose_tokens = ["id", "access"] -> ["id", "access"]
110
+ """
111
+ auth_section = get_secrets_auth_section()
112
+ expose_tokens = auth_section.get("expose_tokens")
113
+
114
+ if isinstance(expose_tokens, str):
115
+ res = [expose_tokens]
116
+ elif isinstance(expose_tokens, list):
117
+ res = [str(token) for token in expose_tokens]
118
+ else:
119
+ return []
120
+
121
+ if set(res) - {"id", "access"}:
122
+ raise StreamlitAuthError(
123
+ "Invalid expose_tokens configuration. Only 'id' and 'access' are allowed."
124
+ )
125
+
126
+ return res
127
+
128
+
129
+ def get_redirect_uri(auth_section: AttrDict) -> str | None:
130
+ """Get the redirect_uri from auth_section - filling in port number if needed."""
131
+
132
+ if "redirect_uri" not in auth_section:
133
+ return None
134
+
135
+ redirect_uri: str = auth_section["redirect_uri"]
136
+ if "{port}" in redirect_uri:
137
+ redirect_uri = redirect_uri.replace(
138
+ "{port}", str(config.get_option("server.port"))
139
+ )
140
+
141
+ try:
142
+ redirect_uri_parsed = urlparse(redirect_uri)
143
+ except ValueError:
144
+ raise StreamlitAuthError(
145
+ f"Invalid redirect_uri: {redirect_uri}. Please check your configuration."
146
+ )
147
+
148
+ return redirect_uri_parsed.geturl()
149
+
150
+
151
+ def get_validated_redirect_uri() -> str | None:
152
+ """Get the redirect_uri from secrets, validating it ends with /oauth2callback.
153
+
154
+ This is used for logout flows where we need a validated redirect URI
155
+ that matches the OAuth callback path.
156
+
157
+ Returns
158
+ -------
159
+ str | None
160
+ The validated redirect URI, or None if not configured or invalid.
161
+ """
162
+ auth_section = get_secrets_auth_section()
163
+ if not auth_section:
164
+ return None
165
+
166
+ redirect_uri = get_redirect_uri(auth_section)
167
+ if not redirect_uri:
168
+ return None
169
+
170
+ if not redirect_uri.endswith("/oauth2callback"):
171
+ _LOGGER.warning("Redirect URI does not end with /oauth2callback")
172
+ return None
173
+
174
+ return redirect_uri
175
+
176
+
177
+ def get_origin_from_redirect_uri() -> str | None:
178
+ """Extract the origin (scheme + host) from the configured redirect_uri.
179
+
180
+ Returns
181
+ -------
182
+ str | None
183
+ The origin in format "scheme://host:port", or None if not configured.
184
+ """
185
+ auth_section = get_secrets_auth_section()
186
+ if not auth_section:
187
+ return None
188
+
189
+ redirect_uri = get_redirect_uri(auth_section)
190
+ if not redirect_uri:
191
+ return None
192
+
193
+ redirect_uri_parsed = urlparse(redirect_uri)
194
+ return f"{redirect_uri_parsed.scheme}://{redirect_uri_parsed.netloc}"
195
+
196
+
197
+ def build_logout_url(
198
+ end_session_endpoint: str,
199
+ client_id: str,
200
+ post_logout_redirect_uri: str,
201
+ id_token: str | None = None,
202
+ ) -> str:
203
+ """Build an OIDC logout URL with the required parameters.
204
+
205
+ Parameters
206
+ ----------
207
+ end_session_endpoint
208
+ The OIDC provider's end_session_endpoint URL.
209
+ client_id
210
+ The OAuth client ID.
211
+ post_logout_redirect_uri
212
+ The URI to redirect to after logout.
213
+ id_token
214
+ Optional ID token to include as id_token_hint for the logout request.
215
+
216
+ Returns
217
+ -------
218
+ str
219
+ The complete logout URL with query parameters.
220
+ """
221
+ from urllib.parse import parse_qsl
222
+
223
+ logout_params: dict[str, str] = {
224
+ "client_id": client_id,
225
+ "post_logout_redirect_uri": post_logout_redirect_uri,
226
+ }
227
+
228
+ if id_token:
229
+ logout_params["id_token_hint"] = id_token
230
+
231
+ # Per OIDC spec, end_session_endpoint should be a clean URL without query params,
232
+ # but we handle existing params defensively for non-standard providers.
233
+ parsed = urlparse(end_session_endpoint)
234
+ existing_params = dict(parse_qsl(parsed.query))
235
+ merged_params = {**existing_params, **logout_params}
236
+ new_query = urlencode(merged_params)
237
+ return parsed._replace(query=new_query).geturl()
238
+
239
+
240
+ def encode_provider_token(provider: str) -> str:
241
+ """Returns a signed JWT token with the provider and expiration time."""
242
+ try:
243
+ from authlib.jose import jwt
244
+ except ImportError:
245
+ raise StreamlitAuthError(
246
+ """To use authentication features, you need to install Authlib>=1.3.2, e.g. via `pip install Authlib`."""
247
+ ) from None
248
+
249
+ header = {"alg": "HS256"}
250
+ payload = {
251
+ "provider": provider,
252
+ "exp": datetime.now(timezone.utc) + timedelta(minutes=2),
253
+ }
254
+ provider_token: bytes = jwt.encode(header, payload, get_signing_secret())
255
+ # JWT token is a byte string, so we need to decode it to a URL compatible string
256
+ return provider_token.decode("latin-1")
257
+
258
+
259
+ def decode_provider_token(provider_token: str) -> ProviderTokenPayload:
260
+ """Decode the JWT token and validate the claims."""
261
+ try:
262
+ from authlib.jose import JoseError, JWTClaims, jwt
263
+ except ImportError:
264
+ raise StreamlitAuthError(
265
+ """To use authentication features, you need to install Authlib>=1.3.2, e.g. via `pip install Authlib`."""
266
+ ) from None
267
+
268
+ # Our JWT token is short-lived (2 minutes), so we check here that it contains
269
+ # the 'exp' (and it is not expired), and 'provider' field exists.
270
+ claim_options = {"exp": {"essential": True}, "provider": {"essential": True}}
271
+ try:
272
+ payload: JWTClaims = jwt.decode(
273
+ provider_token, get_signing_secret(), claims_options=claim_options
274
+ )
275
+ payload.validate()
276
+ except JoseError as e:
277
+ raise StreamlitAuthError(f"Error decoding provider token: {e}") from None
278
+
279
+ return cast("ProviderTokenPayload", payload)
280
+
281
+
282
+ def generate_default_provider_section(auth_section: AttrDict) -> dict[str, Any]:
283
+ """Generate a default provider section for the 'auth' section of secrets.toml."""
284
+ default_provider_section = {}
285
+ if auth_section.get("client_id"):
286
+ default_provider_section["client_id"] = auth_section.get("client_id")
287
+ if auth_section.get("client_secret"):
288
+ default_provider_section["client_secret"] = auth_section.get("client_secret")
289
+ if auth_section.get("server_metadata_url"):
290
+ default_provider_section["server_metadata_url"] = auth_section.get(
291
+ "server_metadata_url"
292
+ )
293
+ if auth_section.get("client_kwargs"):
294
+ default_provider_section["client_kwargs"] = cast(
295
+ "AttrDict", auth_section.get("client_kwargs", AttrDict({}))
296
+ ).to_dict()
297
+ if auth_section.get("expose_tokens"):
298
+ default_provider_section["expose_tokens"] = auth_section.get("expose_tokens")
299
+ return default_provider_section
300
+
301
+
302
+ def set_cookie_with_chunks(
303
+ set_single_cookie_fn: Callable[[str, str], None],
304
+ create_signed_value_fn: Callable[[str, str], bytes],
305
+ cookie_name: str,
306
+ value: dict[str, Any],
307
+ ) -> None:
308
+ """Set a cookie, splitting into multiple cookies if necessary.
309
+
310
+ Args:
311
+ set_single_cookie_fn: Function to set a single cookie (cookie_name, value)
312
+ create_signed_value_fn: Function to create a signed cookie value (cookie_name, value)
313
+ cookie_name: Name of the cookie
314
+ value: Dictionary value to serialize and store
315
+ """
316
+ serialized_cookie_value = json.dumps(value)
317
+
318
+ # Calculate actual cookie size using the provided signing function
319
+ signed_value = create_signed_value_fn(cookie_name, serialized_cookie_value)
320
+
321
+ # Cookie format: "name=value" + COOKIE_ATTRIBUTES
322
+ actual_cookie_size = len(cookie_name) + 1 + len(signed_value) + COOKIE_ATTR_SIZE
323
+
324
+ # Check if cookie needs to be split
325
+ if actual_cookie_size > MAX_COOKIE_BYTES:
326
+ _LOGGER.debug(
327
+ "Cookie size (%d bytes) exceeds browser limit. Splitting into multiple cookies.",
328
+ actual_cookie_size,
329
+ )
330
+ _set_split_cookie(
331
+ set_single_cookie_fn,
332
+ create_signed_value_fn,
333
+ cookie_name,
334
+ serialized_cookie_value,
335
+ )
336
+ else:
337
+ set_single_cookie_fn(cookie_name, serialized_cookie_value)
338
+
339
+
340
+ def _calculate_signing_overhead(
341
+ create_signed_value_fn: Callable[[str, str], bytes],
342
+ cookie_name: str,
343
+ ) -> int:
344
+ """Calculate the server's signing overhead by measuring the size difference.
345
+
346
+ This empirically measures the overhead added by the signing function (e.g., Tornado's
347
+ create_signed_value) by signing a minimal test value and computing the difference.
348
+
349
+ Args:
350
+ create_signed_value_fn: Function to create a signed cookie value
351
+ cookie_name: Name of the cookie (affects overhead due to length prefix)
352
+
353
+ Returns
354
+ -------
355
+ The number of bytes added by signing (excluding the base64-encoded value)
356
+ """
357
+ test_value = "x" # Minimal test value (1 byte)
358
+ signed = create_signed_value_fn(cookie_name, test_value)
359
+ return len(signed) - SINGLE_BYTE_BASE64_SIZE
360
+
361
+
362
+ def _set_split_cookie(
363
+ set_single_cookie_fn: Callable[[str, str], None],
364
+ create_signed_value_fn: Callable[[str, str], bytes],
365
+ cookie_name: str,
366
+ value: str,
367
+ ) -> None:
368
+ """Split a large cookie value into multiple smaller cookies.
369
+
370
+ The main cookie always exists and either contains the whole value or the chunk count.
371
+ Additional chunks are stored as cookie_name_1, cookie_name_2, etc.
372
+
373
+ Args:
374
+ set_single_cookie_fn: Function to set a single cookie (cookie_name, value)
375
+ create_signed_value_fn: Function to create a signed cookie value
376
+ cookie_name: Name of the cookie
377
+ value: Serialized string value to split and store
378
+ """
379
+ # Calculate overhead empirically from the actual signing function, plus safety buffer
380
+ signing_overhead = (
381
+ _calculate_signing_overhead(create_signed_value_fn, cookie_name)
382
+ + SIGNING_OVERHEAD_SAFETY_BUFFER
383
+ )
384
+
385
+ # Available space for the signed value:
386
+ # MAX_COOKIE_BYTES - cookie_name - "=" (1 byte) - cookie attributes
387
+ available_for_signed_value = (
388
+ MAX_COOKIE_BYTES - len(cookie_name) - 1 - COOKIE_ATTR_SIZE
389
+ )
390
+
391
+ # Space available for the base64-encoded value (after subtracting signing overhead)
392
+ available_for_base64_value = available_for_signed_value - signing_overhead
393
+
394
+ # If there is not enough space for the base64-encoded value, raise an error.
395
+ # We need at least 4 bytes for a minimal base64-encoded value.
396
+ if available_for_base64_value < SINGLE_BYTE_BASE64_SIZE:
397
+ raise StreamlitAuthError("Not enough space available for the signed value.")
398
+
399
+ # Convert from base64 space to raw value space (base64 has 4/3 expansion ratio)
400
+ chunk_size = (available_for_base64_value * 3) // 4
401
+ chunks = []
402
+ for i in range(0, len(value), chunk_size):
403
+ chunk = value[i : i + chunk_size]
404
+ chunks.append(chunk)
405
+
406
+ if len(chunks) == 1:
407
+ set_single_cookie_fn(cookie_name, chunks[0])
408
+ return
409
+
410
+ # Store count in the main cookie
411
+ set_single_cookie_fn(cookie_name, f"chunks-{len(chunks)}")
412
+
413
+ # Store remaining chunks as cookie_name_1, cookie_name_2, etc.
414
+ for i in range(len(chunks)):
415
+ chunk_name = f"{cookie_name}_{i + 1}"
416
+ set_single_cookie_fn(chunk_name, chunks[i])
417
+
418
+ _LOGGER.info(
419
+ "Split cookie '%s' into %d chunks",
420
+ cookie_name,
421
+ len(chunks),
422
+ )
423
+
424
+
425
+ _chunks_regex = re.compile(rb"chunks-(\d+)")
426
+
427
+
428
+ def get_cookie_with_chunks(
429
+ get_single_cookie_fn: Callable[[str], bytes | None],
430
+ cookie_name: str,
431
+ ) -> bytes | None:
432
+ """Get a cookie, reconstructing from chunks if it was split.
433
+
434
+ If a count cookie exists, the main cookie contains the first chunk,
435
+ and additional chunks are in cookie_name_1, cookie_name_2, etc.
436
+ If no count cookie exists, the main cookie contains the entire value.
437
+
438
+ Args:
439
+ get_single_cookie_fn: Function to get a single cookie (cookie_name) -> bytes | None
440
+ cookie_name: Name of the cookie
441
+
442
+ Returns
443
+ -------
444
+ Cookie value as bytes, or None if not found
445
+ """
446
+ cookie_value = get_single_cookie_fn(cookie_name)
447
+ if cookie_value is None:
448
+ return cookie_value
449
+
450
+ match = _chunks_regex.match(cookie_value)
451
+ if match is None:
452
+ return cookie_value
453
+
454
+ # Parse chunk count
455
+ try:
456
+ chunk_count = int(match.group(1))
457
+ except (ValueError, TypeError):
458
+ _LOGGER.exception("Invalid chunk count for cookie '%s'", cookie_name)
459
+ return None
460
+
461
+ # Reconstruct the original value from chunks
462
+ chunks = []
463
+
464
+ for i in range(chunk_count):
465
+ chunk_name = f"{cookie_name}_{i + 1}"
466
+ chunk_value = get_single_cookie_fn(chunk_name)
467
+ if chunk_value is None:
468
+ _LOGGER.error("Missing chunk %d for cookie '%s'", i + 1, cookie_name)
469
+ return None
470
+ chunks.append(chunk_value)
471
+
472
+ reconstructed_value = b"".join(chunks)
473
+ return reconstructed_value
474
+
475
+
476
+ def clear_cookie_and_chunks(
477
+ get_single_cookie_fn: Callable[[str], bytes | None],
478
+ clear_single_cookie_fn: Callable[[str], None],
479
+ cookie_name: str,
480
+ ) -> None:
481
+ """Clear a cookie and any associated chunk cookies.
482
+
483
+ The main cookie always exists. If there are chunks, also clear
484
+ cookie_name_1, cookie_name_2, etc., and the count cookie.
485
+
486
+ Args:
487
+ get_single_cookie_fn: Function to get a single cookie (cookie_name) -> bytes | None
488
+ clear_single_cookie_fn: Function to clear a single cookie (cookie_name)
489
+ cookie_name: Name of the cookie
490
+ """
491
+ cookie_value = get_single_cookie_fn(cookie_name)
492
+ clear_single_cookie_fn(cookie_name)
493
+ if cookie_value is None:
494
+ return
495
+
496
+ match = _chunks_regex.match(cookie_value)
497
+ if match is None:
498
+ return
499
+
500
+ try:
501
+ chunk_count = int(match.group(1))
502
+ # Clear additional chunk cookies (starting from 1, since main cookie is chunk 0)
503
+ for i in range(1, chunk_count + 1):
504
+ clear_single_cookie_fn(f"{cookie_name}_{i}")
505
+ except (ValueError, TypeError):
506
+ # If count is invalid, but we already cleared the main cookie
507
+ # so we can ignore it
508
+ pass
509
+
510
+
511
+ def validate_auth_credentials(provider: str) -> None:
512
+ """Validate the general auth credentials and auth credentials for the given
513
+ provider.
514
+ """
515
+ if not secrets_singleton.load_if_toml_exists():
516
+ raise StreamlitAuthError(
517
+ """To use authentication features you need to configure credentials for at
518
+ least one authentication provider in `.streamlit/secrets.toml`."""
519
+ )
520
+
521
+ auth_section = secrets_singleton.get("auth")
522
+ if auth_section is None:
523
+ raise StreamlitAuthError(
524
+ """To use authentication features you need to configure credentials for at
525
+ least one authentication provider in `.streamlit/secrets.toml`."""
526
+ )
527
+ if "redirect_uri" not in auth_section:
528
+ raise StreamlitAuthError(
529
+ """Authentication credentials in `.streamlit/secrets.toml` are missing the
530
+ "redirect_uri" key. Please check your configuration."""
531
+ )
532
+ if "cookie_secret" not in auth_section:
533
+ raise StreamlitAuthError(
534
+ """Authentication credentials in `.streamlit/secrets.toml` are missing the
535
+ "cookie_secret" key. Please check your configuration."""
536
+ )
537
+
538
+ provider_section = auth_section.get(provider)
539
+
540
+ # TODO(kajarenc): Revisit this check later when investigating the ability
541
+ # TODO(kajarenc): to add "_" to the provider name.
542
+ if "_" in provider:
543
+ raise StreamlitAuthError(
544
+ f'Auth provider name "{provider}" contains an underscore. '
545
+ f"Please use a provider name without underscores."
546
+ )
547
+
548
+ if provider_section is None and provider == "default":
549
+ provider_section = generate_default_provider_section(auth_section)
550
+
551
+ if provider_section is None:
552
+ if provider == "default":
553
+ raise StreamlitAuthError(
554
+ """Authentication credentials in `.streamlit/secrets.toml` are missing for
555
+ the default authentication provider. Please check your configuration."""
556
+ )
557
+ raise StreamlitAuthError(
558
+ f"Authentication credentials in `.streamlit/secrets.toml` are missing for "
559
+ f'the authentication provider "{provider}". Please check your '
560
+ f"configuration."
561
+ )
562
+
563
+ if not isinstance(provider_section, Mapping):
564
+ raise StreamlitAuthError(
565
+ f"Authentication credentials in `.streamlit/secrets.toml` for the "
566
+ f'authentication provider "{provider}" must be valid TOML. Please check '
567
+ f"your configuration."
568
+ )
569
+
570
+ required_keys = ["client_id", "client_secret", "server_metadata_url"]
571
+ missing_keys = [key for key in required_keys if key not in provider_section]
572
+ if missing_keys:
573
+ if provider == "default":
574
+ raise StreamlitAuthError(
575
+ "Authentication credentials in `.streamlit/secrets.toml` for the "
576
+ f"default authentication provider are missing the following keys: "
577
+ f"{missing_keys}. Please check your configuration."
578
+ )
579
+ raise StreamlitAuthError(
580
+ "Authentication credentials in `.streamlit/secrets.toml` for the "
581
+ f'authentication provider "{provider}" are missing the following keys: '
582
+ f"{missing_keys}. Please check your configuration."
583
+ )
streamlit/cli_util.py ADDED
@@ -0,0 +1,107 @@
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
+ """Utilities related to the CLI."""
16
+
17
+ from __future__ import annotations
18
+
19
+ import os
20
+ from typing import Any
21
+
22
+ from streamlit import env_util, errors
23
+
24
+
25
+ def print_to_cli(message: str, **kwargs: Any) -> None:
26
+ """Print a message to the terminal using click if available, else print
27
+ using the built-in print function.
28
+
29
+ You can provide any keyword arguments that click.secho supports.
30
+ """
31
+ try:
32
+ import click
33
+
34
+ click.secho(message, **kwargs)
35
+ except ImportError:
36
+ print(message, flush=True) # noqa: T201
37
+
38
+
39
+ def style_for_cli(message: str, **kwargs: Any) -> str:
40
+ """Style a message using click if available, else return the message
41
+ unchanged.
42
+
43
+ You can provide any keyword arguments that click.style supports.
44
+ """
45
+
46
+ try:
47
+ import click
48
+
49
+ return click.style(message, **kwargs)
50
+ except ImportError:
51
+ return message
52
+
53
+
54
+ def _open_browser_with_webbrowser(url: str) -> None:
55
+ import webbrowser
56
+
57
+ webbrowser.open(url)
58
+
59
+
60
+ def _open_browser_with_command(command: str, url: str) -> None:
61
+ cmd_line = [command, url]
62
+ with open(os.devnull, "w", encoding="utf-8") as devnull:
63
+ import subprocess # noqa: S404
64
+
65
+ subprocess.Popen(cmd_line, stdout=devnull, stderr=subprocess.STDOUT) # noqa: S603
66
+
67
+
68
+ def open_browser(url: str) -> None:
69
+ """Open a web browser pointing to a given URL.
70
+
71
+ We use this function instead of Python's `webbrowser` module because this
72
+ way we can capture stdout/stderr to avoid polluting the terminal with the
73
+ browser's messages. For example, Chrome always prints things like "Created
74
+ new window in existing browser session", and those get on the user's way.
75
+
76
+ url : str
77
+ The URL. Must include the protocol.
78
+
79
+ """
80
+ # Treat Windows separately because:
81
+ # 1. /dev/null doesn't exist.
82
+ # 2. subprocess.Popen(['start', url]) doesn't actually pop up the
83
+ # browser even though 'start url' works from the command prompt.
84
+ # Fun!
85
+ # Also, use webbrowser if we are on Linux and xdg-open is not installed.
86
+ #
87
+ # We don't use the webbrowser module on Linux and Mac because some browsers
88
+ # (ahem... Chrome) always print "Opening in existing browser session" to
89
+ # the terminal, which is spammy and annoying. So instead we start the
90
+ # browser ourselves and send all its output to /dev/null.
91
+
92
+ if env_util.IS_WINDOWS:
93
+ _open_browser_with_webbrowser(url)
94
+ return
95
+ if env_util.IS_LINUX_OR_BSD:
96
+ if env_util.is_executable_in_path("xdg-open"):
97
+ _open_browser_with_command("xdg-open", url)
98
+ return
99
+ _open_browser_with_webbrowser(url)
100
+ return
101
+ if env_util.IS_DARWIN:
102
+ _open_browser_with_command("open", url)
103
+ return
104
+
105
+ import platform
106
+
107
+ raise errors.Error(f'Cannot open browser in platform "{platform.system()}"') # ty: ignore[unresolved-attribute]