streamlit-nightly 1.46.2.dev20250630__py3-none-any.whl → 1.46.2.dev20250702__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/config.py +21 -0
- streamlit/elements/doc_string.py +3 -0
- streamlit/elements/lib/column_types.py +37 -20
- streamlit/proto/NewSession_pb2.py +16 -16
- streamlit/proto/NewSession_pb2.pyi +5 -1
- streamlit/runtime/app_session.py +49 -1
- streamlit/runtime/caching/cache_data_api.py +34 -55
- streamlit/runtime/caching/cache_errors.py +9 -9
- streamlit/runtime/caching/cache_resource_api.py +26 -44
- streamlit/runtime/caching/cache_utils.py +37 -24
- streamlit/runtime/caching/cached_message_replay.py +13 -6
- streamlit/static/index.html +1 -1
- streamlit/static/manifest.json +219 -219
- streamlit/static/static/css/index.2-JSeiMQ.css +1 -0
- streamlit/static/static/js/{ErrorOutline.esm.Dxjg6IRT.js → ErrorOutline.esm.MhIoYdvl.js} +1 -1
- streamlit/static/static/js/{FileDownload.esm.DUks_wV_.js → FileDownload.esm.MFVQ8TeX.js} +1 -1
- streamlit/static/static/js/{FileHelper.BbPTP1JB.js → FileHelper.4MaelLoM.js} +1 -1
- streamlit/static/static/js/{FormClearHelper.CPbpL4Iu.js → FormClearHelper.B0jg0hVR.js} +1 -1
- streamlit/static/static/js/{Hooks.CxUXOgrk.js → Hooks.BRUa3Uwi.js} +1 -1
- streamlit/static/static/js/{InputInstructions.DJ1rHmtw.js → InputInstructions.DIDrjUfW.js} +1 -1
- streamlit/static/static/js/{ProgressBar.CHuwkwTP.js → ProgressBar.D5j4Grse.js} +1 -1
- streamlit/static/static/js/{RenderInPortalIfExists.D95lvcfx.js → RenderInPortalIfExists.DSEpNV6C.js} +1 -1
- streamlit/static/static/js/Toolbar.CQcJmHso.js +1 -0
- streamlit/static/static/js/{base-input.WYe9-ubW.js → base-input.BnnSlzQu.js} +1 -1
- streamlit/static/static/js/{checkbox.q85ZLs2m.js → checkbox.C3U3kKA7.js} +1 -1
- streamlit/static/static/js/{createSuper.B8q8YLDg.js → createSuper.Cy5MeyJ7.js} +1 -1
- streamlit/static/static/js/data-grid-overlay-editor.DfB1NFlz.js +1 -0
- streamlit/static/static/js/{downloader.DNzHzyGV.js → downloader.DJTt16dd.js} +1 -1
- streamlit/static/static/js/{es6.DspIW9uw.js → es6.wyhZ4wJk.js} +2 -2
- streamlit/static/static/js/{iframeResizer.contentWindow.r0RTNCiE.js → iframeResizer.contentWindow.DFyVP9Ig.js} +1 -1
- streamlit/static/static/js/{index.yF9-AUSb.js → index.B-X33K52.js} +1 -1
- streamlit/static/static/js/{index.BlCKLDOZ.js → index.B22EHN7-.js} +1 -1
- streamlit/static/static/js/{index.Da1q_KWc.js → index.B465_5qO.js} +1 -1
- streamlit/static/static/js/{index.CW9dTClp.js → index.BHoorvVC.js} +70 -70
- streamlit/static/static/js/{index.ChKqi4i3.js → index.BMd4yctQ.js} +1 -1
- streamlit/static/static/js/{index.Cu1q0VW4.js → index.BNiT2b3M.js} +1 -1
- streamlit/static/static/js/{index.Ca1oUX_q.js → index.BSHRz8-s.js} +1 -1
- streamlit/static/static/js/{index.fwPkRvFV.js → index.C2ZYRKqZ.js} +1 -1
- streamlit/static/static/js/{index.3xavF_0n.js → index.C4H2pObU.js} +1 -1
- streamlit/static/static/js/{index.CBTQKi-P.js → index.CDhJdsJ-.js} +1 -1
- streamlit/static/static/js/{index.CAv2zgjK.js → index.CDk53kDT.js} +1 -1
- streamlit/static/static/js/{index.1c3oM2N7.js → index.CG52VtIR.js} +1 -1
- streamlit/static/static/js/{index.DcWAX9sK.js → index.CLg6_EPb.js} +1 -1
- streamlit/static/static/js/{index.3gA1NIOE.js → index.C_ub2Pfc.js} +1 -1
- streamlit/static/static/js/{index.fMByvmk4.js → index.Cipw6ZXy.js} +1 -1
- streamlit/static/static/js/{index.Dl3PDYIm.js → index.CvEUwmm2.js} +1 -1
- streamlit/static/static/js/{index.DI6GST9u.js → index.Cx59kXcb.js} +1 -1
- streamlit/static/static/js/{index.CJVmqGWb.js → index.D-doFAXM.js} +1 -1
- streamlit/static/static/js/{index.JtAQSo4b.js → index.DEc9e9_6.js} +1 -1
- streamlit/static/static/js/index.DFtXcE7L.js +197 -0
- streamlit/static/static/js/{index.CVmI74U6.js → index.DJzsQdj5.js} +1 -1
- streamlit/static/static/js/{index.FoBY-QWg.js → index.DLQQdwIj.js} +1 -1
- streamlit/static/static/js/{index.CGyNHtSj.js → index.DRodAgy0.js} +1 -1
- streamlit/static/static/js/{index.AWNbYFrb.js → index.DT8f2xQ9.js} +1 -1
- streamlit/static/static/js/{index.BN07pOu4.js → index.DcEV00Pn.js} +1 -1
- streamlit/static/static/js/{index.BJraUn_t.js → index.Do1oo2Dl.js} +1 -1
- streamlit/static/static/js/{index.BxV4Hdx3.js → index.Dr7_LGVT.js} +1 -1
- streamlit/static/static/js/{index.QwG_LwRf.js → index.DsdPm0SS.js} +1 -1
- streamlit/static/static/js/{index.DAHN8zmn.js → index.Dsil7y_J.js} +1 -1
- streamlit/static/static/js/{index.-rWhcLJP.js → index.DtbQuv3u.js} +1 -1
- streamlit/static/static/js/{index.mPd_0hyY.js → index.DyTIPa0F.js} +1 -1
- streamlit/static/static/js/{index.CobtVpzC.js → index.DynUmaAG.js} +1 -1
- streamlit/static/static/js/{index.CZ4DY8ui.js → index.DzC4L_Us.js} +1 -1
- streamlit/static/static/js/{index.CwsfJRGf.js → index.UFChjuC3.js} +1 -1
- streamlit/static/static/js/{index.TRFuoL4h.js → index.gMS0zlRZ.js} +2 -2
- streamlit/static/static/js/{index.BRKsam4Y.js → index.lQYpmexK.js} +1 -1
- streamlit/static/static/js/{index.zK_Kki5l.js → index.z241Yt9a.js} +1 -1
- streamlit/static/static/js/{input.BblW0DV1.js → input.C8HOZnEd.js} +1 -1
- streamlit/static/static/js/{memory.BFpqnunH.js → memory.8fc2LDiK.js} +1 -1
- streamlit/static/static/js/{mergeWith.DMaSpMaL.js → mergeWith.xmxP-tqn.js} +1 -1
- streamlit/static/static/js/{number-overlay-editor.Cr9sn26b.js → number-overlay-editor.DtoLRumH.js} +1 -1
- streamlit/static/static/js/{possibleConstructorReturn.B3NcCHDd.js → possibleConstructorReturn.DqUCGXEg.js} +1 -1
- streamlit/static/static/js/{sandbox.3jFBLWNf.js → sandbox.de7mpc93.js} +1 -1
- streamlit/static/static/js/{textarea.dsrIcAKo.js → textarea.9dU6cbLz.js} +1 -1
- streamlit/static/static/js/{timepicker.CbF35-hi.js → timepicker.DANyzCwb.js} +1 -1
- streamlit/static/static/js/{toConsumableArray.BqKiiQ0r.js → toConsumableArray.Bja4AaDo.js} +1 -1
- streamlit/static/static/js/{uniqueId.DrVTLd2c.js → uniqueId.q5r0gCRd.js} +1 -1
- streamlit/static/static/js/{useBasicWidgetState.CTBqw8Kt.js → useBasicWidgetState.B0oxOutH.js} +1 -1
- streamlit/static/static/js/{useOnInputChange.D1XRaVU2.js → useOnInputChange.XxzVJXCy.js} +1 -1
- streamlit/static/static/js/{withFullScreenWrapper.CbCRhtqE.js → withFullScreenWrapper.eXXoswSf.js} +1 -1
- {streamlit_nightly-1.46.2.dev20250630.dist-info → streamlit_nightly-1.46.2.dev20250702.dist-info}/METADATA +1 -1
- {streamlit_nightly-1.46.2.dev20250630.dist-info → streamlit_nightly-1.46.2.dev20250702.dist-info}/RECORD +86 -86
- streamlit/static/static/css/index.C5t3M85E.css +0 -1
- streamlit/static/static/js/Toolbar.BseShgV1.js +0 -1
- streamlit/static/static/js/data-grid-overlay-editor.Dvyj9Tv8.js +0 -1
- streamlit/static/static/js/index.BlBZJ-Nr.js +0 -197
- {streamlit_nightly-1.46.2.dev20250630.data → streamlit_nightly-1.46.2.dev20250702.data}/scripts/streamlit.cmd +0 -0
- {streamlit_nightly-1.46.2.dev20250630.dist-info → streamlit_nightly-1.46.2.dev20250702.dist-info}/WHEEL +0 -0
- {streamlit_nightly-1.46.2.dev20250630.dist-info → streamlit_nightly-1.46.2.dev20250702.dist-info}/entry_points.txt +0 -0
- {streamlit_nightly-1.46.2.dev20250630.dist-info → streamlit_nightly-1.46.2.dev20250702.dist-info}/top_level.txt +0 -0
@@ -24,10 +24,8 @@ from typing import (
|
|
24
24
|
Callable,
|
25
25
|
Final,
|
26
26
|
Literal,
|
27
|
-
Protocol,
|
28
27
|
TypeVar,
|
29
28
|
Union,
|
30
|
-
cast,
|
31
29
|
overload,
|
32
30
|
)
|
33
31
|
|
@@ -41,6 +39,7 @@ from streamlit.runtime.caching.cache_errors import CacheError, CacheKeyNotFoundE
|
|
41
39
|
from streamlit.runtime.caching.cache_type import CacheType
|
42
40
|
from streamlit.runtime.caching.cache_utils import (
|
43
41
|
Cache,
|
42
|
+
CachedFunc,
|
44
43
|
CachedFuncInfo,
|
45
44
|
make_cached_func_wrapper,
|
46
45
|
)
|
@@ -68,7 +67,6 @@ from streamlit.runtime.stats import CacheStat, CacheStatsProvider, group_stats
|
|
68
67
|
from streamlit.time_util import time_to_seconds
|
69
68
|
|
70
69
|
if TYPE_CHECKING:
|
71
|
-
import types
|
72
70
|
from datetime import timedelta
|
73
71
|
|
74
72
|
from streamlit.runtime.caching.hashing import HashFuncsDict
|
@@ -81,7 +79,11 @@ CACHE_DATA_MESSAGE_REPLAY_CTX = CachedMessageReplayContext(CacheType.DATA)
|
|
81
79
|
CachePersistType: TypeAlias = Union[Literal["disk"], None]
|
82
80
|
|
83
81
|
|
84
|
-
|
82
|
+
P = ParamSpec("P")
|
83
|
+
R = TypeVar("R")
|
84
|
+
|
85
|
+
|
86
|
+
class CachedDataFuncInfo(CachedFuncInfo[P, R]):
|
85
87
|
"""Implements the CachedFuncInfo interface for @st.cache_data."""
|
86
88
|
|
87
89
|
persist: CachePersistType
|
@@ -90,7 +92,7 @@ class CachedDataFuncInfo(CachedFuncInfo):
|
|
90
92
|
|
91
93
|
def __init__(
|
92
94
|
self,
|
93
|
-
func:
|
95
|
+
func: Callable[P, R],
|
94
96
|
persist: CachePersistType,
|
95
97
|
max_entries: int | None,
|
96
98
|
ttl: float | timedelta | str | None,
|
@@ -123,7 +125,7 @@ class CachedDataFuncInfo(CachedFuncInfo):
|
|
123
125
|
"""A human-readable name for the cached function."""
|
124
126
|
return f"{self.func.__module__}.{self.func.__qualname__}"
|
125
127
|
|
126
|
-
def get_function_cache(self, function_key: str) -> Cache:
|
128
|
+
def get_function_cache(self, function_key: str) -> Cache[R]:
|
127
129
|
return _data_caches.get_cache(
|
128
130
|
key=function_key,
|
129
131
|
persist=self.persist,
|
@@ -152,7 +154,7 @@ class DataCaches(CacheStatsProvider):
|
|
152
154
|
|
153
155
|
def __init__(self) -> None:
|
154
156
|
self._caches_lock = threading.Lock()
|
155
|
-
self._function_caches: dict[str, DataCache] = {}
|
157
|
+
self._function_caches: dict[str, DataCache[Any]] = {}
|
156
158
|
|
157
159
|
def get_cache(
|
158
160
|
self,
|
@@ -161,7 +163,7 @@ class DataCaches(CacheStatsProvider):
|
|
161
163
|
max_entries: int | None,
|
162
164
|
ttl: int | float | timedelta | str | None,
|
163
165
|
display_name: str,
|
164
|
-
) -> DataCache:
|
166
|
+
) -> DataCache[Any]:
|
165
167
|
"""Return the mem cache for the given key.
|
166
168
|
|
167
169
|
If it doesn't exist, create a new one with the given params.
|
@@ -319,29 +321,6 @@ def get_data_cache_stats_provider() -> CacheStatsProvider:
|
|
319
321
|
return _data_caches
|
320
322
|
|
321
323
|
|
322
|
-
# Type-annotate the decorator function.
|
323
|
-
# (See https://mypy.readthedocs.io/en/stable/generics.html#decorator-factories)
|
324
|
-
P = ParamSpec("P")
|
325
|
-
T_co = TypeVar("T_co", covariant=True)
|
326
|
-
|
327
|
-
|
328
|
-
class CachedFunc(Protocol[P, T_co]):
|
329
|
-
"""Protocol for cached functions that preserve the original function's signature and add a clear method."""
|
330
|
-
|
331
|
-
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T_co: ...
|
332
|
-
|
333
|
-
@overload
|
334
|
-
def clear(self) -> None: ...
|
335
|
-
|
336
|
-
@overload
|
337
|
-
def clear(self, *args: P.args, **kwargs: P.kwargs) -> None: ...
|
338
|
-
|
339
|
-
# Currently we can't define the "all-optional" argument overload with `P.args` and `P.kwargs` in Python.
|
340
|
-
# So we use `Any` as a fallback.
|
341
|
-
@overload
|
342
|
-
def clear(self, *args: Any, **kwargs: Any) -> None: ...
|
343
|
-
|
344
|
-
|
345
324
|
class CacheDataAPI:
|
346
325
|
"""Implements the public st.cache_data API: the @st.cache_data decorator, and
|
347
326
|
st.cache_data.clear().
|
@@ -362,9 +341,12 @@ class CacheDataAPI:
|
|
362
341
|
decorator_metric_name, self._decorator
|
363
342
|
)
|
364
343
|
|
344
|
+
# Type-annotate the decorator function.
|
345
|
+
# (See https://mypy.readthedocs.io/en/stable/generics.html#decorator-factories)
|
346
|
+
|
365
347
|
# Bare decorator usage
|
366
348
|
@overload
|
367
|
-
def __call__(self, func: Callable[P,
|
349
|
+
def __call__(self, func: Callable[P, R]) -> CachedFunc[P, R]: ...
|
368
350
|
|
369
351
|
# Decorator with arguments
|
370
352
|
@overload
|
@@ -378,11 +360,11 @@ class CacheDataAPI:
|
|
378
360
|
persist: CachePersistType | bool = None,
|
379
361
|
experimental_allow_widgets: bool = False,
|
380
362
|
hash_funcs: HashFuncsDict | None = None,
|
381
|
-
) -> Callable[[Callable[P,
|
363
|
+
) -> Callable[[Callable[P, R]], CachedFunc[P, R]]: ...
|
382
364
|
|
383
365
|
def __call__(
|
384
366
|
self,
|
385
|
-
func: Callable[P,
|
367
|
+
func: Callable[P, R] | None = None,
|
386
368
|
*,
|
387
369
|
ttl: float | timedelta | str | None = None,
|
388
370
|
max_entries: int | None = None,
|
@@ -391,7 +373,7 @@ class CacheDataAPI:
|
|
391
373
|
persist: CachePersistType | bool = None,
|
392
374
|
experimental_allow_widgets: bool = False,
|
393
375
|
hash_funcs: HashFuncsDict | None = None,
|
394
|
-
) -> CachedFunc[P,
|
376
|
+
) -> CachedFunc[P, R] | Callable[[Callable[P, R]], CachedFunc[P, R]]:
|
395
377
|
return self._decorator(
|
396
378
|
func,
|
397
379
|
ttl=ttl,
|
@@ -405,7 +387,7 @@ class CacheDataAPI:
|
|
405
387
|
|
406
388
|
def _decorator(
|
407
389
|
self,
|
408
|
-
func: Callable[P,
|
390
|
+
func: Callable[P, R] | None = None,
|
409
391
|
*,
|
410
392
|
ttl: float | timedelta | str | None,
|
411
393
|
max_entries: int | None,
|
@@ -414,7 +396,7 @@ class CacheDataAPI:
|
|
414
396
|
persist: CachePersistType | bool,
|
415
397
|
experimental_allow_widgets: bool,
|
416
398
|
hash_funcs: HashFuncsDict | None = None,
|
417
|
-
) -> CachedFunc[P,
|
399
|
+
) -> CachedFunc[P, R] | Callable[[Callable[P, R]], CachedFunc[P, R]]:
|
418
400
|
"""Decorator to cache functions that return data (e.g. dataframe transforms, database queries, ML inference).
|
419
401
|
|
420
402
|
Cached objects are stored in "pickled" form, which means that the return
|
@@ -598,20 +580,17 @@ class CacheDataAPI:
|
|
598
580
|
if experimental_allow_widgets:
|
599
581
|
show_widget_replay_deprecation("cache_data")
|
600
582
|
|
601
|
-
def wrapper(f: Callable[P,
|
602
|
-
return
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
hash_funcs=hash_funcs,
|
613
|
-
)
|
614
|
-
),
|
583
|
+
def wrapper(f: Callable[P, R]) -> CachedFunc[P, R]:
|
584
|
+
return make_cached_func_wrapper(
|
585
|
+
CachedDataFuncInfo(
|
586
|
+
func=f,
|
587
|
+
persist=persist_string,
|
588
|
+
show_spinner=show_spinner,
|
589
|
+
show_time=show_time,
|
590
|
+
max_entries=max_entries,
|
591
|
+
ttl=ttl,
|
592
|
+
hash_funcs=hash_funcs,
|
593
|
+
)
|
615
594
|
)
|
616
595
|
|
617
596
|
if func is None:
|
@@ -619,7 +598,7 @@ class CacheDataAPI:
|
|
619
598
|
|
620
599
|
return make_cached_func_wrapper(
|
621
600
|
CachedDataFuncInfo(
|
622
|
-
func=
|
601
|
+
func=func,
|
623
602
|
persist=persist_string,
|
624
603
|
show_spinner=show_spinner,
|
625
604
|
show_time=show_time,
|
@@ -635,7 +614,7 @@ class CacheDataAPI:
|
|
635
614
|
_data_caches.clear_all()
|
636
615
|
|
637
616
|
|
638
|
-
class DataCache(Cache):
|
617
|
+
class DataCache(Cache[R]):
|
639
618
|
"""Manages cached values for a single st.cache_data function."""
|
640
619
|
|
641
620
|
def __init__(
|
@@ -660,7 +639,7 @@ class DataCache(Cache):
|
|
660
639
|
return self.storage.get_stats()
|
661
640
|
return []
|
662
641
|
|
663
|
-
def read_result(self, key: str) -> CachedResult:
|
642
|
+
def read_result(self, key: str) -> CachedResult[R]:
|
664
643
|
"""Read a value and messages from the cache. Raise `CacheKeyNotFoundError`
|
665
644
|
if the value doesn't exist, and `CacheError` if the value exists but can't
|
666
645
|
be unpickled.
|
@@ -684,7 +663,7 @@ class DataCache(Cache):
|
|
684
663
|
raise CacheError(f"Failed to unpickle {key}") from exc
|
685
664
|
|
686
665
|
@gather_metrics("_cache_data_object")
|
687
|
-
def write_result(self, key: str, value:
|
666
|
+
def write_result(self, key: str, value: R, messages: list[MsgData]) -> None:
|
688
667
|
"""Write a value and associated messages to the cache.
|
689
668
|
The value must be pickleable.
|
690
669
|
"""
|
@@ -14,15 +14,12 @@
|
|
14
14
|
|
15
15
|
from __future__ import annotations
|
16
16
|
|
17
|
-
from typing import
|
17
|
+
from typing import Any, Callable, Generic, TypeVar
|
18
18
|
|
19
19
|
from streamlit import type_util
|
20
20
|
from streamlit.errors import MarkdownFormattedException, StreamlitAPIException
|
21
21
|
from streamlit.runtime.caching.cache_type import CacheType, get_decorator_api_name
|
22
22
|
|
23
|
-
if TYPE_CHECKING:
|
24
|
-
from types import FunctionType
|
25
|
-
|
26
23
|
CACHE_DOCS_URL = "https://docs.streamlit.io/develop/concepts/architecture/caching"
|
27
24
|
|
28
25
|
|
@@ -49,7 +46,7 @@ class UnhashableParamError(StreamlitAPIException):
|
|
49
46
|
def __init__(
|
50
47
|
self,
|
51
48
|
cache_type: CacheType,
|
52
|
-
func:
|
49
|
+
func: Callable[..., Any],
|
53
50
|
arg_name: str | None,
|
54
51
|
arg_value: Any,
|
55
52
|
orig_exc: BaseException,
|
@@ -61,7 +58,7 @@ class UnhashableParamError(StreamlitAPIException):
|
|
61
58
|
@staticmethod
|
62
59
|
def _create_message(
|
63
60
|
cache_type: CacheType,
|
64
|
-
func:
|
61
|
+
func: Callable[..., Any],
|
65
62
|
arg_name: str | None,
|
66
63
|
arg_value: Any,
|
67
64
|
) -> str:
|
@@ -98,7 +95,7 @@ class CacheReplayClosureError(StreamlitAPIException):
|
|
98
95
|
def __init__(
|
99
96
|
self,
|
100
97
|
cache_type: CacheType,
|
101
|
-
cached_func:
|
98
|
+
cached_func: Callable[..., Any],
|
102
99
|
) -> None:
|
103
100
|
func_name = get_cached_func_name_md(cached_func)
|
104
101
|
decorator_name = get_decorator_api_name(cache_type)
|
@@ -120,8 +117,11 @@ How to fix this:
|
|
120
117
|
super().__init__(msg)
|
121
118
|
|
122
119
|
|
123
|
-
|
124
|
-
|
120
|
+
R = TypeVar("R")
|
121
|
+
|
122
|
+
|
123
|
+
class UnserializableReturnValueError(MarkdownFormattedException, Generic[R]):
|
124
|
+
def __init__(self, func: Callable[..., R], return_value: R) -> None:
|
125
125
|
MarkdownFormattedException.__init__(
|
126
126
|
self,
|
127
127
|
f"""
|
@@ -23,9 +23,7 @@ from typing import (
|
|
23
23
|
Any,
|
24
24
|
Callable,
|
25
25
|
Final,
|
26
|
-
Protocol,
|
27
26
|
TypeVar,
|
28
|
-
cast,
|
29
27
|
overload,
|
30
28
|
)
|
31
29
|
|
@@ -39,6 +37,7 @@ from streamlit.runtime.caching.cache_errors import CacheKeyNotFoundError
|
|
39
37
|
from streamlit.runtime.caching.cache_type import CacheType
|
40
38
|
from streamlit.runtime.caching.cache_utils import (
|
41
39
|
Cache,
|
40
|
+
CachedFunc,
|
42
41
|
CachedFuncInfo,
|
43
42
|
make_cached_func_wrapper,
|
44
43
|
)
|
@@ -53,7 +52,6 @@ from streamlit.runtime.stats import CacheStat, CacheStatsProvider, group_stats
|
|
53
52
|
from streamlit.time_util import time_to_seconds
|
54
53
|
|
55
54
|
if TYPE_CHECKING:
|
56
|
-
import types
|
57
55
|
from datetime import timedelta
|
58
56
|
|
59
57
|
from streamlit.runtime.caching.hashing import HashFuncsDict
|
@@ -80,7 +78,7 @@ class ResourceCaches(CacheStatsProvider):
|
|
80
78
|
|
81
79
|
def __init__(self) -> None:
|
82
80
|
self._caches_lock = threading.Lock()
|
83
|
-
self._function_caches: dict[str, ResourceCache] = {}
|
81
|
+
self._function_caches: dict[str, ResourceCache[Any]] = {}
|
84
82
|
|
85
83
|
def get_cache(
|
86
84
|
self,
|
@@ -89,7 +87,7 @@ class ResourceCaches(CacheStatsProvider):
|
|
89
87
|
max_entries: int | float | None,
|
90
88
|
ttl: float | timedelta | str | None,
|
91
89
|
validate: ValidateFunc | None,
|
92
|
-
) -> ResourceCache:
|
90
|
+
) -> ResourceCache[Any]:
|
93
91
|
"""Return the mem cache for the given key.
|
94
92
|
|
95
93
|
If it doesn't exist, create a new one with the given params.
|
@@ -149,12 +147,16 @@ def get_resource_cache_stats_provider() -> CacheStatsProvider:
|
|
149
147
|
return _resource_caches
|
150
148
|
|
151
149
|
|
152
|
-
|
150
|
+
P = ParamSpec("P")
|
151
|
+
R = TypeVar("R")
|
152
|
+
|
153
|
+
|
154
|
+
class CachedResourceFuncInfo(CachedFuncInfo[P, R]):
|
153
155
|
"""Implements the CachedFuncInfo interface for @st.cache_resource."""
|
154
156
|
|
155
157
|
def __init__(
|
156
158
|
self,
|
157
|
-
func:
|
159
|
+
func: Callable[P, R],
|
158
160
|
show_spinner: bool | str,
|
159
161
|
max_entries: int | None,
|
160
162
|
ttl: float | timedelta | str | None,
|
@@ -185,7 +187,7 @@ class CachedResourceFuncInfo(CachedFuncInfo):
|
|
185
187
|
"""A human-readable name for the cached function."""
|
186
188
|
return f"{self.func.__module__}.{self.func.__qualname__}"
|
187
189
|
|
188
|
-
def get_function_cache(self, function_key: str) -> Cache:
|
190
|
+
def get_function_cache(self, function_key: str) -> Cache[R]:
|
189
191
|
return _resource_caches.get_cache(
|
190
192
|
key=function_key,
|
191
193
|
display_name=self.display_name,
|
@@ -195,29 +197,6 @@ class CachedResourceFuncInfo(CachedFuncInfo):
|
|
195
197
|
)
|
196
198
|
|
197
199
|
|
198
|
-
# Type-annotate the decorator function.
|
199
|
-
# (See https://mypy.readthedocs.io/en/stable/generics.html#decorator-factories)
|
200
|
-
P = ParamSpec("P")
|
201
|
-
T_co = TypeVar("T_co", covariant=True)
|
202
|
-
|
203
|
-
|
204
|
-
class CachedFunc(Protocol[P, T_co]):
|
205
|
-
"""Protocol for cached functions that preserve the original function's signature and add a clear method."""
|
206
|
-
|
207
|
-
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T_co: ...
|
208
|
-
|
209
|
-
@overload
|
210
|
-
def clear(self) -> None: ...
|
211
|
-
|
212
|
-
@overload
|
213
|
-
def clear(self, *args: P.args, **kwargs: P.kwargs) -> None: ...
|
214
|
-
|
215
|
-
# Currently we can't define the "all-optional" argument overload with `P.args` and `P.kwargs` in Python.
|
216
|
-
# So we use `Any` as a fallback.
|
217
|
-
@overload
|
218
|
-
def clear(self, *args: Any, **kwargs: Any) -> None: ...
|
219
|
-
|
220
|
-
|
221
200
|
class CacheResourceAPI:
|
222
201
|
"""Implements the public st.cache_resource API: the @st.cache_resource decorator,
|
223
202
|
and st.cache_resource.clear().
|
@@ -236,9 +215,12 @@ class CacheResourceAPI:
|
|
236
215
|
# (Ignore spurious mypy complaints - https://github.com/python/mypy/issues/2427)
|
237
216
|
self._decorator = gather_metrics(decorator_metric_name, self._decorator) # type: ignore
|
238
217
|
|
218
|
+
# Type-annotate the decorator function.
|
219
|
+
# (See https://mypy.readthedocs.io/en/stable/generics.html#decorator-factories)
|
220
|
+
|
239
221
|
# Bare decorator usage
|
240
222
|
@overload
|
241
|
-
def __call__(self, func: Callable[P,
|
223
|
+
def __call__(self, func: Callable[P, R]) -> CachedFunc[P, R]: ...
|
242
224
|
|
243
225
|
# Decorator with arguments
|
244
226
|
@overload
|
@@ -252,11 +234,11 @@ class CacheResourceAPI:
|
|
252
234
|
validate: ValidateFunc | None = None,
|
253
235
|
experimental_allow_widgets: bool = False,
|
254
236
|
hash_funcs: HashFuncsDict | None = None,
|
255
|
-
) -> Callable[[Callable[P,
|
237
|
+
) -> Callable[[Callable[P, R]], CachedFunc[P, R]]: ...
|
256
238
|
|
257
239
|
def __call__(
|
258
240
|
self,
|
259
|
-
func: Callable[P,
|
241
|
+
func: Callable[P, R] | None = None,
|
260
242
|
*,
|
261
243
|
ttl: float | timedelta | str | None = None,
|
262
244
|
max_entries: int | None = None,
|
@@ -265,7 +247,7 @@ class CacheResourceAPI:
|
|
265
247
|
validate: ValidateFunc | None = None,
|
266
248
|
experimental_allow_widgets: bool = False,
|
267
249
|
hash_funcs: HashFuncsDict | None = None,
|
268
|
-
) -> CachedFunc[P,
|
250
|
+
) -> CachedFunc[P, R] | Callable[[Callable[P, R]], CachedFunc[P, R]]:
|
269
251
|
return self._decorator(
|
270
252
|
func,
|
271
253
|
ttl=ttl,
|
@@ -279,7 +261,7 @@ class CacheResourceAPI:
|
|
279
261
|
|
280
262
|
def _decorator(
|
281
263
|
self,
|
282
|
-
func: Callable[P,
|
264
|
+
func: Callable[P, R] | None,
|
283
265
|
*,
|
284
266
|
ttl: float | timedelta | str | None,
|
285
267
|
max_entries: int | None,
|
@@ -288,7 +270,7 @@ class CacheResourceAPI:
|
|
288
270
|
validate: ValidateFunc | None,
|
289
271
|
experimental_allow_widgets: bool,
|
290
272
|
hash_funcs: HashFuncsDict | None = None,
|
291
|
-
) -> CachedFunc[P,
|
273
|
+
) -> CachedFunc[P, R] | Callable[[Callable[P, R]], CachedFunc[P, R]]:
|
292
274
|
"""Decorator to cache functions that return global resources (e.g. database connections, ML models).
|
293
275
|
|
294
276
|
Cached objects are shared across all users, sessions, and reruns. They
|
@@ -456,9 +438,9 @@ class CacheResourceAPI:
|
|
456
438
|
# Support passing the params via function decorator, e.g.
|
457
439
|
# @st.cache_resource(show_spinner=False)
|
458
440
|
if func is None:
|
459
|
-
return lambda f: make_cached_func_wrapper(
|
441
|
+
return lambda f: make_cached_func_wrapper(
|
460
442
|
CachedResourceFuncInfo(
|
461
|
-
func=f,
|
443
|
+
func=f,
|
462
444
|
show_spinner=show_spinner,
|
463
445
|
show_time=show_time,
|
464
446
|
max_entries=max_entries,
|
@@ -470,7 +452,7 @@ class CacheResourceAPI:
|
|
470
452
|
|
471
453
|
return make_cached_func_wrapper(
|
472
454
|
CachedResourceFuncInfo(
|
473
|
-
func=
|
455
|
+
func=func,
|
474
456
|
show_spinner=show_spinner,
|
475
457
|
show_time=show_time,
|
476
458
|
max_entries=max_entries,
|
@@ -486,7 +468,7 @@ class CacheResourceAPI:
|
|
486
468
|
_resource_caches.clear_all()
|
487
469
|
|
488
470
|
|
489
|
-
class ResourceCache(Cache):
|
471
|
+
class ResourceCache(Cache[R]):
|
490
472
|
"""Manages cached values for a single st.cache_resource function."""
|
491
473
|
|
492
474
|
def __init__(
|
@@ -500,7 +482,7 @@ class ResourceCache(Cache):
|
|
500
482
|
super().__init__()
|
501
483
|
self.key = key
|
502
484
|
self.display_name = display_name
|
503
|
-
self._mem_cache: TTLCache[str, CachedResult] = TTLCache(
|
485
|
+
self._mem_cache: TTLCache[str, CachedResult[R]] = TTLCache(
|
504
486
|
maxsize=max_entries, ttl=ttl_seconds, timer=cache_utils.TTLCACHE_TIMER
|
505
487
|
)
|
506
488
|
self._mem_cache_lock = threading.Lock()
|
@@ -514,7 +496,7 @@ class ResourceCache(Cache):
|
|
514
496
|
def ttl_seconds(self) -> float:
|
515
497
|
return self._mem_cache.ttl
|
516
498
|
|
517
|
-
def read_result(self, key: str) -> CachedResult:
|
499
|
+
def read_result(self, key: str) -> CachedResult[R]:
|
518
500
|
"""Read a value and associated messages from the cache.
|
519
501
|
Raise `CacheKeyNotFoundError` if the value doesn't exist.
|
520
502
|
"""
|
@@ -533,7 +515,7 @@ class ResourceCache(Cache):
|
|
533
515
|
return result
|
534
516
|
|
535
517
|
@gather_metrics("_cache_resource_object")
|
536
|
-
def write_result(self, key: str, value:
|
518
|
+
def write_result(self, key: str, value: R, messages: list[MsgData]) -> None:
|
537
519
|
"""Write a value and associated messages to the cache."""
|
538
520
|
main_id = st._main.id
|
539
521
|
sidebar_id = st.sidebar.id
|
@@ -24,7 +24,9 @@ import threading
|
|
24
24
|
import time
|
25
25
|
from abc import abstractmethod
|
26
26
|
from collections import defaultdict
|
27
|
-
from typing import TYPE_CHECKING, Any, Callable, Final
|
27
|
+
from typing import TYPE_CHECKING, Any, Callable, Final, Generic, TypeVar, cast, overload
|
28
|
+
|
29
|
+
from typing_extensions import ParamSpec
|
28
30
|
|
29
31
|
from streamlit import type_util
|
30
32
|
from streamlit.dataframe_util import is_unevaluated_data_object
|
@@ -51,8 +53,6 @@ from streamlit.runtime.scriptrunner_utils.script_run_context import (
|
|
51
53
|
)
|
52
54
|
|
53
55
|
if TYPE_CHECKING:
|
54
|
-
from types import FunctionType
|
55
|
-
|
56
56
|
from streamlit.runtime.caching.cache_type import CacheType
|
57
57
|
|
58
58
|
_LOGGER: Final = get_logger(__name__)
|
@@ -61,8 +61,12 @@ _LOGGER: Final = get_logger(__name__)
|
|
61
61
|
# is exposed here as a constant so that it can be patched in unit tests.
|
62
62
|
TTLCACHE_TIMER = time.monotonic
|
63
63
|
|
64
|
+
# Type-annotate the cached function.
|
65
|
+
P = ParamSpec("P")
|
66
|
+
R = TypeVar("R")
|
67
|
+
|
64
68
|
|
65
|
-
class Cache:
|
69
|
+
class Cache(Generic[R]):
|
66
70
|
"""Function cache interface. Caches persist across script runs."""
|
67
71
|
|
68
72
|
def __init__(self) -> None:
|
@@ -70,7 +74,7 @@ class Cache:
|
|
70
74
|
self._value_locks_lock = threading.Lock()
|
71
75
|
|
72
76
|
@abstractmethod
|
73
|
-
def read_result(self, value_key: str) -> CachedResult:
|
77
|
+
def read_result(self, value_key: str) -> CachedResult[R]:
|
74
78
|
"""Read a value and associated messages from the cache.
|
75
79
|
|
76
80
|
Raises
|
@@ -82,7 +86,7 @@ class Cache:
|
|
82
86
|
raise NotImplementedError
|
83
87
|
|
84
88
|
@abstractmethod
|
85
|
-
def write_result(self, value_key: str, value:
|
89
|
+
def write_result(self, value_key: str, value: R, messages: list[MsgData]) -> None:
|
86
90
|
"""Write a value and associated messages to the cache, overwriting any existing
|
87
91
|
result that uses the value_key.
|
88
92
|
"""
|
@@ -118,7 +122,7 @@ class Cache:
|
|
118
122
|
raise NotImplementedError
|
119
123
|
|
120
124
|
|
121
|
-
class CachedFuncInfo:
|
125
|
+
class CachedFuncInfo(Generic[P, R]):
|
122
126
|
"""Encapsulates data for a cached function instance.
|
123
127
|
|
124
128
|
CachedFuncInfo instances are scoped to a single script run - they're not
|
@@ -127,7 +131,7 @@ class CachedFuncInfo:
|
|
127
131
|
|
128
132
|
def __init__(
|
129
133
|
self,
|
130
|
-
func:
|
134
|
+
func: Callable[P, R],
|
131
135
|
hash_funcs: HashFuncsDict | None,
|
132
136
|
show_spinner: bool | str,
|
133
137
|
show_time: bool = False,
|
@@ -145,12 +149,12 @@ class CachedFuncInfo:
|
|
145
149
|
def cached_message_replay_ctx(self) -> CachedMessageReplayContext:
|
146
150
|
raise NotImplementedError
|
147
151
|
|
148
|
-
def get_function_cache(self, function_key: str) -> Cache:
|
152
|
+
def get_function_cache(self, function_key: str) -> Cache[R]:
|
149
153
|
"""Get or create the function cache for the given key."""
|
150
154
|
raise NotImplementedError
|
151
155
|
|
152
156
|
|
153
|
-
def make_cached_func_wrapper(info: CachedFuncInfo) ->
|
157
|
+
def make_cached_func_wrapper(info: CachedFuncInfo[P, R]) -> CachedFunc[P, R]:
|
154
158
|
"""Create a callable wrapper around a CachedFunctionInfo.
|
155
159
|
|
156
160
|
Calling the wrapper will return the cached value if it's already been
|
@@ -161,19 +165,19 @@ def make_cached_func_wrapper(info: CachedFuncInfo) -> Callable[..., Any]:
|
|
161
165
|
some or all of the wrapper's cached values.
|
162
166
|
"""
|
163
167
|
cached_func = CachedFunc(info)
|
164
|
-
return functools.update_wrapper(cached_func, info.func)
|
168
|
+
return cast("CachedFunc[P, R]", functools.update_wrapper(cached_func, info.func))
|
165
169
|
|
166
170
|
|
167
|
-
class BoundCachedFunc:
|
171
|
+
class BoundCachedFunc(Generic[P, R]):
|
168
172
|
"""A wrapper around a CachedFunc that binds it to a specific instance in case of
|
169
173
|
decorated function is a class method.
|
170
174
|
"""
|
171
175
|
|
172
|
-
def __init__(self, cached_func: CachedFunc, instance: Any) -> None:
|
176
|
+
def __init__(self, cached_func: CachedFunc[P, R], instance: Any) -> None:
|
173
177
|
self._cached_func = cached_func
|
174
178
|
self._instance = instance
|
175
179
|
|
176
|
-
def __call__(self, *args:
|
180
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
|
177
181
|
return self._cached_func(self._instance, *args, **kwargs)
|
178
182
|
|
179
183
|
def __repr__(self) -> str:
|
@@ -190,8 +194,8 @@ class BoundCachedFunc:
|
|
190
194
|
self._cached_func.clear()
|
191
195
|
|
192
196
|
|
193
|
-
class CachedFunc:
|
194
|
-
def __init__(self, info: CachedFuncInfo) -> None:
|
197
|
+
class CachedFunc(Generic[P, R]):
|
198
|
+
def __init__(self, info: CachedFuncInfo[P, R]) -> None:
|
195
199
|
self._info = info
|
196
200
|
self._function_key = _make_function_key(info.cache_type, info.func)
|
197
201
|
|
@@ -205,7 +209,7 @@ class CachedFunc:
|
|
205
209
|
|
206
210
|
return functools.update_wrapper(BoundCachedFunc(self, instance), self)
|
207
211
|
|
208
|
-
def __call__(self, *args:
|
212
|
+
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
|
209
213
|
"""The wrapper. We'll only call our underlying function on a cache miss."""
|
210
214
|
|
211
215
|
spinner_message: str | None = None
|
@@ -225,7 +229,7 @@ class CachedFunc:
|
|
225
229
|
func_args: tuple[Any, ...],
|
226
230
|
func_kwargs: dict[str, Any],
|
227
231
|
spinner_message: str | None = None,
|
228
|
-
) ->
|
232
|
+
) -> R:
|
229
233
|
# Retrieve the function's cache object. We must do this "just-in-time"
|
230
234
|
# (as opposed to in the constructor), because caches can be invalidated
|
231
235
|
# at any time.
|
@@ -262,7 +266,7 @@ class CachedFunc:
|
|
262
266
|
with spinner_or_no_context:
|
263
267
|
return self._handle_cache_miss(cache, value_key, func_args, func_kwargs)
|
264
268
|
|
265
|
-
def _handle_cache_hit(self, result: CachedResult) ->
|
269
|
+
def _handle_cache_hit(self, result: CachedResult[R]) -> R:
|
266
270
|
"""Handle a cache hit: replay the result's cached messages, and return its
|
267
271
|
value.
|
268
272
|
"""
|
@@ -275,11 +279,11 @@ class CachedFunc:
|
|
275
279
|
|
276
280
|
def _handle_cache_miss(
|
277
281
|
self,
|
278
|
-
cache: Cache,
|
282
|
+
cache: Cache[R],
|
279
283
|
value_key: str,
|
280
284
|
func_args: tuple[Any, ...],
|
281
285
|
func_kwargs: dict[str, Any],
|
282
|
-
) ->
|
286
|
+
) -> R:
|
283
287
|
"""Handle a cache miss: compute a new cached value, write it back to the cache,
|
284
288
|
and return that newly-computed value.
|
285
289
|
"""
|
@@ -348,6 +352,15 @@ class CachedFunc:
|
|
348
352
|
return_value=computed_value, func=self._info.func
|
349
353
|
)
|
350
354
|
|
355
|
+
@overload
|
356
|
+
def clear(self) -> None: ...
|
357
|
+
|
358
|
+
@overload
|
359
|
+
def clear(self, *args: P.args, **kwargs: P.kwargs) -> None: ...
|
360
|
+
|
361
|
+
@overload
|
362
|
+
def clear(self, *args: Any, **kwargs: Any) -> None: ...
|
363
|
+
|
351
364
|
def clear(self, *args: Any, **kwargs: Any) -> None:
|
352
365
|
"""Clear the cached function's associated cache.
|
353
366
|
|
@@ -400,7 +413,7 @@ class CachedFunc:
|
|
400
413
|
|
401
414
|
def _make_value_key(
|
402
415
|
cache_type: CacheType,
|
403
|
-
func:
|
416
|
+
func: Callable[..., Any],
|
404
417
|
func_args: tuple[Any, ...],
|
405
418
|
func_kwargs: dict[str, Any],
|
406
419
|
hash_funcs: HashFuncsDict | None,
|
@@ -465,7 +478,7 @@ def _make_value_key(
|
|
465
478
|
return value_key
|
466
479
|
|
467
480
|
|
468
|
-
def _make_function_key(cache_type: CacheType, func:
|
481
|
+
def _make_function_key(cache_type: CacheType, func: Callable[..., Any]) -> str:
|
469
482
|
"""Create the unique key for a function's cache.
|
470
483
|
|
471
484
|
A function's key is stable across reruns of the app, and changes when
|
@@ -505,7 +518,7 @@ def _make_function_key(cache_type: CacheType, func: FunctionType) -> str:
|
|
505
518
|
return func_hasher.hexdigest()
|
506
519
|
|
507
520
|
|
508
|
-
def _get_positional_arg_name(func:
|
521
|
+
def _get_positional_arg_name(func: Callable[..., Any], arg_index: int) -> str | None:
|
509
522
|
"""Return the name of a function's positional argument.
|
510
523
|
|
511
524
|
If arg_index is out of range, or refers to a parameter that is not a
|