streamlit 1.50.0__py3-none-any.whl → 1.51.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 (232) hide show
  1. streamlit/__init__.py +4 -1
  2. streamlit/commands/navigation.py +4 -6
  3. streamlit/commands/page_config.py +4 -6
  4. streamlit/components/v2/__init__.py +458 -0
  5. streamlit/components/v2/bidi_component/__init__.py +20 -0
  6. streamlit/components/v2/bidi_component/constants.py +29 -0
  7. streamlit/components/v2/bidi_component/main.py +386 -0
  8. streamlit/components/v2/bidi_component/serialization.py +265 -0
  9. streamlit/components/v2/bidi_component/state.py +92 -0
  10. streamlit/components/v2/component_definition_resolver.py +143 -0
  11. streamlit/components/v2/component_file_watcher.py +403 -0
  12. streamlit/components/v2/component_manager.py +431 -0
  13. streamlit/components/v2/component_manifest_handler.py +122 -0
  14. streamlit/components/v2/component_path_utils.py +245 -0
  15. streamlit/components/v2/component_registry.py +409 -0
  16. streamlit/components/v2/get_bidi_component_manager.py +51 -0
  17. streamlit/components/v2/manifest_scanner.py +615 -0
  18. streamlit/components/v2/presentation.py +198 -0
  19. streamlit/components/v2/types.py +324 -0
  20. streamlit/config.py +456 -53
  21. streamlit/config_option.py +4 -1
  22. streamlit/config_util.py +650 -1
  23. streamlit/dataframe_util.py +15 -8
  24. streamlit/delta_generator.py +6 -4
  25. streamlit/delta_generator_singletons.py +3 -1
  26. streamlit/deprecation_util.py +17 -6
  27. streamlit/elements/arrow.py +37 -9
  28. streamlit/elements/deck_gl_json_chart.py +97 -39
  29. streamlit/elements/dialog_decorator.py +2 -1
  30. streamlit/elements/exception.py +3 -1
  31. streamlit/elements/graphviz_chart.py +1 -3
  32. streamlit/elements/heading.py +3 -5
  33. streamlit/elements/image.py +2 -4
  34. streamlit/elements/layouts.py +31 -11
  35. streamlit/elements/lib/built_in_chart_utils.py +1 -3
  36. streamlit/elements/lib/color_util.py +8 -18
  37. streamlit/elements/lib/column_config_utils.py +4 -8
  38. streamlit/elements/lib/column_types.py +40 -12
  39. streamlit/elements/lib/dialog.py +2 -2
  40. streamlit/elements/lib/image_utils.py +3 -5
  41. streamlit/elements/lib/layout_utils.py +50 -13
  42. streamlit/elements/lib/mutable_status_container.py +2 -2
  43. streamlit/elements/lib/options_selector_utils.py +2 -2
  44. streamlit/elements/lib/utils.py +4 -4
  45. streamlit/elements/map.py +80 -37
  46. streamlit/elements/media.py +5 -7
  47. streamlit/elements/metric.py +3 -5
  48. streamlit/elements/pdf.py +2 -4
  49. streamlit/elements/plotly_chart.py +125 -17
  50. streamlit/elements/progress.py +2 -4
  51. streamlit/elements/space.py +113 -0
  52. streamlit/elements/vega_charts.py +339 -148
  53. streamlit/elements/widgets/audio_input.py +5 -5
  54. streamlit/elements/widgets/button.py +2 -4
  55. streamlit/elements/widgets/button_group.py +33 -7
  56. streamlit/elements/widgets/camera_input.py +2 -4
  57. streamlit/elements/widgets/chat.py +7 -1
  58. streamlit/elements/widgets/color_picker.py +1 -1
  59. streamlit/elements/widgets/data_editor.py +28 -24
  60. streamlit/elements/widgets/file_uploader.py +5 -10
  61. streamlit/elements/widgets/multiselect.py +4 -3
  62. streamlit/elements/widgets/number_input.py +2 -4
  63. streamlit/elements/widgets/radio.py +10 -3
  64. streamlit/elements/widgets/select_slider.py +8 -5
  65. streamlit/elements/widgets/selectbox.py +6 -3
  66. streamlit/elements/widgets/slider.py +38 -42
  67. streamlit/elements/widgets/time_widgets.py +6 -12
  68. streamlit/elements/write.py +27 -6
  69. streamlit/emojis.py +1 -1
  70. streamlit/errors.py +115 -0
  71. streamlit/hello/hello.py +8 -0
  72. streamlit/hello/utils.py +2 -1
  73. streamlit/material_icon_names.py +1 -1
  74. streamlit/navigation/page.py +4 -1
  75. streamlit/proto/ArrowData_pb2.py +27 -0
  76. streamlit/proto/ArrowData_pb2.pyi +46 -0
  77. streamlit/proto/BidiComponent_pb2.py +34 -0
  78. streamlit/proto/BidiComponent_pb2.pyi +153 -0
  79. streamlit/proto/Block_pb2.py +7 -7
  80. streamlit/proto/Block_pb2.pyi +4 -1
  81. streamlit/proto/DeckGlJsonChart_pb2.py +10 -4
  82. streamlit/proto/DeckGlJsonChart_pb2.pyi +9 -3
  83. streamlit/proto/Element_pb2.py +5 -3
  84. streamlit/proto/Element_pb2.pyi +14 -4
  85. streamlit/proto/HeightConfig_pb2.py +2 -2
  86. streamlit/proto/HeightConfig_pb2.pyi +6 -3
  87. streamlit/proto/NewSession_pb2.py +18 -18
  88. streamlit/proto/NewSession_pb2.pyi +25 -6
  89. streamlit/proto/PlotlyChart_pb2.py +8 -6
  90. streamlit/proto/PlotlyChart_pb2.pyi +3 -1
  91. streamlit/proto/Space_pb2.py +27 -0
  92. streamlit/proto/Space_pb2.pyi +42 -0
  93. streamlit/proto/WidgetStates_pb2.py +2 -2
  94. streamlit/proto/WidgetStates_pb2.pyi +13 -3
  95. streamlit/proto/WidthConfig_pb2.py +2 -2
  96. streamlit/proto/WidthConfig_pb2.pyi +6 -3
  97. streamlit/runtime/app_session.py +27 -1
  98. streamlit/runtime/caching/cache_data_api.py +4 -4
  99. streamlit/runtime/caching/cache_errors.py +4 -1
  100. streamlit/runtime/caching/cache_resource_api.py +3 -2
  101. streamlit/runtime/caching/cache_utils.py +2 -1
  102. streamlit/runtime/caching/cached_message_replay.py +3 -3
  103. streamlit/runtime/caching/hashing.py +3 -4
  104. streamlit/runtime/caching/legacy_cache_api.py +2 -1
  105. streamlit/runtime/connection_factory.py +1 -3
  106. streamlit/runtime/forward_msg_queue.py +4 -1
  107. streamlit/runtime/fragment.py +2 -1
  108. streamlit/runtime/memory_media_file_storage.py +1 -1
  109. streamlit/runtime/metrics_util.py +6 -2
  110. streamlit/runtime/runtime.py +14 -0
  111. streamlit/runtime/scriptrunner/exec_code.py +2 -1
  112. streamlit/runtime/scriptrunner/script_runner.py +2 -2
  113. streamlit/runtime/scriptrunner_utils/script_run_context.py +3 -6
  114. streamlit/runtime/secrets.py +2 -4
  115. streamlit/runtime/session_manager.py +3 -1
  116. streamlit/runtime/state/common.py +30 -5
  117. streamlit/runtime/state/presentation.py +85 -0
  118. streamlit/runtime/state/safe_session_state.py +2 -2
  119. streamlit/runtime/state/session_state.py +220 -16
  120. streamlit/runtime/state/widgets.py +19 -3
  121. streamlit/runtime/websocket_session_manager.py +3 -1
  122. streamlit/source_util.py +2 -2
  123. streamlit/static/index.html +2 -2
  124. streamlit/static/manifest.json +243 -226
  125. streamlit/static/static/css/{index.CIiu7Ygf.css → index.BpABIXK9.css} +1 -1
  126. streamlit/static/static/css/index.DgR7E2CV.css +1 -0
  127. streamlit/static/static/js/{ErrorOutline.esm.DUpR0_Ka.js → ErrorOutline.esm.YoJdlW1p.js} +1 -1
  128. streamlit/static/static/js/{FileDownload.esm.CN4j9-1w.js → FileDownload.esm.Ddx8VEYy.js} +1 -1
  129. streamlit/static/static/js/{FileHelper.CaIUKG91.js → FileHelper.90EtOmj9.js} +1 -1
  130. streamlit/static/static/js/{FormClearHelper.DTcdrasw.js → FormClearHelper.BB1Km6eP.js} +1 -1
  131. streamlit/static/static/js/InputInstructions.jhH15PqV.js +1 -0
  132. streamlit/static/static/js/{Particles.CElH0XX2.js → Particles.DUsputn1.js} +1 -1
  133. streamlit/static/static/js/{ProgressBar.DetlP5aY.js → ProgressBar.DLY8H6nE.js} +1 -1
  134. streamlit/static/static/js/{Toolbar.C77ar7rq.js → Toolbar.D8nHCkuz.js} +1 -1
  135. streamlit/static/static/js/{base-input.BQft14La.js → base-input.CJGiNqed.js} +3 -3
  136. streamlit/static/static/js/{checkbox.yZOfXCeX.js → checkbox.Cpdd482O.js} +1 -1
  137. streamlit/static/static/js/{createSuper.Dh9w1cs8.js → createSuper.CuQIogbW.js} +1 -1
  138. streamlit/static/static/js/{data-grid-overlay-editor.DcuHuCyW.js → data-grid-overlay-editor.2Ufgxc6y.js} +1 -1
  139. streamlit/static/static/js/{downloader.MeHtkq8r.js → downloader.CN0K7xlu.js} +1 -1
  140. streamlit/static/static/js/{es6.VpBPGCnM.js → es6.BJcsVXQ0.js} +2 -2
  141. streamlit/static/static/js/{iframeResizer.contentWindow.yMw_ARIL.js → iframeResizer.contentWindow.XzUvQqcZ.js} +1 -1
  142. streamlit/static/static/js/index.B1ZQh4P1.js +1 -0
  143. streamlit/static/static/js/index.BKstZk0M.js +27 -0
  144. streamlit/static/static/js/{index.Cnpi3o3E.js → index.BMcFsUee.js} +1 -1
  145. streamlit/static/static/js/{index.DKv_lNO7.js → index.BR-IdcTb.js} +1 -1
  146. streamlit/static/static/js/{index.FFOzOWzC.js → index.B_dWA3vd.js} +1 -1
  147. streamlit/static/static/js/{index.Bj9JgOEC.js → index.BgnZEMVh.js} +1 -1
  148. streamlit/static/static/js/{index.Bxz2yX3P.js → index.BohqXifI.js} +1 -1
  149. streamlit/static/static/js/{index.Dbe-Q3C-.js → index.Br5nxKNj.js} +1 -1
  150. streamlit/static/static/js/{index.BjCwMzj4.js → index.BrIKVbNc.js} +2 -2
  151. streamlit/static/static/js/index.BtWUPzle.js +1 -0
  152. streamlit/static/static/js/{index.CGYqqs6j.js → index.C0RLraek.js} +1 -1
  153. streamlit/static/static/js/{index.D2QEXQq_.js → index.CAIjskgG.js} +1 -1
  154. streamlit/static/static/js/{index.6xX1278W.js → index.CAj-7vWz.js} +131 -157
  155. streamlit/static/static/js/{index.DK7hD7_w.js → index.CMtEit2O.js} +1 -1
  156. streamlit/static/static/js/{index.DNLrMXgm.js → index.CkRlykEE.js} +1 -1
  157. streamlit/static/static/js/{index.ClELlchS.js → index.CmN3FXfI.js} +1 -1
  158. streamlit/static/static/js/{index.GRUzrudl.js → index.CwbFI1_-.js} +1 -1
  159. streamlit/static/static/js/{index.Ctn27_AE.js → index.CxIUUfab.js} +27 -27
  160. streamlit/static/static/js/index.D2KPNy7e.js +1 -0
  161. streamlit/static/static/js/{index.B0H9IXUJ.js → index.D3GPA5k4.js} +3 -3
  162. streamlit/static/static/js/{index.BycLveZ4.js → index.DGAh7DMq.js} +1 -1
  163. streamlit/static/static/js/index.DKb_NvmG.js +197 -0
  164. streamlit/static/static/js/{index.BPQo7BKk.js → index.DMqgUYKq.js} +1 -1
  165. streamlit/static/static/js/{index.CH1tqnSs.js → index.DOFlg3dS.js} +1 -1
  166. streamlit/static/static/js/{index.64ejlaaT.js → index.DPUXkcQL.js} +1 -1
  167. streamlit/static/static/js/{index.B-hiXRzw.js → index.DX1xY89g.js} +1 -1
  168. streamlit/static/static/js/index.DYATBCsq.js +2 -0
  169. streamlit/static/static/js/{index.DHh-U0dK.js → index.DaSmGJ76.js} +3 -3
  170. streamlit/static/static/js/{index.DuxqVQpd.js → index.Dd7bMeLP.js} +1 -1
  171. streamlit/static/static/js/{index.B4cAbHP6.js → index.DjmmgI5U.js} +1 -1
  172. streamlit/static/static/js/{index.DcPNYEUo.js → index.Dq56CyM2.js} +1 -1
  173. streamlit/static/static/js/{index.CiAQIz1H.js → index.DuiXaS5_.js} +1 -1
  174. streamlit/static/static/js/index.DvFidMLe.js +2 -0
  175. streamlit/static/static/js/{index.C9BdUqTi.js → index.DwkhC5Pc.js} +1 -1
  176. streamlit/static/static/js/{index.B4dUQfni.js → index.Q-3sFn1v.js} +1 -1
  177. streamlit/static/static/js/{index.CMItVsFA.js → index.QJ5QO9sJ.js} +1 -1
  178. streamlit/static/static/js/{index.CTBk8Vk2.js → index.VwTaeety.js} +1 -1
  179. streamlit/static/static/js/{index.Ck8rQ9OL.js → index.YOqQbeX8.js} +1 -1
  180. streamlit/static/static/js/{input.s6pjQ49A.js → input.D4MN_FzN.js} +1 -1
  181. streamlit/static/static/js/{memory.Cuvsdfrl.js → memory.DrZjtdGT.js} +1 -1
  182. streamlit/static/static/js/{number-overlay-editor.DdgVR5m3.js → number-overlay-editor.DRwAw1In.js} +1 -1
  183. streamlit/static/static/js/{possibleConstructorReturn.CqidKeei.js → possibleConstructorReturn.exeeJQEP.js} +1 -1
  184. streamlit/static/static/js/record.B-tDciZb.js +1 -0
  185. streamlit/static/static/js/{sandbox.CCQREcJx.js → sandbox.ClO3IuUr.js} +1 -1
  186. streamlit/static/static/js/{timepicker.mkJF97Bb.js → timepicker.DAhu-vcF.js} +1 -1
  187. streamlit/static/static/js/{toConsumableArray.De7I7KVR.js → toConsumableArray.DNbljYEC.js} +1 -1
  188. streamlit/static/static/js/{uniqueId.RI1LJdtz.js → uniqueId.oG4Gvj1v.js} +1 -1
  189. streamlit/static/static/js/{useBasicWidgetState.CedkNjUW.js → useBasicWidgetState.D6sOH6oI.js} +1 -1
  190. streamlit/static/static/js/{useTextInputAutoExpand.Ca7w8dVs.js → useTextInputAutoExpand.4u3_GcuN.js} +1 -1
  191. streamlit/static/static/js/{useUpdateUiValue.DeXelfRH.js → useUpdateUiValue.F2R3eTeR.js} +1 -1
  192. streamlit/static/static/js/wavesurfer.esm.vI8Eid4k.js +73 -0
  193. streamlit/static/static/js/{withFullScreenWrapper.C3561XxJ.js → withFullScreenWrapper.zothJIsI.js} +1 -1
  194. streamlit/static/static/media/MaterialSymbols-Rounded.C7IFxh57.woff2 +0 -0
  195. streamlit/string_util.py +1 -3
  196. streamlit/testing/v1/app_test.py +2 -2
  197. streamlit/testing/v1/element_tree.py +23 -9
  198. streamlit/testing/v1/util.py +2 -2
  199. streamlit/type_util.py +3 -4
  200. streamlit/url_util.py +1 -3
  201. streamlit/user_info.py +1 -2
  202. streamlit/util.py +3 -1
  203. streamlit/watcher/event_based_path_watcher.py +23 -12
  204. streamlit/watcher/local_sources_watcher.py +11 -1
  205. streamlit/watcher/path_watcher.py +9 -6
  206. streamlit/watcher/polling_path_watcher.py +4 -1
  207. streamlit/watcher/util.py +2 -2
  208. streamlit/web/cli.py +51 -22
  209. streamlit/web/server/bidi_component_request_handler.py +193 -0
  210. streamlit/web/server/component_file_utils.py +97 -0
  211. streamlit/web/server/component_request_handler.py +8 -21
  212. streamlit/web/server/oidc_mixin.py +3 -1
  213. streamlit/web/server/routes.py +2 -2
  214. streamlit/web/server/server.py +9 -0
  215. streamlit/web/server/server_util.py +3 -1
  216. streamlit/web/server/upload_file_request_handler.py +3 -1
  217. {streamlit-1.50.0.dist-info → streamlit-1.51.0.dist-info}/METADATA +4 -5
  218. {streamlit-1.50.0.dist-info → streamlit-1.51.0.dist-info}/RECORD +222 -194
  219. streamlit/static/static/css/index.CHEnSPGk.css +0 -1
  220. streamlit/static/static/js/Hooks.BRba_Own.js +0 -1
  221. streamlit/static/static/js/InputInstructions.xnSDuYeQ.js +0 -1
  222. streamlit/static/static/js/index.Baqa90pe.js +0 -2
  223. streamlit/static/static/js/index.Bm3VbPB5.js +0 -1
  224. streamlit/static/static/js/index.CFMf5_ez.js +0 -197
  225. streamlit/static/static/js/index.Cj7DSzVR.js +0 -73
  226. streamlit/static/static/js/index.DH71Ezyj.js +0 -1
  227. streamlit/static/static/js/index.DW0Grddz.js +0 -1
  228. streamlit/static/static/media/MaterialSymbols-Rounded.DeCZgS-4.woff2 +0 -0
  229. {streamlit-1.50.0.data → streamlit-1.51.0.data}/scripts/streamlit.cmd +0 -0
  230. {streamlit-1.50.0.dist-info → streamlit-1.51.0.dist-info}/WHEEL +0 -0
  231. {streamlit-1.50.0.dist-info → streamlit-1.51.0.dist-info}/entry_points.txt +0 -0
  232. {streamlit-1.50.0.dist-info → streamlit-1.51.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,193 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """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, ``403`` for
81
+ forbidden 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("forbidden")
109
+ self.set_status(403)
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,97 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
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
+ _OCTET_STREAM: Final[str] = "application/octet-stream"
31
+
32
+
33
+ def build_safe_abspath(component_root: str, relative_url_path: str) -> str | None:
34
+ """Build an absolute path inside ``component_root`` if safe.
35
+
36
+ The function joins ``relative_url_path`` with ``component_root`` and
37
+ normalizes and resolves symlinks. If the resulting path escapes the
38
+ component root, ``None`` is returned to indicate a forbidden traversal.
39
+
40
+ Parameters
41
+ ----------
42
+ component_root : str
43
+ Absolute path to the component's root directory.
44
+ relative_url_path : str
45
+ Relative URL path from the component root to the requested file.
46
+
47
+ Returns
48
+ -------
49
+ str or None
50
+ The resolved absolute path if it stays within ``component_root``;
51
+ otherwise ``None`` when the path would traverse outside the root.
52
+ """
53
+ root_real = os.path.realpath(component_root)
54
+ candidate = os.path.normpath(os.path.join(root_real, relative_url_path))
55
+ candidate_real = os.path.realpath(candidate)
56
+
57
+ try:
58
+ # Ensure the candidate stays within the real component root
59
+ if os.path.commonpath([root_real, candidate_real]) != root_real:
60
+ return None
61
+ except ValueError:
62
+ # On some platforms, commonpath can raise if drives differ; treat as forbidden.
63
+ return None
64
+
65
+ return candidate_real
66
+
67
+
68
+ def guess_content_type(abspath: str) -> str:
69
+ """Guess the HTTP ``Content-Type`` for a file path.
70
+
71
+ This logic mirrors Tornado's ``StaticFileHandler`` by respecting encoding
72
+ metadata from ``mimetypes.guess_type`` and falling back to
73
+ ``application/octet-stream`` when no specific type can be determined.
74
+
75
+ Parameters
76
+ ----------
77
+ abspath : str
78
+ Absolute file path used for type detection (only the suffix matters).
79
+
80
+ Returns
81
+ -------
82
+ str
83
+ Guessed content type string suitable for the ``Content-Type`` header.
84
+ """
85
+ mime_type, encoding = mimetypes.guess_type(abspath)
86
+ # per RFC 6713, use the appropriate type for a gzip compressed file
87
+ if encoding == "gzip":
88
+ return "application/gzip"
89
+ # As of 2015-07-21 there is no bzip2 encoding defined at
90
+ # http://www.iana.org/assignments/media-types/media-types.xhtml
91
+ # So for that (and any other encoding), use octet-stream.
92
+ if encoding is not None:
93
+ return _OCTET_STREAM
94
+ if mime_type is not None:
95
+ return mime_type
96
+ # if mime_type not detected, use application/octet-stream
97
+ return _OCTET_STREAM
@@ -14,14 +14,16 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- import mimetypes
18
- import os
19
17
  from typing import TYPE_CHECKING, Final, cast
20
18
 
21
19
  import tornado.web
22
20
 
23
21
  import streamlit.web.server.routes
24
22
  from streamlit.logger import get_logger
23
+ from streamlit.web.server.component_file_utils import (
24
+ build_safe_abspath,
25
+ guess_content_type,
26
+ )
25
27
 
26
28
  if TYPE_CHECKING:
27
29
  from streamlit.components.types.base_component_registry import BaseComponentRegistry
@@ -42,13 +44,10 @@ class ComponentRequestHandler(tornado.web.RequestHandler):
42
44
  self.set_status(404)
43
45
  return
44
46
 
45
- # follow symlinks to get an accurate normalized path
46
- component_root = os.path.realpath(component_root)
47
+ # Build a safe absolute path within the component root
47
48
  filename = "/".join(parts[1:])
48
- abspath = os.path.normpath(os.path.join(component_root, filename))
49
-
50
- # Do NOT expose anything outside of the component root.
51
- if os.path.commonpath([component_root, abspath]) != component_root:
49
+ abspath = build_safe_abspath(component_root, filename)
50
+ if abspath is None:
52
51
  self.write("forbidden")
53
52
  self.set_status(403)
54
53
  return
@@ -100,19 +99,7 @@ class ComponentRequestHandler(tornado.web.RequestHandler):
100
99
  """Returns the ``Content-Type`` header to be used for this request.
101
100
  From tornado.web.StaticFileHandler.
102
101
  """
103
- mime_type, encoding = mimetypes.guess_type(abspath)
104
- # per RFC 6713, use the appropriate type for a gzip compressed file
105
- if encoding == "gzip":
106
- return "application/gzip"
107
- # As of 2015-07-21 there is no bzip2 encoding defined at
108
- # http://www.iana.org/assignments/media-types/media-types.xhtml
109
- # So for that (and any other encoding), use octet-stream.
110
- if encoding is not None:
111
- return "application/octet-stream"
112
- if mime_type is not None:
113
- return mime_type
114
- # if mime_type not detected, use application/octet-stream
115
- return "application/octet-stream"
102
+ return guess_content_type(abspath)
116
103
 
117
104
  @staticmethod
118
105
  def get_url(file_id: str) -> str:
@@ -16,7 +16,7 @@
16
16
 
17
17
  from __future__ import annotations
18
18
 
19
- from typing import TYPE_CHECKING, Any, Callable, cast
19
+ from typing import TYPE_CHECKING, Any, cast
20
20
 
21
21
  from authlib.integrations.base_client import (
22
22
  BaseApp,
@@ -32,6 +32,8 @@ from authlib.integrations.requests_client import (
32
32
  from streamlit.web.server.authlib_tornado_integration import TornadoIntegration
33
33
 
34
34
  if TYPE_CHECKING:
35
+ from collections.abc import Callable
36
+
35
37
  import tornado.web
36
38
 
37
39
  from streamlit.auth_util import AuthCache
@@ -16,7 +16,7 @@ from __future__ import annotations
16
16
 
17
17
  import os
18
18
  import re
19
- from typing import TYPE_CHECKING, Any, Callable, Final, cast
19
+ from typing import TYPE_CHECKING, Any, Final, cast
20
20
 
21
21
  import tornado.web
22
22
 
@@ -28,7 +28,7 @@ from streamlit.web.server.server_util import (
28
28
  )
29
29
 
30
30
  if TYPE_CHECKING:
31
- from collections.abc import Awaitable, Sequence
31
+ from collections.abc import Awaitable, Callable, Sequence
32
32
 
33
33
 
34
34
  # Files that match this pattern do not get cached.
@@ -38,6 +38,9 @@ from streamlit.web.cache_storage_manager_config import (
38
38
  create_default_cache_storage_manager,
39
39
  )
40
40
  from streamlit.web.server.app_static_file_handler import AppStaticFileHandler
41
+ from streamlit.web.server.bidi_component_request_handler import (
42
+ BidiComponentRequestHandler,
43
+ )
41
44
  from streamlit.web.server.browser_websocket_handler import BrowserWebSocketHandler
42
45
  from streamlit.web.server.component_request_handler import ComponentRequestHandler
43
46
  from streamlit.web.server.media_file_handler import MediaFileHandler
@@ -133,6 +136,7 @@ UNIX_SOCKET_PREFIX: Final = "unix://"
133
136
  # as the endpoints in frontend/connection/src/DefaultStreamlitEndpoints
134
137
  MEDIA_ENDPOINT: Final = "/media"
135
138
  COMPONENT_ENDPOINT: Final = "/component"
139
+ BIDI_COMPONENT_ENDPOINT: Final = "/_stcore/bidi-components"
136
140
  STATIC_SERVING_ENDPOINT: Final = "/app/static"
137
141
  UPLOAD_FILE_ENDPOINT: Final = "/_stcore/upload_file"
138
142
  STREAM_ENDPOINT: Final = r"_stcore/stream"
@@ -395,6 +399,11 @@ class Server:
395
399
  ComponentRequestHandler,
396
400
  {"registry": self._runtime.component_registry},
397
401
  ),
402
+ (
403
+ make_url_path_regex(base, f"{BIDI_COMPONENT_ENDPOINT}/(.*)"),
404
+ BidiComponentRequestHandler,
405
+ {"component_manager": self._runtime.bidi_component_registry},
406
+ ),
398
407
  ]
399
408
 
400
409
  if config.get_option("server.scriptHealthCheckEnabled"):
@@ -16,7 +16,7 @@
16
16
 
17
17
  from __future__ import annotations
18
18
 
19
- from typing import TYPE_CHECKING, Callable, Final, Literal, cast
19
+ from typing import TYPE_CHECKING, Final, Literal, cast
20
20
  from urllib.parse import urljoin
21
21
 
22
22
  from streamlit import config, net_util, url_util
@@ -24,6 +24,8 @@ from streamlit.runtime.secrets import secrets_singleton
24
24
  from streamlit.type_util import is_version_less_than
25
25
 
26
26
  if TYPE_CHECKING:
27
+ from collections.abc import Callable
28
+
27
29
  from tornado.web import RequestHandler
28
30
 
29
31
  # The port used for internal development.
@@ -14,7 +14,7 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- from typing import TYPE_CHECKING, Any, Callable, cast
17
+ from typing import TYPE_CHECKING, Any, cast
18
18
 
19
19
  import tornado.httputil
20
20
  import tornado.web
@@ -25,6 +25,8 @@ from streamlit.web.server import routes, server_util
25
25
  from streamlit.web.server.server_util import is_xsrf_enabled
26
26
 
27
27
  if TYPE_CHECKING:
28
+ from collections.abc import Callable
29
+
28
30
  from streamlit.runtime.memory_uploaded_file_manager import MemoryUploadedFileManager
29
31
 
30
32
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: streamlit
3
- Version: 1.50.0
3
+ Version: 1.51.0
4
4
  Summary: A faster way to build and share data apps
5
5
  Home-page: https://streamlit.io
6
6
  Author: Snowflake Inc
@@ -18,7 +18,6 @@ Classifier: Environment :: Web Environment
18
18
  Classifier: Intended Audience :: Developers
19
19
  Classifier: Intended Audience :: Science/Research
20
20
  Classifier: License :: OSI Approved :: Apache Software License
21
- Classifier: Programming Language :: Python :: 3.9
22
21
  Classifier: Programming Language :: Python :: 3.10
23
22
  Classifier: Programming Language :: Python :: 3.11
24
23
  Classifier: Programming Language :: Python :: 3.12
@@ -29,7 +28,7 @@ Classifier: Topic :: Scientific/Engineering :: Information Analysis
29
28
  Classifier: Topic :: Scientific/Engineering :: Visualization
30
29
  Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
31
30
  Classifier: Topic :: Software Development :: Widget Sets
32
- Requires-Python: >=3.9, !=3.9.7
31
+ Requires-Python: >=3.10
33
32
  Description-Content-Type: text/markdown
34
33
  Requires-Dist: altair!=5.4.0,!=5.4.1,<6,>=4.0
35
34
  Requires-Dist: blinker<2,>=1.5.0
@@ -38,9 +37,9 @@ Requires-Dist: click<9,>=7.0
38
37
  Requires-Dist: numpy<3,>=1.23
39
38
  Requires-Dist: packaging<26,>=20
40
39
  Requires-Dist: pandas<3,>=1.4.0
41
- Requires-Dist: pillow<12,>=7.1.0
40
+ Requires-Dist: pillow<13,>=7.1.0
42
41
  Requires-Dist: protobuf<7,>=3.20
43
- Requires-Dist: pyarrow>=7.0
42
+ Requires-Dist: pyarrow<22,>=7.0
44
43
  Requires-Dist: requests<3,>=2.27
45
44
  Requires-Dist: tenacity<10,>=8.1.0
46
45
  Requires-Dist: toml<2,>=0.10.1