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,193 @@
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
+ """Serve static assets for Custom Components v2.
16
+
17
+ This module defines a Tornado ``RequestHandler`` that serves static files for
18
+ Custom Components v2 from their registered component directories. Requests are
19
+ resolved safely within the component's root to avoid directory traversal and are
20
+ served with appropriate content type and cache headers. CORS headers are applied
21
+ based on the server configuration.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import os
27
+ from typing import TYPE_CHECKING, Final, cast
28
+
29
+ import tornado.web
30
+
31
+ import streamlit.web.server.routes
32
+ from streamlit.logger import get_logger
33
+ from streamlit.web.server.component_file_utils import (
34
+ build_safe_abspath,
35
+ guess_content_type,
36
+ )
37
+
38
+ if TYPE_CHECKING:
39
+ from streamlit.components.v2.component_manager import BidiComponentManager
40
+
41
+ _LOGGER: Final = get_logger(__name__)
42
+
43
+
44
+ class BidiComponentRequestHandler(tornado.web.RequestHandler):
45
+ """Request handler for serving Custom Components v2 static assets.
46
+
47
+ The handler resolves a requested path to a registered component's asset
48
+ within its component root, writes the file contents to the response, and
49
+ sets appropriate ``Content-Type`` and cache headers. If the component or
50
+ asset cannot be found, a suitable HTTP status is returned.
51
+ """
52
+
53
+ def initialize(self, component_manager: BidiComponentManager) -> None:
54
+ """Initialize the handler with the given component manager.
55
+
56
+ Parameters
57
+ ----------
58
+ component_manager : BidiComponentManager
59
+ Manager used to look up registered components and their root paths.
60
+ """
61
+ self._component_manager = component_manager
62
+
63
+ def get(self, path: str) -> None:
64
+ """Serve a component asset for the given URL path.
65
+
66
+ The first path segment is interpreted as the component name. The rest
67
+ of the path is resolved to a file within that component's root
68
+ directory. If the file exists and is readable, its bytes are written to
69
+ the response and the ``Content-Type`` header is set based on the file
70
+ type.
71
+
72
+ Parameters
73
+ ----------
74
+ path : str
75
+ Request path in the form ``"<component_name>/<relative_file>"``.
76
+
77
+ Notes
78
+ -----
79
+ This method writes directly to the response and sets appropriate HTTP
80
+ status codes on error (``404`` for missing components/files, ``400`` for
81
+ unsafe paths).
82
+ """
83
+ parts = path.split("/")
84
+ component_name = parts[0]
85
+ component_def = self._component_manager.get(component_name)
86
+ if component_def is None:
87
+ self.write("not found")
88
+ self.set_status(404)
89
+ return
90
+
91
+ # Get the component path from the component manager
92
+ component_path = self._component_manager.get_component_path(component_name)
93
+ if component_path is None:
94
+ self.write("not found")
95
+ self.set_status(404)
96
+ return
97
+
98
+ # Build a safe absolute path within the component root
99
+ filename = "/".join(parts[1:])
100
+ # If no file segment is provided (e.g. only component name or trailing slash),
101
+ # treat as not found rather than attempting to open a directory.
102
+ if not filename or filename.endswith("/"):
103
+ self.write("not found")
104
+ self.set_status(404)
105
+ return
106
+ abspath = build_safe_abspath(component_path, filename)
107
+ if abspath is None:
108
+ self.write("Bad Request")
109
+ self.set_status(400)
110
+ return
111
+
112
+ # If the resolved path is a directory, return 404 not found.
113
+ if os.path.isdir(abspath):
114
+ self.write("not found")
115
+ self.set_status(404)
116
+ return
117
+
118
+ try:
119
+ with open(abspath, "rb") as file:
120
+ contents = file.read()
121
+ except OSError:
122
+ sanitized_abspath = abspath.replace("\n", "").replace("\r", "")
123
+ _LOGGER.exception(
124
+ "BidiComponentRequestHandler: GET %s read error", sanitized_abspath
125
+ )
126
+ self.write("read error")
127
+ self.set_status(404)
128
+ return
129
+
130
+ self.write(contents)
131
+ self.set_header("Content-Type", guess_content_type(abspath))
132
+
133
+ self.set_extra_headers(path)
134
+
135
+ def set_extra_headers(self, path: str) -> None:
136
+ """Disable cache for HTML files.
137
+
138
+ We assume other assets like JS and CSS are suffixed with their hash, so
139
+ they can be cached indefinitely.
140
+ """
141
+ if path.endswith(".html"):
142
+ self.set_header("Cache-Control", "no-cache")
143
+ else:
144
+ self.set_header("Cache-Control", "public")
145
+
146
+ def set_default_headers(self) -> None:
147
+ """Set default CORS headers based on server configuration.
148
+
149
+ If cross-origin requests are fully allowed, ``Access-Control-Allow-
150
+ Origin`` is set to ``"*"``. Otherwise, if the request ``Origin`` header
151
+ is an allowed origin, the header is echoed back.
152
+ """
153
+ if streamlit.web.server.routes.allow_all_cross_origin_requests():
154
+ self.set_header("Access-Control-Allow-Origin", "*")
155
+ elif streamlit.web.server.routes.is_allowed_origin(
156
+ origin := self.request.headers.get("Origin")
157
+ ):
158
+ self.set_header("Access-Control-Allow-Origin", cast("str", origin))
159
+
160
+ def options(self) -> None:
161
+ """Handle preflight CORS requests.
162
+
163
+ Returns
164
+ -------
165
+ None
166
+ Responds with HTTP ``204 No Content`` to indicate that the actual
167
+ request can proceed.
168
+ """
169
+ self.set_status(204)
170
+ self.finish()
171
+
172
+ @staticmethod
173
+ def get_url(file_id: str) -> str:
174
+ """Return the URL for a component asset identified by ``file_id``.
175
+
176
+ Parameters
177
+ ----------
178
+ file_id : str
179
+ Component file identifier (typically a relative path or hashed
180
+ filename).
181
+
182
+ Returns
183
+ -------
184
+ str
185
+ Relative URL path for the resource, to be joined with the server
186
+ base URL.
187
+
188
+ Examples
189
+ --------
190
+ >>> BidiComponentRequestHandler.get_url("my_component/main.js")
191
+ '_stcore/bidi-components/my_component/main.js'
192
+ """
193
+ return f"_stcore/bidi-components/{file_id}"
@@ -0,0 +1,341 @@
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 hmac
18
+ import json
19
+ from typing import TYPE_CHECKING, Any, Final
20
+ from urllib.parse import urlparse
21
+
22
+ import tornado.websocket
23
+ from tornado.escape import utf8
24
+ from tornado.websocket import WebSocketHandler
25
+
26
+ from streamlit import config
27
+ from streamlit.auth_util import get_cookie_with_chunks, get_expose_tokens_config
28
+ from streamlit.logger import get_logger
29
+ from streamlit.proto.BackMsg_pb2 import BackMsg
30
+ from streamlit.runtime import Runtime, SessionClient, SessionClientDisconnectedError
31
+ from streamlit.runtime.runtime_util import serialize_forward_msg
32
+ from streamlit.runtime.session_manager import ClientContext
33
+ from streamlit.web.server.server_util import (
34
+ AUTH_COOKIE_NAME,
35
+ TOKENS_COOKIE_NAME,
36
+ is_url_from_allowed_origins,
37
+ is_xsrf_enabled,
38
+ )
39
+
40
+ if TYPE_CHECKING:
41
+ from collections.abc import Awaitable, Iterable, Mapping
42
+
43
+ from tornado.httputil import HTTPServerRequest
44
+
45
+ from streamlit.proto.ForwardMsg_pb2 import ForwardMsg
46
+
47
+ _LOGGER: Final = get_logger(__name__)
48
+
49
+
50
+ class TornadoClientContext(ClientContext):
51
+ """Tornado-specific implementation of ClientContext.
52
+
53
+ Captures headers, cookies, and client info from the initial WebSocket handshake.
54
+ Values are cached at construction time since they represent the initial request
55
+ context and should not change during the connection lifetime.
56
+ """
57
+
58
+ def __init__(self, tornado_request: HTTPServerRequest) -> None:
59
+ self._headers: list[tuple[str, str]] = list(tornado_request.headers.get_all())
60
+ self._cookies: dict[str, str] = {
61
+ k: m.value for k, m in tornado_request.cookies.items()
62
+ }
63
+ self._remote_ip: str | None = tornado_request.remote_ip
64
+
65
+ @property
66
+ def headers(self) -> Iterable[tuple[str, str]]:
67
+ """All headers as (name, value) tuples."""
68
+ return self._headers
69
+
70
+ @property
71
+ def cookies(self) -> Mapping[str, str]:
72
+ """Cookies as a name-to-value mapping."""
73
+ return self._cookies
74
+
75
+ @property
76
+ def remote_ip(self) -> str | None:
77
+ """The client's remote IP address."""
78
+ return self._remote_ip
79
+
80
+
81
+ class BrowserWebSocketHandler(WebSocketHandler, SessionClient):
82
+ """Handles a WebSocket connection from the browser."""
83
+
84
+ def initialize(self, runtime: Runtime) -> None:
85
+ self._runtime = runtime
86
+ self._session_id: str | None = None
87
+ # The XSRF cookie is normally set when xsrf_form_html is used, but in a
88
+ # pure-Javascript application that does not use any regular forms we just
89
+ # need to read the self.xsrf_token manually to set the cookie as a side
90
+ # effect. See https://www.tornadoweb.org/en/stable/guide/security.html#cross-site-request-forgery-protection
91
+ # for more details.
92
+ if is_xsrf_enabled():
93
+ _ = self.xsrf_token
94
+
95
+ # Do this once instead of on every open
96
+ self.expose_tokens = get_expose_tokens_config()
97
+
98
+ def get_signed_cookie(
99
+ self,
100
+ name: str,
101
+ value: str | None = None,
102
+ max_age_days: float = 31,
103
+ min_version: int | None = None,
104
+ ) -> bytes | None:
105
+ """Get a signed cookie from the request, reconstructing from chunks if needed.
106
+
107
+ Added for compatibility with Tornado < 6.3.0. Also handles chunked cookies
108
+ automatically.
109
+
110
+ See release notes: https://www.tornadoweb.org/en/stable/releases/v6.3.0.html#deprecation-notices
111
+ """
112
+
113
+ def get_single_cookie(cookie_name: str) -> bytes | None:
114
+ try:
115
+ return super(BrowserWebSocketHandler, self).get_signed_cookie(
116
+ cookie_name, value, max_age_days, min_version
117
+ )
118
+ except AttributeError:
119
+ return super(BrowserWebSocketHandler, self).get_secure_cookie(
120
+ cookie_name, value, max_age_days, min_version
121
+ )
122
+
123
+ return get_cookie_with_chunks(get_single_cookie, name)
124
+
125
+ def check_origin(self, origin: str) -> bool:
126
+ """Set up CORS."""
127
+ return super().check_origin(origin) or is_url_from_allowed_origins(origin)
128
+
129
+ def _validate_xsrf_token(self, supplied_token: str) -> bool:
130
+ """Inspired by tornado.web.RequestHandler.check_xsrf_cookie method,
131
+ to check the XSRF token passed in Websocket connection header.
132
+ """
133
+ _, token, _ = self._decode_xsrf_token(supplied_token)
134
+ _, expected_token, _ = self._get_raw_xsrf_token()
135
+
136
+ decoded_token = utf8(token)
137
+ decoded_expected_token = utf8(expected_token)
138
+
139
+ if not decoded_token or not decoded_expected_token:
140
+ return False
141
+ return hmac.compare_digest(decoded_token, decoded_expected_token)
142
+
143
+ def _parse_user_cookie(self, raw_cookie_value: bytes) -> dict[str, Any]:
144
+ """Process the user cookie and extract the user info after
145
+ validating the origin. Origin is validated for security reasons.
146
+ """
147
+ cookie_value = json.loads(raw_cookie_value)
148
+ user_info = {}
149
+
150
+ cookie_value_origin = cookie_value.get("origin", None)
151
+ parsed_origin_from_header = urlparse(self.request.headers["Origin"])
152
+ expected_origin_value = (
153
+ parsed_origin_from_header.scheme + "://" + parsed_origin_from_header.netloc
154
+ )
155
+ if cookie_value_origin == expected_origin_value:
156
+ user_info["is_logged_in"] = cookie_value.get("is_logged_in", False)
157
+ del cookie_value["origin"]
158
+ del cookie_value["is_logged_in"]
159
+ user_info.update(cookie_value)
160
+
161
+ else:
162
+ _LOGGER.error(
163
+ "Origin mismatch, the origin of websocket request is not the "
164
+ "same origin of redirect_uri in secrets.toml",
165
+ )
166
+
167
+ return user_info
168
+
169
+ def write_forward_msg(self, msg: ForwardMsg) -> None:
170
+ """Send a ForwardMsg to the browser."""
171
+ try:
172
+ self.write_message(serialize_forward_msg(msg), binary=True)
173
+ except tornado.websocket.WebSocketClosedError as e:
174
+ raise SessionClientDisconnectedError from e
175
+
176
+ @property
177
+ def client_context(self) -> ClientContext:
178
+ """Return the client's connection context.
179
+
180
+ The context is cached on first access to avoid repeatedly
181
+ constructing a new TornadoClientContext instance.
182
+ """
183
+ context = getattr(self, "_client_context", None)
184
+ if context is None:
185
+ context = TornadoClientContext(self.request)
186
+ self._client_context = context
187
+ return context
188
+
189
+ def select_subprotocol(self, subprotocols: list[str]) -> str | None:
190
+ """Return the first subprotocol in the given list.
191
+
192
+ This method is used by Tornado to select a protocol when the
193
+ Sec-WebSocket-Protocol header is set in an HTTP Upgrade request.
194
+
195
+ NOTE: We repurpose the Sec-WebSocket-Protocol header here in a slightly
196
+ unfortunate (but necessary) way. The browser WebSocket API doesn't allow us to
197
+ set arbitrary HTTP headers, and this header is the only one where we have the
198
+ ability to set it to arbitrary values, so we use it to pass tokens (in this
199
+ case, the previous session ID to allow us to reconnect to it) from client to
200
+ server as the *third* value in the list.
201
+
202
+ The reason why the auth token is set as the third value is that:
203
+ - when Sec-WebSocket-Protocol is set, many clients expect the server to
204
+ respond with a selected subprotocol to use. We don't want that reply to be
205
+ the session token, so we by convention have the client always set the first
206
+ protocol to "streamlit" and select that.
207
+ - the second protocol in the list is reserved in some deployment environments
208
+ for an auth token that we currently don't use
209
+ """
210
+ if subprotocols:
211
+ return subprotocols[0]
212
+
213
+ return None
214
+
215
+ def open(self, *args: Any, **kwargs: Any) -> Awaitable[None] | None:
216
+ user_info: dict[str, dict[str, str] | str | bool | None] = {}
217
+
218
+ existing_session_id = None
219
+ try:
220
+ ws_protocols = [
221
+ p.strip()
222
+ for p in self.request.headers["Sec-Websocket-Protocol"].split(",")
223
+ ]
224
+
225
+ raw_cookie_value = self.get_signed_cookie(AUTH_COOKIE_NAME)
226
+ if is_xsrf_enabled() and raw_cookie_value:
227
+ csrf_protocol_value = ws_protocols[1]
228
+
229
+ if self._validate_xsrf_token(csrf_protocol_value):
230
+ user_info.update(self._parse_user_cookie(raw_cookie_value))
231
+
232
+ # Also read in tokens if token cookie exists
233
+ raw_token_cookie_value = self.get_signed_cookie(TOKENS_COOKIE_NAME)
234
+ if raw_token_cookie_value:
235
+ all_tokens = json.loads(raw_token_cookie_value)
236
+
237
+ # Filter tokens based on expose_tokens configuration
238
+ filtered_tokens: dict[str, str] = {}
239
+ for token_type in self.expose_tokens:
240
+ token_key = f"{token_type}_token"
241
+ if token_key in all_tokens:
242
+ filtered_tokens[token_type] = all_tokens[token_key]
243
+
244
+ user_info["tokens"] = filtered_tokens
245
+
246
+ if len(ws_protocols) >= 3:
247
+ # See the NOTE in the docstring of the `select_subprotocol` method above
248
+ # for a detailed explanation of why this is done.
249
+ existing_session_id = ws_protocols[2]
250
+ except (KeyError, json.JSONDecodeError):
251
+ # Just let existing_session_id=None if we run into any error while trying to
252
+ # extract it from the Sec-Websocket-Protocol header or parsing cookie JSON.
253
+ pass
254
+
255
+ # Map in any user-configured headers. Note that these override anything coming
256
+ # from the auth cookie.
257
+ mapping_config = config.get_option("server.trustedUserHeaders")
258
+ for header_name, user_info_key in mapping_config.items():
259
+ header_values = self.request.headers.get_list(header_name)
260
+ if header_values:
261
+ # If there's at least one value, use the first value.
262
+ # NOTE: Tornado doesn't document the order of these values, so it's
263
+ # possible this won't be the first value that was received by the
264
+ # server.
265
+ user_info[user_info_key] = header_values[0]
266
+ else:
267
+ # Default to explicit None.
268
+ user_info[user_info_key] = None
269
+
270
+ self._session_id = self._runtime.connect_session(
271
+ client=self,
272
+ user_info=user_info,
273
+ existing_session_id=existing_session_id,
274
+ )
275
+ return None
276
+
277
+ def on_close(self) -> None:
278
+ if not self._session_id:
279
+ return
280
+ self._runtime.disconnect_session(self._session_id)
281
+ self._session_id = None
282
+
283
+ def get_compression_options(self) -> dict[Any, Any] | None:
284
+ """Enable WebSocket compression.
285
+
286
+ Returning an empty dict enables websocket compression. Returning
287
+ None disables it.
288
+
289
+ (See the docstring in the parent class.)
290
+ """
291
+ if config.get_option("server.enableWebsocketCompression"):
292
+ return {}
293
+ return None
294
+
295
+ def on_message(self, message: str | bytes) -> None:
296
+ if not self._session_id:
297
+ return
298
+
299
+ try:
300
+ if isinstance(message, str):
301
+ # Sanity check. (The frontend should only be sending us bytes;
302
+ # Protobuf.ParseFromString does not accept str input.)
303
+ raise TypeError( # noqa: TRY301
304
+ "WebSocket received an unexpected `str` message. "
305
+ "(We expect `bytes` only.)"
306
+ )
307
+
308
+ msg = BackMsg()
309
+ msg.ParseFromString(message)
310
+ _LOGGER.debug("Received the following back message:\n%s", msg)
311
+
312
+ except Exception as ex:
313
+ _LOGGER.exception("Error deserializing back message")
314
+ self._runtime.handle_backmsg_deserialization_exception(self._session_id, ex)
315
+ return
316
+
317
+ # "debug_disconnect_websocket" and "debug_shutdown_runtime" are special
318
+ # developmentMode-only messages used in e2e tests to test reconnect handling and
319
+ # disabling widgets.
320
+ if msg.WhichOneof("type") == "debug_disconnect_websocket":
321
+ if config.get_option("global.developmentMode") or config.get_option(
322
+ "global.e2eTest"
323
+ ):
324
+ self.close()
325
+ else:
326
+ _LOGGER.warning(
327
+ "Client tried to disconnect websocket when not in development mode or e2e testing."
328
+ )
329
+ elif msg.WhichOneof("type") == "debug_shutdown_runtime":
330
+ if config.get_option("global.developmentMode") or config.get_option(
331
+ "global.e2eTest"
332
+ ):
333
+ self._runtime.stop()
334
+ else:
335
+ _LOGGER.warning(
336
+ "Client tried to shut down runtime when not in development mode or e2e testing."
337
+ )
338
+ else:
339
+ # AppSession handles all other BackMsg types.
340
+ self._runtime.handle_backmsg(self._session_id, msg)
341
+ return
@@ -0,0 +1,105 @@
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
+
16
+ """Utilities for safely resolving component asset paths and content types.
17
+
18
+ These helpers are used by server handlers to construct safe absolute paths for
19
+ component files and to determine the appropriate ``Content-Type`` header for
20
+ responses. Path resolution prevents directory traversal by normalizing and
21
+ checking real paths against a component's root directory.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import mimetypes
27
+ import os
28
+ from typing import Final
29
+
30
+ from streamlit.path_security import is_unsafe_path_pattern
31
+
32
+ _OCTET_STREAM: Final[str] = "application/octet-stream"
33
+
34
+
35
+ def build_safe_abspath(component_root: str, relative_url_path: str) -> str | None:
36
+ r"""Build an absolute path inside ``component_root`` if safe.
37
+
38
+ The function first validates that ``relative_url_path`` does not contain
39
+ dangerous patterns using :func:`~streamlit.path_security.is_unsafe_path_pattern`,
40
+ then joins it with ``component_root`` and resolves symlinks.
41
+ Returns ``None`` if the path is rejected by security checks or escapes the root.
42
+
43
+ Parameters
44
+ ----------
45
+ component_root : str
46
+ Absolute path to the component's root directory.
47
+ relative_url_path : str
48
+ Relative URL path from the component root to the requested file.
49
+ Must be a simple relative path without dangerous patterns.
50
+
51
+ Returns
52
+ -------
53
+ str or None
54
+ The resolved absolute path if it passes all validation and stays
55
+ within ``component_root``; otherwise ``None``.
56
+ """
57
+ # See is_unsafe_path_pattern() for security details.
58
+ if is_unsafe_path_pattern(relative_url_path):
59
+ return None
60
+
61
+ root_real = os.path.realpath(component_root)
62
+ candidate = os.path.normpath(os.path.join(root_real, relative_url_path))
63
+ candidate_real = os.path.realpath(candidate)
64
+
65
+ try:
66
+ # Ensure the candidate stays within the real component root
67
+ if os.path.commonpath([root_real, candidate_real]) != root_real:
68
+ return None
69
+ except ValueError:
70
+ # On some platforms, commonpath can raise if drives differ; treat as forbidden.
71
+ return None
72
+
73
+ return candidate_real
74
+
75
+
76
+ def guess_content_type(abspath: str) -> str:
77
+ """Guess the HTTP ``Content-Type`` for a file path.
78
+
79
+ This logic mirrors Tornado's ``StaticFileHandler`` by respecting encoding
80
+ metadata from ``mimetypes.guess_type`` and falling back to
81
+ ``application/octet-stream`` when no specific type can be determined.
82
+
83
+ Parameters
84
+ ----------
85
+ abspath : str
86
+ Absolute file path used for type detection (only the suffix matters).
87
+
88
+ Returns
89
+ -------
90
+ str
91
+ Guessed content type string suitable for the ``Content-Type`` header.
92
+ """
93
+ mime_type, encoding = mimetypes.guess_type(abspath)
94
+ # per RFC 6713, use the appropriate type for a gzip compressed file
95
+ if encoding == "gzip":
96
+ return "application/gzip"
97
+ # As of 2015-07-21 there is no bzip2 encoding defined at
98
+ # http://www.iana.org/assignments/media-types/media-types.xhtml
99
+ # So for that (and any other encoding), use octet-stream.
100
+ if encoding is not None:
101
+ return _OCTET_STREAM
102
+ if mime_type is not None:
103
+ return mime_type
104
+ # if mime_type not detected, use application/octet-stream
105
+ return _OCTET_STREAM