streamlit-nightly 1.46.2.dev20250701__py3-none-any.whl → 1.46.2.dev20250704__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 (98) hide show
  1. streamlit/auth_util.py +1 -1
  2. streamlit/config.py +21 -0
  3. streamlit/elements/doc_string.py +3 -0
  4. streamlit/elements/lib/column_types.py +46 -20
  5. streamlit/errors.py +6 -0
  6. streamlit/proto/NewSession_pb2.py +16 -16
  7. streamlit/proto/NewSession_pb2.pyi +5 -1
  8. streamlit/runtime/app_session.py +49 -1
  9. streamlit/runtime/caching/cache_data_api.py +34 -55
  10. streamlit/runtime/caching/cache_errors.py +9 -9
  11. streamlit/runtime/caching/cache_resource_api.py +26 -44
  12. streamlit/runtime/caching/cache_utils.py +37 -24
  13. streamlit/runtime/caching/cached_message_replay.py +13 -6
  14. streamlit/runtime/secrets.py +7 -2
  15. streamlit/static/index.html +1 -1
  16. streamlit/static/manifest.json +219 -219
  17. streamlit/static/static/css/index.2-JSeiMQ.css +1 -0
  18. streamlit/static/static/js/{ErrorOutline.esm.BPc3D9N9.js → ErrorOutline.esm.Dl5aYA7P.js} +1 -1
  19. streamlit/static/static/js/{FileDownload.esm.BiFtKhyO.js → FileDownload.esm.b9aAtFUL.js} +1 -1
  20. streamlit/static/static/js/{FileHelper.D4zH-LPy.js → FileHelper.CZ-fo3AT.js} +1 -1
  21. streamlit/static/static/js/{FormClearHelper.C0oAv5iO.js → FormClearHelper.X_JAWIDE.js} +1 -1
  22. streamlit/static/static/js/{Hooks.DUp04RsA.js → Hooks.DgDu8WWe.js} +1 -1
  23. streamlit/static/static/js/{InputInstructions.DDo33kg9.js → InputInstructions.DRcWCO4-.js} +1 -1
  24. streamlit/static/static/js/{ProgressBar.DluhCn8Y.js → ProgressBar.TfYA4ywP.js} +1 -1
  25. streamlit/static/static/js/{RenderInPortalIfExists.CgnRTw5b.js → RenderInPortalIfExists.DdXJh4rR.js} +1 -1
  26. streamlit/static/static/js/{Toolbar.Ds5GetiN.js → Toolbar.Vgf9NeBF.js} +1 -1
  27. streamlit/static/static/js/{base-input.WGo1U6ZF.js → base-input.DVejzOG3.js} +1 -1
  28. streamlit/static/static/js/{checkbox.D6n54iT6.js → checkbox.CHzUs1FT.js} +1 -1
  29. streamlit/static/static/js/{createSuper.5U626-en.js → createSuper.DtVUSUG3.js} +1 -1
  30. streamlit/static/static/js/data-grid-overlay-editor.0g3IWOH4.js +1 -0
  31. streamlit/static/static/js/{downloader.O2_sntfr.js → downloader.C1bmi7_d.js} +1 -1
  32. streamlit/static/static/js/{es6.q_Hnl4aM.js → es6.DB9CkkEz.js} +2 -2
  33. streamlit/static/static/js/{iframeResizer.contentWindow.CI0jLiDM.js → iframeResizer.contentWindow.BUdo9Eut.js} +1 -1
  34. streamlit/static/static/js/{index.DGehTu4y.js → index.B7_hmluD.js} +1 -1
  35. streamlit/static/static/js/{index.DFr6h2Mp.js → index.BFvFGupf.js} +1 -1
  36. streamlit/static/static/js/{index.TZ6Mn-5O.js → index.BGQkiO_L.js} +1 -1
  37. streamlit/static/static/js/{index.Chck3OtZ.js → index.BLe8vNj1.js} +1 -1
  38. streamlit/static/static/js/{index.BK_N8ZQx.js → index.BPv94ryA.js} +1 -1
  39. streamlit/static/static/js/{index.CSmIUY0Y.js → index.BSU3uPDy.js} +1 -1
  40. streamlit/static/static/js/{index.CydAyglg.js → index.BYDy3exs.js} +1 -1
  41. streamlit/static/static/js/{index.ivZN8Zb2.js → index.Bxl-lvf7.js} +1 -1
  42. streamlit/static/static/js/{index.DX-uvB3F.js → index.Bz1rmVZY.js} +1 -1
  43. streamlit/static/static/js/{index.BHq3ymE5.js → index.C6lv7LZF.js} +1 -1
  44. streamlit/static/static/js/{index.DLIPqPyW.js → index.CL2tKcqp.js} +1 -1
  45. streamlit/static/static/js/{index.BpJHZBLD.js → index.CUgLUHzz.js} +1 -1
  46. streamlit/static/static/js/{index.DExpy6fp.js → index.Ca71cNwT.js} +1 -1
  47. streamlit/static/static/js/{index.C4Ex7o_F.js → index.ChFrryup.js} +1 -1
  48. streamlit/static/static/js/{index.CJ2HNyRu.js → index.CsowBPbe.js} +1 -1
  49. streamlit/static/static/js/{index.fwYnPZir.js → index.D9G9SbyS.js} +1 -1
  50. streamlit/static/static/js/{index.d8SU82Ov.js → index.D9lVoZ0g.js} +1 -1
  51. streamlit/static/static/js/{index.C3nRmcoA.js → index.D9m0XlZY.js} +1 -1
  52. streamlit/static/static/js/{index.CVeKGjlc.js → index.DBSP2Mzg.js} +1 -1
  53. streamlit/static/static/js/{index.BIFkuq05.js → index.DJFU0ONs.js} +1 -1
  54. streamlit/static/static/js/{index.CssTumWz.js → index.DJM4CJwf.js} +1 -1
  55. streamlit/static/static/js/{index.DYssBBFR.js → index.DQtc0wgD.js} +1 -1
  56. streamlit/static/static/js/{index.D3dJUIaK.js → index.DYOc3lvA.js} +1 -1
  57. streamlit/static/static/js/index.DkDMkGP_.js +197 -0
  58. streamlit/static/static/js/{index.BcBDhWfd.js → index.DlvTB7Ab.js} +1 -1
  59. streamlit/static/static/js/{index.DvfUlU7_.js → index.Dmsp2dPx.js} +68 -68
  60. streamlit/static/static/js/index.MJ13AaAl.js +1 -0
  61. streamlit/static/static/js/{index.DKkRsEHh.js → index.Vqz4Rf-H.js} +1 -1
  62. streamlit/static/static/js/{index.Bx8y-PCC.js → index.WCZtB2fX.js} +1 -1
  63. streamlit/static/static/js/{index.Df3rWQZG.js → index.ZDNW54LS.js} +1 -1
  64. streamlit/static/static/js/{index.BgjJWPwA.js → index.bBcPxeDg.js} +1 -1
  65. streamlit/static/static/js/{index.BBvZmL8i.js → index.ffsleSUc.js} +1 -1
  66. streamlit/static/static/js/{index.6EiR9ocA.js → index.fnZJbfUF.js} +1 -1
  67. streamlit/static/static/js/{index.CdMiaTQu.js → index.lM0dtLa7.js} +1 -1
  68. streamlit/static/static/js/{index.DXUf_iJw.js → index.pxx1WhzW.js} +1 -1
  69. streamlit/static/static/js/{index.9k0LltIg.js → index.rSXfDs8E.js} +1 -1
  70. streamlit/static/static/js/{index.CuZeb5ME.js → index.zmzbyDg-.js} +1 -1
  71. streamlit/static/static/js/{input.Da7mGl-e.js → input.zZ5PCHhH.js} +1 -1
  72. streamlit/static/static/js/{memory.Ba9R8N9F.js → memory.BGxwio87.js} +1 -1
  73. streamlit/static/static/js/{mergeWith.DuOn5KYt.js → mergeWith.CAjjDs4h.js} +1 -1
  74. streamlit/static/static/js/{number-overlay-editor.Cies2BnB.js → number-overlay-editor.D6O6Urda.js} +1 -1
  75. streamlit/static/static/js/{possibleConstructorReturn.PFROJe03.js → possibleConstructorReturn.Bv3kil8b.js} +1 -1
  76. streamlit/static/static/js/{sandbox.BJEPeXCj.js → sandbox.DqowwceX.js} +1 -1
  77. streamlit/static/static/js/{textarea.C9d_M8Ah.js → textarea.mjUutfC5.js} +1 -1
  78. streamlit/static/static/js/{timepicker.DqRJAopc.js → timepicker.D5asOTRg.js} +1 -1
  79. streamlit/static/static/js/{toConsumableArray.BFmb1pdq.js → toConsumableArray.CbOloveN.js} +1 -1
  80. streamlit/static/static/js/{uniqueId.BJtPrMww.js → uniqueId.DldNV5wM.js} +1 -1
  81. streamlit/static/static/js/{useBasicWidgetState.DKrm_jMv.js → useBasicWidgetState.DILnXvLY.js} +1 -1
  82. streamlit/static/static/js/{useOnInputChange.DVjdiDhw.js → useOnInputChange.Bpbev6mM.js} +1 -1
  83. streamlit/static/static/js/withFullScreenWrapper.Dgyv-A2q.js +1 -0
  84. streamlit/watcher/event_based_path_watcher.py +76 -44
  85. streamlit/watcher/local_sources_watcher.py +22 -19
  86. streamlit/watcher/polling_path_watcher.py +29 -18
  87. streamlit/watcher/util.py +7 -8
  88. {streamlit_nightly-1.46.2.dev20250701.dist-info → streamlit_nightly-1.46.2.dev20250704.dist-info}/METADATA +1 -1
  89. {streamlit_nightly-1.46.2.dev20250701.dist-info → streamlit_nightly-1.46.2.dev20250704.dist-info}/RECORD +93 -93
  90. streamlit/static/static/css/index.C5t3M85E.css +0 -1
  91. streamlit/static/static/js/data-grid-overlay-editor.BArb4CJR.js +0 -1
  92. streamlit/static/static/js/index.BE0RFnDe.js +0 -197
  93. streamlit/static/static/js/index.Cut1d44J.js +0 -1
  94. streamlit/static/static/js/withFullScreenWrapper.CAgTFtpw.js +0 -1
  95. {streamlit_nightly-1.46.2.dev20250701.data → streamlit_nightly-1.46.2.dev20250704.data}/scripts/streamlit.cmd +0 -0
  96. {streamlit_nightly-1.46.2.dev20250701.dist-info → streamlit_nightly-1.46.2.dev20250704.dist-info}/WHEEL +0 -0
  97. {streamlit_nightly-1.46.2.dev20250701.dist-info → streamlit_nightly-1.46.2.dev20250704.dist-info}/entry_points.txt +0 -0
  98. {streamlit_nightly-1.46.2.dev20250701.dist-info → streamlit_nightly-1.46.2.dev20250704.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
- class CachedDataFuncInfo(CachedFuncInfo):
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: types.FunctionType,
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, T_co]) -> CachedFunc[P, T_co]: ...
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, T_co]], CachedFunc[P, T_co]]: ...
363
+ ) -> Callable[[Callable[P, R]], CachedFunc[P, R]]: ...
382
364
 
383
365
  def __call__(
384
366
  self,
385
- func: Callable[P, T_co] | None = None,
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, T_co] | Callable[[Callable[P, T_co]], CachedFunc[P, T_co]]:
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, T_co] | None = None,
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, T_co] | Callable[[Callable[P, T_co]], CachedFunc[P, T_co]]:
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, T_co]) -> CachedFunc[P, T_co]:
602
- return cast(
603
- "CachedFunc[P, T_co]",
604
- make_cached_func_wrapper(
605
- CachedDataFuncInfo(
606
- func=f, # type: ignore
607
- persist=persist_string,
608
- show_spinner=show_spinner,
609
- show_time=show_time,
610
- max_entries=max_entries,
611
- ttl=ttl,
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=cast("types.FunctionType", 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: Any, messages: list[MsgData]) -> None:
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 TYPE_CHECKING, Any
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: FunctionType,
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: FunctionType,
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: FunctionType,
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
- class UnserializableReturnValueError(MarkdownFormattedException):
124
- def __init__(self, func: FunctionType, return_value: FunctionType) -> None:
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
- class CachedResourceFuncInfo(CachedFuncInfo):
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: types.FunctionType,
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, T_co]) -> CachedFunc[P, T_co]: ...
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, T_co]], CachedFunc[P, T_co]]: ...
237
+ ) -> Callable[[Callable[P, R]], CachedFunc[P, R]]: ...
256
238
 
257
239
  def __call__(
258
240
  self,
259
- func: Callable[P, T_co] | None = None,
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, T_co] | Callable[[Callable[P, T_co]], CachedFunc[P, T_co]]:
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, T_co] | None,
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, T_co] | Callable[[Callable[P, T_co]], CachedFunc[P, T_co]]:
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( # type: ignore
441
+ return lambda f: make_cached_func_wrapper(
460
442
  CachedResourceFuncInfo(
461
- func=f, # type: ignore
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=cast("types.FunctionType", 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: Any, messages: list[MsgData]) -> None:
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: Any, messages: list[MsgData]) -> None:
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: FunctionType,
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) -> Callable[..., Any]:
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: Any, **kwargs: Any) -> Any:
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: Any, **kwargs: Any) -> Any:
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
- ) -> Any:
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) -> Any:
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
- ) -> Any:
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: FunctionType,
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: FunctionType) -> str:
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: FunctionType, arg_index: int) -> str | None:
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