streamlit-nightly 1.31.2.dev20240212__py2.py3-none-any.whl → 1.31.2.dev20240214__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. streamlit/case_converters.py +9 -4
  2. streamlit/cli_util.py +2 -0
  3. streamlit/code_util.py +5 -2
  4. streamlit/color_util.py +2 -0
  5. streamlit/column_config.py +2 -0
  6. streamlit/commands/execution_control.py +4 -2
  7. streamlit/commands/experimental_query_params.py +7 -4
  8. streamlit/commands/page_config.py +11 -9
  9. streamlit/components/v1/components.py +23 -16
  10. streamlit/config.py +3 -5
  11. streamlit/config_option.py +12 -11
  12. streamlit/connections/base_connection.py +4 -2
  13. streamlit/connections/snowflake_connection.py +4 -4
  14. streamlit/connections/snowpark_connection.py +3 -3
  15. streamlit/connections/sql_connection.py +6 -6
  16. streamlit/connections/util.py +8 -5
  17. streamlit/constants.py +2 -0
  18. streamlit/cursor.py +16 -14
  19. streamlit/delta_generator.py +10 -13
  20. streamlit/deprecation_util.py +4 -3
  21. streamlit/echo.py +5 -3
  22. streamlit/elements/alert.py +16 -14
  23. streamlit/elements/altair_utils.py +8 -6
  24. streamlit/elements/arrow.py +4 -4
  25. streamlit/elements/arrow_altair.py +24 -34
  26. streamlit/elements/arrow_vega_lite.py +9 -14
  27. streamlit/elements/balloons.py +4 -2
  28. streamlit/elements/bokeh_chart.py +7 -7
  29. streamlit/elements/code.py +6 -4
  30. streamlit/elements/deck_gl_json_chart.py +8 -8
  31. streamlit/elements/doc_string.py +5 -9
  32. streamlit/elements/empty.py +4 -2
  33. streamlit/elements/exception.py +10 -10
  34. streamlit/elements/form.py +1 -3
  35. streamlit/elements/graphviz_chart.py +5 -6
  36. streamlit/elements/heading.py +16 -14
  37. streamlit/elements/iframe.py +14 -12
  38. streamlit/elements/image.py +8 -8
  39. streamlit/elements/json.py +6 -4
  40. streamlit/elements/layouts.py +12 -10
  41. streamlit/elements/lib/column_config_utils.py +2 -2
  42. streamlit/elements/lib/column_types.py +23 -23
  43. streamlit/elements/lib/dicttools.py +10 -6
  44. streamlit/elements/lib/mutable_status_container.py +7 -7
  45. streamlit/elements/lib/pandas_styler_utils.py +6 -6
  46. streamlit/elements/lib/streamlit_plotly_theme.py +2 -0
  47. streamlit/elements/map.py +11 -22
  48. streamlit/elements/markdown.py +16 -14
  49. streamlit/elements/media.py +16 -16
  50. streamlit/elements/metric.py +9 -7
  51. streamlit/elements/plotly_chart.py +5 -5
  52. streamlit/elements/progress.py +6 -6
  53. streamlit/elements/pyplot.py +10 -13
  54. streamlit/elements/snow.py +4 -2
  55. streamlit/elements/spinner.py +2 -0
  56. streamlit/elements/text.py +7 -5
  57. streamlit/elements/toast.py +6 -4
  58. streamlit/elements/utils.py +15 -28
  59. streamlit/elements/widgets/button.py +39 -39
  60. streamlit/elements/widgets/camera_input.py +21 -17
  61. streamlit/elements/widgets/chat.py +6 -7
  62. streamlit/elements/widgets/checkbox.py +21 -19
  63. streamlit/elements/widgets/color_picker.py +18 -16
  64. streamlit/elements/widgets/data_editor.py +7 -7
  65. streamlit/elements/widgets/file_uploader.py +59 -55
  66. streamlit/elements/widgets/multiselect.py +33 -42
  67. streamlit/elements/widgets/number_input.py +10 -5
  68. streamlit/elements/widgets/radio.py +1 -1
  69. streamlit/elements/widgets/select_slider.py +25 -34
  70. streamlit/elements/widgets/selectbox.py +1 -1
  71. streamlit/elements/widgets/slider.py +28 -36
  72. streamlit/elements/widgets/text_widgets.py +6 -6
  73. streamlit/elements/widgets/time_widgets.py +13 -13
  74. streamlit/elements/write.py +21 -29
  75. streamlit/env_util.py +5 -3
  76. streamlit/error_util.py +7 -3
  77. streamlit/errors.py +3 -1
  78. streamlit/external/langchain/streamlit_callback_handler.py +26 -24
  79. streamlit/file_util.py +18 -14
  80. streamlit/folder_black_list.py +3 -1
  81. streamlit/git_util.py +5 -3
  82. streamlit/js_number.py +10 -13
  83. streamlit/logger.py +5 -5
  84. streamlit/net_util.py +14 -11
  85. streamlit/platform.py +2 -0
  86. streamlit/runtime/__init__.py +2 -0
  87. streamlit/runtime/app_session.py +42 -42
  88. streamlit/runtime/caching/__init__.py +4 -4
  89. streamlit/runtime/caching/cache_data_api.py +3 -3
  90. streamlit/runtime/caching/cache_errors.py +5 -3
  91. streamlit/runtime/caching/cache_type.py +2 -0
  92. streamlit/runtime/caching/cache_utils.py +2 -4
  93. streamlit/runtime/caching/cached_message_replay.py +12 -5
  94. streamlit/runtime/caching/hashing.py +29 -21
  95. streamlit/runtime/caching/storage/cache_storage_protocol.py +1 -2
  96. streamlit/runtime/caching/storage/local_disk_cache_storage.py +6 -5
  97. streamlit/runtime/connection_factory.py +8 -8
  98. streamlit/runtime/forward_msg_cache.py +20 -18
  99. streamlit/runtime/forward_msg_queue.py +8 -9
  100. streamlit/runtime/legacy_caching/caching.py +32 -42
  101. streamlit/runtime/legacy_caching/hashing.py +29 -25
  102. streamlit/runtime/media_file_manager.py +16 -14
  103. streamlit/runtime/media_file_storage.py +8 -8
  104. streamlit/runtime/memory_media_file_storage.py +12 -14
  105. streamlit/runtime/memory_session_storage.py +4 -3
  106. streamlit/runtime/memory_uploaded_file_manager.py +9 -10
  107. streamlit/runtime/metrics_util.py +20 -20
  108. streamlit/runtime/runtime.py +25 -27
  109. streamlit/runtime/runtime_util.py +5 -3
  110. streamlit/runtime/script_data.py +2 -0
  111. streamlit/runtime/scriptrunner/magic.py +17 -11
  112. streamlit/runtime/scriptrunner/magic_funcs.py +2 -0
  113. streamlit/runtime/scriptrunner/script_requests.py +6 -4
  114. streamlit/runtime/scriptrunner/script_run_context.py +17 -17
  115. streamlit/runtime/scriptrunner/script_runner.py +7 -5
  116. streamlit/runtime/secrets.py +4 -6
  117. streamlit/runtime/session_manager.py +14 -14
  118. streamlit/runtime/state/common.py +5 -4
  119. streamlit/runtime/state/query_params.py +8 -6
  120. streamlit/runtime/state/query_params_proxy.py +7 -5
  121. streamlit/runtime/state/safe_session_state.py +7 -5
  122. streamlit/runtime/state/session_state.py +3 -4
  123. streamlit/runtime/state/session_state_proxy.py +5 -5
  124. streamlit/runtime/state/widgets.py +20 -18
  125. streamlit/runtime/stats.py +13 -15
  126. streamlit/runtime/uploaded_file_manager.py +6 -5
  127. streamlit/runtime/websocket_session_manager.py +14 -14
  128. streamlit/source_util.py +13 -11
  129. streamlit/static/asset-manifest.json +13 -13
  130. streamlit/static/index.html +1 -1
  131. streamlit/static/static/css/2411.8b8f33d6.chunk.css +1 -0
  132. streamlit/static/static/css/43.e3b876c5.chunk.css +1 -0
  133. streamlit/static/static/css/6692.65519639.chunk.css +1 -0
  134. streamlit/static/static/js/{3075.76725a14.chunk.js → 2411.714d213e.chunk.js} +2 -2
  135. streamlit/static/static/js/4185.21ca0590.chunk.js +1 -0
  136. streamlit/static/static/js/43.36939bb1.chunk.js +1 -0
  137. streamlit/static/static/js/{5117.6a701db1.chunk.js → 5117.04bfe5d3.chunk.js} +1 -1
  138. streamlit/static/static/js/{5791.30b01ee8.chunk.js → 5791.c5138157.chunk.js} +1 -1
  139. streamlit/static/static/js/656.8c998bc8.chunk.js +2 -0
  140. streamlit/static/static/js/{6692.6ac4ea6f.chunk.js → 6692.6496cbc2.chunk.js} +1 -1
  141. streamlit/static/static/js/7142.400eefdd.chunk.js +1 -0
  142. streamlit/static/static/js/main.2737c0f9.js +2 -0
  143. streamlit/static/static/js/{main.043d802e.js.LICENSE.txt → main.2737c0f9.js.LICENSE.txt} +23 -25
  144. streamlit/string_util.py +13 -9
  145. streamlit/temporary_directory.py +3 -1
  146. streamlit/testing/v1/element_tree.py +1 -2
  147. streamlit/testing/v1/util.py +7 -3
  148. streamlit/type_util.py +30 -25
  149. streamlit/url_util.py +6 -4
  150. streamlit/user_info.py +8 -6
  151. streamlit/util.py +23 -37
  152. streamlit/version.py +16 -9
  153. streamlit/watcher/event_based_path_watcher.py +10 -10
  154. streamlit/watcher/local_sources_watcher.py +15 -13
  155. streamlit/watcher/path_watcher.py +0 -3
  156. streamlit/watcher/polling_path_watcher.py +9 -8
  157. streamlit/watcher/util.py +3 -2
  158. streamlit/web/cache_storage_manager_config.py +2 -0
  159. streamlit/web/server/app_static_file_handler.py +6 -5
  160. streamlit/web/server/browser_websocket_handler.py +10 -8
  161. streamlit/web/server/component_request_handler.py +7 -4
  162. streamlit/web/server/media_file_handler.py +5 -4
  163. streamlit/web/server/routes.py +6 -3
  164. streamlit/web/server/server.py +41 -34
  165. streamlit/web/server/server_util.py +8 -3
  166. streamlit/web/server/stats_request_handler.py +14 -5
  167. streamlit/web/server/upload_file_request_handler.py +7 -8
  168. streamlit/web/server/websocket_headers.py +2 -2
  169. {streamlit_nightly-1.31.2.dev20240212.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/METADATA +1 -1
  170. {streamlit_nightly-1.31.2.dev20240212.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/RECORD +176 -176
  171. streamlit/static/static/css/3075.81b3d18f.chunk.css +0 -1
  172. streamlit/static/static/css/43.c24b25fa.chunk.css +0 -1
  173. streamlit/static/static/css/6692.bb444a79.chunk.css +0 -1
  174. streamlit/static/static/js/1215.baf3721f.chunk.js +0 -2
  175. streamlit/static/static/js/4185.90e929dc.chunk.js +0 -1
  176. streamlit/static/static/js/43.8ca4bc8a.chunk.js +0 -1
  177. streamlit/static/static/js/7142.a359ed63.chunk.js +0 -1
  178. streamlit/static/static/js/main.043d802e.js +0 -2
  179. /streamlit/static/static/js/{3075.76725a14.chunk.js.LICENSE.txt → 2411.714d213e.chunk.js.LICENSE.txt} +0 -0
  180. /streamlit/static/static/js/{1215.baf3721f.chunk.js.LICENSE.txt → 656.8c998bc8.chunk.js.LICENSE.txt} +0 -0
  181. {streamlit_nightly-1.31.2.dev20240212.data → streamlit_nightly-1.31.2.dev20240214.data}/scripts/streamlit.cmd +0 -0
  182. {streamlit_nightly-1.31.2.dev20240212.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/WHEEL +0 -0
  183. {streamlit_nightly-1.31.2.dev20240212.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/entry_points.txt +0 -0
  184. {streamlit_nightly-1.31.2.dev20240212.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/top_level.txt +0 -0
@@ -12,8 +12,10 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from __future__ import annotations
16
+
15
17
  import hashlib
16
- from typing import TYPE_CHECKING, Dict, List, MutableMapping, Optional
18
+ from typing import TYPE_CHECKING, Final, MutableMapping
17
19
  from weakref import WeakKeyDictionary
18
20
 
19
21
  from streamlit import config, util
@@ -25,7 +27,7 @@ from streamlit.util import HASHLIB_KWARGS
25
27
  if TYPE_CHECKING:
26
28
  from streamlit.runtime.app_session import AppSession
27
29
 
28
- LOGGER = get_logger(__name__)
30
+ _LOGGER: Final = get_logger(__name__)
29
31
 
30
32
 
31
33
  def populate_hash_if_needed(msg: ForwardMsg) -> str:
@@ -107,16 +109,16 @@ class ForwardMsgCache(CacheStatsProvider):
107
109
 
108
110
  """
109
111
 
110
- def __init__(self, msg: Optional[ForwardMsg]):
112
+ def __init__(self, msg: ForwardMsg | None):
111
113
  self.msg = msg
112
114
  self._session_script_run_counts: MutableMapping[
113
- "AppSession", int
115
+ AppSession, int
114
116
  ] = WeakKeyDictionary()
115
117
 
116
118
  def __repr__(self) -> str:
117
119
  return util.repr_(self)
118
120
 
119
- def add_session_ref(self, session: "AppSession", script_run_count: int) -> None:
121
+ def add_session_ref(self, session: AppSession, script_run_count: int) -> None:
120
122
  """Adds a reference to a AppSession that has referenced
121
123
  this Entry's message.
122
124
 
@@ -129,18 +131,18 @@ class ForwardMsgCache(CacheStatsProvider):
129
131
  """
130
132
  prev_run_count = self._session_script_run_counts.get(session, 0)
131
133
  if script_run_count < prev_run_count:
132
- LOGGER.error(
134
+ _LOGGER.error(
133
135
  "New script_run_count (%s) is < prev_run_count (%s). "
134
136
  "This should never happen!" % (script_run_count, prev_run_count)
135
137
  )
136
138
  script_run_count = prev_run_count
137
139
  self._session_script_run_counts[session] = script_run_count
138
140
 
139
- def has_session_ref(self, session: "AppSession") -> bool:
141
+ def has_session_ref(self, session: AppSession) -> bool:
140
142
  return session in self._session_script_run_counts
141
143
 
142
144
  def get_session_ref_age(
143
- self, session: "AppSession", script_run_count: int
145
+ self, session: AppSession, script_run_count: int
144
146
  ) -> int:
145
147
  """The age of the given session's reference to the Entry,
146
148
  given a new script_run_count.
@@ -148,7 +150,7 @@ class ForwardMsgCache(CacheStatsProvider):
148
150
  """
149
151
  return script_run_count - self._session_script_run_counts[session]
150
152
 
151
- def remove_session_ref(self, session: "AppSession") -> None:
153
+ def remove_session_ref(self, session: AppSession) -> None:
152
154
  del self._session_script_run_counts[session]
153
155
 
154
156
  def has_refs(self) -> bool:
@@ -159,13 +161,13 @@ class ForwardMsgCache(CacheStatsProvider):
159
161
  return len(self._session_script_run_counts) > 0
160
162
 
161
163
  def __init__(self):
162
- self._entries: Dict[str, "ForwardMsgCache.Entry"] = {}
164
+ self._entries: dict[str, ForwardMsgCache.Entry] = {}
163
165
 
164
166
  def __repr__(self) -> str:
165
167
  return util.repr_(self)
166
168
 
167
169
  def add_message(
168
- self, msg: ForwardMsg, session: "AppSession", script_run_count: int
170
+ self, msg: ForwardMsg, session: AppSession, script_run_count: int
169
171
  ) -> None:
170
172
  """Add a ForwardMsg to the cache.
171
173
 
@@ -191,7 +193,7 @@ class ForwardMsgCache(CacheStatsProvider):
191
193
  self._entries[msg.hash] = entry
192
194
  entry.add_session_ref(session, script_run_count)
193
195
 
194
- def get_message(self, hash: str) -> Optional[ForwardMsg]:
196
+ def get_message(self, hash: str) -> ForwardMsg | None:
195
197
  """Return the message with the given ID if it exists in the cache.
196
198
 
197
199
  Parameters
@@ -208,7 +210,7 @@ class ForwardMsgCache(CacheStatsProvider):
208
210
  return entry.msg if entry else None
209
211
 
210
212
  def has_message_reference(
211
- self, msg: ForwardMsg, session: "AppSession", script_run_count: int
213
+ self, msg: ForwardMsg, session: AppSession, script_run_count: int
212
214
  ) -> bool:
213
215
  """Return True if a session has a reference to a message."""
214
216
  populate_hash_if_needed(msg)
@@ -221,7 +223,7 @@ class ForwardMsgCache(CacheStatsProvider):
221
223
  age = entry.get_session_ref_age(session, script_run_count)
222
224
  return age <= int(config.get_option("global.maxCachedMessageAge"))
223
225
 
224
- def remove_refs_for_session(self, session: "AppSession") -> None:
226
+ def remove_refs_for_session(self, session: AppSession) -> None:
225
227
  """Remove refs for all entries for the given session.
226
228
 
227
229
  This should be called when an AppSession is disconnected or closed.
@@ -243,7 +245,7 @@ class ForwardMsgCache(CacheStatsProvider):
243
245
  del self._entries[msg_hash]
244
246
 
245
247
  def remove_expired_entries_for_session(
246
- self, session: "AppSession", script_run_count: int
248
+ self, session: AppSession, script_run_count: int
247
249
  ) -> None:
248
250
  """Remove any cached messages that have expired from the given session.
249
251
 
@@ -266,7 +268,7 @@ class ForwardMsgCache(CacheStatsProvider):
266
268
 
267
269
  age = entry.get_session_ref_age(session, script_run_count)
268
270
  if age > max_age:
269
- LOGGER.debug(
271
+ _LOGGER.debug(
270
272
  "Removing expired entry [session=%s, hash=%s, age=%s]",
271
273
  id(session),
272
274
  msg_hash,
@@ -282,8 +284,8 @@ class ForwardMsgCache(CacheStatsProvider):
282
284
  """Remove all entries from the cache"""
283
285
  self._entries.clear()
284
286
 
285
- def get_stats(self) -> List[CacheStat]:
286
- stats: List[CacheStat] = []
287
+ def get_stats(self) -> list[CacheStat]:
288
+ stats: list[CacheStat] = []
287
289
  for entry_hash, entry in self._entries.items():
288
290
  stats.append(
289
291
  CacheStat(
@@ -12,14 +12,13 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Any, Dict, List, Optional, Tuple
15
+ from __future__ import annotations
16
+
17
+ from typing import Any
16
18
 
17
- from streamlit.logger import get_logger
18
19
  from streamlit.proto.Delta_pb2 import Delta
19
20
  from streamlit.proto.ForwardMsg_pb2 import ForwardMsg
20
21
 
21
- LOGGER = get_logger(__name__)
22
-
23
22
 
24
23
  class ForwardMsgQueue:
25
24
  """Accumulates a session's outgoing ForwardMsgs.
@@ -33,15 +32,15 @@ class ForwardMsgQueue:
33
32
  """
34
33
 
35
34
  def __init__(self):
36
- self._queue: List[ForwardMsg] = []
35
+ self._queue: list[ForwardMsg] = []
37
36
  # A mapping of (delta_path -> _queue.indexof(msg)) for each
38
37
  # Delta message in the queue. We use this for coalescing
39
38
  # redundant outgoing Deltas (where a newer Delta supersedes
40
39
  # an older Delta, with the same delta_path, that's still in the
41
40
  # queue).
42
- self._delta_index_map: Dict[Tuple[int, ...], int] = dict()
41
+ self._delta_index_map: dict[tuple[int, ...], int] = dict()
43
42
 
44
- def get_debug(self) -> Dict[str, Any]:
43
+ def get_debug(self) -> dict[str, Any]:
45
44
  from google.protobuf.json_format import MessageToDict
46
45
 
47
46
  return {
@@ -85,7 +84,7 @@ class ForwardMsgQueue:
85
84
  self._queue = []
86
85
  self._delta_index_map = dict()
87
86
 
88
- def flush(self) -> List[ForwardMsg]:
87
+ def flush(self) -> list[ForwardMsg]:
89
88
  """Clear the queue and return a list of the messages it contained
90
89
  before being cleared.
91
90
  """
@@ -107,7 +106,7 @@ def _is_composable_message(msg: ForwardMsg) -> bool:
107
106
  return delta_type != "add_rows" and delta_type != "arrow_add_rows"
108
107
 
109
108
 
110
- def _maybe_compose_deltas(old_delta: Delta, new_delta: Delta) -> Optional[Delta]:
109
+ def _maybe_compose_deltas(old_delta: Delta, new_delta: Delta) -> Delta | None:
111
110
  """Combines new_delta onto old_delta if possible.
112
111
 
113
112
  If the combination takes place, the function returns a new Delta that
@@ -14,6 +14,8 @@
14
14
 
15
15
  """A library of caching utilities."""
16
16
 
17
+ from __future__ import annotations
18
+
17
19
  import contextlib
18
20
  import functools
19
21
  import hashlib
@@ -26,19 +28,7 @@ import threading
26
28
  import time
27
29
  from collections import namedtuple
28
30
  from dataclasses import dataclass
29
- from typing import (
30
- Any,
31
- Callable,
32
- Dict,
33
- Final,
34
- Iterator,
35
- List,
36
- Optional,
37
- TypeVar,
38
- Union,
39
- cast,
40
- overload,
41
- )
31
+ from typing import Any, Callable, Final, Iterator, TypeVar, cast, overload
42
32
 
43
33
  from cachetools import TTLCache
44
34
 
@@ -72,7 +62,7 @@ _DiskCacheEntry = namedtuple("_DiskCacheEntry", ["value"])
72
62
 
73
63
  # When we show the "st.cache is deprecated" warning, we make a recommendation about which new
74
64
  # cache decorator to switch to for the following data types:
75
- NEW_CACHE_FUNC_RECOMMENDATIONS: Dict[str, CacheType] = {
65
+ NEW_CACHE_FUNC_RECOMMENDATIONS: dict[str, CacheType] = {
76
66
  # cache_data recommendations:
77
67
  "str": CacheType.DATA,
78
68
  "float": CacheType.DATA,
@@ -165,7 +155,7 @@ class _MemCaches(CacheStatsProvider):
165
155
  def __init__(self):
166
156
  # Contains a cache object for each st.cache'd function
167
157
  self._lock = threading.RLock()
168
- self._function_caches: Dict[str, MemCache] = {}
158
+ self._function_caches: dict[str, MemCache] = {}
169
159
 
170
160
  def __repr__(self) -> str:
171
161
  return util.repr_(self)
@@ -173,8 +163,8 @@ class _MemCaches(CacheStatsProvider):
173
163
  def get_cache(
174
164
  self,
175
165
  key: str,
176
- max_entries: Optional[float],
177
- ttl: Optional[float],
166
+ max_entries: float | None,
167
+ ttl: float | None,
178
168
  display_name: str = "",
179
169
  ) -> MemCache:
180
170
  """Return the mem cache for the given key.
@@ -220,7 +210,7 @@ class _MemCaches(CacheStatsProvider):
220
210
  with self._lock:
221
211
  self._function_caches = {}
222
212
 
223
- def get_stats(self) -> List[CacheStat]:
213
+ def get_stats(self) -> list[CacheStat]:
224
214
  with self._lock:
225
215
  # Shallow-clone our caches. We don't want to hold the global
226
216
  # lock during stats-gathering.
@@ -245,7 +235,7 @@ _mem_caches = _MemCaches()
245
235
  # and decremented when we exit.
246
236
  class ThreadLocalCacheInfo(threading.local):
247
237
  def __init__(self):
248
- self.cached_func_stack: List[Callable[..., Any]] = []
238
+ self.cached_func_stack: list[Callable[..., Any]] = []
249
239
  self.suppress_st_function_warning = 0
250
240
 
251
241
  def __repr__(self) -> str:
@@ -275,7 +265,7 @@ def suppress_cached_st_function_warning() -> Iterator[None]:
275
265
 
276
266
 
277
267
  def _show_cached_st_function_warning(
278
- dg: "st.delta_generator.DeltaGenerator",
268
+ dg: st.delta_generator.DeltaGenerator,
279
269
  st_func_name: str,
280
270
  cached_func: Callable[..., Any],
281
271
  ) -> None:
@@ -287,7 +277,7 @@ def _show_cached_st_function_warning(
287
277
 
288
278
 
289
279
  def maybe_show_cached_st_function_warning(
290
- dg: "st.delta_generator.DeltaGenerator", st_func_name: str
280
+ dg: st.delta_generator.DeltaGenerator, st_func_name: str
291
281
  ) -> None:
292
282
  """If appropriate, warn about calling st.foo inside @cache.
293
283
 
@@ -317,7 +307,7 @@ def _read_from_mem_cache(
317
307
  key: str,
318
308
  allow_output_mutation: bool,
319
309
  func_or_code: Callable[..., Any],
320
- hash_funcs: Optional[HashFuncsDict],
310
+ hash_funcs: HashFuncsDict | None,
321
311
  ) -> Any:
322
312
  cache = mem_cache.cache
323
313
  if key in cache:
@@ -347,7 +337,7 @@ def _write_to_mem_cache(
347
337
  value: Any,
348
338
  allow_output_mutation: bool,
349
339
  func_or_code: Callable[..., Any],
350
- hash_funcs: Optional[HashFuncsDict],
340
+ hash_funcs: HashFuncsDict | None,
351
341
  ) -> None:
352
342
  if allow_output_mutation:
353
343
  hash = None
@@ -359,7 +349,7 @@ def _write_to_mem_cache(
359
349
 
360
350
 
361
351
  def _get_output_hash(
362
- value: Any, func_or_code: Callable[..., Any], hash_funcs: Optional[HashFuncsDict]
352
+ value: Any, func_or_code: Callable[..., Any], hash_funcs: HashFuncsDict | None
363
353
  ) -> bytes:
364
354
  hasher = hashlib.new("md5", **HASHLIB_KWARGS)
365
355
  update_hash(
@@ -400,7 +390,7 @@ def _write_to_disk_cache(key: str, value: Any) -> None:
400
390
  # Clean up file so we don't leave zero byte files.
401
391
  try:
402
392
  os.remove(path)
403
- except (FileNotFoundError, IOError, OSError):
393
+ except (FileNotFoundError, OSError):
404
394
  # If we can't remove the file, it's not a big deal.
405
395
  pass
406
396
  raise CacheError("Unable to write to cache: %s" % e)
@@ -412,7 +402,7 @@ def _read_from_cache(
412
402
  persist: bool,
413
403
  allow_output_mutation: bool,
414
404
  func_or_code: Callable[..., Any],
415
- hash_funcs: Optional[HashFuncsDict] = None,
405
+ hash_funcs: HashFuncsDict | None = None,
416
406
  ) -> Any:
417
407
  """Read a value from the cache.
418
408
 
@@ -447,7 +437,7 @@ def _write_to_cache(
447
437
  persist: bool,
448
438
  allow_output_mutation: bool,
449
439
  func_or_code: Callable[..., Any],
450
- hash_funcs: Optional[HashFuncsDict] = None,
440
+ hash_funcs: HashFuncsDict | None = None,
451
441
  ):
452
442
  _write_to_mem_cache(
453
443
  mem_cache, key, value, allow_output_mutation, func_or_code, hash_funcs
@@ -466,9 +456,9 @@ def cache(
466
456
  allow_output_mutation: bool = False,
467
457
  show_spinner: bool = True,
468
458
  suppress_st_warning: bool = False,
469
- hash_funcs: Optional[HashFuncsDict] = None,
470
- max_entries: Optional[int] = None,
471
- ttl: Optional[float] = None,
459
+ hash_funcs: HashFuncsDict | None = None,
460
+ max_entries: int | None = None,
461
+ ttl: float | None = None,
472
462
  ) -> F:
473
463
  ...
474
464
 
@@ -480,23 +470,23 @@ def cache(
480
470
  allow_output_mutation: bool = False,
481
471
  show_spinner: bool = True,
482
472
  suppress_st_warning: bool = False,
483
- hash_funcs: Optional[HashFuncsDict] = None,
484
- max_entries: Optional[int] = None,
485
- ttl: Optional[float] = None,
473
+ hash_funcs: HashFuncsDict | None = None,
474
+ max_entries: int | None = None,
475
+ ttl: float | None = None,
486
476
  ) -> Callable[[F], F]:
487
477
  ...
488
478
 
489
479
 
490
480
  def cache(
491
- func: Optional[F] = None,
481
+ func: F | None = None,
492
482
  persist: bool = False,
493
483
  allow_output_mutation: bool = False,
494
484
  show_spinner: bool = True,
495
485
  suppress_st_warning: bool = False,
496
- hash_funcs: Optional[HashFuncsDict] = None,
497
- max_entries: Optional[int] = None,
498
- ttl: Optional[float] = None,
499
- ) -> Union[Callable[[F], F], F]:
486
+ hash_funcs: HashFuncsDict | None = None,
487
+ max_entries: int | None = None,
488
+ ttl: float | None = None,
489
+ ) -> Callable[[F], F] | F:
500
490
  """Function decorator to memoize function executions.
501
491
 
502
492
  Parameters
@@ -674,7 +664,7 @@ def cache(
674
664
 
675
665
  # Avoid recomputing the body's hash by just appending the
676
666
  # previously-computed hash to the arg hash.
677
- value_key = "%s-%s" % (value_key, cache_key)
667
+ value_key = "{}-{}".format(value_key, cache_key)
678
668
 
679
669
  _LOGGER.debug("Cache key: %s", value_key)
680
670
 
@@ -731,7 +721,7 @@ def cache(
731
721
  return cast(F, wrapped_func)
732
722
 
733
723
 
734
- def _hash_func(func: Callable[..., Any], hash_funcs: Optional[HashFuncsDict]) -> str:
724
+ def _hash_func(func: Callable[..., Any], hash_funcs: HashFuncsDict | None) -> str:
735
725
  # Create the unique key for a function's cache. The cache will be retrieved
736
726
  # from inside the wrapped function.
737
727
  #
@@ -839,7 +829,7 @@ class CachedObjectMutationError(ValueError):
839
829
  class CachedStFunctionWarning(StreamlitAPIWarning):
840
830
  def __init__(self, st_func_name, cached_func):
841
831
  msg = self._get_message(st_func_name, cached_func)
842
- super(CachedStFunctionWarning, self).__init__(msg)
832
+ super().__init__(msg)
843
833
 
844
834
  def _get_message(self, st_func_name, cached_func):
845
835
  args = {
@@ -865,7 +855,7 @@ to suppress the warning.
865
855
  class CachedObjectMutationWarning(StreamlitAPIWarning):
866
856
  def __init__(self, orig_exc):
867
857
  msg = self._get_message(orig_exc)
868
- super(CachedObjectMutationWarning, self).__init__(msg)
858
+ super().__init__(msg)
869
859
 
870
860
  def _get_message(self, orig_exc):
871
861
  return (
@@ -14,12 +14,12 @@
14
14
 
15
15
  """A hashing utility for code."""
16
16
 
17
+ from __future__ import annotations
18
+
17
19
  import collections
18
- import dis
19
20
  import enum
20
21
  import functools
21
22
  import hashlib
22
- import importlib
23
23
  import inspect
24
24
  import io
25
25
  import os
@@ -28,9 +28,8 @@ import sys
28
28
  import tempfile
29
29
  import textwrap
30
30
  import threading
31
- import unittest.mock
32
31
  import weakref
33
- from typing import Any, Callable, Dict, List, Optional, Pattern, Type, Union
32
+ from typing import Any, Callable, Dict, Pattern, Type, Union
34
33
 
35
34
  from streamlit import config, file_util, type_util, util
36
35
  from streamlit.errors import MarkdownFormattedException, StreamlitAPIException
@@ -95,8 +94,8 @@ def update_hash(
95
94
  hasher,
96
95
  hash_reason: HashReason,
97
96
  hash_source: Callable[..., Any],
98
- context: Optional[Context] = None,
99
- hash_funcs: Optional[HashFuncsDict] = None,
97
+ context: Context | None = None,
98
+ hash_funcs: HashFuncsDict | None = None,
100
99
  ) -> None:
101
100
  """Updates a hashlib hasher with the hash of val.
102
101
 
@@ -122,15 +121,15 @@ class _HashStack:
122
121
  """
123
122
 
124
123
  def __init__(self):
125
- self._stack: collections.OrderedDict[int, List[Any]] = collections.OrderedDict()
124
+ self._stack: collections.OrderedDict[int, list[Any]] = collections.OrderedDict()
126
125
 
127
126
  # The reason why we're doing this hashing, for debug purposes.
128
- self.hash_reason: Optional[HashReason] = None
127
+ self.hash_reason: HashReason | None = None
129
128
 
130
129
  # Either a function or a code block, depending on whether the reason is
131
130
  # due to hashing part of a function (i.e. body, args, output) or an
132
131
  # st.Cache codeblock.
133
- self.hash_source: Optional[Callable[..., Any]] = None
132
+ self.hash_source: Callable[..., Any] | None = None
134
133
 
135
134
  def __repr__(self) -> str:
136
135
  return util.repr_(self)
@@ -147,7 +146,7 @@ class _HashStack:
147
146
  def pretty_print(self):
148
147
  def to_str(v):
149
148
  try:
150
- return "Object of type %s: %s" % (type_util.get_fqn_type(v), str(v))
149
+ return "Object of type {}: {}".format(type_util.get_fqn_type(v), str(v))
151
150
  except Exception:
152
151
  return "<Unable to convert item to string>"
153
152
 
@@ -272,7 +271,7 @@ def _int_to_bytes(i: int) -> bytes:
272
271
  return i.to_bytes(num_bytes, "little", signed=True)
273
272
 
274
273
 
275
- def _key(obj: Optional[Any]) -> Any:
274
+ def _key(obj: Any | None) -> Any:
276
275
  """Return key for memoization."""
277
276
 
278
277
  if obj is None:
@@ -315,7 +314,7 @@ def _key(obj: Optional[Any]) -> Any:
315
314
  class _CodeHasher:
316
315
  """A hasher that can hash code objects including dependencies."""
317
316
 
318
- def __init__(self, hash_funcs: Optional[HashFuncsDict] = None):
317
+ def __init__(self, hash_funcs: HashFuncsDict | None = None):
319
318
  # Can't use types as the keys in the internal _hash_funcs because
320
319
  # we always remove user-written modules from memory when rerunning a
321
320
  # script in order to reload it and grab the latest code changes.
@@ -332,7 +331,7 @@ class _CodeHasher:
332
331
  else:
333
332
  self._hash_funcs = {}
334
333
 
335
- self._hashes: Dict[Any, bytes] = {}
334
+ self._hashes: dict[Any, bytes] = {}
336
335
 
337
336
  # The number of the bytes in the hash.
338
337
  self.size = 0
@@ -340,7 +339,7 @@ class _CodeHasher:
340
339
  def __repr__(self) -> str:
341
340
  return util.repr_(self)
342
341
 
343
- def to_bytes(self, obj: Any, context: Optional[Context] = None) -> bytes:
342
+ def to_bytes(self, obj: Any, context: Context | None = None) -> bytes:
344
343
  """Add memoization to _to_bytes and protect against cycles in data structures."""
345
344
  tname = type(obj).__qualname__.encode()
346
345
  key = (tname, _key(obj))
@@ -381,7 +380,7 @@ class _CodeHasher:
381
380
 
382
381
  return b
383
382
 
384
- def update(self, hasher, obj: Any, context: Optional[Context] = None) -> None:
383
+ def update(self, hasher, obj: Any, context: Context | None = None) -> None:
385
384
  """Update the provided hasher with the hash of an object."""
386
385
  b = self.to_bytes(obj, context)
387
386
  hasher.update(b)
@@ -403,7 +402,7 @@ class _CodeHasher:
403
402
  filepath, self._get_main_script_directory()
404
403
  ) or file_util.file_in_pythonpath(filepath)
405
404
 
406
- def _to_bytes(self, obj: Any, context: Optional[Context]) -> bytes:
405
+ def _to_bytes(self, obj: Any, context: Context | None) -> bytes:
407
406
  """Hash objects to bytes, including code with dependencies.
408
407
 
409
408
  Python's built in `hash` does not produce consistent results across
@@ -412,7 +411,9 @@ class _CodeHasher:
412
411
 
413
412
  h = hashlib.new("md5", **HASHLIB_KWARGS)
414
413
 
415
- if isinstance(obj, unittest.mock.Mock):
414
+ if type_util.is_type(obj, "unittest.mock.Mock") or type_util.is_type(
415
+ obj, "unittest.mock.MagicMock"
416
+ ):
416
417
  # Mock objects can appear to be infinitely
417
418
  # deep, so we don't try to hash them at all.
418
419
  return self.to_bytes(id(obj))
@@ -615,7 +616,7 @@ class _CodeHasher:
615
616
  if obj.__module__.startswith("streamlit"):
616
617
  # Ignore streamlit modules even if they are in the CWD
617
618
  # (e.g. during development).
618
- return self.to_bytes("%s.%s" % (obj.__module__, obj.__name__))
619
+ return self.to_bytes("{}.{}".format(obj.__module__, obj.__name__))
619
620
 
620
621
  code = getattr(obj, "__code__", None)
621
622
  assert code is not None
@@ -710,11 +711,11 @@ class _CodeHasher:
710
711
  return str(abs_main_path.parent)
711
712
 
712
713
 
713
- def get_referenced_objects(code, context: Context) -> List[Any]:
714
+ def get_referenced_objects(code, context: Context) -> list[Any]:
714
715
  # Top of the stack
715
716
  tos: Any = None
716
717
  lineno = None
717
- refs: List[Any] = []
718
+ refs: list[Any] = []
718
719
 
719
720
  def set_tos(t):
720
721
  nonlocal tos
@@ -729,6 +730,7 @@ def get_referenced_objects(code, context: Context) -> List[Any]:
729
730
  # code reads `bar` of `foo`. We are going over the bytecode to resolve
730
731
  # from which object an attribute is requested.
731
732
  # Read more about bytecode at https://docs.python.org/3/library/dis.html
733
+ import dis
732
734
 
733
735
  for op in dis.get_instructions(code):
734
736
  try:
@@ -747,6 +749,8 @@ def get_referenced_objects(code, context: Context) -> List[Any]:
747
749
  set_tos(context.cells.values[op.argval])
748
750
  elif op.opname == "IMPORT_NAME":
749
751
  try:
752
+ import importlib
753
+
750
754
  set_tos(importlib.import_module(op.argval))
751
755
  except ImportError:
752
756
  set_tos(op.argval)
@@ -785,7 +789,7 @@ class NoResult:
785
789
  class UnhashableTypeError(StreamlitAPIException):
786
790
  def __init__(self, orig_exc, failed_obj):
787
791
  msg = self._get_message(orig_exc, failed_obj)
788
- super(UnhashableTypeError, self).__init__(msg)
792
+ super().__init__(msg)
789
793
  self.with_traceback(orig_exc.__traceback__)
790
794
 
791
795
  def _get_message(self, orig_exc, failed_obj):
@@ -834,7 +838,7 @@ class UserHashError(StreamlitAPIException):
834
838
  else:
835
839
  msg = self._get_message_from_code(orig_exc, cached_func_or_code, lineno)
836
840
 
837
- super(UserHashError, self).__init__(msg)
841
+ super().__init__(msg)
838
842
  self.with_traceback(orig_exc.__traceback__)
839
843
 
840
844
  def _get_message_from_func(self, orig_exc, cached_func, hash_func):
@@ -906,7 +910,7 @@ class InternalHashError(MarkdownFormattedException):
906
910
 
907
911
  def __init__(self, orig_exc: BaseException, failed_obj: Any):
908
912
  msg = self._get_message(orig_exc, failed_obj)
909
- super(InternalHashError, self).__init__(msg)
913
+ super().__init__(msg)
910
914
  self.with_traceback(orig_exc.__traceback__)
911
915
 
912
916
  def _get_message(self, orig_exc: BaseException, failed_obj: Any) -> str:
@@ -949,7 +953,7 @@ for more details.
949
953
  ).strip("\n")
950
954
 
951
955
 
952
- def _get_error_message_args(orig_exc: BaseException, failed_obj: Any) -> Dict[str, Any]:
956
+ def _get_error_message_args(orig_exc: BaseException, failed_obj: Any) -> dict[str, Any]:
953
957
  hash_reason = hash_stacks.current.hash_reason
954
958
  hash_source = hash_stacks.current.hash_source
955
959
 
@@ -984,7 +988,7 @@ def _get_error_message_args(orig_exc: BaseException, failed_obj: Any) -> Dict[st
984
988
  }
985
989
 
986
990
 
987
- def _get_failing_lines(code, lineno: int) -> List[str]:
991
+ def _get_failing_lines(code, lineno: int) -> list[str]:
988
992
  """Get list of strings (lines of code) from lineno to lineno+3.
989
993
 
990
994
  Ideally we'd return the exact line where the error took place, but there