streamlit-nightly 1.45.1.dev20250511__py3-none-any.whl → 1.45.2.dev20250513__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 (165) hide show
  1. streamlit/__init__.py +1 -0
  2. streamlit/auth_util.py +6 -6
  3. streamlit/commands/echo.py +5 -3
  4. streamlit/commands/experimental_query_params.py +1 -1
  5. streamlit/commands/navigation.py +1 -4
  6. streamlit/components/v1/custom_component.py +2 -2
  7. streamlit/config.py +16 -18
  8. streamlit/config_util.py +6 -7
  9. streamlit/connections/sql_connection.py +1 -1
  10. streamlit/dataframe_util.py +50 -51
  11. streamlit/delta_generator.py +4 -1
  12. streamlit/deprecation_util.py +5 -5
  13. streamlit/elements/arrow.py +1 -1
  14. streamlit/elements/deck_gl_json_chart.py +1 -4
  15. streamlit/elements/doc_string.py +5 -10
  16. streamlit/elements/exception.py +5 -7
  17. streamlit/elements/layouts.py +4 -3
  18. streamlit/elements/lib/built_in_chart_utils.py +22 -22
  19. streamlit/elements/lib/pandas_styler_utils.py +6 -8
  20. streamlit/elements/lib/policies.py +1 -1
  21. streamlit/elements/map.py +2 -8
  22. streamlit/elements/markdown.py +1 -4
  23. streamlit/elements/media.py +50 -2
  24. streamlit/elements/metric.py +9 -10
  25. streamlit/elements/spinner.py +1 -1
  26. streamlit/elements/vega_charts.py +1 -1
  27. streamlit/elements/widgets/audio_input.py +1 -4
  28. streamlit/elements/widgets/camera_input.py +1 -4
  29. streamlit/elements/widgets/chat.py +52 -5
  30. streamlit/elements/widgets/data_editor.py +1 -1
  31. streamlit/elements/widgets/slider.py +4 -5
  32. streamlit/elements/widgets/time_widgets.py +1 -4
  33. streamlit/elements/write.py +2 -2
  34. streamlit/env_util.py +2 -7
  35. streamlit/error_util.py +15 -8
  36. streamlit/errors.py +11 -8
  37. streamlit/file_util.py +6 -3
  38. streamlit/git_util.py +26 -21
  39. streamlit/hello/dataframe_demo.py +1 -1
  40. streamlit/hello/mapping_demo.py +1 -1
  41. streamlit/hello/plotting_demo.py +3 -3
  42. streamlit/hello/streamlit_app.py +1 -1
  43. streamlit/hello/utils.py +2 -1
  44. streamlit/logger.py +4 -5
  45. streamlit/proto/Audio_pb2.py +4 -3
  46. streamlit/proto/Audio_pb2.pyi +8 -1
  47. streamlit/proto/Block_pb2.py +5 -5
  48. streamlit/proto/Block_pb2.pyi +7 -1
  49. streamlit/proto/ChatInput_pb2.py +8 -7
  50. streamlit/proto/ChatInput_pb2.pyi +10 -1
  51. streamlit/proto/Video_pb2.py +8 -7
  52. streamlit/proto/Video_pb2.pyi +8 -1
  53. streamlit/runtime/app_session.py +5 -5
  54. streamlit/runtime/caching/cache_data_api.py +2 -2
  55. streamlit/runtime/caching/cache_resource_api.py +1 -1
  56. streamlit/runtime/caching/cache_utils.py +3 -3
  57. streamlit/runtime/caching/hashing.py +10 -10
  58. streamlit/runtime/credentials.py +9 -8
  59. streamlit/runtime/forward_msg_queue.py +1 -1
  60. streamlit/runtime/memory_media_file_storage.py +5 -4
  61. streamlit/runtime/memory_uploaded_file_manager.py +1 -1
  62. streamlit/runtime/metrics_util.py +2 -2
  63. streamlit/runtime/runtime.py +1 -1
  64. streamlit/runtime/scriptrunner/exec_code.py +1 -1
  65. streamlit/runtime/scriptrunner/script_cache.py +1 -1
  66. streamlit/runtime/scriptrunner/script_runner.py +10 -3
  67. streamlit/runtime/scriptrunner_utils/script_requests.py +3 -3
  68. streamlit/runtime/scriptrunner_utils/script_run_context.py +3 -3
  69. streamlit/runtime/secrets.py +6 -7
  70. streamlit/runtime/state/query_params.py +3 -5
  71. streamlit/runtime/state/session_state.py +15 -15
  72. streamlit/runtime/stats.py +1 -1
  73. streamlit/static/index.html +1 -1
  74. streamlit/static/static/js/{ErrorOutline.esm.B5LmzpSn.js → ErrorOutline.esm.CxkgXqSh.js} +1 -1
  75. streamlit/static/static/js/{FileDownload.esm.BgWveG3s.js → FileDownload.esm.DVrjmwoh.js} +1 -1
  76. streamlit/static/static/js/{FileHelper.DepIpP48.js → FileHelper.CMA9s0t3.js} +1 -1
  77. streamlit/static/static/js/{FormClearHelper.C_BjP_35.js → FormClearHelper.Ca3GFjxv.js} +1 -1
  78. streamlit/static/static/js/{Hooks.BpH4YuRQ.js → Hooks.BpCPXt5n.js} +1 -1
  79. streamlit/static/static/js/{InputInstructions.CjdwGigq.js → InputInstructions.BO_BnHv5.js} +1 -1
  80. streamlit/static/static/js/{ProgressBar.D1hXcW3N.js → ProgressBar.Ctk1m4EX.js} +1 -1
  81. streamlit/static/static/js/{RenderInPortalIfExists.A1T2s6z-.js → RenderInPortalIfExists.kuKoxpXt.js} +1 -1
  82. streamlit/static/static/js/{Toolbar.y7vr7z0G.js → Toolbar.Cde1fEcQ.js} +1 -1
  83. streamlit/static/static/js/{base-input.BKhd-BLG.js → base-input.BwCmIYba.js} +1 -1
  84. streamlit/static/static/js/{checkbox.DC-GFdrh.js → checkbox.CwPOyuag.js} +1 -1
  85. streamlit/static/static/js/{createSuper.D5WUtJEy.js → createSuper.BMtevhyt.js} +1 -1
  86. streamlit/static/static/js/{data-grid-overlay-editor.BPJ38mWp.js → data-grid-overlay-editor.gtfE9z1L.js} +1 -1
  87. streamlit/static/static/js/{downloader.C6a4LRBw.js → downloader.-58ZXBvx.js} +1 -1
  88. streamlit/static/static/js/{es6.DgMlYq8q.js → es6.6JpsZqpF.js} +2 -2
  89. streamlit/static/static/js/{iframeResizer.contentWindow.BpQxex73.js → iframeResizer.contentWindow.Dvm_jxul.js} +1 -1
  90. streamlit/static/static/js/{index.CiO2JPl_.js → index.9Bu4pGgs.js} +1 -1
  91. streamlit/static/static/js/{index.4Gtr9egA.js → index.B1T1N6vQ.js} +1 -1
  92. streamlit/static/static/js/{index.20fI9wQF.js → index.B9LBeTzL.js} +1 -1
  93. streamlit/static/static/js/{index.VPO2zcSN.js → index.BXdNB_A0.js} +1 -1
  94. streamlit/static/static/js/{index.sKXwUANU.js → index.Bd91GXu8.js} +1 -1
  95. streamlit/static/static/js/{index.DT0CWGt3.js → index.BdEKCy-o.js} +1 -1
  96. streamlit/static/static/js/{index.D3tgBU6x.js → index.BhODUTaJ.js} +1 -1
  97. streamlit/static/static/js/{index.BcbB70bz.js → index.BjtSRm-c.js} +1 -1
  98. streamlit/static/static/js/{index.DPiBGVm8.js → index.BnK8pWHN.js} +1 -1
  99. streamlit/static/static/js/{index.49Q6A5_9.js → index.C5xsotRs.js} +1 -1
  100. streamlit/static/static/js/{index.xIQLhQKv.js → index.CD3lJu6g.js} +1 -1
  101. streamlit/static/static/js/{index.BydIp_VR.js → index.CGJjlswG.js} +1 -1
  102. streamlit/static/static/js/{index.Bj81ZRkx.js → index.CIZd1q4K.js} +1 -1
  103. streamlit/static/static/js/{index.5k30-U6O.js → index.CZy9JHE4.js} +2 -2
  104. streamlit/static/static/js/{index.D5PANKHd.js → index.Ce-7kIl6.js} +1 -1
  105. streamlit/static/static/js/{index.Bo-z1JM2.js → index.CiiU1-bS.js} +1 -1
  106. streamlit/static/static/js/{index.Cqla7uBZ.js → index.ClE8XHxl.js} +1 -1
  107. streamlit/static/static/js/{index.CY8tg9lY.js → index.CvKH37SN.js} +1 -1
  108. streamlit/static/static/js/{index.CkBnHCIQ.js → index.D0G-y_z6.js} +1 -1
  109. streamlit/static/static/js/{index.B3UoUwtN.js → index.D1ccH_2Z.js} +1 -1
  110. streamlit/static/static/js/{index.DB42JCH0.js → index.D3ES4sSL.js} +1 -1
  111. streamlit/static/static/js/{index.BLpDfH1w.js → index.DJ0X7aeY.js} +1 -1
  112. streamlit/static/static/js/{index.BRN41-Jy.js → index.DNNQBTM6.js} +1 -1
  113. streamlit/static/static/js/{index.br6zwNIk.js → index.DQJE0i9s.js} +62 -62
  114. streamlit/static/static/js/{index.DSPS70gp.js → index.DR9ekgzX.js} +1 -1
  115. streamlit/static/static/js/{index.BbhQIRth.js → index.DUizq_aW.js} +1 -1
  116. streamlit/static/static/js/{index.ryD8hMpi.js → index.DVE5BhiT.js} +1 -1
  117. streamlit/static/static/js/{index.C2GjbQtf.js → index.DW60zbv4.js} +1 -1
  118. streamlit/static/static/js/{index.BhGEwO_d.js → index.DenamHJl.js} +1 -1
  119. streamlit/static/static/js/{index._oOkpIXS.js → index.DgnhzFgr.js} +1 -1
  120. streamlit/static/static/js/{index.CHnID3dI.js → index.Dk_aZplH.js} +1 -1
  121. streamlit/static/static/js/index.Dqcp7EZB.js +779 -0
  122. streamlit/static/static/js/{index.DAO4Lj-D.js → index.NfOJ2GJ6.js} +1 -1
  123. streamlit/static/static/js/{index.CcvbHTJu.js → index.WVgPkrrw.js} +1 -1
  124. streamlit/static/static/js/{index.Du1Pbz0y.js → index.bkU6rhIM.js} +1 -1
  125. streamlit/static/static/js/{index.Cfi3u2-T.js → index.hQ5adhxG.js} +1 -1
  126. streamlit/static/static/js/{index.B8ca7fYO.js → index.lYSTjxV_.js} +1 -1
  127. streamlit/static/static/js/{input.CNjMviuo.js → input.JEUWF6Z-.js} +1 -1
  128. streamlit/static/static/js/{memory.f8X97LPt.js → memory.BToPJrCN.js} +1 -1
  129. streamlit/static/static/js/{mergeWith.DU9BO8BA.js → mergeWith.DGon2YId.js} +1 -1
  130. streamlit/static/static/js/{number-overlay-editor.xJPBNdGQ.js → number-overlay-editor.kqkFTYSn.js} +1 -1
  131. streamlit/static/static/js/{possibleConstructorReturn.BjPX9m9m.js → possibleConstructorReturn.twGQoCQl.js} +1 -1
  132. streamlit/static/static/js/{sandbox.B23JuuVd.js → sandbox.o85HOKwq.js} +1 -1
  133. streamlit/static/static/js/{textarea.DodnI6GX.js → textarea.CdOYpTta.js} +1 -1
  134. streamlit/static/static/js/{timepicker.ClHKNfP9.js → timepicker.Cy1BKBo3.js} +1 -1
  135. streamlit/static/static/js/{toConsumableArray.CK6YGgcW.js → toConsumableArray.BVXfsvDc.js} +1 -1
  136. streamlit/static/static/js/{uniqueId.BAAVLIKj.js → uniqueId.Dz7-nY8K.js} +1 -1
  137. streamlit/static/static/js/{useBasicWidgetState.B1oropKY.js → useBasicWidgetState.CeKdNkz-.js} +1 -1
  138. streamlit/static/static/js/{useOnInputChange.CHQJz2sf.js → useOnInputChange.CM8BtP-c.js} +1 -1
  139. streamlit/static/static/js/{withFullScreenWrapper.DZ1W8JUx.js → withFullScreenWrapper.DuyW554J.js} +1 -1
  140. streamlit/string_util.py +1 -4
  141. streamlit/temporary_directory.py +2 -2
  142. streamlit/testing/v1/app_test.py +2 -2
  143. streamlit/testing/v1/element_tree.py +10 -9
  144. streamlit/testing/v1/local_script_runner.py +1 -4
  145. streamlit/type_util.py +2 -5
  146. streamlit/watcher/folder_black_list.py +1 -1
  147. streamlit/watcher/local_sources_watcher.py +5 -3
  148. streamlit/watcher/path_watcher.py +3 -6
  149. streamlit/watcher/polling_path_watcher.py +8 -7
  150. streamlit/watcher/util.py +6 -5
  151. streamlit/web/bootstrap.py +4 -4
  152. streamlit/web/cli.py +13 -13
  153. streamlit/web/server/app_static_file_handler.py +1 -1
  154. streamlit/web/server/authlib_tornado_integration.py +6 -1
  155. streamlit/web/server/oauth_authlib_routes.py +3 -3
  156. streamlit/web/server/oidc_mixin.py +15 -3
  157. streamlit/web/server/routes.py +11 -11
  158. streamlit/web/server/stats_request_handler.py +2 -2
  159. {streamlit_nightly-1.45.1.dev20250511.dist-info → streamlit_nightly-1.45.2.dev20250513.dist-info}/METADATA +1 -1
  160. {streamlit_nightly-1.45.1.dev20250511.dist-info → streamlit_nightly-1.45.2.dev20250513.dist-info}/RECORD +164 -164
  161. streamlit/static/static/js/index.BgEbLy94.js +0 -779
  162. {streamlit_nightly-1.45.1.dev20250511.data → streamlit_nightly-1.45.2.dev20250513.data}/scripts/streamlit.cmd +0 -0
  163. {streamlit_nightly-1.45.1.dev20250511.dist-info → streamlit_nightly-1.45.2.dev20250513.dist-info}/WHEEL +0 -0
  164. {streamlit_nightly-1.45.1.dev20250511.dist-info → streamlit_nightly-1.45.2.dev20250513.dist-info}/entry_points.txt +0 -0
  165. {streamlit_nightly-1.45.1.dev20250511.dist-info → streamlit_nightly-1.45.2.dev20250513.dist-info}/top_level.txt +0 -0
@@ -861,17 +861,14 @@ def _get_x_encoding(
861
861
  # Only show a label in the x axis if the user passed a column explicitly. We
862
862
  # could go either way here, but I'm keeping this to avoid breaking the existing
863
863
  # behavior.
864
- if x_from_user is None:
865
- x_title = ""
866
- else:
867
- x_title = x_column
864
+ x_title = "" if x_from_user is None else x_column
868
865
 
869
866
  # User specified x-axis label takes precedence
870
867
  if x_axis_label is not None:
871
868
  x_title = x_axis_label
872
869
 
873
870
  # grid lines on x axis for horizontal bar charts only
874
- grid = True if chart_type == ChartType.HORIZONTAL_BAR else False
871
+ grid = chart_type == ChartType.HORIZONTAL_BAR
875
872
 
876
873
  return alt.X(
877
874
  x_field,
@@ -909,17 +906,14 @@ def _get_y_encoding(
909
906
  # Only show a label in the y axis if the user passed a column explicitly. We
910
907
  # could go either way here, but I'm keeping this to avoid breaking the existing
911
908
  # behavior.
912
- if y_from_user is None:
913
- y_title = ""
914
- else:
915
- y_title = y_column
909
+ y_title = "" if y_from_user is None else y_column
916
910
 
917
911
  # User specified y-axis label takes precedence
918
912
  if y_axis_label is not None:
919
913
  y_title = y_axis_label
920
914
 
921
915
  # grid lines on y axis for all charts except horizontal bar charts
922
- grid = False if chart_type == ChartType.HORIZONTAL_BAR else True
916
+ grid = chart_type != ChartType.HORIZONTAL_BAR
923
917
 
924
918
  return alt.Y(
925
919
  field=y_field,
@@ -960,7 +954,9 @@ def _get_color_encoding(
960
954
  # If the color value is color-like, return that.
961
955
  if is_color_like(cast("Any", color_value)):
962
956
  if len(y_column_list) != 1:
963
- raise StreamlitColorLengthError([color_value], y_column_list)
957
+ raise StreamlitColorLengthError(
958
+ [color_value] if color_value else [], y_column_list
959
+ )
964
960
 
965
961
  return alt.ColorValue(to_css_color(cast("Any", color_value)))
966
962
 
@@ -981,15 +977,16 @@ def _get_color_encoding(
981
977
  title=" ",
982
978
  )
983
979
 
984
- raise StreamlitInvalidColorError(df, color_from_user)
980
+ raise StreamlitInvalidColorError(color_from_user)
985
981
 
986
982
  if color_column is not None:
987
983
  column_type: VegaLiteType
988
984
 
989
- if color_column == _MELTED_COLOR_COLUMN_NAME:
990
- column_type = "nominal"
991
- else:
992
- column_type = _infer_vegalite_type(df[color_column])
985
+ column_type = (
986
+ "nominal"
987
+ if color_column == _MELTED_COLOR_COLUMN_NAME
988
+ else _infer_vegalite_type(df[color_column])
989
+ )
993
990
 
994
991
  color_enc = alt.Color(
995
992
  field=color_column, legend=_COLOR_LEGEND_SETTINGS, type=column_type
@@ -1137,7 +1134,7 @@ def _get_y_encoding_type(
1137
1134
 
1138
1135
 
1139
1136
  class StreamlitColumnNotFoundError(StreamlitAPIException):
1140
- def __init__(self, df, col_name, *args):
1137
+ def __init__(self, df: pd.DataFrame, col_name: str, *args: Any) -> None:
1141
1138
  available_columns = ", ".join(str(c) for c in list(df.columns))
1142
1139
  message = (
1143
1140
  f'Data does not have a column named `"{col_name}"`. '
@@ -1147,8 +1144,7 @@ class StreamlitColumnNotFoundError(StreamlitAPIException):
1147
1144
 
1148
1145
 
1149
1146
  class StreamlitInvalidColorError(StreamlitAPIException):
1150
- def __init__(self, df, color_from_user, *args):
1151
- ", ".join(str(c) for c in list(df.columns))
1147
+ def __init__(self, color_from_user: str | Color | list[Color] | None) -> None:
1152
1148
  message = f"""
1153
1149
  This does not look like a valid color argument: `{color_from_user}`.
1154
1150
 
@@ -1161,14 +1157,18 @@ The color argument can be:
1161
1157
  * The name of a column.
1162
1158
  * Or a list of colors, matching the number of y columns to draw.
1163
1159
  """
1164
- super().__init__(message, *args)
1160
+ super().__init__(message)
1165
1161
 
1166
1162
 
1167
1163
  class StreamlitColorLengthError(StreamlitAPIException):
1168
- def __init__(self, color_values, y_column_list, *args):
1164
+ def __init__(
1165
+ self,
1166
+ color_values: str | Color | Collection[Color] | None,
1167
+ y_column_list: list[str],
1168
+ ) -> None:
1169
1169
  message = (
1170
1170
  f"The list of colors `{color_values}` must have the same "
1171
1171
  "length as the list of columns to be colored "
1172
1172
  f"`{y_column_list}`."
1173
1173
  )
1174
- super().__init__(message, *args)
1174
+ super().__init__(message)
@@ -203,10 +203,9 @@ def _pandas_style_to_css(
203
203
  # > }
204
204
  # > ...
205
205
  # > ]
206
- if style_type == "table_styles":
207
- cell_selectors = [style["selector"]]
208
- else:
209
- cell_selectors = style["selectors"]
206
+ cell_selectors = (
207
+ [style["selector"]] if style_type == "table_styles" else style["selectors"]
208
+ )
210
209
 
211
210
  selectors = [
212
211
  table_selector + separator + cell_selector for cell_selector in cell_selectors
@@ -266,9 +265,8 @@ def _use_display_values(df: DataFrame, styles: Mapping[str, Any]) -> DataFrame:
266
265
  rows = styles["body"]
267
266
  for row in rows:
268
267
  for cell in row:
269
- if "id" in cell:
270
- if match := cell_selector_regex.match(cell["id"]):
271
- r, c = map(int, match.groups())
272
- new_df.iloc[r, c] = str(cell["display_value"])
268
+ if "id" in cell and (match := cell_selector_regex.match(cell["id"])):
269
+ r, c = map(int, match.groups())
270
+ new_df.iloc[r, c] = str(cell["display_value"])
273
271
 
274
272
  return new_df
@@ -97,7 +97,7 @@ def check_session_state_rules(
97
97
 
98
98
 
99
99
  class CachedWidgetWarning(StreamlitAPIWarning):
100
- def __init__(self):
100
+ def __init__(self) -> None:
101
101
  super().__init__(
102
102
  """
103
103
  Your script uses a widget command in a cached function
streamlit/elements/map.py CHANGED
@@ -399,10 +399,7 @@ def _get_value_and_col_name(
399
399
  else:
400
400
  col_name = None
401
401
 
402
- if value_or_name is None:
403
- pydeck_arg = default_value
404
- else:
405
- pydeck_arg = value_or_name
402
+ pydeck_arg = default_value if value_or_name is None else value_or_name
406
403
 
407
404
  return pydeck_arg, col_name
408
405
 
@@ -462,10 +459,7 @@ def _get_viewport_details(
462
459
  range_lat = abs(max_lat - min_lat)
463
460
 
464
461
  if zoom is None:
465
- if range_lon > range_lat:
466
- longitude_distance = range_lon
467
- else:
468
- longitude_distance = range_lat
462
+ longitude_distance = max(range_lat, range_lon)
469
463
  zoom = _get_zoom_level(longitude_distance)
470
464
 
471
465
  return zoom, center_lat, center_lon
@@ -368,10 +368,7 @@ class MarkdownMixin:
368
368
  height: 220px
369
369
 
370
370
  """
371
- if icon is not None:
372
- icon_str = validate_icon_or_emoji(icon) + " "
373
- else:
374
- icon_str = ""
371
+ icon_str = validate_icon_or_emoji(icon) + " " if icon is not None else ""
375
372
 
376
373
  # Escape [ and ] characters in the label to prevent breaking the directive syntax
377
374
  escaped_label = label.replace("[", "\\[").replace("]", "\\]")
@@ -24,11 +24,13 @@ from typing_extensions import TypeAlias
24
24
 
25
25
  from streamlit import runtime, type_util, url_util
26
26
  from streamlit.elements.lib.form_utils import current_form_id
27
+ from streamlit.elements.lib.layout_utils import WidthWithoutContent, validate_width
27
28
  from streamlit.elements.lib.subtitle_utils import process_subtitle_data
28
29
  from streamlit.elements.lib.utils import compute_and_register_element_id
29
30
  from streamlit.errors import StreamlitAPIException
30
31
  from streamlit.proto.Audio_pb2 import Audio as AudioProto
31
32
  from streamlit.proto.Video_pb2 import Video as VideoProto
33
+ from streamlit.proto.WidthConfig_pb2 import WidthConfig
32
34
  from streamlit.runtime import caching
33
35
  from streamlit.runtime.metrics_util import gather_metrics
34
36
  from streamlit.time_util import time_to_seconds
@@ -80,6 +82,7 @@ class MediaMixin:
80
82
  end_time: MediaTime | None = None,
81
83
  loop: bool = False,
82
84
  autoplay: bool = False,
85
+ width: WidthWithoutContent = "stretch",
83
86
  ) -> DeltaGenerator:
84
87
  """Display an audio player.
85
88
 
@@ -141,6 +144,12 @@ class MediaMixin:
141
144
  Whether the audio file should start playing automatically. This is
142
145
  ``False`` by default. Browsers will not autoplay audio files if the
143
146
  user has not interacted with the page by clicking somewhere.
147
+ width: int or "stretch"
148
+ The width of the audio player. This can be one of the following:
149
+
150
+ - An int: The width in pixels, e.g. ``200`` for a width of 200 pixels.
151
+ - ``"stretch"``: The default value. The audio player stretches to fill
152
+ available space in its container.
144
153
 
145
154
  Examples
146
155
  --------
@@ -181,6 +190,7 @@ class MediaMixin:
181
190
 
182
191
  """
183
192
  start_time, end_time = _parse_start_time_end_time(start_time, end_time)
193
+ validate_width(width)
184
194
 
185
195
  audio_proto = AudioProto()
186
196
 
@@ -207,6 +217,7 @@ class MediaMixin:
207
217
  loop,
208
218
  autoplay,
209
219
  form_id=current_form_id(self.dg),
220
+ width=width,
210
221
  )
211
222
  return self.dg._enqueue("audio", audio_proto)
212
223
 
@@ -222,6 +233,7 @@ class MediaMixin:
222
233
  loop: bool = False,
223
234
  autoplay: bool = False,
224
235
  muted: bool = False,
236
+ width: WidthWithoutContent = "stretch",
225
237
  ) -> DeltaGenerator:
226
238
  """Display a video player.
227
239
 
@@ -306,6 +318,12 @@ class MediaMixin:
306
318
  Whether the video should play with the audio silenced. This is
307
319
  ``False`` by default. Use this in conjunction with ``autoplay=True``
308
320
  to enable autoplay without user interaction.
321
+ width: int or "stretch"
322
+ The width of the video player. This can be one of the following:
323
+
324
+ - An int: The width in pixels, e.g. ``200`` for a width of 200 pixels.
325
+ - ``"stretch"``: The default value. The video player stretches to fill
326
+ available space in its container.
309
327
 
310
328
  Example
311
329
  -------
@@ -359,6 +377,7 @@ class MediaMixin:
359
377
 
360
378
  """
361
379
  start_time, end_time = _parse_start_time_end_time(start_time, end_time)
380
+ validate_width(width)
362
381
 
363
382
  video_proto = VideoProto()
364
383
  coordinates = self.dg._get_delta_path_str()
@@ -374,6 +393,7 @@ class MediaMixin:
374
393
  autoplay,
375
394
  muted,
376
395
  form_id=current_form_id(self.dg),
396
+ width=width,
377
397
  )
378
398
  return self.dg._enqueue("video", video_proto)
379
399
 
@@ -449,14 +469,14 @@ def _marshall_av_media(
449
469
  elif isinstance(data, io.BytesIO):
450
470
  data.seek(0)
451
471
  data_or_filename = data.getvalue()
452
- elif isinstance(data, io.RawIOBase) or isinstance(data, io.BufferedReader):
472
+ elif isinstance(data, (io.RawIOBase, io.BufferedReader)):
453
473
  data.seek(0)
454
474
  read_data = data.read()
455
475
  if read_data is None:
456
476
  return
457
477
  data_or_filename = read_data
458
478
  elif type_util.is_type(data, "numpy.ndarray"):
459
- data_or_filename = data.tobytes()
479
+ data_or_filename = cast("npt.NDArray[Any]", data).tobytes()
460
480
  else:
461
481
  raise RuntimeError("Invalid binary data format: %s" % type(data))
462
482
 
@@ -484,6 +504,7 @@ def marshall_video(
484
504
  autoplay: bool = False,
485
505
  muted: bool = False,
486
506
  form_id: str | None = None,
507
+ width: WidthWithoutContent = "stretch",
487
508
  ) -> None:
488
509
  """Marshalls a video proto, using url processors as needed.
489
510
 
@@ -530,6 +551,11 @@ def marshall_video(
530
551
  form_id: str | None
531
552
  The ID of the form that this element is placed in. Provide None if
532
553
  the element is not placed in a form.
554
+ width: int or "stretch"
555
+ The width of the video player. This can be one of the following:
556
+ - An int: The width in pixels, e.g. 200 for a width of 200 pixels.
557
+ - "stretch": The default value. The video player stretches to fill
558
+ available space in its container.
533
559
  """
534
560
 
535
561
  if start_time < 0 or (end_time is not None and end_time <= start_time):
@@ -542,6 +568,13 @@ def marshall_video(
542
568
  proto.end_time = end_time
543
569
  proto.loop = loop
544
570
 
571
+ width_config = WidthConfig()
572
+ if isinstance(width, int):
573
+ width_config.pixel_width = width
574
+ else:
575
+ width_config.use_stretch = True
576
+ proto.width_config.CopyFrom(width_config)
577
+
545
578
  # "type" distinguishes between YouTube and non-YouTube links
546
579
  proto.type = VideoProto.Type.NATIVE
547
580
 
@@ -611,6 +644,7 @@ def marshall_video(
611
644
  loop=loop,
612
645
  autoplay=autoplay,
613
646
  muted=muted,
647
+ width=width,
614
648
  )
615
649
 
616
650
 
@@ -732,6 +766,7 @@ def marshall_audio(
732
766
  loop: bool = False,
733
767
  autoplay: bool = False,
734
768
  form_id: str | None = None,
769
+ width: WidthWithoutContent = "stretch",
735
770
  ) -> None:
736
771
  """Marshalls an audio proto, using data and url processors as needed.
737
772
 
@@ -761,6 +796,11 @@ def marshall_audio(
761
796
  form_id: str | None
762
797
  The ID of the form that this element is placed in. Provide None if
763
798
  the element is not placed in a form.
799
+ width: int or "stretch"
800
+ The width of the audio player. This can be one of the following:
801
+ - An int: The width in pixels, e.g. 200 for a width of 200 pixels.
802
+ - "stretch": The default value. The audio player stretches to fill
803
+ available space in its container.
764
804
  """
765
805
 
766
806
  proto.start_time = start_time
@@ -768,6 +808,13 @@ def marshall_audio(
768
808
  proto.end_time = end_time
769
809
  proto.loop = loop
770
810
 
811
+ width_config = WidthConfig()
812
+ if isinstance(width, int):
813
+ width_config.pixel_width = width
814
+ else:
815
+ width_config.use_stretch = True
816
+ proto.width_config.CopyFrom(width_config)
817
+
771
818
  if isinstance(data, Path):
772
819
  data = str(data) # Convert Path to string
773
820
 
@@ -792,4 +839,5 @@ def marshall_audio(
792
839
  end_time=end_time,
793
840
  loop=loop,
794
841
  autoplay=autoplay,
842
+ width=width,
795
843
  )
@@ -223,13 +223,13 @@ def _parse_label(label: str) -> str:
223
223
  def _parse_value(value: Value) -> str:
224
224
  if value is None:
225
225
  return "—"
226
- if isinstance(value, int) or isinstance(value, float) or isinstance(value, str):
226
+ if isinstance(value, (int, float, str)):
227
227
  return str(value)
228
- elif hasattr(value, "item"):
228
+ if hasattr(value, "item"):
229
229
  # Add support for numpy values (e.g. int16, float64, etc.)
230
230
  try:
231
231
  # Item could also be just a variable, so we use try, except
232
- if isinstance(value.item(), float) or isinstance(value.item(), int):
232
+ if isinstance(value.item(), (float, int)):
233
233
  return str(value.item())
234
234
  except Exception: # noqa: S110
235
235
  # If the numpy item is not a valid value, the TypeError below will be raised.
@@ -247,14 +247,13 @@ def _parse_delta(delta: Delta) -> str:
247
247
  return ""
248
248
  if isinstance(delta, str):
249
249
  return dedent(delta)
250
- elif isinstance(delta, int) or isinstance(delta, float):
250
+ if isinstance(delta, (int, float)):
251
251
  return str(delta)
252
- else:
253
- raise TypeError(
254
- f"'{delta}' is of type {type(delta)}, which is not an accepted type."
255
- " delta only accepts: int, float, str, or None."
256
- " Please convert the value to an accepted type."
257
- )
252
+ raise TypeError(
253
+ f"'{delta}' is of type {type(delta)}, which is not an accepted type."
254
+ " delta only accepts: int, float, str, or None."
255
+ " Please convert the value to an accepted type."
256
+ )
258
257
 
259
258
 
260
259
  def _determine_delta_color_and_direction(
@@ -83,7 +83,7 @@ def spinner(
83
83
 
84
84
  try:
85
85
 
86
- def set_message():
86
+ def set_message() -> None:
87
87
  with display_message_lock:
88
88
  if display_message:
89
89
  spinner_proto = SpinnerProto()
@@ -360,7 +360,7 @@ def _convert_altair_to_vega_lite_spec(
360
360
  datasets[name] = data_bytes
361
361
  return {"name": name}
362
362
 
363
- alt.data_transformers.register("id", id_transform) # type: ignore[attr-defined,unused-ignore]
363
+ alt.data_transformers.register("id", id_transform) # type: ignore[arg-type,attr-defined,unused-ignore]
364
364
 
365
365
  # The default altair theme has some width/height defaults defined
366
366
  # which are not useful for Streamlit. Therefore, we change the theme to
@@ -79,10 +79,7 @@ class AudioInputSerde:
79
79
  self, ui_value: FileUploaderStateProto | None
80
80
  ) -> SomeUploadedAudioFile:
81
81
  upload_files = _get_upload_files(ui_value)
82
- if len(upload_files) == 0:
83
- return_value = None
84
- else:
85
- return_value = upload_files[0]
82
+ return_value = None if len(upload_files) == 0 else upload_files[0]
86
83
  if return_value is not None and not isinstance(return_value, DeletedFile):
87
84
  enforce_filename_restriction(return_value.name, [".wav"])
88
85
  return return_value
@@ -79,10 +79,7 @@ class CameraInputSerde:
79
79
  self, ui_value: FileUploaderStateProto | None
80
80
  ) -> SomeUploadedSnapshotFile:
81
81
  upload_files = _get_upload_files(ui_value)
82
- if len(upload_files) == 0:
83
- return_value = None
84
- else:
85
- return_value = upload_files[0]
82
+ return_value = None if len(upload_files) == 0 else upload_files[0]
86
83
  if return_value is not None and not isinstance(return_value, DeletedFile):
87
84
  enforce_filename_restriction(return_value.name, [".jpg"])
88
85
  return return_value
@@ -33,6 +33,11 @@ from streamlit.elements.lib.file_uploader_utils import (
33
33
  )
34
34
  from streamlit.elements.lib.form_utils import is_in_form
35
35
  from streamlit.elements.lib.image_utils import AtomicImage, WidthBehavior, image_to_url
36
+ from streamlit.elements.lib.layout_utils import (
37
+ Width,
38
+ WidthWithoutContent,
39
+ validate_width,
40
+ )
36
41
  from streamlit.elements.lib.policies import check_widget_policies
37
42
  from streamlit.elements.lib.utils import (
38
43
  Key,
@@ -47,6 +52,7 @@ from streamlit.proto.ChatInput_pb2 import ChatInput as ChatInputProto
47
52
  from streamlit.proto.Common_pb2 import ChatInputValue as ChatInputValueProto
48
53
  from streamlit.proto.Common_pb2 import FileUploaderState as FileUploaderStateProto
49
54
  from streamlit.proto.RootContainer_pb2 import RootContainer
55
+ from streamlit.proto.WidthConfig_pb2 import WidthConfig
50
56
  from streamlit.runtime.metrics_util import gather_metrics
51
57
  from streamlit.runtime.scriptrunner_utils.script_run_context import get_script_run_ctx
52
58
  from streamlit.runtime.state import (
@@ -224,6 +230,7 @@ class ChatMixin:
224
230
  name: Literal["user", "assistant", "ai", "human"] | str,
225
231
  *,
226
232
  avatar: Literal["user", "assistant"] | str | AtomicImage | None = None,
233
+ width: Width = "stretch",
227
234
  ) -> DeltaGenerator:
228
235
  """Insert a chat message container.
229
236
 
@@ -274,6 +281,14 @@ class ChatMixin:
274
281
  .. |st.image| replace:: ``st.image``
275
282
  .. _st.image: https://docs.streamlit.io/develop/api-reference/media/st.image
276
283
 
284
+ width: int, "auto", or "stretch"
285
+ The width of the chat message. This can be one of the following:
286
+
287
+ - An int: The width in pixels, e.g. ``200`` for a width of 200 pixels.
288
+ - ``"auto"``: Expands to fit the content.
289
+ - ``"stretch"``: The default value. The chat message stretches to fill
290
+ available space in its container.
291
+
277
292
  Returns
278
293
  -------
279
294
  Container
@@ -322,10 +337,23 @@ class ChatMixin:
322
337
  avatar, self.dg._get_delta_path_str()
323
338
  )
324
339
 
340
+ validate_width(width, allow_content=True)
341
+
325
342
  message_container_proto = BlockProto.ChatMessage()
326
343
  message_container_proto.name = name
327
344
  message_container_proto.avatar = converted_avatar
328
345
  message_container_proto.avatar_type = avatar_type
346
+
347
+ # Set up width configuration
348
+ width_config = WidthConfig()
349
+ if isinstance(width, int):
350
+ width_config.pixel_width = width
351
+ elif width == "content":
352
+ width_config.use_content = True
353
+ else:
354
+ width_config.use_stretch = True
355
+ message_container_proto.width_config.CopyFrom(width_config)
356
+
329
357
  block_proto = BlockProto()
330
358
  block_proto.allow_empty = True
331
359
  block_proto.chat_message.CopyFrom(message_container_proto)
@@ -345,6 +373,7 @@ class ChatMixin:
345
373
  on_submit: WidgetCallback | None = None,
346
374
  args: WidgetArgs | None = None,
347
375
  kwargs: WidgetKwargs | None = None,
376
+ width: WidthWithoutContent = "stretch",
348
377
  ) -> str | None: ...
349
378
 
350
379
  @overload
@@ -360,6 +389,7 @@ class ChatMixin:
360
389
  on_submit: WidgetCallback | None = None,
361
390
  args: WidgetArgs | None = None,
362
391
  kwargs: WidgetKwargs | None = None,
392
+ width: WidthWithoutContent = "stretch",
363
393
  ) -> ChatInputValue | None: ...
364
394
 
365
395
  @gather_metrics("chat_input")
@@ -375,6 +405,7 @@ class ChatMixin:
375
405
  on_submit: WidgetCallback | None = None,
376
406
  args: WidgetArgs | None = None,
377
407
  kwargs: WidgetKwargs | None = None,
408
+ width: WidthWithoutContent = "stretch",
378
409
  ) -> str | ChatInputValue | None:
379
410
  """Display a chat input widget.
380
411
 
@@ -439,6 +470,13 @@ class ChatMixin:
439
470
  kwargs : dict
440
471
  An optional dict of kwargs to pass to the callback.
441
472
 
473
+ width: int or "stretch"
474
+ The width of the chat input widget. This can be one of the following:
475
+
476
+ - An int: The width in pixels, e.g. ``200`` for a width of 200 pixels.
477
+ - ``"stretch"``: The default value. The chat input stretches to fill
478
+ available space in its container.
479
+
442
480
  Returns
443
481
  -------
444
482
  None, str, or dict-like
@@ -546,6 +584,8 @@ class ChatMixin:
546
584
  writes_allowed=False,
547
585
  )
548
586
 
587
+ validate_width(width)
588
+
549
589
  if accept_file not in {True, False, "multiple"}:
550
590
  raise StreamlitAPIException(
551
591
  "The `accept_file` parameter must be a boolean or 'multiple'."
@@ -562,6 +602,7 @@ class ChatMixin:
562
602
  max_chars=max_chars,
563
603
  accept_file=accept_file,
564
604
  file_type=file_type,
605
+ width=width,
565
606
  )
566
607
 
567
608
  if file_type:
@@ -571,11 +612,10 @@ class ChatMixin:
571
612
  # We throw an error to warn the user about this.
572
613
  # We omit this check for scripts running outside streamlit, because
573
614
  # they will have no script_run_ctx.
574
- if runtime.exists():
575
- if is_in_form(self.dg):
576
- raise StreamlitAPIException(
577
- "`st.chat_input()` can't be used in a `st.form()`."
578
- )
615
+ if runtime.exists() and is_in_form(self.dg):
616
+ raise StreamlitAPIException(
617
+ "`st.chat_input()` can't be used in a `st.form()`."
618
+ )
579
619
 
580
620
  # Determine the position of the chat input:
581
621
  # Use bottom position if chat input is within the main container
@@ -606,6 +646,13 @@ class ChatMixin:
606
646
  chat_input_proto.file_type[:] = file_type if file_type is not None else []
607
647
  chat_input_proto.max_upload_size_mb = config.get_option("server.maxUploadSize")
608
648
 
649
+ width_config = WidthConfig()
650
+ if isinstance(width, int):
651
+ width_config.pixel_width = width
652
+ else:
653
+ width_config.use_stretch = True
654
+ chat_input_proto.width_config.CopyFrom(width_config)
655
+
609
656
  serde = ChatInputSerde(
610
657
  accept_files=bool(accept_file),
611
658
  allowed_types=file_type,
@@ -949,7 +949,7 @@ class DataEditorMixin:
949
949
  if use_container_width is None:
950
950
  # If use_container_width was not explicitly set by the user, we set
951
951
  # it to True if width was not set explicitly, and False otherwise.
952
- use_container_width = True if width is None else False
952
+ use_container_width = width is None
953
953
 
954
954
  proto.use_container_width = use_container_width
955
955
 
@@ -692,7 +692,7 @@ class SliderMixin:
692
692
  if single_value:
693
693
  value = [value]
694
694
 
695
- def value_to_generic_type(v):
695
+ def value_to_generic_type(v: Any) -> SliderProto.DataType.ValueType:
696
696
  if isinstance(v, Integral):
697
697
  return SUPPORTED_TYPES[Integral]
698
698
  if isinstance(v, Real):
@@ -708,10 +708,9 @@ class SliderMixin:
708
708
  f"But were: {list(map(type, value))}"
709
709
  )
710
710
 
711
- if len(value) == 0:
712
- data_type = SliderProto.INT
713
- else:
714
- data_type = value_to_generic_type(value[0])
711
+ data_type = (
712
+ SliderProto.INT if len(value) == 0 else value_to_generic_type(value[0])
713
+ )
715
714
 
716
715
  datetime_min = time.min
717
716
  datetime_max = time.max
@@ -517,10 +517,7 @@ class TimeWidgetsMixin:
517
517
  validate_width(width)
518
518
 
519
519
  parsed_time: time | None
520
- if value is None:
521
- parsed_time = None
522
- else:
523
- parsed_time = _convert_timelike_to_time(value)
520
+ parsed_time = None if value is None else _convert_timelike_to_time(value)
524
521
 
525
522
  element_id = compute_and_register_element_id(
526
523
  "time_input",
@@ -156,7 +156,7 @@ class WriteMixin:
156
156
  streamed_response: str = ""
157
157
  written_content: list[Any] = StreamingOutput()
158
158
 
159
- def flush_stream_response():
159
+ def flush_stream_response() -> None:
160
160
  """Write the full response to the app."""
161
161
  nonlocal streamed_response
162
162
  nonlocal stream_container
@@ -428,7 +428,7 @@ class WriteMixin:
428
428
  "when called as `st.write()` or `st.sidebar.write()`."
429
429
  )
430
430
 
431
- def flush_buffer():
431
+ def flush_buffer() -> None:
432
432
  if string_buffer:
433
433
  text_content = " ".join(string_buffer)
434
434
  # The usage of empty here prevents