streamlit 1.48.1__py3-none-any.whl → 1.49.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 (203) hide show
  1. streamlit/__init__.py +3 -10
  2. streamlit/commands/logo.py +4 -3
  3. streamlit/commands/navigation.py +1 -1
  4. streamlit/commands/page_config.py +4 -1
  5. streamlit/components/v1/custom_component.py +2 -2
  6. streamlit/config.py +82 -1
  7. streamlit/connections/snowflake_connection.py +3 -1
  8. streamlit/delta_generator.py +3 -0
  9. streamlit/elements/arrow.py +155 -70
  10. streamlit/elements/bokeh_chart.py +13 -3
  11. streamlit/elements/deck_gl_json_chart.py +0 -1
  12. streamlit/elements/dialog_decorator.py +7 -59
  13. streamlit/elements/form.py +10 -1
  14. streamlit/elements/graphviz_chart.py +57 -6
  15. streamlit/elements/heading.py +17 -16
  16. streamlit/elements/image.py +64 -37
  17. streamlit/elements/layouts.py +2 -2
  18. streamlit/elements/lib/built_in_chart_utils.py +2 -5
  19. streamlit/elements/lib/column_config_utils.py +18 -4
  20. streamlit/elements/lib/column_types.py +75 -30
  21. streamlit/elements/lib/dialog.py +3 -3
  22. streamlit/elements/lib/image_utils.py +19 -11
  23. streamlit/elements/lib/layout_utils.py +15 -2
  24. streamlit/elements/lib/utils.py +20 -41
  25. streamlit/elements/markdown.py +7 -6
  26. streamlit/elements/media.py +6 -13
  27. streamlit/elements/metric.py +78 -1
  28. streamlit/elements/pdf.py +192 -0
  29. streamlit/elements/plotly_chart.py +3 -2
  30. streamlit/elements/pyplot.py +53 -11
  31. streamlit/elements/toast.py +81 -5
  32. streamlit/elements/vega_charts.py +3 -8
  33. streamlit/elements/widgets/audio_input.py +0 -1
  34. streamlit/elements/widgets/button.py +0 -4
  35. streamlit/elements/widgets/button_group.py +5 -4
  36. streamlit/elements/widgets/camera_input.py +0 -1
  37. streamlit/elements/widgets/chat.py +11 -13
  38. streamlit/elements/widgets/checkbox.py +0 -1
  39. streamlit/elements/widgets/color_picker.py +0 -1
  40. streamlit/elements/widgets/data_editor.py +142 -62
  41. streamlit/elements/widgets/file_uploader.py +74 -37
  42. streamlit/elements/widgets/multiselect.py +0 -1
  43. streamlit/elements/widgets/number_input.py +0 -1
  44. streamlit/elements/widgets/radio.py +0 -1
  45. streamlit/elements/widgets/select_slider.py +0 -1
  46. streamlit/elements/widgets/selectbox.py +0 -1
  47. streamlit/elements/widgets/slider.py +0 -1
  48. streamlit/elements/widgets/text_widgets.py +0 -2
  49. streamlit/elements/widgets/time_widgets.py +0 -2
  50. streamlit/errors.py +11 -0
  51. streamlit/material_icon_names.py +1 -1
  52. streamlit/proto/Arrow_pb2.py +14 -8
  53. streamlit/proto/Arrow_pb2.pyi +11 -3
  54. streamlit/proto/Block_pb2.py +16 -16
  55. streamlit/proto/Block_pb2.pyi +2 -0
  56. streamlit/proto/ChatInput_pb2.py +3 -3
  57. streamlit/proto/ChatInput_pb2.pyi +2 -0
  58. streamlit/proto/FileUploader_pb2.py +2 -2
  59. streamlit/proto/FileUploader_pb2.pyi +5 -1
  60. streamlit/proto/GraphVizChart_pb2.py +4 -2
  61. streamlit/proto/GraphVizChart_pb2.pyi +1 -1
  62. streamlit/proto/Image_pb2.py +4 -2
  63. streamlit/proto/Image_pb2.pyi +1 -10
  64. streamlit/proto/Metric_pb2.py +8 -6
  65. streamlit/proto/Metric_pb2.pyi +34 -10
  66. streamlit/proto/Toast_pb2.py +2 -2
  67. streamlit/proto/Toast_pb2.pyi +10 -1
  68. streamlit/runtime/caching/__init__.py +14 -2
  69. streamlit/runtime/caching/cache_data_api.py +0 -17
  70. streamlit/runtime/caching/cache_resource_api.py +0 -16
  71. streamlit/runtime/caching/cached_message_replay.py +8 -20
  72. streamlit/runtime/caching/hashing.py +31 -1
  73. streamlit/runtime/credentials.py +4 -4
  74. streamlit/runtime/fragment.py +0 -42
  75. streamlit/runtime/websocket_session_manager.py +1 -1
  76. streamlit/static/index.html +2 -2
  77. streamlit/static/manifest.json +224 -252
  78. streamlit/static/static/css/{index.CJVRHjQZ.css → index.C8X8rNzw.css} +1 -1
  79. streamlit/static/static/css/index.COe1010n.css +1 -0
  80. streamlit/static/static/js/{ErrorOutline.esm.DjObtx4K.js → ErrorOutline.esm.u9XvzxL8.js} +1 -1
  81. streamlit/static/static/js/{FileDownload.esm.Bz9nxNC5.js → FileDownload.esm.CaRyZ-b2.js} +1 -1
  82. streamlit/static/static/js/FileHelper.Dk2SwIi3.js +5 -0
  83. streamlit/static/static/js/FormClearHelper.l_UPPvkg.js +1 -0
  84. streamlit/static/static/js/{Hooks.DEoLCfOE.js → Hooks.BxrVEftw.js} +1 -1
  85. streamlit/static/static/js/InputInstructions.C254RU9X.js +1 -0
  86. streamlit/static/static/js/Particles.DkY6FDnc.js +1 -0
  87. streamlit/static/static/js/ProgressBar.BPtSM82n.js +2 -0
  88. streamlit/static/static/js/Toolbar.BO_3WBaS.js +1 -0
  89. streamlit/static/static/js/{base-input.BmvSaPd2.js → base-input.egUI4LjJ.js} +4 -4
  90. streamlit/static/static/js/{checkbox.Cgxgc0et.js → checkbox.ButpszcE.js} +2 -2
  91. streamlit/static/static/js/createSuper.DYJA5xa6.js +1 -0
  92. streamlit/static/static/js/data-grid-overlay-editor.C9gQLEnU.js +1 -0
  93. streamlit/static/static/js/{downloader.M6jQeNDf.js → downloader.B3TjsSPZ.js} +1 -1
  94. streamlit/static/static/js/es6.BYSNuG4D.js +2 -0
  95. streamlit/static/static/js/iframeResizer.contentWindow.CNPHJsF2.js +1 -0
  96. streamlit/static/static/js/index.0tDq1WXk.js +1 -0
  97. streamlit/static/static/js/index.BBnWuh07.js +976 -0
  98. streamlit/static/static/js/index.BDZorv41.js +1 -0
  99. streamlit/static/static/js/{index.CbdWnLqS.js → index.BH79B25f.js} +3 -3
  100. streamlit/static/static/js/index.BeTC4Yl-.js +197 -0
  101. streamlit/static/static/js/index.BnOd05Ko.js +2 -0
  102. streamlit/static/static/js/index.BoJaJReB.js +1 -0
  103. streamlit/static/static/js/index.Bp1Of6L8.js +1 -0
  104. streamlit/static/static/js/index.Bpe4-O2W.js +1 -0
  105. streamlit/static/static/js/index.BrD9sbpx.js +1 -0
  106. streamlit/static/static/js/index.C1qCS-sd.js +1 -0
  107. streamlit/static/static/js/index.C3EXAI-u.js +1 -0
  108. streamlit/static/static/js/index.C77g9sAQ.js +3 -0
  109. streamlit/static/static/js/{index.BXDq9dj4.js → index.CFePF7s4.js} +1 -1
  110. streamlit/static/static/js/index.CFjU0x00.js +1 -0
  111. streamlit/static/static/js/index.Ca3y4ztK.js +1 -0
  112. streamlit/static/static/js/{index.CgZDfhN4.js → index.Cb9gN2T2.js} +2 -2
  113. streamlit/static/static/js/{index.tsvTLdio.js → index.CbwuUwu4.js} +9 -9
  114. streamlit/static/static/js/index.CeXLlclc.js +1 -0
  115. streamlit/static/static/js/index.CfiZGqj3.js +3 -0
  116. streamlit/static/static/js/index.CjQnYKID.js +1 -0
  117. streamlit/static/static/js/index.Cl_966eE.js +3858 -0
  118. streamlit/static/static/js/{index.Cqa4gqqN.js → index.CqSRo6zQ.js} +1 -1
  119. streamlit/static/static/js/index.CuEFSQ-o.js +1 -0
  120. streamlit/static/static/js/index.D4jR1m1z.js +1 -0
  121. streamlit/static/static/js/index.DGcW849X.js +1 -0
  122. streamlit/static/static/js/index.DKb-BAE2.js +1 -0
  123. streamlit/static/static/js/index.DP1rDFP0.js +1 -0
  124. streamlit/static/static/js/{index.D1EayrNh.js → index.DStzYLqM.js} +2 -2
  125. streamlit/static/static/js/index.DVKQKDLu.js +1 -0
  126. streamlit/static/static/js/index.DWedOrkQ.js +1 -0
  127. streamlit/static/static/js/index.DYbRPmVF.js +1 -0
  128. streamlit/static/static/js/index.DgpIMUsr.js +1 -0
  129. streamlit/static/static/js/index.DtwkPJs5.js +5367 -0
  130. streamlit/static/static/js/{index.D1jHqUJq.js → index.MQLQLR5Z.js} +1 -1
  131. streamlit/static/static/js/index.uInpwWAP.js +1 -0
  132. streamlit/static/static/js/index.z992t-BQ.js +7 -0
  133. streamlit/static/static/js/{input.DZd6EQlV.js → input.CbP5ZuQ7.js} +2 -2
  134. streamlit/static/static/js/{memory.ptkfuI71.js → memory.BuacVo2L.js} +1 -1
  135. streamlit/static/static/js/number-overlay-editor.BZb9zRl_.js +9 -0
  136. streamlit/static/static/js/{possibleConstructorReturn.Bd4ImlQ9.js → possibleConstructorReturn.DSM84rOS.js} +1 -1
  137. streamlit/static/static/js/{sandbox.DsH8LuID.js → sandbox.C480llMG.js} +1 -1
  138. streamlit/static/static/js/{timepicker.QVekV78C.js → timepicker.BunxCVp7.js} +4 -4
  139. streamlit/static/static/js/{toConsumableArray.BJvaP8gb.js → toConsumableArray.B4o8rEx1.js} +3 -3
  140. streamlit/static/static/js/uniqueId.tii0yosY.js +1 -0
  141. streamlit/static/static/js/{useBasicWidgetState.DB3vMS9V.js → useBasicWidgetState.Bnm4FD6K.js} +1 -1
  142. streamlit/static/static/js/{useTextInputAutoExpand.CBkGkaRt.js → useTextInputAutoExpand.Dgtwc1m0.js} +2 -2
  143. streamlit/static/static/js/useUpdateUiValue.DjXdMFGw.js +1 -0
  144. streamlit/static/static/js/withFullScreenWrapper.0cy2pVf5.js +1 -0
  145. streamlit/static/static/media/MaterialSymbols-Rounded.CBxVaFdk.woff2 +0 -0
  146. streamlit/user_info.py +3 -1
  147. streamlit/web/server/browser_websocket_handler.py +15 -0
  148. {streamlit-1.48.1.dist-info → streamlit-1.49.0.dist-info}/METADATA +4 -2
  149. {streamlit-1.48.1.dist-info → streamlit-1.49.0.dist-info}/RECORD +153 -156
  150. streamlit/static/static/css/index.CQt5TjGB.css +0 -1
  151. streamlit/static/static/js/FileHelper.BrQvUXVD.js +0 -5
  152. streamlit/static/static/js/FormClearHelper.DF4gFAOO.js +0 -1
  153. streamlit/static/static/js/InputInstructions.D8zoMog9.js +0 -1
  154. streamlit/static/static/js/Particles.CCFySwdL.js +0 -1
  155. streamlit/static/static/js/ProgressBar.COK9j1l0.js +0 -2
  156. streamlit/static/static/js/Toolbar.Dt4jIKlY.js +0 -1
  157. streamlit/static/static/js/createSuper.siQeagI2.js +0 -1
  158. streamlit/static/static/js/data-grid-overlay-editor.Ct51iCb_.js +0 -1
  159. streamlit/static/static/js/es6.CMaUdEZ5.js +0 -2
  160. streamlit/static/static/js/iframeResizer.contentWindow.C33BryyP.js +0 -1
  161. streamlit/static/static/js/index.8GJD0eeD.js +0 -1
  162. streamlit/static/static/js/index.8QEYHMQD.js +0 -1
  163. streamlit/static/static/js/index.Ay41Wnu9.js +0 -1
  164. streamlit/static/static/js/index.BLiKiJ7_.js +0 -1
  165. streamlit/static/static/js/index.BT78cJmU.js +0 -1
  166. streamlit/static/static/js/index.BdGvnhlM.js +0 -1
  167. streamlit/static/static/js/index.BfasrT0d.js +0 -1
  168. streamlit/static/static/js/index.CCdtFMFG.js +0 -1
  169. streamlit/static/static/js/index.CFRGZDz1.js +0 -1
  170. streamlit/static/static/js/index.CFSFYiPA.js +0 -5366
  171. streamlit/static/static/js/index.CeiIiXap.js +0 -1
  172. streamlit/static/static/js/index.CzX2xpyc.js +0 -1
  173. streamlit/static/static/js/index.D1ErX5go.js +0 -2
  174. streamlit/static/static/js/index.D5gweoL5.js +0 -7
  175. streamlit/static/static/js/index.DByVKZgq.js +0 -1
  176. streamlit/static/static/js/index.DEND45D1.js +0 -3
  177. streamlit/static/static/js/index.DKN5MVff.js +0 -781
  178. streamlit/static/static/js/index.DfoxW1gP.js +0 -3855
  179. streamlit/static/static/js/index.Dtf1Ac0x.js +0 -1
  180. streamlit/static/static/js/index.DxrLhpeO.js +0 -1
  181. streamlit/static/static/js/index.J7o-_HIh.js +0 -1
  182. streamlit/static/static/js/index.LU8juINp.js +0 -197
  183. streamlit/static/static/js/index.L_N2iylt.js +0 -1
  184. streamlit/static/static/js/index.PZUX2kRz.js +0 -3
  185. streamlit/static/static/js/index.ROjU6K0k.js +0 -1
  186. streamlit/static/static/js/index.WSNLkF94.js +0 -1
  187. streamlit/static/static/js/index.X5W3gJLn.js +0 -1
  188. streamlit/static/static/js/index.k9LYqfSL.js +0 -1
  189. streamlit/static/static/js/index.pnHtHv_c.js +0 -203
  190. streamlit/static/static/js/index.tPUXqsfW.js +0 -1
  191. streamlit/static/static/js/mergeWith.GRNk8iwv.js +0 -1
  192. streamlit/static/static/js/number-overlay-editor.DXS2qb1U.js +0 -9
  193. streamlit/static/static/js/threshold.DjX0wlsa.js +0 -1
  194. streamlit/static/static/js/timer.CAwTRJ_g.js +0 -1
  195. streamlit/static/static/js/uniqueId.D_5M8Dgf.js +0 -1
  196. streamlit/static/static/js/useUpdateUiValue.C7ZKpLQK.js +0 -1
  197. streamlit/static/static/js/value.CgPGBV_l.js +0 -1
  198. streamlit/static/static/js/withFullScreenWrapper.C-gXt0Rl.js +0 -1
  199. streamlit/static/static/media/MaterialSymbols-Rounded.DsbC8sYI.woff2 +0 -0
  200. {streamlit-1.48.1.data → streamlit-1.49.0.data}/scripts/streamlit.cmd +0 -0
  201. {streamlit-1.48.1.dist-info → streamlit-1.49.0.dist-info}/WHEEL +0 -0
  202. {streamlit-1.48.1.dist-info → streamlit-1.49.0.dist-info}/entry_points.txt +0 -0
  203. {streamlit-1.48.1.dist-info → streamlit-1.49.0.dist-info}/top_level.txt +0 -0
@@ -20,6 +20,7 @@ from typing import TYPE_CHECKING, Any, Literal, Union, cast
20
20
 
21
21
  from typing_extensions import TypeAlias
22
22
 
23
+ from streamlit.dataframe_util import OptionSequence, convert_anything_to_list
23
24
  from streamlit.elements.lib.layout_utils import (
24
25
  Height,
25
26
  LayoutConfig,
@@ -62,11 +63,14 @@ class MetricMixin:
62
63
  value: Value,
63
64
  delta: Delta = None,
64
65
  delta_color: DeltaColor = "normal",
66
+ *,
65
67
  help: str | None = None,
66
68
  label_visibility: LabelVisibility = "visible",
67
69
  border: bool = False,
68
70
  width: Width = "stretch",
69
71
  height: Height = "content",
72
+ chart_data: OptionSequence[Any] | None = None,
73
+ chart_type: Literal["line", "bar", "area"] = "line",
70
74
  ) -> DeltaGenerator:
71
75
  r"""Display a metric in big bold font, with an optional indicator of how the metric changed.
72
76
 
@@ -156,6 +160,22 @@ class MetricMixin:
156
160
  the parent container, the width of the element matches the width
157
161
  of the parent container.
158
162
 
163
+ chart_data : Iterable or None
164
+ A sequence of numeric values to display as a sparkline chart. If
165
+ this is ``None`` (default), no chart is displayed. The sequence can
166
+ be anything supported by ``st.dataframe``, including a ``list`` or
167
+ ``set``. If the sequence is dataframe-like, the first column will
168
+ be used. Each value will be cast to ``float`` internally by
169
+ default.
170
+
171
+ chart_type : "line", "bar", or "area"
172
+ The type of sparkline chart to display. This can be one of the
173
+ following:
174
+
175
+ - ``"line"`` (default): A simple sparkline.
176
+ - ``"area"``: A sparkline with area shading.
177
+ - ``"bar"``: A bar chart.
178
+
159
179
  Examples
160
180
  --------
161
181
  **Example 1: Show a metric**
@@ -192,7 +212,10 @@ class MetricMixin:
192
212
  >>> st.metric(label="Gas price", value=4, delta=-0.5, delta_color="inverse")
193
213
  >>>
194
214
  >>> st.metric(
195
- ... label="Active developers", value=123, delta=123, delta_color="off"
215
+ ... label="Active developers",
216
+ ... value=123,
217
+ ... delta=123,
218
+ ... delta_color="off",
196
219
  ... )
197
220
 
198
221
  .. output::
@@ -218,6 +241,33 @@ class MetricMixin:
218
241
  https://doc-metric-example4.streamlit.app/
219
242
  height: 350px
220
243
 
244
+ **Example 5: Show sparklines**
245
+
246
+ To show trends over time, add sparklines.
247
+
248
+ >>> import streamlit as st
249
+ >>> from numpy.random import default_rng as rng
250
+ >>>
251
+ >>> changes = list(rng(4).standard_normal(20))
252
+ >>> data = [sum(changes[:i]) for i in range(20)]
253
+ >>> delta = round(data[-1], 2)
254
+ >>>
255
+ >>> row = st.container(horizontal=True)
256
+ >>> with row:
257
+ >>> st.metric(
258
+ ... "Line", 10, delta, chart_data=data, chart_type="line", border=True
259
+ ... )
260
+ >>> st.metric(
261
+ ... "Area", 10, delta, chart_data=data, chart_type="area", border=True
262
+ ... )
263
+ >>> st.metric(
264
+ ... "Bar", 10, delta, chart_data=data, chart_type="bar", border=True
265
+ ... )
266
+
267
+ .. output::
268
+ https://doc-metric-example5.streamlit.app/
269
+ height: 300px
270
+
221
271
  """
222
272
  maybe_raise_label_warnings(label, label_visibility)
223
273
 
@@ -238,6 +288,22 @@ class MetricMixin:
238
288
  label_visibility
239
289
  )
240
290
 
291
+ if chart_data is not None:
292
+ prepared_data: list[float] = []
293
+ for val in convert_anything_to_list(chart_data):
294
+ try:
295
+ prepared_data.append(float(val))
296
+ except Exception as ex: # noqa: PERF203
297
+ raise StreamlitAPIException(
298
+ "Only numeric values are supported for chart data sequence. The "
299
+ f"value '{val}' is of type {type(val)} and "
300
+ "cannot be converted to float."
301
+ ) from ex
302
+ if len(prepared_data) > 0:
303
+ metric_proto.chart_data.extend(prepared_data)
304
+
305
+ metric_proto.chart_type = _parse_chart_type(chart_type)
306
+
241
307
  validate_height(height, allow_content=True)
242
308
  validate_width(width, allow_content=True)
243
309
  layout_config = LayoutConfig(width=width, height=height)
@@ -249,6 +315,17 @@ class MetricMixin:
249
315
  return cast("DeltaGenerator", self)
250
316
 
251
317
 
318
+ def _parse_chart_type(
319
+ chart_type: Literal["line", "bar", "area"],
320
+ ) -> MetricProto.ChartType.ValueType:
321
+ if chart_type == "bar":
322
+ return MetricProto.ChartType.BAR
323
+ if chart_type == "area":
324
+ return MetricProto.ChartType.AREA
325
+ # Use line as default chart:
326
+ return MetricProto.ChartType.LINE
327
+
328
+
252
329
  def _parse_label(label: str) -> str:
253
330
  if not isinstance(label, str):
254
331
  raise TypeError(
@@ -0,0 +1,192 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ import io
18
+ from pathlib import Path
19
+ from typing import TYPE_CHECKING, Any, Union, cast
20
+
21
+ from typing_extensions import TypeAlias
22
+
23
+ from streamlit import url_util
24
+ from streamlit.elements.lib.layout_utils import validate_height
25
+ from streamlit.errors import StreamlitAPIException
26
+ from streamlit.runtime.metrics_util import gather_metrics
27
+
28
+ if TYPE_CHECKING:
29
+ from streamlit.delta_generator import DeltaGenerator
30
+ from streamlit.elements.lib.layout_utils import HeightWithoutContent
31
+
32
+ PdfData: TypeAlias = Union[str, Path, bytes, io.BytesIO]
33
+
34
+
35
+ def _get_pdf_component() -> Any | None:
36
+ """Get the PDF custom component if available.
37
+
38
+ Returns
39
+ -------
40
+ Any | None
41
+ The pdf_viewer function if the streamlit-pdf component is available,
42
+ None otherwise.
43
+ """
44
+ try:
45
+ import streamlit_pdf # type: ignore
46
+
47
+ return streamlit_pdf.pdf_viewer
48
+ except ImportError:
49
+ return None
50
+
51
+
52
+ class PdfMixin:
53
+ @gather_metrics("pdf")
54
+ def pdf(
55
+ self,
56
+ data: PdfData,
57
+ *,
58
+ height: HeightWithoutContent = 500,
59
+ key: str | None = None,
60
+ ) -> DeltaGenerator:
61
+ """Display a PDF viewer.
62
+
63
+ .. Important::
64
+
65
+ You must install |streamlit-pdf|_ to use this command. You can
66
+ install it as an extra with Streamlit:
67
+
68
+ .. code-block:: shell
69
+
70
+ pip install streamlit[pdf]
71
+
72
+ .. |streamlit-pdf| replace:: ``streamlit-pdf``
73
+ .. _streamlit-pdf: https://github.com/streamlit/streamlit-pdf
74
+
75
+ Parameters
76
+ ----------
77
+ data : str, Path, BytesIO, or bytes
78
+ The PDF file to show. This can be one of the following:
79
+
80
+ - A URL (string) for a hosted PDF file.
81
+ - A path to a local PDF file. If you use a relative path, it must
82
+ be relative to the current working directory.
83
+ - A file-like object. For example, this can be an ``UploadedFile``
84
+ from ``st.file_uploader``, or this can be a local file opened
85
+ with ``open()``.
86
+ - Raw bytes data.
87
+
88
+ height : int or "stretch"
89
+ The height of the PDF viewer. This can be one of the following:
90
+
91
+ - An integer specifying the height in pixels: The viewer has a
92
+ fixed height. If the content is larger than the specified
93
+ height, scrolling is enabled. This is ``500`` by default.
94
+ - ``"stretch"``: The height of the viewer matches the height of
95
+ its content or the height of the parent container, whichever is
96
+ larger. If the viewer is not in a parent container, the height
97
+ of the viewer matches the height of its content.
98
+
99
+ Example
100
+ -------
101
+ >>> st.pdf("https://example.com/sample.pdf")
102
+ >>> st.pdf("https://example.com/sample.pdf", height=600)
103
+ """
104
+ # Validate data parameter early
105
+ if data is None:
106
+ raise StreamlitAPIException(
107
+ "The PDF data cannot be None. Please provide a valid PDF file path, URL, "
108
+ "bytes data, or file-like object."
109
+ )
110
+
111
+ # Check if custom PDF component is available first
112
+ pdf_component = _get_pdf_component()
113
+ if pdf_component is None:
114
+ return self._show_pdf_warning()
115
+
116
+ return self._call_pdf_component(pdf_component, data, height, key)
117
+
118
+ def _call_pdf_component(
119
+ self,
120
+ pdf_component: Any,
121
+ data: PdfData,
122
+ height: HeightWithoutContent,
123
+ key: str | None,
124
+ ) -> DeltaGenerator:
125
+ """Call the custom PDF component with the provided data."""
126
+ # Validate height parameter after confirming component is available
127
+ validate_height(height, allow_content=False)
128
+
129
+ # Convert data to the format expected by pdf_viewer component
130
+ file_param: str | bytes
131
+
132
+ if isinstance(data, (str, Path)):
133
+ data_str = str(data).strip() # Strip whitespace from URLs
134
+ if url_util.is_url(data_str, allowed_schemas=("http", "https")):
135
+ # It's a URL - pass directly
136
+ file_param = data_str
137
+ else:
138
+ # It's a local file path - read the content as bytes for security
139
+ try:
140
+ with open(data_str, "rb") as file:
141
+ file_param = file.read()
142
+ except (FileNotFoundError, PermissionError) as e:
143
+ raise StreamlitAPIException(
144
+ f"Unable to read file '{data_str}': {e}"
145
+ )
146
+
147
+ elif isinstance(data, bytes):
148
+ # Pass bytes directly - the component will handle uploading to media storage
149
+ file_param = data
150
+ elif hasattr(data, "read") and hasattr(data, "getvalue"):
151
+ # Handle BytesIO and similar
152
+ file_param = data.getvalue()
153
+ elif hasattr(data, "read"):
154
+ # Handle other file-like objects
155
+ file_param = data.read()
156
+ else:
157
+ # Provide a more helpful error message
158
+ raise StreamlitAPIException(
159
+ f"Unsupported data type for PDF: {type(data).__name__}. "
160
+ f"Please provide a file path (str or Path), URL (str), bytes data, "
161
+ f"or file-like object (such as BytesIO or UploadedFile)."
162
+ )
163
+
164
+ # Convert to component-compatible format
165
+ if height == "stretch":
166
+ # For stretch, we need to pass a special value the component understands
167
+ # This maintains compatibility with the component while using standard layout
168
+ component_height = "stretch"
169
+ else:
170
+ component_height = str(height)
171
+
172
+ result = pdf_component(
173
+ file=file_param,
174
+ height=component_height,
175
+ key=key,
176
+ )
177
+ return cast("DeltaGenerator", result)
178
+
179
+ def _show_pdf_warning(self) -> DeltaGenerator:
180
+ """Raise an exception that the PDF component is not available."""
181
+ raise StreamlitAPIException(
182
+ "The PDF viewer requires the `streamlit-pdf` component to be installed.\n\n"
183
+ "Please run `pip install streamlit[pdf]` to install it.\n\n"
184
+ "For more information, see the Streamlit PDF documentation at "
185
+ "https://docs.streamlit.io/develop/api-reference/media/st.pdf."
186
+ # TODO: Update this URL when docs are updated
187
+ )
188
+
189
+ @property
190
+ def dg(self) -> DeltaGenerator:
191
+ """Get our DeltaGenerator."""
192
+ return cast("DeltaGenerator", self)
@@ -333,7 +333,9 @@ class PlotlyMixin:
333
333
  can install all charting dependencies (except Bokeh) as an extra
334
334
  with Streamlit:
335
335
 
336
- >>> pip install streamlit[charts]
336
+ .. code-block:: shell
337
+
338
+ pip install streamlit[charts]
337
339
 
338
340
  Parameters
339
341
  ----------
@@ -556,7 +558,6 @@ class PlotlyMixin:
556
558
  plotly_chart_proto.id = compute_and_register_element_id(
557
559
  "plotly_chart",
558
560
  user_key=key,
559
- form_id=plotly_chart_proto.form_id,
560
561
  dg=self.dg,
561
562
  plotly_spec=plotly_chart_proto.spec,
562
563
  plotly_config=plotly_chart_proto.config,
@@ -19,8 +19,12 @@ from __future__ import annotations
19
19
  import io
20
20
  from typing import TYPE_CHECKING, Any, cast
21
21
 
22
- from streamlit.deprecation_util import show_deprecation_warning
23
- from streamlit.elements.lib.image_utils import WidthBehavior, marshall_images
22
+ from streamlit.deprecation_util import (
23
+ make_deprecated_name_warning,
24
+ show_deprecation_warning,
25
+ )
26
+ from streamlit.elements.lib.image_utils import marshall_images
27
+ from streamlit.elements.lib.layout_utils import LayoutConfig, Width, validate_width
24
28
  from streamlit.proto.Image_pb2 import ImageList as ImageListProto
25
29
  from streamlit.runtime.metrics_util import gather_metrics
26
30
 
@@ -36,7 +40,9 @@ class PyplotMixin:
36
40
  self,
37
41
  fig: Figure | None = None,
38
42
  clear_figure: bool | None = None,
39
- use_container_width: bool = True,
43
+ *,
44
+ width: Width = "stretch",
45
+ use_container_width: bool | None = None,
40
46
  **kwargs: Any,
41
47
  ) -> DeltaGenerator:
42
48
  """Display a matplotlib.pyplot figure.
@@ -46,7 +52,9 @@ class PyplotMixin:
46
52
  install all charting dependencies (except Bokeh) as an extra with
47
53
  Streamlit:
48
54
 
49
- >>> pip install streamlit[charts]
55
+ .. code-block:: shell
56
+
57
+ pip install streamlit[charts]
50
58
 
51
59
  Parameters
52
60
  ----------
@@ -69,6 +77,19 @@ class PyplotMixin:
69
77
  - If ``fig`` is not set, defaults to ``True``. This simulates Jupyter's
70
78
  approach to matplotlib rendering.
71
79
 
80
+ width : "stretch", "content", or int
81
+ The width of the chart element. This can be one of the following:
82
+
83
+ - ``"stretch"`` (default): The width of the element matches the
84
+ width of the parent container.
85
+ - ``"content"``: The width of the element matches the
86
+ width of its content, but doesn't exceed the width of the parent
87
+ container.
88
+ - An integer specifying the width in pixels: The element has a
89
+ fixed width. If the specified width is greater than the width of
90
+ the parent container, the width of the element matches the width
91
+ of the parent container.
92
+
72
93
  use_container_width : bool
73
94
  Whether to override the figure's native width with the width of
74
95
  the parent container. If ``use_container_width`` is ``True``
@@ -81,6 +102,12 @@ class PyplotMixin:
81
102
  **kwargs : any
82
103
  Arguments to pass to Matplotlib's savefig function.
83
104
 
105
+ .. deprecated::
106
+ ``use_container_width`` is deprecated and will be removed in a
107
+ future release. For ``use_container_width=True``, use
108
+ ``width="stretch"``. For ``use_container_width=False``, use
109
+ ``width="content"``.
110
+
84
111
  Example
85
112
  -------
86
113
  >>> import matplotlib.pyplot as plt
@@ -106,6 +133,21 @@ class PyplotMixin:
106
133
 
107
134
  """
108
135
 
136
+ if use_container_width is not None:
137
+ show_deprecation_warning(
138
+ make_deprecated_name_warning(
139
+ "use_container_width",
140
+ "width",
141
+ "2025-12-31",
142
+ "For `use_container_width=True`, use `width='stretch'`. "
143
+ "For `use_container_width=False`, use `width='content'`.",
144
+ include_st_prefix=False,
145
+ ),
146
+ show_in_browser=False,
147
+ )
148
+
149
+ width = "stretch" if use_container_width else "content"
150
+
109
151
  if not fig:
110
152
  show_deprecation_warning("""
111
153
  Calling `st.pyplot()` without providing a figure argument has been deprecated
@@ -125,16 +167,19 @@ If you have a specific use case that requires this functionality, please let us
125
167
  know via [issue on Github](https://github.com/streamlit/streamlit/issues).
126
168
  """)
127
169
 
170
+ validate_width(width, allow_content=True)
171
+ layout_config = LayoutConfig(width=width)
172
+
128
173
  image_list_proto = ImageListProto()
129
174
  marshall(
130
175
  self.dg._get_delta_path_str(),
131
176
  image_list_proto,
177
+ layout_config,
132
178
  fig,
133
179
  clear_figure,
134
- use_container_width,
135
180
  **kwargs,
136
181
  )
137
- return self.dg._enqueue("imgs", image_list_proto)
182
+ return self.dg._enqueue("imgs", image_list_proto, layout_config=layout_config)
138
183
 
139
184
  @property
140
185
  def dg(self) -> DeltaGenerator:
@@ -145,9 +190,9 @@ know via [issue on Github](https://github.com/streamlit/streamlit/issues).
145
190
  def marshall(
146
191
  coordinates: str,
147
192
  image_list_proto: ImageListProto,
193
+ layout_config: LayoutConfig,
148
194
  fig: Figure | None = None,
149
195
  clear_figure: bool | None = True,
150
- use_container_width: bool = True,
151
196
  **kwargs: Any,
152
197
  ) -> None:
153
198
  try:
@@ -178,14 +223,11 @@ def marshall(
178
223
 
179
224
  image = io.BytesIO()
180
225
  fig.savefig(image, **kwargs)
181
- image_width = (
182
- WidthBehavior.COLUMN if use_container_width else WidthBehavior.ORIGINAL
183
- )
184
226
  marshall_images(
185
227
  coordinates=coordinates,
186
228
  image=image,
187
229
  caption=None,
188
- width=image_width,
230
+ layout_config=layout_config,
189
231
  proto_imgs=image_list_proto,
190
232
  clamp=False,
191
233
  channels="RGB",
@@ -14,9 +14,9 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- from typing import TYPE_CHECKING, cast
17
+ from typing import TYPE_CHECKING, Literal, cast
18
18
 
19
- from streamlit.errors import StreamlitAPIException
19
+ from streamlit.errors import StreamlitAPIException, StreamlitValueError
20
20
  from streamlit.proto.Toast_pb2 import Toast as ToastProto
21
21
  from streamlit.runtime.metrics_util import gather_metrics
22
22
  from streamlit.string_util import clean_text, validate_icon_or_emoji
@@ -41,6 +41,7 @@ class ToastMixin:
41
41
  body: SupportsStr,
42
42
  *, # keyword-only args:
43
43
  icon: str | None = None,
44
+ duration: Literal["short", "long", "infinite"] | int = "short",
44
45
  ) -> DeltaGenerator:
45
46
  """Display a short message, known as a notification "toast".
46
47
  The toast appears in the app's top-right corner and disappears after four seconds.
@@ -79,16 +80,91 @@ class ToastMixin:
79
80
  <https://fonts.google.com/icons?icon.set=Material+Symbols&icon.style=Rounded>`_
80
81
  font library.
81
82
 
83
+ duration : "short", "long", "infinite", or int
84
+ The time to display the toast message. This can be one of the
85
+ following:
82
86
 
83
- Example
84
- -------
87
+ - ``"short"`` (default): Displays for 4 seconds.
88
+ - ``"long"``: Displays for 10 seconds.
89
+ - ``"infinite"``: Shows the toast until the user dismisses it.
90
+ - An integer: Displays for the specified number of seconds.
91
+
92
+ Examples
93
+ --------
94
+ **Example 1: Show a toast message**
95
+
96
+ >>> import streamlit as st
97
+ >>>
98
+ >>> st.toast("Your edited image was saved!", icon="😍")
99
+
100
+ .. output::
101
+ https://doc-status-toast.streamlit.app
102
+ height: 200px
103
+
104
+ **Example 2: Show multiple toasts**
105
+
106
+ When multiple toasts are generated, they will stack. Hovering over a
107
+ toast will stop it from disappearing. When hovering ends, the toast
108
+ will disappear after time specified in ``duration``.
109
+
110
+ >>> import time
85
111
  >>> import streamlit as st
86
112
  >>>
87
- >>> st.toast('Your edited image was saved!', icon='😍')
113
+ >>> if st.button("Three cheers"):
114
+ >>> st.toast("Hip!")
115
+ >>> time.sleep(0.5)
116
+ >>> st.toast("Hip!")
117
+ >>> time.sleep(0.5)
118
+ >>> st.toast("Hooray!", icon="🎉")
119
+
120
+ .. output::
121
+ https://doc-status-toast1.streamlit.app
122
+ height: 300px
123
+
124
+ **Example 3: Update a toast message**
125
+
126
+ Toast messages can also be updated. Assign ``st.toast(my_message)`` to
127
+ a variable and use the ``.toast()`` method to update it. If a toast has
128
+ already disappeared or been dismissed, the update will not be seen.
129
+
130
+ >>> import time
131
+ >>> import streamlit as st
132
+ >>>
133
+ >>> def cook_breakfast():
134
+ >>> msg = st.toast("Gathering ingredients...")
135
+ >>> time.sleep(1)
136
+ >>> msg.toast("Cooking...")
137
+ >>> time.sleep(1)
138
+ >>> msg.toast("Ready!", icon="🥞")
139
+ >>>
140
+ >>> if st.button("Cook breakfast"):
141
+ >>> cook_breakfast()
142
+
143
+ .. output::
144
+ https://doc-status-toast2.streamlit.app
145
+ height: 200px
146
+
88
147
  """
89
148
  toast_proto = ToastProto()
90
149
  toast_proto.body = clean_text(validate_text(body))
91
150
  toast_proto.icon = validate_icon_or_emoji(icon)
151
+
152
+ if duration in ["short", "long", "infinite"] or (
153
+ isinstance(duration, int) and duration > 0
154
+ ):
155
+ if duration == "short":
156
+ toast_proto.duration = 4
157
+ elif duration == "long":
158
+ toast_proto.duration = 10
159
+ elif duration == "infinite":
160
+ toast_proto.duration = 0
161
+ else:
162
+ toast_proto.duration = duration
163
+ else:
164
+ raise StreamlitValueError(
165
+ "duration", ["short", "long", "infinite", "a positive integer"]
166
+ )
167
+
92
168
  return self.dg._enqueue("toast", toast_proto)
93
169
 
94
170
  @property
@@ -387,18 +387,14 @@ def _convert_altair_to_vega_lite_spec(
387
387
  alt.data_transformers.register("id", id_transform) # type: ignore[arg-type,attr-defined,unused-ignore]
388
388
 
389
389
  # alt.themes was deprecated in Altair 5.5.0 in favor of alt.theme
390
- altair_theme = (
391
- alt.themes if type_util.is_altair_version_less_than("5.5.0") else alt.theme
390
+ alt_theme = (
391
+ alt.themes if type_util.is_altair_version_less_than("5.5.0") else alt.theme # ty: ignore[unresolved-attribute]
392
392
  )
393
393
 
394
394
  # The default altair theme has some width/height defaults defined
395
395
  # which are not useful for Streamlit. Therefore, we change the theme to
396
396
  # "none" to avoid those defaults.
397
- with (
398
- altair_theme.enable("none")
399
- if altair_theme.active == "default"
400
- else nullcontext()
401
- ): # type: ignore[attr-defined,unused-ignore]
397
+ with alt_theme.enable("none") if alt_theme.active == "default" else nullcontext(): # ty: ignore
402
398
  with alt.data_transformers.enable("id"): # type: ignore[attr-defined,unused-ignore]
403
399
  chart_dict = altair_chart.to_dict()
404
400
 
@@ -2083,7 +2079,6 @@ class VegaChartsMixin:
2083
2079
  vega_lite_proto.id = compute_and_register_element_id(
2084
2080
  "arrow_vega_lite_chart",
2085
2081
  user_key=key,
2086
- form_id=vega_lite_proto.form_id,
2087
2082
  dg=self.dg,
2088
2083
  vega_lite_spec=vega_lite_proto.spec,
2089
2084
  # The data is either in vega_lite_proto.data.data
@@ -239,7 +239,6 @@ class AudioInputMixin:
239
239
  element_id = compute_and_register_element_id(
240
240
  "audio_input",
241
241
  user_key=key,
242
- form_id=current_form_id(self.dg),
243
242
  dg=self.dg,
244
243
  label=label,
245
244
  help=help,
@@ -914,8 +914,6 @@ class ButtonMixin:
914
914
  element_id = compute_and_register_element_id(
915
915
  "download_button",
916
916
  user_key=key,
917
- # download_button is not allowed to be used in a form.
918
- form_id=None,
919
917
  dg=self.dg,
920
918
  label=label,
921
919
  icon=icon,
@@ -1123,8 +1121,6 @@ class ButtonMixin:
1123
1121
  element_id = compute_and_register_element_id(
1124
1122
  "button",
1125
1123
  user_key=key,
1126
- # Only the
1127
- form_id=form_id,
1128
1124
  dg=self.dg,
1129
1125
  label=label,
1130
1126
  icon=icon,
@@ -1031,7 +1031,6 @@ class ButtonGroupMixin:
1031
1031
 
1032
1032
  check_widget_policies(self.dg, key, on_change, default_value=_default)
1033
1033
 
1034
- widget_name = "button_group"
1035
1034
  ctx = get_script_run_ctx()
1036
1035
  form_id = current_form_id(self.dg)
1037
1036
  formatted_options = (
@@ -1042,10 +1041,12 @@ class ButtonGroupMixin:
1042
1041
  for index, _ in enumerate(indexable_options)
1043
1042
  ]
1044
1043
  )
1044
+
1045
1045
  element_id = compute_and_register_element_id(
1046
- widget_name,
1046
+ # The borderless style is used by st.feedback, but users expect to see
1047
+ # "feedback" in errors
1048
+ "feedback" if style == "borderless" else style,
1047
1049
  user_key=key,
1048
- form_id=form_id,
1049
1050
  dg=self.dg,
1050
1051
  options=formatted_options,
1051
1052
  default=default,
@@ -1088,7 +1089,7 @@ class ButtonGroupMixin:
1088
1089
  if ctx:
1089
1090
  save_for_app_testing(ctx, element_id, format_func)
1090
1091
 
1091
- self.dg._enqueue(widget_name, proto, layout_config=layout_config)
1092
+ self.dg._enqueue("button_group", proto, layout_config=layout_config)
1092
1093
 
1093
1094
  return widget_state
1094
1095