streamlit-nightly 1.38.1.dev20240905__py2.py3-none-any.whl → 1.38.1.dev20240906__py2.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/page_config.py +12 -14
- streamlit/elements/layouts.py +10 -14
- streamlit/elements/lib/policies.py +11 -12
- streamlit/elements/widgets/button.py +16 -9
- streamlit/elements/widgets/multiselect.py +5 -15
- streamlit/elements/widgets/number_input.py +22 -26
- streamlit/errors.py +274 -2
- streamlit/runtime/pages_manager.py +4 -0
- streamlit/runtime/scriptrunner_utils/script_run_context.py +6 -7
- streamlit/static/asset-manifest.json +3 -3
- streamlit/static/index.html +1 -1
- streamlit/static/static/js/5180.5e064ef1.chunk.js +1 -0
- streamlit/static/static/js/{main.7feaefcb.js → main.abc0ee04.js} +2 -2
- streamlit/testing/v1/local_script_runner.py +1 -1
- {streamlit_nightly-1.38.1.dev20240905.dist-info → streamlit_nightly-1.38.1.dev20240906.dist-info}/METADATA +1 -1
- {streamlit_nightly-1.38.1.dev20240905.dist-info → streamlit_nightly-1.38.1.dev20240906.dist-info}/RECORD +21 -21
- streamlit/static/static/js/5281.1a0ea2eb.chunk.js +0 -1
- /streamlit/static/static/js/{main.7feaefcb.js.LICENSE.txt → main.abc0ee04.js.LICENSE.txt} +0 -0
- {streamlit_nightly-1.38.1.dev20240905.data → streamlit_nightly-1.38.1.dev20240906.data}/scripts/streamlit.cmd +0 -0
- {streamlit_nightly-1.38.1.dev20240905.dist-info → streamlit_nightly-1.38.1.dev20240906.dist-info}/WHEEL +0 -0
- {streamlit_nightly-1.38.1.dev20240905.dist-info → streamlit_nightly-1.38.1.dev20240906.dist-info}/entry_points.txt +0 -0
- {streamlit_nightly-1.38.1.dev20240905.dist-info → streamlit_nightly-1.38.1.dev20240906.dist-info}/top_level.txt +0 -0
@@ -21,7 +21,12 @@ from typing import TYPE_CHECKING, Final, Literal, Mapping, Union, cast
|
|
21
21
|
from typing_extensions import TypeAlias
|
22
22
|
|
23
23
|
from streamlit.elements import image
|
24
|
-
from streamlit.errors import
|
24
|
+
from streamlit.errors import (
|
25
|
+
StreamlitInvalidMenuItemKeyError,
|
26
|
+
StreamlitInvalidPageLayoutError,
|
27
|
+
StreamlitInvalidSidebarStateError,
|
28
|
+
StreamlitInvalidURLError,
|
29
|
+
)
|
25
30
|
from streamlit.proto.ForwardMsg_pb2 import ForwardMsg as ForwardProto
|
26
31
|
from streamlit.proto.PageConfig_pb2 import PageConfig as PageConfigProto
|
27
32
|
from streamlit.runtime.metrics_util import gather_metrics
|
@@ -227,9 +232,8 @@ def set_page_config(
|
|
227
232
|
elif layout == "wide":
|
228
233
|
pb_layout = PageConfigProto.WIDE
|
229
234
|
else:
|
230
|
-
raise
|
231
|
-
|
232
|
-
)
|
235
|
+
raise StreamlitInvalidPageLayoutError(layout=layout)
|
236
|
+
|
233
237
|
msg.page_config_changed.layout = pb_layout
|
234
238
|
|
235
239
|
pb_sidebar_state: PageConfigProto.SidebarState.ValueType
|
@@ -240,10 +244,8 @@ def set_page_config(
|
|
240
244
|
elif initial_sidebar_state == "collapsed":
|
241
245
|
pb_sidebar_state = PageConfigProto.COLLAPSED
|
242
246
|
else:
|
243
|
-
raise
|
244
|
-
|
245
|
-
'"auto" or "expanded" or "collapsed" '
|
246
|
-
f'(got "{initial_sidebar_state}")'
|
247
|
+
raise StreamlitInvalidSidebarStateError(
|
248
|
+
initial_sidebar_state=initial_sidebar_state
|
247
249
|
)
|
248
250
|
|
249
251
|
msg.page_config_changed.initial_sidebar_state = pb_sidebar_state
|
@@ -287,15 +289,11 @@ def set_menu_items_proto(lowercase_menu_items, menu_items_proto) -> None:
|
|
287
289
|
def validate_menu_items(menu_items: MenuItems) -> None:
|
288
290
|
for k, v in menu_items.items():
|
289
291
|
if not valid_menu_item_key(k):
|
290
|
-
raise
|
291
|
-
"We only accept the keys: "
|
292
|
-
'"Get help", "Report a bug", and "About" '
|
293
|
-
f'("{k}" is not a valid key.)'
|
294
|
-
)
|
292
|
+
raise StreamlitInvalidMenuItemKeyError(key=k)
|
295
293
|
if v is not None and (
|
296
294
|
not is_url(v, ("http", "https", "mailto")) and k != ABOUT_KEY
|
297
295
|
):
|
298
|
-
raise
|
296
|
+
raise StreamlitInvalidURLError(url=v)
|
299
297
|
|
300
298
|
|
301
299
|
def valid_menu_item_key(key: str) -> TypeGuard[MenuKey]:
|
streamlit/elements/layouts.py
CHANGED
@@ -20,7 +20,12 @@ from typing_extensions import TypeAlias
|
|
20
20
|
|
21
21
|
from streamlit.delta_generator_singletons import get_dg_singleton_instance
|
22
22
|
from streamlit.elements.lib.utils import Key, compute_and_register_element_id, to_key
|
23
|
-
from streamlit.errors import
|
23
|
+
from streamlit.errors import (
|
24
|
+
StreamlitAPIException,
|
25
|
+
StreamlitInvalidColumnGapError,
|
26
|
+
StreamlitInvalidColumnSpecError,
|
27
|
+
StreamlitInvalidVerticalAlignmentError,
|
28
|
+
)
|
24
29
|
from streamlit.proto.Block_pb2 import Block as BlockProto
|
25
30
|
from streamlit.runtime.metrics_util import gather_metrics
|
26
31
|
from streamlit.string_util import validate_icon_or_emoji
|
@@ -294,12 +299,7 @@ class LayoutsMixin:
|
|
294
299
|
weights = (1,) * weights
|
295
300
|
|
296
301
|
if len(weights) == 0 or any(weight <= 0 for weight in weights):
|
297
|
-
raise
|
298
|
-
"The input argument to st.columns must be either a "
|
299
|
-
"positive integer or a list of positive numeric weights. "
|
300
|
-
"See [documentation](https://docs.streamlit.io/develop/api-reference/layout/st.columns) "
|
301
|
-
"for more information."
|
302
|
-
)
|
302
|
+
raise StreamlitInvalidColumnSpecError()
|
303
303
|
|
304
304
|
vertical_alignment_mapping: dict[
|
305
305
|
str, BlockProto.Column.VerticalAlignment.ValueType
|
@@ -310,9 +310,8 @@ class LayoutsMixin:
|
|
310
310
|
}
|
311
311
|
|
312
312
|
if vertical_alignment not in vertical_alignment_mapping:
|
313
|
-
raise
|
314
|
-
|
315
|
-
f"The argument passed was {vertical_alignment}."
|
313
|
+
raise StreamlitInvalidVerticalAlignmentError(
|
314
|
+
vertical_alignment=vertical_alignment
|
316
315
|
)
|
317
316
|
|
318
317
|
def column_gap(gap):
|
@@ -323,10 +322,7 @@ class LayoutsMixin:
|
|
323
322
|
if gap_size in valid_sizes:
|
324
323
|
return gap_size
|
325
324
|
|
326
|
-
raise
|
327
|
-
'The gap argument to st.columns must be "small", "medium", or "large". \n'
|
328
|
-
f"The argument passed was {gap}."
|
329
|
-
)
|
325
|
+
raise StreamlitInvalidColumnGapError(gap=gap)
|
330
326
|
|
331
327
|
gap_size = column_gap(gap)
|
332
328
|
|
@@ -18,7 +18,12 @@ from typing import TYPE_CHECKING, Any, Final, Sequence
|
|
18
18
|
|
19
19
|
from streamlit import config, errors, logger, runtime
|
20
20
|
from streamlit.elements.form_utils import is_in_form
|
21
|
-
from streamlit.errors import
|
21
|
+
from streamlit.errors import (
|
22
|
+
StreamlitAPIWarning,
|
23
|
+
StreamlitFragmentWidgetsNotAllowedOutsideError,
|
24
|
+
StreamlitInvalidFormCallbackError,
|
25
|
+
StreamlitValueAssignmentNotAllowedError,
|
26
|
+
)
|
22
27
|
from streamlit.runtime.scriptrunner_utils.script_run_context import (
|
23
28
|
get_script_run_ctx,
|
24
29
|
in_cached_function,
|
@@ -38,15 +43,12 @@ def check_callback_rules(dg: DeltaGenerator, on_change: WidgetCallback | None) -
|
|
38
43
|
|
39
44
|
Raises
|
40
45
|
------
|
41
|
-
|
46
|
+
StreamlitInvalidFormCallbackError:
|
42
47
|
Raised when the described rule is violated.
|
43
48
|
"""
|
44
49
|
|
45
50
|
if runtime.exists() and is_in_form(dg) and on_change is not None:
|
46
|
-
raise
|
47
|
-
"With forms, callbacks can only be defined on the `st.form_submit_button`."
|
48
|
-
" Defining callbacks on other widgets inside a form is not allowed."
|
49
|
-
)
|
51
|
+
raise StreamlitInvalidFormCallbackError()
|
50
52
|
|
51
53
|
|
52
54
|
_shown_default_value_warning: bool = False
|
@@ -76,10 +78,7 @@ def check_session_state_rules(
|
|
76
78
|
return
|
77
79
|
|
78
80
|
if not writes_allowed:
|
79
|
-
raise
|
80
|
-
f"Values for the widget with key '{key}' cannot be set using"
|
81
|
-
" `st.session_state`."
|
82
|
-
)
|
81
|
+
raise StreamlitValueAssignmentNotAllowedError(key=key)
|
83
82
|
|
84
83
|
if (
|
85
84
|
default_value is not None
|
@@ -155,13 +154,13 @@ def check_fragment_path_policy(dg: DeltaGenerator):
|
|
155
154
|
# the elements delta path cannot be smaller than the fragment's delta path if it is
|
156
155
|
# inside of the fragment
|
157
156
|
if len(current_cursor_delta_path) < len(current_fragment_delta_path):
|
158
|
-
raise
|
157
|
+
raise StreamlitFragmentWidgetsNotAllowedOutsideError()
|
159
158
|
|
160
159
|
# all path indices of the fragment-path must occur in the inner-elements delta path,
|
161
160
|
# otherwise it is outside of the fragment container
|
162
161
|
for index, path_index in enumerate(current_fragment_delta_path):
|
163
162
|
if current_cursor_delta_path[index] != path_index:
|
164
|
-
raise
|
163
|
+
raise StreamlitFragmentWidgetsNotAllowedOutsideError()
|
165
164
|
|
166
165
|
|
167
166
|
def check_widget_policies(
|
@@ -34,7 +34,11 @@ from streamlit import runtime
|
|
34
34
|
from streamlit.elements.form_utils import current_form_id, is_in_form
|
35
35
|
from streamlit.elements.lib.policies import check_widget_policies
|
36
36
|
from streamlit.elements.lib.utils import Key, compute_and_register_element_id, to_key
|
37
|
-
from streamlit.errors import
|
37
|
+
from streamlit.errors import (
|
38
|
+
StreamlitAPIException,
|
39
|
+
StreamlitMissingPageLabelError,
|
40
|
+
StreamlitPageNotFoundError,
|
41
|
+
)
|
38
42
|
from streamlit.file_util import get_main_script_directory, normalize_path_join
|
39
43
|
from streamlit.navigation.page import StreamlitPage
|
40
44
|
from streamlit.proto.Button_pb2 import Button as ButtonProto
|
@@ -776,9 +780,7 @@ class ButtonMixin:
|
|
776
780
|
# Handle external links:
|
777
781
|
if is_url(page):
|
778
782
|
if label is None or label == "":
|
779
|
-
raise
|
780
|
-
"The label param is required for external links used with st.page_link - please provide a label."
|
781
|
-
)
|
783
|
+
raise StreamlitMissingPageLabelError()
|
782
784
|
else:
|
783
785
|
page_link_proto.page = page
|
784
786
|
page_link_proto.external = True
|
@@ -808,11 +810,16 @@ class ButtonMixin:
|
|
808
810
|
break
|
809
811
|
|
810
812
|
if page_link_proto.page_script_hash == "":
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
813
|
+
is_mpa_v2 = (
|
814
|
+
ctx is not None
|
815
|
+
and ctx.pages_manager is not None
|
816
|
+
and ctx.pages_manager.mpa_version == 2
|
817
|
+
)
|
818
|
+
|
819
|
+
raise StreamlitPageNotFoundError(
|
820
|
+
is_mpa_v2=is_mpa_v2,
|
821
|
+
page=page,
|
822
|
+
main_script_directory=main_script_directory,
|
816
823
|
)
|
817
824
|
|
818
825
|
return self.dg._enqueue("page_link", page_link_proto)
|
@@ -37,7 +37,9 @@ from streamlit.elements.lib.utils import (
|
|
37
37
|
maybe_coerce_enum_sequence,
|
38
38
|
to_key,
|
39
39
|
)
|
40
|
-
from streamlit.errors import
|
40
|
+
from streamlit.errors import (
|
41
|
+
StreamlitSelectionCountExceedsMaxError,
|
42
|
+
)
|
41
43
|
from streamlit.proto.MultiSelect_pb2 import MultiSelect as MultiSelectProto
|
42
44
|
from streamlit.runtime.metrics_util import gather_metrics
|
43
45
|
from streamlit.runtime.scriptrunner import ScriptRunContext, get_script_run_ctx
|
@@ -80,18 +82,6 @@ class MultiSelectSerde(Generic[T]):
|
|
80
82
|
return [self.options[i] for i in current_value]
|
81
83
|
|
82
84
|
|
83
|
-
def _get_over_max_options_message(current_selections: int, max_selections: int):
|
84
|
-
curr_selections_noun = "option" if current_selections == 1 else "options"
|
85
|
-
max_selections_noun = "option" if max_selections == 1 else "options"
|
86
|
-
return f"""
|
87
|
-
Multiselect has {current_selections} {curr_selections_noun} selected but `max_selections`
|
88
|
-
is set to {max_selections}. This happened because you either gave too many options to `default`
|
89
|
-
or you manipulated the widget's state through `st.session_state`. Note that
|
90
|
-
the latter can happen before the line indicated in the traceback.
|
91
|
-
Please select at most {max_selections} {max_selections_noun}.
|
92
|
-
"""
|
93
|
-
|
94
|
-
|
95
85
|
def _get_default_count(default: Sequence[Any] | Any | None) -> int:
|
96
86
|
if default is None:
|
97
87
|
return 0
|
@@ -108,8 +98,8 @@ def _check_max_selections(
|
|
108
98
|
|
109
99
|
default_count = _get_default_count(selections)
|
110
100
|
if default_count > max_selections:
|
111
|
-
raise
|
112
|
-
|
101
|
+
raise StreamlitSelectionCountExceedsMaxError(
|
102
|
+
current_selections_count=default_count, max_selections_count=max_selections
|
113
103
|
)
|
114
104
|
|
115
105
|
|
@@ -33,7 +33,13 @@ from streamlit.elements.lib.utils import (
|
|
33
33
|
get_label_visibility_proto_value,
|
34
34
|
to_key,
|
35
35
|
)
|
36
|
-
from streamlit.errors import
|
36
|
+
from streamlit.errors import (
|
37
|
+
StreamlitInvalidNumberFormatError,
|
38
|
+
StreamlitJSNumberBoundsError,
|
39
|
+
StreamlitMixedNumericTypesError,
|
40
|
+
StreamlitValueAboveMaxError,
|
41
|
+
StreamlitValueBelowMinError,
|
42
|
+
)
|
37
43
|
from streamlit.js_number import JSNumber, JSNumberBoundsException
|
38
44
|
from streamlit.proto.NumberInput_pb2 import NumberInput as NumberInputProto
|
39
45
|
from streamlit.runtime.metrics_util import gather_metrics
|
@@ -369,23 +375,17 @@ class NumberInputMixin:
|
|
369
375
|
# Ensure that all arguments are of the same type.
|
370
376
|
number_input_args = [min_value, max_value, value, step]
|
371
377
|
|
372
|
-
|
378
|
+
all_int_args = all(
|
373
379
|
isinstance(a, (numbers.Integral, type(None), str))
|
374
380
|
for a in number_input_args
|
375
381
|
)
|
376
382
|
|
377
|
-
|
383
|
+
all_float_args = all(
|
378
384
|
isinstance(a, (float, type(None), str)) for a in number_input_args
|
379
385
|
)
|
380
386
|
|
381
|
-
if not
|
382
|
-
raise
|
383
|
-
"All numerical arguments must be of the same type."
|
384
|
-
f"\n`value` has {type(value).__name__} type."
|
385
|
-
f"\n`min_value` has {type(min_value).__name__} type."
|
386
|
-
f"\n`max_value` has {type(max_value).__name__} type."
|
387
|
-
f"\n`step` has {type(step).__name__} type."
|
388
|
-
)
|
387
|
+
if not all_int_args and not all_float_args:
|
388
|
+
raise StreamlitMixedNumericTypesError(value=value, min_value=min_value, max_value=max_value, step=step)
|
389
389
|
|
390
390
|
session_state = get_session_state().filtered_state
|
391
391
|
if key is not None and key in session_state and session_state[key] is None:
|
@@ -394,9 +394,9 @@ class NumberInputMixin:
|
|
394
394
|
if value == "min":
|
395
395
|
if min_value is not None:
|
396
396
|
value = min_value
|
397
|
-
elif
|
397
|
+
elif all_int_args and all_float_args:
|
398
398
|
value = 0.0 # if no values are provided, defaults to float
|
399
|
-
elif
|
399
|
+
elif all_int_args:
|
400
400
|
value = 0
|
401
401
|
else:
|
402
402
|
value = 0.0
|
@@ -405,7 +405,7 @@ class NumberInputMixin:
|
|
405
405
|
float_value = isinstance(value, float)
|
406
406
|
|
407
407
|
if value is None:
|
408
|
-
if
|
408
|
+
if all_int_args and not all_float_args:
|
409
409
|
# Select int type if all relevant args are ints:
|
410
410
|
int_value = True
|
411
411
|
else:
|
@@ -437,22 +437,18 @@ class NumberInputMixin:
|
|
437
437
|
try:
|
438
438
|
float(format % 2)
|
439
439
|
except (TypeError, ValueError):
|
440
|
-
raise
|
441
|
-
|
442
|
-
% format
|
443
|
-
)
|
440
|
+
raise StreamlitInvalidNumberFormatError(format)
|
441
|
+
|
444
442
|
|
445
443
|
# Ensure that the value matches arguments' types.
|
446
|
-
all_ints = int_value and
|
444
|
+
all_ints = int_value and all_int_args
|
447
445
|
|
448
446
|
if min_value is not None and value is not None and min_value > value:
|
449
|
-
raise
|
450
|
-
|
451
|
-
|
447
|
+
raise StreamlitValueBelowMinError(value=value, min_value=min_value)
|
448
|
+
|
449
|
+
|
452
450
|
if max_value is not None and value is not None and max_value < value:
|
453
|
-
raise
|
454
|
-
f"The default `value` {value} must be less than or equal to the `max_value` {max_value}"
|
455
|
-
)
|
451
|
+
raise StreamlitValueAboveMaxError(value=value, max_value=max_value)
|
456
452
|
|
457
453
|
# Bounds checks. JSNumber produces human-readable exceptions that
|
458
454
|
# we simply re-package as StreamlitAPIExceptions.
|
@@ -476,7 +472,7 @@ class NumberInputMixin:
|
|
476
472
|
if value is not None:
|
477
473
|
JSNumber.validate_float_bounds(value, "`value`")
|
478
474
|
except JSNumberBoundsException as e:
|
479
|
-
raise
|
475
|
+
raise StreamlitJSNumberBoundsError(str(e))
|
480
476
|
|
481
477
|
data_type = NumberInputProto.INT if all_ints else NumberInputProto.FLOAT
|
482
478
|
|
streamlit/errors.py
CHANGED
@@ -14,6 +14,9 @@
|
|
14
14
|
|
15
15
|
from __future__ import annotations
|
16
16
|
|
17
|
+
import os
|
18
|
+
from typing import Any, Literal
|
19
|
+
|
17
20
|
from streamlit import util
|
18
21
|
|
19
22
|
|
@@ -154,7 +157,276 @@ class StreamlitModuleNotFoundError(StreamlitAPIWarning):
|
|
154
157
|
|
155
158
|
def __init__(self, module_name, *args):
|
156
159
|
message = (
|
157
|
-
f'This Streamlit command requires module "{module_name}"
|
158
|
-
"installed."
|
160
|
+
f'This Streamlit command requires module "{module_name}" '
|
161
|
+
"to be installed."
|
159
162
|
)
|
160
163
|
super().__init__(message, *args)
|
164
|
+
|
165
|
+
|
166
|
+
class LocalizableStreamlitException(StreamlitAPIException):
|
167
|
+
def __init__(self, message: str, **kwargs):
|
168
|
+
super().__init__((message).format(**kwargs))
|
169
|
+
self._exec_kwargs = kwargs
|
170
|
+
|
171
|
+
@property
|
172
|
+
def exec_kwargs(self) -> dict[str, Any]:
|
173
|
+
return self._exec_kwargs
|
174
|
+
|
175
|
+
|
176
|
+
# st.set_page_config
|
177
|
+
class StreamlitSetPageConfigMustBeFirstCommandError(LocalizableStreamlitException):
|
178
|
+
"""Exception raised when the set_page_config command is not the first executed streamlit command."""
|
179
|
+
|
180
|
+
def __init__(self):
|
181
|
+
super().__init__(
|
182
|
+
"`set_page_config()` can only be called once per app page, "
|
183
|
+
"and must be called as the first Streamlit command in your script.\n\n"
|
184
|
+
"For more information refer to the [docs]"
|
185
|
+
"(https://docs.streamlit.io/develop/api-reference/configuration/st.set_page_config)."
|
186
|
+
)
|
187
|
+
|
188
|
+
|
189
|
+
class StreamlitInvalidPageLayoutError(LocalizableStreamlitException):
|
190
|
+
"""Exception raised when an invalid value is specified for layout."""
|
191
|
+
|
192
|
+
def __init__(self, layout: str):
|
193
|
+
super().__init__(
|
194
|
+
'`layout` must be `"centered"` or `"wide"` (got `"{layout}"`)',
|
195
|
+
layout=layout,
|
196
|
+
)
|
197
|
+
|
198
|
+
|
199
|
+
class StreamlitInvalidSidebarStateError(LocalizableStreamlitException):
|
200
|
+
"""Exception raised when an invalid value is specified for `initial_sidebar_state`."""
|
201
|
+
|
202
|
+
def __init__(self, initial_sidebar_state: str):
|
203
|
+
super().__init__(
|
204
|
+
'`initial_sidebar_state` must be `"auto"` or `"expanded"` or `"collapsed"` (got `"{initial_sidebar_state}"`)',
|
205
|
+
initial_sidebar_state=initial_sidebar_state,
|
206
|
+
)
|
207
|
+
|
208
|
+
|
209
|
+
class StreamlitInvalidMenuItemKeyError(LocalizableStreamlitException):
|
210
|
+
"""Exception raised when an invalid key is specified."""
|
211
|
+
|
212
|
+
def __init__(self, key: str):
|
213
|
+
super().__init__(
|
214
|
+
'We only accept the keys: `"Get help"`, `"Report a bug"`, and `"About"` (`"{key}"` is not a valid key.)',
|
215
|
+
key=key,
|
216
|
+
)
|
217
|
+
|
218
|
+
|
219
|
+
class StreamlitInvalidURLError(LocalizableStreamlitException):
|
220
|
+
"""Exception raised when an invalid URL is specified for any of the menu items except for “About”."""
|
221
|
+
|
222
|
+
def __init__(self, url: str):
|
223
|
+
super().__init__(
|
224
|
+
'"{url}" is a not a valid URL. '
|
225
|
+
'You must use a fully qualified domain beginning with "http://", "https://", or "mailto:".',
|
226
|
+
url=url,
|
227
|
+
)
|
228
|
+
|
229
|
+
|
230
|
+
# st.columns
|
231
|
+
class StreamlitInvalidColumnSpecError(LocalizableStreamlitException):
|
232
|
+
"""Exception raised when no weights are specified, or a negative weight is specified."""
|
233
|
+
|
234
|
+
def __init__(self):
|
235
|
+
super().__init__(
|
236
|
+
"The `spec` argument to `st.columns` must be either a "
|
237
|
+
"positive integer (number of columns) or a list of positive numbers (width ratios of the columns). "
|
238
|
+
"See [documentation](https://docs.streamlit.io/develop/api-reference/layout/st.columns) "
|
239
|
+
"for more information."
|
240
|
+
)
|
241
|
+
|
242
|
+
|
243
|
+
class StreamlitInvalidVerticalAlignmentError(LocalizableStreamlitException):
|
244
|
+
"""Exception raised when an invalid value is specified for vertical_alignment."""
|
245
|
+
|
246
|
+
def __init__(self, vertical_alignment: str):
|
247
|
+
super().__init__(
|
248
|
+
'The `vertical_alignment` argument to `st.columns` must be `"top"`, `"center"`, or `"bottom"`. \n'
|
249
|
+
"The argument passed was {vertical_alignment}.",
|
250
|
+
vertical_alignment=vertical_alignment,
|
251
|
+
)
|
252
|
+
|
253
|
+
|
254
|
+
class StreamlitInvalidColumnGapError(LocalizableStreamlitException):
|
255
|
+
"""Exception raised when an invalid value is specified for gap."""
|
256
|
+
|
257
|
+
def __init__(self, gap: str):
|
258
|
+
super().__init__(
|
259
|
+
'The `gap` argument to `st.columns` must be `"small"`, `"medium"`, or `"large"`. \n'
|
260
|
+
"The argument passed was {gap}.",
|
261
|
+
gap=gap,
|
262
|
+
)
|
263
|
+
|
264
|
+
|
265
|
+
# st.multiselect
|
266
|
+
class StreamlitSelectionCountExceedsMaxError(LocalizableStreamlitException):
|
267
|
+
"""Exception raised when there are more default selections specified than the max allowable selections."""
|
268
|
+
|
269
|
+
def __init__(self, current_selections_count: int, max_selections_count: int):
|
270
|
+
super().__init__(
|
271
|
+
"Multiselect has {current_selections_count} {current_selections_noun} "
|
272
|
+
"selected but `max_selections` is set to {max_selections_count}. "
|
273
|
+
"This happened because you either gave too many options to `default` "
|
274
|
+
"or you manipulated the widget's state through `st.session_state`. "
|
275
|
+
"Note that the latter can happen before the line indicated in the traceback. "
|
276
|
+
"Please select at most {max_selections_count} {options_noun}.",
|
277
|
+
current_selections_count=current_selections_count,
|
278
|
+
current_selections_noun="option"
|
279
|
+
if current_selections_count == 1
|
280
|
+
else "options",
|
281
|
+
max_selections_count=max_selections_count,
|
282
|
+
options_noun="option" if max_selections_count == 1 else "options",
|
283
|
+
)
|
284
|
+
|
285
|
+
|
286
|
+
# st.number_input
|
287
|
+
class StreamlitMixedNumericTypesError(LocalizableStreamlitException):
|
288
|
+
"""Exception raised mixing floats and ints in st.number_input."""
|
289
|
+
|
290
|
+
def __init__(
|
291
|
+
self,
|
292
|
+
value: int | float | Literal["min"] | None,
|
293
|
+
min_value: int | float | None,
|
294
|
+
max_value: int | float | None,
|
295
|
+
step: int | float | None,
|
296
|
+
):
|
297
|
+
value_type = None
|
298
|
+
min_value_type = None
|
299
|
+
max_value_type = None
|
300
|
+
step_type = None
|
301
|
+
|
302
|
+
error_message = "All numerical arguments must be of the same type."
|
303
|
+
|
304
|
+
if value:
|
305
|
+
value_type = type(value).__name__
|
306
|
+
error_message += "\n`value` has {value_type} type."
|
307
|
+
|
308
|
+
if min_value:
|
309
|
+
min_value_type = type(min_value).__name__
|
310
|
+
error_message += "\n`min_value` has {min_value_type} type."
|
311
|
+
|
312
|
+
if max_value:
|
313
|
+
max_value_type = type(max_value).__name__
|
314
|
+
error_message += "\n`max_value` has {max_value_type} type."
|
315
|
+
|
316
|
+
if step:
|
317
|
+
step_type = type(step).__name__
|
318
|
+
error_message += "\n`step` has {step_type} type."
|
319
|
+
|
320
|
+
super().__init__(
|
321
|
+
error_message,
|
322
|
+
value_type=value_type,
|
323
|
+
min_value_type=min_value_type,
|
324
|
+
max_value_type=max_value_type,
|
325
|
+
step_type=step_type,
|
326
|
+
)
|
327
|
+
|
328
|
+
|
329
|
+
class StreamlitValueBelowMinError(LocalizableStreamlitException):
|
330
|
+
"""Exception raised when the `min_value` is greater than the `value`."""
|
331
|
+
|
332
|
+
def __init__(self, value: int | float, min_value: int | float):
|
333
|
+
super().__init__(
|
334
|
+
"The `value` {value} is less than the `min_value` {min_value}.",
|
335
|
+
value=value,
|
336
|
+
min_value=min_value,
|
337
|
+
)
|
338
|
+
|
339
|
+
|
340
|
+
class StreamlitValueAboveMaxError(LocalizableStreamlitException):
|
341
|
+
"""Exception raised when the `max_value` is less than the `value`."""
|
342
|
+
|
343
|
+
def __init__(self, value: int | float, max_value: int | float):
|
344
|
+
super().__init__(
|
345
|
+
"The `value` {value} is greater than than the `max_value` {max_value}.",
|
346
|
+
value=value,
|
347
|
+
max_value=max_value,
|
348
|
+
)
|
349
|
+
|
350
|
+
|
351
|
+
class StreamlitJSNumberBoundsError(LocalizableStreamlitException):
|
352
|
+
"""Exception raised when a number exceeds the Javascript limits."""
|
353
|
+
|
354
|
+
def __init__(self, message: str):
|
355
|
+
super().__init__(message)
|
356
|
+
|
357
|
+
|
358
|
+
class StreamlitInvalidNumberFormatError(LocalizableStreamlitException):
|
359
|
+
"""Exception raised when the format string for `st.number_input` contains
|
360
|
+
invalid characters.
|
361
|
+
"""
|
362
|
+
|
363
|
+
def __init__(self, format: str):
|
364
|
+
super().__init__(
|
365
|
+
"Format string for `st.number_input` contains invalid characters: {format}",
|
366
|
+
format=format,
|
367
|
+
)
|
368
|
+
|
369
|
+
|
370
|
+
# st.page_link
|
371
|
+
class StreamlitMissingPageLabelError(LocalizableStreamlitException):
|
372
|
+
"""Exception raised when a page_link is created without a label."""
|
373
|
+
|
374
|
+
def __init__(self):
|
375
|
+
super().__init__(
|
376
|
+
"The `label` param is required for external links used with `st.page_link` - please provide a `label`."
|
377
|
+
)
|
378
|
+
|
379
|
+
|
380
|
+
class StreamlitPageNotFoundError(LocalizableStreamlitException):
|
381
|
+
"""Exception raised the linked page can not be found."""
|
382
|
+
|
383
|
+
def __init__(self, page: str, main_script_directory: str, is_mpa_v2: bool):
|
384
|
+
directory = os.path.basename(main_script_directory)
|
385
|
+
|
386
|
+
message = (
|
387
|
+
"Could not find page: `{page}`. You must provide a file path "
|
388
|
+
"relative to the entrypoint file (from the directory `{directory}`). "
|
389
|
+
"Only the entrypoint file and files in the `pages/` directory are supported."
|
390
|
+
)
|
391
|
+
|
392
|
+
if is_mpa_v2:
|
393
|
+
message = (
|
394
|
+
"Could not find page: `{page}`. You must provide a `StreamlitPage` "
|
395
|
+
"object or file path relative to the entrypoint file. Only pages "
|
396
|
+
"previously defined by `st.Page` and passed to `st.navigation` are "
|
397
|
+
"allowed."
|
398
|
+
)
|
399
|
+
|
400
|
+
super().__init__(
|
401
|
+
message,
|
402
|
+
page=page,
|
403
|
+
directory=directory,
|
404
|
+
)
|
405
|
+
|
406
|
+
|
407
|
+
# policies
|
408
|
+
class StreamlitFragmentWidgetsNotAllowedOutsideError(LocalizableStreamlitException):
|
409
|
+
"""Exception raised when the fragment attempts to write to an element outside of its container."""
|
410
|
+
|
411
|
+
def __init__(self):
|
412
|
+
super().__init__("Fragments cannot write widgets to outside containers.")
|
413
|
+
|
414
|
+
|
415
|
+
class StreamlitInvalidFormCallbackError(LocalizableStreamlitException):
|
416
|
+
"""Exception raised a `on_change` callback is set on any element in a form except for the `st.form_submit_button`."""
|
417
|
+
|
418
|
+
def __init__(self):
|
419
|
+
super().__init__(
|
420
|
+
"Within a form, callbacks can only be defined on `st.form_submit_button`. "
|
421
|
+
"Defining callbacks on other widgets inside a form is not allowed."
|
422
|
+
)
|
423
|
+
|
424
|
+
|
425
|
+
class StreamlitValueAssignmentNotAllowedError(LocalizableStreamlitException):
|
426
|
+
"""Exception raised when trying to set values where writes are not allowed."""
|
427
|
+
|
428
|
+
def __init__(self, key: str):
|
429
|
+
super().__init__(
|
430
|
+
"Values for the widget with `key` '{key}' cannot be set using `st.session_state`.",
|
431
|
+
key=key,
|
432
|
+
)
|
@@ -269,6 +269,10 @@ class PagesManager:
|
|
269
269
|
def intended_page_script_hash(self) -> PageHash | None:
|
270
270
|
return self._intended_page_script_hash
|
271
271
|
|
272
|
+
@property
|
273
|
+
def mpa_version(self) -> int:
|
274
|
+
return 2 if isinstance(self.pages_strategy, PagesStrategyV2) else 1
|
275
|
+
|
272
276
|
def get_main_page(self) -> PageInfo:
|
273
277
|
return {
|
274
278
|
"script_path": self._main_script_path,
|