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
streamlit/runtime/app_session.py
CHANGED
@@ -11,11 +11,14 @@
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
|
+
|
15
|
+
from __future__ import annotations
|
16
|
+
|
14
17
|
import asyncio
|
15
18
|
import sys
|
16
19
|
import uuid
|
17
20
|
from enum import Enum
|
18
|
-
from typing import TYPE_CHECKING, Callable,
|
21
|
+
from typing import TYPE_CHECKING, Callable, Final
|
19
22
|
|
20
23
|
import streamlit.elements.exception as exception_utils
|
21
24
|
from streamlit import config, runtime, source_util
|
@@ -44,10 +47,11 @@ from streamlit.runtime.uploaded_file_manager import UploadedFileManager
|
|
44
47
|
from streamlit.version import STREAMLIT_VERSION_STRING
|
45
48
|
from streamlit.watcher import LocalSourcesWatcher
|
46
49
|
|
47
|
-
LOGGER = get_logger(__name__)
|
48
50
|
if TYPE_CHECKING:
|
49
51
|
from streamlit.runtime.state import SessionState
|
50
52
|
|
53
|
+
_LOGGER: Final = get_logger(__name__)
|
54
|
+
|
51
55
|
|
52
56
|
class AppSessionState(Enum):
|
53
57
|
APP_NOT_RUNNING = "APP_NOT_RUNNING"
|
@@ -77,10 +81,10 @@ class AppSession:
|
|
77
81
|
script_data: ScriptData,
|
78
82
|
uploaded_file_manager: UploadedFileManager,
|
79
83
|
script_cache: ScriptCache,
|
80
|
-
message_enqueued_callback:
|
84
|
+
message_enqueued_callback: Callable[[], None] | None,
|
81
85
|
local_sources_watcher: LocalSourcesWatcher,
|
82
|
-
user_info:
|
83
|
-
session_id_override:
|
86
|
+
user_info: dict[str, str | None],
|
87
|
+
session_id_override: str | None = None,
|
84
88
|
) -> None:
|
85
89
|
"""Initialize the AppSession.
|
86
90
|
|
@@ -139,17 +143,15 @@ class AppSession:
|
|
139
143
|
# due to the source code changing we need to pass in the previous client state.
|
140
144
|
self._client_state = ClientState()
|
141
145
|
|
142
|
-
self._local_sources_watcher:
|
143
|
-
|
144
|
-
] =
|
145
|
-
self._stop_config_listener: Optional[Callable[[], bool]] = None
|
146
|
-
self._stop_pages_listener: Optional[Callable[[], bool]] = None
|
146
|
+
self._local_sources_watcher: LocalSourcesWatcher | None = local_sources_watcher
|
147
|
+
self._stop_config_listener: Callable[[], bool] | None = None
|
148
|
+
self._stop_pages_listener: Callable[[], None] | None = None
|
147
149
|
|
148
150
|
self.register_file_watchers()
|
149
151
|
|
150
152
|
self._run_on_save = config.get_option("server.runOnSave")
|
151
153
|
|
152
|
-
self._scriptrunner:
|
154
|
+
self._scriptrunner: ScriptRunner | None = None
|
153
155
|
|
154
156
|
# This needs to be lazily imported to avoid a dependency cycle.
|
155
157
|
from streamlit.runtime.state import SessionState
|
@@ -157,9 +159,9 @@ class AppSession:
|
|
157
159
|
self._session_state = SessionState()
|
158
160
|
self._user_info = user_info
|
159
161
|
|
160
|
-
self._debug_last_backmsg_id:
|
162
|
+
self._debug_last_backmsg_id: str | None = None
|
161
163
|
|
162
|
-
|
164
|
+
_LOGGER.debug("AppSession initialized (id=%s)", self.id)
|
163
165
|
|
164
166
|
def __del__(self) -> None:
|
165
167
|
"""Ensure that we call shutdown() when an AppSession is garbage collected."""
|
@@ -210,7 +212,7 @@ class AppSession:
|
|
210
212
|
self._stop_config_listener = None
|
211
213
|
self._stop_pages_listener = None
|
212
214
|
|
213
|
-
def flush_browser_queue(self) ->
|
215
|
+
def flush_browser_queue(self) -> list[ForwardMsg]:
|
214
216
|
"""Clear the forward message queue and return the messages it contained.
|
215
217
|
|
216
218
|
The Server calls this periodically to deliver new messages
|
@@ -232,7 +234,7 @@ class AppSession:
|
|
232
234
|
|
233
235
|
"""
|
234
236
|
if self._state != AppSessionState.SHUTDOWN_REQUESTED:
|
235
|
-
|
237
|
+
_LOGGER.debug("Shutting down (id=%s)", self.id)
|
236
238
|
# Clear any unused session files in upload file manager and media
|
237
239
|
# file manager
|
238
240
|
self._uploaded_file_mgr.remove_session_files(self.id)
|
@@ -298,10 +300,10 @@ class AppSession:
|
|
298
300
|
elif msg_type == "file_urls_request":
|
299
301
|
self._handle_file_urls_request(msg.file_urls_request)
|
300
302
|
else:
|
301
|
-
|
303
|
+
_LOGGER.warning('No handler for "%s"', msg_type)
|
302
304
|
|
303
305
|
except Exception as ex:
|
304
|
-
|
306
|
+
_LOGGER.error(ex)
|
305
307
|
self.handle_backmsg_exception(ex)
|
306
308
|
|
307
309
|
def handle_backmsg_exception(self, e: BaseException) -> None:
|
@@ -333,7 +335,7 @@ class AppSession:
|
|
333
335
|
lambda: self._enqueue_forward_msg(self._create_exception_message(e))
|
334
336
|
)
|
335
337
|
|
336
|
-
def request_rerun(self, client_state:
|
338
|
+
def request_rerun(self, client_state: ClientState | None) -> None:
|
337
339
|
"""Signal that we're interested in running the script.
|
338
340
|
|
339
341
|
If the script is not already running, it will be started immediately.
|
@@ -347,7 +349,7 @@ class AppSession:
|
|
347
349
|
|
348
350
|
"""
|
349
351
|
if self._state == AppSessionState.SHUTDOWN_REQUESTED:
|
350
|
-
|
352
|
+
_LOGGER.warning("Discarding rerun request after shutdown")
|
351
353
|
return
|
352
354
|
|
353
355
|
if client_state:
|
@@ -403,7 +405,7 @@ class AppSession:
|
|
403
405
|
self._scriptrunner.start()
|
404
406
|
|
405
407
|
@property
|
406
|
-
def session_state(self) ->
|
408
|
+
def session_state(self) -> SessionState:
|
407
409
|
return self._session_state
|
408
410
|
|
409
411
|
def _should_rerun_on_file_change(self, filepath: str) -> bool:
|
@@ -421,7 +423,7 @@ class AppSession:
|
|
421
423
|
|
422
424
|
return True
|
423
425
|
|
424
|
-
def _on_source_file_changed(self, filepath:
|
426
|
+
def _on_source_file_changed(self, filepath: str | None = None) -> None:
|
425
427
|
"""One of our source files changed. Clear the cache and schedule a rerun if appropriate."""
|
426
428
|
self._script_cache.clear()
|
427
429
|
|
@@ -456,12 +458,12 @@ class AppSession:
|
|
456
458
|
|
457
459
|
def _on_scriptrunner_event(
|
458
460
|
self,
|
459
|
-
sender:
|
461
|
+
sender: ScriptRunner | None,
|
460
462
|
event: ScriptRunnerEvent,
|
461
|
-
forward_msg:
|
462
|
-
exception:
|
463
|
-
client_state:
|
464
|
-
page_script_hash:
|
463
|
+
forward_msg: ForwardMsg | None = None,
|
464
|
+
exception: BaseException | None = None,
|
465
|
+
client_state: ClientState | None = None,
|
466
|
+
page_script_hash: str | None = None,
|
465
467
|
) -> None:
|
466
468
|
"""Called when our ScriptRunner emits an event.
|
467
469
|
|
@@ -477,12 +479,12 @@ class AppSession:
|
|
477
479
|
|
478
480
|
def _handle_scriptrunner_event_on_event_loop(
|
479
481
|
self,
|
480
|
-
sender:
|
482
|
+
sender: ScriptRunner | None,
|
481
483
|
event: ScriptRunnerEvent,
|
482
|
-
forward_msg:
|
483
|
-
exception:
|
484
|
-
client_state:
|
485
|
-
page_script_hash:
|
484
|
+
forward_msg: ForwardMsg | None = None,
|
485
|
+
exception: BaseException | None = None,
|
486
|
+
client_state: ClientState | None = None,
|
487
|
+
page_script_hash: str | None = None,
|
486
488
|
) -> None:
|
487
489
|
"""Handle a ScriptRunner event.
|
488
490
|
|
@@ -525,7 +527,7 @@ class AppSession:
|
|
525
527
|
# rerun request, for example) while another ScriptRunner is still
|
526
528
|
# shutting down. The shutting-down ScriptRunner may still
|
527
529
|
# emit events.
|
528
|
-
|
530
|
+
_LOGGER.debug("Ignoring event from non-current ScriptRunner: %s", event)
|
529
531
|
return
|
530
532
|
|
531
533
|
prev_state = self._state
|
@@ -663,7 +665,7 @@ class AppSession:
|
|
663
665
|
return msg
|
664
666
|
|
665
667
|
def _create_script_finished_message(
|
666
|
-
self, status:
|
668
|
+
self, status: ForwardMsg.ScriptFinishedStatus.ValueType
|
667
669
|
) -> ForwardMsg:
|
668
670
|
"""Create and return a script_finished ForwardMsg."""
|
669
671
|
msg = ForwardMsg()
|
@@ -712,10 +714,10 @@ class AppSession:
|
|
712
714
|
except Exception as ex:
|
713
715
|
# Users may never even install Git in the first place, so this
|
714
716
|
# error requires no action. It can be useful for debugging.
|
715
|
-
|
717
|
+
_LOGGER.debug("Obtaining Git information produced an error", exc_info=ex)
|
716
718
|
|
717
719
|
def _handle_rerun_script_request(
|
718
|
-
self, client_state:
|
720
|
+
self, client_state: ClientState | None = None
|
719
721
|
) -> None:
|
720
722
|
"""Tell the ScriptRunner to re-run its script.
|
721
723
|
|
@@ -794,10 +796,10 @@ class AppSession:
|
|
794
796
|
# This field will be available at runtime as of protobuf 3.20.1, but
|
795
797
|
# we are using an older version.
|
796
798
|
# For details, see: https://github.com/protocolbuffers/protobuf/issues/8175
|
797
|
-
def _get_toolbar_mode() ->
|
799
|
+
def _get_toolbar_mode() -> Config.ToolbarMode.ValueType:
|
798
800
|
config_key = "client.toolbarMode"
|
799
801
|
config_value = config.get_option(config_key)
|
800
|
-
enum_value:
|
802
|
+
enum_value: Config.ToolbarMode.ValueType | None = getattr(
|
801
803
|
Config.ToolbarMode, config_value.upper()
|
802
804
|
)
|
803
805
|
if enum_value is None:
|
@@ -844,7 +846,7 @@ def _populate_theme_msg(msg: CustomThemeConfig) -> None:
|
|
844
846
|
base = theme_opts["base"]
|
845
847
|
if base is not None:
|
846
848
|
if base not in base_map:
|
847
|
-
|
849
|
+
_LOGGER.warning(
|
848
850
|
f'"{base}" is an invalid value for theme.base.'
|
849
851
|
f" Allowed values include {list(base_map.keys())}."
|
850
852
|
' Setting theme.base to "light".'
|
@@ -860,7 +862,7 @@ def _populate_theme_msg(msg: CustomThemeConfig) -> None:
|
|
860
862
|
font = theme_opts["font"]
|
861
863
|
if font is not None:
|
862
864
|
if font not in font_map:
|
863
|
-
|
865
|
+
_LOGGER.warning(
|
864
866
|
f'"{font}" is an invalid value for theme.font.'
|
865
867
|
f" Allowed values include {list(font_map.keys())}."
|
866
868
|
' Setting theme.font to "sans serif".'
|
@@ -874,9 +876,7 @@ def _populate_user_info_msg(msg: UserInfo) -> None:
|
|
874
876
|
msg.installation_id_v3 = Installation.instance().installation_id_v3
|
875
877
|
|
876
878
|
|
877
|
-
def _populate_app_pages(
|
878
|
-
msg: Union[NewSession, PagesChanged], main_script_path: str
|
879
|
-
) -> None:
|
879
|
+
def _populate_app_pages(msg: NewSession | PagesChanged, main_script_path: str) -> None:
|
880
880
|
for page_script_hash, page_info in source_util.get_pages(main_script_path).items():
|
881
881
|
page_proto = msg.app_pages.add()
|
882
882
|
|
@@ -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 contextlib
|
16
|
-
from typing import Any, Iterator
|
18
|
+
from typing import Any, Iterator
|
17
19
|
|
18
20
|
from google.protobuf.message import Message
|
19
21
|
|
@@ -77,9 +79,7 @@ def save_widget_metadata(metadata: WidgetMetadata[Any]) -> None:
|
|
77
79
|
CACHE_RESOURCE_MESSAGE_REPLAY_CTX.save_widget_metadata(metadata)
|
78
80
|
|
79
81
|
|
80
|
-
def save_media_data(
|
81
|
-
image_data: Union[bytes, str], mimetype: str, image_id: str
|
82
|
-
) -> None:
|
82
|
+
def save_media_data(image_data: bytes | str, mimetype: str, image_id: str) -> None:
|
83
83
|
CACHE_DATA_MESSAGE_REPLAY_CTX.save_image_data(image_data, mimetype, image_id)
|
84
84
|
CACHE_RESOURCE_MESSAGE_REPLAY_CTX.save_image_data(image_data, mimetype, image_id)
|
85
85
|
|
@@ -20,9 +20,9 @@ import pickle
|
|
20
20
|
import threading
|
21
21
|
import types
|
22
22
|
from datetime import timedelta
|
23
|
-
from typing import Any, Callable, TypeVar, Union, cast, overload
|
23
|
+
from typing import Any, Callable, Final, Literal, TypeVar, Union, cast, overload
|
24
24
|
|
25
|
-
from typing_extensions import
|
25
|
+
from typing_extensions import TypeAlias
|
26
26
|
|
27
27
|
import streamlit as st
|
28
28
|
from streamlit import runtime
|
@@ -62,7 +62,7 @@ from streamlit.runtime.metrics_util import gather_metrics
|
|
62
62
|
from streamlit.runtime.scriptrunner.script_run_context import get_script_run_ctx
|
63
63
|
from streamlit.runtime.stats import CacheStat, CacheStatsProvider, group_stats
|
64
64
|
|
65
|
-
_LOGGER = get_logger(__name__)
|
65
|
+
_LOGGER: Final = get_logger(__name__)
|
66
66
|
|
67
67
|
CACHE_DATA_MESSAGE_REPLAY_CTX = CachedMessageReplayContext(CacheType.DATA)
|
68
68
|
|
@@ -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 types
|
16
|
-
from typing import Any
|
18
|
+
from typing import Any
|
17
19
|
|
18
20
|
from streamlit import type_util
|
19
21
|
from streamlit.errors import (
|
@@ -50,7 +52,7 @@ class UnhashableParamError(StreamlitAPIException):
|
|
50
52
|
self,
|
51
53
|
cache_type: CacheType,
|
52
54
|
func: types.FunctionType,
|
53
|
-
arg_name:
|
55
|
+
arg_name: str | None,
|
54
56
|
arg_value: Any,
|
55
57
|
orig_exc: BaseException,
|
56
58
|
):
|
@@ -62,7 +64,7 @@ class UnhashableParamError(StreamlitAPIException):
|
|
62
64
|
def _create_message(
|
63
65
|
cache_type: CacheType,
|
64
66
|
func: types.FunctionType,
|
65
|
-
arg_name:
|
67
|
+
arg_name: str | None,
|
66
68
|
arg_value: Any,
|
67
69
|
) -> str:
|
68
70
|
arg_name_str = arg_name if arg_name is not None else "(unnamed)"
|
@@ -26,9 +26,7 @@ import types
|
|
26
26
|
from abc import abstractmethod
|
27
27
|
from collections import defaultdict
|
28
28
|
from datetime import timedelta
|
29
|
-
from typing import Any, Callable, overload
|
30
|
-
|
31
|
-
from typing_extensions import Literal
|
29
|
+
from typing import Any, Callable, Final, Literal, overload
|
32
30
|
|
33
31
|
from streamlit import type_util
|
34
32
|
from streamlit.elements.spinner import spinner
|
@@ -53,7 +51,7 @@ from streamlit.runtime.caching.cached_message_replay import (
|
|
53
51
|
from streamlit.runtime.caching.hashing import HashFuncsDict, update_hash
|
54
52
|
from streamlit.util import HASHLIB_KWARGS
|
55
53
|
|
56
|
-
_LOGGER = get_logger(__name__)
|
54
|
+
_LOGGER: Final = get_logger(__name__)
|
57
55
|
|
58
56
|
# The timer function we use with TTLCache. This is the default timer func, but
|
59
57
|
# is exposed here as a constant so that it can be patched in unit tests.
|
@@ -19,10 +19,17 @@ import hashlib
|
|
19
19
|
import threading
|
20
20
|
import types
|
21
21
|
from dataclasses import dataclass
|
22
|
-
from typing import
|
22
|
+
from typing import (
|
23
|
+
TYPE_CHECKING,
|
24
|
+
Any,
|
25
|
+
Final,
|
26
|
+
Iterator,
|
27
|
+
Protocol,
|
28
|
+
Union,
|
29
|
+
runtime_checkable,
|
30
|
+
)
|
23
31
|
|
24
32
|
from google.protobuf.message import Message
|
25
|
-
from typing_extensions import Protocol, runtime_checkable
|
26
33
|
|
27
34
|
import streamlit as st
|
28
35
|
from streamlit import runtime, util
|
@@ -45,7 +52,7 @@ from streamlit.util import HASHLIB_KWARGS
|
|
45
52
|
if TYPE_CHECKING:
|
46
53
|
from streamlit.delta_generator import DeltaGenerator
|
47
54
|
|
48
|
-
_LOGGER = get_logger(__name__)
|
55
|
+
_LOGGER: Final = get_logger(__name__)
|
49
56
|
|
50
57
|
|
51
58
|
@runtime_checkable
|
@@ -369,7 +376,7 @@ class CachedMessageReplayContext(threading.local):
|
|
369
376
|
|
370
377
|
def maybe_show_cached_st_function_warning(
|
371
378
|
self,
|
372
|
-
dg:
|
379
|
+
dg: DeltaGenerator,
|
373
380
|
st_func_name: str,
|
374
381
|
) -> None:
|
375
382
|
"""If appropriate, warn about calling st.foo inside @memo.
|
@@ -401,7 +408,7 @@ class CachedMessageReplayContext(threading.local):
|
|
401
408
|
|
402
409
|
def _show_cached_st_function_warning(
|
403
410
|
self,
|
404
|
-
dg:
|
411
|
+
dg: DeltaGenerator,
|
405
412
|
st_func_name: str,
|
406
413
|
cached_func: types.FunctionType,
|
407
414
|
) -> None:
|
@@ -13,6 +13,9 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
15
|
"""Hashing for st.cache_data and st.cache_resource."""
|
16
|
+
|
17
|
+
from __future__ import annotations
|
18
|
+
|
16
19
|
import collections
|
17
20
|
import dataclasses
|
18
21
|
import datetime
|
@@ -22,15 +25,15 @@ import inspect
|
|
22
25
|
import io
|
23
26
|
import os
|
24
27
|
import pickle
|
25
|
-
import struct
|
26
28
|
import sys
|
27
29
|
import tempfile
|
28
30
|
import threading
|
29
|
-
import unittest.mock
|
30
31
|
import uuid
|
31
32
|
import weakref
|
32
33
|
from enum import Enum
|
33
|
-
from typing import Any, Callable, Dict,
|
34
|
+
from typing import Any, Callable, Dict, Final, Pattern, Type, Union
|
35
|
+
|
36
|
+
from typing_extensions import TypeAlias
|
34
37
|
|
35
38
|
from streamlit import type_util, util
|
36
39
|
from streamlit.errors import StreamlitAPIException
|
@@ -40,18 +43,20 @@ from streamlit.runtime.uploaded_file_manager import UploadedFile
|
|
40
43
|
from streamlit.util import HASHLIB_KWARGS
|
41
44
|
|
42
45
|
# If a dataframe has more than this many rows, we consider it large and hash a sample.
|
43
|
-
_PANDAS_ROWS_LARGE = 100000
|
44
|
-
_PANDAS_SAMPLE_SIZE = 10000
|
46
|
+
_PANDAS_ROWS_LARGE: Final = 100000
|
47
|
+
_PANDAS_SAMPLE_SIZE: Final = 10000
|
45
48
|
|
46
49
|
# Similar to dataframes, we also sample large numpy arrays.
|
47
|
-
_NP_SIZE_LARGE = 1000000
|
48
|
-
_NP_SAMPLE_SIZE = 100000
|
50
|
+
_NP_SIZE_LARGE: Final = 1000000
|
51
|
+
_NP_SAMPLE_SIZE: Final = 100000
|
49
52
|
|
50
|
-
HashFuncsDict = Dict[Union[str, Type[Any]], Callable[[Any], Any]]
|
53
|
+
HashFuncsDict: TypeAlias = Dict[Union[str, Type[Any]], Callable[[Any], Any]]
|
51
54
|
|
52
55
|
# Arbitrary item to denote where we found a cycle in a hashed object.
|
53
56
|
# This allows us to hash self-referencing lists, dictionaries, etc.
|
54
|
-
_CYCLE_PLACEHOLDER =
|
57
|
+
_CYCLE_PLACEHOLDER: Final = (
|
58
|
+
b"streamlit-57R34ML17-hesamagicalponyflyingthroughthesky-CYCLE"
|
59
|
+
)
|
55
60
|
|
56
61
|
|
57
62
|
class UserHashError(StreamlitAPIException):
|
@@ -60,7 +65,7 @@ class UserHashError(StreamlitAPIException):
|
|
60
65
|
orig_exc,
|
61
66
|
object_to_hash,
|
62
67
|
hash_func,
|
63
|
-
cache_type:
|
68
|
+
cache_type: CacheType | None = None,
|
64
69
|
):
|
65
70
|
self.alternate_name = type(orig_exc).__name__
|
66
71
|
self.hash_func = hash_func
|
@@ -101,7 +106,7 @@ If you think this is actually a Streamlit bug, please
|
|
101
106
|
self,
|
102
107
|
orig_exc: BaseException,
|
103
108
|
failed_obj: Any,
|
104
|
-
) ->
|
109
|
+
) -> dict[str, Any]:
|
105
110
|
hash_source = hash_stacks.current.hash_source
|
106
111
|
|
107
112
|
failed_obj_type_str = type_util.get_fqn_type(failed_obj)
|
@@ -139,8 +144,8 @@ def update_hash(
|
|
139
144
|
val: Any,
|
140
145
|
hasher,
|
141
146
|
cache_type: CacheType,
|
142
|
-
hash_source:
|
143
|
-
hash_funcs:
|
147
|
+
hash_source: Callable[..., Any] | None = None,
|
148
|
+
hash_funcs: HashFuncsDict | None = None,
|
144
149
|
) -> None:
|
145
150
|
"""Updates a hashlib hasher with the hash of val.
|
146
151
|
|
@@ -166,10 +171,10 @@ class _HashStack:
|
|
166
171
|
"""
|
167
172
|
|
168
173
|
def __init__(self):
|
169
|
-
self._stack: collections.OrderedDict[int,
|
174
|
+
self._stack: collections.OrderedDict[int, list[Any]] = collections.OrderedDict()
|
170
175
|
# A function that we decorate with streamlit cache
|
171
176
|
# primitive (st.cache_data or st.cache_resource).
|
172
|
-
self.hash_source:
|
177
|
+
self.hash_source: Callable[..., Any] | None = None
|
173
178
|
|
174
179
|
def __repr__(self) -> str:
|
175
180
|
return util.repr_(self)
|
@@ -226,11 +231,14 @@ def _int_to_bytes(i: int) -> bytes:
|
|
226
231
|
|
227
232
|
|
228
233
|
def _float_to_bytes(f: float) -> bytes:
|
234
|
+
# Lazy-load for performance reasons.
|
235
|
+
import struct
|
236
|
+
|
229
237
|
# Floats are 64bit in Python, so we need to use the "d" format.
|
230
238
|
return struct.pack("<d", f)
|
231
239
|
|
232
240
|
|
233
|
-
def _key(obj:
|
241
|
+
def _key(obj: Any | None) -> Any:
|
234
242
|
"""Return key for memoization."""
|
235
243
|
|
236
244
|
if obj is None:
|
@@ -268,9 +276,7 @@ def _key(obj: Optional[Any]) -> Any:
|
|
268
276
|
class _CacheFuncHasher:
|
269
277
|
"""A hasher that can hash objects with cycles."""
|
270
278
|
|
271
|
-
def __init__(
|
272
|
-
self, cache_type: CacheType, hash_funcs: Optional[HashFuncsDict] = None
|
273
|
-
):
|
279
|
+
def __init__(self, cache_type: CacheType, hash_funcs: HashFuncsDict | None = None):
|
274
280
|
# Can't use types as the keys in the internal _hash_funcs because
|
275
281
|
# we always remove user-written modules from memory when rerunning a
|
276
282
|
# script in order to reload it and grab the latest code changes.
|
@@ -286,7 +292,7 @@ class _CacheFuncHasher:
|
|
286
292
|
}
|
287
293
|
else:
|
288
294
|
self._hash_funcs = {}
|
289
|
-
self._hashes:
|
295
|
+
self._hashes: dict[Any, bytes] = {}
|
290
296
|
|
291
297
|
# The number of the bytes in the hash.
|
292
298
|
self.size = 0
|
@@ -344,7 +350,9 @@ class _CacheFuncHasher:
|
|
344
350
|
|
345
351
|
h = hashlib.new("md5", **HASHLIB_KWARGS)
|
346
352
|
|
347
|
-
if
|
353
|
+
if type_util.is_type(obj, "unittest.mock.Mock") or type_util.is_type(
|
354
|
+
obj, "unittest.mock.MagicMock"
|
355
|
+
):
|
348
356
|
# Mock objects can appear to be infinitely
|
349
357
|
# deep, so we don't try to hash them at all.
|
350
358
|
return self.to_bytes(id(obj))
|
@@ -61,6 +61,7 @@ from __future__ import annotations
|
|
61
61
|
import math
|
62
62
|
import os
|
63
63
|
import shutil
|
64
|
+
from typing import Final
|
64
65
|
|
65
66
|
from streamlit import util
|
66
67
|
from streamlit.file_util import get_streamlit_file_path, streamlit_read, streamlit_write
|
@@ -76,16 +77,16 @@ from streamlit.runtime.caching.storage.in_memory_cache_storage_wrapper import (
|
|
76
77
|
InMemoryCacheStorageWrapper,
|
77
78
|
)
|
78
79
|
|
80
|
+
_LOGGER: Final = get_logger(__name__)
|
81
|
+
|
79
82
|
# Streamlit directory where persisted @st.cache_data objects live.
|
80
83
|
# (This is the same directory that @st.cache persisted objects live.
|
81
84
|
# But @st.cache_data uses a different extension, so they don't overlap.)
|
82
|
-
_CACHE_DIR_NAME = "cache"
|
85
|
+
_CACHE_DIR_NAME: Final = "cache"
|
83
86
|
|
84
87
|
# The extension for our persisted @st.cache_data objects.
|
85
88
|
# (`@st.cache_data` was originally called `@st.memo`)
|
86
|
-
_CACHED_FILE_EXTENSION = "memo"
|
87
|
-
|
88
|
-
_LOGGER = get_logger(__name__)
|
89
|
+
_CACHED_FILE_EXTENSION: Final = "memo"
|
89
90
|
|
90
91
|
|
91
92
|
class LocalDiskCacheStorageManager(CacheStorageManager):
|
@@ -168,7 +169,7 @@ class LocalDiskCacheStorage(CacheStorage):
|
|
168
169
|
# Clean up file so we don't leave zero byte files.
|
169
170
|
try:
|
170
171
|
os.remove(path)
|
171
|
-
except (FileNotFoundError,
|
172
|
+
except (FileNotFoundError, OSError):
|
172
173
|
# If we can't remove the file, it's not a big deal.
|
173
174
|
pass
|
174
175
|
raise CacheStorageError("Unable to write to cache") from e
|
@@ -14,13 +14,10 @@
|
|
14
14
|
|
15
15
|
from __future__ import annotations
|
16
16
|
|
17
|
-
import importlib
|
18
17
|
import os
|
19
18
|
import re
|
20
19
|
from datetime import timedelta
|
21
|
-
from typing import Any,
|
22
|
-
|
23
|
-
from typing_extensions import Final, Literal
|
20
|
+
from typing import Any, Final, Literal, TypeVar, overload
|
24
21
|
|
25
22
|
from streamlit.connections import (
|
26
23
|
BaseConnection,
|
@@ -45,7 +42,7 @@ FIRST_PARTY_CONNECTIONS = {
|
|
45
42
|
"sql": SQLConnection,
|
46
43
|
}
|
47
44
|
MODULE_EXTRACTION_REGEX = re.compile(r"No module named \'(.+)\'")
|
48
|
-
MODULES_TO_PYPI_PACKAGES: Final[
|
45
|
+
MODULES_TO_PYPI_PACKAGES: Final[dict[str, str]] = {
|
49
46
|
"MySQLdb": "mysqlclient",
|
50
47
|
"psycopg2": "psycopg2-binary",
|
51
48
|
"sqlalchemy": "sqlalchemy",
|
@@ -63,7 +60,7 @@ ConnectionClass = TypeVar("ConnectionClass", bound=BaseConnection[Any])
|
|
63
60
|
@gather_metrics("connection")
|
64
61
|
def _create_connection(
|
65
62
|
name: str,
|
66
|
-
connection_class:
|
63
|
+
connection_class: type[ConnectionClass],
|
67
64
|
max_entries: int | None = None,
|
68
65
|
ttl: float | timedelta | None = None,
|
69
66
|
**kwargs,
|
@@ -77,7 +74,7 @@ def _create_connection(
|
|
77
74
|
"""
|
78
75
|
|
79
76
|
def __create_connection(
|
80
|
-
name: str, connection_class:
|
77
|
+
name: str, connection_class: type[ConnectionClass], **kwargs
|
81
78
|
) -> ConnectionClass:
|
82
79
|
return connection_class(connection_name=name, **kwargs)
|
83
80
|
|
@@ -184,7 +181,7 @@ def connection_factory(
|
|
184
181
|
@overload
|
185
182
|
def connection_factory(
|
186
183
|
name: str,
|
187
|
-
type:
|
184
|
+
type: type[ConnectionClass],
|
188
185
|
max_entries: int | None = None,
|
189
186
|
ttl: float | timedelta | None = None,
|
190
187
|
**kwargs,
|
@@ -312,6 +309,9 @@ def connection_factory(
|
|
312
309
|
if "." in connection_class:
|
313
310
|
parts = connection_class.split(".")
|
314
311
|
classname = parts.pop()
|
312
|
+
|
313
|
+
import importlib
|
314
|
+
|
315
315
|
connection_module = importlib.import_module(".".join(parts))
|
316
316
|
connection_class = getattr(connection_module, classname)
|
317
317
|
else:
|