streamlit-nightly 1.45.2.dev20250513__py3-none-any.whl → 1.45.2.dev20250515__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.
- streamlit/commands/execution_control.py +30 -30
- streamlit/connections/sql_connection.py +10 -7
- streamlit/connections/util.py +2 -2
- streamlit/elements/heading.py +3 -4
- streamlit/elements/html.py +3 -4
- streamlit/elements/lib/built_in_chart_utils.py +1 -3
- streamlit/elements/lib/image_utils.py +2 -3
- streamlit/elements/lib/options_selector_utils.py +11 -4
- streamlit/elements/lib/pandas_styler_utils.py +1 -3
- streamlit/elements/lib/subtitle_utils.py +6 -9
- streamlit/elements/map.py +1 -2
- streamlit/elements/markdown.py +1 -1
- streamlit/elements/plotly_chart.py +8 -9
- streamlit/elements/toast.py +1 -2
- streamlit/elements/vega_charts.py +7 -7
- streamlit/elements/widgets/chat.py +25 -27
- streamlit/elements/widgets/file_uploader.py +4 -4
- streamlit/elements/widgets/number_input.py +3 -3
- streamlit/elements/widgets/radio.py +25 -5
- streamlit/elements/widgets/select_slider.py +10 -24
- streamlit/elements/widgets/selectbox.py +22 -0
- streamlit/elements/widgets/slider.py +35 -9
- streamlit/navigation/page.py +5 -6
- streamlit/runtime/caching/cache_data_api.py +18 -14
- streamlit/runtime/caching/cache_errors.py +1 -1
- streamlit/runtime/caching/cached_message_replay.py +1 -2
- streamlit/runtime/caching/storage/in_memory_cache_storage_wrapper.py +2 -3
- streamlit/runtime/connection_factory.py +1 -1
- streamlit/runtime/context.py +1 -5
- streamlit/runtime/media_file_manager.py +1 -2
- streamlit/runtime/scriptrunner/script_runner.py +1 -2
- streamlit/runtime/secrets.py +10 -8
- streamlit/runtime/state/query_params_proxy.py +14 -12
- streamlit/runtime/state/session_state.py +3 -4
- streamlit/static/index.html +1 -1
- streamlit/static/static/js/{ErrorOutline.esm.CxkgXqSh.js → ErrorOutline.esm.DZL6W-d3.js} +1 -1
- streamlit/static/static/js/{FileDownload.esm.DVrjmwoh.js → FileDownload.esm.BTdAw4zC.js} +1 -1
- streamlit/static/static/js/{FileHelper.CMA9s0t3.js → FileHelper.Beb1sf3m.js} +1 -1
- streamlit/static/static/js/{FormClearHelper.Ca3GFjxv.js → FormClearHelper.BJursVXh.js} +1 -1
- streamlit/static/static/js/{Hooks.BpCPXt5n.js → Hooks.C9-XVXXu.js} +1 -1
- streamlit/static/static/js/{InputInstructions.BO_BnHv5.js → InputInstructions.DFH2wY93.js} +1 -1
- streamlit/static/static/js/{ProgressBar.Ctk1m4EX.js → ProgressBar.6M24tsRk.js} +1 -1
- streamlit/static/static/js/{RenderInPortalIfExists.kuKoxpXt.js → RenderInPortalIfExists.BjKKk4eZ.js} +1 -1
- streamlit/static/static/js/{Toolbar.Cde1fEcQ.js → Toolbar.AcZDbsfk.js} +1 -1
- streamlit/static/static/js/{base-input.BwCmIYba.js → base-input.DbRAJnqp.js} +1 -1
- streamlit/static/static/js/{checkbox.CwPOyuag.js → checkbox.0qSLqyAl.js} +1 -1
- streamlit/static/static/js/{createSuper.BMtevhyt.js → createSuper.BVzb0XXY.js} +1 -1
- streamlit/static/static/js/{data-grid-overlay-editor.gtfE9z1L.js → data-grid-overlay-editor.veQYOXpb.js} +1 -1
- streamlit/static/static/js/{downloader.-58ZXBvx.js → downloader.C_vYtX1r.js} +1 -1
- streamlit/static/static/js/{es6.6JpsZqpF.js → es6.DlS5aK2H.js} +2 -2
- streamlit/static/static/js/{iframeResizer.contentWindow.Dvm_jxul.js → iframeResizer.contentWindow.DYLRAnnn.js} +1 -1
- streamlit/static/static/js/{index.DenamHJl.js → index.7-HGyLk8.js} +1 -1
- streamlit/static/static/js/{index.CiiU1-bS.js → index.B4jkkFu1.js} +1 -1
- streamlit/static/static/js/{index.D1ccH_2Z.js → index.B7lVUFiI.js} +1 -1
- streamlit/static/static/js/{index.BhODUTaJ.js → index.BA7OvX6X.js} +1 -1
- streamlit/static/static/js/{index.WVgPkrrw.js → index.BNNdZZ73.js} +1 -1
- streamlit/static/static/js/{index.hQ5adhxG.js → index.Bs42fx1x.js} +1 -1
- streamlit/static/static/js/{index.D0G-y_z6.js → index.C-SVYz-x.js} +1 -1
- streamlit/static/static/js/{index.bkU6rhIM.js → index.C8AfWtJ-.js} +1 -1
- streamlit/static/static/js/{index.D3ES4sSL.js → index.CEN6fgTl.js} +1 -1
- streamlit/static/static/js/{index.BXdNB_A0.js → index.COQpmMRs.js} +1 -1
- streamlit/static/static/js/{index.DNNQBTM6.js → index.CVpOBIPc.js} +1 -1
- streamlit/static/static/js/{index.Ce-7kIl6.js → index.CZ_7kbSY.js} +1 -1
- streamlit/static/static/js/{index.BnK8pWHN.js → index.CdJve-c-.js} +1 -1
- streamlit/static/static/js/{index.B9LBeTzL.js → index.ChCdyltc.js} +1 -1
- streamlit/static/static/js/{index.lYSTjxV_.js → index.CnuHgtvi.js} +1 -1
- streamlit/static/static/js/{index.DR9ekgzX.js → index.CpBBbkiT.js} +1 -1
- streamlit/static/static/js/{index.DJ0X7aeY.js → index.CqJn4aNa.js} +1 -1
- streamlit/static/static/js/{index.9Bu4pGgs.js → index.CsMycx_g.js} +1 -1
- streamlit/static/static/js/{index.Bd91GXu8.js → index.CzjlNese.js} +1 -1
- streamlit/static/static/js/{index.DUizq_aW.js → index.D6vjCnF6.js} +1 -1
- streamlit/static/static/js/{index.NfOJ2GJ6.js → index.DI4lQWQ9.js} +1 -1
- streamlit/static/static/js/{index.CGJjlswG.js → index.DOZrTqtO.js} +1 -1
- streamlit/static/static/js/{index.Dqcp7EZB.js → index.DQoQce-F.js} +77 -77
- streamlit/static/static/js/{index.ClE8XHxl.js → index.DU1Hm4Vy.js} +1 -1
- streamlit/static/static/js/{index.CD3lJu6g.js → index.D_NeaxbH.js} +1 -1
- streamlit/static/static/js/{index.DW60zbv4.js → index.DawYHwts.js} +1 -1
- streamlit/static/static/js/{index.CIZd1q4K.js → index.DgKQMQQ-.js} +1 -1
- streamlit/static/static/js/{index.BdEKCy-o.js → index.Dh9pGVkc.js} +1 -1
- streamlit/static/static/js/{index.DgnhzFgr.js → index.DvaGxMeZ.js} +1 -1
- streamlit/static/static/js/{index.DQJE0i9s.js → index.Dx0WWCBs.js} +5 -5
- streamlit/static/static/js/{index.DVE5BhiT.js → index.KEBclOiy.js} +1 -1
- streamlit/static/static/js/{index.CZy9JHE4.js → index.SG-0kuQk.js} +6 -6
- streamlit/static/static/js/{index.CvKH37SN.js → index.TZA6glxs.js} +1 -1
- streamlit/static/static/js/{index.C5xsotRs.js → index.bbxNeuVP.js} +1 -1
- streamlit/static/static/js/{index.B1T1N6vQ.js → index.pKO-QKd7.js} +1 -1
- streamlit/static/static/js/{index.Dk_aZplH.js → index.qHLLYZv1.js} +1 -1
- streamlit/static/static/js/{index.BjtSRm-c.js → index.vXLfHXtB.js} +1 -1
- streamlit/static/static/js/{input.JEUWF6Z-.js → input.4UlvIkvl.js} +1 -1
- streamlit/static/static/js/{memory.BToPJrCN.js → memory.CjAKg4CS.js} +1 -1
- streamlit/static/static/js/{mergeWith.DGon2YId.js → mergeWith.Bnn9a11J.js} +1 -1
- streamlit/static/static/js/{number-overlay-editor.kqkFTYSn.js → number-overlay-editor.BGyA8GbD.js} +1 -1
- streamlit/static/static/js/{possibleConstructorReturn.twGQoCQl.js → possibleConstructorReturn.Cu-FTKlF.js} +1 -1
- streamlit/static/static/js/{sandbox.o85HOKwq.js → sandbox.D4Wf-7jE.js} +1 -1
- streamlit/static/static/js/{textarea.CdOYpTta.js → textarea.B0_fGlpz.js} +1 -1
- streamlit/static/static/js/{timepicker.Cy1BKBo3.js → timepicker._IYyhmJr.js} +1 -1
- streamlit/static/static/js/{toConsumableArray.BVXfsvDc.js → toConsumableArray.BRgjiMf8.js} +1 -1
- streamlit/static/static/js/{uniqueId.Dz7-nY8K.js → uniqueId.DWtxcoYp.js} +1 -1
- streamlit/static/static/js/{useBasicWidgetState.CeKdNkz-.js → useBasicWidgetState.CJa4vrfr.js} +1 -1
- streamlit/static/static/js/{useOnInputChange.CM8BtP-c.js → useOnInputChange.DXDP3ow3.js} +1 -1
- streamlit/static/static/js/{withFullScreenWrapper.DuyW554J.js → withFullScreenWrapper.BYUNeZvy.js} +1 -1
- streamlit/string_util.py +1 -5
- streamlit/testing/v1/element_tree.py +2 -4
- streamlit/testing/v1/local_script_runner.py +1 -2
- streamlit/type_util.py +1 -1
- streamlit/url_util.py +1 -1
- streamlit/vendor/pympler/asizeof.py +2 -1
- streamlit/watcher/local_sources_watcher.py +3 -3
- streamlit/web/server/oidc_mixin.py +2 -2
- {streamlit_nightly-1.45.2.dev20250513.dist-info → streamlit_nightly-1.45.2.dev20250515.dist-info}/METADATA +1 -1
- {streamlit_nightly-1.45.2.dev20250513.dist-info → streamlit_nightly-1.45.2.dev20250515.dist-info}/RECORD +115 -115
- {streamlit_nightly-1.45.2.dev20250513.dist-info → streamlit_nightly-1.45.2.dev20250515.dist-info}/WHEEL +1 -1
- {streamlit_nightly-1.45.2.dev20250513.data → streamlit_nightly-1.45.2.dev20250515.data}/scripts/streamlit.cmd +0 -0
- {streamlit_nightly-1.45.2.dev20250513.dist-info → streamlit_nightly-1.45.2.dev20250515.dist-info}/entry_points.txt +0 -0
- {streamlit_nightly-1.45.2.dev20250513.dist-info → streamlit_nightly-1.45.2.dev20250515.dist-info}/top_level.txt +0 -0
@@ -64,38 +64,38 @@ def _new_fragment_id_queue(
|
|
64
64
|
if scope == "app":
|
65
65
|
return []
|
66
66
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
)
|
92
|
-
|
93
|
-
new_queue = list(dropwhile(lambda x: x != ctx.current_fragment_id, curr_queue))
|
94
|
-
assert new_queue, (
|
95
|
-
"Could not find current_fragment_id in fragment_id_queue. This should never happen."
|
67
|
+
# > scope == "fragment"
|
68
|
+
curr_queue = ctx.fragment_ids_this_run
|
69
|
+
|
70
|
+
# If st.rerun(scope="fragment") is called during a full script run, we raise an
|
71
|
+
# exception. This occurs, of course, if st.rerun(scope="fragment") is called
|
72
|
+
# outside of a fragment, but it somewhat surprisingly occurs if it gets called
|
73
|
+
# from within a fragment during a run of the full script. While this behavior may
|
74
|
+
# be surprising, it seems somewhat reasonable given that the correct behavior of
|
75
|
+
# calling st.rerun(scope="fragment") in this situation is unclear to me:
|
76
|
+
# * Rerunning just the fragment immediately may cause weirdness down the line
|
77
|
+
# as any part of the script that occurs after the fragment will not be
|
78
|
+
# executed.
|
79
|
+
# * Waiting until the full script run completes before rerunning the fragment
|
80
|
+
# seems odd (even if we normally do this before running a fragment not
|
81
|
+
# triggered by st.rerun()) because it defers the execution of st.rerun().
|
82
|
+
# * Rerunning the full app feels incorrect as we're seemingly ignoring the
|
83
|
+
# `scope` argument.
|
84
|
+
# With these issues and given that it seems pretty unnatural to have a
|
85
|
+
# fragment-scoped rerun happen during a full script run to begin with, it seems
|
86
|
+
# reasonable to just disallow this completely for now.
|
87
|
+
if not curr_queue:
|
88
|
+
raise StreamlitAPIException(
|
89
|
+
'scope="fragment" can only be specified from `@st.fragment`-decorated '
|
90
|
+
"functions during fragment reruns."
|
96
91
|
)
|
97
92
|
|
98
|
-
|
93
|
+
new_queue = list(dropwhile(lambda x: x != ctx.current_fragment_id, curr_queue))
|
94
|
+
assert new_queue, (
|
95
|
+
"Could not find current_fragment_id in fragment_id_queue. This should never happen."
|
96
|
+
)
|
97
|
+
|
98
|
+
return new_queue
|
99
99
|
|
100
100
|
|
101
101
|
@gather_metrics("rerun")
|
@@ -319,13 +319,16 @@ class SQLConnection(BaseConnection["Engine"]):
|
|
319
319
|
import pandas as pd
|
320
320
|
|
321
321
|
instance = self._instance.connect()
|
322
|
-
return
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
322
|
+
return cast(
|
323
|
+
"DataFrame",
|
324
|
+
pd.read_sql(
|
325
|
+
text(sql),
|
326
|
+
instance,
|
327
|
+
index_col=index_col,
|
328
|
+
chunksize=chunksize,
|
329
|
+
params=params,
|
330
|
+
**kwargs,
|
331
|
+
),
|
329
332
|
)
|
330
333
|
|
331
334
|
# We modify our helper function's `__qualname__` here to work around default
|
streamlit/connections/util.py
CHANGED
@@ -21,7 +21,7 @@
|
|
21
21
|
from __future__ import annotations
|
22
22
|
|
23
23
|
import os
|
24
|
-
from typing import TYPE_CHECKING, Any
|
24
|
+
from typing import TYPE_CHECKING, Any
|
25
25
|
|
26
26
|
if TYPE_CHECKING:
|
27
27
|
from collections.abc import Collection
|
@@ -92,6 +92,6 @@ def running_in_sis() -> bool:
|
|
92
92
|
is_in_stored_procedure,
|
93
93
|
)
|
94
94
|
|
95
|
-
return
|
95
|
+
return is_in_stored_procedure() # type: ignore
|
96
96
|
except ModuleNotFoundError:
|
97
97
|
return False
|
streamlit/elements/heading.py
CHANGED
@@ -262,10 +262,9 @@ class HeadingMixin:
|
|
262
262
|
]
|
263
263
|
if divider in valid_colors:
|
264
264
|
return cast("str", divider)
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
)
|
265
|
+
raise StreamlitAPIException(
|
266
|
+
f"Divider parameter has invalid value: `{divider}`. Please choose from: {', '.join(valid_colors)}."
|
267
|
+
)
|
269
268
|
|
270
269
|
@staticmethod
|
271
270
|
def _create_heading_proto(
|
streamlit/elements/html.py
CHANGED
@@ -113,10 +113,9 @@ class HtmlMixin:
|
|
113
113
|
# If true, there are only style tags - send html to the event container
|
114
114
|
html_proto.body = html_content
|
115
115
|
return self._event_dg._enqueue("html", html_proto)
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
return self.dg._enqueue("html", html_proto)
|
116
|
+
# Otherwise, send the html to the main container as normal
|
117
|
+
html_proto.body = html_content
|
118
|
+
return self.dg._enqueue("html", html_proto)
|
120
119
|
|
121
120
|
@property
|
122
121
|
def dg(self) -> DeltaGenerator:
|
@@ -559,7 +559,7 @@ def _melt_data(
|
|
559
559
|
|
560
560
|
# Arrow has problems with object types after melting two different dtypes
|
561
561
|
# > pyarrow.lib.ArrowTypeError: "Expected a <TYPE> object, got a object"
|
562
|
-
|
562
|
+
return dataframe_util.fix_arrow_incompatible_column_types(
|
563
563
|
melted_df,
|
564
564
|
selected_columns=[
|
565
565
|
*columns_to_leave_alone,
|
@@ -568,8 +568,6 @@ def _melt_data(
|
|
568
568
|
],
|
569
569
|
)
|
570
570
|
|
571
|
-
return fixed_df
|
572
|
-
|
573
571
|
|
574
572
|
def _maybe_reset_index_in_place(
|
575
573
|
df: pd.DataFrame, x_column: str | None, y_column_list: list[str]
|
@@ -335,9 +335,8 @@ def image_to_url(
|
|
335
335
|
url = runtime.get_instance().media_file_mgr.add(image_data, mimetype, image_id)
|
336
336
|
caching.save_media_data(image_data, mimetype, image_id)
|
337
337
|
return url
|
338
|
-
|
339
|
-
|
340
|
-
return ""
|
338
|
+
# When running in "raw mode", we can't access the MediaFileManager.
|
339
|
+
return ""
|
341
340
|
|
342
341
|
|
343
342
|
def _4d_to_list_3d(array: npt.NDArray[Any]) -> list[npt.NDArray[Any]]:
|
@@ -92,8 +92,7 @@ def get_default_indices(
|
|
92
92
|
indexable_options: Sequence[T], default: Sequence[Any] | Any | None = None
|
93
93
|
) -> list[int]:
|
94
94
|
default_indices = check_and_convert_to_indices(indexable_options, default)
|
95
|
-
|
96
|
-
return default_indices
|
95
|
+
return default_indices if default_indices is not None else []
|
97
96
|
|
98
97
|
|
99
98
|
E1 = TypeVar("E1", bound=Enum)
|
@@ -186,7 +185,11 @@ def maybe_coerce_enum(
|
|
186
185
|
) -> RegisterWidgetResult[T]: ...
|
187
186
|
|
188
187
|
|
189
|
-
def maybe_coerce_enum(
|
188
|
+
def maybe_coerce_enum(
|
189
|
+
register_widget_result: RegisterWidgetResult[Any],
|
190
|
+
options: OptionSequence[Any],
|
191
|
+
opt_sequence: Sequence[Any],
|
192
|
+
) -> RegisterWidgetResult[Any]:
|
190
193
|
"""Maybe Coerce a RegisterWidgetResult with an Enum member value to
|
191
194
|
RegisterWidgetResult[option] if option is an EnumType, otherwise just return
|
192
195
|
the original RegisterWidgetResult.
|
@@ -228,7 +231,11 @@ def maybe_coerce_enum_sequence(
|
|
228
231
|
) -> RegisterWidgetResult[tuple[T, T]]: ...
|
229
232
|
|
230
233
|
|
231
|
-
def maybe_coerce_enum_sequence(
|
234
|
+
def maybe_coerce_enum_sequence(
|
235
|
+
register_widget_result: RegisterWidgetResult[list[Any] | tuple[Any, ...]],
|
236
|
+
options: OptionSequence[Any],
|
237
|
+
opt_sequence: Sequence[Any],
|
238
|
+
) -> RegisterWidgetResult[list[Any] | tuple[Any, ...]]:
|
232
239
|
"""Maybe Coerce a RegisterWidgetResult with a sequence of Enum members as value
|
233
240
|
to RegisterWidgetResult[Sequence[option]] if option is an EnumType, otherwise just
|
234
241
|
return the original RegisterWidgetResult.
|
@@ -213,9 +213,7 @@ def _pandas_style_to_css(
|
|
213
213
|
selector = ", ".join(selectors)
|
214
214
|
|
215
215
|
declaration_block = "; ".join(declarations)
|
216
|
-
|
217
|
-
|
218
|
-
return rule_set
|
216
|
+
return selector + " { " + declaration_block + " }"
|
219
217
|
|
220
218
|
|
221
219
|
def _marshall_display_values(
|
@@ -104,9 +104,7 @@ def _srt_to_vtt(srt_data: str | bytes) -> bytes:
|
|
104
104
|
# Add WebVTT file header
|
105
105
|
vtt_content = "WEBVTT\n\n" + vtt_data
|
106
106
|
# Convert the vtt content to bytes
|
107
|
-
|
108
|
-
|
109
|
-
return vtt_content
|
107
|
+
return vtt_content.strip().encode("utf-8")
|
110
108
|
|
111
109
|
|
112
110
|
def _handle_string_or_path_data(data_or_path: str | Path) -> bytes:
|
@@ -123,14 +121,14 @@ def _handle_string_or_path_data(data_or_path: str | Path) -> bytes:
|
|
123
121
|
with open(data_or_path, "rb") as file:
|
124
122
|
content = file.read()
|
125
123
|
return _srt_to_vtt(content) if file_extension == ".srt" else content
|
126
|
-
|
127
|
-
raise ValueError(f"File {data_or_path} does not exist.")
|
124
|
+
if isinstance(data_or_path, Path):
|
125
|
+
raise ValueError(f"File {data_or_path} does not exist.") # noqa: TRY004
|
128
126
|
|
129
127
|
content_string = data_or_path.strip()
|
130
128
|
|
131
129
|
if content_string.startswith("WEBVTT") or content_string == "":
|
132
130
|
return content_string.encode("utf-8")
|
133
|
-
|
131
|
+
if _is_srt(content_string):
|
134
132
|
return _srt_to_vtt(content_string)
|
135
133
|
raise ValueError("The provided string neither matches valid VTT nor SRT format.")
|
136
134
|
|
@@ -173,6 +171,5 @@ def process_subtitle_data(
|
|
173
171
|
)
|
174
172
|
caching.save_media_data(subtitle_data, "text/vtt", coordinates)
|
175
173
|
return file_url
|
176
|
-
|
177
|
-
|
178
|
-
return ""
|
174
|
+
# When running in "raw mode", we can't access the MediaFileManager.
|
175
|
+
return ""
|
streamlit/elements/map.py
CHANGED
@@ -357,8 +357,7 @@ def _get_lat_or_lon_col_name(
|
|
357
357
|
f"Map data must contain a {human_readable_name} column named: "
|
358
358
|
f"{formatted_allowed_col_name}. Existing columns: {formmated_col_names}"
|
359
359
|
)
|
360
|
-
|
361
|
-
col_name = candidate_col_name
|
360
|
+
col_name = candidate_col_name
|
362
361
|
|
363
362
|
# Check that the column is well-formed.
|
364
363
|
# IMPLEMENTATION NOTE: We can't use isnull().values.any() because .values can return
|
streamlit/elements/markdown.py
CHANGED
@@ -22,7 +22,7 @@ from streamlit.string_util import clean_text, validate_icon_or_emoji
|
|
22
22
|
from streamlit.type_util import SupportsStr, is_sympy_expression
|
23
23
|
|
24
24
|
if TYPE_CHECKING:
|
25
|
-
import sympy
|
25
|
+
import sympy
|
26
26
|
|
27
27
|
from streamlit.delta_generator import DeltaGenerator
|
28
28
|
|
@@ -29,7 +29,7 @@ from typing import (
|
|
29
29
|
overload,
|
30
30
|
)
|
31
31
|
|
32
|
-
from typing_extensions import TypeAlias
|
32
|
+
from typing_extensions import Required, TypeAlias
|
33
33
|
|
34
34
|
from streamlit import type_util
|
35
35
|
from streamlit.deprecation_util import show_deprecation_warning
|
@@ -159,10 +159,10 @@ class PlotlySelectionState(TypedDict, total=False):
|
|
159
159
|
|
160
160
|
"""
|
161
161
|
|
162
|
-
points: list[dict[str, Any]]
|
163
|
-
point_indices: list[int]
|
164
|
-
box: list[dict[str, Any]]
|
165
|
-
lasso: list[dict[str, Any]]
|
162
|
+
points: Required[list[dict[str, Any]]]
|
163
|
+
point_indices: Required[list[int]]
|
164
|
+
box: Required[list[dict[str, Any]]]
|
165
|
+
lasso: Required[list[dict[str, Any]]]
|
166
166
|
|
167
167
|
|
168
168
|
class PlotlyState(TypedDict, total=False):
|
@@ -205,7 +205,7 @@ class PlotlyState(TypedDict, total=False):
|
|
205
205
|
|
206
206
|
"""
|
207
207
|
|
208
|
-
selection: PlotlySelectionState
|
208
|
+
selection: Required[PlotlySelectionState]
|
209
209
|
|
210
210
|
|
211
211
|
@dataclass
|
@@ -231,7 +231,7 @@ class PlotlyChartSelectionSerde:
|
|
231
231
|
)
|
232
232
|
|
233
233
|
if "selection" not in selection_state:
|
234
|
-
selection_state = empty_selection_state
|
234
|
+
selection_state = empty_selection_state # type: ignore[unreachable]
|
235
235
|
|
236
236
|
return cast("PlotlyState", AttributeDictionary(selection_state))
|
237
237
|
|
@@ -537,8 +537,7 @@ class PlotlyMixin:
|
|
537
537
|
|
538
538
|
self.dg._enqueue("plotly_chart", plotly_chart_proto)
|
539
539
|
return cast("PlotlyState", widget_state.value)
|
540
|
-
|
541
|
-
return self.dg._enqueue("plotly_chart", plotly_chart_proto)
|
540
|
+
return self.dg._enqueue("plotly_chart", plotly_chart_proto)
|
542
541
|
|
543
542
|
@property
|
544
543
|
def dg(self) -> DeltaGenerator:
|
streamlit/elements/toast.py
CHANGED
@@ -31,7 +31,7 @@ from typing import (
|
|
31
31
|
overload,
|
32
32
|
)
|
33
33
|
|
34
|
-
from typing_extensions import TypeAlias
|
34
|
+
from typing_extensions import Required, TypeAlias
|
35
35
|
|
36
36
|
from streamlit import dataframe_util, type_util
|
37
37
|
from streamlit.elements.lib import dicttools
|
@@ -218,7 +218,7 @@ class VegaLiteState(TypedDict, total=False):
|
|
218
218
|
|
219
219
|
"""
|
220
220
|
|
221
|
-
selection: AttributeDictionary
|
221
|
+
selection: Required[AttributeDictionary]
|
222
222
|
|
223
223
|
|
224
224
|
@dataclass
|
@@ -242,7 +242,7 @@ class VegaLiteStateSerde:
|
|
242
242
|
)
|
243
243
|
|
244
244
|
if "selection" not in selection_state:
|
245
|
-
selection_state = empty_selection_state
|
245
|
+
selection_state = empty_selection_state # type: ignore[unreachable]
|
246
246
|
|
247
247
|
return cast("VegaLiteState", AttributeDictionary(selection_state))
|
248
248
|
|
@@ -1479,7 +1479,7 @@ class VegaChartsMixin:
|
|
1479
1479
|
use_container_width: bool | None = None,
|
1480
1480
|
theme: Literal["streamlit"] | None = "streamlit",
|
1481
1481
|
key: Key | None = None,
|
1482
|
-
on_select: Literal["ignore"]
|
1482
|
+
on_select: Literal["ignore"] = "ignore",
|
1483
1483
|
selection_mode: str | Iterable[str] | None = None,
|
1484
1484
|
) -> DeltaGenerator: ...
|
1485
1485
|
|
@@ -1492,7 +1492,7 @@ class VegaChartsMixin:
|
|
1492
1492
|
use_container_width: bool | None = None,
|
1493
1493
|
theme: Literal["streamlit"] | None = "streamlit",
|
1494
1494
|
key: Key | None = None,
|
1495
|
-
on_select: Literal["rerun"] | WidgetCallback
|
1495
|
+
on_select: Literal["rerun"] | WidgetCallback,
|
1496
1496
|
selection_mode: str | Iterable[str] | None = None,
|
1497
1497
|
) -> VegaLiteState: ...
|
1498
1498
|
|
@@ -1638,7 +1638,7 @@ class VegaChartsMixin:
|
|
1638
1638
|
use_container_width: bool | None = None,
|
1639
1639
|
theme: Literal["streamlit"] | None = "streamlit",
|
1640
1640
|
key: Key | None = None,
|
1641
|
-
on_select: Literal["ignore"]
|
1641
|
+
on_select: Literal["ignore"] = "ignore",
|
1642
1642
|
selection_mode: str | Iterable[str] | None = None,
|
1643
1643
|
**kwargs: Any,
|
1644
1644
|
) -> DeltaGenerator: ...
|
@@ -1653,7 +1653,7 @@ class VegaChartsMixin:
|
|
1653
1653
|
use_container_width: bool | None = None,
|
1654
1654
|
theme: Literal["streamlit"] | None = "streamlit",
|
1655
1655
|
key: Key | None = None,
|
1656
|
-
on_select: Literal["rerun"] | WidgetCallback
|
1656
|
+
on_select: Literal["rerun"] | WidgetCallback,
|
1657
1657
|
selection_mode: str | Iterable[str] | None = None,
|
1658
1658
|
**kwargs: Any,
|
1659
1659
|
) -> VegaLiteState: ...
|
@@ -127,7 +127,7 @@ def _process_avatar_input(
|
|
127
127
|
|
128
128
|
if avatar is None:
|
129
129
|
return AvatarType.ICON, ""
|
130
|
-
|
130
|
+
if isinstance(avatar, str) and avatar in {item.value for item in PresetNames}:
|
131
131
|
# On the frontend, we only support "assistant" and "user" for the avatar.
|
132
132
|
return (
|
133
133
|
AvatarType.ICON,
|
@@ -137,25 +137,24 @@ def _process_avatar_input(
|
|
137
137
|
else "user"
|
138
138
|
),
|
139
139
|
)
|
140
|
-
|
140
|
+
if isinstance(avatar, str) and is_emoji(avatar):
|
141
141
|
return AvatarType.EMOJI, avatar
|
142
142
|
|
143
|
-
|
143
|
+
if isinstance(avatar, str) and avatar.startswith(":material"):
|
144
144
|
return AvatarType.ICON, validate_material_icon(avatar)
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
) from ex
|
145
|
+
try:
|
146
|
+
return AvatarType.IMAGE, image_to_url(
|
147
|
+
avatar,
|
148
|
+
width=WidthBehavior.ORIGINAL,
|
149
|
+
clamp=False,
|
150
|
+
channels="RGB",
|
151
|
+
output_format="auto",
|
152
|
+
image_id=delta_path,
|
153
|
+
)
|
154
|
+
except Exception as ex:
|
155
|
+
raise StreamlitAPIException(
|
156
|
+
"Failed to load the provided avatar value as an image."
|
157
|
+
) from ex
|
159
158
|
|
160
159
|
|
161
160
|
def _pop_upload_files(
|
@@ -208,16 +207,15 @@ class ChatInputSerde:
|
|
208
207
|
return None
|
209
208
|
if not self.accept_files:
|
210
209
|
return ui_value.data
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
)
|
210
|
+
uploaded_files = _pop_upload_files(ui_value.file_uploader_state)
|
211
|
+
for file in uploaded_files:
|
212
|
+
if self.allowed_types and not isinstance(file, DeletedFile):
|
213
|
+
enforce_filename_restriction(file.name, self.allowed_types)
|
214
|
+
|
215
|
+
return ChatInputValue(
|
216
|
+
text=ui_value.data,
|
217
|
+
files=uploaded_files,
|
218
|
+
)
|
221
219
|
|
222
220
|
def serialize(self, v: str | None) -> ChatInputValueProto:
|
223
221
|
return ChatInputValueProto(data=v)
|
@@ -127,7 +127,7 @@ class FileUploaderSerde:
|
|
127
127
|
|
128
128
|
if not files:
|
129
129
|
return state_proto
|
130
|
-
|
130
|
+
if not isinstance(files, list):
|
131
131
|
files = [files]
|
132
132
|
|
133
133
|
for f in files:
|
@@ -170,7 +170,7 @@ class FileUploaderMixin:
|
|
170
170
|
disabled: bool = False,
|
171
171
|
label_visibility: LabelVisibility = "visible",
|
172
172
|
width: WidthWithoutContent = "stretch",
|
173
|
-
) -> list[UploadedFile]
|
173
|
+
) -> list[UploadedFile]: ...
|
174
174
|
|
175
175
|
# 1. type is given as not a keyword-only argument
|
176
176
|
# 2. accept_multiple_files = False or omitted
|
@@ -213,7 +213,7 @@ class FileUploaderMixin:
|
|
213
213
|
disabled: bool = False,
|
214
214
|
label_visibility: LabelVisibility = "visible",
|
215
215
|
width: WidthWithoutContent = "stretch",
|
216
|
-
) -> list[UploadedFile]
|
216
|
+
) -> list[UploadedFile]: ...
|
217
217
|
|
218
218
|
# 1. type is skipped or a keyword argument
|
219
219
|
# 2. accept_multiple_files = False or omitted
|
@@ -497,7 +497,7 @@ class FileUploaderMixin:
|
|
497
497
|
|
498
498
|
if isinstance(widget_state.value, DeletedFile):
|
499
499
|
return None
|
500
|
-
|
500
|
+
if isinstance(widget_state.value, list):
|
501
501
|
return [f for f in widget_state.value if not isinstance(f, DeletedFile)]
|
502
502
|
|
503
503
|
return widget_state.value
|
@@ -116,7 +116,7 @@ class NumberInputMixin:
|
|
116
116
|
def number_input(
|
117
117
|
self,
|
118
118
|
label: str,
|
119
|
-
min_value:
|
119
|
+
min_value: None = None,
|
120
120
|
*,
|
121
121
|
max_value: int,
|
122
122
|
value: IntOrNone | Literal["min"] = "min",
|
@@ -166,8 +166,8 @@ class NumberInputMixin:
|
|
166
166
|
def number_input(
|
167
167
|
self,
|
168
168
|
label: str,
|
169
|
-
min_value:
|
170
|
-
max_value:
|
169
|
+
min_value: None = None,
|
170
|
+
max_value: None = None,
|
171
171
|
value: IntOrNone | Literal["min"] = "min",
|
172
172
|
*,
|
173
173
|
step: int,
|
@@ -18,6 +18,8 @@ from dataclasses import dataclass
|
|
18
18
|
from textwrap import dedent
|
19
19
|
from typing import TYPE_CHECKING, Any, Callable, Generic, cast, overload
|
20
20
|
|
21
|
+
from typing_extensions import Never
|
22
|
+
|
21
23
|
from streamlit.dataframe_util import OptionSequence, convert_anything_to_list
|
22
24
|
from streamlit.elements.lib.form_utils import current_form_id
|
23
25
|
from streamlit.elements.lib.options_selector_utils import index_, maybe_coerce_enum
|
@@ -79,6 +81,25 @@ class RadioSerde(Generic[T]):
|
|
79
81
|
|
80
82
|
|
81
83
|
class RadioMixin:
|
84
|
+
@overload
|
85
|
+
def radio(
|
86
|
+
self,
|
87
|
+
label: str,
|
88
|
+
options: Sequence[Never],
|
89
|
+
index: int = 0,
|
90
|
+
format_func: Callable[[Any], Any] = str,
|
91
|
+
key: Key | None = None,
|
92
|
+
help: str | None = None,
|
93
|
+
on_change: WidgetCallback | None = None,
|
94
|
+
args: WidgetArgs | None = None,
|
95
|
+
kwargs: WidgetKwargs | None = None,
|
96
|
+
*, # keyword-only args:
|
97
|
+
disabled: bool = False,
|
98
|
+
horizontal: bool = False,
|
99
|
+
captions: Sequence[str] | None = None,
|
100
|
+
label_visibility: LabelVisibility = "visible",
|
101
|
+
) -> None: ...
|
102
|
+
|
82
103
|
@overload
|
83
104
|
def radio(
|
84
105
|
self,
|
@@ -341,12 +362,11 @@ class RadioMixin:
|
|
341
362
|
def handle_captions(caption: str | None) -> str:
|
342
363
|
if caption is None:
|
343
364
|
return ""
|
344
|
-
|
365
|
+
if isinstance(caption, str):
|
345
366
|
return caption
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
)
|
367
|
+
raise StreamlitAPIException(
|
368
|
+
f"Radio captions must be strings. Passed type: {type(caption).__name__}"
|
369
|
+
)
|
350
370
|
|
351
371
|
session_state = get_session_state().filtered_state
|
352
372
|
if key is not None and key in session_state and session_state[key] is None:
|
@@ -102,13 +102,12 @@ class SelectSliderSerde(Generic[T]):
|
|
102
102
|
if start > end:
|
103
103
|
slider_value = [end, start]
|
104
104
|
return slider_value
|
105
|
-
|
106
|
-
return [index_(self.options, v)]
|
105
|
+
return [index_(self.options, v)]
|
107
106
|
|
108
107
|
|
109
108
|
class SelectSliderMixin:
|
110
109
|
@overload
|
111
|
-
def select_slider(
|
110
|
+
def select_slider(
|
112
111
|
self,
|
113
112
|
label: str,
|
114
113
|
options: OptionSequence[T],
|
@@ -125,18 +124,6 @@ class SelectSliderMixin:
|
|
125
124
|
width: WidthWithoutContent = "stretch",
|
126
125
|
) -> tuple[T, T]: ...
|
127
126
|
|
128
|
-
# The overload-overlap error given by mypy here stems from
|
129
|
-
# the fact that
|
130
|
-
#
|
131
|
-
# > opt:List[object] = [1, 2, "3"]
|
132
|
-
# > select_slider("foo", options=opt, value=[1, 2])
|
133
|
-
#
|
134
|
-
# matches both overloads; "opt" matches
|
135
|
-
# OptionsSequence[T] in each case, binding T to object.
|
136
|
-
# However, the list[int] type of "value" can be interpreted
|
137
|
-
# as subtype of object, or as a subtype of List[object],
|
138
|
-
# meaning it matches both signatures.
|
139
|
-
|
140
127
|
@overload
|
141
128
|
def select_slider(
|
142
129
|
self,
|
@@ -367,15 +354,14 @@ class SelectSliderMixin:
|
|
367
354
|
if start > end:
|
368
355
|
slider_value = [end, start]
|
369
356
|
return slider_value
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
return [0]
|
357
|
+
# Simplify future logic by always making value a list
|
358
|
+
try:
|
359
|
+
return [index_(opt, v)]
|
360
|
+
except ValueError:
|
361
|
+
if value is not None:
|
362
|
+
raise
|
363
|
+
|
364
|
+
return [0]
|
379
365
|
|
380
366
|
# Convert element to index of the elements
|
381
367
|
slider_value = as_index_list(value)
|