streamlit 1.49.1__py3-none-any.whl → 1.51.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/__init__.py +4 -1
- streamlit/column_config.py +2 -0
- streamlit/commands/navigation.py +7 -7
- streamlit/commands/page_config.py +4 -6
- streamlit/components/v1/custom_component.py +17 -42
- streamlit/components/v2/__init__.py +458 -0
- streamlit/components/v2/bidi_component/__init__.py +20 -0
- streamlit/components/v2/bidi_component/constants.py +29 -0
- streamlit/components/v2/bidi_component/main.py +386 -0
- streamlit/components/v2/bidi_component/serialization.py +265 -0
- streamlit/components/v2/bidi_component/state.py +92 -0
- streamlit/components/v2/component_definition_resolver.py +143 -0
- streamlit/components/v2/component_file_watcher.py +403 -0
- streamlit/components/v2/component_manager.py +431 -0
- streamlit/components/v2/component_manifest_handler.py +122 -0
- streamlit/components/v2/component_path_utils.py +245 -0
- streamlit/components/v2/component_registry.py +409 -0
- streamlit/components/v2/get_bidi_component_manager.py +51 -0
- streamlit/components/v2/manifest_scanner.py +615 -0
- streamlit/components/v2/presentation.py +198 -0
- streamlit/components/v2/types.py +324 -0
- streamlit/config.py +741 -32
- streamlit/config_option.py +4 -1
- streamlit/config_util.py +650 -1
- streamlit/connections/base_connection.py +4 -2
- streamlit/dataframe_util.py +18 -10
- streamlit/delta_generator.py +8 -7
- streamlit/delta_generator_singletons.py +3 -1
- streamlit/deprecation_util.py +17 -6
- streamlit/elements/arrow.py +90 -42
- streamlit/elements/deck_gl_json_chart.py +98 -39
- streamlit/elements/dialog_decorator.py +2 -1
- streamlit/elements/exception.py +3 -1
- streamlit/elements/form.py +6 -6
- streamlit/elements/graphviz_chart.py +24 -9
- streamlit/elements/heading.py +3 -5
- streamlit/elements/iframe.py +0 -2
- streamlit/elements/image.py +12 -13
- streamlit/elements/layouts.py +89 -22
- streamlit/elements/lib/built_in_chart_utils.py +95 -31
- streamlit/elements/lib/color_util.py +8 -18
- streamlit/elements/lib/column_config_utils.py +9 -8
- streamlit/elements/lib/column_types.py +595 -148
- streamlit/elements/lib/dialog.py +3 -2
- streamlit/elements/lib/image_utils.py +3 -5
- streamlit/elements/lib/layout_utils.py +50 -13
- streamlit/elements/lib/mutable_status_container.py +2 -2
- streamlit/elements/lib/options_selector_utils.py +2 -2
- streamlit/elements/lib/pandas_styler_utils.py +30 -14
- streamlit/elements/lib/utils.py +21 -9
- streamlit/elements/map.py +81 -40
- streamlit/elements/media.py +7 -7
- streamlit/elements/metric.py +11 -35
- streamlit/elements/pdf.py +2 -4
- streamlit/elements/plotly_chart.py +142 -26
- streamlit/elements/progress.py +2 -4
- streamlit/elements/pyplot.py +6 -6
- streamlit/elements/space.py +113 -0
- streamlit/elements/vega_charts.py +400 -143
- streamlit/elements/widgets/audio_input.py +52 -4
- streamlit/elements/widgets/button.py +29 -29
- streamlit/elements/widgets/button_group.py +33 -6
- streamlit/elements/widgets/camera_input.py +3 -4
- streamlit/elements/widgets/chat.py +7 -0
- streamlit/elements/widgets/checkbox.py +1 -0
- streamlit/elements/widgets/color_picker.py +1 -0
- streamlit/elements/widgets/data_editor.py +34 -29
- streamlit/elements/widgets/file_uploader.py +6 -10
- streamlit/elements/widgets/multiselect.py +14 -3
- streamlit/elements/widgets/number_input.py +5 -4
- streamlit/elements/widgets/radio.py +10 -2
- streamlit/elements/widgets/select_slider.py +8 -4
- streamlit/elements/widgets/selectbox.py +9 -2
- streamlit/elements/widgets/slider.py +38 -41
- streamlit/elements/widgets/text_widgets.py +6 -0
- streamlit/elements/widgets/time_widgets.py +15 -12
- streamlit/elements/write.py +28 -23
- streamlit/emojis.py +1 -1
- streamlit/errors.py +115 -0
- streamlit/git_util.py +65 -43
- streamlit/hello/hello.py +8 -0
- streamlit/hello/utils.py +2 -1
- streamlit/material_icon_names.py +1 -1
- streamlit/navigation/page.py +4 -1
- streamlit/proto/ArrowData_pb2.py +27 -0
- streamlit/proto/ArrowData_pb2.pyi +46 -0
- streamlit/proto/Arrow_pb2.py +10 -8
- streamlit/proto/Arrow_pb2.pyi +31 -2
- streamlit/proto/AudioInput_pb2.py +2 -2
- streamlit/proto/AudioInput_pb2.pyi +6 -2
- streamlit/proto/BidiComponent_pb2.py +34 -0
- streamlit/proto/BidiComponent_pb2.pyi +153 -0
- streamlit/proto/Block_pb2.py +11 -11
- streamlit/proto/Block_pb2.pyi +9 -1
- streamlit/proto/DeckGlJsonChart_pb2.py +10 -4
- streamlit/proto/DeckGlJsonChart_pb2.pyi +9 -3
- streamlit/proto/Element_pb2.py +5 -3
- streamlit/proto/Element_pb2.pyi +14 -4
- streamlit/proto/HeightConfig_pb2.py +2 -2
- streamlit/proto/HeightConfig_pb2.pyi +6 -3
- streamlit/proto/NewSession_pb2.py +18 -16
- streamlit/proto/NewSession_pb2.pyi +158 -6
- streamlit/proto/PlotlyChart_pb2.py +8 -6
- streamlit/proto/PlotlyChart_pb2.pyi +3 -1
- streamlit/proto/Space_pb2.py +27 -0
- streamlit/proto/Space_pb2.pyi +42 -0
- streamlit/proto/WidgetStates_pb2.py +2 -2
- streamlit/proto/WidgetStates_pb2.pyi +13 -3
- streamlit/proto/WidthConfig_pb2.py +2 -2
- streamlit/proto/WidthConfig_pb2.pyi +6 -3
- streamlit/runtime/app_session.py +45 -6
- streamlit/runtime/caching/cache_data_api.py +4 -4
- streamlit/runtime/caching/cache_errors.py +4 -1
- streamlit/runtime/caching/cache_resource_api.py +3 -2
- streamlit/runtime/caching/cache_utils.py +2 -1
- streamlit/runtime/caching/cached_message_replay.py +3 -3
- streamlit/runtime/caching/hashing.py +3 -4
- streamlit/runtime/caching/legacy_cache_api.py +2 -1
- streamlit/runtime/connection_factory.py +1 -3
- streamlit/runtime/forward_msg_queue.py +4 -1
- streamlit/runtime/fragment.py +2 -1
- streamlit/runtime/memory_media_file_storage.py +1 -1
- streamlit/runtime/metrics_util.py +6 -2
- streamlit/runtime/runtime.py +14 -0
- streamlit/runtime/scriptrunner/exec_code.py +2 -1
- streamlit/runtime/scriptrunner/script_runner.py +2 -2
- streamlit/runtime/scriptrunner_utils/script_run_context.py +3 -6
- streamlit/runtime/secrets.py +2 -4
- streamlit/runtime/session_manager.py +3 -1
- streamlit/runtime/state/common.py +30 -5
- streamlit/runtime/state/presentation.py +85 -0
- streamlit/runtime/state/safe_session_state.py +2 -2
- streamlit/runtime/state/session_state.py +220 -16
- streamlit/runtime/state/widgets.py +19 -3
- streamlit/runtime/theme_util.py +148 -0
- streamlit/runtime/websocket_session_manager.py +3 -1
- streamlit/source_util.py +2 -2
- streamlit/static/index.html +2 -2
- streamlit/static/manifest.json +244 -227
- streamlit/static/static/css/{index.C8X8rNzw.css → index.BpABIXK9.css} +1 -1
- streamlit/static/static/css/index.DgR7E2CV.css +1 -0
- streamlit/static/static/js/{ErrorOutline.esm.DcGrhbBP.js → ErrorOutline.esm.YoJdlW1p.js} +1 -1
- streamlit/static/static/js/{FileDownload.esm.DgBvV6Pq.js → FileDownload.esm.Ddx8VEYy.js} +1 -1
- streamlit/static/static/js/{FileHelper.M6AAaeuA.js → FileHelper.90EtOmj9.js} +1 -1
- streamlit/static/static/js/{FormClearHelper.DHh1GFzm.js → FormClearHelper.BB1Km6eP.js} +1 -1
- streamlit/static/static/js/InputInstructions.jhH15PqV.js +1 -0
- streamlit/static/static/js/{Particles.DDVT-6Qc.js → Particles.DUsputn1.js} +1 -1
- streamlit/static/static/js/{ProgressBar.BEY0cXXV.js → ProgressBar.DLY8H6nE.js} +2 -2
- streamlit/static/static/js/Toolbar.D8nHCkuz.js +1 -0
- streamlit/static/static/js/{base-input.CK3UVGp1.js → base-input.CJGiNqed.js} +3 -3
- streamlit/static/static/js/{checkbox.D8W881TL.js → checkbox.Cpdd482O.js} +1 -1
- streamlit/static/static/js/{createSuper.B6W-Dh9S.js → createSuper.CuQIogbW.js} +1 -1
- streamlit/static/static/js/data-grid-overlay-editor.2Ufgxc6y.js +1 -0
- streamlit/static/static/js/{downloader.DiKpuU_S.js → downloader.CN0K7xlu.js} +1 -1
- streamlit/static/static/js/{es6.B8zRNPZ-.js → es6.BJcsVXQ0.js} +2 -2
- streamlit/static/static/js/{iframeResizer.contentWindow.DIewJmmh.js → iframeResizer.contentWindow.XzUvQqcZ.js} +1 -1
- streamlit/static/static/js/index.B1ZQh4P1.js +1 -0
- streamlit/static/static/js/index.BKstZk0M.js +27 -0
- streamlit/static/static/js/{index.Bte_9Lyq.js → index.BMcFsUee.js} +1 -1
- streamlit/static/static/js/{index.qhs54UAB.js → index.BR-IdcTb.js} +1 -1
- streamlit/static/static/js/{index.CejBxbg1.js → index.B_dWA3vd.js} +1 -1
- streamlit/static/static/js/{index.D5naqx-J.js → index.BgnZEMVh.js} +1 -1
- streamlit/static/static/js/{index.C7fRKRs4.js → index.BohqXifI.js} +1 -1
- streamlit/static/static/js/{index.cnnXF7xQ.js → index.Br5nxKNj.js} +1 -1
- streamlit/static/static/js/index.BrIKVbNc.js +3 -0
- streamlit/static/static/js/index.BtWUPzle.js +1 -0
- streamlit/static/static/js/index.C0RLraek.js +1 -0
- streamlit/static/static/js/{index.CP5TD2z1.js → index.CAIjskgG.js} +1 -1
- streamlit/static/static/js/{index.CD8HuT3N.js → index.CAj-7vWz.js} +135 -162
- streamlit/static/static/js/{index.DtYN2x4k.js → index.CMtEit2O.js} +1 -1
- streamlit/static/static/js/index.CkRlykEE.js +12 -0
- streamlit/static/static/js/{index.Ts_0SdB9.js → index.CmN3FXfI.js} +2 -2
- streamlit/static/static/js/{index.BnEpvLEz.js → index.CwbFI1_-.js} +1 -1
- streamlit/static/static/js/{index.CcJf6BCU.js → index.CxIUUfab.js} +27 -27
- streamlit/static/static/js/index.D2KPNy7e.js +1 -0
- streamlit/static/static/js/{index.Ch7MBCx0.js → index.D3GPA5k4.js} +47 -47
- streamlit/static/static/js/{index.ho6NIXGl.js → index.DGAh7DMq.js} +1 -1
- streamlit/static/static/js/index.DKb_NvmG.js +197 -0
- streamlit/static/static/js/{index.CvYYtxD_.js → index.DMqgUYKq.js} +1 -1
- streamlit/static/static/js/{index.zecpGxtj.js → index.DOFlg3dS.js} +1 -1
- streamlit/static/static/js/{index.B9mjBcgE.js → index.DPUXkcQL.js} +1 -1
- streamlit/static/static/js/index.DX1xY89g.js +1 -0
- streamlit/static/static/js/index.DYATBCsq.js +2 -0
- streamlit/static/static/js/{index.D2-atlaQ.js → index.DaSmGJ76.js} +3 -3
- streamlit/static/static/js/index.Dd7bMeLP.js +1 -0
- streamlit/static/static/js/{index.4eF4NxG2.js → index.DjmmgI5U.js} +1 -1
- streamlit/static/static/js/index.Dq56CyM2.js +1 -0
- streamlit/static/static/js/index.DuiXaS5_.js +7 -0
- streamlit/static/static/js/index.DvFidMLe.js +2 -0
- streamlit/static/static/js/{index.452cqrrL.js → index.DwkhC5Pc.js} +1 -1
- streamlit/static/static/js/{index.Dk4C7X3i.js → index.Q-3sFn1v.js} +1 -1
- streamlit/static/static/js/{index.CjXWwH-y.js → index.QJ5QO9sJ.js} +1 -1
- streamlit/static/static/js/{index.B6U8LQo3.js → index.VwTaeety.js} +1 -1
- streamlit/static/static/js/index.YOqQbeX8.js +1 -0
- streamlit/static/static/js/{input.nzVJphXi.js → input.D4MN_FzN.js} +1 -1
- streamlit/static/static/js/{memory.CjCgTQz3.js → memory.DrZjtdGT.js} +1 -1
- streamlit/static/static/js/{number-overlay-editor.DaRFzZEO.js → number-overlay-editor.DRwAw1In.js} +1 -1
- streamlit/static/static/js/{possibleConstructorReturn.DgiPnZ9N.js → possibleConstructorReturn.exeeJQEP.js} +1 -1
- streamlit/static/static/js/record.B-tDciZb.js +1 -0
- streamlit/static/static/js/{sandbox.mithfq7Z.js → sandbox.ClO3IuUr.js} +1 -1
- streamlit/static/static/js/{timepicker.Dbl5KFh6.js → timepicker.DAhu-vcF.js} +4 -4
- streamlit/static/static/js/{toConsumableArray.D-Dx88BQ.js → toConsumableArray.DNbljYEC.js} +1 -1
- streamlit/static/static/js/{uniqueId.Bh26R_3S.js → uniqueId.oG4Gvj1v.js} +1 -1
- streamlit/static/static/js/{useBasicWidgetState.DeK-QJpD.js → useBasicWidgetState.D6sOH6oI.js} +1 -1
- streamlit/static/static/js/{useTextInputAutoExpand.4iAdLWD-.js → useTextInputAutoExpand.4u3_GcuN.js} +2 -2
- streamlit/static/static/js/{useUpdateUiValue.CmT7_nJN.js → useUpdateUiValue.F2R3eTeR.js} +1 -1
- streamlit/static/static/js/wavesurfer.esm.vI8Eid4k.js +73 -0
- streamlit/static/static/js/withFullScreenWrapper.zothJIsI.js +1 -0
- streamlit/static/static/media/MaterialSymbols-Rounded.C7IFxh57.woff2 +0 -0
- streamlit/string_util.py +56 -1
- streamlit/testing/v1/app_test.py +2 -2
- streamlit/testing/v1/element_tree.py +23 -9
- streamlit/testing/v1/util.py +2 -2
- streamlit/type_util.py +3 -4
- streamlit/url_util.py +1 -3
- streamlit/user_info.py +1 -2
- streamlit/util.py +3 -1
- streamlit/watcher/event_based_path_watcher.py +23 -12
- streamlit/watcher/local_sources_watcher.py +11 -1
- streamlit/watcher/path_watcher.py +9 -6
- streamlit/watcher/polling_path_watcher.py +4 -1
- streamlit/watcher/util.py +2 -2
- streamlit/web/bootstrap.py +0 -31
- streamlit/web/cli.py +51 -22
- streamlit/web/server/bidi_component_request_handler.py +193 -0
- streamlit/web/server/component_file_utils.py +97 -0
- streamlit/web/server/component_request_handler.py +8 -21
- streamlit/web/server/oidc_mixin.py +3 -1
- streamlit/web/server/routes.py +18 -5
- streamlit/web/server/server.py +10 -0
- streamlit/web/server/server_util.py +3 -1
- streamlit/web/server/upload_file_request_handler.py +3 -1
- {streamlit-1.49.1.dist-info → streamlit-1.51.0.dist-info}/METADATA +4 -5
- {streamlit-1.49.1.dist-info → streamlit-1.51.0.dist-info}/RECORD +238 -209
- streamlit/static/static/css/index.COe1010n.css +0 -1
- streamlit/static/static/js/Hooks.DGu1od_L.js +0 -1
- streamlit/static/static/js/InputInstructions.z6sVgyYt.js +0 -1
- streamlit/static/static/js/Toolbar.DSnK1fUh.js +0 -1
- streamlit/static/static/js/data-grid-overlay-editor.DRTHOydk.js +0 -1
- streamlit/static/static/js/index.BXYmrqnf.js +0 -1
- streamlit/static/static/js/index.B_8AnktO.js +0 -1
- streamlit/static/static/js/index.Bl7zGQSh.js +0 -7
- streamlit/static/static/js/index.BnJIOYn9.js +0 -73
- streamlit/static/static/js/index.C1HcTl5K.js +0 -1
- streamlit/static/static/js/index.C7lSmSOP.js +0 -1
- streamlit/static/static/js/index.C_tmcx4B.js +0 -1
- streamlit/static/static/js/index.D3K5nOu9.js +0 -197
- streamlit/static/static/js/index.DkKT3LUI.js +0 -1
- streamlit/static/static/js/index.MTPPBDHk.js +0 -2
- streamlit/static/static/js/index.pqW9AMJD.js +0 -3
- streamlit/static/static/js/index.urHgTgMQ.js +0 -12
- streamlit/static/static/js/index.wzkv_11M.js +0 -1
- streamlit/static/static/js/index.yF5AncHY.js +0 -1
- streamlit/static/static/js/withFullScreenWrapper.DLp1ENGm.js +0 -1
- streamlit/static/static/media/MaterialSymbols-Rounded.CBxVaFdk.woff2 +0 -0
- {streamlit-1.49.1.data → streamlit-1.51.0.data}/scripts/streamlit.cmd +0 -0
- {streamlit-1.49.1.dist-info → streamlit-1.51.0.dist-info}/WHEEL +0 -0
- {streamlit-1.49.1.dist-info → streamlit-1.51.0.dist-info}/entry_points.txt +0 -0
- {streamlit-1.49.1.dist-info → streamlit-1.51.0.dist-info}/top_level.txt +0 -0
streamlit/web/cli.py
CHANGED
|
@@ -18,7 +18,9 @@ from __future__ import annotations
|
|
|
18
18
|
|
|
19
19
|
import os
|
|
20
20
|
import sys
|
|
21
|
-
from
|
|
21
|
+
from collections.abc import Callable
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import TYPE_CHECKING, Any, Final, TypeVar
|
|
22
24
|
|
|
23
25
|
# We cannot lazy-load click here because its used via decorators.
|
|
24
26
|
import click
|
|
@@ -195,46 +197,73 @@ def main_hello(**kwargs: Any) -> None:
|
|
|
195
197
|
|
|
196
198
|
@main.command("run")
|
|
197
199
|
@configurator_options
|
|
198
|
-
@click.argument("target",
|
|
200
|
+
@click.argument("target", default="streamlit_app.py", envvar="STREAMLIT_RUN_TARGET")
|
|
199
201
|
@click.argument("args", nargs=-1)
|
|
200
202
|
def main_run(target: str, args: list[str] | None = None, **kwargs: Any) -> None:
|
|
201
203
|
"""Run a Python script, piping stderr to Streamlit.
|
|
202
204
|
|
|
203
|
-
|
|
204
|
-
will download the script to a temporary file and runs this file.
|
|
205
|
+
If omitted, the target script will be assumed to be "streamlit_app.py".
|
|
205
206
|
|
|
207
|
+
Otherwise, the target script should be one of the following:
|
|
208
|
+
- The path to a local Python file.
|
|
209
|
+
- The path to a local folder where "streamlit_app.py" can be found.
|
|
210
|
+
- A URL pointing to a Python file. In this case Streamlit will download the
|
|
211
|
+
file to a temporary file and run it.
|
|
212
|
+
|
|
213
|
+
To pass command-line arguments to the script, add " -- " before them. For example:
|
|
214
|
+
|
|
215
|
+
$ streamlit run my_app.py -- --my_arg1=123 my_arg2
|
|
216
|
+
↑
|
|
217
|
+
Your CLI args start after this.
|
|
206
218
|
"""
|
|
207
219
|
from streamlit import url_util
|
|
208
220
|
|
|
209
|
-
_, extension = os.path.splitext(target)
|
|
210
|
-
if extension[1:] not in ACCEPTED_FILE_EXTENSIONS:
|
|
211
|
-
if extension[1:] == "":
|
|
212
|
-
raise click.BadArgumentUsage(
|
|
213
|
-
"Streamlit requires raw Python (.py) files, but the provided file has no extension.\n"
|
|
214
|
-
"For more information, please see https://docs.streamlit.io"
|
|
215
|
-
)
|
|
216
|
-
raise click.BadArgumentUsage(
|
|
217
|
-
f"Streamlit requires raw Python (.py) files, not {extension}.\nFor more information, please see https://docs.streamlit.io"
|
|
218
|
-
)
|
|
219
|
-
|
|
220
221
|
if url_util.is_url(target):
|
|
221
222
|
from streamlit.temporary_directory import TemporaryDirectory
|
|
222
223
|
|
|
223
224
|
with TemporaryDirectory() as temp_dir:
|
|
224
225
|
from urllib.parse import urlparse
|
|
225
226
|
|
|
226
|
-
|
|
227
|
+
url_subpath = urlparse(target).path
|
|
228
|
+
|
|
229
|
+
_check_extension_or_raise(url_subpath)
|
|
230
|
+
|
|
227
231
|
main_script_path = os.path.join(
|
|
228
|
-
temp_dir,
|
|
232
|
+
temp_dir, url_subpath.strip("/").rsplit("/", 1)[-1]
|
|
229
233
|
)
|
|
230
234
|
# if this is a GitHub/Gist blob url, convert to a raw URL first.
|
|
231
|
-
|
|
232
|
-
_download_remote(main_script_path,
|
|
235
|
+
url = url_util.process_gitblob_url(target)
|
|
236
|
+
_download_remote(main_script_path, url)
|
|
233
237
|
_main_run(main_script_path, args, flag_options=kwargs)
|
|
238
|
+
|
|
234
239
|
else:
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
240
|
+
path = Path(target)
|
|
241
|
+
|
|
242
|
+
if path.is_dir():
|
|
243
|
+
path /= "streamlit_app.py"
|
|
244
|
+
|
|
245
|
+
path_str = str(path)
|
|
246
|
+
_check_extension_or_raise(path_str)
|
|
247
|
+
|
|
248
|
+
if not path.exists():
|
|
249
|
+
raise click.BadParameter(f"File does not exist: {path}")
|
|
250
|
+
|
|
251
|
+
_main_run(path_str, args, flag_options=kwargs)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _check_extension_or_raise(path_str: str) -> None:
|
|
255
|
+
_, extension = os.path.splitext(path_str)
|
|
256
|
+
|
|
257
|
+
if extension[1:] not in ACCEPTED_FILE_EXTENSIONS:
|
|
258
|
+
if extension[1:] == "":
|
|
259
|
+
raise click.BadArgumentUsage(
|
|
260
|
+
"Streamlit requires raw Python (.py) files, but the provided file has no extension.\n"
|
|
261
|
+
"For more information, please see https://docs.streamlit.io"
|
|
262
|
+
)
|
|
263
|
+
raise click.BadArgumentUsage(
|
|
264
|
+
f"Streamlit requires raw Python (.py) files, not {extension}.\n"
|
|
265
|
+
"For more information, please see https://docs.streamlit.io"
|
|
266
|
+
)
|
|
238
267
|
|
|
239
268
|
|
|
240
269
|
def _get_command_line_as_string() -> str | None:
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Serve static assets for Custom Components v2.
|
|
16
|
+
|
|
17
|
+
This module defines a Tornado ``RequestHandler`` that serves static files for
|
|
18
|
+
Custom Components v2 from their registered component directories. Requests are
|
|
19
|
+
resolved safely within the component's root to avoid directory traversal and are
|
|
20
|
+
served with appropriate content type and cache headers. CORS headers are applied
|
|
21
|
+
based on the server configuration.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import os
|
|
27
|
+
from typing import TYPE_CHECKING, Final, cast
|
|
28
|
+
|
|
29
|
+
import tornado.web
|
|
30
|
+
|
|
31
|
+
import streamlit.web.server.routes
|
|
32
|
+
from streamlit.logger import get_logger
|
|
33
|
+
from streamlit.web.server.component_file_utils import (
|
|
34
|
+
build_safe_abspath,
|
|
35
|
+
guess_content_type,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
if TYPE_CHECKING:
|
|
39
|
+
from streamlit.components.v2.component_manager import BidiComponentManager
|
|
40
|
+
|
|
41
|
+
_LOGGER: Final = get_logger(__name__)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class BidiComponentRequestHandler(tornado.web.RequestHandler):
|
|
45
|
+
"""Request handler for serving Custom Components v2 static assets.
|
|
46
|
+
|
|
47
|
+
The handler resolves a requested path to a registered component's asset
|
|
48
|
+
within its component root, writes the file contents to the response, and
|
|
49
|
+
sets appropriate ``Content-Type`` and cache headers. If the component or
|
|
50
|
+
asset cannot be found, a suitable HTTP status is returned.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def initialize(self, component_manager: BidiComponentManager) -> None:
|
|
54
|
+
"""Initialize the handler with the given component manager.
|
|
55
|
+
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
component_manager : BidiComponentManager
|
|
59
|
+
Manager used to look up registered components and their root paths.
|
|
60
|
+
"""
|
|
61
|
+
self._component_manager = component_manager
|
|
62
|
+
|
|
63
|
+
def get(self, path: str) -> None:
|
|
64
|
+
"""Serve a component asset for the given URL path.
|
|
65
|
+
|
|
66
|
+
The first path segment is interpreted as the component name. The rest
|
|
67
|
+
of the path is resolved to a file within that component's root
|
|
68
|
+
directory. If the file exists and is readable, its bytes are written to
|
|
69
|
+
the response and the ``Content-Type`` header is set based on the file
|
|
70
|
+
type.
|
|
71
|
+
|
|
72
|
+
Parameters
|
|
73
|
+
----------
|
|
74
|
+
path : str
|
|
75
|
+
Request path in the form ``"<component_name>/<relative_file>"``.
|
|
76
|
+
|
|
77
|
+
Notes
|
|
78
|
+
-----
|
|
79
|
+
This method writes directly to the response and sets appropriate HTTP
|
|
80
|
+
status codes on error (``404`` for missing components/files, ``403`` for
|
|
81
|
+
forbidden paths).
|
|
82
|
+
"""
|
|
83
|
+
parts = path.split("/")
|
|
84
|
+
component_name = parts[0]
|
|
85
|
+
component_def = self._component_manager.get(component_name)
|
|
86
|
+
if component_def is None:
|
|
87
|
+
self.write("not found")
|
|
88
|
+
self.set_status(404)
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
# Get the component path from the component manager
|
|
92
|
+
component_path = self._component_manager.get_component_path(component_name)
|
|
93
|
+
if component_path is None:
|
|
94
|
+
self.write("not found")
|
|
95
|
+
self.set_status(404)
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
# Build a safe absolute path within the component root
|
|
99
|
+
filename = "/".join(parts[1:])
|
|
100
|
+
# If no file segment is provided (e.g. only component name or trailing slash),
|
|
101
|
+
# treat as not found rather than attempting to open a directory.
|
|
102
|
+
if not filename or filename.endswith("/"):
|
|
103
|
+
self.write("not found")
|
|
104
|
+
self.set_status(404)
|
|
105
|
+
return
|
|
106
|
+
abspath = build_safe_abspath(component_path, filename)
|
|
107
|
+
if abspath is None:
|
|
108
|
+
self.write("forbidden")
|
|
109
|
+
self.set_status(403)
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
# If the resolved path is a directory, return 404 not found.
|
|
113
|
+
if os.path.isdir(abspath):
|
|
114
|
+
self.write("not found")
|
|
115
|
+
self.set_status(404)
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
with open(abspath, "rb") as file:
|
|
120
|
+
contents = file.read()
|
|
121
|
+
except OSError:
|
|
122
|
+
sanitized_abspath = abspath.replace("\n", "").replace("\r", "")
|
|
123
|
+
_LOGGER.exception(
|
|
124
|
+
"BidiComponentRequestHandler: GET %s read error", sanitized_abspath
|
|
125
|
+
)
|
|
126
|
+
self.write("read error")
|
|
127
|
+
self.set_status(404)
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
self.write(contents)
|
|
131
|
+
self.set_header("Content-Type", guess_content_type(abspath))
|
|
132
|
+
|
|
133
|
+
self.set_extra_headers(path)
|
|
134
|
+
|
|
135
|
+
def set_extra_headers(self, path: str) -> None:
|
|
136
|
+
"""Disable cache for HTML files.
|
|
137
|
+
|
|
138
|
+
We assume other assets like JS and CSS are suffixed with their hash, so
|
|
139
|
+
they can be cached indefinitely.
|
|
140
|
+
"""
|
|
141
|
+
if path.endswith(".html"):
|
|
142
|
+
self.set_header("Cache-Control", "no-cache")
|
|
143
|
+
else:
|
|
144
|
+
self.set_header("Cache-Control", "public")
|
|
145
|
+
|
|
146
|
+
def set_default_headers(self) -> None:
|
|
147
|
+
"""Set default CORS headers based on server configuration.
|
|
148
|
+
|
|
149
|
+
If cross-origin requests are fully allowed, ``Access-Control-Allow-
|
|
150
|
+
Origin`` is set to ``"*"``. Otherwise, if the request ``Origin`` header
|
|
151
|
+
is an allowed origin, the header is echoed back.
|
|
152
|
+
"""
|
|
153
|
+
if streamlit.web.server.routes.allow_all_cross_origin_requests():
|
|
154
|
+
self.set_header("Access-Control-Allow-Origin", "*")
|
|
155
|
+
elif streamlit.web.server.routes.is_allowed_origin(
|
|
156
|
+
origin := self.request.headers.get("Origin")
|
|
157
|
+
):
|
|
158
|
+
self.set_header("Access-Control-Allow-Origin", cast("str", origin))
|
|
159
|
+
|
|
160
|
+
def options(self) -> None:
|
|
161
|
+
"""Handle preflight CORS requests.
|
|
162
|
+
|
|
163
|
+
Returns
|
|
164
|
+
-------
|
|
165
|
+
None
|
|
166
|
+
Responds with HTTP ``204 No Content`` to indicate that the actual
|
|
167
|
+
request can proceed.
|
|
168
|
+
"""
|
|
169
|
+
self.set_status(204)
|
|
170
|
+
self.finish()
|
|
171
|
+
|
|
172
|
+
@staticmethod
|
|
173
|
+
def get_url(file_id: str) -> str:
|
|
174
|
+
"""Return the URL for a component asset identified by ``file_id``.
|
|
175
|
+
|
|
176
|
+
Parameters
|
|
177
|
+
----------
|
|
178
|
+
file_id : str
|
|
179
|
+
Component file identifier (typically a relative path or hashed
|
|
180
|
+
filename).
|
|
181
|
+
|
|
182
|
+
Returns
|
|
183
|
+
-------
|
|
184
|
+
str
|
|
185
|
+
Relative URL path for the resource, to be joined with the server
|
|
186
|
+
base URL.
|
|
187
|
+
|
|
188
|
+
Examples
|
|
189
|
+
--------
|
|
190
|
+
>>> BidiComponentRequestHandler.get_url("my_component/main.js")
|
|
191
|
+
'_stcore/bidi-components/my_component/main.js'
|
|
192
|
+
"""
|
|
193
|
+
return f"_stcore/bidi-components/{file_id}"
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
"""Utilities for safely resolving component asset paths and content types.
|
|
17
|
+
|
|
18
|
+
These helpers are used by server handlers to construct safe absolute paths for
|
|
19
|
+
component files and to determine the appropriate ``Content-Type`` header for
|
|
20
|
+
responses. Path resolution prevents directory traversal by normalizing and
|
|
21
|
+
checking real paths against a component's root directory.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import mimetypes
|
|
27
|
+
import os
|
|
28
|
+
from typing import Final
|
|
29
|
+
|
|
30
|
+
_OCTET_STREAM: Final[str] = "application/octet-stream"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def build_safe_abspath(component_root: str, relative_url_path: str) -> str | None:
|
|
34
|
+
"""Build an absolute path inside ``component_root`` if safe.
|
|
35
|
+
|
|
36
|
+
The function joins ``relative_url_path`` with ``component_root`` and
|
|
37
|
+
normalizes and resolves symlinks. If the resulting path escapes the
|
|
38
|
+
component root, ``None`` is returned to indicate a forbidden traversal.
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
component_root : str
|
|
43
|
+
Absolute path to the component's root directory.
|
|
44
|
+
relative_url_path : str
|
|
45
|
+
Relative URL path from the component root to the requested file.
|
|
46
|
+
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
str or None
|
|
50
|
+
The resolved absolute path if it stays within ``component_root``;
|
|
51
|
+
otherwise ``None`` when the path would traverse outside the root.
|
|
52
|
+
"""
|
|
53
|
+
root_real = os.path.realpath(component_root)
|
|
54
|
+
candidate = os.path.normpath(os.path.join(root_real, relative_url_path))
|
|
55
|
+
candidate_real = os.path.realpath(candidate)
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
# Ensure the candidate stays within the real component root
|
|
59
|
+
if os.path.commonpath([root_real, candidate_real]) != root_real:
|
|
60
|
+
return None
|
|
61
|
+
except ValueError:
|
|
62
|
+
# On some platforms, commonpath can raise if drives differ; treat as forbidden.
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
return candidate_real
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def guess_content_type(abspath: str) -> str:
|
|
69
|
+
"""Guess the HTTP ``Content-Type`` for a file path.
|
|
70
|
+
|
|
71
|
+
This logic mirrors Tornado's ``StaticFileHandler`` by respecting encoding
|
|
72
|
+
metadata from ``mimetypes.guess_type`` and falling back to
|
|
73
|
+
``application/octet-stream`` when no specific type can be determined.
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
abspath : str
|
|
78
|
+
Absolute file path used for type detection (only the suffix matters).
|
|
79
|
+
|
|
80
|
+
Returns
|
|
81
|
+
-------
|
|
82
|
+
str
|
|
83
|
+
Guessed content type string suitable for the ``Content-Type`` header.
|
|
84
|
+
"""
|
|
85
|
+
mime_type, encoding = mimetypes.guess_type(abspath)
|
|
86
|
+
# per RFC 6713, use the appropriate type for a gzip compressed file
|
|
87
|
+
if encoding == "gzip":
|
|
88
|
+
return "application/gzip"
|
|
89
|
+
# As of 2015-07-21 there is no bzip2 encoding defined at
|
|
90
|
+
# http://www.iana.org/assignments/media-types/media-types.xhtml
|
|
91
|
+
# So for that (and any other encoding), use octet-stream.
|
|
92
|
+
if encoding is not None:
|
|
93
|
+
return _OCTET_STREAM
|
|
94
|
+
if mime_type is not None:
|
|
95
|
+
return mime_type
|
|
96
|
+
# if mime_type not detected, use application/octet-stream
|
|
97
|
+
return _OCTET_STREAM
|
|
@@ -14,14 +14,16 @@
|
|
|
14
14
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
|
-
import mimetypes
|
|
18
|
-
import os
|
|
19
17
|
from typing import TYPE_CHECKING, Final, cast
|
|
20
18
|
|
|
21
19
|
import tornado.web
|
|
22
20
|
|
|
23
21
|
import streamlit.web.server.routes
|
|
24
22
|
from streamlit.logger import get_logger
|
|
23
|
+
from streamlit.web.server.component_file_utils import (
|
|
24
|
+
build_safe_abspath,
|
|
25
|
+
guess_content_type,
|
|
26
|
+
)
|
|
25
27
|
|
|
26
28
|
if TYPE_CHECKING:
|
|
27
29
|
from streamlit.components.types.base_component_registry import BaseComponentRegistry
|
|
@@ -42,13 +44,10 @@ class ComponentRequestHandler(tornado.web.RequestHandler):
|
|
|
42
44
|
self.set_status(404)
|
|
43
45
|
return
|
|
44
46
|
|
|
45
|
-
#
|
|
46
|
-
component_root = os.path.realpath(component_root)
|
|
47
|
+
# Build a safe absolute path within the component root
|
|
47
48
|
filename = "/".join(parts[1:])
|
|
48
|
-
abspath =
|
|
49
|
-
|
|
50
|
-
# Do NOT expose anything outside of the component root.
|
|
51
|
-
if os.path.commonpath([component_root, abspath]) != component_root:
|
|
49
|
+
abspath = build_safe_abspath(component_root, filename)
|
|
50
|
+
if abspath is None:
|
|
52
51
|
self.write("forbidden")
|
|
53
52
|
self.set_status(403)
|
|
54
53
|
return
|
|
@@ -100,19 +99,7 @@ class ComponentRequestHandler(tornado.web.RequestHandler):
|
|
|
100
99
|
"""Returns the ``Content-Type`` header to be used for this request.
|
|
101
100
|
From tornado.web.StaticFileHandler.
|
|
102
101
|
"""
|
|
103
|
-
|
|
104
|
-
# per RFC 6713, use the appropriate type for a gzip compressed file
|
|
105
|
-
if encoding == "gzip":
|
|
106
|
-
return "application/gzip"
|
|
107
|
-
# As of 2015-07-21 there is no bzip2 encoding defined at
|
|
108
|
-
# http://www.iana.org/assignments/media-types/media-types.xhtml
|
|
109
|
-
# So for that (and any other encoding), use octet-stream.
|
|
110
|
-
if encoding is not None:
|
|
111
|
-
return "application/octet-stream"
|
|
112
|
-
if mime_type is not None:
|
|
113
|
-
return mime_type
|
|
114
|
-
# if mime_type not detected, use application/octet-stream
|
|
115
|
-
return "application/octet-stream"
|
|
102
|
+
return guess_content_type(abspath)
|
|
116
103
|
|
|
117
104
|
@staticmethod
|
|
118
105
|
def get_url(file_id: str) -> str:
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
|
-
from typing import TYPE_CHECKING, Any,
|
|
19
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
20
20
|
|
|
21
21
|
from authlib.integrations.base_client import (
|
|
22
22
|
BaseApp,
|
|
@@ -32,6 +32,8 @@ from authlib.integrations.requests_client import (
|
|
|
32
32
|
from streamlit.web.server.authlib_tornado_integration import TornadoIntegration
|
|
33
33
|
|
|
34
34
|
if TYPE_CHECKING:
|
|
35
|
+
from collections.abc import Callable
|
|
36
|
+
|
|
35
37
|
import tornado.web
|
|
36
38
|
|
|
37
39
|
from streamlit.auth_util import AuthCache
|
streamlit/web/server/routes.py
CHANGED
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
17
|
import os
|
|
18
|
-
|
|
18
|
+
import re
|
|
19
|
+
from typing import TYPE_CHECKING, Any, Final, cast
|
|
19
20
|
|
|
20
21
|
import tornado.web
|
|
21
22
|
|
|
@@ -27,7 +28,14 @@ from streamlit.web.server.server_util import (
|
|
|
27
28
|
)
|
|
28
29
|
|
|
29
30
|
if TYPE_CHECKING:
|
|
30
|
-
from collections.abc import Awaitable, Sequence
|
|
31
|
+
from collections.abc import Awaitable, Callable, Sequence
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# Files that match this pattern do not get cached.
|
|
35
|
+
NO_CACHE_PATTERN = re.compile(r"(?:\.html$|^manifest\.json$)")
|
|
36
|
+
|
|
37
|
+
# The max-age value to send with cached assets. Set to one year.
|
|
38
|
+
STATIC_ASSET_CACHE_MAX_AGE_SECONDS: Final = 365 * 24 * 60 * 60
|
|
31
39
|
|
|
32
40
|
|
|
33
41
|
def allow_all_cross_origin_requests() -> bool:
|
|
@@ -62,16 +70,21 @@ class StaticFileHandler(tornado.web.StaticFileHandler):
|
|
|
62
70
|
super().initialize(path, default_filename)
|
|
63
71
|
|
|
64
72
|
def set_extra_headers(self, path: str) -> None:
|
|
65
|
-
"""Disable cache for HTML files.
|
|
73
|
+
"""Disable cache for HTML files and manifest.json.
|
|
66
74
|
|
|
67
75
|
Other assets like JS and CSS are suffixed with their hash, so they can
|
|
68
76
|
be cached indefinitely.
|
|
69
77
|
"""
|
|
78
|
+
|
|
70
79
|
is_index_url = len(path) == 0
|
|
71
|
-
if is_index_url or
|
|
80
|
+
if is_index_url or NO_CACHE_PATTERN.search(path):
|
|
72
81
|
self.set_header("Cache-Control", "no-cache")
|
|
73
82
|
else:
|
|
74
|
-
|
|
83
|
+
# For all other static files suffixed with their hash, we set a long cache time.
|
|
84
|
+
self.set_header(
|
|
85
|
+
"Cache-Control",
|
|
86
|
+
f"public, immutable, max-age={STATIC_ASSET_CACHE_MAX_AGE_SECONDS}",
|
|
87
|
+
)
|
|
75
88
|
|
|
76
89
|
def validate_absolute_path(self, root: str, absolute_path: str) -> str | None:
|
|
77
90
|
try:
|
streamlit/web/server/server.py
CHANGED
|
@@ -38,6 +38,9 @@ from streamlit.web.cache_storage_manager_config import (
|
|
|
38
38
|
create_default_cache_storage_manager,
|
|
39
39
|
)
|
|
40
40
|
from streamlit.web.server.app_static_file_handler import AppStaticFileHandler
|
|
41
|
+
from streamlit.web.server.bidi_component_request_handler import (
|
|
42
|
+
BidiComponentRequestHandler,
|
|
43
|
+
)
|
|
41
44
|
from streamlit.web.server.browser_websocket_handler import BrowserWebSocketHandler
|
|
42
45
|
from streamlit.web.server.component_request_handler import ComponentRequestHandler
|
|
43
46
|
from streamlit.web.server.media_file_handler import MediaFileHandler
|
|
@@ -133,6 +136,7 @@ UNIX_SOCKET_PREFIX: Final = "unix://"
|
|
|
133
136
|
# as the endpoints in frontend/connection/src/DefaultStreamlitEndpoints
|
|
134
137
|
MEDIA_ENDPOINT: Final = "/media"
|
|
135
138
|
COMPONENT_ENDPOINT: Final = "/component"
|
|
139
|
+
BIDI_COMPONENT_ENDPOINT: Final = "/_stcore/bidi-components"
|
|
136
140
|
STATIC_SERVING_ENDPOINT: Final = "/app/static"
|
|
137
141
|
UPLOAD_FILE_ENDPOINT: Final = "/_stcore/upload_file"
|
|
138
142
|
STREAM_ENDPOINT: Final = r"_stcore/stream"
|
|
@@ -318,6 +322,7 @@ class Server:
|
|
|
318
322
|
"""Ensures that common mime-types are robust against system misconfiguration."""
|
|
319
323
|
mimetypes.add_type("text/html", ".html")
|
|
320
324
|
mimetypes.add_type("application/javascript", ".js")
|
|
325
|
+
mimetypes.add_type("application/javascript", ".mjs")
|
|
321
326
|
mimetypes.add_type("text/css", ".css")
|
|
322
327
|
mimetypes.add_type("image/webp", ".webp")
|
|
323
328
|
|
|
@@ -394,6 +399,11 @@ class Server:
|
|
|
394
399
|
ComponentRequestHandler,
|
|
395
400
|
{"registry": self._runtime.component_registry},
|
|
396
401
|
),
|
|
402
|
+
(
|
|
403
|
+
make_url_path_regex(base, f"{BIDI_COMPONENT_ENDPOINT}/(.*)"),
|
|
404
|
+
BidiComponentRequestHandler,
|
|
405
|
+
{"component_manager": self._runtime.bidi_component_registry},
|
|
406
|
+
),
|
|
397
407
|
]
|
|
398
408
|
|
|
399
409
|
if config.get_option("server.scriptHealthCheckEnabled"):
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
|
-
from typing import TYPE_CHECKING,
|
|
19
|
+
from typing import TYPE_CHECKING, Final, Literal, cast
|
|
20
20
|
from urllib.parse import urljoin
|
|
21
21
|
|
|
22
22
|
from streamlit import config, net_util, url_util
|
|
@@ -24,6 +24,8 @@ from streamlit.runtime.secrets import secrets_singleton
|
|
|
24
24
|
from streamlit.type_util import is_version_less_than
|
|
25
25
|
|
|
26
26
|
if TYPE_CHECKING:
|
|
27
|
+
from collections.abc import Callable
|
|
28
|
+
|
|
27
29
|
from tornado.web import RequestHandler
|
|
28
30
|
|
|
29
31
|
# The port used for internal development.
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
|
-
from typing import TYPE_CHECKING, Any,
|
|
17
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
18
18
|
|
|
19
19
|
import tornado.httputil
|
|
20
20
|
import tornado.web
|
|
@@ -25,6 +25,8 @@ from streamlit.web.server import routes, server_util
|
|
|
25
25
|
from streamlit.web.server.server_util import is_xsrf_enabled
|
|
26
26
|
|
|
27
27
|
if TYPE_CHECKING:
|
|
28
|
+
from collections.abc import Callable
|
|
29
|
+
|
|
28
30
|
from streamlit.runtime.memory_uploaded_file_manager import MemoryUploadedFileManager
|
|
29
31
|
|
|
30
32
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: streamlit
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.51.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
|
|
@@ -18,7 +18,6 @@ Classifier: Environment :: Web Environment
|
|
|
18
18
|
Classifier: Intended Audience :: Developers
|
|
19
19
|
Classifier: Intended Audience :: Science/Research
|
|
20
20
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
21
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.10
|
|
23
22
|
Classifier: Programming Language :: Python :: 3.11
|
|
24
23
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -29,7 +28,7 @@ Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
|
29
28
|
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
30
29
|
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
31
30
|
Classifier: Topic :: Software Development :: Widget Sets
|
|
32
|
-
Requires-Python: >=3.
|
|
31
|
+
Requires-Python: >=3.10
|
|
33
32
|
Description-Content-Type: text/markdown
|
|
34
33
|
Requires-Dist: altair!=5.4.0,!=5.4.1,<6,>=4.0
|
|
35
34
|
Requires-Dist: blinker<2,>=1.5.0
|
|
@@ -38,9 +37,9 @@ Requires-Dist: click<9,>=7.0
|
|
|
38
37
|
Requires-Dist: numpy<3,>=1.23
|
|
39
38
|
Requires-Dist: packaging<26,>=20
|
|
40
39
|
Requires-Dist: pandas<3,>=1.4.0
|
|
41
|
-
Requires-Dist: pillow<
|
|
40
|
+
Requires-Dist: pillow<13,>=7.1.0
|
|
42
41
|
Requires-Dist: protobuf<7,>=3.20
|
|
43
|
-
Requires-Dist: pyarrow
|
|
42
|
+
Requires-Dist: pyarrow<22,>=7.0
|
|
44
43
|
Requires-Dist: requests<3,>=2.27
|
|
45
44
|
Requires-Dist: tenacity<10,>=8.1.0
|
|
46
45
|
Requires-Dist: toml<2,>=0.10.1
|