streamlit 1.47.1__py3-none-any.whl → 1.48.0__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/cli_util.py +1 -1
- streamlit/commands/echo.py +2 -2
- streamlit/commands/execution_control.py +1 -1
- streamlit/commands/page_config.py +16 -16
- streamlit/components/v1/component_arrow.py +4 -4
- streamlit/config.py +23 -5
- streamlit/connections/base_connection.py +2 -2
- streamlit/connections/snowflake_connection.py +2 -2
- streamlit/connections/sql_connection.py +4 -3
- streamlit/dataframe_util.py +1 -1
- streamlit/deprecation_util.py +20 -5
- streamlit/elements/arrow.py +105 -79
- streamlit/elements/deck_gl_json_chart.py +6 -6
- streamlit/elements/dialog_decorator.py +72 -17
- streamlit/elements/form.py +36 -7
- streamlit/elements/graphviz_chart.py +7 -0
- streamlit/elements/json.py +3 -3
- streamlit/elements/layouts.py +241 -75
- streamlit/elements/lib/built_in_chart_utils.py +11 -3
- streamlit/elements/lib/dialog.py +43 -0
- streamlit/elements/lib/file_uploader_utils.py +21 -2
- streamlit/elements/lib/layout_utils.py +88 -2
- streamlit/elements/lib/options_selector_utils.py +1 -1
- streamlit/elements/lib/utils.py +23 -3
- streamlit/elements/map.py +12 -11
- streamlit/elements/plotly_chart.py +21 -21
- streamlit/elements/pyplot.py +8 -4
- streamlit/elements/vega_charts.py +237 -143
- streamlit/elements/widgets/audio_input.py +2 -2
- streamlit/elements/widgets/button.py +147 -35
- streamlit/elements/widgets/button_group.py +9 -8
- streamlit/elements/widgets/camera_input.py +2 -2
- streamlit/elements/widgets/chat.py +8 -2
- streamlit/elements/widgets/checkbox.py +4 -4
- streamlit/elements/widgets/color_picker.py +2 -2
- streamlit/elements/widgets/data_editor.py +22 -15
- streamlit/elements/widgets/file_uploader.py +8 -2
- streamlit/elements/widgets/multiselect.py +27 -17
- streamlit/elements/widgets/number_input.py +2 -2
- streamlit/elements/widgets/radio.py +5 -4
- streamlit/elements/widgets/select_slider.py +8 -5
- streamlit/elements/widgets/selectbox.py +14 -4
- streamlit/elements/widgets/slider.py +59 -33
- streamlit/elements/widgets/text_widgets.py +4 -4
- streamlit/elements/widgets/time_widgets.py +4 -4
- streamlit/elements/write.py +9 -8
- streamlit/env_util.py +1 -1
- streamlit/errors.py +20 -4
- streamlit/file_util.py +1 -1
- streamlit/git_util.py +1 -1
- streamlit/logger.py +2 -2
- streamlit/proto/Block_pb2.py +39 -31
- streamlit/proto/Block_pb2.pyi +54 -3
- streamlit/proto/Button_pb2.py +4 -2
- streamlit/proto/Button_pb2.pyi +1 -0
- streamlit/runtime/caching/cache_data_api.py +15 -2
- streamlit/runtime/caching/cache_errors.py +1 -1
- streamlit/runtime/caching/cache_resource_api.py +24 -2
- streamlit/runtime/caching/cache_utils.py +6 -3
- streamlit/runtime/caching/hashing.py +6 -9
- streamlit/runtime/fragment.py +3 -2
- streamlit/runtime/runtime.py +19 -2
- streamlit/runtime/scriptrunner/script_runner.py +3 -3
- streamlit/runtime/state/common.py +2 -1
- streamlit/runtime/state/query_params.py +2 -1
- streamlit/runtime/state/session_state.py +1 -1
- streamlit/static/index.html +1 -1
- streamlit/static/manifest.json +231 -231
- streamlit/static/static/css/index.CQt5TjGB.css +1 -0
- streamlit/static/static/js/{ErrorOutline.esm.BEZPMjuG.js → ErrorOutline.esm.D_4oFNKB.js} +1 -1
- streamlit/static/static/js/{FileDownload.esm.Dy1V9a2E.js → FileDownload.esm.NPgaLlUE.js} +1 -1
- streamlit/static/static/js/{FileHelper.D0K06YBq.js → FileHelper.B2t9ikoS.js} +1 -1
- streamlit/static/static/js/{FormClearHelper.Cdw5Y7_m.js → FormClearHelper.BLEIUk6L.js} +1 -1
- streamlit/static/static/js/{Hooks.C_qx1sSw.js → Hooks.BGm9sd4U.js} +1 -1
- streamlit/static/static/js/{InputInstructions.D3IDU-eY.js → InputInstructions.DtUxCBS8.js} +1 -1
- streamlit/static/static/js/Particles.BDRPO7r3.js +1 -0
- streamlit/static/static/js/ProgressBar.B64DUUqp.js +2 -0
- streamlit/static/static/js/Toolbar.B3FquPk5.js +1 -0
- streamlit/static/static/js/{base-input.DMlw5p7n.js → base-input.DeBqm5mN.js} +4 -4
- streamlit/static/static/js/{checkbox.C7QR6llE.js → checkbox.C0odQfKb.js} +2 -2
- streamlit/static/static/js/createDownloadLinkElement.ZaXNnPK4.js +1 -0
- streamlit/static/static/js/{createSuper.C5k_2vfB.js → createSuper.DqQ5L3XG.js} +1 -1
- streamlit/static/static/js/data-grid-overlay-editor.DbNsQa8Y.js +1 -0
- streamlit/static/static/js/{downloader.Nj6v3ioB.js → downloader.Bx1D0jhz.js} +1 -1
- streamlit/static/static/js/{es6.CVz13CSz.js → es6.CbPK4m0H.js} +2 -2
- streamlit/static/static/js/{iframeResizer.contentWindow.gZ8zjT0K.js → iframeResizer.contentWindow.CfLKrptA.js} +1 -1
- streamlit/static/static/js/{index.D3wOJJsg.js → index.0XDwe9RV.js} +12 -12
- streamlit/static/static/js/{index.BscWuWHL.js → index.4lI9TuZm.js} +3 -3
- streamlit/static/static/js/index.6s0nVIis.js +3855 -0
- streamlit/static/static/js/index.9E7bRUBU.js +1 -0
- streamlit/static/static/js/index.B9PgeLrZ.js +1 -0
- streamlit/static/static/js/index.B9vzGbOt.js +1 -0
- streamlit/static/static/js/index.BDrQKMCm.js +1 -0
- streamlit/static/static/js/index.BPsoiGgP.js +1 -0
- streamlit/static/static/js/index.CJzdLAun.js +1 -0
- streamlit/static/static/js/index.CNNlC1NL.js +1 -0
- streamlit/static/static/js/index.CO1sClzJ.js +2 -0
- streamlit/static/static/js/index.CZnagxXD.js +1 -0
- streamlit/static/static/js/{index.MbqsiUV4.js → index.Cb0xSF7V.js} +289 -289
- streamlit/static/static/js/index.CgUt3tz_.js +1 -0
- streamlit/static/static/js/index.CjImmcsV.js +1 -0
- streamlit/static/static/js/index.ClRTsv8m.js +2 -0
- streamlit/static/static/js/{index.CTT2YqEU.js → index.CnfWsQzS.js} +1 -1
- streamlit/static/static/js/index.CrJ1XD_V.js +1 -0
- streamlit/static/static/js/index.CtiKsjSC.js +1 -0
- streamlit/static/static/js/index.CwAuytgV.js +1 -0
- streamlit/static/static/js/index.D7GB-kly.js +1 -0
- streamlit/static/static/js/index.DA5wU0mQ.js +1 -0
- streamlit/static/static/js/{index.xfcNJBLM.js → index.DCpyIFTV.js} +1 -1
- streamlit/static/static/js/index.DE9wNOje.js +1 -0
- streamlit/static/static/js/{index.mihWZKb1.js → index.DHnB-C8A.js} +1 -1
- streamlit/static/static/js/{index.BSFzxMXi.js → index.DRTn9zvD.js} +1 -1
- streamlit/static/static/js/index.DjMjyJl9.js +7 -0
- streamlit/static/static/js/{index.CbQtRkVt.js → index.DvRPFfw6.js} +162 -188
- streamlit/static/static/js/{index.B2L574n6.js → index.DwaoC4Zp.js} +3 -3
- streamlit/static/static/js/index.F9tSej94.js +1 -0
- streamlit/static/static/js/index.HeVbRh9H.js +73 -0
- streamlit/static/static/js/index.J2D_m7LY.js +197 -0
- streamlit/static/static/js/index.PyIqRRSR.js +1 -0
- streamlit/static/static/js/index.dfivzJNz.js +1 -0
- streamlit/static/static/js/index.mRztGO69.js +3 -0
- streamlit/static/static/js/index.tB1kn_7z.js +1 -0
- streamlit/static/static/js/index.wDYef4mQ.js +12 -0
- streamlit/static/static/js/{input.D_45B0P-.js → input.BL2buuce.js} +2 -2
- streamlit/static/static/js/{memory.BmhrRyO2.js → memory.CUxjUWS7.js} +1 -1
- streamlit/static/static/js/{mergeWith.DvOME7eH.js → mergeWith.C1kp1zIi.js} +1 -1
- streamlit/static/static/js/{number-overlay-editor.BRNxOzEZ.js → number-overlay-editor.WpheGpmR.js} +1 -1
- streamlit/static/static/js/{possibleConstructorReturn.C5GiK_Ob.js → possibleConstructorReturn.DbvQboK3.js} +1 -1
- streamlit/static/static/js/{sandbox.B-Q9S7vW.js → sandbox.6lnFVWhX.js} +1 -1
- streamlit/static/static/js/{timepicker.DOMbfm_a.js → timepicker.Bg4xAK95.js} +1 -1
- streamlit/static/static/js/{toConsumableArray.CbNz0Ciu.js → toConsumableArray.D9x7Ktv4.js} +1 -1
- streamlit/static/static/js/{uniqueId.-ygIU7IL.js → uniqueId.Bm8FHN92.js} +1 -1
- streamlit/static/static/js/{useBasicWidgetState.Bx3VaRHk.js → useBasicWidgetState.CUSYQZpm.js} +1 -1
- streamlit/static/static/js/{useTextInputAutoExpand.BuE9l5TG.js → useTextInputAutoExpand.Bf2egQOG.js} +2 -2
- streamlit/static/static/js/useUpdateUiValue.lE5xnYWF.js +1 -0
- streamlit/static/static/js/withFullScreenWrapper.CCOXR7N6.js +1 -0
- streamlit/temporary_directory.py +5 -3
- streamlit/testing/v1/app_test.py +4 -1
- streamlit/testing/v1/element_tree.py +4 -5
- streamlit/type_util.py +10 -3
- streamlit/user_info.py +18 -14
- streamlit/util.py +11 -1
- streamlit/watcher/local_sources_watcher.py +4 -3
- streamlit/web/bootstrap.py +18 -12
- streamlit/web/cli.py +2 -1
- streamlit/web/server/oauth_authlib_routes.py +59 -3
- streamlit/web/server/server.py +72 -21
- {streamlit-1.47.1.dist-info → streamlit-1.48.0.dist-info}/METADATA +14 -2
- {streamlit-1.47.1.dist-info → streamlit-1.48.0.dist-info}/RECORD +153 -153
- streamlit/static/static/css/index.CsLB_Bnz.css +0 -1
- streamlit/static/static/js/ProgressBar.EhJ_lCOf.js +0 -2
- streamlit/static/static/js/RenderInPortalIfExists.D6a0mMll.js +0 -1
- streamlit/static/static/js/Toolbar.D6yqQ65-.js +0 -1
- streamlit/static/static/js/createDownloadLinkElement.DZMwyjvU.js +0 -1
- streamlit/static/static/js/data-grid-overlay-editor.CoquyZNK.js +0 -1
- streamlit/static/static/js/index.B3n-pURl.js +0 -2
- streamlit/static/static/js/index.B9jJp9aE.js +0 -1
- streamlit/static/static/js/index.BBVtld-D.js +0 -1
- streamlit/static/static/js/index.BKHPnvYd.js +0 -1
- streamlit/static/static/js/index.B_1CXynz.js +0 -1
- streamlit/static/static/js/index.Bm1LklYO.js +0 -1
- streamlit/static/static/js/index.Byi6__iF.js +0 -1
- streamlit/static/static/js/index.BzJeMpQ-.js +0 -197
- streamlit/static/static/js/index.C4tw7-Cl.js +0 -2
- streamlit/static/static/js/index.CG0C49ex.js +0 -1
- streamlit/static/static/js/index.CMuSJPv-.js +0 -1
- streamlit/static/static/js/index.COPFcr_K.js +0 -3855
- streamlit/static/static/js/index.CU3TLDlu.js +0 -1
- streamlit/static/static/js/index.CVKKDwaf.js +0 -1
- streamlit/static/static/js/index.CWZeK3mV.js +0 -12
- streamlit/static/static/js/index.CWbiNJQl.js +0 -1
- streamlit/static/static/js/index.CWxefYP6.js +0 -73
- streamlit/static/static/js/index.DOdWa88b.js +0 -1
- streamlit/static/static/js/index.DXtnaPua.js +0 -1
- streamlit/static/static/js/index.DatDwFl3.js +0 -3
- streamlit/static/static/js/index.DeWYPvQR.js +0 -1
- streamlit/static/static/js/index.Do4vzIvK.js +0 -1
- streamlit/static/static/js/index.Dvrstlh8.js +0 -1
- streamlit/static/static/js/index.DvznfdF_.js +0 -1
- streamlit/static/static/js/index.U1vvXeGn.js +0 -1
- streamlit/static/static/js/index._WAuWRkp.js +0 -7
- streamlit/static/static/js/index.bwA9_eWC.js +0 -1
- streamlit/static/static/js/index.m1njXuKl.js +0 -1
- streamlit/static/static/js/useOnInputChange.CDZx-L6q.js +0 -1
- streamlit/static/static/js/withFullScreenWrapper.DWXejOhQ.js +0 -1
- {streamlit-1.47.1.data → streamlit-1.48.0.data}/scripts/streamlit.cmd +0 -0
- {streamlit-1.47.1.dist-info → streamlit-1.48.0.dist-info}/WHEEL +0 -0
- {streamlit-1.47.1.dist-info → streamlit-1.48.0.dist-info}/entry_points.txt +0 -0
- {streamlit-1.47.1.dist-info → streamlit-1.48.0.dist-info}/top_level.txt +0 -0
|
@@ -23,6 +23,7 @@ from streamlit.logger import get_logger
|
|
|
23
23
|
from streamlit.watcher.folder_black_list import FolderBlackList
|
|
24
24
|
from streamlit.watcher.path_watcher import (
|
|
25
25
|
NoOpPathWatcher,
|
|
26
|
+
PathWatcherType,
|
|
26
27
|
get_default_path_watcher_class,
|
|
27
28
|
)
|
|
28
29
|
|
|
@@ -36,12 +37,12 @@ _LOGGER: Final = get_logger(__name__)
|
|
|
36
37
|
|
|
37
38
|
class WatchedModule(NamedTuple):
|
|
38
39
|
watcher: Any
|
|
39
|
-
module_name:
|
|
40
|
+
module_name: str | None
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
# This needs to be initialized lazily to avoid calling config.get_option() and
|
|
43
44
|
# thus initializing config options when this file is first imported.
|
|
44
|
-
PathWatcher = None
|
|
45
|
+
PathWatcher: PathWatcherType | None = None
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
class LocalSourcesWatcher:
|
|
@@ -169,7 +170,7 @@ class LocalSourcesWatcher:
|
|
|
169
170
|
glob_pattern = "**/*" if is_directory else None
|
|
170
171
|
|
|
171
172
|
wm = WatchedModule(
|
|
172
|
-
watcher=PathWatcher(
|
|
173
|
+
watcher=PathWatcher( # ty: ignore
|
|
173
174
|
filepath,
|
|
174
175
|
self.on_path_changed,
|
|
175
176
|
glob_pattern=glob_pattern, # Pass as named parameter
|
streamlit/web/bootstrap.py
CHANGED
|
@@ -335,23 +335,29 @@ def run(
|
|
|
335
335
|
# by a debug websocket session.
|
|
336
336
|
await server.stopped
|
|
337
337
|
|
|
338
|
-
# Run the server. This function will not return until the server is shut down.
|
|
339
|
-
# FIX RuntimeError: asyncio.run() cannot be called from a running event loop
|
|
340
|
-
# asyncio.run(run_server()) # noqa: ERA001
|
|
341
|
-
|
|
342
338
|
# Define a main function to handle the event loop logic
|
|
343
339
|
async def main() -> None:
|
|
344
340
|
await run_server()
|
|
345
341
|
|
|
342
|
+
# Handle running in existing event loop vs creating new one
|
|
343
|
+
running_in_event_loop = False
|
|
346
344
|
try:
|
|
347
345
|
# Check if we're already in an event loop
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
# TODO(lukasmasuch): Do we have to store a reference for the task here?
|
|
351
|
-
asyncio.create_task(main()) # noqa: RUF006
|
|
352
|
-
else:
|
|
353
|
-
# Otherwise, use `asyncio.run`
|
|
354
|
-
asyncio.run(main())
|
|
346
|
+
asyncio.get_running_loop()
|
|
347
|
+
running_in_event_loop = True
|
|
355
348
|
except RuntimeError:
|
|
356
|
-
#
|
|
349
|
+
# No running event loop - this is expected for normal CLI usage
|
|
350
|
+
pass
|
|
351
|
+
|
|
352
|
+
if running_in_event_loop:
|
|
353
|
+
_LOGGER.debug("Running server in existing event loop.")
|
|
354
|
+
# We're in an existing event loop.
|
|
355
|
+
task = asyncio.create_task(main(), name="bootstrap.run_server")
|
|
356
|
+
# Store task reference on the server to keep it alive
|
|
357
|
+
# This prevents the task from being garbage collected
|
|
358
|
+
server._bootstrap_task = task
|
|
359
|
+
else:
|
|
360
|
+
# No running event loop, so we can use asyncio.run
|
|
361
|
+
# This is the normal case when running streamlit from the command line
|
|
362
|
+
_LOGGER.debug("Starting new event loop for server")
|
|
357
363
|
asyncio.run(main())
|
streamlit/web/cli.py
CHANGED
|
@@ -114,13 +114,14 @@ def configurator_options(func: F) -> F:
|
|
|
114
114
|
def _download_remote(main_script_path: str, url_path: str) -> None:
|
|
115
115
|
"""Fetch remote file at url_path to main_script_path."""
|
|
116
116
|
import requests
|
|
117
|
+
from requests.exceptions import RequestException
|
|
117
118
|
|
|
118
119
|
with open(main_script_path, "wb") as fp:
|
|
119
120
|
try:
|
|
120
121
|
resp = requests.get(url_path, timeout=30)
|
|
121
122
|
resp.raise_for_status()
|
|
122
123
|
fp.write(resp.content)
|
|
123
|
-
except
|
|
124
|
+
except RequestException as e:
|
|
124
125
|
raise click.BadParameter(f"Unable to fetch {url_path}.\n{e}")
|
|
125
126
|
|
|
126
127
|
|
|
@@ -15,7 +15,7 @@ from __future__ import annotations
|
|
|
15
15
|
|
|
16
16
|
import json
|
|
17
17
|
from typing import Any, Final, cast
|
|
18
|
-
from urllib.parse import urlparse
|
|
18
|
+
from urllib.parse import urlencode, urlparse
|
|
19
19
|
|
|
20
20
|
import tornado.web
|
|
21
21
|
|
|
@@ -130,7 +130,63 @@ class AuthLoginHandler(AuthHandlerMixin, tornado.web.RequestHandler):
|
|
|
130
130
|
class AuthLogoutHandler(AuthHandlerMixin, tornado.web.RequestHandler):
|
|
131
131
|
def get(self) -> None:
|
|
132
132
|
self.clear_auth_cookie()
|
|
133
|
-
|
|
133
|
+
|
|
134
|
+
provider_logout_url = self._get_provider_logout_url()
|
|
135
|
+
if provider_logout_url:
|
|
136
|
+
self.redirect(provider_logout_url)
|
|
137
|
+
else:
|
|
138
|
+
self.redirect_to_base()
|
|
139
|
+
|
|
140
|
+
def _get_redirect_root(self) -> str | None:
|
|
141
|
+
auth_section = get_secrets_auth_section()
|
|
142
|
+
if not auth_section or not auth_section.get("redirect_uri"):
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
redirect_uri: str = auth_section["redirect_uri"]
|
|
146
|
+
if not redirect_uri.endswith("/oauth2callback"):
|
|
147
|
+
_LOGGER.warning("Redirect URI does not end with /oauth2callback")
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
return redirect_uri.removesuffix("oauth2callback")
|
|
151
|
+
|
|
152
|
+
def _get_provider_logout_url(self) -> str | None:
|
|
153
|
+
"""Get the OAuth provider's logout URL from OIDC metadata."""
|
|
154
|
+
try:
|
|
155
|
+
cookie_value = self.get_signed_cookie(AUTH_COOKIE_NAME)
|
|
156
|
+
except AttributeError: # Backward compatibility with Tornado < 6.3.0
|
|
157
|
+
cookie_value = self.get_secure_cookie(AUTH_COOKIE_NAME)
|
|
158
|
+
|
|
159
|
+
if not cookie_value:
|
|
160
|
+
return None
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
user_info = json.loads(cookie_value)
|
|
164
|
+
provider = user_info.get("provider")
|
|
165
|
+
if not provider:
|
|
166
|
+
return None
|
|
167
|
+
|
|
168
|
+
client, _ = create_oauth_client(provider)
|
|
169
|
+
|
|
170
|
+
metadata = client.load_server_metadata()
|
|
171
|
+
end_session_endpoint = metadata.get("end_session_endpoint")
|
|
172
|
+
|
|
173
|
+
if not end_session_endpoint:
|
|
174
|
+
_LOGGER.info("No end_session_endpoint found for provider %s", provider)
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
redirect_root = self._get_redirect_root()
|
|
178
|
+
if redirect_root is None:
|
|
179
|
+
_LOGGER.info("Redirect url could not be determined")
|
|
180
|
+
return None
|
|
181
|
+
|
|
182
|
+
logout_params = {"post_logout_redirect_uri": redirect_root}
|
|
183
|
+
|
|
184
|
+
# Not using id_token_hint as we don't store the id token
|
|
185
|
+
return f"{end_session_endpoint}?{urlencode(logout_params)}"
|
|
186
|
+
|
|
187
|
+
except Exception as e:
|
|
188
|
+
_LOGGER.warning("Failed to get provider logout URL: %s", e)
|
|
189
|
+
return None
|
|
134
190
|
|
|
135
191
|
|
|
136
192
|
class AuthCallbackHandler(AuthHandlerMixin, tornado.web.RequestHandler):
|
|
@@ -172,7 +228,7 @@ class AuthCallbackHandler(AuthHandlerMixin, tornado.web.RequestHandler):
|
|
|
172
228
|
token = client.authorize_access_token(self)
|
|
173
229
|
user = cast("dict[str, Any]", token.get("userinfo"))
|
|
174
230
|
|
|
175
|
-
cookie_value = dict(user, origin=origin, is_logged_in=True)
|
|
231
|
+
cookie_value = dict(user, origin=origin, is_logged_in=True, provider=provider)
|
|
176
232
|
if user:
|
|
177
233
|
self.set_auth_cookie(cookie_value)
|
|
178
234
|
else:
|
streamlit/web/server/server.py
CHANGED
|
@@ -22,11 +22,7 @@ import sys
|
|
|
22
22
|
from pathlib import Path
|
|
23
23
|
from typing import TYPE_CHECKING, Any, Final
|
|
24
24
|
|
|
25
|
-
import tornado.concurrent
|
|
26
|
-
import tornado.locks
|
|
27
|
-
import tornado.netutil
|
|
28
25
|
import tornado.web
|
|
29
|
-
import tornado.websocket
|
|
30
26
|
from tornado.httpserver import HTTPServer
|
|
31
27
|
|
|
32
28
|
from streamlit import cli_util, config, file_util, util
|
|
@@ -62,25 +58,67 @@ from streamlit.web.server.stats_request_handler import StatsRequestHandler
|
|
|
62
58
|
from streamlit.web.server.upload_file_request_handler import UploadFileRequestHandler
|
|
63
59
|
|
|
64
60
|
if TYPE_CHECKING:
|
|
61
|
+
import asyncio
|
|
65
62
|
from collections.abc import Awaitable
|
|
66
63
|
from ssl import SSLContext
|
|
67
64
|
|
|
68
65
|
_LOGGER: Final = get_logger(__name__)
|
|
69
66
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
"
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
67
|
+
|
|
68
|
+
def _get_websocket_ping_interval_and_timeout() -> tuple[int, int]:
|
|
69
|
+
"""Get the websocket ping interval and timeout from config or defaults.
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------
|
|
73
|
+
tuple: (ping_interval, ping_timeout)
|
|
74
|
+
"""
|
|
75
|
+
configured_interval = config.get_option("server.websocketPingInterval")
|
|
76
|
+
|
|
77
|
+
if configured_interval is not None:
|
|
78
|
+
# User has explicitly set a value
|
|
79
|
+
interval = int(configured_interval)
|
|
80
|
+
|
|
81
|
+
# Warn if using Tornado 6.5+ with low interval
|
|
82
|
+
if not is_tornado_version_less_than("6.5.0") and interval < 30:
|
|
83
|
+
_LOGGER.warning(
|
|
84
|
+
"You have set server.websocketPingInterval to %s, but Tornado >= 6.5 "
|
|
85
|
+
"requires websocket_ping_interval >= websocket_ping_timeout. "
|
|
86
|
+
"To comply, we are setting both the ping interval and ping timeout to %s. "
|
|
87
|
+
"Depending on the specific deployment setup, this may cause connection issues.",
|
|
88
|
+
interval,
|
|
89
|
+
interval,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# When user configures interval, set timeout to match
|
|
93
|
+
return interval, interval
|
|
94
|
+
|
|
95
|
+
# Default behavior: respect Tornado version for interval, always 30s timeout
|
|
96
|
+
default_interval = 1 if is_tornado_version_less_than("6.5.0") else 30
|
|
97
|
+
return default_interval, 30
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def get_tornado_settings() -> dict[str, Any]:
|
|
101
|
+
"""Get Tornado settings for the server.
|
|
102
|
+
|
|
103
|
+
This is a function to allow for testing and dynamic configuration.
|
|
104
|
+
"""
|
|
105
|
+
ping_interval, ping_timeout = _get_websocket_ping_interval_and_timeout()
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
# Gzip HTTP responses.
|
|
109
|
+
"compress_response": True,
|
|
110
|
+
# Ping interval for websocket keepalive.
|
|
111
|
+
# With recent versions of Tornado, this value must be greater than or
|
|
112
|
+
# equal to websocket_ping_timeout.
|
|
113
|
+
# For details, see https://github.com/tornadoweb/tornado/pull/3376
|
|
114
|
+
# For compatibility with older versions of Tornado, we set the value to 1.
|
|
115
|
+
"websocket_ping_interval": ping_interval,
|
|
116
|
+
# If we don't get a ping response within this time, the connection
|
|
117
|
+
# is timed out.
|
|
118
|
+
"websocket_ping_timeout": ping_timeout,
|
|
119
|
+
"xsrf_cookie_name": "_streamlit_xsrf",
|
|
120
|
+
}
|
|
121
|
+
|
|
84
122
|
|
|
85
123
|
# When server.port is not available it will look for the next available port
|
|
86
124
|
# up to MAX_PORT_SEARCH_RETRIES.
|
|
@@ -191,8 +229,16 @@ def start_listening_unix_socket(http_server: HTTPServer) -> None:
|
|
|
191
229
|
address = config.get_option("server.address")
|
|
192
230
|
file_name = os.path.expanduser(address[len(UNIX_SOCKET_PREFIX) :])
|
|
193
231
|
|
|
194
|
-
|
|
195
|
-
|
|
232
|
+
import tornado.netutil
|
|
233
|
+
|
|
234
|
+
if hasattr(tornado.netutil, "bind_unix_socket"):
|
|
235
|
+
unix_socket = tornado.netutil.bind_unix_socket(file_name)
|
|
236
|
+
http_server.add_socket(unix_socket)
|
|
237
|
+
else:
|
|
238
|
+
_LOGGER.error(
|
|
239
|
+
"Unix socket support is not available in this version of Tornado."
|
|
240
|
+
)
|
|
241
|
+
sys.exit(1)
|
|
196
242
|
|
|
197
243
|
|
|
198
244
|
def start_listening_tcp_socket(http_server: HTTPServer) -> None:
|
|
@@ -240,6 +286,11 @@ class Server:
|
|
|
240
286
|
|
|
241
287
|
self._main_script_path = main_script_path
|
|
242
288
|
|
|
289
|
+
# The task that runs the server if an event loop is already running.
|
|
290
|
+
# We need to save a reference to it so that it doesn't get
|
|
291
|
+
# garbage collected while running.
|
|
292
|
+
self._bootstrap_task: asyncio.Task[None] | None = None
|
|
293
|
+
|
|
243
294
|
# Initialize MediaFileStorage and its associated endpoint
|
|
244
295
|
media_file_storage = MemoryMediaFileStorage(MEDIA_ENDPOINT)
|
|
245
296
|
MediaFileHandler.initialize_storage(media_file_storage)
|
|
@@ -440,7 +491,7 @@ class Server:
|
|
|
440
491
|
xsrf_cookies=is_xsrf_enabled(),
|
|
441
492
|
# Set the websocket message size. The default value is too low.
|
|
442
493
|
websocket_max_message_size=get_max_message_size_bytes(),
|
|
443
|
-
**
|
|
494
|
+
**get_tornado_settings(),
|
|
444
495
|
)
|
|
445
496
|
|
|
446
497
|
@property
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: streamlit
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.48.0
|
|
4
4
|
Summary: A faster way to build and share data apps
|
|
5
5
|
Home-page: https://streamlit.io
|
|
6
6
|
Author: Snowflake Inc
|
|
@@ -31,7 +31,7 @@ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
|
31
31
|
Classifier: Topic :: Software Development :: Widget Sets
|
|
32
32
|
Requires-Python: >=3.9, !=3.9.7
|
|
33
33
|
Description-Content-Type: text/markdown
|
|
34
|
-
Requires-Dist: altair
|
|
34
|
+
Requires-Dist: altair!=5.4.0,!=5.4.1,<6,>=4.0
|
|
35
35
|
Requires-Dist: blinker<2,>=1.5.0
|
|
36
36
|
Requires-Dist: cachetools<7,>=4.0
|
|
37
37
|
Requires-Dist: click<9,>=7.0
|
|
@@ -52,6 +52,18 @@ Requires-Dist: tornado!=6.5.0,<7,>=6.0.3
|
|
|
52
52
|
Provides-Extra: snowflake
|
|
53
53
|
Requires-Dist: snowflake-snowpark-python[modin]>=1.17.0; python_version < "3.12" and extra == "snowflake"
|
|
54
54
|
Requires-Dist: snowflake-connector-python>=3.3.0; python_version < "3.12" and extra == "snowflake"
|
|
55
|
+
Provides-Extra: auth
|
|
56
|
+
Requires-Dist: Authlib>=1.3.2; extra == "auth"
|
|
57
|
+
Provides-Extra: charts
|
|
58
|
+
Requires-Dist: matplotlib>=3.0.0; extra == "charts"
|
|
59
|
+
Requires-Dist: graphviz>=0.19.0; extra == "charts"
|
|
60
|
+
Requires-Dist: plotly>=4.0.0; extra == "charts"
|
|
61
|
+
Requires-Dist: orjson>=3.5.0; extra == "charts"
|
|
62
|
+
Provides-Extra: sql
|
|
63
|
+
Requires-Dist: SQLAlchemy>=2.0.0; extra == "sql"
|
|
64
|
+
Provides-Extra: all
|
|
65
|
+
Requires-Dist: streamlit[auth,charts,snowflake,sql]; extra == "all"
|
|
66
|
+
Requires-Dist: rich>=11.0.0; extra == "all"
|
|
55
67
|
Dynamic: author
|
|
56
68
|
Dynamic: author-email
|
|
57
69
|
Dynamic: classifier
|