streamlit-nightly 1.33.1.dev20240409__py2.py3-none-any.whl → 1.33.1.dev20240414__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.
Files changed (29) hide show
  1. streamlit/__init__.py +4 -1
  2. streamlit/delta_generator.py +54 -36
  3. streamlit/elements/dialog_decorator.py +166 -0
  4. streamlit/elements/image.py +5 -3
  5. streamlit/elements/layouts.py +19 -0
  6. streamlit/elements/lib/dialog.py +148 -0
  7. streamlit/proto/Block_pb2.py +26 -22
  8. streamlit/proto/Block_pb2.pyi +43 -3
  9. streamlit/proto/Common_pb2.py +1 -1
  10. streamlit/runtime/scriptrunner/script_run_context.py +3 -0
  11. streamlit/runtime/scriptrunner/script_runner.py +16 -0
  12. streamlit/runtime/state/query_params.py +28 -11
  13. streamlit/runtime/state/query_params_proxy.py +51 -3
  14. streamlit/runtime/state/session_state.py +3 -0
  15. streamlit/static/asset-manifest.json +5 -5
  16. streamlit/static/index.html +1 -1
  17. streamlit/static/static/js/{1168.3029456a.chunk.js → 1168.1d6408e6.chunk.js} +1 -1
  18. streamlit/static/static/js/{4666.b694c5a9.chunk.js → 4666.492dcf72.chunk.js} +1 -1
  19. streamlit/static/static/js/8427.d30dffe1.chunk.js +1 -0
  20. streamlit/static/static/js/main.a41ffabd.js +2 -0
  21. {streamlit_nightly-1.33.1.dev20240409.dist-info → streamlit_nightly-1.33.1.dev20240414.dist-info}/METADATA +1 -1
  22. {streamlit_nightly-1.33.1.dev20240409.dist-info → streamlit_nightly-1.33.1.dev20240414.dist-info}/RECORD +27 -25
  23. streamlit/static/static/js/8427.b0ed496b.chunk.js +0 -1
  24. streamlit/static/static/js/main.37ad7d13.js +0 -2
  25. /streamlit/static/static/js/{main.37ad7d13.js.LICENSE.txt → main.a41ffabd.js.LICENSE.txt} +0 -0
  26. {streamlit_nightly-1.33.1.dev20240409.data → streamlit_nightly-1.33.1.dev20240414.data}/scripts/streamlit.cmd +0 -0
  27. {streamlit_nightly-1.33.1.dev20240409.dist-info → streamlit_nightly-1.33.1.dev20240414.dist-info}/WHEEL +0 -0
  28. {streamlit_nightly-1.33.1.dev20240409.dist-info → streamlit_nightly-1.33.1.dev20240414.dist-info}/entry_points.txt +0 -0
  29. {streamlit_nightly-1.33.1.dev20240409.dist-info → streamlit_nightly-1.33.1.dev20240414.dist-info}/top_level.txt +0 -0
streamlit/__init__.py CHANGED
@@ -72,6 +72,10 @@ from streamlit.delta_generator import (
72
72
  event_dg as _event_dg,
73
73
  bottom_dg as _bottom_dg,
74
74
  )
75
+ from streamlit.elements.dialog_decorator import (
76
+ # rename so that it is available as st.dialog
77
+ dialog_decorator as experimental_dialog,
78
+ )
75
79
  from streamlit.runtime.caching import (
76
80
  cache_resource as _cache_resource,
77
81
  cache_data as _cache_data,
@@ -129,7 +133,6 @@ _config.on_config_parsed(_update_logger, True)
129
133
  secrets = _secrets_singleton
130
134
 
131
135
  # DeltaGenerator methods:
132
-
133
136
  _main = _main_dg
134
137
  sidebar = _sidebar_dg
135
138
  _event = _event_dg
@@ -26,6 +26,7 @@ from typing import (
26
26
  Final,
27
27
  Hashable,
28
28
  Iterable,
29
+ List,
29
30
  Literal,
30
31
  NoReturn,
31
32
  TypeVar,
@@ -314,9 +315,9 @@ class DeltaGenerator(
314
315
  if self == self._main_dg:
315
316
  # We're being invoked via an `st.foo` pattern - use the current
316
317
  # `with` dg (aka the top of the stack).
317
- current_stack = dg_stack.get()
318
- if len(current_stack) > 1:
319
- return current_stack[-1]
318
+ last_context_stack_dg = get_last_dg_added_to_context_stack()
319
+ if last_context_stack_dg is not None:
320
+ return last_context_stack_dg
320
321
 
321
322
  # We're being invoked via an `st.sidebar.foo` pattern - ignore the
322
323
  # current `with` dg.
@@ -606,34 +607,7 @@ class DeltaGenerator(
606
607
  block_type = block_proto.WhichOneof("type")
607
608
  # Convert the generator to a list, so we can use it multiple times.
608
609
  ancestor_block_types = list(dg._ancestor_block_types)
609
-
610
- if block_type == "column":
611
- num_of_parent_columns = self._count_num_of_parent_columns(
612
- ancestor_block_types
613
- )
614
- if (
615
- self._root_container == RootContainer.SIDEBAR
616
- and num_of_parent_columns > 0
617
- ):
618
- raise StreamlitAPIException(
619
- "Columns cannot be placed inside other columns in the sidebar. This is only possible in the main area of the app."
620
- )
621
- if num_of_parent_columns > 1:
622
- raise StreamlitAPIException(
623
- "Columns can only be placed inside other columns up to one level of nesting."
624
- )
625
- if block_type == "chat_message" and block_type in ancestor_block_types:
626
- raise StreamlitAPIException(
627
- "Chat messages cannot nested inside other chat messages."
628
- )
629
- if block_type == "expandable" and block_type in ancestor_block_types:
630
- raise StreamlitAPIException(
631
- "Expanders may not be nested inside other expanders."
632
- )
633
- if block_type == "popover" and block_type in ancestor_block_types:
634
- raise StreamlitAPIException(
635
- "Popovers may not be nested inside other popovers."
636
- )
610
+ _check_nested_element_violation(self, block_type, ancestor_block_types)
637
611
 
638
612
  if dg._root_container is None or dg._cursor is None:
639
613
  return dg
@@ -684,11 +658,9 @@ class DeltaGenerator(
684
658
  def _arrow_add_rows(
685
659
  self: DG,
686
660
  data: Data = None,
687
- **kwargs: DataFrame
688
- | npt.NDArray[Any]
689
- | Iterable[Any]
690
- | dict[Hashable, Any]
691
- | None,
661
+ **kwargs: (
662
+ DataFrame | npt.NDArray[Any] | Iterable[Any] | dict[Hashable, Any] | None
663
+ ),
692
664
  ) -> DG | None:
693
665
  """Concatenate a dataframe to the bottom of the current one.
694
666
 
@@ -814,6 +786,20 @@ dg_stack: ContextVar[tuple[DeltaGenerator, ...]] = ContextVar(
814
786
  )
815
787
 
816
788
 
789
+ def get_last_dg_added_to_context_stack() -> DeltaGenerator | None:
790
+ """Get the last added DeltaGenerator of the stack in the current context.
791
+
792
+ Returns None if the stack has only one element or is empty for whatever reason.
793
+ """
794
+ current_stack = dg_stack.get()
795
+ # If set to "> 0" and thus return the only delta generator in the stack - which logically makes more sense -, some unit tests
796
+ # fail. It looks like the reason is that they create their own main delta generator but do not populate the dg_stack correctly. However, to be on the safe-side,
797
+ # we keep the logic but leave the comment as shared knowledge for whoever will look into this in the future.
798
+ if len(current_stack) > 1:
799
+ return current_stack[-1]
800
+ return None
801
+
802
+
817
803
  def _prep_data_for_add_rows(
818
804
  data: Data,
819
805
  delta_type: str,
@@ -927,3 +913,35 @@ def _writes_directly_to_sidebar(dg: DG) -> bool:
927
913
  in_sidebar = any(a._root_container == RootContainer.SIDEBAR for a in dg._ancestors)
928
914
  has_container = bool(len(list(dg._ancestor_block_types)))
929
915
  return in_sidebar and not has_container
916
+
917
+
918
+ def _check_nested_element_violation(
919
+ dg: DeltaGenerator, block_type: str | None, ancestor_block_types: List[BlockType]
920
+ ) -> None:
921
+ """Check if elements are nested in a forbidden way.
922
+
923
+ Raises
924
+ ------
925
+ StreamlitAPIException: throw if an invalid element nesting is detected.
926
+ """
927
+
928
+ if block_type == "column":
929
+ num_of_parent_columns = dg._count_num_of_parent_columns(ancestor_block_types)
930
+ if dg._root_container == RootContainer.SIDEBAR and num_of_parent_columns > 0:
931
+ raise StreamlitAPIException(
932
+ "Columns cannot be placed inside other columns in the sidebar. This is only possible in the main area of the app."
933
+ )
934
+ if num_of_parent_columns > 1:
935
+ raise StreamlitAPIException(
936
+ "Columns can only be placed inside other columns up to one level of nesting."
937
+ )
938
+ if block_type == "chat_message" and block_type in ancestor_block_types:
939
+ raise StreamlitAPIException(
940
+ "Chat messages cannot nested inside other chat messages."
941
+ )
942
+ if block_type == "expandable" and block_type in ancestor_block_types:
943
+ raise StreamlitAPIException(
944
+ "Expanders may not be nested inside other expanders."
945
+ )
946
+ if block_type == "popover" and block_type in ancestor_block_types:
947
+ raise StreamlitAPIException("Popovers may not be nested inside other popovers.")
@@ -0,0 +1,166 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ from functools import wraps
18
+ from typing import Callable, TypeVar, cast, overload
19
+
20
+ from streamlit.delta_generator import event_dg, get_last_dg_added_to_context_stack
21
+ from streamlit.elements.lib.dialog import DialogWidth
22
+ from streamlit.errors import StreamlitAPIException
23
+ from streamlit.runtime.fragment import fragment as _fragment
24
+ from streamlit.runtime.metrics_util import gather_metrics
25
+
26
+
27
+ def _assert_no_nested_dialogs() -> None:
28
+ """Check the current stack for existing DeltaGenerator's of type 'dialog'.
29
+ Note that the check like this only works when Dialog is called as a context manager, as this populates the dg_stack in delta_generator correctly.
30
+
31
+ This does not detect the edge case in which someone calls, for example, `with st.sidebar` inside of a dialog function and opens a dialog in there,
32
+ as `with st.sidebar` pushes the new DeltaGenerator to the stack. In order to check for that edge case, we could try to check all DeltaGenerators in the stack,
33
+ and not only the last one. Since we deem this to be an edge case, we lean towards simplicity here.
34
+
35
+ Raises
36
+ ------
37
+ StreamlitAPIException
38
+ Raised if the user tries to nest dialogs inside of each other.
39
+ """
40
+ last_dg_in_current_context = get_last_dg_added_to_context_stack()
41
+ if last_dg_in_current_context and "dialog" in set(
42
+ last_dg_in_current_context._ancestor_block_types
43
+ ):
44
+ raise StreamlitAPIException("Dialogs may not be nested inside other dialogs.")
45
+
46
+
47
+ F = TypeVar("F", bound=Callable[..., None])
48
+
49
+
50
+ def _dialog_decorator(
51
+ non_optional_func: F, title: str, *, width: DialogWidth = "small"
52
+ ) -> F:
53
+ if title is None or title == "":
54
+ raise StreamlitAPIException(
55
+ 'A non-empty `title` argument has to be provided for dialogs, for example `@st.experimental_dialog("Example Title")`.'
56
+ )
57
+
58
+ @wraps(non_optional_func)
59
+ def wrap(*args, **kwargs) -> None:
60
+ _assert_no_nested_dialogs()
61
+ # Call the Dialog on the event_dg because it lives outside of the normal
62
+ # Streamlit UI flow. For example, if it is called from the sidebar, it should not
63
+ # inherit the sidebar theming.
64
+ dialog = event_dg._dialog(title=title, dismissible=True, width=width)
65
+ dialog.open()
66
+
67
+ @_fragment
68
+ def dialog_content() -> None:
69
+ # if the dialog should be closed, st.rerun() has to be called (same behavior as with st.fragment)
70
+ _ = non_optional_func(*args, **kwargs)
71
+ return None
72
+
73
+ with dialog:
74
+ return dialog_content()
75
+
76
+ return cast(F, wrap)
77
+
78
+
79
+ @overload
80
+ def dialog_decorator(title: str, *, width: DialogWidth = "small") -> Callable[[F], F]:
81
+ ...
82
+
83
+
84
+ # 'title' can be a function since `dialog_decorator` is a decorator. We just call it 'title' here though
85
+ # to make the user-doc more friendly as we want the user to pass a title, not a function.
86
+ # The user is supposed to call it like @st.dialog("my_title") , which makes 'title' a positional arg, hence
87
+ # this 'trick'. The overload is required to have a good type hint for the decorated function args.
88
+ @overload
89
+ def dialog_decorator(title: F | None, *, width: DialogWidth = "small") -> F:
90
+ ...
91
+
92
+
93
+ @gather_metrics("experimental_dialog")
94
+ def dialog_decorator(
95
+ title: F | None | str = "", *, width: DialogWidth = "small"
96
+ ) -> F | Callable[[F], F]:
97
+ r"""Decorate a function to mark it as a Streamlit dialog. When the decorated function is called, a dialog element is inserted with the function's body as the content.
98
+
99
+ The decorated function can hold multiple elements which are rendered inside of a modal when the decorated function is called.
100
+ The decorated function is using `st.experimental_fragment`, which means that interacting with elements inside of the dialog will
101
+ only re-run the dialog function.
102
+
103
+ The decorated function can accept arguments that can be passed when it is called.
104
+
105
+ Dismissing a dialog does not cause an app re-run.
106
+ You can close the dialog programmatically by executing `st.rerun()` explicitly inside of the decorated function.
107
+
108
+ In order to pass state from dialog widgets to the app, you can leverage `st.session_state`.
109
+
110
+ .. warning::
111
+ Currently, a dialog may not open another dialog.
112
+ Also, only one dialog-decorated function may be called in a script run, which means that only one dialog can be open at any given time.
113
+
114
+ Parameters
115
+ ----------
116
+ title : str
117
+ A string that will be used as the dialog's title. It cannot be empty.
118
+ width : "small", "large"
119
+ The width of the dialog. Defaults to "small".
120
+
121
+ Returns
122
+ -------
123
+ A decorated function that, when called, inserts a dialog element context container. The container itself contains the decorated function's elements.
124
+
125
+ Examples
126
+ --------
127
+ You can annotate a function to mark it as a Streamlit dialog function and pass arguments to it. You can either dismiss the dialog via the ESC-key or the X-button or close it programmatically and trigger a re-run by using `st.rerun()`.
128
+ Leverage `st.session_state` if you want to pass dialog widget states to the overall app:
129
+
130
+ >>> import streamlit as st
131
+ >>>
132
+ >>> @st.experimental_dialog("Streamlit Example Dialog")
133
+ >>> def example_dialog(some_arg: str, some_other_arg: int):
134
+ >>> st.write(f"You passed following args: {some_arg} | {some_other_arg}")
135
+ >>> # interacting with the text_input only re-runs `example_dialog`
136
+ >>> some_text_input = st.text_input("Type something:", key="example_dialog_some_text_input")
137
+ >>> # following write is updated when chaning the text_input inside the dialog
138
+ >>> st.write(f"You wrote '{some_text_input}' in the dialog")
139
+ >>> if st.button("Close the dialog"):
140
+ >>> st.rerun()
141
+ >>>
142
+ >>> if st.button("Open dialog"):
143
+ >>> example_dialog("Some string arg", 42)
144
+ >>>
145
+ >>> # following write is updated with the dialog's text input when the dialog was opened, the text input was interacted with and a re-run was triggered, e.g. by clicking the Close-button defined in `example_dialog`
146
+ >>> st.write(f"You wrote '{st.session_state.get('example_dialog_some_text_input', '')}' in the dialog")
147
+
148
+ """
149
+
150
+ func_or_title = title
151
+ if func_or_title is None:
152
+ # Support passing the params via function decorator
153
+ def wrapper(f: F) -> F:
154
+ return _dialog_decorator(non_optional_func=f, title="", width=width)
155
+
156
+ return wrapper
157
+ elif type(func_or_title) is str:
158
+ # Support passing the params via function decorator
159
+ def wrapper(f: F) -> F:
160
+ title: str = func_or_title
161
+ return _dialog_decorator(non_optional_func=f, title=title, width=width)
162
+
163
+ return wrapper
164
+
165
+ func: F = cast(F, func_or_title)
166
+ return _dialog_decorator(func, "", width=width)
@@ -296,9 +296,11 @@ def _ensure_image_size_and_format(
296
296
  if width > 0 and actual_width > width:
297
297
  # We need to resize the image.
298
298
  new_height = int(1.0 * actual_height * width / actual_width)
299
- pil_image = pil_image.resize(
300
- (width, new_height), resample=Image.Resampling.BILINEAR
301
- )
299
+ # pillow reexports Image.Resampling.BILINEAR as Image.BILINEAR for backwards
300
+ # compatibility reasons, so we use the reexport to support older pillow
301
+ # versions. The types don't seem to reflect this, though, hence the type: ignore
302
+ # below.
303
+ pil_image = pil_image.resize((width, new_height), resample=Image.BILINEAR) # type: ignore[attr-defined]
302
304
  return _PIL_to_bytes(pil_image, format=image_format, quality=90)
303
305
 
304
306
  if pil_image.format != image_format:
@@ -24,6 +24,7 @@ from streamlit.runtime.metrics_util import gather_metrics
24
24
 
25
25
  if TYPE_CHECKING:
26
26
  from streamlit.delta_generator import DeltaGenerator
27
+ from streamlit.elements.lib.dialog import Dialog
27
28
  from streamlit.elements.lib.mutable_status_container import StatusContainer
28
29
 
29
30
  SpecType: TypeAlias = Union[int, Sequence[Union[int, float]]]
@@ -710,6 +711,24 @@ class LayoutsMixin:
710
711
  self.dg, label=label, expanded=expanded, state=state
711
712
  )
712
713
 
714
+ def _dialog(
715
+ self,
716
+ title: str,
717
+ *,
718
+ dismissible: bool = True,
719
+ width: Literal["small", "large"] = "small",
720
+ ) -> "Dialog":
721
+ """Inserts the dialog container.
722
+
723
+ Marked as internal because it is used by the dialog_decorator and is not supposed to be used directly.
724
+ The dialog_decorator also has a more descriptive docstring since it is user-facing.
725
+ """
726
+
727
+ # We need to import Dialog here to avoid a circular import
728
+ from streamlit.elements.lib.dialog import Dialog
729
+
730
+ return Dialog._create(self.dg, title, dismissible=dismissible, width=width)
731
+
713
732
  @property
714
733
  def dg(self) -> DeltaGenerator:
715
734
  """Get our DeltaGenerator."""
@@ -0,0 +1,148 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ import time
18
+ from types import TracebackType
19
+ from typing import Literal, cast
20
+
21
+ from typing_extensions import TypeAlias
22
+
23
+ from streamlit.cursor import Cursor
24
+ from streamlit.delta_generator import DeltaGenerator, _enqueue_message
25
+ from streamlit.errors import StreamlitAPIException
26
+ from streamlit.proto.Block_pb2 import Block as BlockProto
27
+ from streamlit.proto.ForwardMsg_pb2 import ForwardMsg
28
+ from streamlit.runtime.scriptrunner import get_script_run_ctx
29
+
30
+ DialogWidth: TypeAlias = Literal["small", "large"]
31
+
32
+
33
+ def _process_dialog_width_input(
34
+ width: DialogWidth,
35
+ ) -> BlockProto.Dialog.DialogWidth.ValueType:
36
+ """Maps the user-provided literal to a value of the DialogWidth proto enum.
37
+
38
+ Returns the mapped enum field for "small" by default and otherwise the mapped type.
39
+ """
40
+ if width == "large":
41
+ return BlockProto.Dialog.DialogWidth.LARGE
42
+
43
+ return BlockProto.Dialog.DialogWidth.SMALL
44
+
45
+
46
+ def _assert_first_dialog_to_be_opened(should_open: bool) -> None:
47
+ """Check whether a dialog has already been opened in the same script run.
48
+
49
+ Only one dialog is supposed to be opened. The check is implemented in a way
50
+ that for a script run, the open function can only be called once.
51
+ One dialog at a time is a product decision and not a technical one.
52
+
53
+ Raises
54
+ ------
55
+ StreamlitAPIException
56
+ Raised when a dialog has already been opened in the current script run.
57
+ """
58
+ script_run_ctx = get_script_run_ctx()
59
+ # We don't reset the ctx.has_dialog_opened when the flag is False because
60
+ # it is reset in a new scriptrun anyways. If the execution model ever changes,
61
+ # this might need to change.
62
+ if should_open and script_run_ctx:
63
+ if script_run_ctx.has_dialog_opened:
64
+ raise StreamlitAPIException(
65
+ "Only one dialog is allowed to be opened at the same time. Please make sure to not call a dialog-decorated function more than once in a script run."
66
+ )
67
+ script_run_ctx.has_dialog_opened = True
68
+
69
+
70
+ class Dialog(DeltaGenerator):
71
+ @staticmethod
72
+ def _create(
73
+ parent: DeltaGenerator,
74
+ title: str,
75
+ *,
76
+ dismissible: bool = True,
77
+ width: DialogWidth = "small",
78
+ ) -> Dialog:
79
+ block_proto = BlockProto()
80
+ block_proto.dialog.title = title
81
+ block_proto.dialog.dismissible = dismissible
82
+ block_proto.dialog.width = _process_dialog_width_input(width)
83
+
84
+ # We store the delta path here, because in _update we enqueue a new proto message to update the
85
+ # open status. Without this, the dialog content is gone when the _update message is sent
86
+ delta_path: list[int] = (
87
+ parent._active_dg._cursor.delta_path if parent._active_dg._cursor else []
88
+ )
89
+ dialog = cast(Dialog, parent._block(block_proto=block_proto, dg_type=Dialog))
90
+
91
+ dialog._delta_path = delta_path
92
+ dialog._current_proto = block_proto
93
+ # We add a sleep here to give the web app time to react to the update. Otherwise,
94
+ # we might run into issues where the dialog cannot be opened again after closing
95
+ time.sleep(0.05)
96
+ return dialog
97
+
98
+ def __init__(
99
+ self,
100
+ root_container: int | None,
101
+ cursor: Cursor | None,
102
+ parent: DeltaGenerator | None,
103
+ block_type: str | None,
104
+ ):
105
+ super().__init__(root_container, cursor, parent, block_type)
106
+
107
+ # Initialized in `_create()`:
108
+ self._current_proto: BlockProto | None = None
109
+ self._delta_path: list[int] | None = None
110
+
111
+ def _update(self, should_open: bool):
112
+ """Send an updated proto message to indicate the open-status for the dialog."""
113
+
114
+ assert self._current_proto is not None, "Dialog not correctly initialized!"
115
+ assert self._delta_path is not None, "Dialog not correctly initialized!"
116
+ _assert_first_dialog_to_be_opened(should_open)
117
+ msg = ForwardMsg()
118
+ msg.metadata.delta_path[:] = self._delta_path
119
+ msg.delta.add_block.CopyFrom(self._current_proto)
120
+ msg.delta.add_block.dialog.is_open = should_open
121
+
122
+ self._current_proto = msg.delta.add_block
123
+
124
+ # We add a sleep here to give the web app time to react to the update. Otherwise,
125
+ # we might run into issues where the dialog cannot be opened again after closing
126
+ time.sleep(0.05)
127
+ _enqueue_message(msg)
128
+
129
+ def open(self) -> None:
130
+ self._update(True)
131
+
132
+ def close(self) -> None:
133
+ self._update(False)
134
+
135
+ def __enter__(self) -> Dialog: # type: ignore[override]
136
+ # This is a little dubious: we're returning a different type than
137
+ # our superclass' `__enter__` function. Maybe DeltaGenerator.__enter__
138
+ # should always return `self`?
139
+ super().__enter__()
140
+ return self
141
+
142
+ def __exit__(
143
+ self,
144
+ exc_type: type[BaseException] | None,
145
+ exc_val: BaseException | None,
146
+ exc_tb: TracebackType | None,
147
+ ) -> Literal[False]:
148
+ return super().__exit__(exc_type, exc_val, exc_tb)
@@ -13,7 +13,7 @@ _sym_db = _symbol_database.Default()
13
13
 
14
14
 
15
15
 
16
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bstreamlit/proto/Block.proto\"\xfd\x06\n\x05\x42lock\x12#\n\x08vertical\x18\x01 \x01(\x0b\x32\x0f.Block.VerticalH\x00\x12\'\n\nhorizontal\x18\x02 \x01(\x0b\x32\x11.Block.HorizontalH\x00\x12\x1f\n\x06\x63olumn\x18\x03 \x01(\x0b\x32\r.Block.ColumnH\x00\x12\'\n\nexpandable\x18\x04 \x01(\x0b\x32\x11.Block.ExpandableH\x00\x12\x1b\n\x04\x66orm\x18\x05 \x01(\x0b\x32\x0b.Block.FormH\x00\x12,\n\rtab_container\x18\x06 \x01(\x0b\x32\x13.Block.TabContainerH\x00\x12\x19\n\x03tab\x18\x07 \x01(\x0b\x32\n.Block.TabH\x00\x12*\n\x0c\x63hat_message\x18\t \x01(\x0b\x32\x12.Block.ChatMessageH\x00\x12!\n\x07popover\x18\n \x01(\x0b\x32\x0e.Block.PopoverH\x00\x12\x13\n\x0b\x61llow_empty\x18\x08 \x01(\x08\x1a*\n\x08Vertical\x12\x0e\n\x06\x62order\x18\x01 \x01(\x08\x12\x0e\n\x06height\x18\x02 \x01(\r\x1a\x19\n\nHorizontal\x12\x0b\n\x03gap\x18\x01 \x01(\t\x1a%\n\x06\x43olumn\x12\x0e\n\x06weight\x18\x01 \x01(\x01\x12\x0b\n\x03gap\x18\x02 \x01(\t\x1aM\n\nExpandable\x12\r\n\x05label\x18\x01 \x01(\t\x12\x15\n\x08\x65xpanded\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x0c\n\x04icon\x18\x03 \x01(\tB\x0b\n\t_expanded\x1a@\n\x04\x46orm\x12\x0f\n\x07\x66orm_id\x18\x01 \x01(\t\x12\x17\n\x0f\x63lear_on_submit\x18\x02 \x01(\x08\x12\x0e\n\x06\x62order\x18\x03 \x01(\x08\x1a\x0e\n\x0cTabContainer\x1a\x14\n\x03Tab\x12\r\n\x05label\x18\x01 \x01(\t\x1aU\n\x07Popover\x12\r\n\x05label\x18\x01 \x01(\t\x12\x1b\n\x13use_container_width\x18\x02 \x01(\x08\x12\x0c\n\x04help\x18\x03 \x01(\t\x12\x10\n\x08\x64isabled\x18\x04 \x01(\x08\x1a\x8d\x01\n\x0b\x43hatMessage\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06\x61vatar\x18\x02 \x01(\t\x12\x32\n\x0b\x61vatar_type\x18\x03 \x01(\x0e\x32\x1d.Block.ChatMessage.AvatarType\",\n\nAvatarType\x12\t\n\x05IMAGE\x10\x00\x12\t\n\x05\x45MOJI\x10\x01\x12\x08\n\x04ICON\x10\x02\x42\x06\n\x04typeB*\n\x1c\x63om.snowflake.apps.streamlitB\nBlockProtob\x06proto3')
16
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bstreamlit/proto/Block.proto\"\xbe\x08\n\x05\x42lock\x12#\n\x08vertical\x18\x01 \x01(\x0b\x32\x0f.Block.VerticalH\x00\x12\'\n\nhorizontal\x18\x02 \x01(\x0b\x32\x11.Block.HorizontalH\x00\x12\x1f\n\x06\x63olumn\x18\x03 \x01(\x0b\x32\r.Block.ColumnH\x00\x12\'\n\nexpandable\x18\x04 \x01(\x0b\x32\x11.Block.ExpandableH\x00\x12\x1b\n\x04\x66orm\x18\x05 \x01(\x0b\x32\x0b.Block.FormH\x00\x12,\n\rtab_container\x18\x06 \x01(\x0b\x32\x13.Block.TabContainerH\x00\x12\x19\n\x03tab\x18\x07 \x01(\x0b\x32\n.Block.TabH\x00\x12*\n\x0c\x63hat_message\x18\t \x01(\x0b\x32\x12.Block.ChatMessageH\x00\x12!\n\x07popover\x18\n \x01(\x0b\x32\x0e.Block.PopoverH\x00\x12\x1f\n\x06\x64ialog\x18\x0b \x01(\x0b\x32\r.Block.DialogH\x00\x12\x13\n\x0b\x61llow_empty\x18\x08 \x01(\x08\x1a*\n\x08Vertical\x12\x0e\n\x06\x62order\x18\x01 \x01(\x08\x12\x0e\n\x06height\x18\x02 \x01(\r\x1a\x19\n\nHorizontal\x12\x0b\n\x03gap\x18\x01 \x01(\t\x1a%\n\x06\x43olumn\x12\x0e\n\x06weight\x18\x01 \x01(\x01\x12\x0b\n\x03gap\x18\x02 \x01(\t\x1aM\n\nExpandable\x12\r\n\x05label\x18\x01 \x01(\t\x12\x15\n\x08\x65xpanded\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x0c\n\x04icon\x18\x03 \x01(\tB\x0b\n\t_expanded\x1a\x9d\x01\n\x06\x44ialog\x12\r\n\x05title\x18\x01 \x01(\t\x12\x13\n\x0b\x64ismissible\x18\x02 \x01(\x08\x12(\n\x05width\x18\x03 \x01(\x0e\x32\x19.Block.Dialog.DialogWidth\x12\x14\n\x07is_open\x18\x04 \x01(\x08H\x00\x88\x01\x01\"#\n\x0b\x44ialogWidth\x12\t\n\x05SMALL\x10\x00\x12\t\n\x05LARGE\x10\x01\x42\n\n\x08_is_open\x1a@\n\x04\x46orm\x12\x0f\n\x07\x66orm_id\x18\x01 \x01(\t\x12\x17\n\x0f\x63lear_on_submit\x18\x02 \x01(\x08\x12\x0e\n\x06\x62order\x18\x03 \x01(\x08\x1a\x0e\n\x0cTabContainer\x1a\x14\n\x03Tab\x12\r\n\x05label\x18\x01 \x01(\t\x1aU\n\x07Popover\x12\r\n\x05label\x18\x01 \x01(\t\x12\x1b\n\x13use_container_width\x18\x02 \x01(\x08\x12\x0c\n\x04help\x18\x03 \x01(\t\x12\x10\n\x08\x64isabled\x18\x04 \x01(\x08\x1a\x8d\x01\n\x0b\x43hatMessage\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06\x61vatar\x18\x02 \x01(\t\x12\x32\n\x0b\x61vatar_type\x18\x03 \x01(\x0e\x32\x1d.Block.ChatMessage.AvatarType\",\n\nAvatarType\x12\t\n\x05IMAGE\x10\x00\x12\t\n\x05\x45MOJI\x10\x01\x12\x08\n\x04ICON\x10\x02\x42\x06\n\x04typeB*\n\x1c\x63om.snowflake.apps.streamlitB\nBlockProtob\x06proto3')
17
17
 
18
18
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
19
19
  _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'streamlit.proto.Block_pb2', globals())
@@ -22,25 +22,29 @@ if _descriptor._USE_C_DESCRIPTORS == False:
22
22
  DESCRIPTOR._options = None
23
23
  DESCRIPTOR._serialized_options = b'\n\034com.snowflake.apps.streamlitB\nBlockProto'
24
24
  _BLOCK._serialized_start=32
25
- _BLOCK._serialized_end=925
26
- _BLOCK_VERTICAL._serialized_start=395
27
- _BLOCK_VERTICAL._serialized_end=437
28
- _BLOCK_HORIZONTAL._serialized_start=439
29
- _BLOCK_HORIZONTAL._serialized_end=464
30
- _BLOCK_COLUMN._serialized_start=466
31
- _BLOCK_COLUMN._serialized_end=503
32
- _BLOCK_EXPANDABLE._serialized_start=505
33
- _BLOCK_EXPANDABLE._serialized_end=582
34
- _BLOCK_FORM._serialized_start=584
35
- _BLOCK_FORM._serialized_end=648
36
- _BLOCK_TABCONTAINER._serialized_start=650
37
- _BLOCK_TABCONTAINER._serialized_end=664
38
- _BLOCK_TAB._serialized_start=666
39
- _BLOCK_TAB._serialized_end=686
40
- _BLOCK_POPOVER._serialized_start=688
41
- _BLOCK_POPOVER._serialized_end=773
42
- _BLOCK_CHATMESSAGE._serialized_start=776
43
- _BLOCK_CHATMESSAGE._serialized_end=917
44
- _BLOCK_CHATMESSAGE_AVATARTYPE._serialized_start=873
45
- _BLOCK_CHATMESSAGE_AVATARTYPE._serialized_end=917
25
+ _BLOCK._serialized_end=1118
26
+ _BLOCK_VERTICAL._serialized_start=428
27
+ _BLOCK_VERTICAL._serialized_end=470
28
+ _BLOCK_HORIZONTAL._serialized_start=472
29
+ _BLOCK_HORIZONTAL._serialized_end=497
30
+ _BLOCK_COLUMN._serialized_start=499
31
+ _BLOCK_COLUMN._serialized_end=536
32
+ _BLOCK_EXPANDABLE._serialized_start=538
33
+ _BLOCK_EXPANDABLE._serialized_end=615
34
+ _BLOCK_DIALOG._serialized_start=618
35
+ _BLOCK_DIALOG._serialized_end=775
36
+ _BLOCK_DIALOG_DIALOGWIDTH._serialized_start=728
37
+ _BLOCK_DIALOG_DIALOGWIDTH._serialized_end=763
38
+ _BLOCK_FORM._serialized_start=777
39
+ _BLOCK_FORM._serialized_end=841
40
+ _BLOCK_TABCONTAINER._serialized_start=843
41
+ _BLOCK_TABCONTAINER._serialized_end=857
42
+ _BLOCK_TAB._serialized_start=859
43
+ _BLOCK_TAB._serialized_end=879
44
+ _BLOCK_POPOVER._serialized_start=881
45
+ _BLOCK_POPOVER._serialized_end=966
46
+ _BLOCK_CHATMESSAGE._serialized_start=969
47
+ _BLOCK_CHATMESSAGE._serialized_end=1110
48
+ _BLOCK_CHATMESSAGE_AVATARTYPE._serialized_start=1066
49
+ _BLOCK_CHATMESSAGE_AVATARTYPE._serialized_end=1110
46
50
  # @@protoc_insertion_point(module_scope)
@@ -96,6 +96,42 @@ class Block(google.protobuf.message.Message):
96
96
  def ClearField(self, field_name: typing_extensions.Literal["_expanded", b"_expanded", "expanded", b"expanded", "icon", b"icon", "label", b"label"]) -> None: ...
97
97
  def WhichOneof(self, oneof_group: typing_extensions.Literal["_expanded", b"_expanded"]) -> typing_extensions.Literal["expanded"] | None: ...
98
98
 
99
+ class Dialog(google.protobuf.message.Message):
100
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
101
+
102
+ class _DialogWidth:
103
+ ValueType = typing.NewType("ValueType", builtins.int)
104
+ V: typing_extensions.TypeAlias = ValueType
105
+
106
+ class _DialogWidthEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Block.Dialog._DialogWidth.ValueType], builtins.type): # noqa: F821
107
+ DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
108
+ SMALL: Block.Dialog._DialogWidth.ValueType # 0
109
+ LARGE: Block.Dialog._DialogWidth.ValueType # 1
110
+
111
+ class DialogWidth(_DialogWidth, metaclass=_DialogWidthEnumTypeWrapper): ...
112
+ SMALL: Block.Dialog.DialogWidth.ValueType # 0
113
+ LARGE: Block.Dialog.DialogWidth.ValueType # 1
114
+
115
+ TITLE_FIELD_NUMBER: builtins.int
116
+ DISMISSIBLE_FIELD_NUMBER: builtins.int
117
+ WIDTH_FIELD_NUMBER: builtins.int
118
+ IS_OPEN_FIELD_NUMBER: builtins.int
119
+ title: builtins.str
120
+ dismissible: builtins.bool
121
+ width: global___Block.Dialog.DialogWidth.ValueType
122
+ is_open: builtins.bool
123
+ def __init__(
124
+ self,
125
+ *,
126
+ title: builtins.str = ...,
127
+ dismissible: builtins.bool = ...,
128
+ width: global___Block.Dialog.DialogWidth.ValueType = ...,
129
+ is_open: builtins.bool | None = ...,
130
+ ) -> None: ...
131
+ def HasField(self, field_name: typing_extensions.Literal["_is_open", b"_is_open", "is_open", b"is_open"]) -> builtins.bool: ...
132
+ def ClearField(self, field_name: typing_extensions.Literal["_is_open", b"_is_open", "dismissible", b"dismissible", "is_open", b"is_open", "title", b"title", "width", b"width"]) -> None: ...
133
+ def WhichOneof(self, oneof_group: typing_extensions.Literal["_is_open", b"_is_open"]) -> typing_extensions.Literal["is_open"] | None: ...
134
+
99
135
  class Form(google.protobuf.message.Message):
100
136
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
101
137
 
@@ -196,6 +232,7 @@ class Block(google.protobuf.message.Message):
196
232
  TAB_FIELD_NUMBER: builtins.int
197
233
  CHAT_MESSAGE_FIELD_NUMBER: builtins.int
198
234
  POPOVER_FIELD_NUMBER: builtins.int
235
+ DIALOG_FIELD_NUMBER: builtins.int
199
236
  ALLOW_EMPTY_FIELD_NUMBER: builtins.int
200
237
  @property
201
238
  def vertical(self) -> global___Block.Vertical: ...
@@ -215,6 +252,8 @@ class Block(google.protobuf.message.Message):
215
252
  def chat_message(self) -> global___Block.ChatMessage: ...
216
253
  @property
217
254
  def popover(self) -> global___Block.Popover: ...
255
+ @property
256
+ def dialog(self) -> global___Block.Dialog: ...
218
257
  allow_empty: builtins.bool
219
258
  def __init__(
220
259
  self,
@@ -228,10 +267,11 @@ class Block(google.protobuf.message.Message):
228
267
  tab: global___Block.Tab | None = ...,
229
268
  chat_message: global___Block.ChatMessage | None = ...,
230
269
  popover: global___Block.Popover | None = ...,
270
+ dialog: global___Block.Dialog | None = ...,
231
271
  allow_empty: builtins.bool = ...,
232
272
  ) -> None: ...
233
- def HasField(self, field_name: typing_extensions.Literal["chat_message", b"chat_message", "column", b"column", "expandable", b"expandable", "form", b"form", "horizontal", b"horizontal", "popover", b"popover", "tab", b"tab", "tab_container", b"tab_container", "type", b"type", "vertical", b"vertical"]) -> builtins.bool: ...
234
- def ClearField(self, field_name: typing_extensions.Literal["allow_empty", b"allow_empty", "chat_message", b"chat_message", "column", b"column", "expandable", b"expandable", "form", b"form", "horizontal", b"horizontal", "popover", b"popover", "tab", b"tab", "tab_container", b"tab_container", "type", b"type", "vertical", b"vertical"]) -> None: ...
235
- def WhichOneof(self, oneof_group: typing_extensions.Literal["type", b"type"]) -> typing_extensions.Literal["vertical", "horizontal", "column", "expandable", "form", "tab_container", "tab", "chat_message", "popover"] | None: ...
273
+ def HasField(self, field_name: typing_extensions.Literal["chat_message", b"chat_message", "column", b"column", "dialog", b"dialog", "expandable", b"expandable", "form", b"form", "horizontal", b"horizontal", "popover", b"popover", "tab", b"tab", "tab_container", b"tab_container", "type", b"type", "vertical", b"vertical"]) -> builtins.bool: ...
274
+ def ClearField(self, field_name: typing_extensions.Literal["allow_empty", b"allow_empty", "chat_message", b"chat_message", "column", b"column", "dialog", b"dialog", "expandable", b"expandable", "form", b"form", "horizontal", b"horizontal", "popover", b"popover", "tab", b"tab", "tab_container", b"tab_container", "type", b"type", "vertical", b"vertical"]) -> None: ...
275
+ def WhichOneof(self, oneof_group: typing_extensions.Literal["type", b"type"]) -> typing_extensions.Literal["vertical", "horizontal", "column", "expandable", "form", "tab_container", "tab", "chat_message", "popover", "dialog"] | None: ...
236
276
 
237
277
  global___Block = Block