streamlit-nightly 1.31.2.dev20240213__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 +8 -19
- 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/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/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/uploaded_file_manager.py +6 -5
- streamlit/runtime/websocket_session_manager.py +14 -14
- streamlit/source_util.py +13 -11
- streamlit/string_util.py +13 -9
- streamlit/temporary_directory.py +3 -1
- streamlit/testing/v1/element_tree.py +1 -2
- streamlit/type_util.py +21 -25
- streamlit/url_util.py +6 -4
- streamlit/user_info.py +8 -6
- streamlit/util.py +23 -37
- 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 +31 -31
- streamlit/web/server/server_util.py +4 -2
- streamlit/web/server/upload_file_request_handler.py +7 -8
- streamlit/web/server/websocket_headers.py +2 -2
- {streamlit_nightly-1.31.2.dev20240213.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/METADATA +1 -1
- {streamlit_nightly-1.31.2.dev20240213.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/RECORD +153 -153
- {streamlit_nightly-1.31.2.dev20240213.data → streamlit_nightly-1.31.2.dev20240214.data}/scripts/streamlit.cmd +0 -0
- {streamlit_nightly-1.31.2.dev20240213.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/WHEEL +0 -0
- {streamlit_nightly-1.31.2.dev20240213.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/entry_points.txt +0 -0
- {streamlit_nightly-1.31.2.dev20240213.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/top_level.txt +0 -0
@@ -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,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="",
|