streamlit-nightly 1.53.1.dev20260115__py3-none-any.whl → 1.53.1.dev20260116__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/auth_util.py +90 -1
- streamlit/cli_util.py +2 -1
- streamlit/commands/echo.py +2 -2
- streamlit/commands/execution_control.py +1 -1
- streamlit/commands/navigation.py +1 -1
- streamlit/components/types/base_custom_component.py +0 -2
- streamlit/components/v1/custom_component.py +0 -2
- streamlit/components/v2/component_path_utils.py +1 -1
- streamlit/components/v2/manifest_scanner.py +8 -3
- streamlit/components/v2/presentation.py +1 -1
- streamlit/config.py +15 -13
- streamlit/config_util.py +5 -5
- streamlit/dataframe_util.py +5 -5
- streamlit/elements/arrow.py +11 -6
- streamlit/elements/deck_gl_json_chart.py +1 -1
- streamlit/elements/exception.py +4 -2
- streamlit/elements/form.py +1 -1
- streamlit/elements/layouts.py +1 -1
- streamlit/elements/lib/built_in_chart_utils.py +7 -7
- streamlit/elements/lib/column_config_utils.py +6 -6
- streamlit/elements/lib/dialog.py +1 -1
- streamlit/elements/lib/image_utils.py +4 -4
- streamlit/elements/lib/layout_utils.py +1 -1
- streamlit/elements/lib/policies.py +1 -1
- streamlit/elements/lib/utils.py +1 -1
- streamlit/elements/map.py +6 -6
- streamlit/elements/plotly_chart.py +2 -2
- streamlit/elements/toast.py +1 -1
- streamlit/elements/vega_charts.py +2 -2
- streamlit/elements/widgets/button.py +3 -3
- streamlit/elements/widgets/button_group.py +3 -3
- streamlit/elements/widgets/chat.py +1 -1
- streamlit/elements/widgets/data_editor.py +6 -6
- streamlit/elements/widgets/number_input.py +1 -1
- streamlit/elements/widgets/slider.py +5 -5
- streamlit/elements/widgets/time_widgets.py +91 -10
- streamlit/elements/write.py +1 -1
- streamlit/env_util.py +1 -1
- streamlit/errors.py +0 -14
- streamlit/runtime/app_session.py +0 -1
- streamlit/runtime/caching/cache_data_api.py +3 -3
- streamlit/runtime/caching/cache_errors.py +0 -2
- streamlit/runtime/caching/cache_resource_api.py +1 -1
- streamlit/runtime/caching/cache_utils.py +2 -2
- streamlit/runtime/caching/hashing.py +1 -3
- streamlit/runtime/caching/storage/cache_storage_protocol.py +0 -3
- streamlit/runtime/connection_factory.py +1 -1
- streamlit/runtime/runtime.py +6 -6
- streamlit/runtime/scriptrunner_utils/exceptions.py +0 -4
- streamlit/runtime/secrets.py +2 -3
- streamlit/runtime/state/session_state.py +1 -1
- streamlit/runtime/stats.py +0 -7
- streamlit/string_util.py +2 -2
- streamlit/testing/v1/element_tree.py +8 -10
- streamlit/type_util.py +2 -2
- streamlit/url_util.py +2 -2
- streamlit/user_info.py +2 -2
- streamlit/util.py +1 -1
- streamlit/watcher/path_watcher.py +1 -1
- streamlit/web/cli.py +1 -4
- streamlit/web/server/app_discovery.py +2 -1
- streamlit/web/server/oauth_authlib_routes.py +14 -42
- streamlit/web/server/server.py +1 -1
- streamlit/web/server/server_util.py +1 -1
- streamlit/web/server/starlette/starlette_auth_routes.py +94 -16
- streamlit/web/server/starlette/starlette_routes.py +9 -3
- streamlit/web/server/starlette/starlette_server.py +2 -2
- {streamlit_nightly-1.53.1.dev20260115.dist-info → streamlit_nightly-1.53.1.dev20260116.dist-info}/METADATA +1 -1
- {streamlit_nightly-1.53.1.dev20260115.dist-info → streamlit_nightly-1.53.1.dev20260116.dist-info}/RECORD +73 -73
- {streamlit_nightly-1.53.1.dev20260115.data → streamlit_nightly-1.53.1.dev20260116.data}/scripts/streamlit.cmd +0 -0
- {streamlit_nightly-1.53.1.dev20260115.dist-info → streamlit_nightly-1.53.1.dev20260116.dist-info}/WHEEL +0 -0
- {streamlit_nightly-1.53.1.dev20260115.dist-info → streamlit_nightly-1.53.1.dev20260116.dist-info}/entry_points.txt +0 -0
- {streamlit_nightly-1.53.1.dev20260115.dist-info → streamlit_nightly-1.53.1.dev20260116.dist-info}/top_level.txt +0 -0
streamlit/elements/map.py
CHANGED
|
@@ -93,8 +93,8 @@ class MapMixin:
|
|
|
93
93
|
*,
|
|
94
94
|
latitude: str | None = None,
|
|
95
95
|
longitude: str | None = None,
|
|
96
|
-
color:
|
|
97
|
-
size:
|
|
96
|
+
color: str | Color | None = None,
|
|
97
|
+
size: str | float | None = None,
|
|
98
98
|
zoom: int | None = None,
|
|
99
99
|
width: WidthWithoutContent = "stretch",
|
|
100
100
|
height: HeightWithoutContent = 500,
|
|
@@ -296,8 +296,8 @@ def to_deckgl_json(
|
|
|
296
296
|
data: Data,
|
|
297
297
|
lat: str | None,
|
|
298
298
|
lon: str | None,
|
|
299
|
-
size:
|
|
300
|
-
color:
|
|
299
|
+
size: str | float | None,
|
|
300
|
+
color: str | Collection[float] | None,
|
|
301
301
|
zoom: int | None,
|
|
302
302
|
) -> str:
|
|
303
303
|
if data is None:
|
|
@@ -432,7 +432,7 @@ def _convert_color_arg_or_column(
|
|
|
432
432
|
data: DataFrame,
|
|
433
433
|
color_arg: str,
|
|
434
434
|
color_col_name: str | None,
|
|
435
|
-
) ->
|
|
435
|
+
) -> str | IntColorTuple | None:
|
|
436
436
|
"""Converts color to a format accepted by PyDeck.
|
|
437
437
|
|
|
438
438
|
For example:
|
|
@@ -443,7 +443,7 @@ def _convert_color_arg_or_column(
|
|
|
443
443
|
NOTE: This function mutates the data argument.
|
|
444
444
|
"""
|
|
445
445
|
|
|
446
|
-
color_arg_out:
|
|
446
|
+
color_arg_out: str | IntColorTuple | None = None
|
|
447
447
|
|
|
448
448
|
if color_col_name is not None:
|
|
449
449
|
# Convert color column to the right format.
|
|
@@ -677,14 +677,14 @@ class PlotlyMixin:
|
|
|
677
677
|
"options."
|
|
678
678
|
)
|
|
679
679
|
|
|
680
|
-
if theme not in
|
|
680
|
+
if theme not in {"streamlit", None}:
|
|
681
681
|
raise StreamlitAPIException(
|
|
682
682
|
f'You set theme="{theme}" while Streamlit charts only support '
|
|
683
683
|
"theme=”streamlit” or theme=None to fallback to the default "
|
|
684
684
|
"library theme."
|
|
685
685
|
)
|
|
686
686
|
|
|
687
|
-
if on_select not in
|
|
687
|
+
if on_select not in {"ignore", "rerun"} and not callable(on_select):
|
|
688
688
|
raise StreamlitAPIException(
|
|
689
689
|
f"You have passed {on_select} to `on_select`. But only 'ignore', "
|
|
690
690
|
"'rerun', or a callable is supported."
|
streamlit/elements/toast.py
CHANGED
|
@@ -151,7 +151,7 @@ class ToastMixin:
|
|
|
151
151
|
toast_proto.body = clean_text(validate_text(body))
|
|
152
152
|
toast_proto.icon = validate_icon_or_emoji(icon)
|
|
153
153
|
|
|
154
|
-
if duration in
|
|
154
|
+
if duration in {"short", "long", "infinite"} or (
|
|
155
155
|
isinstance(duration, int) and duration > 0
|
|
156
156
|
):
|
|
157
157
|
if duration == "short":
|
|
@@ -2282,14 +2282,14 @@ class VegaChartsMixin:
|
|
|
2282
2282
|
|
|
2283
2283
|
See the `vega_lite_chart` method docstring for more information.
|
|
2284
2284
|
"""
|
|
2285
|
-
if theme not in
|
|
2285
|
+
if theme not in {"streamlit", None}:
|
|
2286
2286
|
raise StreamlitAPIException(
|
|
2287
2287
|
f'You set theme="{theme}" while Streamlit charts only support '
|
|
2288
2288
|
"theme=”streamlit” or theme=None to fallback to the default "
|
|
2289
2289
|
"library theme."
|
|
2290
2290
|
)
|
|
2291
2291
|
|
|
2292
|
-
if on_select not in
|
|
2292
|
+
if on_select not in {"ignore", "rerun"} and not callable(on_select):
|
|
2293
2293
|
raise StreamlitAPIException(
|
|
2294
2294
|
f"You have passed {on_select} to `on_select`. But only 'ignore', "
|
|
2295
2295
|
"'rerun', or a callable is supported."
|
|
@@ -356,7 +356,7 @@ class ButtonMixin:
|
|
|
356
356
|
width = "stretch" if use_container_width else "content"
|
|
357
357
|
|
|
358
358
|
# Checks whether the entered button type is one of the allowed options
|
|
359
|
-
if type not in
|
|
359
|
+
if type not in {"primary", "secondary", "tertiary"}:
|
|
360
360
|
raise StreamlitAPIException(
|
|
361
361
|
'The type argument to st.button must be "primary", "secondary", or "tertiary". '
|
|
362
362
|
f'\nThe argument passed was "{type}".'
|
|
@@ -736,7 +736,7 @@ class ButtonMixin:
|
|
|
736
736
|
if use_container_width is not None:
|
|
737
737
|
width = "stretch" if use_container_width else "content"
|
|
738
738
|
|
|
739
|
-
if type not in
|
|
739
|
+
if type not in {"primary", "secondary", "tertiary"}:
|
|
740
740
|
raise StreamlitAPIException(
|
|
741
741
|
'The type argument to st.download_button must be "primary", "secondary", or "tertiary". \n'
|
|
742
742
|
f'The argument passed was "{type}".'
|
|
@@ -915,7 +915,7 @@ class ButtonMixin:
|
|
|
915
915
|
|
|
916
916
|
"""
|
|
917
917
|
# Checks whether the entered button type is one of the allowed options - either "primary" or "secondary"
|
|
918
|
-
if type not in
|
|
918
|
+
if type not in {"primary", "secondary", "tertiary"}:
|
|
919
919
|
raise StreamlitAPIException(
|
|
920
920
|
'The type argument to st.link_button must be "primary", "secondary", or "tertiary". '
|
|
921
921
|
f'\nThe argument passed was "{type}".'
|
|
@@ -255,7 +255,7 @@ def _build_proto(
|
|
|
255
255
|
|
|
256
256
|
def _maybe_raise_selection_mode_warning(selection_mode: SelectionMode) -> None:
|
|
257
257
|
"""Check if the selection_mode value is valid or raise exception otherwise."""
|
|
258
|
-
if selection_mode not in
|
|
258
|
+
if selection_mode not in {"single", "multi"}:
|
|
259
259
|
raise StreamlitAPIException(
|
|
260
260
|
"The selection_mode argument must be one of ['single', 'multi']. "
|
|
261
261
|
f"The argument passed was '{selection_mode}'."
|
|
@@ -410,7 +410,7 @@ class ButtonGroupMixin:
|
|
|
410
410
|
|
|
411
411
|
"""
|
|
412
412
|
|
|
413
|
-
if options not in
|
|
413
|
+
if options not in {"thumbs", "faces", "stars"}:
|
|
414
414
|
raise StreamlitAPIException(
|
|
415
415
|
"The options argument to st.feedback must be one of "
|
|
416
416
|
"['thumbs', 'faces', 'stars']. "
|
|
@@ -1062,7 +1062,7 @@ class ButtonGroupMixin:
|
|
|
1062
1062
|
"`selection_mode='single'`."
|
|
1063
1063
|
)
|
|
1064
1064
|
|
|
1065
|
-
if style not in
|
|
1065
|
+
if style not in {"borderless", "pills", "segmented_control"}:
|
|
1066
1066
|
raise StreamlitAPIException(
|
|
1067
1067
|
"The style argument must be one of ['borderless', 'pills', 'segmented_control']. "
|
|
1068
1068
|
f"The argument passed was '{style}'."
|
|
@@ -238,11 +238,11 @@ def _parse_value(
|
|
|
238
238
|
if column_data_kind == ColumnDataKind.TIMEDELTA:
|
|
239
239
|
return pd.Timedelta(value)
|
|
240
240
|
|
|
241
|
-
if column_data_kind in
|
|
241
|
+
if column_data_kind in {
|
|
242
242
|
ColumnDataKind.DATETIME,
|
|
243
243
|
ColumnDataKind.DATE,
|
|
244
244
|
ColumnDataKind.TIME,
|
|
245
|
-
|
|
245
|
+
}:
|
|
246
246
|
datetime_value = pd.Timestamp(value)
|
|
247
247
|
|
|
248
248
|
if pd.isna(datetime_value):
|
|
@@ -481,7 +481,7 @@ def _is_supported_index(df_index: pd.Index[Any]) -> bool:
|
|
|
481
481
|
|
|
482
482
|
return (
|
|
483
483
|
type(df_index)
|
|
484
|
-
in
|
|
484
|
+
in {
|
|
485
485
|
pd.RangeIndex,
|
|
486
486
|
pd.Index,
|
|
487
487
|
pd.DatetimeIndex,
|
|
@@ -490,7 +490,7 @@ def _is_supported_index(df_index: pd.Index[Any]) -> bool:
|
|
|
490
490
|
# pd.IntervalIndex,
|
|
491
491
|
# Period type isn't editable currently:
|
|
492
492
|
# pd.PeriodIndex,
|
|
493
|
-
|
|
493
|
+
}
|
|
494
494
|
# We need to check these index types without importing, since they are
|
|
495
495
|
# deprecated and planned to be removed soon.
|
|
496
496
|
or is_type(df_index, "pandas.core.indexes.numeric.Int64Index")
|
|
@@ -1029,7 +1029,7 @@ class DataEditorMixin:
|
|
|
1029
1029
|
update_column_config(
|
|
1030
1030
|
column_config_mapping, INDEX_IDENTIFIER, {"required": True}
|
|
1031
1031
|
)
|
|
1032
|
-
if num_rows in
|
|
1032
|
+
if num_rows in {"dynamic", "add"} and hide_index is True:
|
|
1033
1033
|
_LOGGER.warning(
|
|
1034
1034
|
"Setting `hide_index=True` in data editor with a non-range index will not have any effect "
|
|
1035
1035
|
"when `num_rows` is '%s'. It is required for the user to fill in index values for "
|
|
@@ -1038,7 +1038,7 @@ class DataEditorMixin:
|
|
|
1038
1038
|
num_rows,
|
|
1039
1039
|
)
|
|
1040
1040
|
|
|
1041
|
-
if hide_index is None and has_range_index and num_rows in
|
|
1041
|
+
if hide_index is None and has_range_index and num_rows in {"dynamic", "add"}:
|
|
1042
1042
|
# Temporary workaround:
|
|
1043
1043
|
# We hide range indices if num_rows allows adding rows.
|
|
1044
1044
|
# since the current way of handling this index during editing is a
|
|
@@ -512,7 +512,7 @@ class NumberInputMixin:
|
|
|
512
512
|
number_format = ("%d" if int_value else "%0.2f") if format is None else format
|
|
513
513
|
|
|
514
514
|
# Warn user if they format an int type as a float or vice versa.
|
|
515
|
-
if number_format in
|
|
515
|
+
if number_format in {"%d", "%u", "%i"} and float_value:
|
|
516
516
|
import streamlit as st
|
|
517
517
|
|
|
518
518
|
st.warning(
|
|
@@ -739,7 +739,7 @@ class SliderMixin:
|
|
|
739
739
|
|
|
740
740
|
# Ensure that the value is either a single value or a range of values.
|
|
741
741
|
single_value = isinstance(value, tuple(SUPPORTED_TYPES.keys()))
|
|
742
|
-
range_value = isinstance(value, (list, tuple)) and len(value) in
|
|
742
|
+
range_value = isinstance(value, (list, tuple)) and len(value) in {0, 1, 2}
|
|
743
743
|
if not single_value and not range_value:
|
|
744
744
|
raise StreamlitAPIException(
|
|
745
745
|
"Slider value should either be an int/float/datetime or a list/tuple of "
|
|
@@ -778,7 +778,7 @@ class SliderMixin:
|
|
|
778
778
|
|
|
779
779
|
datetime_min = time.min.replace(tzinfo=prepared_value[0].tzinfo)
|
|
780
780
|
datetime_max = time.max.replace(tzinfo=prepared_value[0].tzinfo)
|
|
781
|
-
if data_type in
|
|
781
|
+
if data_type in {SliderProto.DATETIME, SliderProto.DATE}:
|
|
782
782
|
prepared_value = cast("Sequence[datetime]", prepared_value)
|
|
783
783
|
|
|
784
784
|
datetime_min = prepared_value[0] - timedelta(days=14)
|
|
@@ -823,10 +823,10 @@ class SliderMixin:
|
|
|
823
823
|
max_value = defaults[data_type]["max_value"]
|
|
824
824
|
if step is None:
|
|
825
825
|
step = defaults[data_type]["step"]
|
|
826
|
-
if data_type in
|
|
826
|
+
if data_type in {
|
|
827
827
|
SliderProto.DATETIME,
|
|
828
828
|
SliderProto.DATE,
|
|
829
|
-
|
|
829
|
+
} and max_value - min_value < timedelta(days=1):
|
|
830
830
|
step = timedelta(minutes=15)
|
|
831
831
|
if format is None:
|
|
832
832
|
format = cast("str", defaults[data_type]["format"]) # noqa: A001
|
|
@@ -945,7 +945,7 @@ class SliderMixin:
|
|
|
945
945
|
# Restore times/datetimes to original timezone (dates are always naive)
|
|
946
946
|
orig_tz = (
|
|
947
947
|
prepared_value[0].tzinfo
|
|
948
|
-
if data_type in
|
|
948
|
+
if data_type in {SliderProto.TIME, SliderProto.DATETIME}
|
|
949
949
|
else None
|
|
950
950
|
)
|
|
951
951
|
|
|
@@ -472,6 +472,73 @@ class TimeInputSerde:
|
|
|
472
472
|
return time.strftime(v, "%H:%M")
|
|
473
473
|
|
|
474
474
|
|
|
475
|
+
def _validate_date_value(
|
|
476
|
+
current_value: DateWidgetReturn,
|
|
477
|
+
parsed_values: _DateInputValues,
|
|
478
|
+
has_explicit_bounds: bool,
|
|
479
|
+
) -> tuple[DateWidgetReturn, bool]:
|
|
480
|
+
"""Validate current value against min/max bounds and reset if needed.
|
|
481
|
+
|
|
482
|
+
Only validates when has_explicit_bounds is True (user provided min_value or max_value).
|
|
483
|
+
This avoids incorrectly resetting values against computed default bounds.
|
|
484
|
+
|
|
485
|
+
Parameters
|
|
486
|
+
----------
|
|
487
|
+
current_value : DateWidgetReturn
|
|
488
|
+
The current value of the date input widget. Can be a single date, a tuple of
|
|
489
|
+
dates (for range mode), or None.
|
|
490
|
+
parsed_values : _DateInputValues
|
|
491
|
+
Parsed configuration containing min, max, default value, and whether the widget
|
|
492
|
+
is in range mode.
|
|
493
|
+
has_explicit_bounds : bool
|
|
494
|
+
Whether the user explicitly provided min_value or max_value. If False, validation
|
|
495
|
+
is skipped to avoid resetting against computed default bounds.
|
|
496
|
+
|
|
497
|
+
Returns
|
|
498
|
+
-------
|
|
499
|
+
tuple[DateWidgetReturn, bool]
|
|
500
|
+
A tuple of (validated_value, was_reset) where validated_value is either the
|
|
501
|
+
original value (if valid) or the default value (if reset was needed), and
|
|
502
|
+
was_reset indicates whether a reset occurred.
|
|
503
|
+
"""
|
|
504
|
+
value_needs_reset = False
|
|
505
|
+
|
|
506
|
+
if current_value is None or not has_explicit_bounds:
|
|
507
|
+
return current_value, value_needs_reset
|
|
508
|
+
|
|
509
|
+
# For range inputs, current_value is a tuple; for single inputs, it's a date
|
|
510
|
+
if (
|
|
511
|
+
parsed_values.is_range
|
|
512
|
+
and isinstance(current_value, tuple)
|
|
513
|
+
and len(current_value) > 0
|
|
514
|
+
):
|
|
515
|
+
# For range mode, check if any date in the tuple is outside bounds.
|
|
516
|
+
# Cast to tuple[date, ...] to satisfy the type checker after the length check.
|
|
517
|
+
non_empty_value = cast("tuple[date, ...]", current_value)
|
|
518
|
+
start_date = non_empty_value[0]
|
|
519
|
+
end_date = non_empty_value[-1] if len(non_empty_value) > 1 else start_date
|
|
520
|
+
if start_date < parsed_values.min or end_date > parsed_values.max:
|
|
521
|
+
value_needs_reset = True
|
|
522
|
+
elif not parsed_values.is_range and isinstance(current_value, date):
|
|
523
|
+
# For single date mode
|
|
524
|
+
if current_value < parsed_values.min or current_value > parsed_values.max:
|
|
525
|
+
value_needs_reset = True
|
|
526
|
+
else:
|
|
527
|
+
# Type mismatch: widget mode doesn't match current value type (e.g., range mode
|
|
528
|
+
# with a single date value or single mode with a tuple). Reset to match the mode.
|
|
529
|
+
value_needs_reset = True
|
|
530
|
+
|
|
531
|
+
if not value_needs_reset:
|
|
532
|
+
return current_value, value_needs_reset
|
|
533
|
+
|
|
534
|
+
# Reset to the default value from parsed_values
|
|
535
|
+
if parsed_values.value is None or len(parsed_values.value) == 0:
|
|
536
|
+
return (() if parsed_values.is_range else None), True
|
|
537
|
+
if not parsed_values.is_range:
|
|
538
|
+
return parsed_values.value[0], True
|
|
539
|
+
return cast("DateWidgetReturn", tuple(parsed_values.value)), True
|
|
540
|
+
|
|
541
|
+
|
|
475
542
|
@dataclass
|
|
476
543
|
class DateInputSerde:
|
|
477
544
|
value: _DateInputValues
|
|
@@ -1467,7 +1534,7 @@ class TimeWidgetsMixin:
|
|
|
1467
1534
|
parsed_min_date = parse_date_deterministic_for_id(min_value)
|
|
1468
1535
|
parsed_max_date = parse_date_deterministic_for_id(max_value)
|
|
1469
1536
|
|
|
1470
|
-
parsed: str |
|
|
1537
|
+
parsed: str | list[str | None] | None
|
|
1471
1538
|
if value == "today":
|
|
1472
1539
|
parsed = None
|
|
1473
1540
|
elif isinstance(value, Sequence):
|
|
@@ -1480,12 +1547,10 @@ class TimeWidgetsMixin:
|
|
|
1480
1547
|
element_id = compute_and_register_element_id(
|
|
1481
1548
|
"date_input",
|
|
1482
1549
|
user_key=key,
|
|
1483
|
-
# Ensure stable ID when key is provided
|
|
1484
|
-
#
|
|
1485
|
-
#
|
|
1486
|
-
|
|
1487
|
-
# So, we whitelist it for now until we migrate this away from baseweb.
|
|
1488
|
-
key_as_main_identity={"min_value", "max_value", "format"},
|
|
1550
|
+
# Ensure stable ID when key is provided. Only format is whitelisted because
|
|
1551
|
+
# there is a bug in baseweb where changing the format dynamically leads to
|
|
1552
|
+
# a wrongly formatted date. min_value and max_value support dynamic changes.
|
|
1553
|
+
key_as_main_identity={"format"},
|
|
1489
1554
|
dg=self.dg,
|
|
1490
1555
|
label=label,
|
|
1491
1556
|
value=parsed,
|
|
@@ -1508,6 +1573,9 @@ class TimeWidgetsMixin:
|
|
|
1508
1573
|
max_value=max_value,
|
|
1509
1574
|
)
|
|
1510
1575
|
|
|
1576
|
+
# Track if user explicitly set bounds (before del)
|
|
1577
|
+
has_explicit_bounds = min_value is not None or max_value is not None
|
|
1578
|
+
|
|
1511
1579
|
if value == "today":
|
|
1512
1580
|
# We need to know if this is a single or range date_input, but don't have
|
|
1513
1581
|
# a default value, so we check if session_state can tell us.
|
|
@@ -1564,15 +1632,28 @@ class TimeWidgetsMixin:
|
|
|
1564
1632
|
value_type="string_array_value",
|
|
1565
1633
|
)
|
|
1566
1634
|
|
|
1567
|
-
|
|
1568
|
-
|
|
1635
|
+
# Validate the current value against the new min/max bounds.
|
|
1636
|
+
# Only validate when user explicitly provided min_value or max_value.
|
|
1637
|
+
current_value, value_needs_reset = _validate_date_value(
|
|
1638
|
+
widget_state.value, parsed_values, has_explicit_bounds
|
|
1639
|
+
)
|
|
1640
|
+
|
|
1641
|
+
# Reset if needed.
|
|
1642
|
+
if value_needs_reset and key is not None:
|
|
1643
|
+
# Update session_state so subsequent accesses in this run
|
|
1644
|
+
# return the corrected value. Use reset_state_value to avoid
|
|
1645
|
+
# the "cannot be modified after widget instantiated" error.
|
|
1646
|
+
get_session_state().reset_state_value(key, current_value)
|
|
1647
|
+
|
|
1648
|
+
if value_needs_reset or widget_state.value_changed:
|
|
1649
|
+
date_input_proto.value[:] = serde.serialize(current_value)
|
|
1569
1650
|
date_input_proto.set_value = True
|
|
1570
1651
|
|
|
1571
1652
|
validate_width(width)
|
|
1572
1653
|
layout_config = LayoutConfig(width=width)
|
|
1573
1654
|
|
|
1574
1655
|
self.dg._enqueue("date_input", date_input_proto, layout_config=layout_config)
|
|
1575
|
-
return
|
|
1656
|
+
return current_value
|
|
1576
1657
|
|
|
1577
1658
|
@property
|
|
1578
1659
|
def dg(self) -> DeltaGenerator:
|
streamlit/elements/write.py
CHANGED
streamlit/env_util.py
CHANGED
|
@@ -46,7 +46,7 @@ def is_repl() -> bool:
|
|
|
46
46
|
|
|
47
47
|
# <stdin> is what the basic Python REPL calls the root frame's
|
|
48
48
|
# filename, and <string> is what iPython sometimes calls it.
|
|
49
|
-
return filename in
|
|
49
|
+
return filename in {"<stdin>", "<string>"}
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
def is_executable_in_path(name: str) -> bool:
|
streamlit/errors.py
CHANGED
|
@@ -34,14 +34,10 @@ class Error(Exception):
|
|
|
34
34
|
code.
|
|
35
35
|
"""
|
|
36
36
|
|
|
37
|
-
pass
|
|
38
|
-
|
|
39
37
|
|
|
40
38
|
class CustomComponentError(Error):
|
|
41
39
|
"""Exceptions thrown in the custom components code path."""
|
|
42
40
|
|
|
43
|
-
pass
|
|
44
|
-
|
|
45
41
|
|
|
46
42
|
class StreamlitComponentRegistryError(Error):
|
|
47
43
|
"""Exceptions raised while discovering or registering Streamlit components.
|
|
@@ -51,8 +47,6 @@ class StreamlitComponentRegistryError(Error):
|
|
|
51
47
|
registry.
|
|
52
48
|
"""
|
|
53
49
|
|
|
54
|
-
pass
|
|
55
|
-
|
|
56
50
|
|
|
57
51
|
class DeprecationError(Error):
|
|
58
52
|
pass
|
|
@@ -63,16 +57,12 @@ class FragmentStorageKeyError(Error, KeyError):
|
|
|
63
57
|
operation.
|
|
64
58
|
"""
|
|
65
59
|
|
|
66
|
-
pass
|
|
67
|
-
|
|
68
60
|
|
|
69
61
|
class FragmentHandledException(Exception): # noqa: N818
|
|
70
62
|
"""An exception that is raised by the fragment
|
|
71
63
|
when it has handled the exception itself.
|
|
72
64
|
"""
|
|
73
65
|
|
|
74
|
-
pass
|
|
75
|
-
|
|
76
66
|
|
|
77
67
|
class NoStaticFiles(Error): # noqa: N818
|
|
78
68
|
pass
|
|
@@ -89,14 +79,10 @@ class MarkdownFormattedException(Error): # noqa: N818
|
|
|
89
79
|
nicely formatted on the frontend.
|
|
90
80
|
"""
|
|
91
81
|
|
|
92
|
-
pass
|
|
93
|
-
|
|
94
82
|
|
|
95
83
|
class StreamlitMaxRetriesError(Error):
|
|
96
84
|
"""An exception raised when a file or folder cannot be accessed after multiple retries."""
|
|
97
85
|
|
|
98
|
-
pass
|
|
99
|
-
|
|
100
86
|
|
|
101
87
|
class StreamlitAPIException(MarkdownFormattedException):
|
|
102
88
|
"""Base class for Streamlit API exceptions.
|
streamlit/runtime/app_session.py
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
|
-
import pickle
|
|
19
|
+
import pickle # noqa: S403
|
|
20
20
|
import threading
|
|
21
21
|
from typing import (
|
|
22
22
|
TYPE_CHECKING,
|
|
@@ -654,13 +654,13 @@ class CacheDataAPI:
|
|
|
654
654
|
else:
|
|
655
655
|
persist_string = persist
|
|
656
656
|
|
|
657
|
-
if persist_string not in
|
|
657
|
+
if persist_string not in {None, "disk"}:
|
|
658
658
|
# We'll eventually have more persist options.
|
|
659
659
|
raise StreamlitAPIException(
|
|
660
660
|
f"Unsupported persist option '{persist}'. Valid values are 'disk' or None."
|
|
661
661
|
)
|
|
662
662
|
|
|
663
|
-
if scope not in
|
|
663
|
+
if scope not in {"global", "session"}:
|
|
664
664
|
raise StreamlitAPIException(
|
|
665
665
|
f"Unsupported scope option '{scope}'. Valid values are 'global' or 'session'."
|
|
666
666
|
)
|
|
@@ -592,10 +592,10 @@ def _get_positional_arg_name(func: Callable[..., Any], arg_index: int) -> str |
|
|
|
592
592
|
if arg_index >= len(params):
|
|
593
593
|
return None
|
|
594
594
|
|
|
595
|
-
if params[arg_index].kind in
|
|
595
|
+
if params[arg_index].kind in {
|
|
596
596
|
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
597
597
|
inspect.Parameter.POSITIONAL_ONLY,
|
|
598
|
-
|
|
598
|
+
}:
|
|
599
599
|
return params[arg_index].name
|
|
600
600
|
|
|
601
601
|
return None
|
|
@@ -25,7 +25,7 @@ import hashlib
|
|
|
25
25
|
import inspect
|
|
26
26
|
import io
|
|
27
27
|
import os
|
|
28
|
-
import pickle
|
|
28
|
+
import pickle # noqa: S403
|
|
29
29
|
import sys
|
|
30
30
|
import tempfile
|
|
31
31
|
import threading
|
|
@@ -653,5 +653,3 @@ class _CacheFuncHasher:
|
|
|
653
653
|
|
|
654
654
|
class NoResult:
|
|
655
655
|
"""Placeholder class for return values when None is meaningful."""
|
|
656
|
-
|
|
657
|
-
pass
|
|
@@ -156,7 +156,6 @@ class CacheStorage(Protocol):
|
|
|
156
156
|
to close open resources, before we delete the storage instance.
|
|
157
157
|
e.g. close the database connection etc.
|
|
158
158
|
"""
|
|
159
|
-
pass
|
|
160
159
|
|
|
161
160
|
|
|
162
161
|
class CacheStorageManager(Protocol):
|
|
@@ -235,5 +234,3 @@ class CacheStorageManager(Protocol):
|
|
|
235
234
|
-----
|
|
236
235
|
Threading: Should be safe to call from any thread.
|
|
237
236
|
"""
|
|
238
|
-
|
|
239
|
-
pass
|
|
@@ -99,7 +99,7 @@ def _create_connection(
|
|
|
99
99
|
)
|
|
100
100
|
|
|
101
101
|
scope = connection_class.scope()
|
|
102
|
-
if scope not in
|
|
102
|
+
if scope not in {"global", "session"}:
|
|
103
103
|
raise StreamlitAPIException(
|
|
104
104
|
f"Connection class {connection_class} has scope '{scope}'. Valid values "
|
|
105
105
|
"are 'global' or 'session'."
|
streamlit/runtime/runtime.py
CHANGED
|
@@ -345,7 +345,7 @@ class Runtime:
|
|
|
345
345
|
async_objs = self._get_async_objs()
|
|
346
346
|
|
|
347
347
|
def stop_on_eventloop() -> None:
|
|
348
|
-
if self._state in
|
|
348
|
+
if self._state in {RuntimeState.STOPPING, RuntimeState.STOPPED}:
|
|
349
349
|
return
|
|
350
350
|
|
|
351
351
|
_LOGGER.debug("Runtime stopping...")
|
|
@@ -411,7 +411,7 @@ class Runtime:
|
|
|
411
411
|
"This should never happen."
|
|
412
412
|
)
|
|
413
413
|
|
|
414
|
-
if self._state in
|
|
414
|
+
if self._state in {RuntimeState.STOPPING, RuntimeState.STOPPED}:
|
|
415
415
|
raise RuntimeStoppedError(f"Can't connect_session (state={self._state})")
|
|
416
416
|
|
|
417
417
|
session_id = self._session_mgr.connect_session(
|
|
@@ -510,7 +510,7 @@ class Runtime:
|
|
|
510
510
|
-----
|
|
511
511
|
Threading: UNSAFE. Must be called on the eventloop thread.
|
|
512
512
|
"""
|
|
513
|
-
if self._state in
|
|
513
|
+
if self._state in {RuntimeState.STOPPING, RuntimeState.STOPPED}:
|
|
514
514
|
raise RuntimeStoppedError(f"Can't handle_backmsg (state={self._state})")
|
|
515
515
|
|
|
516
516
|
session_info = self._session_mgr.get_active_session_info(session_id)
|
|
@@ -538,7 +538,7 @@ class Runtime:
|
|
|
538
538
|
-----
|
|
539
539
|
Threading: UNSAFE. Must be called on the eventloop thread.
|
|
540
540
|
"""
|
|
541
|
-
if self._state in
|
|
541
|
+
if self._state in {RuntimeState.STOPPING, RuntimeState.STOPPED}:
|
|
542
542
|
raise RuntimeStoppedError(
|
|
543
543
|
f"Can't handle_backmsg_deserialization_exception (state={self._state})"
|
|
544
544
|
)
|
|
@@ -555,11 +555,11 @@ class Runtime:
|
|
|
555
555
|
|
|
556
556
|
@property
|
|
557
557
|
async def is_ready_for_browser_connection(self) -> tuple[bool, str]:
|
|
558
|
-
if self._state not in
|
|
558
|
+
if self._state not in {
|
|
559
559
|
RuntimeState.INITIAL,
|
|
560
560
|
RuntimeState.STOPPING,
|
|
561
561
|
RuntimeState.STOPPED,
|
|
562
|
-
|
|
562
|
+
}:
|
|
563
563
|
return True, "ok"
|
|
564
564
|
|
|
565
565
|
return False, "unavailable"
|
|
@@ -22,14 +22,10 @@ from streamlit.util import repr_
|
|
|
22
22
|
class ScriptControlException(BaseException): # NOSONAR
|
|
23
23
|
"""Base exception for ScriptRunner."""
|
|
24
24
|
|
|
25
|
-
pass
|
|
26
|
-
|
|
27
25
|
|
|
28
26
|
class StopException(ScriptControlException):
|
|
29
27
|
"""Silently stop the execution of the user's script."""
|
|
30
28
|
|
|
31
|
-
pass
|
|
32
|
-
|
|
33
29
|
|
|
34
30
|
class RerunException(ScriptControlException):
|
|
35
31
|
"""Silently stop and rerun the user's script."""
|