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
@@ -14,14 +14,16 @@
|
|
14
14
|
|
15
15
|
"""Provides global MediaFileManager object as `media_file_manager`."""
|
16
16
|
|
17
|
+
from __future__ import annotations
|
18
|
+
|
17
19
|
import collections
|
18
20
|
import threading
|
19
|
-
from typing import
|
21
|
+
from typing import Final
|
20
22
|
|
21
23
|
from streamlit.logger import get_logger
|
22
24
|
from streamlit.runtime.media_file_storage import MediaFileKind, MediaFileStorage
|
23
25
|
|
24
|
-
|
26
|
+
_LOGGER: Final = get_logger(__name__)
|
25
27
|
|
26
28
|
|
27
29
|
def _get_session_id() -> str:
|
@@ -80,11 +82,11 @@ class MediaFileManager:
|
|
80
82
|
self._storage = storage
|
81
83
|
|
82
84
|
# Dict of [file_id -> MediaFileMetadata]
|
83
|
-
self._file_metadata:
|
85
|
+
self._file_metadata: dict[str, MediaFileMetadata] = dict()
|
84
86
|
|
85
87
|
# Dict[session ID][coordinates] -> file_id.
|
86
|
-
self._files_by_session_and_coord:
|
87
|
-
str,
|
88
|
+
self._files_by_session_and_coord: dict[
|
89
|
+
str, dict[str, str]
|
88
90
|
] = collections.defaultdict(dict)
|
89
91
|
|
90
92
|
# MediaFileManager is used from multiple threads, so all operations
|
@@ -92,7 +94,7 @@ class MediaFileManager:
|
|
92
94
|
# means taking it multiple times from the same thread will deadlock.)
|
93
95
|
self._lock = threading.Lock()
|
94
96
|
|
95
|
-
def _get_inactive_file_ids(self) ->
|
97
|
+
def _get_inactive_file_ids(self) -> set[str]:
|
96
98
|
"""Compute the set of files that are stored in the manager, but are
|
97
99
|
not referenced by any active session. These are files that can be
|
98
100
|
safely deleted.
|
@@ -113,7 +115,7 @@ class MediaFileManager:
|
|
113
115
|
|
114
116
|
Safe to call from any thread.
|
115
117
|
"""
|
116
|
-
|
118
|
+
_LOGGER.debug("Removing orphaned files...")
|
117
119
|
|
118
120
|
with self._lock:
|
119
121
|
for file_id in self._get_inactive_file_ids():
|
@@ -132,11 +134,11 @@ class MediaFileManager:
|
|
132
134
|
|
133
135
|
Thread safety: callers must hold `self._lock`.
|
134
136
|
"""
|
135
|
-
|
137
|
+
_LOGGER.debug("Deleting File: %s", file_id)
|
136
138
|
self._storage.delete_file(file_id)
|
137
139
|
del self._file_metadata[file_id]
|
138
140
|
|
139
|
-
def clear_session_refs(self, session_id:
|
141
|
+
def clear_session_refs(self, session_id: str | None = None) -> None:
|
140
142
|
"""Remove the given session's file references.
|
141
143
|
|
142
144
|
(This does not remove any files from the manager - you must call
|
@@ -149,17 +151,17 @@ class MediaFileManager:
|
|
149
151
|
if session_id is None:
|
150
152
|
session_id = _get_session_id()
|
151
153
|
|
152
|
-
|
154
|
+
_LOGGER.debug("Disconnecting files for session with ID %s", session_id)
|
153
155
|
|
154
156
|
with self._lock:
|
155
157
|
if session_id in self._files_by_session_and_coord:
|
156
158
|
del self._files_by_session_and_coord[session_id]
|
157
159
|
|
158
|
-
|
160
|
+
_LOGGER.debug(
|
159
161
|
"Sessions still active: %r", self._files_by_session_and_coord.keys()
|
160
162
|
)
|
161
163
|
|
162
|
-
|
164
|
+
_LOGGER.debug(
|
163
165
|
"Files: %s; Sessions with files: %s",
|
164
166
|
len(self._file_metadata),
|
165
167
|
len(self._files_by_session_and_coord),
|
@@ -167,10 +169,10 @@ class MediaFileManager:
|
|
167
169
|
|
168
170
|
def add(
|
169
171
|
self,
|
170
|
-
path_or_data:
|
172
|
+
path_or_data: bytes | str,
|
171
173
|
mimetype: str,
|
172
174
|
coordinates: str,
|
173
|
-
file_name:
|
175
|
+
file_name: str | None = None,
|
174
176
|
is_for_static_download: bool = False,
|
175
177
|
) -> str:
|
176
178
|
"""Add a new MediaFile with the given parameters and return its URL.
|
@@ -12,11 +12,11 @@
|
|
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
|
from abc import abstractmethod
|
16
18
|
from enum import Enum
|
17
|
-
from typing import
|
18
|
-
|
19
|
-
from typing_extensions import Protocol
|
19
|
+
from typing import Protocol
|
20
20
|
|
21
21
|
|
22
22
|
class MediaFileKind(Enum):
|
@@ -43,17 +43,17 @@ class MediaFileStorage(Protocol):
|
|
43
43
|
@abstractmethod
|
44
44
|
def load_and_get_id(
|
45
45
|
self,
|
46
|
-
path_or_data:
|
46
|
+
path_or_data: str | bytes,
|
47
47
|
mimetype: str,
|
48
48
|
kind: MediaFileKind,
|
49
|
-
filename:
|
49
|
+
filename: str | None = None,
|
50
50
|
) -> str:
|
51
51
|
"""Load the given file path or bytes into the manager and return
|
52
52
|
an ID that uniquely identifies it.
|
53
53
|
|
54
|
-
It
|
54
|
+
It's an error to pass a URL to this function. (Media stored at
|
55
55
|
external URLs can be served directly to the Streamlit frontend;
|
56
|
-
there
|
56
|
+
there's no need to store this data in MediaFileStorage.)
|
57
57
|
|
58
58
|
Parameters
|
59
59
|
----------
|
@@ -61,7 +61,7 @@ class MediaFileStorage(Protocol):
|
|
61
61
|
A path to a file, or the file's raw data as bytes.
|
62
62
|
|
63
63
|
mimetype
|
64
|
-
The media
|
64
|
+
The media's mimetype. Used to set the Content-Type header when
|
65
65
|
serving the media over HTTP.
|
66
66
|
|
67
67
|
kind
|
@@ -14,13 +14,13 @@
|
|
14
14
|
|
15
15
|
"""MediaFileStorage implementation that stores files in memory."""
|
16
16
|
|
17
|
+
from __future__ import annotations
|
18
|
+
|
17
19
|
import contextlib
|
18
20
|
import hashlib
|
19
21
|
import mimetypes
|
20
22
|
import os.path
|
21
|
-
from typing import
|
22
|
-
|
23
|
-
from typing_extensions import Final
|
23
|
+
from typing import Final, NamedTuple
|
24
24
|
|
25
25
|
from streamlit.logger import get_logger
|
26
26
|
from streamlit.runtime.media_file_storage import (
|
@@ -31,7 +31,7 @@ from streamlit.runtime.media_file_storage import (
|
|
31
31
|
from streamlit.runtime.stats import CacheStat, CacheStatsProvider, group_stats
|
32
32
|
from streamlit.util import HASHLIB_KWARGS
|
33
33
|
|
34
|
-
|
34
|
+
_LOGGER: Final = get_logger(__name__)
|
35
35
|
|
36
36
|
# Mimetype -> filename extension map for the `get_extension_for_mimetype`
|
37
37
|
# function. We use Python's `mimetypes.guess_extension` for most mimetypes,
|
@@ -42,9 +42,7 @@ PREFERRED_MIMETYPE_EXTENSION_MAP: Final = {
|
|
42
42
|
}
|
43
43
|
|
44
44
|
|
45
|
-
def _calculate_file_id(
|
46
|
-
data: bytes, mimetype: str, filename: Optional[str] = None
|
47
|
-
) -> str:
|
45
|
+
def _calculate_file_id(data: bytes, mimetype: str, filename: str | None = None) -> str:
|
48
46
|
"""Hash data, mimetype, and an optional filename to generate a stable file ID.
|
49
47
|
|
50
48
|
Parameters
|
@@ -83,7 +81,7 @@ class MemoryFile(NamedTuple):
|
|
83
81
|
content: bytes
|
84
82
|
mimetype: str
|
85
83
|
kind: MediaFileKind
|
86
|
-
filename:
|
84
|
+
filename: str | None
|
87
85
|
|
88
86
|
@property
|
89
87
|
def content_size(self) -> int:
|
@@ -100,15 +98,15 @@ class MemoryMediaFileStorage(MediaFileStorage, CacheStatsProvider):
|
|
100
98
|
The name of the local endpoint that media is served from.
|
101
99
|
This endpoint should start with a forward-slash (e.g. "/media").
|
102
100
|
"""
|
103
|
-
self._files_by_id:
|
101
|
+
self._files_by_id: dict[str, MemoryFile] = {}
|
104
102
|
self._media_endpoint = media_endpoint
|
105
103
|
|
106
104
|
def load_and_get_id(
|
107
105
|
self,
|
108
|
-
path_or_data:
|
106
|
+
path_or_data: str | bytes,
|
109
107
|
mimetype: str,
|
110
108
|
kind: MediaFileKind,
|
111
|
-
filename:
|
109
|
+
filename: str | None = None,
|
112
110
|
) -> str:
|
113
111
|
"""Add a file to the manager and return its ID."""
|
114
112
|
file_data: bytes
|
@@ -121,7 +119,7 @@ class MemoryMediaFileStorage(MediaFileStorage, CacheStatsProvider):
|
|
121
119
|
# given ID, we don't need to create a new one.
|
122
120
|
file_id = _calculate_file_id(file_data, mimetype, filename)
|
123
121
|
if file_id not in self._files_by_id:
|
124
|
-
|
122
|
+
_LOGGER.debug("Adding media file %s", file_id)
|
125
123
|
media_file = MemoryFile(
|
126
124
|
content=file_data, mimetype=mimetype, kind=kind, filename=filename
|
127
125
|
)
|
@@ -167,12 +165,12 @@ class MemoryMediaFileStorage(MediaFileStorage, CacheStatsProvider):
|
|
167
165
|
except Exception as ex:
|
168
166
|
raise MediaFileStorageError(f"Error opening '{filename}'") from ex
|
169
167
|
|
170
|
-
def get_stats(self) ->
|
168
|
+
def get_stats(self) -> list[CacheStat]:
|
171
169
|
# We operate on a copy of our dict, to avoid race conditions
|
172
170
|
# with other threads that may be manipulating the cache.
|
173
171
|
files_by_id = self._files_by_id.copy()
|
174
172
|
|
175
|
-
stats:
|
173
|
+
stats: list[CacheStat] = []
|
176
174
|
for file_id, file in files_by_id.items():
|
177
175
|
stats.append(
|
178
176
|
CacheStat(
|
@@ -12,8 +12,9 @@
|
|
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
|
15
16
|
|
16
|
-
from typing import
|
17
|
+
from typing import MutableMapping
|
17
18
|
|
18
19
|
from streamlit.runtime.session_manager import SessionInfo, SessionStorage
|
19
20
|
from streamlit.util import TimedCleanupCache
|
@@ -59,7 +60,7 @@ class MemorySessionStorage(SessionStorage):
|
|
59
60
|
maxsize=maxsize, ttl=ttl_seconds
|
60
61
|
)
|
61
62
|
|
62
|
-
def get(self, session_id: str) ->
|
63
|
+
def get(self, session_id: str) -> SessionInfo | None:
|
63
64
|
return self._cache.get(session_id, None)
|
64
65
|
|
65
66
|
def save(self, session_info: SessionInfo) -> None:
|
@@ -68,5 +69,5 @@ class MemorySessionStorage(SessionStorage):
|
|
68
69
|
def delete(self, session_id: str) -> None:
|
69
70
|
del self._cache[session_id]
|
70
71
|
|
71
|
-
def list(self) ->
|
72
|
+
def list(self) -> list[SessionInfo]:
|
72
73
|
return list(self._cache.values())
|
@@ -12,12 +12,13 @@
|
|
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 uuid
|
16
18
|
from collections import defaultdict
|
17
|
-
from typing import
|
19
|
+
from typing import Sequence
|
18
20
|
|
19
21
|
from streamlit import util
|
20
|
-
from streamlit.logger import get_logger
|
21
22
|
from streamlit.runtime.stats import CacheStat, group_stats
|
22
23
|
from streamlit.runtime.uploaded_file_manager import (
|
23
24
|
UploadedFileManager,
|
@@ -25,8 +26,6 @@ from streamlit.runtime.uploaded_file_manager import (
|
|
25
26
|
UploadFileUrlInfo,
|
26
27
|
)
|
27
28
|
|
28
|
-
LOGGER = get_logger(__name__)
|
29
|
-
|
30
29
|
|
31
30
|
class MemoryUploadedFileManager(UploadedFileManager):
|
32
31
|
"""Holds files uploaded by users of the running Streamlit app.
|
@@ -34,12 +33,12 @@ class MemoryUploadedFileManager(UploadedFileManager):
|
|
34
33
|
"""
|
35
34
|
|
36
35
|
def __init__(self, upload_endpoint: str):
|
37
|
-
self.file_storage:
|
36
|
+
self.file_storage: dict[str, dict[str, UploadedFileRec]] = defaultdict(dict)
|
38
37
|
self.endpoint = upload_endpoint
|
39
38
|
|
40
39
|
def get_files(
|
41
40
|
self, session_id: str, file_ids: Sequence[str]
|
42
|
-
) ->
|
41
|
+
) -> list[UploadedFileRec]:
|
43
42
|
"""Return a list of UploadedFileRec for a given sequence of file_ids.
|
44
43
|
|
45
44
|
Parameters
|
@@ -97,7 +96,7 @@ class MemoryUploadedFileManager(UploadedFileManager):
|
|
97
96
|
|
98
97
|
def get_upload_urls(
|
99
98
|
self, session_id: str, file_names: Sequence[str]
|
100
|
-
) ->
|
99
|
+
) -> list[UploadFileUrlInfo]:
|
101
100
|
"""Return a list of UploadFileUrlInfo for a given sequence of file_names."""
|
102
101
|
result = []
|
103
102
|
for _ in file_names:
|
@@ -111,13 +110,13 @@ class MemoryUploadedFileManager(UploadedFileManager):
|
|
111
110
|
)
|
112
111
|
return result
|
113
112
|
|
114
|
-
def get_stats(self) ->
|
113
|
+
def get_stats(self) -> list[CacheStat]:
|
115
114
|
"""Return the manager's CacheStats.
|
116
115
|
|
117
116
|
Safe to call from any thread.
|
118
117
|
"""
|
119
118
|
# Flatten all files into a single list
|
120
|
-
all_files:
|
119
|
+
all_files: list[UploadedFileRec] = []
|
121
120
|
# Make copy of self.file_storage for thread safety, to be sure
|
122
121
|
# that main storage won't be changed form other thread
|
123
122
|
file_storage_copy = self.file_storage.copy()
|
@@ -125,7 +124,7 @@ class MemoryUploadedFileManager(UploadedFileManager):
|
|
125
124
|
for session_storage in file_storage_copy.values():
|
126
125
|
all_files.extend(session_storage.values())
|
127
126
|
|
128
|
-
stats:
|
127
|
+
stats: list[CacheStat] = [
|
129
128
|
CacheStat(
|
130
129
|
category_name="UploadedFileManager",
|
131
130
|
cache_name="",
|
@@ -12,6 +12,8 @@
|
|
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
18
|
import inspect
|
17
19
|
import os
|
@@ -21,17 +23,14 @@ import time
|
|
21
23
|
import uuid
|
22
24
|
from collections.abc import Sized
|
23
25
|
from functools import wraps
|
24
|
-
from
|
25
|
-
from typing import Any, Callable, List, Optional, Set, TypeVar, Union, cast, overload
|
26
|
-
|
27
|
-
from typing_extensions import Final
|
26
|
+
from typing import Any, Callable, Final, TypeVar, cast, overload
|
28
27
|
|
29
28
|
from streamlit import config, util
|
30
29
|
from streamlit.logger import get_logger
|
31
30
|
from streamlit.proto.ForwardMsg_pb2 import ForwardMsg
|
32
31
|
from streamlit.proto.PageProfile_pb2 import Argument, Command
|
33
32
|
|
34
|
-
_LOGGER = get_logger(__name__)
|
33
|
+
_LOGGER: Final = get_logger(__name__)
|
35
34
|
|
36
35
|
# Limit the number of commands to keep the page profile message small
|
37
36
|
# since Segment allows only a maximum of 32kb per event.
|
@@ -161,11 +160,11 @@ def _get_machine_id_v3() -> str:
|
|
161
160
|
|
162
161
|
machine_id = str(uuid.getnode())
|
163
162
|
if os.path.isfile(_ETC_MACHINE_ID_PATH):
|
164
|
-
with open(_ETC_MACHINE_ID_PATH
|
163
|
+
with open(_ETC_MACHINE_ID_PATH) as f:
|
165
164
|
machine_id = f.read()
|
166
165
|
|
167
166
|
elif os.path.isfile(_DBUS_MACHINE_ID_PATH):
|
168
|
-
with open(_DBUS_MACHINE_ID_PATH
|
167
|
+
with open(_DBUS_MACHINE_ID_PATH) as f:
|
169
168
|
machine_id = f.read()
|
170
169
|
|
171
170
|
return machine_id
|
@@ -173,10 +172,10 @@ def _get_machine_id_v3() -> str:
|
|
173
172
|
|
174
173
|
class Installation:
|
175
174
|
_instance_lock = threading.Lock()
|
176
|
-
_instance:
|
175
|
+
_instance: Installation | None = None
|
177
176
|
|
178
177
|
@classmethod
|
179
|
-
def instance(cls) ->
|
178
|
+
def instance(cls) -> Installation:
|
180
179
|
"""Returns the singleton Installation"""
|
181
180
|
# We use a double-checked locking optimization to avoid the overhead
|
182
181
|
# of acquiring the lock in the common case:
|
@@ -228,7 +227,7 @@ def _get_top_level_module(func: Callable[..., Any]) -> str:
|
|
228
227
|
return module.__name__.split(".")[0]
|
229
228
|
|
230
229
|
|
231
|
-
def _get_arg_metadata(arg: object) ->
|
230
|
+
def _get_arg_metadata(arg: object) -> str | None:
|
232
231
|
"""Get metadata information related to the value of the given object."""
|
233
232
|
with contextlib.suppress(Exception):
|
234
233
|
if isinstance(arg, (bool)):
|
@@ -245,8 +244,8 @@ def _get_command_telemetry(
|
|
245
244
|
) -> Command:
|
246
245
|
"""Get telemetry information for the given callable and its arguments."""
|
247
246
|
arg_keywords = inspect.getfullargspec(_command_func).args
|
248
|
-
self_arg:
|
249
|
-
arguments:
|
247
|
+
self_arg: Any | None = None
|
248
|
+
arguments: list[Argument] = []
|
250
249
|
is_method = inspect.ismethod(_command_func)
|
251
250
|
name = _command_name
|
252
251
|
|
@@ -315,7 +314,7 @@ def gather_metrics(
|
|
315
314
|
...
|
316
315
|
|
317
316
|
|
318
|
-
def gather_metrics(name: str, func:
|
317
|
+
def gather_metrics(name: str, func: F | None = None) -> Callable[[F], F] | F:
|
319
318
|
"""Function decorator to add telemetry tracking to commands.
|
320
319
|
|
321
320
|
Parameters
|
@@ -351,11 +350,13 @@ def gather_metrics(name: str, func: Optional[F] = None) -> Union[Callable[[F], F
|
|
351
350
|
|
352
351
|
return wrapper
|
353
352
|
else:
|
354
|
-
# To make mypy type narrow
|
353
|
+
# To make mypy type narrow F | None -> F
|
355
354
|
non_optional_func = func
|
356
355
|
|
357
356
|
@wraps(non_optional_func)
|
358
357
|
def wrapped_func(*args, **kwargs):
|
358
|
+
from timeit import default_timer as timer
|
359
|
+
|
359
360
|
exec_start = timer()
|
360
361
|
# Local imports to prevent circular dependencies
|
361
362
|
from streamlit.runtime.scriptrunner import get_script_run_ctx
|
@@ -371,8 +372,7 @@ def gather_metrics(name: str, func: Optional[F] = None) -> Union[Callable[[F], F
|
|
371
372
|
< _MAX_TRACKED_COMMANDS # Prevent too much memory usage
|
372
373
|
)
|
373
374
|
|
374
|
-
|
375
|
-
command_telemetry: Optional[Command] = None
|
375
|
+
command_telemetry: Command | None = None
|
376
376
|
|
377
377
|
if ctx and tracking_activated:
|
378
378
|
try:
|
@@ -421,10 +421,10 @@ def gather_metrics(name: str, func: Optional[F] = None) -> Union[Callable[[F], F
|
|
421
421
|
|
422
422
|
|
423
423
|
def create_page_profile_message(
|
424
|
-
commands:
|
424
|
+
commands: list[Command],
|
425
425
|
exec_time: int,
|
426
426
|
prep_time: int,
|
427
|
-
uncaught_exception:
|
427
|
+
uncaught_exception: str | None = None,
|
428
428
|
) -> ForwardMsg:
|
429
429
|
"""Create and return the full PageProfile ForwardMsg."""
|
430
430
|
msg = ForwardMsg()
|
@@ -435,7 +435,7 @@ def create_page_profile_message(
|
|
435
435
|
msg.page_profile.headless = config.get_option("server.headless")
|
436
436
|
|
437
437
|
# Collect all config options that have been manually set
|
438
|
-
config_options:
|
438
|
+
config_options: set[str] = set()
|
439
439
|
if config._config_options:
|
440
440
|
for option_name in config._config_options.keys():
|
441
441
|
if not config.is_manually_set(option_name):
|
@@ -450,7 +450,7 @@ def create_page_profile_message(
|
|
450
450
|
msg.page_profile.config.extend(config_options)
|
451
451
|
|
452
452
|
# Check the predefined set of modules for attribution
|
453
|
-
attributions:
|
453
|
+
attributions: set[str] = {
|
454
454
|
attribution
|
455
455
|
for attribution in _ATTRIBUTIONS_TO_CHECK
|
456
456
|
if attribution in sys.modules
|
streamlit/runtime/runtime.py
CHANGED
@@ -19,9 +19,7 @@ import time
|
|
19
19
|
import traceback
|
20
20
|
from dataclasses import dataclass, field
|
21
21
|
from enum import Enum
|
22
|
-
from typing import TYPE_CHECKING, Awaitable,
|
23
|
-
|
24
|
-
from typing_extensions import Final
|
22
|
+
from typing import TYPE_CHECKING, Awaitable, Final, NamedTuple
|
25
23
|
|
26
24
|
from streamlit import config
|
27
25
|
from streamlit.logger import get_logger
|
@@ -69,7 +67,7 @@ if TYPE_CHECKING:
|
|
69
67
|
# Wait for the script run result for 60s and if no result is available give up
|
70
68
|
SCRIPT_RUN_CHECK_TIMEOUT: Final = 60
|
71
69
|
|
72
|
-
|
70
|
+
_LOGGER: Final = get_logger(__name__)
|
73
71
|
|
74
72
|
|
75
73
|
class RuntimeStoppedError(Exception):
|
@@ -85,7 +83,7 @@ class RuntimeConfig:
|
|
85
83
|
|
86
84
|
# DEPRECATED: We need to keep this field around for compatibility reasons, but we no
|
87
85
|
# longer use this anywhere.
|
88
|
-
command_line:
|
86
|
+
command_line: str | None
|
89
87
|
|
90
88
|
# The storage backend for Streamlit's MediaFileManager.
|
91
89
|
media_file_storage: MediaFileStorage
|
@@ -99,7 +97,7 @@ class RuntimeConfig:
|
|
99
97
|
)
|
100
98
|
|
101
99
|
# The SessionManager class to be used.
|
102
|
-
session_manager_class:
|
100
|
+
session_manager_class: type[SessionManager] = WebsocketSessionManager
|
103
101
|
|
104
102
|
# The SessionStorage instance for the SessionManager to use.
|
105
103
|
session_storage: SessionStorage = field(default_factory=MemorySessionStorage)
|
@@ -141,7 +139,7 @@ class AsyncObjects(NamedTuple):
|
|
141
139
|
|
142
140
|
|
143
141
|
class Runtime:
|
144
|
-
_instance:
|
142
|
+
_instance: Runtime | None = None
|
145
143
|
|
146
144
|
@classmethod
|
147
145
|
def instance(cls) -> Runtime:
|
@@ -179,11 +177,11 @@ class Runtime:
|
|
179
177
|
Runtime._instance = self
|
180
178
|
|
181
179
|
# Will be created when we start.
|
182
|
-
self._async_objs:
|
180
|
+
self._async_objs: AsyncObjects | None = None
|
183
181
|
|
184
182
|
# The task that runs our main loop. We need to save a reference
|
185
183
|
# to it so that it doesn't get garbage collected while running.
|
186
|
-
self._loop_coroutine_task:
|
184
|
+
self._loop_coroutine_task: asyncio.Task[None] | None = None
|
187
185
|
|
188
186
|
self._main_script_path = config.script_path
|
189
187
|
self._is_hello = config.is_hello
|
@@ -247,7 +245,7 @@ class Runtime:
|
|
247
245
|
# happen to be threadsafe. This may change with future SessionManager implementations,
|
248
246
|
# at which point we'll need to formalize our thread safety rules for each
|
249
247
|
# SessionManager method.
|
250
|
-
def get_client(self, session_id: str) ->
|
248
|
+
def get_client(self, session_id: str) -> SessionClient | None:
|
251
249
|
"""Get the SessionClient for the given session_id, or None
|
252
250
|
if no such session exists.
|
253
251
|
|
@@ -304,7 +302,7 @@ class Runtime:
|
|
304
302
|
if self._state in (RuntimeState.STOPPING, RuntimeState.STOPPED):
|
305
303
|
return
|
306
304
|
|
307
|
-
|
305
|
+
_LOGGER.debug("Runtime stopping...")
|
308
306
|
self._set_state(RuntimeState.STOPPING)
|
309
307
|
async_objs.must_stop.set()
|
310
308
|
|
@@ -322,9 +320,9 @@ class Runtime:
|
|
322
320
|
def connect_session(
|
323
321
|
self,
|
324
322
|
client: SessionClient,
|
325
|
-
user_info:
|
326
|
-
existing_session_id:
|
327
|
-
session_id_override:
|
323
|
+
user_info: dict[str, str | None],
|
324
|
+
existing_session_id: str | None = None,
|
325
|
+
session_id_override: str | None = None,
|
328
326
|
) -> str:
|
329
327
|
"""Create a new session (or connect to an existing one) and return its unique ID.
|
330
328
|
|
@@ -383,9 +381,9 @@ class Runtime:
|
|
383
381
|
def create_session(
|
384
382
|
self,
|
385
383
|
client: SessionClient,
|
386
|
-
user_info:
|
387
|
-
existing_session_id:
|
388
|
-
session_id_override:
|
384
|
+
user_info: dict[str, str | None],
|
385
|
+
existing_session_id: str | None = None,
|
386
|
+
session_id_override: str | None = None,
|
389
387
|
) -> str:
|
390
388
|
"""Create a new session (or connect to an existing one) and return its unique ID.
|
391
389
|
|
@@ -394,7 +392,7 @@ class Runtime:
|
|
394
392
|
This method is simply an alias for connect_session added for backwards
|
395
393
|
compatibility.
|
396
394
|
"""
|
397
|
-
|
395
|
+
_LOGGER.warning("create_session is deprecated! Use connect_session instead.")
|
398
396
|
return self.connect_session(
|
399
397
|
client=client,
|
400
398
|
user_info=user_info,
|
@@ -478,7 +476,7 @@ class Runtime:
|
|
478
476
|
|
479
477
|
session_info = self._session_mgr.get_active_session_info(session_id)
|
480
478
|
if session_info is None:
|
481
|
-
|
479
|
+
_LOGGER.debug(
|
482
480
|
"Discarding BackMsg for disconnected session (id=%s)", session_id
|
483
481
|
)
|
484
482
|
return
|
@@ -508,7 +506,7 @@ class Runtime:
|
|
508
506
|
|
509
507
|
session_info = self._session_mgr.get_active_session_info(session_id)
|
510
508
|
if session_info is None:
|
511
|
-
|
509
|
+
_LOGGER.debug(
|
512
510
|
"Discarding BackMsg Exception for disconnected session (id=%s)",
|
513
511
|
session_id,
|
514
512
|
)
|
@@ -517,7 +515,7 @@ class Runtime:
|
|
517
515
|
session_info.session.handle_backmsg_exception(exc)
|
518
516
|
|
519
517
|
@property
|
520
|
-
async def is_ready_for_browser_connection(self) ->
|
518
|
+
async def is_ready_for_browser_connection(self) -> tuple[bool, str]:
|
521
519
|
if self._state not in (
|
522
520
|
RuntimeState.INITIAL,
|
523
521
|
RuntimeState.STOPPING,
|
@@ -527,7 +525,7 @@ class Runtime:
|
|
527
525
|
|
528
526
|
return False, "unavailable"
|
529
527
|
|
530
|
-
async def does_script_run_without_error(self) ->
|
528
|
+
async def does_script_run_without_error(self) -> tuple[bool, str]:
|
531
529
|
"""Load and execute the app's script to verify it runs without an error.
|
532
530
|
|
533
531
|
Returns
|
@@ -572,7 +570,7 @@ class Runtime:
|
|
572
570
|
session.shutdown()
|
573
571
|
|
574
572
|
def _set_state(self, new_state: RuntimeState) -> None:
|
575
|
-
|
573
|
+
_LOGGER.debug("Runtime state: %s -> %s", self._state, new_state)
|
576
574
|
self._state = new_state
|
577
575
|
|
578
576
|
async def _loop_coroutine(self) -> None:
|
@@ -655,7 +653,7 @@ class Runtime:
|
|
655
653
|
except Exception as e:
|
656
654
|
async_objs.stopped.set_exception(e)
|
657
655
|
traceback.print_exc()
|
658
|
-
|
656
|
+
_LOGGER.info(
|
659
657
|
"""
|
660
658
|
Please report this bug at https://github.com/streamlit/streamlit/issues.
|
661
659
|
"""
|
@@ -689,13 +687,13 @@ Please report this bug at https://github.com/streamlit/streamlit/issues.
|
|
689
687
|
):
|
690
688
|
# This session has probably cached this message. Send
|
691
689
|
# a reference instead.
|
692
|
-
|
690
|
+
_LOGGER.debug("Sending cached message ref (hash=%s)", msg.hash)
|
693
691
|
msg_to_send = create_reference_msg(msg)
|
694
692
|
|
695
693
|
# Cache the message so it can be referenced in the future.
|
696
694
|
# If the message is already cached, this will reset its
|
697
695
|
# age.
|
698
|
-
|
696
|
+
_LOGGER.debug("Caching message (hash=%s)", msg.hash)
|
699
697
|
self._message_cache.add_message(
|
700
698
|
msg, session_info.session, session_info.script_run_count
|
701
699
|
)
|
@@ -706,7 +704,7 @@ Please report this bug at https://github.com/streamlit/streamlit/issues.
|
|
706
704
|
msg.WhichOneof("type") == "script_finished"
|
707
705
|
and msg.script_finished == ForwardMsg.FINISHED_SUCCESSFULLY
|
708
706
|
):
|
709
|
-
|
707
|
+
_LOGGER.debug(
|
710
708
|
"Script run finished successfully; "
|
711
709
|
"removing expired entries from MessageCache "
|
712
710
|
"(max_age=%s)",
|