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.
- streamlit/case_converters.py +9 -4
- streamlit/cli_util.py +2 -0
- streamlit/code_util.py +5 -2
- streamlit/color_util.py +2 -0
- streamlit/column_config.py +2 -0
- streamlit/commands/execution_control.py +4 -2
- streamlit/commands/experimental_query_params.py +7 -4
- streamlit/commands/page_config.py +11 -9
- streamlit/components/v1/components.py +23 -16
- streamlit/config.py +3 -5
- streamlit/config_option.py +12 -11
- streamlit/connections/base_connection.py +4 -2
- streamlit/connections/snowflake_connection.py +4 -4
- streamlit/connections/snowpark_connection.py +3 -3
- streamlit/connections/sql_connection.py +6 -6
- streamlit/connections/util.py +8 -5
- streamlit/constants.py +2 -0
- streamlit/cursor.py +16 -14
- streamlit/delta_generator.py +10 -13
- streamlit/deprecation_util.py +4 -3
- streamlit/echo.py +5 -3
- streamlit/elements/alert.py +16 -14
- streamlit/elements/altair_utils.py +8 -6
- streamlit/elements/arrow.py +4 -4
- streamlit/elements/arrow_altair.py +24 -34
- streamlit/elements/arrow_vega_lite.py +9 -14
- streamlit/elements/balloons.py +4 -2
- streamlit/elements/bokeh_chart.py +7 -7
- streamlit/elements/code.py +6 -4
- streamlit/elements/deck_gl_json_chart.py +8 -8
- streamlit/elements/doc_string.py +5 -9
- streamlit/elements/empty.py +4 -2
- streamlit/elements/exception.py +10 -10
- streamlit/elements/form.py +1 -3
- streamlit/elements/graphviz_chart.py +5 -6
- streamlit/elements/heading.py +16 -14
- streamlit/elements/iframe.py +14 -12
- streamlit/elements/image.py +8 -8
- streamlit/elements/json.py +6 -4
- streamlit/elements/layouts.py +12 -10
- streamlit/elements/lib/column_config_utils.py +2 -2
- streamlit/elements/lib/column_types.py +23 -23
- streamlit/elements/lib/dicttools.py +10 -6
- streamlit/elements/lib/mutable_status_container.py +7 -7
- streamlit/elements/lib/pandas_styler_utils.py +6 -6
- streamlit/elements/lib/streamlit_plotly_theme.py +2 -0
- streamlit/elements/map.py +11 -22
- streamlit/elements/markdown.py +16 -14
- streamlit/elements/media.py +16 -16
- streamlit/elements/metric.py +9 -7
- streamlit/elements/plotly_chart.py +5 -5
- streamlit/elements/progress.py +6 -6
- streamlit/elements/pyplot.py +10 -13
- streamlit/elements/snow.py +4 -2
- streamlit/elements/spinner.py +2 -0
- streamlit/elements/text.py +7 -5
- streamlit/elements/toast.py +6 -4
- streamlit/elements/utils.py +15 -28
- streamlit/elements/widgets/button.py +39 -39
- streamlit/elements/widgets/camera_input.py +21 -17
- streamlit/elements/widgets/chat.py +6 -7
- streamlit/elements/widgets/checkbox.py +21 -19
- streamlit/elements/widgets/color_picker.py +18 -16
- streamlit/elements/widgets/data_editor.py +7 -7
- streamlit/elements/widgets/file_uploader.py +59 -55
- streamlit/elements/widgets/multiselect.py +33 -42
- streamlit/elements/widgets/number_input.py +10 -5
- streamlit/elements/widgets/radio.py +1 -1
- streamlit/elements/widgets/select_slider.py +25 -34
- streamlit/elements/widgets/selectbox.py +1 -1
- streamlit/elements/widgets/slider.py +28 -36
- streamlit/elements/widgets/text_widgets.py +6 -6
- streamlit/elements/widgets/time_widgets.py +13 -13
- streamlit/elements/write.py +21 -29
- streamlit/env_util.py +5 -3
- streamlit/error_util.py +7 -3
- streamlit/errors.py +3 -1
- streamlit/external/langchain/streamlit_callback_handler.py +26 -24
- streamlit/file_util.py +18 -14
- streamlit/folder_black_list.py +3 -1
- streamlit/git_util.py +5 -3
- streamlit/js_number.py +10 -13
- streamlit/logger.py +5 -5
- streamlit/net_util.py +14 -11
- streamlit/platform.py +2 -0
- streamlit/runtime/__init__.py +2 -0
- streamlit/runtime/app_session.py +42 -42
- streamlit/runtime/caching/__init__.py +4 -4
- streamlit/runtime/caching/cache_data_api.py +3 -3
- streamlit/runtime/caching/cache_errors.py +5 -3
- streamlit/runtime/caching/cache_type.py +2 -0
- streamlit/runtime/caching/cache_utils.py +2 -4
- streamlit/runtime/caching/cached_message_replay.py +12 -5
- streamlit/runtime/caching/hashing.py +29 -21
- streamlit/runtime/caching/storage/cache_storage_protocol.py +1 -2
- streamlit/runtime/caching/storage/local_disk_cache_storage.py +6 -5
- streamlit/runtime/connection_factory.py +8 -8
- streamlit/runtime/forward_msg_cache.py +20 -18
- streamlit/runtime/forward_msg_queue.py +8 -9
- streamlit/runtime/legacy_caching/caching.py +32 -42
- streamlit/runtime/legacy_caching/hashing.py +29 -25
- streamlit/runtime/media_file_manager.py +16 -14
- streamlit/runtime/media_file_storage.py +8 -8
- streamlit/runtime/memory_media_file_storage.py +12 -14
- streamlit/runtime/memory_session_storage.py +4 -3
- streamlit/runtime/memory_uploaded_file_manager.py +9 -10
- streamlit/runtime/metrics_util.py +20 -20
- streamlit/runtime/runtime.py +25 -27
- streamlit/runtime/runtime_util.py +5 -3
- streamlit/runtime/script_data.py +2 -0
- streamlit/runtime/scriptrunner/magic.py +17 -11
- streamlit/runtime/scriptrunner/magic_funcs.py +2 -0
- streamlit/runtime/scriptrunner/script_requests.py +6 -4
- streamlit/runtime/scriptrunner/script_run_context.py +17 -17
- streamlit/runtime/scriptrunner/script_runner.py +7 -5
- streamlit/runtime/secrets.py +4 -6
- streamlit/runtime/session_manager.py +14 -14
- streamlit/runtime/state/common.py +5 -4
- streamlit/runtime/state/query_params.py +8 -6
- streamlit/runtime/state/query_params_proxy.py +7 -5
- streamlit/runtime/state/safe_session_state.py +7 -5
- streamlit/runtime/state/session_state.py +3 -4
- streamlit/runtime/state/session_state_proxy.py +5 -5
- streamlit/runtime/state/widgets.py +20 -18
- streamlit/runtime/stats.py +13 -15
- streamlit/runtime/uploaded_file_manager.py +6 -5
- streamlit/runtime/websocket_session_manager.py +14 -14
- streamlit/source_util.py +13 -11
- streamlit/static/asset-manifest.json +13 -13
- streamlit/static/index.html +1 -1
- streamlit/static/static/css/2411.8b8f33d6.chunk.css +1 -0
- streamlit/static/static/css/43.e3b876c5.chunk.css +1 -0
- streamlit/static/static/css/6692.65519639.chunk.css +1 -0
- streamlit/static/static/js/{3075.76725a14.chunk.js → 2411.714d213e.chunk.js} +2 -2
- streamlit/static/static/js/4185.21ca0590.chunk.js +1 -0
- streamlit/static/static/js/43.36939bb1.chunk.js +1 -0
- streamlit/static/static/js/{5117.6a701db1.chunk.js → 5117.04bfe5d3.chunk.js} +1 -1
- streamlit/static/static/js/{5791.30b01ee8.chunk.js → 5791.c5138157.chunk.js} +1 -1
- streamlit/static/static/js/656.8c998bc8.chunk.js +2 -0
- streamlit/static/static/js/{6692.6ac4ea6f.chunk.js → 6692.6496cbc2.chunk.js} +1 -1
- streamlit/static/static/js/7142.400eefdd.chunk.js +1 -0
- streamlit/static/static/js/main.2737c0f9.js +2 -0
- streamlit/static/static/js/{main.043d802e.js.LICENSE.txt → main.2737c0f9.js.LICENSE.txt} +23 -25
- streamlit/string_util.py +13 -9
- streamlit/temporary_directory.py +3 -1
- streamlit/testing/v1/element_tree.py +1 -2
- streamlit/testing/v1/util.py +7 -3
- streamlit/type_util.py +30 -25
- streamlit/url_util.py +6 -4
- streamlit/user_info.py +8 -6
- streamlit/util.py +23 -37
- streamlit/version.py +16 -9
- streamlit/watcher/event_based_path_watcher.py +10 -10
- streamlit/watcher/local_sources_watcher.py +15 -13
- streamlit/watcher/path_watcher.py +0 -3
- streamlit/watcher/polling_path_watcher.py +9 -8
- streamlit/watcher/util.py +3 -2
- streamlit/web/cache_storage_manager_config.py +2 -0
- streamlit/web/server/app_static_file_handler.py +6 -5
- streamlit/web/server/browser_websocket_handler.py +10 -8
- streamlit/web/server/component_request_handler.py +7 -4
- streamlit/web/server/media_file_handler.py +5 -4
- streamlit/web/server/routes.py +6 -3
- streamlit/web/server/server.py +41 -34
- streamlit/web/server/server_util.py +8 -3
- streamlit/web/server/stats_request_handler.py +14 -5
- streamlit/web/server/upload_file_request_handler.py +7 -8
- streamlit/web/server/websocket_headers.py +2 -2
- {streamlit_nightly-1.31.2.dev20240212.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/METADATA +1 -1
- {streamlit_nightly-1.31.2.dev20240212.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/RECORD +176 -176
- streamlit/static/static/css/3075.81b3d18f.chunk.css +0 -1
- streamlit/static/static/css/43.c24b25fa.chunk.css +0 -1
- streamlit/static/static/css/6692.bb444a79.chunk.css +0 -1
- streamlit/static/static/js/1215.baf3721f.chunk.js +0 -2
- streamlit/static/static/js/4185.90e929dc.chunk.js +0 -1
- streamlit/static/static/js/43.8ca4bc8a.chunk.js +0 -1
- streamlit/static/static/js/7142.a359ed63.chunk.js +0 -1
- streamlit/static/static/js/main.043d802e.js +0 -2
- /streamlit/static/static/js/{3075.76725a14.chunk.js.LICENSE.txt → 2411.714d213e.chunk.js.LICENSE.txt} +0 -0
- /streamlit/static/static/js/{1215.baf3721f.chunk.js.LICENSE.txt → 656.8c998bc8.chunk.js.LICENSE.txt} +0 -0
- {streamlit_nightly-1.31.2.dev20240212.data → streamlit_nightly-1.31.2.dev20240214.data}/scripts/streamlit.cmd +0 -0
- {streamlit_nightly-1.31.2.dev20240212.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/WHEEL +0 -0
- {streamlit_nightly-1.31.2.dev20240212.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/entry_points.txt +0 -0
- {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,
|
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
|
-
|
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:
|
112
|
+
def __init__(self, msg: ForwardMsg | None):
|
111
113
|
self.msg = msg
|
112
114
|
self._session_script_run_counts: MutableMapping[
|
113
|
-
|
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:
|
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
|
-
|
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:
|
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:
|
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:
|
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:
|
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:
|
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) ->
|
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:
|
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:
|
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:
|
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
|
-
|
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) ->
|
286
|
-
stats:
|
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
|
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:
|
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:
|
41
|
+
self._delta_index_map: dict[tuple[int, ...], int] = dict()
|
43
42
|
|
44
|
-
def get_debug(self) ->
|
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) ->
|
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) ->
|
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:
|
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:
|
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:
|
177
|
-
ttl:
|
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) ->
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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,
|
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:
|
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:
|
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:
|
470
|
-
max_entries:
|
471
|
-
ttl:
|
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:
|
484
|
-
max_entries:
|
485
|
-
ttl:
|
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:
|
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:
|
497
|
-
max_entries:
|
498
|
-
ttl:
|
499
|
-
) ->
|
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 = "
|
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:
|
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(
|
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(
|
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,
|
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:
|
99
|
-
hash_funcs:
|
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,
|
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:
|
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:
|
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
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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
|
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("
|
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) ->
|
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:
|
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(
|
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(
|
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(
|
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) ->
|
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) ->
|
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
|