streamlit-nightly 1.32.3.dev20240325__py2.py3-none-any.whl → 1.32.3.dev20240328__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/__init__.py +4 -2
- streamlit/components/v1/__init__.py +3 -17
- streamlit/components/v1/{custom_component.py → components.py} +159 -11
- streamlit/delta_generator.py +3 -0
- streamlit/elements/media.py +52 -4
- streamlit/elements/widgets/time_widgets.py +5 -21
- streamlit/errors.py +0 -6
- streamlit/proto/AutoRerun_pb2.py +25 -0
- streamlit/proto/AutoRerun_pb2.pyi +48 -0
- streamlit/proto/ClientState_pb2.py +3 -3
- streamlit/proto/ClientState_pb2.pyi +4 -1
- streamlit/proto/Delta_pb2.py +2 -2
- streamlit/proto/Delta_pb2.pyi +4 -1
- streamlit/proto/ForwardMsg_pb2.py +10 -9
- streamlit/proto/ForwardMsg_pb2.pyi +12 -3
- streamlit/proto/NewSession_pb2.py +24 -24
- streamlit/proto/NewSession_pb2.pyi +8 -1
- streamlit/proto/PageProfile_pb2.py +6 -6
- streamlit/proto/PageProfile_pb2.pyi +4 -1
- streamlit/runtime/app_session.py +67 -24
- streamlit/runtime/caching/cache_data_api.py +3 -3
- streamlit/runtime/caching/cache_errors.py +0 -11
- streamlit/runtime/caching/cache_resource_api.py +2 -2
- streamlit/runtime/caching/cache_utils.py +1 -43
- streamlit/runtime/fragment.py +239 -0
- streamlit/runtime/metrics_util.py +17 -9
- streamlit/runtime/runtime.py +6 -12
- streamlit/runtime/runtime_util.py +54 -2
- streamlit/runtime/scriptrunner/script_requests.py +53 -37
- streamlit/runtime/scriptrunner/script_run_context.py +15 -2
- streamlit/runtime/scriptrunner/script_runner.py +63 -14
- streamlit/runtime/state/common.py +2 -0
- streamlit/runtime/state/session_state.py +51 -7
- streamlit/runtime/state/widgets.py +10 -2
- streamlit/static/asset-manifest.json +19 -19
- streamlit/static/index.html +1 -1
- streamlit/static/static/js/1074.73973756.chunk.js +1 -0
- streamlit/static/static/js/1451.3b0a3e31.chunk.js +1 -0
- streamlit/static/static/js/1792.b8efa879.chunk.js +1 -0
- streamlit/static/static/js/{3092.3d4df25e.chunk.js → 3092.ad569cc8.chunk.js} +1 -1
- streamlit/static/static/js/3513.e3e7300a.chunk.js +1 -0
- streamlit/static/static/js/4177.69f9f18d.chunk.js +1 -0
- streamlit/static/static/js/4319.a6745434.chunk.js +1 -0
- streamlit/static/static/js/{4477.2555c11a.chunk.js → 4477.e10e4373.chunk.js} +1 -1
- streamlit/static/static/js/{4666.99f3abc3.chunk.js → 4666.b694c5a9.chunk.js} +1 -1
- streamlit/static/static/js/5106.44f0ff51.chunk.js +1 -0
- streamlit/static/static/js/5379.6571574f.chunk.js +1 -0
- streamlit/static/static/js/6013.8e80e091.chunk.js +1 -0
- streamlit/static/static/js/6718.802da17e.chunk.js +1 -0
- streamlit/static/static/js/7175.be4076bc.chunk.js +1 -0
- streamlit/static/static/js/{7602.f0420392.chunk.js → 7602.6175e969.chunk.js} +1 -1
- streamlit/static/static/js/{8492.e6dab83f.chunk.js → 8492.f56c9d4c.chunk.js} +1 -1
- streamlit/static/static/js/8691.9ccf7f89.chunk.js +1 -0
- streamlit/static/static/js/main.722453f0.js +2 -0
- streamlit/testing/v1/local_script_runner.py +2 -0
- streamlit/time_util.py +88 -0
- streamlit/watcher/local_sources_watcher.py +2 -1
- streamlit/web/server/component_request_handler.py +2 -2
- streamlit/web/server/server.py +2 -1
- {streamlit_nightly-1.32.3.dev20240325.dist-info → streamlit_nightly-1.32.3.dev20240328.dist-info}/METADATA +1 -1
- {streamlit_nightly-1.32.3.dev20240325.dist-info → streamlit_nightly-1.32.3.dev20240328.dist-info}/RECORD +66 -68
- streamlit/components/lib/__init__.py +0 -13
- streamlit/components/lib/local_component_registry.py +0 -82
- streamlit/components/types/__init__.py +0 -13
- streamlit/components/types/base_component_registry.py +0 -98
- streamlit/components/types/base_custom_component.py +0 -137
- streamlit/components/v1/component_registry.py +0 -103
- streamlit/static/static/js/1074.71719df6.chunk.js +0 -1
- streamlit/static/static/js/1451.e3be1711.chunk.js +0 -1
- streamlit/static/static/js/1792.16c16498.chunk.js +0 -1
- streamlit/static/static/js/3513.57cff89c.chunk.js +0 -1
- streamlit/static/static/js/4177.ab9a7aa1.chunk.js +0 -1
- streamlit/static/static/js/4319.213fc321.chunk.js +0 -1
- streamlit/static/static/js/5106.22187bfc.chunk.js +0 -1
- streamlit/static/static/js/5379.e466522d.chunk.js +0 -1
- streamlit/static/static/js/6013.75c92264.chunk.js +0 -1
- streamlit/static/static/js/6718.97945fc6.chunk.js +0 -1
- streamlit/static/static/js/7175.8c1b4d38.chunk.js +0 -1
- streamlit/static/static/js/8691.24a5792f.chunk.js +0 -1
- streamlit/static/static/js/main.7fde7092.js +0 -2
- /streamlit/static/static/js/{main.7fde7092.js.LICENSE.txt → main.722453f0.js.LICENSE.txt} +0 -0
- {streamlit_nightly-1.32.3.dev20240325.data → streamlit_nightly-1.32.3.dev20240328.data}/scripts/streamlit.cmd +0 -0
- {streamlit_nightly-1.32.3.dev20240325.dist-info → streamlit_nightly-1.32.3.dev20240328.dist-info}/WHEEL +0 -0
- {streamlit_nightly-1.32.3.dev20240325.dist-info → streamlit_nightly-1.32.3.dev20240328.dist-info}/entry_points.txt +0 -0
- {streamlit_nightly-1.32.3.dev20240325.dist-info → streamlit_nightly-1.32.3.dev20240328.dist-info}/top_level.txt +0 -0
streamlit/__init__.py
CHANGED
@@ -81,6 +81,7 @@ from streamlit.runtime.caching import (
|
|
81
81
|
from streamlit.runtime.connection_factory import (
|
82
82
|
connection_factory as _connection,
|
83
83
|
)
|
84
|
+
from streamlit.runtime.fragment import fragment as _fragment
|
84
85
|
from streamlit.runtime.metrics_util import gather_metrics as _gather_metrics
|
85
86
|
from streamlit.runtime.secrets import secrets_singleton as _secrets_singleton
|
86
87
|
from streamlit.runtime.state import (
|
@@ -230,9 +231,10 @@ column_config = _column_config
|
|
230
231
|
connection = _connection
|
231
232
|
|
232
233
|
# Experimental APIs
|
233
|
-
|
234
|
-
experimental_singleton = _experimental_singleton
|
234
|
+
experimental_fragment = _fragment
|
235
235
|
experimental_memo = _experimental_memo
|
236
|
+
experimental_singleton = _experimental_singleton
|
237
|
+
experimental_user = _UserInfoProxy()
|
236
238
|
|
237
239
|
_EXPERIMENTAL_QUERY_PARAMS_DEPRECATE_MSG = "Refer to our [docs page](https://docs.streamlit.io/library/api-reference/utilities/st.query_params) for more information."
|
238
240
|
|
@@ -12,29 +12,15 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
"""
|
16
|
-
This directory contains the files and modules for the exposed API.
|
17
|
-
"""
|
18
|
-
|
19
|
-
import streamlit
|
20
|
-
|
21
|
-
# The `custom_component as components` import exists as existing custom components have started
|
22
|
-
# to rely on internals of the components package. For example, streamlit-option-menu accesses
|
23
|
-
# [register_widget](https://github.com/victoryhb/streamlit-option-menu/blob/master/streamlit_option_menu/streamlit_callback.py#L28),
|
24
|
-
# which is only a transitive import through `streamlit.components.v1.custom_component`.
|
25
|
-
# Since we do not know what other internals are used out in the wild, let's try to
|
26
|
-
# model the old behavior and not to break things. Ideally, custom component implementors
|
27
|
-
# use officially exposed APIs only or do direct imports instead of indirect imports.
|
28
|
-
from streamlit.components.v1 import custom_component as components
|
29
|
-
from streamlit.components.v1.component_registry import declare_component
|
30
|
-
|
31
15
|
# `html` and `iframe` are part of Custom Components, so they appear in this
|
32
16
|
# `streamlit.components.v1` namespace.
|
17
|
+
import streamlit
|
18
|
+
from streamlit.components.v1.components import declare_component
|
19
|
+
|
33
20
|
html = streamlit._main._html
|
34
21
|
iframe = streamlit._main._iframe
|
35
22
|
|
36
23
|
__all__ = [
|
37
|
-
"components",
|
38
24
|
"declare_component",
|
39
25
|
"html",
|
40
26
|
"iframe",
|
@@ -14,13 +14,17 @@
|
|
14
14
|
|
15
15
|
from __future__ import annotations
|
16
16
|
|
17
|
+
import inspect
|
17
18
|
import json
|
18
|
-
|
19
|
+
import os
|
20
|
+
import threading
|
21
|
+
from typing import TYPE_CHECKING, Any, Final
|
19
22
|
|
20
|
-
|
21
|
-
from streamlit
|
23
|
+
import streamlit
|
24
|
+
from streamlit import type_util, util
|
22
25
|
from streamlit.elements.form import current_form_id
|
23
26
|
from streamlit.errors import StreamlitAPIException
|
27
|
+
from streamlit.logger import get_logger
|
24
28
|
from streamlit.proto.Components_pb2 import ArrowTable as ArrowTableProto
|
25
29
|
from streamlit.proto.Components_pb2 import SpecialArg
|
26
30
|
from streamlit.proto.Element_pb2 import Element
|
@@ -33,6 +37,8 @@ from streamlit.type_util import to_bytes
|
|
33
37
|
if TYPE_CHECKING:
|
34
38
|
from streamlit.delta_generator import DeltaGenerator
|
35
39
|
|
40
|
+
_LOGGER: Final = get_logger(__name__)
|
41
|
+
|
36
42
|
|
37
43
|
class MarshallComponentException(StreamlitAPIException):
|
38
44
|
"""Class for exceptions generated during custom component marshalling."""
|
@@ -40,9 +46,34 @@ class MarshallComponentException(StreamlitAPIException):
|
|
40
46
|
pass
|
41
47
|
|
42
48
|
|
43
|
-
class CustomComponent
|
49
|
+
class CustomComponent:
|
44
50
|
"""A Custom Component declaration."""
|
45
51
|
|
52
|
+
def __init__(
|
53
|
+
self,
|
54
|
+
name: str,
|
55
|
+
path: str | None = None,
|
56
|
+
url: str | None = None,
|
57
|
+
):
|
58
|
+
if (path is None and url is None) or (path is not None and url is not None):
|
59
|
+
raise StreamlitAPIException(
|
60
|
+
"Either 'path' or 'url' must be set, but not both."
|
61
|
+
)
|
62
|
+
|
63
|
+
self.name = name
|
64
|
+
self.path = path
|
65
|
+
self.url = url
|
66
|
+
|
67
|
+
def __repr__(self) -> str:
|
68
|
+
return util.repr_(self)
|
69
|
+
|
70
|
+
@property
|
71
|
+
def abspath(self) -> str | None:
|
72
|
+
"""The absolute path that the component is served from."""
|
73
|
+
if self.path is None:
|
74
|
+
return None
|
75
|
+
return os.path.abspath(self.path)
|
76
|
+
|
46
77
|
def __call__(
|
47
78
|
self,
|
48
79
|
*args,
|
@@ -158,7 +189,7 @@ And if you're using Streamlit Cloud, add "pyarrow" to your requirements.txt."""
|
|
158
189
|
|
159
190
|
if key is None:
|
160
191
|
marshall_element_args()
|
161
|
-
|
192
|
+
id = compute_widget_id(
|
162
193
|
"component_instance",
|
163
194
|
user_key=key,
|
164
195
|
name=self.name,
|
@@ -170,7 +201,7 @@ And if you're using Streamlit Cloud, add "pyarrow" to your requirements.txt."""
|
|
170
201
|
page=ctx.page_script_hash if ctx else None,
|
171
202
|
)
|
172
203
|
else:
|
173
|
-
|
204
|
+
id = compute_widget_id(
|
174
205
|
"component_instance",
|
175
206
|
user_key=key,
|
176
207
|
name=self.name,
|
@@ -179,7 +210,7 @@ And if you're using Streamlit Cloud, add "pyarrow" to your requirements.txt."""
|
|
179
210
|
key=key,
|
180
211
|
page=ctx.page_script_hash if ctx else None,
|
181
212
|
)
|
182
|
-
element.component_instance.id =
|
213
|
+
element.component_instance.id = id
|
183
214
|
|
184
215
|
def deserialize_component(ui_value, widget_id=""):
|
185
216
|
# ui_value is an object from json, an ArrowTable proto, or a bytearray
|
@@ -211,7 +242,7 @@ And if you're using Streamlit Cloud, add "pyarrow" to your requirements.txt."""
|
|
211
242
|
|
212
243
|
# We currently only support writing to st._main, but this will change
|
213
244
|
# when we settle on an improved API in a post-layout world.
|
214
|
-
dg = _main
|
245
|
+
dg = streamlit._main
|
215
246
|
|
216
247
|
element = Element()
|
217
248
|
return_value = marshall_component(dg, element)
|
@@ -228,14 +259,131 @@ And if you're using Streamlit Cloud, add "pyarrow" to your requirements.txt."""
|
|
228
259
|
and self.name == other.name
|
229
260
|
and self.path == other.path
|
230
261
|
and self.url == other.url
|
231
|
-
and self.module_name == other.module_name
|
232
262
|
)
|
233
263
|
|
234
264
|
def __ne__(self, other) -> bool:
|
235
265
|
"""Inequality operator."""
|
236
|
-
|
237
|
-
# we have to use "not X == Y"" here because if we use "X != Y" we call __ne__ again and end up in recursion
|
238
266
|
return not self == other
|
239
267
|
|
240
268
|
def __str__(self) -> str:
|
241
269
|
return f"'{self.name}': {self.path if self.path is not None else self.url}"
|
270
|
+
|
271
|
+
|
272
|
+
def declare_component(
|
273
|
+
name: str,
|
274
|
+
path: str | None = None,
|
275
|
+
url: str | None = None,
|
276
|
+
) -> CustomComponent:
|
277
|
+
"""Create and register a custom component.
|
278
|
+
|
279
|
+
Parameters
|
280
|
+
----------
|
281
|
+
name: str
|
282
|
+
A short, descriptive name for the component. Like, "slider".
|
283
|
+
path: str or None
|
284
|
+
The path to serve the component's frontend files from. Either
|
285
|
+
`path` or `url` must be specified, but not both.
|
286
|
+
url: str or None
|
287
|
+
The URL that the component is served from. Either `path` or `url`
|
288
|
+
must be specified, but not both.
|
289
|
+
|
290
|
+
Returns
|
291
|
+
-------
|
292
|
+
CustomComponent
|
293
|
+
A CustomComponent that can be called like a function.
|
294
|
+
Calling the component will create a new instance of the component
|
295
|
+
in the Streamlit app.
|
296
|
+
|
297
|
+
"""
|
298
|
+
|
299
|
+
# Get our stack frame.
|
300
|
+
current_frame = inspect.currentframe()
|
301
|
+
assert current_frame is not None
|
302
|
+
|
303
|
+
# Get the stack frame of our calling function.
|
304
|
+
caller_frame = current_frame.f_back
|
305
|
+
assert caller_frame is not None
|
306
|
+
|
307
|
+
# Get the caller's module name. `__name__` gives us the module's
|
308
|
+
# fully-qualified name, which includes its package.
|
309
|
+
module = inspect.getmodule(caller_frame)
|
310
|
+
assert module is not None
|
311
|
+
module_name = module.__name__
|
312
|
+
|
313
|
+
# If the caller was the main module that was executed (that is, if the
|
314
|
+
# user executed `python my_component.py`), then this name will be
|
315
|
+
# "__main__" instead of the actual package name. In this case, we use
|
316
|
+
# the main module's filename, sans `.py` extension, as the component name.
|
317
|
+
if module_name == "__main__":
|
318
|
+
file_path = inspect.getfile(caller_frame)
|
319
|
+
filename = os.path.basename(file_path)
|
320
|
+
module_name, _ = os.path.splitext(filename)
|
321
|
+
|
322
|
+
# Build the component name.
|
323
|
+
component_name = f"{module_name}.{name}"
|
324
|
+
|
325
|
+
# Create our component object, and register it.
|
326
|
+
component = CustomComponent(name=component_name, path=path, url=url)
|
327
|
+
ComponentRegistry.instance().register_component(component)
|
328
|
+
|
329
|
+
return component
|
330
|
+
|
331
|
+
|
332
|
+
class ComponentRegistry:
|
333
|
+
_instance_lock: threading.Lock = threading.Lock()
|
334
|
+
_instance: ComponentRegistry | None = None
|
335
|
+
|
336
|
+
@classmethod
|
337
|
+
def instance(cls) -> ComponentRegistry:
|
338
|
+
"""Returns the singleton ComponentRegistry"""
|
339
|
+
# We use a double-checked locking optimization to avoid the overhead
|
340
|
+
# of acquiring the lock in the common case:
|
341
|
+
# https://en.wikipedia.org/wiki/Double-checked_locking
|
342
|
+
if cls._instance is None:
|
343
|
+
with cls._instance_lock:
|
344
|
+
if cls._instance is None:
|
345
|
+
cls._instance = ComponentRegistry()
|
346
|
+
return cls._instance
|
347
|
+
|
348
|
+
def __init__(self):
|
349
|
+
self._components: dict[str, CustomComponent] = {}
|
350
|
+
self._lock = threading.Lock()
|
351
|
+
|
352
|
+
def __repr__(self) -> str:
|
353
|
+
return util.repr_(self)
|
354
|
+
|
355
|
+
def register_component(self, component: CustomComponent) -> None:
|
356
|
+
"""Register a CustomComponent.
|
357
|
+
|
358
|
+
Parameters
|
359
|
+
----------
|
360
|
+
component : CustomComponent
|
361
|
+
The component to register.
|
362
|
+
"""
|
363
|
+
|
364
|
+
# Validate the component's path
|
365
|
+
abspath = component.abspath
|
366
|
+
if abspath is not None and not os.path.isdir(abspath):
|
367
|
+
raise StreamlitAPIException(f"No such component directory: '{abspath}'")
|
368
|
+
|
369
|
+
with self._lock:
|
370
|
+
existing = self._components.get(component.name)
|
371
|
+
self._components[component.name] = component
|
372
|
+
|
373
|
+
if existing is not None and component != existing:
|
374
|
+
_LOGGER.warning(
|
375
|
+
"%s overriding previously-registered %s",
|
376
|
+
component,
|
377
|
+
existing,
|
378
|
+
)
|
379
|
+
|
380
|
+
_LOGGER.debug("Registered component %s", component)
|
381
|
+
|
382
|
+
def get_component_path(self, name: str) -> str | None:
|
383
|
+
"""Return the filesystem path for the component with the given name.
|
384
|
+
|
385
|
+
If no such component is registered, or if the component exists but is
|
386
|
+
being served from a URL, return None instead.
|
387
|
+
"""
|
388
|
+
component = self._components.get(name, None)
|
389
|
+
return component.abspath if component is not None else None
|
streamlit/delta_generator.py
CHANGED
@@ -897,4 +897,7 @@ def _enqueue_message(msg: ForwardMsg_pb2.ForwardMsg) -> None:
|
|
897
897
|
if ctx is None:
|
898
898
|
raise NoSessionContext()
|
899
899
|
|
900
|
+
if ctx.current_fragment_id and msg.WhichOneof("type") == "delta":
|
901
|
+
msg.delta.fragment_id = ctx.current_fragment_id
|
902
|
+
|
900
903
|
ctx.enqueue(msg)
|
streamlit/elements/media.py
CHANGED
@@ -16,6 +16,7 @@ from __future__ import annotations
|
|
16
16
|
|
17
17
|
import io
|
18
18
|
import re
|
19
|
+
from datetime import timedelta
|
19
20
|
from pathlib import Path
|
20
21
|
from typing import TYPE_CHECKING, Dict, Final, Union, cast
|
21
22
|
|
@@ -29,6 +30,7 @@ from streamlit.proto.Audio_pb2 import Audio as AudioProto
|
|
29
30
|
from streamlit.proto.Video_pb2 import Video as VideoProto
|
30
31
|
from streamlit.runtime import caching
|
31
32
|
from streamlit.runtime.metrics_util import gather_metrics
|
33
|
+
from streamlit.runtime.runtime_util import duration_to_seconds
|
32
34
|
|
33
35
|
if TYPE_CHECKING:
|
34
36
|
from typing import Any
|
@@ -45,6 +47,16 @@ SubtitleData: TypeAlias = Union[
|
|
45
47
|
str, Path, bytes, io.BytesIO, Dict[str, Union[str, Path, bytes, io.BytesIO]], None
|
46
48
|
]
|
47
49
|
|
50
|
+
MediaTime: TypeAlias = Union[int, float, timedelta, str]
|
51
|
+
|
52
|
+
TIMEDELTA_PARSE_ERROR_MESSAGE: Final = (
|
53
|
+
"Failed to convert '{param_name}' to a timedelta. "
|
54
|
+
"Please use a string in a format supported by "
|
55
|
+
"[Pandas Timedelta constructor]"
|
56
|
+
"(https://pandas.pydata.org/docs/reference/api/pandas.Timedelta.html), "
|
57
|
+
'e.g. `"10s"`, `"15 seconds"`, or `"1h23s"`. Got: {param_value}'
|
58
|
+
)
|
59
|
+
|
48
60
|
|
49
61
|
class MediaMixin:
|
50
62
|
@gather_metrics("audio")
|
@@ -52,10 +64,10 @@ class MediaMixin:
|
|
52
64
|
self,
|
53
65
|
data: MediaData,
|
54
66
|
format: str = "audio/wav",
|
55
|
-
start_time:
|
67
|
+
start_time: MediaTime = 0,
|
56
68
|
*,
|
57
69
|
sample_rate: int | None = None,
|
58
|
-
end_time:
|
70
|
+
end_time: MediaTime | None = None,
|
59
71
|
loop: bool = False,
|
60
72
|
) -> DeltaGenerator:
|
61
73
|
"""Display an audio player.
|
@@ -111,6 +123,8 @@ class MediaMixin:
|
|
111
123
|
height: 865px
|
112
124
|
|
113
125
|
"""
|
126
|
+
start_time, end_time = _parse_start_time_end_time(start_time, end_time)
|
127
|
+
|
114
128
|
audio_proto = AudioProto()
|
115
129
|
coordinates = self.dg._get_delta_path_str()
|
116
130
|
|
@@ -143,10 +157,10 @@ class MediaMixin:
|
|
143
157
|
self,
|
144
158
|
data: MediaData,
|
145
159
|
format: str = "video/mp4",
|
146
|
-
start_time:
|
160
|
+
start_time: MediaTime = 0,
|
147
161
|
*, # keyword-only arguments:
|
148
162
|
subtitles: SubtitleData = None,
|
149
|
-
end_time:
|
163
|
+
end_time: MediaTime | None = None,
|
150
164
|
loop: bool = False,
|
151
165
|
) -> DeltaGenerator:
|
152
166
|
"""Display a video player.
|
@@ -246,6 +260,9 @@ class MediaMixin:
|
|
246
260
|
for more information.
|
247
261
|
|
248
262
|
"""
|
263
|
+
|
264
|
+
start_time, end_time = _parse_start_time_end_time(start_time, end_time)
|
265
|
+
|
249
266
|
video_proto = VideoProto()
|
250
267
|
coordinates = self.dg._get_delta_path_str()
|
251
268
|
marshall_video(
|
@@ -461,6 +478,37 @@ def marshall_video(
|
|
461
478
|
) from original_err
|
462
479
|
|
463
480
|
|
481
|
+
def _parse_start_time_end_time(
|
482
|
+
start_time: MediaTime, end_time: MediaTime | None
|
483
|
+
) -> tuple[int, int | None]:
|
484
|
+
"""Parse start_time and end_time and return them as int."""
|
485
|
+
|
486
|
+
try:
|
487
|
+
maybe_start_time = duration_to_seconds(start_time, coerce_none_to_inf=False)
|
488
|
+
if maybe_start_time is None:
|
489
|
+
raise ValueError
|
490
|
+
start_time = int(maybe_start_time)
|
491
|
+
except (StreamlitAPIException, ValueError):
|
492
|
+
error_msg = TIMEDELTA_PARSE_ERROR_MESSAGE.format(
|
493
|
+
param_name="start_time", param_value=start_time
|
494
|
+
)
|
495
|
+
raise StreamlitAPIException(error_msg) from None
|
496
|
+
|
497
|
+
try:
|
498
|
+
# TODO[kajarenc]: Replace `duration_to_seconds` with `time_to_seconds`
|
499
|
+
# when PR #8343 is merged.
|
500
|
+
end_time = duration_to_seconds(end_time, coerce_none_to_inf=False)
|
501
|
+
if end_time is not None:
|
502
|
+
end_time = int(end_time)
|
503
|
+
except StreamlitAPIException:
|
504
|
+
error_msg = TIMEDELTA_PARSE_ERROR_MESSAGE.format(
|
505
|
+
param_name="end_time", param_value=end_time
|
506
|
+
)
|
507
|
+
raise StreamlitAPIException(error_msg) from None
|
508
|
+
|
509
|
+
return start_time, end_time
|
510
|
+
|
511
|
+
|
464
512
|
def _validate_and_normalize(data: npt.NDArray[Any]) -> tuple[bytes, int]:
|
465
513
|
"""Validates and normalizes numpy array data.
|
466
514
|
We validate numpy array shape (should be 1d or 2d)
|
@@ -50,6 +50,7 @@ from streamlit.runtime.state import (
|
|
50
50
|
register_widget,
|
51
51
|
)
|
52
52
|
from streamlit.runtime.state.common import compute_widget_id
|
53
|
+
from streamlit.time_util import adjust_years
|
53
54
|
from streamlit.type_util import Key, LabelVisibility, maybe_raise_label_warnings, to_key
|
54
55
|
|
55
56
|
if TYPE_CHECKING:
|
@@ -67,23 +68,6 @@ ALLOWED_DATE_FORMATS: Final = re.compile(
|
|
67
68
|
)
|
68
69
|
|
69
70
|
|
70
|
-
def _adjust_years(input_date: date, years: int) -> date:
|
71
|
-
"""Add or subtract years from a date."""
|
72
|
-
try:
|
73
|
-
# Attempt to directly add/subtract years
|
74
|
-
return input_date.replace(year=input_date.year + years)
|
75
|
-
except ValueError as err:
|
76
|
-
# Handle case for leap year date (February 29) that doesn't exist in the target year
|
77
|
-
# by moving the date to February 28
|
78
|
-
if input_date.month == 2 and input_date.day == 29:
|
79
|
-
return input_date.replace(year=input_date.year + years, month=2, day=28)
|
80
|
-
|
81
|
-
raise StreamlitAPIException(
|
82
|
-
f"Date {input_date} does not exist in the target year {input_date.year + years}. "
|
83
|
-
"This should never happen. Please report this bug."
|
84
|
-
) from err
|
85
|
-
|
86
|
-
|
87
71
|
def _parse_date_value(
|
88
72
|
value: DateValue | Literal["today"] | Literal["default_value_today"],
|
89
73
|
) -> tuple[list[date] | None, bool]:
|
@@ -128,9 +112,9 @@ def _parse_min_date(
|
|
128
112
|
parsed_min_date = min_value
|
129
113
|
elif min_value is None:
|
130
114
|
if parsed_dates:
|
131
|
-
parsed_min_date =
|
115
|
+
parsed_min_date = adjust_years(parsed_dates[0], years=-10)
|
132
116
|
else:
|
133
|
-
parsed_min_date =
|
117
|
+
parsed_min_date = adjust_years(date.today(), years=-10)
|
134
118
|
else:
|
135
119
|
raise StreamlitAPIException(
|
136
120
|
"DateInput min should either be a date/datetime or None"
|
@@ -149,9 +133,9 @@ def _parse_max_date(
|
|
149
133
|
parsed_max_date = max_value
|
150
134
|
elif max_value is None:
|
151
135
|
if parsed_dates:
|
152
|
-
parsed_max_date =
|
136
|
+
parsed_max_date = adjust_years(parsed_dates[-1], years=10)
|
153
137
|
else:
|
154
|
-
parsed_max_date =
|
138
|
+
parsed_max_date = adjust_years(date.today(), years=10)
|
155
139
|
else:
|
156
140
|
raise StreamlitAPIException(
|
157
141
|
"DateInput max should either be a date/datetime or None"
|
streamlit/errors.py
CHANGED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
3
|
+
# source: streamlit/proto/AutoRerun.proto
|
4
|
+
"""Generated protocol buffer code."""
|
5
|
+
from google.protobuf.internal import builder as _builder
|
6
|
+
from google.protobuf import descriptor as _descriptor
|
7
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
8
|
+
from google.protobuf import symbol_database as _symbol_database
|
9
|
+
# @@protoc_insertion_point(imports)
|
10
|
+
|
11
|
+
_sym_db = _symbol_database.Default()
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fstreamlit/proto/AutoRerun.proto\"2\n\tAutoRerun\x12\x10\n\x08interval\x18\x01 \x01(\x02\x12\x13\n\x0b\x66ragment_id\x18\x02 \x01(\tb\x06proto3')
|
17
|
+
|
18
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
19
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'streamlit.proto.AutoRerun_pb2', globals())
|
20
|
+
if _descriptor._USE_C_DESCRIPTORS == False:
|
21
|
+
|
22
|
+
DESCRIPTOR._options = None
|
23
|
+
_AUTORERUN._serialized_start=35
|
24
|
+
_AUTORERUN._serialized_end=85
|
25
|
+
# @@protoc_insertion_point(module_scope)
|
@@ -0,0 +1,48 @@
|
|
1
|
+
"""
|
2
|
+
@generated by mypy-protobuf. Do not edit manually!
|
3
|
+
isort:skip_file
|
4
|
+
*!
|
5
|
+
Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
|
6
|
+
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
you may not use this file except in compliance with the License.
|
9
|
+
You may obtain a copy of the License at
|
10
|
+
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
See the License for the specific language governing permissions and
|
17
|
+
limitations under the License.
|
18
|
+
"""
|
19
|
+
import builtins
|
20
|
+
import google.protobuf.descriptor
|
21
|
+
import google.protobuf.message
|
22
|
+
import sys
|
23
|
+
|
24
|
+
if sys.version_info >= (3, 8):
|
25
|
+
import typing as typing_extensions
|
26
|
+
else:
|
27
|
+
import typing_extensions
|
28
|
+
|
29
|
+
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
|
30
|
+
|
31
|
+
class AutoRerun(google.protobuf.message.Message):
|
32
|
+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
33
|
+
|
34
|
+
INTERVAL_FIELD_NUMBER: builtins.int
|
35
|
+
FRAGMENT_ID_FIELD_NUMBER: builtins.int
|
36
|
+
interval: builtins.float
|
37
|
+
"""The interval of reruns in seconds"""
|
38
|
+
fragment_id: builtins.str
|
39
|
+
"""The fragment ID to rerun"""
|
40
|
+
def __init__(
|
41
|
+
self,
|
42
|
+
*,
|
43
|
+
interval: builtins.float = ...,
|
44
|
+
fragment_id: builtins.str = ...,
|
45
|
+
) -> None: ...
|
46
|
+
def ClearField(self, field_name: typing_extensions.Literal["fragment_id", b"fragment_id", "interval", b"interval"]) -> None: ...
|
47
|
+
|
48
|
+
global___AutoRerun = AutoRerun
|
@@ -14,7 +14,7 @@ _sym_db = _symbol_database.Default()
|
|
14
14
|
from streamlit.proto import WidgetStates_pb2 as streamlit_dot_proto_dot_WidgetStates__pb2
|
15
15
|
|
16
16
|
|
17
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!streamlit/proto/ClientState.proto\x1a\"streamlit/proto/WidgetStates.proto\"
|
17
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!streamlit/proto/ClientState.proto\x1a\"streamlit/proto/WidgetStates.proto\"\x8b\x01\n\x0b\x43lientState\x12\x14\n\x0cquery_string\x18\x01 \x01(\t\x12$\n\rwidget_states\x18\x02 \x01(\x0b\x32\r.WidgetStates\x12\x18\n\x10page_script_hash\x18\x03 \x01(\t\x12\x11\n\tpage_name\x18\x04 \x01(\t\x12\x13\n\x0b\x66ragment_id\x18\x05 \x01(\tB0\n\x1c\x63om.snowflake.apps.streamlitB\x10\x43lientStateProtob\x06proto3')
|
18
18
|
|
19
19
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
20
20
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'streamlit.proto.ClientState_pb2', globals())
|
@@ -22,6 +22,6 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
|
22
22
|
|
23
23
|
DESCRIPTOR._options = None
|
24
24
|
DESCRIPTOR._serialized_options = b'\n\034com.snowflake.apps.streamlitB\020ClientStateProto'
|
25
|
-
_CLIENTSTATE._serialized_start=
|
26
|
-
_CLIENTSTATE._serialized_end=
|
25
|
+
_CLIENTSTATE._serialized_start=74
|
26
|
+
_CLIENTSTATE._serialized_end=213
|
27
27
|
# @@protoc_insertion_point(module_scope)
|
@@ -36,11 +36,13 @@ class ClientState(google.protobuf.message.Message):
|
|
36
36
|
WIDGET_STATES_FIELD_NUMBER: builtins.int
|
37
37
|
PAGE_SCRIPT_HASH_FIELD_NUMBER: builtins.int
|
38
38
|
PAGE_NAME_FIELD_NUMBER: builtins.int
|
39
|
+
FRAGMENT_ID_FIELD_NUMBER: builtins.int
|
39
40
|
query_string: builtins.str
|
40
41
|
@property
|
41
42
|
def widget_states(self) -> streamlit.proto.WidgetStates_pb2.WidgetStates: ...
|
42
43
|
page_script_hash: builtins.str
|
43
44
|
page_name: builtins.str
|
45
|
+
fragment_id: builtins.str
|
44
46
|
def __init__(
|
45
47
|
self,
|
46
48
|
*,
|
@@ -48,8 +50,9 @@ class ClientState(google.protobuf.message.Message):
|
|
48
50
|
widget_states: streamlit.proto.WidgetStates_pb2.WidgetStates | None = ...,
|
49
51
|
page_script_hash: builtins.str = ...,
|
50
52
|
page_name: builtins.str = ...,
|
53
|
+
fragment_id: builtins.str = ...,
|
51
54
|
) -> None: ...
|
52
55
|
def HasField(self, field_name: typing_extensions.Literal["widget_states", b"widget_states"]) -> builtins.bool: ...
|
53
|
-
def ClearField(self, field_name: typing_extensions.Literal["page_name", b"page_name", "page_script_hash", b"page_script_hash", "query_string", b"query_string", "widget_states", b"widget_states"]) -> None: ...
|
56
|
+
def ClearField(self, field_name: typing_extensions.Literal["fragment_id", b"fragment_id", "page_name", b"page_name", "page_script_hash", b"page_script_hash", "query_string", b"query_string", "widget_states", b"widget_states"]) -> None: ...
|
54
57
|
|
55
58
|
global___ClientState = ClientState
|
streamlit/proto/Delta_pb2.py
CHANGED
@@ -17,7 +17,7 @@ from streamlit.proto import NamedDataSet_pb2 as streamlit_dot_proto_dot_NamedDat
|
|
17
17
|
from streamlit.proto import ArrowNamedDataSet_pb2 as streamlit_dot_proto_dot_ArrowNamedDataSet__pb2
|
18
18
|
|
19
19
|
|
20
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bstreamlit/proto/Delta.proto\x1a\x1bstreamlit/proto/Block.proto\x1a\x1dstreamlit/proto/Element.proto\x1a\"streamlit/proto/NamedDataSet.proto\x1a\'streamlit/proto/ArrowNamedDataSet.proto\"\
|
20
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1bstreamlit/proto/Delta.proto\x1a\x1bstreamlit/proto/Block.proto\x1a\x1dstreamlit/proto/Element.proto\x1a\"streamlit/proto/NamedDataSet.proto\x1a\'streamlit/proto/ArrowNamedDataSet.proto\"\xb3\x01\n\x05\x44\x65lta\x12\x1f\n\x0bnew_element\x18\x03 \x01(\x0b\x32\x08.ElementH\x00\x12\x1b\n\tadd_block\x18\x06 \x01(\x0b\x32\x06.BlockH\x00\x12!\n\x08\x61\x64\x64_rows\x18\x05 \x01(\x0b\x32\r.NamedDataSetH\x00\x12,\n\x0e\x61rrow_add_rows\x18\x07 \x01(\x0b\x32\x12.ArrowNamedDataSetH\x00\x12\x13\n\x0b\x66ragment_id\x18\x08 \x01(\tB\x06\n\x04typeB*\n\x1c\x63om.snowflake.apps.streamlitB\nDeltaProtob\x06proto3')
|
21
21
|
|
22
22
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
23
23
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'streamlit.proto.Delta_pb2', globals())
|
@@ -26,5 +26,5 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
|
26
26
|
DESCRIPTOR._options = None
|
27
27
|
DESCRIPTOR._serialized_options = b'\n\034com.snowflake.apps.streamlitB\nDeltaProto'
|
28
28
|
_DELTA._serialized_start=169
|
29
|
-
_DELTA._serialized_end=
|
29
|
+
_DELTA._serialized_end=348
|
30
30
|
# @@protoc_insertion_point(module_scope)
|
streamlit/proto/Delta_pb2.pyi
CHANGED
@@ -41,6 +41,7 @@ class Delta(google.protobuf.message.Message):
|
|
41
41
|
ADD_BLOCK_FIELD_NUMBER: builtins.int
|
42
42
|
ADD_ROWS_FIELD_NUMBER: builtins.int
|
43
43
|
ARROW_ADD_ROWS_FIELD_NUMBER: builtins.int
|
44
|
+
FRAGMENT_ID_FIELD_NUMBER: builtins.int
|
44
45
|
@property
|
45
46
|
def new_element(self) -> streamlit.proto.Element_pb2.Element:
|
46
47
|
"""Append a new element to the frontend."""
|
@@ -56,6 +57,7 @@ class Delta(google.protobuf.message.Message):
|
|
56
57
|
"""
|
57
58
|
@property
|
58
59
|
def arrow_add_rows(self) -> streamlit.proto.ArrowNamedDataSet_pb2.ArrowNamedDataSet: ...
|
60
|
+
fragment_id: builtins.str
|
59
61
|
def __init__(
|
60
62
|
self,
|
61
63
|
*,
|
@@ -63,9 +65,10 @@ class Delta(google.protobuf.message.Message):
|
|
63
65
|
add_block: streamlit.proto.Block_pb2.Block | None = ...,
|
64
66
|
add_rows: streamlit.proto.NamedDataSet_pb2.NamedDataSet | None = ...,
|
65
67
|
arrow_add_rows: streamlit.proto.ArrowNamedDataSet_pb2.ArrowNamedDataSet | None = ...,
|
68
|
+
fragment_id: builtins.str = ...,
|
66
69
|
) -> None: ...
|
67
70
|
def HasField(self, field_name: typing_extensions.Literal["add_block", b"add_block", "add_rows", b"add_rows", "arrow_add_rows", b"arrow_add_rows", "new_element", b"new_element", "type", b"type"]) -> builtins.bool: ...
|
68
|
-
def ClearField(self, field_name: typing_extensions.Literal["add_block", b"add_block", "add_rows", b"add_rows", "arrow_add_rows", b"arrow_add_rows", "new_element", b"new_element", "type", b"type"]) -> None: ...
|
71
|
+
def ClearField(self, field_name: typing_extensions.Literal["add_block", b"add_block", "add_rows", b"add_rows", "arrow_add_rows", b"arrow_add_rows", "fragment_id", b"fragment_id", "new_element", b"new_element", "type", b"type"]) -> None: ...
|
69
72
|
def WhichOneof(self, oneof_group: typing_extensions.Literal["type", b"type"]) -> typing_extensions.Literal["new_element", "add_block", "add_rows", "arrow_add_rows"] | None: ...
|
70
73
|
|
71
74
|
global___Delta = Delta
|