streamlit-nightly 1.45.1.dev20250508__py3-none-any.whl → 1.45.1.dev20250510__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/auth_util.py +3 -3
- streamlit/cli_util.py +3 -2
- streamlit/commands/execution_control.py +1 -1
- streamlit/commands/logo.py +2 -2
- streamlit/commands/navigation.py +6 -5
- streamlit/commands/page_config.py +3 -1
- streamlit/components/types/base_custom_component.py +7 -7
- streamlit/components/v1/custom_component.py +6 -6
- streamlit/config.py +9 -9
- streamlit/config_option.py +2 -2
- streamlit/connections/base_connection.py +3 -3
- streamlit/connections/snowflake_connection.py +5 -5
- streamlit/connections/snowpark_connection.py +3 -3
- streamlit/connections/sql_connection.py +9 -10
- streamlit/cursor.py +6 -6
- streamlit/delta_generator.py +7 -6
- streamlit/delta_generator_singletons.py +3 -3
- streamlit/deprecation_util.py +1 -1
- streamlit/elements/arrow.py +4 -5
- streamlit/elements/code.py +1 -1
- streamlit/elements/dialog_decorator.py +3 -3
- streamlit/elements/doc_string.py +20 -20
- streamlit/elements/layouts.py +26 -7
- streamlit/elements/lib/built_in_chart_utils.py +38 -40
- streamlit/elements/lib/color_util.py +3 -3
- streamlit/elements/lib/column_types.py +4 -4
- streamlit/elements/lib/dialog.py +2 -2
- streamlit/elements/lib/event_utils.py +1 -1
- streamlit/elements/lib/image_utils.py +1 -1
- streamlit/elements/lib/mutable_status_container.py +1 -1
- streamlit/elements/lib/options_selector_utils.py +2 -2
- streamlit/elements/lib/policies.py +3 -3
- streamlit/elements/lib/utils.py +5 -5
- streamlit/elements/metric.py +1 -1
- streamlit/elements/progress.py +9 -12
- streamlit/elements/vega_charts.py +3 -3
- streamlit/elements/widgets/audio_input.py +1 -1
- streamlit/elements/widgets/button.py +1 -1
- streamlit/elements/widgets/button_group.py +4 -4
- streamlit/elements/widgets/camera_input.py +1 -1
- streamlit/elements/widgets/checkbox.py +2 -2
- streamlit/elements/widgets/color_picker.py +1 -1
- streamlit/elements/widgets/data_editor.py +3 -3
- streamlit/elements/widgets/file_uploader.py +1 -1
- streamlit/elements/widgets/multiselect.py +3 -3
- streamlit/elements/widgets/number_input.py +2 -2
- streamlit/elements/widgets/radio.py +1 -1
- streamlit/elements/widgets/select_slider.py +1 -1
- streamlit/elements/widgets/selectbox.py +2 -2
- streamlit/elements/widgets/slider.py +19 -17
- streamlit/elements/widgets/text_widgets.py +2 -2
- streamlit/elements/widgets/time_widgets.py +2 -2
- streamlit/elements/write.py +3 -6
- streamlit/errors.py +34 -21
- streamlit/external/langchain/streamlit_callback_handler.py +2 -2
- streamlit/file_util.py +8 -8
- streamlit/git_util.py +2 -2
- streamlit/hello/dataframe_demo.py +1 -1
- streamlit/hello/mapping_demo.py +1 -1
- streamlit/navigation/page.py +1 -1
- streamlit/proto/Block_pb2.py +38 -29
- streamlit/proto/Block_pb2.pyi +68 -4
- streamlit/proto/DataFrame_pb2.pyi +1 -1
- streamlit/proto/GapSize_pb2.py +29 -0
- streamlit/proto/GapSize_pb2.pyi +70 -0
- streamlit/proto/HeightConfig_pb2.py +27 -0
- streamlit/proto/HeightConfig_pb2.pyi +48 -0
- streamlit/proto/NamedDataSet_pb2.pyi +1 -1
- streamlit/proto/WidthConfig_pb2.py +2 -2
- streamlit/proto/WidthConfig_pb2.pyi +15 -1
- streamlit/runtime/app_session.py +2 -2
- streamlit/runtime/caching/cache_data_api.py +9 -5
- streamlit/runtime/caching/cache_errors.py +3 -3
- streamlit/runtime/caching/cache_resource_api.py +7 -7
- streamlit/runtime/caching/cache_utils.py +9 -9
- streamlit/runtime/caching/cached_message_replay.py +1 -1
- streamlit/runtime/caching/hashing.py +70 -74
- streamlit/runtime/caching/legacy_cache_api.py +1 -1
- streamlit/runtime/caching/storage/in_memory_cache_storage_wrapper.py +3 -1
- streamlit/runtime/caching/storage/local_disk_cache_storage.py +1 -1
- streamlit/runtime/connection_factory.py +15 -15
- streamlit/runtime/context.py +2 -2
- streamlit/runtime/credentials.py +3 -3
- streamlit/runtime/fragment.py +3 -4
- streamlit/runtime/media_file_manager.py +2 -2
- streamlit/runtime/memory_media_file_storage.py +1 -1
- streamlit/runtime/memory_uploaded_file_manager.py +1 -1
- streamlit/runtime/metrics_util.py +5 -6
- streamlit/runtime/pages_manager.py +3 -3
- streamlit/runtime/runtime.py +1 -1
- streamlit/runtime/runtime_util.py +2 -2
- streamlit/runtime/scriptrunner/exec_code.py +10 -3
- streamlit/runtime/scriptrunner/magic.py +13 -8
- streamlit/runtime/scriptrunner/script_runner.py +2 -2
- streamlit/runtime/scriptrunner_utils/exceptions.py +1 -1
- streamlit/runtime/scriptrunner_utils/script_run_context.py +6 -5
- streamlit/runtime/secrets.py +8 -11
- streamlit/runtime/state/query_params.py +2 -2
- streamlit/runtime/state/query_params_proxy.py +1 -1
- streamlit/runtime/state/safe_session_state.py +2 -2
- streamlit/runtime/state/session_state.py +17 -15
- streamlit/runtime/stats.py +1 -1
- streamlit/runtime/uploaded_file_manager.py +1 -1
- streamlit/source_util.py +3 -4
- streamlit/static/index.html +1 -1
- streamlit/static/static/js/{ErrorOutline.esm.C9UoaGEN.js → ErrorOutline.esm.BewaDzzJ.js} +1 -1
- streamlit/static/static/js/{FileDownload.esm.DoToR9q0.js → FileDownload.esm.D6ilL7v-.js} +1 -1
- streamlit/static/static/js/{FileHelper.Bt4VJ--Q.js → FileHelper.q7erXtkb.js} +1 -1
- streamlit/static/static/js/{FormClearHelper.D5PbW8FI.js → FormClearHelper.Dd0v0IXt.js} +1 -1
- streamlit/static/static/js/{Hooks.DgkQ2Xp9.js → Hooks.CchmJZGs.js} +1 -1
- streamlit/static/static/js/{InputInstructions.DLnLhwHI.js → InputInstructions.DI9h4PJ7.js} +1 -1
- streamlit/static/static/js/{ProgressBar.BtSgh_K-.js → ProgressBar.DfcVR_JA.js} +1 -1
- streamlit/static/static/js/{RenderInPortalIfExists.CjdyBvQX.js → RenderInPortalIfExists.BAkT-LV5.js} +1 -1
- streamlit/static/static/js/{Toolbar.CAYIzVZk.js → Toolbar.CNzefYiX.js} +1 -1
- streamlit/static/static/js/{base-input.CD4xW4_9.js → base-input.BmA1ZV9Y.js} +1 -1
- streamlit/static/static/js/{checkbox.Ba4jj5dR.js → checkbox.mT4TKcAG.js} +1 -1
- streamlit/static/static/js/{createSuper.BBGT9Ijd.js → createSuper.Duo43uhY.js} +1 -1
- streamlit/static/static/js/{data-grid-overlay-editor.CYuk1Aj7.js → data-grid-overlay-editor.CY3l_gmP.js} +1 -1
- streamlit/static/static/js/{downloader.DM9KEOCw.js → downloader.CM5asV91.js} +1 -1
- streamlit/static/static/js/{es6.FqM62T4b.js → es6.uiOeU51e.js} +2 -2
- streamlit/static/static/js/{iframeResizer.contentWindow.s2B09mw2.js → iframeResizer.contentWindow.ClkMmI3q.js} +1 -1
- streamlit/static/static/js/{index.Cwtbhfaf.js → index.1tDlzcmX.js} +1 -1
- streamlit/static/static/js/index.3d64wPzr.js +1 -0
- streamlit/static/static/js/{index.DQi04JYE.js → index.B-oXGsp7.js} +1 -1
- streamlit/static/static/js/{index.0pkoyBcb.js → index.B2EfQ-qM.js} +1 -1
- streamlit/static/static/js/{index.DkGCnTMe.js → index.B5wTpdet.js} +1 -1
- streamlit/static/static/js/{index.DG8IxVeM.js → index.B6y8ns6u.js} +2 -2
- streamlit/static/static/js/{index.B9FiDQ3U.js → index.BArkjssT.js} +1 -1
- streamlit/static/static/js/{index.CpDFZQ4Y.js → index.BPK3aPVr.js} +1 -1
- streamlit/static/static/js/index.BWubePhz.js +1 -0
- streamlit/static/static/js/{index.T5LKPcND.js → index.BmBUBll0.js} +1 -1
- streamlit/static/static/js/{index.BMXmhMlZ.js → index.BnmoQ0mJ.js} +1 -1
- streamlit/static/static/js/{index.BsjuUMyW.js → index.C1_voZpt.js} +1 -1
- streamlit/static/static/js/index.C6tuB1Tp.js +2 -0
- streamlit/static/static/js/{index.BoMRR3tN.js → index.CB8SwV8L.js} +1 -1
- streamlit/static/static/js/{index.CEKMSkSh.js → index.CXmZFP__.js} +36 -36
- streamlit/static/static/js/{index.DTi8Lw0k.js → index.CYAmxIg2.js} +1 -1
- streamlit/static/static/js/{index.DMrO9G6A.js → index.D-zMZsin.js} +1 -1
- streamlit/static/static/js/{index.JTeVe9GQ.js → index.D56z-_rt.js} +1 -1
- streamlit/static/static/js/index.D6AKDy4z.js +1 -0
- streamlit/static/static/js/{index.BFqpCk2P.js → index.D9lEoddJ.js} +1 -1
- streamlit/static/static/js/index.DAEzs_UL.js +1 -0
- streamlit/static/static/js/{index.BDTYk2an.js → index.DLO5_tVd.js} +1 -1
- streamlit/static/static/js/{index.BY7-qCf5.js → index.DLyB6OQM.js} +1 -1
- streamlit/static/static/js/index.DNWXtL99.js +1 -0
- streamlit/static/static/js/index.DbgE46VI.js +1 -0
- streamlit/static/static/js/{index.b9kK7Vzl.js → index.DeLPvnoy.js} +1 -1
- streamlit/static/static/js/{index.CH9XN_-G.js → index.DeeFVG0Y.js} +2 -2
- streamlit/static/static/js/{index.BVA8TXNT.js → index.Dhu_cVNQ.js} +1 -1
- streamlit/static/static/js/{index.R0peMEpV.js → index.Dj8nqcpI.js} +1 -1
- streamlit/static/static/js/{index.D9FvPPrI.js → index.DpWg6flp.js} +1 -1
- streamlit/static/static/js/{index.CeiCniCg.js → index.IqYlA3jn.js} +1 -1
- streamlit/static/static/js/{index.DSDkXosb.js → index.JA6OF_5t.js} +2 -2
- streamlit/static/static/js/{index.C2qCX1Lh.js → index.PoIwzrI7.js} +151 -151
- streamlit/static/static/js/{index.CqaahZPf.js → index.SW3uv22k.js} +1 -1
- streamlit/static/static/js/{index.DWaB7Scf.js → index.WlA_Ju7i.js} +1 -1
- streamlit/static/static/js/{index.CP2PmB93.js → index.blwBIOHe.js} +1 -1
- streamlit/static/static/js/{index.CyE1OdOj.js → index.wiodyGjO.js} +1 -1
- streamlit/static/static/js/{input.DMOGBelK.js → input.B3uNJm6d.js} +1 -1
- streamlit/static/static/js/{memory.DmCktBGW.js → memory.BWtwV556.js} +1 -1
- streamlit/static/static/js/{mergeWith.DeWTsJ5h.js → mergeWith.BeTmYGS_.js} +1 -1
- streamlit/static/static/js/{number-overlay-editor.DDFelcUP.js → number-overlay-editor.MW9-6kFx.js} +1 -1
- streamlit/static/static/js/{possibleConstructorReturn.CYJtAqB-.js → possibleConstructorReturn.ZgHiGHSO.js} +1 -1
- streamlit/static/static/js/{sandbox.CfaU9Ih9.js → sandbox.BZyTt4zT.js} +1 -1
- streamlit/static/static/js/{textarea.7hWYKDw2.js → textarea.CgvEg9Xi.js} +1 -1
- streamlit/static/static/js/{timepicker.DZsgZ9oE.js → timepicker.CHPC9KOb.js} +1 -1
- streamlit/static/static/js/{toConsumableArray.CqWB4Jry.js → toConsumableArray.6GvveewD.js} +1 -1
- streamlit/static/static/js/{uniqueId.DQ533D9O.js → uniqueId.BV5h1uCx.js} +1 -1
- streamlit/static/static/js/{useBasicWidgetState.-99xbU_o.js → useBasicWidgetState.DzE2MsN8.js} +1 -1
- streamlit/static/static/js/{useOnInputChange.B4G2Q7Bu.js → useOnInputChange.sGAnyCSU.js} +1 -1
- streamlit/static/static/js/{withFullScreenWrapper.BJQZ2aNc.js → withFullScreenWrapper.Bh08pTH8.js} +1 -1
- streamlit/string_util.py +6 -7
- streamlit/temporary_directory.py +12 -3
- streamlit/testing/v1/app_test.py +11 -6
- streamlit/testing/v1/element_tree.py +134 -158
- streamlit/testing/v1/local_script_runner.py +5 -5
- streamlit/testing/v1/util.py +11 -4
- streamlit/type_util.py +3 -4
- streamlit/user_info.py +3 -2
- streamlit/util.py +1 -1
- streamlit/vendor/pympler/asizeof.py +1 -1
- streamlit/watcher/event_based_path_watcher.py +1 -1
- streamlit/watcher/folder_black_list.py +1 -1
- streamlit/watcher/local_sources_watcher.py +5 -5
- streamlit/watcher/path_watcher.py +1 -1
- streamlit/web/cli.py +12 -11
- streamlit/web/server/browser_websocket_handler.py +1 -1
- streamlit/web/server/component_request_handler.py +1 -1
- streamlit/web/server/media_file_handler.py +2 -1
- streamlit/web/server/oauth_authlib_routes.py +2 -2
- streamlit/web/server/oidc_mixin.py +13 -6
- streamlit/web/server/routes.py +3 -3
- streamlit/web/server/server.py +1 -1
- streamlit/web/server/server_util.py +7 -6
- streamlit/web/server/upload_file_request_handler.py +5 -5
- {streamlit_nightly-1.45.1.dev20250508.dist-info → streamlit_nightly-1.45.1.dev20250510.dist-info}/METADATA +1 -1
- {streamlit_nightly-1.45.1.dev20250508.dist-info → streamlit_nightly-1.45.1.dev20250510.dist-info}/RECORD +201 -197
- {streamlit_nightly-1.45.1.dev20250508.dist-info → streamlit_nightly-1.45.1.dev20250510.dist-info}/WHEEL +1 -1
- streamlit/static/static/js/index.CCD4LJ9Q.js +0 -1
- streamlit/static/static/js/index.CbLZDRQu.js +0 -1
- streamlit/static/static/js/index.D9qEk5xd.js +0 -1
- streamlit/static/static/js/index.DTXSsTgK.js +0 -1
- streamlit/static/static/js/index.DqVjOqxm.js +0 -1
- streamlit/static/static/js/index.RHbaxsqm.js +0 -1
- streamlit/static/static/js/index.hT9gkW3a.js +0 -2
- {streamlit_nightly-1.45.1.dev20250508.data → streamlit_nightly-1.45.1.dev20250510.data}/scripts/streamlit.cmd +0 -0
- {streamlit_nightly-1.45.1.dev20250508.dist-info → streamlit_nightly-1.45.1.dev20250510.dist-info}/entry_points.txt +0 -0
- {streamlit_nightly-1.45.1.dev20250508.dist-info → streamlit_nightly-1.45.1.dev20250510.dist-info}/top_level.txt +0 -0
@@ -53,7 +53,7 @@ class UnhashableParamError(StreamlitAPIException):
|
|
53
53
|
arg_name: str | None,
|
54
54
|
arg_value: Any,
|
55
55
|
orig_exc: BaseException,
|
56
|
-
):
|
56
|
+
) -> None:
|
57
57
|
msg = self._create_message(cache_type, func, arg_name, arg_value)
|
58
58
|
super().__init__(msg)
|
59
59
|
self.with_traceback(orig_exc.__traceback__)
|
@@ -99,7 +99,7 @@ class CacheReplayClosureError(StreamlitAPIException):
|
|
99
99
|
self,
|
100
100
|
cache_type: CacheType,
|
101
101
|
cached_func: FunctionType,
|
102
|
-
):
|
102
|
+
) -> None:
|
103
103
|
func_name = get_cached_func_name_md(cached_func)
|
104
104
|
decorator_name = get_decorator_api_name(cache_type)
|
105
105
|
|
@@ -121,7 +121,7 @@ How to fix this:
|
|
121
121
|
|
122
122
|
|
123
123
|
class UnserializableReturnValueError(MarkdownFormattedException):
|
124
|
-
def __init__(self, func: FunctionType, return_value: FunctionType):
|
124
|
+
def __init__(self, func: FunctionType, return_value: FunctionType) -> None:
|
125
125
|
MarkdownFormattedException.__init__(
|
126
126
|
self,
|
127
127
|
f"""
|
@@ -151,7 +151,7 @@ class CachedResourceFuncInfo(CachedFuncInfo):
|
|
151
151
|
ttl: float | timedelta | str | None,
|
152
152
|
validate: ValidateFunc | None,
|
153
153
|
hash_funcs: HashFuncsDict | None = None,
|
154
|
-
):
|
154
|
+
) -> None:
|
155
155
|
super().__init__(
|
156
156
|
func,
|
157
157
|
show_spinner=show_spinner,
|
@@ -189,7 +189,7 @@ class CacheResourceAPI:
|
|
189
189
|
and st.cache_resource.clear().
|
190
190
|
"""
|
191
191
|
|
192
|
-
def __init__(self, decorator_metric_name: str):
|
192
|
+
def __init__(self, decorator_metric_name: str) -> None:
|
193
193
|
"""Create a CacheResourceAPI instance.
|
194
194
|
|
195
195
|
Parameters
|
@@ -234,7 +234,7 @@ class CacheResourceAPI:
|
|
234
234
|
validate: ValidateFunc | None = None,
|
235
235
|
experimental_allow_widgets: bool = False,
|
236
236
|
hash_funcs: HashFuncsDict | None = None,
|
237
|
-
):
|
237
|
+
) -> F | Callable[[F], F]:
|
238
238
|
return self._decorator(
|
239
239
|
func,
|
240
240
|
ttl=ttl,
|
@@ -255,7 +255,7 @@ class CacheResourceAPI:
|
|
255
255
|
validate: ValidateFunc | None,
|
256
256
|
experimental_allow_widgets: bool,
|
257
257
|
hash_funcs: HashFuncsDict | None = None,
|
258
|
-
):
|
258
|
+
) -> F | Callable[[F], F]:
|
259
259
|
"""Decorator to cache functions that return global resources (e.g. database connections, ML models).
|
260
260
|
|
261
261
|
Cached objects are shared across all users, sessions, and reruns. They
|
@@ -417,9 +417,9 @@ class CacheResourceAPI:
|
|
417
417
|
# Support passing the params via function decorator, e.g.
|
418
418
|
# @st.cache_resource(show_spinner=False)
|
419
419
|
if func is None:
|
420
|
-
return lambda f: make_cached_func_wrapper(
|
420
|
+
return lambda f: make_cached_func_wrapper( # type: ignore
|
421
421
|
CachedResourceFuncInfo(
|
422
|
-
func=f,
|
422
|
+
func=f, # type: ignore
|
423
423
|
show_spinner=show_spinner,
|
424
424
|
max_entries=max_entries,
|
425
425
|
ttl=ttl,
|
@@ -455,7 +455,7 @@ class ResourceCache(Cache):
|
|
455
455
|
ttl_seconds: float,
|
456
456
|
validate: ValidateFunc | None,
|
457
457
|
display_name: str,
|
458
|
-
):
|
458
|
+
) -> None:
|
459
459
|
super().__init__()
|
460
460
|
self.key = key
|
461
461
|
self.display_name = display_name
|
@@ -100,7 +100,7 @@ class Cache:
|
|
100
100
|
with self._value_locks_lock:
|
101
101
|
return self._value_locks[value_key]
|
102
102
|
|
103
|
-
def clear(self, key: str | None = None):
|
103
|
+
def clear(self, key: str | None = None) -> None:
|
104
104
|
"""Clear values from this cache.
|
105
105
|
If no argument is passed, all items are cleared from the cache.
|
106
106
|
A key can be passed to clear that key from the cache only.
|
@@ -130,7 +130,7 @@ class CachedFuncInfo:
|
|
130
130
|
func: FunctionType,
|
131
131
|
show_spinner: bool | str,
|
132
132
|
hash_funcs: HashFuncsDict | None,
|
133
|
-
):
|
133
|
+
) -> None:
|
134
134
|
self.func = func
|
135
135
|
self.show_spinner = show_spinner
|
136
136
|
self.hash_funcs = hash_funcs
|
@@ -167,17 +167,17 @@ class BoundCachedFunc:
|
|
167
167
|
decorated function is a class method.
|
168
168
|
"""
|
169
169
|
|
170
|
-
def __init__(self, cached_func: CachedFunc, instance: Any):
|
170
|
+
def __init__(self, cached_func: CachedFunc, instance: Any) -> None:
|
171
171
|
self._cached_func = cached_func
|
172
172
|
self._instance = instance
|
173
173
|
|
174
|
-
def __call__(self, *args, **kwargs) -> Any:
|
174
|
+
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
175
175
|
return self._cached_func(self._instance, *args, **kwargs)
|
176
176
|
|
177
|
-
def __repr__(self):
|
177
|
+
def __repr__(self) -> str:
|
178
178
|
return f"<BoundCachedFunc: {self._cached_func._info.func} of {self._instance}>"
|
179
179
|
|
180
|
-
def clear(self, *args, **kwargs):
|
180
|
+
def clear(self, *args: Any, **kwargs: Any) -> None:
|
181
181
|
if args or kwargs:
|
182
182
|
# The instance is required as first parameter to allow
|
183
183
|
# args to be correctly resolved to the parameter names:
|
@@ -189,11 +189,11 @@ class BoundCachedFunc:
|
|
189
189
|
|
190
190
|
|
191
191
|
class CachedFunc:
|
192
|
-
def __init__(self, info: CachedFuncInfo):
|
192
|
+
def __init__(self, info: CachedFuncInfo) -> None:
|
193
193
|
self._info = info
|
194
194
|
self._function_key = _make_function_key(info.cache_type, info.func)
|
195
195
|
|
196
|
-
def __repr__(self):
|
196
|
+
def __repr__(self) -> str:
|
197
197
|
return f"<CachedFunc: {self._info.func}>"
|
198
198
|
|
199
199
|
def __get__(self, instance, owner=None):
|
@@ -203,7 +203,7 @@ class CachedFunc:
|
|
203
203
|
|
204
204
|
return functools.update_wrapper(BoundCachedFunc(self, instance), self)
|
205
205
|
|
206
|
-
def __call__(self, *args, **kwargs) -> Any:
|
206
|
+
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
207
207
|
"""The wrapper. We'll only call our underlying function on a cache miss."""
|
208
208
|
|
209
209
|
spinner_message: str | None = None
|
@@ -121,7 +121,7 @@ class CachedMessageReplayContext(threading.local):
|
|
121
121
|
of this class across multiple threads.
|
122
122
|
"""
|
123
123
|
|
124
|
-
def __init__(self, cache_type: CacheType):
|
124
|
+
def __init__(self, cache_type: CacheType) -> None:
|
125
125
|
self._cached_message_stack: list[list[MsgData]] = []
|
126
126
|
self._seen_dg_stack: list[set[str]] = []
|
127
127
|
self._most_recent_messages: list[MsgData] = []
|
@@ -34,7 +34,7 @@ import weakref
|
|
34
34
|
from enum import Enum
|
35
35
|
from re import Pattern
|
36
36
|
from types import MappingProxyType
|
37
|
-
from typing import Any, Callable, Final, Union, cast
|
37
|
+
from typing import TYPE_CHECKING, Any, Callable, Final, Union, cast
|
38
38
|
|
39
39
|
from typing_extensions import TypeAlias
|
40
40
|
|
@@ -44,6 +44,10 @@ from streamlit.runtime.caching.cache_errors import UnhashableTypeError
|
|
44
44
|
from streamlit.runtime.caching.cache_type import CacheType
|
45
45
|
from streamlit.runtime.uploaded_file_manager import UploadedFile
|
46
46
|
|
47
|
+
if TYPE_CHECKING:
|
48
|
+
import numpy.typing as npt
|
49
|
+
from PIL.Image import Image
|
50
|
+
|
47
51
|
_LOGGER: Final = logger.get_logger(__name__)
|
48
52
|
|
49
53
|
# If a dataframe has more than this many rows, we consider it large and hash a sample.
|
@@ -66,11 +70,11 @@ _CYCLE_PLACEHOLDER: Final = (
|
|
66
70
|
class UserHashError(StreamlitAPIException):
|
67
71
|
def __init__(
|
68
72
|
self,
|
69
|
-
orig_exc,
|
70
|
-
object_to_hash,
|
71
|
-
hash_func,
|
73
|
+
orig_exc: BaseException,
|
74
|
+
object_to_hash: Any,
|
75
|
+
hash_func: Callable[[Any], Any],
|
72
76
|
cache_type: CacheType | None = None,
|
73
|
-
):
|
77
|
+
) -> None:
|
74
78
|
self.alternate_name = type(orig_exc).__name__
|
75
79
|
self.hash_func = hash_func
|
76
80
|
self.cache_type = cache_type
|
@@ -80,7 +84,11 @@ class UserHashError(StreamlitAPIException):
|
|
80
84
|
super().__init__(msg)
|
81
85
|
self.with_traceback(orig_exc.__traceback__)
|
82
86
|
|
83
|
-
def _get_message_from_func(
|
87
|
+
def _get_message_from_func(
|
88
|
+
self,
|
89
|
+
orig_exc: BaseException,
|
90
|
+
cached_func: Any,
|
91
|
+
) -> str:
|
84
92
|
args = self._get_error_message_args(orig_exc, cached_func)
|
85
93
|
|
86
94
|
return (
|
@@ -145,7 +153,7 @@ If you think this is actually a Streamlit bug, please
|
|
145
153
|
|
146
154
|
def update_hash(
|
147
155
|
val: Any,
|
148
|
-
hasher,
|
156
|
+
hasher: Any,
|
149
157
|
cache_type: CacheType,
|
150
158
|
hash_source: Callable[..., Any] | None = None,
|
151
159
|
hash_funcs: HashFuncsDict | None = None,
|
@@ -182,13 +190,13 @@ class _HashStack:
|
|
182
190
|
def __repr__(self) -> str:
|
183
191
|
return util.repr_(self)
|
184
192
|
|
185
|
-
def push(self, val: Any):
|
193
|
+
def push(self, val: Any) -> None:
|
186
194
|
self._stack[id(val)] = val
|
187
195
|
|
188
|
-
def pop(self):
|
196
|
+
def pop(self) -> None:
|
189
197
|
self._stack.popitem()
|
190
198
|
|
191
|
-
def __contains__(self, val: Any):
|
199
|
+
def __contains__(self, val: Any) -> bool:
|
192
200
|
return id(val) in self._stack
|
193
201
|
|
194
202
|
def pretty_print(self) -> str:
|
@@ -247,28 +255,20 @@ def _key(obj: Any | None) -> Any:
|
|
247
255
|
if obj is None:
|
248
256
|
return None
|
249
257
|
|
250
|
-
def is_simple(obj):
|
258
|
+
def is_simple(obj: Any) -> bool:
|
251
259
|
return (
|
252
|
-
isinstance(obj, bytes)
|
253
|
-
or isinstance(obj, bytearray)
|
254
|
-
or isinstance(obj, str)
|
255
|
-
or isinstance(obj, float)
|
256
|
-
or isinstance(obj, int)
|
257
|
-
or isinstance(obj, bool)
|
258
|
-
or isinstance(obj, uuid.UUID)
|
260
|
+
isinstance(obj, (bytes, bytearray, str, float, int, bool, uuid.UUID))
|
259
261
|
or obj is None
|
260
262
|
)
|
261
263
|
|
262
264
|
if is_simple(obj):
|
263
265
|
return obj
|
264
266
|
|
265
|
-
if isinstance(obj, tuple):
|
266
|
-
|
267
|
-
return obj
|
267
|
+
if isinstance(obj, tuple) and all(map(is_simple, obj)):
|
268
|
+
return obj
|
268
269
|
|
269
|
-
if isinstance(obj, list):
|
270
|
-
|
271
|
-
return ("__l", tuple(obj))
|
270
|
+
if isinstance(obj, list) and all(map(is_simple, obj)):
|
271
|
+
return ("__l", tuple(obj))
|
272
272
|
|
273
273
|
if inspect.isbuiltin(obj) or inspect.isroutine(obj) or inspect.iscode(obj):
|
274
274
|
return id(obj)
|
@@ -279,7 +279,9 @@ def _key(obj: Any | None) -> Any:
|
|
279
279
|
class _CacheFuncHasher:
|
280
280
|
"""A hasher that can hash objects with cycles."""
|
281
281
|
|
282
|
-
def __init__(
|
282
|
+
def __init__(
|
283
|
+
self, cache_type: CacheType, hash_funcs: HashFuncsDict | None = None
|
284
|
+
) -> None:
|
283
285
|
# Can't use types as the keys in the internal _hash_funcs because
|
284
286
|
# we always remove user-written modules from memory when rerunning a
|
285
287
|
# script in order to reload it and grab the latest code changes.
|
@@ -339,7 +341,7 @@ class _CacheFuncHasher:
|
|
339
341
|
|
340
342
|
return b
|
341
343
|
|
342
|
-
def update(self, hasher, obj: Any) -> None:
|
344
|
+
def update(self, hasher: Any, obj: Any) -> None:
|
343
345
|
"""Update the provided hasher with the hash of an object."""
|
344
346
|
b = self.to_bytes(obj)
|
345
347
|
hasher.update(b)
|
@@ -360,10 +362,10 @@ class _CacheFuncHasher:
|
|
360
362
|
# deep, so we don't try to hash them at all.
|
361
363
|
return self.to_bytes(id(obj))
|
362
364
|
|
363
|
-
|
365
|
+
if isinstance(obj, bytes) or isinstance(obj, bytearray):
|
364
366
|
return obj
|
365
367
|
|
366
|
-
|
368
|
+
if type_util.get_fqn_type(obj) in self._hash_funcs:
|
367
369
|
# Escape hatch for unsupported objects
|
368
370
|
hash_func = self._hash_funcs[type_util.get_fqn_type(obj)]
|
369
371
|
try:
|
@@ -374,57 +376,59 @@ class _CacheFuncHasher:
|
|
374
376
|
) from ex
|
375
377
|
return self.to_bytes(output)
|
376
378
|
|
377
|
-
|
379
|
+
if isinstance(obj, str):
|
378
380
|
return obj.encode()
|
379
381
|
|
380
|
-
|
382
|
+
if isinstance(obj, float):
|
381
383
|
return _float_to_bytes(obj)
|
382
384
|
|
383
|
-
|
385
|
+
if isinstance(obj, int):
|
384
386
|
return _int_to_bytes(obj)
|
385
387
|
|
386
|
-
|
388
|
+
if isinstance(obj, uuid.UUID):
|
387
389
|
return obj.bytes
|
388
390
|
|
389
|
-
|
391
|
+
if isinstance(obj, datetime.datetime):
|
390
392
|
return obj.isoformat().encode()
|
391
393
|
|
392
|
-
|
394
|
+
if isinstance(obj, (list, tuple)):
|
393
395
|
for item in obj:
|
394
396
|
self.update(h, item)
|
395
397
|
return h.digest()
|
396
398
|
|
397
|
-
|
399
|
+
if isinstance(obj, dict):
|
398
400
|
for item in obj.items():
|
399
401
|
self.update(h, item)
|
400
402
|
return h.digest()
|
401
403
|
|
402
|
-
|
404
|
+
if obj is None:
|
403
405
|
return b"0"
|
404
406
|
|
405
|
-
|
407
|
+
if obj is True:
|
406
408
|
return b"1"
|
407
409
|
|
408
|
-
|
410
|
+
if obj is False:
|
409
411
|
return b"0"
|
410
412
|
|
411
|
-
|
413
|
+
if not isinstance(obj, type) and dataclasses.is_dataclass(obj):
|
412
414
|
return self.to_bytes(dataclasses.asdict(obj))
|
413
|
-
|
415
|
+
if isinstance(obj, Enum):
|
414
416
|
return str(obj).encode()
|
415
417
|
|
416
|
-
|
418
|
+
if type_util.is_type(obj, "pandas.core.series.Series"):
|
417
419
|
import pandas as pd
|
418
420
|
|
419
|
-
|
420
|
-
self.update(h,
|
421
|
-
self.update(h,
|
421
|
+
series_obj: pd.Series = cast("pd.Series", obj)
|
422
|
+
self.update(h, series_obj.size)
|
423
|
+
self.update(h, series_obj.dtype.name)
|
422
424
|
|
423
|
-
if len(
|
424
|
-
|
425
|
+
if len(series_obj) >= _PANDAS_ROWS_LARGE:
|
426
|
+
series_obj = series_obj.sample(n=_PANDAS_SAMPLE_SIZE, random_state=0)
|
425
427
|
|
426
428
|
try:
|
427
|
-
self.update(
|
429
|
+
self.update(
|
430
|
+
h, pd.util.hash_pandas_object(series_obj).to_numpy().tobytes()
|
431
|
+
)
|
428
432
|
return h.digest()
|
429
433
|
except TypeError:
|
430
434
|
_LOGGER.warning(
|
@@ -434,22 +438,22 @@ class _CacheFuncHasher:
|
|
434
438
|
|
435
439
|
# Use pickle if pandas cannot hash the object for example if
|
436
440
|
# it contains unhashable objects.
|
437
|
-
return b"%s" % pickle.dumps(
|
441
|
+
return b"%s" % pickle.dumps(series_obj, pickle.HIGHEST_PROTOCOL)
|
438
442
|
|
439
443
|
elif type_util.is_type(obj, "pandas.core.frame.DataFrame"):
|
440
444
|
import pandas as pd
|
441
445
|
|
442
|
-
|
443
|
-
self.update(h,
|
446
|
+
df_obj: pd.DataFrame = cast("pd.DataFrame", obj)
|
447
|
+
self.update(h, df_obj.shape)
|
444
448
|
|
445
|
-
if len(
|
446
|
-
|
449
|
+
if len(df_obj) >= _PANDAS_ROWS_LARGE:
|
450
|
+
df_obj = df_obj.sample(n=_PANDAS_SAMPLE_SIZE, random_state=0)
|
447
451
|
try:
|
448
452
|
column_hash_bytes = self.to_bytes(
|
449
|
-
pd.util.hash_pandas_object(
|
453
|
+
pd.util.hash_pandas_object(df_obj.dtypes)
|
450
454
|
)
|
451
455
|
self.update(h, column_hash_bytes)
|
452
|
-
values_hash_bytes = self.to_bytes(pd.util.hash_pandas_object(
|
456
|
+
values_hash_bytes = self.to_bytes(pd.util.hash_pandas_object(df_obj))
|
453
457
|
self.update(h, values_hash_bytes)
|
454
458
|
return h.digest()
|
455
459
|
except TypeError:
|
@@ -460,7 +464,7 @@ class _CacheFuncHasher:
|
|
460
464
|
|
461
465
|
# Use pickle if pandas cannot hash the object for example if
|
462
466
|
# it contains unhashable objects.
|
463
|
-
return b"%s" % pickle.dumps(
|
467
|
+
return b"%s" % pickle.dumps(df_obj, pickle.HIGHEST_PROTOCOL)
|
464
468
|
|
465
469
|
elif type_util.is_type(obj, "polars.series.series.Series"):
|
466
470
|
import polars as pl # type: ignore[import-not-found]
|
@@ -513,39 +517,32 @@ class _CacheFuncHasher:
|
|
513
517
|
# it contains unhashable objects.
|
514
518
|
return b"%s" % pickle.dumps(obj, pickle.HIGHEST_PROTOCOL)
|
515
519
|
elif type_util.is_type(obj, "numpy.ndarray"):
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
# - can be removed once we sunset support for Python 3.8
|
520
|
-
obj = cast("np.ndarray[Any, Any]", obj)
|
521
|
-
self.update(h, obj.shape)
|
522
|
-
self.update(h, str(obj.dtype))
|
520
|
+
np_obj: npt.NDArray[Any] = cast("npt.NDArray[Any]", obj)
|
521
|
+
self.update(h, np_obj.shape)
|
522
|
+
self.update(h, str(np_obj.dtype))
|
523
523
|
|
524
|
-
if
|
524
|
+
if np_obj.size >= _NP_SIZE_LARGE:
|
525
525
|
import numpy as np
|
526
526
|
|
527
527
|
state = np.random.RandomState(0)
|
528
|
-
|
528
|
+
np_obj = state.choice(np_obj.flat, size=_NP_SAMPLE_SIZE)
|
529
529
|
|
530
|
-
self.update(h,
|
530
|
+
self.update(h, np_obj.tobytes())
|
531
531
|
return h.digest()
|
532
532
|
elif type_util.is_type(obj, "PIL.Image.Image"):
|
533
533
|
import numpy as np
|
534
|
-
from PIL.Image import Image # noqa: TC002
|
535
534
|
|
536
|
-
|
535
|
+
pil_obj: Image = cast("Image", obj)
|
537
536
|
|
538
537
|
# we don't just hash the results of obj.tobytes() because we want to use
|
539
538
|
# the sampling logic for numpy data
|
540
|
-
np_array = np.frombuffer(
|
539
|
+
np_array = np.frombuffer(pil_obj.tobytes(), dtype="uint8")
|
541
540
|
return self.to_bytes(np_array)
|
542
541
|
|
543
542
|
elif inspect.isbuiltin(obj):
|
544
543
|
return bytes(obj.__name__.encode())
|
545
544
|
|
546
|
-
elif isinstance(obj, MappingProxyType)
|
547
|
-
obj, collections.abc.ItemsView
|
548
|
-
):
|
545
|
+
elif isinstance(obj, (MappingProxyType, collections.abc.ItemsView)):
|
549
546
|
return self.to_bytes(dict(obj))
|
550
547
|
|
551
548
|
elif type_util.is_type(obj, "builtins.getset_descriptor"):
|
@@ -561,9 +558,8 @@ class _CacheFuncHasher:
|
|
561
558
|
return h.digest()
|
562
559
|
|
563
560
|
elif hasattr(obj, "name") and (
|
564
|
-
isinstance(obj, io.IOBase)
|
565
561
|
# Handle temporary files used during testing
|
566
|
-
|
562
|
+
isinstance(obj, (io.IOBase, tempfile._TemporaryFileWrapper))
|
567
563
|
):
|
568
564
|
# Hash files as name + last modification date + offset.
|
569
565
|
# NB: we're using hasattr("name") to differentiate between
|
@@ -579,7 +575,7 @@ class _CacheFuncHasher:
|
|
579
575
|
elif isinstance(obj, Pattern):
|
580
576
|
return self.to_bytes([obj.pattern, obj.flags])
|
581
577
|
|
582
|
-
elif isinstance(obj, io.StringIO
|
578
|
+
elif isinstance(obj, (io.StringIO, io.BytesIO)):
|
583
579
|
# Hash in-memory StringIO/BytesIO by their full contents
|
584
580
|
# and seek position.
|
585
581
|
self.update(h, obj.tell())
|
@@ -40,7 +40,7 @@ def cache(
|
|
40
40
|
hash_funcs: HashFuncsDict | None = None,
|
41
41
|
max_entries: int | None = None,
|
42
42
|
ttl: float | None = None,
|
43
|
-
):
|
43
|
+
) -> F:
|
44
44
|
"""Legacy caching decorator (deprecated).
|
45
45
|
|
46
46
|
Legacy caching with ``st.cache`` has been removed from Streamlit. This is
|
@@ -57,7 +57,9 @@ class InMemoryCacheStorageWrapper(CacheStorage):
|
|
57
57
|
it from multiple threads.
|
58
58
|
"""
|
59
59
|
|
60
|
-
def __init__(
|
60
|
+
def __init__(
|
61
|
+
self, persist_storage: CacheStorage, context: CacheStorageContext
|
62
|
+
) -> None:
|
61
63
|
self.function_key = context.function_key
|
62
64
|
self.function_display_name = context.function_display_name
|
63
65
|
self._ttl_seconds = context.ttl_seconds
|
@@ -120,7 +120,7 @@ class LocalDiskCacheStorage(CacheStorage):
|
|
120
120
|
This is the default cache persistence layer for `@st.cache_data`.
|
121
121
|
"""
|
122
122
|
|
123
|
-
def __init__(self, context: CacheStorageContext):
|
123
|
+
def __init__(self, context: CacheStorageContext) -> None:
|
124
124
|
self.function_key = context.function_key
|
125
125
|
self.persist = context.persist
|
126
126
|
self._ttl_seconds = context.ttl_seconds
|
@@ -38,7 +38,7 @@ if TYPE_CHECKING:
|
|
38
38
|
# 2. Writing two new @overloads for connection_factory (one for the case where the
|
39
39
|
# only the connection name is specified and another when both name and type are).
|
40
40
|
# 3. Updating test_get_first_party_connection_helper in connection_factory_test.py.
|
41
|
-
FIRST_PARTY_CONNECTIONS = {
|
41
|
+
FIRST_PARTY_CONNECTIONS: Final[dict[str, type[BaseConnection[Any]]]] = {
|
42
42
|
"snowflake": SnowflakeConnection,
|
43
43
|
"snowpark": SnowparkConnection,
|
44
44
|
"sql": SQLConnection,
|
@@ -65,7 +65,7 @@ def _create_connection(
|
|
65
65
|
connection_class: type[ConnectionClass],
|
66
66
|
max_entries: int | None = None,
|
67
67
|
ttl: float | timedelta | None = None,
|
68
|
-
**kwargs,
|
68
|
+
**kwargs: Any,
|
69
69
|
) -> ConnectionClass:
|
70
70
|
"""Create an instance of connection_class with the given name and kwargs.
|
71
71
|
|
@@ -76,7 +76,7 @@ def _create_connection(
|
|
76
76
|
"""
|
77
77
|
|
78
78
|
def __create_connection(
|
79
|
-
name: str, connection_class: type[ConnectionClass], **kwargs
|
79
|
+
name: str, connection_class: type[ConnectionClass], **kwargs: Any
|
80
80
|
) -> ConnectionClass:
|
81
81
|
return connection_class(connection_name=name, **kwargs)
|
82
82
|
|
@@ -103,7 +103,7 @@ def _create_connection(
|
|
103
103
|
return __create_connection(name, connection_class, **kwargs)
|
104
104
|
|
105
105
|
|
106
|
-
def _get_first_party_connection(connection_class: str):
|
106
|
+
def _get_first_party_connection(connection_class: str) -> type[BaseConnection[Any]]:
|
107
107
|
if connection_class in FIRST_PARTY_CONNECTIONS:
|
108
108
|
return FIRST_PARTY_CONNECTIONS[connection_class]
|
109
109
|
|
@@ -119,7 +119,7 @@ def connection_factory(
|
|
119
119
|
max_entries: int | None = None,
|
120
120
|
ttl: float | timedelta | None = None,
|
121
121
|
autocommit: bool = False,
|
122
|
-
**kwargs,
|
122
|
+
**kwargs: Any,
|
123
123
|
) -> SQLConnection:
|
124
124
|
pass
|
125
125
|
|
@@ -131,7 +131,7 @@ def connection_factory(
|
|
131
131
|
max_entries: int | None = None,
|
132
132
|
ttl: float | timedelta | None = None,
|
133
133
|
autocommit: bool = False,
|
134
|
-
**kwargs,
|
134
|
+
**kwargs: Any,
|
135
135
|
) -> SQLConnection:
|
136
136
|
pass
|
137
137
|
|
@@ -142,7 +142,7 @@ def connection_factory(
|
|
142
142
|
max_entries: int | None = None,
|
143
143
|
ttl: float | timedelta | None = None,
|
144
144
|
autocommit: bool = False,
|
145
|
-
**kwargs,
|
145
|
+
**kwargs: Any,
|
146
146
|
) -> SnowflakeConnection:
|
147
147
|
pass
|
148
148
|
|
@@ -154,7 +154,7 @@ def connection_factory(
|
|
154
154
|
max_entries: int | None = None,
|
155
155
|
ttl: float | timedelta | None = None,
|
156
156
|
autocommit: bool = False,
|
157
|
-
**kwargs,
|
157
|
+
**kwargs: Any,
|
158
158
|
) -> SnowflakeConnection:
|
159
159
|
pass
|
160
160
|
|
@@ -164,7 +164,7 @@ def connection_factory(
|
|
164
164
|
name: Literal["snowpark"],
|
165
165
|
max_entries: int | None = None,
|
166
166
|
ttl: float | timedelta | None = None,
|
167
|
-
**kwargs,
|
167
|
+
**kwargs: Any,
|
168
168
|
) -> SnowparkConnection:
|
169
169
|
pass
|
170
170
|
|
@@ -175,7 +175,7 @@ def connection_factory(
|
|
175
175
|
type: Literal["snowpark"],
|
176
176
|
max_entries: int | None = None,
|
177
177
|
ttl: float | timedelta | None = None,
|
178
|
-
**kwargs,
|
178
|
+
**kwargs: Any,
|
179
179
|
) -> SnowparkConnection:
|
180
180
|
pass
|
181
181
|
|
@@ -186,7 +186,7 @@ def connection_factory(
|
|
186
186
|
type: type[ConnectionClass],
|
187
187
|
max_entries: int | None = None,
|
188
188
|
ttl: float | timedelta | None = None,
|
189
|
-
**kwargs,
|
189
|
+
**kwargs: Any,
|
190
190
|
) -> ConnectionClass:
|
191
191
|
pass
|
192
192
|
|
@@ -197,7 +197,7 @@ def connection_factory(
|
|
197
197
|
type: str | None = None,
|
198
198
|
max_entries: int | None = None,
|
199
199
|
ttl: float | timedelta | None = None,
|
200
|
-
**kwargs,
|
200
|
+
**kwargs: Any,
|
201
201
|
) -> BaseConnection[Any]:
|
202
202
|
pass
|
203
203
|
|
@@ -233,7 +233,7 @@ def connection_factory(
|
|
233
233
|
The type of connection to create. This can be one of the following:
|
234
234
|
|
235
235
|
- ``None`` (default): Streamlit will infer the connection type from
|
236
|
-
``name``. If the type is not
|
236
|
+
``name``. If the type is not inferable from ``name``, the type must
|
237
237
|
be specified in ``secrets.toml`` instead.
|
238
238
|
- ``"snowflake"``: Streamlit will initialize a connection with
|
239
239
|
|SnowflakeConnection|_.
|
@@ -267,7 +267,7 @@ def connection_factory(
|
|
267
267
|
**kwargs : any
|
268
268
|
Connection-specific keyword arguments that are passed to the
|
269
269
|
connection's ``._connect()`` method. ``**kwargs`` are typically
|
270
|
-
combined with (and take
|
270
|
+
combined with (and take precedence over) key-value pairs in
|
271
271
|
``secrets.toml``. To learn more, see the specific connection's
|
272
272
|
documentation.
|
273
273
|
|
@@ -370,7 +370,7 @@ def connection_factory(
|
|
370
370
|
|
371
371
|
if name.startswith(USE_ENV_PREFIX):
|
372
372
|
# It'd be nice to use str.removeprefix() here, but we won't be able to do that
|
373
|
-
# until the
|
373
|
+
# until the minimum Python version we support is 3.9.
|
374
374
|
envvar_name = name[len(USE_ENV_PREFIX) :]
|
375
375
|
name = os.environ[envvar_name]
|
376
376
|
|
streamlit/runtime/context.py
CHANGED
@@ -64,7 +64,7 @@ def _normalize_header(name: str) -> str:
|
|
64
64
|
|
65
65
|
|
66
66
|
class StreamlitHeaders(Mapping[str, str]):
|
67
|
-
def __init__(self, headers: Iterable[tuple[str, str]]):
|
67
|
+
def __init__(self, headers: Iterable[tuple[str, str]]) -> None:
|
68
68
|
dict_like_headers: dict[str, list[str]] = {}
|
69
69
|
|
70
70
|
for key, value in headers:
|
@@ -98,7 +98,7 @@ class StreamlitHeaders(Mapping[str, str]):
|
|
98
98
|
|
99
99
|
|
100
100
|
class StreamlitCookies(Mapping[str, str]):
|
101
|
-
def __init__(self, cookies: Mapping[str, str]):
|
101
|
+
def __init__(self, cookies: Mapping[str, str]) -> None:
|
102
102
|
self._cookies = MappingProxyType(cookies)
|
103
103
|
|
104
104
|
@classmethod
|