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
|
@@ -19,16 +19,7 @@ from __future__ import annotations
|
|
|
19
19
|
from dataclasses import dataclass
|
|
20
20
|
from datetime import date
|
|
21
21
|
from enum import Enum
|
|
22
|
-
from typing import
|
|
23
|
-
TYPE_CHECKING,
|
|
24
|
-
Any,
|
|
25
|
-
Final,
|
|
26
|
-
Literal,
|
|
27
|
-
TypedDict,
|
|
28
|
-
cast,
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
from typing_extensions import TypeAlias
|
|
22
|
+
from typing import TYPE_CHECKING, Any, Final, Literal, TypeAlias, TypedDict, cast
|
|
32
23
|
|
|
33
24
|
from streamlit import dataframe_util, type_util
|
|
34
25
|
from streamlit.elements.lib.color_util import (
|
|
@@ -47,6 +38,10 @@ if TYPE_CHECKING:
|
|
|
47
38
|
import pandas as pd
|
|
48
39
|
|
|
49
40
|
from streamlit.dataframe_util import Data
|
|
41
|
+
from streamlit.elements.lib.layout_utils import (
|
|
42
|
+
Height,
|
|
43
|
+
Width,
|
|
44
|
+
)
|
|
50
45
|
|
|
51
46
|
VegaLiteType: TypeAlias = Literal["quantitative", "ordinal", "temporal", "nominal"]
|
|
52
47
|
ChartStackType: TypeAlias = Literal["normalize", "center", "layered"]
|
|
@@ -59,6 +54,7 @@ class PrepDataColumns(TypedDict):
|
|
|
59
54
|
y_column_list: list[str]
|
|
60
55
|
color_column: str | None
|
|
61
56
|
size_column: str | None
|
|
57
|
+
sort_column: str | None
|
|
62
58
|
|
|
63
59
|
|
|
64
60
|
@dataclass
|
|
@@ -73,13 +69,14 @@ class AddRowsMetadata:
|
|
|
73
69
|
columns: PrepDataColumns
|
|
74
70
|
# Chart styling properties
|
|
75
71
|
color: str | Color | list[Color] | None = None
|
|
76
|
-
width:
|
|
77
|
-
height:
|
|
78
|
-
use_container_width: bool =
|
|
72
|
+
width: Width | None = None
|
|
73
|
+
height: Height | None = None
|
|
74
|
+
use_container_width: bool | None = None
|
|
79
75
|
# Only applicable for bar & area charts
|
|
80
76
|
stack: bool | ChartStackType | None = None
|
|
81
77
|
# Only applicable for bar charts
|
|
82
78
|
horizontal: bool = False
|
|
79
|
+
sort: bool | str = False
|
|
83
80
|
|
|
84
81
|
|
|
85
82
|
class ChartType(Enum):
|
|
@@ -151,13 +148,14 @@ def generate_chart(
|
|
|
151
148
|
y_axis_label: str | None = None,
|
|
152
149
|
color_from_user: str | Color | list[Color] | None = None,
|
|
153
150
|
size_from_user: str | float | None = None,
|
|
154
|
-
width:
|
|
155
|
-
height:
|
|
156
|
-
use_container_width: bool =
|
|
151
|
+
width: Width | None = None,
|
|
152
|
+
height: Height | None = None,
|
|
153
|
+
use_container_width: bool | None = None,
|
|
157
154
|
# Bar & Area charts only:
|
|
158
155
|
stack: bool | ChartStackType | None = None,
|
|
159
156
|
# Bar charts only:
|
|
160
157
|
horizontal: bool = False,
|
|
158
|
+
sort_from_user: bool | str = False,
|
|
161
159
|
) -> tuple[alt.Chart | alt.LayerChart, AddRowsMetadata]:
|
|
162
160
|
"""Function to use the chart's type, data columns and indices to figure out the
|
|
163
161
|
chart's spec.
|
|
@@ -181,6 +179,8 @@ def generate_chart(
|
|
|
181
179
|
# Get name of column to use for size, or constant value to use. Any/both could
|
|
182
180
|
# be None.
|
|
183
181
|
size_column, size_value = _parse_generic_column(df, size_from_user)
|
|
182
|
+
# Get name of column to use for sort.
|
|
183
|
+
sort_column = _parse_sort_column(df, sort_from_user)
|
|
184
184
|
|
|
185
185
|
# Store some info so we can use it in add_rows.
|
|
186
186
|
add_rows_metadata = AddRowsMetadata(
|
|
@@ -194,6 +194,7 @@ def generate_chart(
|
|
|
194
194
|
"y_column_list": y_column_list,
|
|
195
195
|
"color_column": color_column,
|
|
196
196
|
"size_column": size_column,
|
|
197
|
+
"sort_column": sort_column,
|
|
197
198
|
},
|
|
198
199
|
# Chart styling properties
|
|
199
200
|
color=color_from_user,
|
|
@@ -202,13 +203,13 @@ def generate_chart(
|
|
|
202
203
|
use_container_width=use_container_width,
|
|
203
204
|
stack=stack,
|
|
204
205
|
horizontal=horizontal,
|
|
206
|
+
sort=sort_from_user,
|
|
205
207
|
)
|
|
206
208
|
|
|
207
209
|
# At this point, all foo_column variables are either None/empty or contain actual
|
|
208
210
|
# columns that are guaranteed to exist.
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
df, x_column, y_column_list, color_column, size_column
|
|
211
|
+
df, x_column, y_column, color_column, size_column, sort_column = _prep_data(
|
|
212
|
+
df, x_column, y_column_list, color_column, size_column, sort_column
|
|
212
213
|
)
|
|
213
214
|
|
|
214
215
|
# At this point, x_column is only None if user did not provide one AND df is empty.
|
|
@@ -224,14 +225,18 @@ def generate_chart(
|
|
|
224
225
|
x_axis_label,
|
|
225
226
|
y_axis_label,
|
|
226
227
|
stack,
|
|
228
|
+
sort_from_user,
|
|
227
229
|
)
|
|
228
230
|
|
|
231
|
+
chart_width = width if isinstance(width, int) else None
|
|
232
|
+
chart_height = height if isinstance(height, int) else None
|
|
233
|
+
|
|
229
234
|
# Create a Chart with x and y encodings.
|
|
230
235
|
chart = alt.Chart(
|
|
231
236
|
data=df,
|
|
232
237
|
mark=chart_type.value["mark_type"],
|
|
233
|
-
width=
|
|
234
|
-
height=
|
|
238
|
+
width=chart_width or 0,
|
|
239
|
+
height=chart_height or 0,
|
|
235
240
|
).encode(
|
|
236
241
|
x=x_encoding,
|
|
237
242
|
y=y_encoding,
|
|
@@ -281,7 +286,7 @@ def generate_chart(
|
|
|
281
286
|
and is_altair_version_5_or_greater
|
|
282
287
|
):
|
|
283
288
|
return _add_improved_hover_tooltips(
|
|
284
|
-
chart, x_column,
|
|
289
|
+
chart, x_column, chart_width, chart_height
|
|
285
290
|
).interactive(), add_rows_metadata
|
|
286
291
|
|
|
287
292
|
return chart.interactive(), add_rows_metadata
|
|
@@ -359,6 +364,7 @@ def prep_chart_data_for_add_rows(
|
|
|
359
364
|
y_column_list=add_rows_metadata.columns["y_column_list"],
|
|
360
365
|
color_column=add_rows_metadata.columns["color_column"],
|
|
361
366
|
size_column=add_rows_metadata.columns["size_column"],
|
|
367
|
+
sort_column=add_rows_metadata.columns["sort_column"],
|
|
362
368
|
)
|
|
363
369
|
|
|
364
370
|
return out_data, add_rows_metadata
|
|
@@ -437,7 +443,8 @@ def _prep_data(
|
|
|
437
443
|
y_column_list: list[str],
|
|
438
444
|
color_column: str | None,
|
|
439
445
|
size_column: str | None,
|
|
440
|
-
|
|
446
|
+
sort_column: str | None = None,
|
|
447
|
+
) -> tuple[pd.DataFrame, str | None, str | None, str | None, str | None, str | None]:
|
|
441
448
|
"""Prepares the data for charting. This is also used in add_rows.
|
|
442
449
|
|
|
443
450
|
Returns the prepared dataframe and the new names of the x column (taking the index
|
|
@@ -450,7 +457,7 @@ def _prep_data(
|
|
|
450
457
|
|
|
451
458
|
# Drop columns we're not using.
|
|
452
459
|
selected_data = _drop_unused_columns(
|
|
453
|
-
df, x_column, color_column, size_column, *y_column_list
|
|
460
|
+
df, x_column, color_column, size_column, sort_column, *y_column_list
|
|
454
461
|
)
|
|
455
462
|
|
|
456
463
|
# Maybe convert color to Vega colors.
|
|
@@ -462,17 +469,18 @@ def _prep_data(
|
|
|
462
469
|
y_column_list,
|
|
463
470
|
color_column,
|
|
464
471
|
size_column,
|
|
472
|
+
sort_column,
|
|
465
473
|
) = _convert_col_names_to_str_in_place(
|
|
466
|
-
selected_data, x_column, y_column_list, color_column, size_column
|
|
474
|
+
selected_data, x_column, y_column_list, color_column, size_column, sort_column
|
|
467
475
|
)
|
|
468
476
|
|
|
469
477
|
# Maybe melt data from wide format into long format.
|
|
470
478
|
melted_data, y_column, color_column = _maybe_melt(
|
|
471
|
-
selected_data, x_column, y_column_list, color_column, size_column
|
|
479
|
+
selected_data, x_column, y_column_list, color_column, size_column, sort_column
|
|
472
480
|
)
|
|
473
481
|
|
|
474
482
|
# Return the data, but also the new names to use for x, y, and color.
|
|
475
|
-
return melted_data, x_column, y_column, color_column, size_column
|
|
483
|
+
return melted_data, x_column, y_column, color_column, size_column, sort_column
|
|
476
484
|
|
|
477
485
|
|
|
478
486
|
def _last_index_for_melted_dataframes(
|
|
@@ -505,7 +513,7 @@ def _is_date_column(df: pd.DataFrame, name: str | None) -> bool:
|
|
|
505
513
|
if column.size == 0:
|
|
506
514
|
return False
|
|
507
515
|
|
|
508
|
-
return isinstance(column.
|
|
516
|
+
return isinstance(column.iat[0], date)
|
|
509
517
|
|
|
510
518
|
|
|
511
519
|
def _melt_data(
|
|
@@ -636,7 +644,7 @@ def _maybe_convert_color_column_in_place(
|
|
|
636
644
|
if color_column is None or len(df[color_column]) == 0:
|
|
637
645
|
return
|
|
638
646
|
|
|
639
|
-
first_color_datum = df[color_column].
|
|
647
|
+
first_color_datum = df[color_column].iat[0]
|
|
640
648
|
|
|
641
649
|
if is_hex_color_like(first_color_datum):
|
|
642
650
|
# Hex is already CSS-valid.
|
|
@@ -657,7 +665,8 @@ def _convert_col_names_to_str_in_place(
|
|
|
657
665
|
y_column_list: list[str],
|
|
658
666
|
color_column: str | None,
|
|
659
667
|
size_column: str | None,
|
|
660
|
-
|
|
668
|
+
sort_column: str | None,
|
|
669
|
+
) -> tuple[str | None, list[str], str | None, str | None, str | None]:
|
|
661
670
|
"""Converts column names to strings, since Vega-Lite does not accept ints, etc."""
|
|
662
671
|
import pandas as pd
|
|
663
672
|
|
|
@@ -670,6 +679,7 @@ def _convert_col_names_to_str_in_place(
|
|
|
670
679
|
[str(c) for c in y_column_list],
|
|
671
680
|
None if color_column is None else str(color_column),
|
|
672
681
|
None if size_column is None else str(size_column),
|
|
682
|
+
None if sort_column is None else str(sort_column),
|
|
673
683
|
)
|
|
674
684
|
|
|
675
685
|
|
|
@@ -703,6 +713,17 @@ def _parse_x_column(df: pd.DataFrame, x_from_user: str | None) -> str | None:
|
|
|
703
713
|
)
|
|
704
714
|
|
|
705
715
|
|
|
716
|
+
def _parse_sort_column(df: pd.DataFrame, sort_from_user: bool | str) -> str | None:
|
|
717
|
+
if sort_from_user is False or sort_from_user is True:
|
|
718
|
+
return None
|
|
719
|
+
|
|
720
|
+
sort_column = sort_from_user.removeprefix("-")
|
|
721
|
+
if sort_column not in df.columns:
|
|
722
|
+
raise StreamlitColumnNotFoundError(df, sort_column)
|
|
723
|
+
|
|
724
|
+
return sort_column
|
|
725
|
+
|
|
726
|
+
|
|
706
727
|
def _parse_y_columns(
|
|
707
728
|
df: pd.DataFrame,
|
|
708
729
|
y_from_user: str | Sequence[str] | None,
|
|
@@ -790,6 +811,7 @@ def _maybe_melt(
|
|
|
790
811
|
y_column_list: list[str],
|
|
791
812
|
color_column: str | None,
|
|
792
813
|
size_column: str | None,
|
|
814
|
+
sort_column: str | None,
|
|
793
815
|
) -> tuple[pd.DataFrame, str | None, str | None]:
|
|
794
816
|
"""If multiple columns are set for y, melt the dataframe into long format."""
|
|
795
817
|
y_column: str | None
|
|
@@ -806,6 +828,8 @@ def _maybe_melt(
|
|
|
806
828
|
columns_to_leave_alone = [x_column]
|
|
807
829
|
if size_column:
|
|
808
830
|
columns_to_leave_alone.append(size_column)
|
|
831
|
+
if sort_column:
|
|
832
|
+
columns_to_leave_alone.append(sort_column)
|
|
809
833
|
|
|
810
834
|
df = _melt_data(
|
|
811
835
|
df=df,
|
|
@@ -828,8 +852,10 @@ def _get_axis_encodings(
|
|
|
828
852
|
x_axis_label: str | None,
|
|
829
853
|
y_axis_label: str | None,
|
|
830
854
|
stack: bool | ChartStackType | None,
|
|
855
|
+
sort_from_user: bool | str,
|
|
831
856
|
) -> tuple[alt.X, alt.Y]:
|
|
832
857
|
stack_encoding: alt.X | alt.Y
|
|
858
|
+
sort_encoding: alt.X | alt.Y
|
|
833
859
|
if chart_type == ChartType.HORIZONTAL_BAR:
|
|
834
860
|
# Handle horizontal bar chart - switches x and y data:
|
|
835
861
|
x_encoding = _get_x_encoding(
|
|
@@ -839,6 +865,7 @@ def _get_axis_encodings(
|
|
|
839
865
|
df, x_column, x_from_user, y_axis_label, chart_type
|
|
840
866
|
)
|
|
841
867
|
stack_encoding = x_encoding
|
|
868
|
+
sort_encoding = y_encoding
|
|
842
869
|
else:
|
|
843
870
|
x_encoding = _get_x_encoding(
|
|
844
871
|
df, x_column, x_from_user, x_axis_label, chart_type
|
|
@@ -847,10 +874,15 @@ def _get_axis_encodings(
|
|
|
847
874
|
df, y_column, y_from_user, y_axis_label, chart_type
|
|
848
875
|
)
|
|
849
876
|
stack_encoding = y_encoding
|
|
877
|
+
sort_encoding = x_encoding
|
|
850
878
|
|
|
851
879
|
# Handle stacking - only relevant for bar & area charts
|
|
852
880
|
_update_encoding_with_stack(stack, stack_encoding)
|
|
853
881
|
|
|
882
|
+
# Handle sorting - only relevant for bar charts
|
|
883
|
+
if chart_type in (ChartType.VERTICAL_BAR, ChartType.HORIZONTAL_BAR):
|
|
884
|
+
_update_encoding_with_sort(sort_from_user, sort_encoding)
|
|
885
|
+
|
|
854
886
|
return x_encoding, y_encoding
|
|
855
887
|
|
|
856
888
|
|
|
@@ -957,6 +989,38 @@ def _update_encoding_with_stack(
|
|
|
957
989
|
encoding["stack"] = stack
|
|
958
990
|
|
|
959
991
|
|
|
992
|
+
def _update_encoding_with_sort(
|
|
993
|
+
sort_from_user: bool | str,
|
|
994
|
+
encoding: alt.X | alt.Y,
|
|
995
|
+
) -> None:
|
|
996
|
+
"""Apply sort to the given encoding in-place.
|
|
997
|
+
|
|
998
|
+
- If sort is False: disable Altair's default sorting on the bar's categorical axis
|
|
999
|
+
(i.e., set to None).
|
|
1000
|
+
- If sort is True: use Altair's default sorting.
|
|
1001
|
+
- If sort is a column name (optionally starting with '-') set a SortField with the correct order.
|
|
1002
|
+
|
|
1003
|
+
Note: Column validation should be done before calling this function.
|
|
1004
|
+
"""
|
|
1005
|
+
import altair as alt
|
|
1006
|
+
|
|
1007
|
+
if sort_from_user is False:
|
|
1008
|
+
# Disable Altair's default sorting
|
|
1009
|
+
encoding["sort"] = None
|
|
1010
|
+
elif sort_from_user is True:
|
|
1011
|
+
# Use Altair's default sorting
|
|
1012
|
+
pass
|
|
1013
|
+
else:
|
|
1014
|
+
# String: sort by column name (optional '-' prefix for descending)
|
|
1015
|
+
sort_order: Literal["ascending", "descending"]
|
|
1016
|
+
if sort_from_user.startswith("-"):
|
|
1017
|
+
sort_order = "descending"
|
|
1018
|
+
else:
|
|
1019
|
+
sort_order = "ascending"
|
|
1020
|
+
sort_field = sort_from_user.removeprefix("-")
|
|
1021
|
+
encoding["sort"] = alt.SortField(field=sort_field, order=sort_order)
|
|
1022
|
+
|
|
1023
|
+
|
|
960
1024
|
def _get_color_encoding(
|
|
961
1025
|
df: pd.DataFrame,
|
|
962
1026
|
color_value: Color | None,
|
|
@@ -1022,7 +1086,7 @@ def _get_color_encoding(
|
|
|
1022
1086
|
|
|
1023
1087
|
# If the 0th element in the color column looks like a color, we'll use the color
|
|
1024
1088
|
# column's values as the colors in our chart.
|
|
1025
|
-
elif len(df[color_column]) and is_color_like(df[color_column].
|
|
1089
|
+
elif len(df[color_column]) and is_color_like(df[color_column].iat[0]):
|
|
1026
1090
|
color_range = [to_css_color(c) for c in df[color_column].unique()]
|
|
1027
1091
|
color_enc["scale"] = alt.Scale(range=color_range)
|
|
1028
1092
|
# Don't show the color legend, because it will just show text with the
|
|
@@ -14,10 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
|
-
from collections.abc import Collection
|
|
18
|
-
from typing import Any,
|
|
19
|
-
|
|
20
|
-
from typing_extensions import TypeAlias
|
|
17
|
+
from collections.abc import Callable, Collection
|
|
18
|
+
from typing import Any, TypeAlias, cast
|
|
21
19
|
|
|
22
20
|
from streamlit.errors import StreamlitInvalidColorError
|
|
23
21
|
|
|
@@ -35,26 +33,18 @@ IntRGBAColorTuple: TypeAlias = tuple[int, int, int, int]
|
|
|
35
33
|
# CSS uses these.
|
|
36
34
|
MixedRGBAColorTuple: TypeAlias = tuple[int, int, int, float]
|
|
37
35
|
|
|
38
|
-
Color4Tuple: TypeAlias =
|
|
39
|
-
FloatRGBAColorTuple,
|
|
40
|
-
IntRGBAColorTuple,
|
|
41
|
-
MixedRGBAColorTuple,
|
|
42
|
-
]
|
|
36
|
+
Color4Tuple: TypeAlias = FloatRGBAColorTuple | IntRGBAColorTuple | MixedRGBAColorTuple
|
|
43
37
|
|
|
44
|
-
Color3Tuple: TypeAlias =
|
|
45
|
-
FloatRGBColorTuple,
|
|
46
|
-
IntRGBColorTuple,
|
|
47
|
-
]
|
|
38
|
+
Color3Tuple: TypeAlias = FloatRGBColorTuple | IntRGBColorTuple
|
|
48
39
|
|
|
49
|
-
ColorTuple: TypeAlias =
|
|
40
|
+
ColorTuple: TypeAlias = Color4Tuple | Color3Tuple
|
|
50
41
|
|
|
51
|
-
IntColorTuple =
|
|
52
|
-
CSSColorStr = Union[IntRGBAColorTuple, MixedRGBAColorTuple]
|
|
42
|
+
IntColorTuple: TypeAlias = IntRGBColorTuple | IntRGBAColorTuple
|
|
53
43
|
|
|
54
44
|
ColorStr: TypeAlias = str
|
|
55
45
|
|
|
56
|
-
Color: TypeAlias =
|
|
57
|
-
MaybeColor: TypeAlias =
|
|
46
|
+
Color: TypeAlias = ColorTuple | ColorStr
|
|
47
|
+
MaybeColor: TypeAlias = str | Collection[Any]
|
|
58
48
|
|
|
59
49
|
|
|
60
50
|
def to_int_color_tuple(color: MaybeColor) -> IntColorTuple:
|
|
@@ -18,9 +18,7 @@ import copy
|
|
|
18
18
|
import json
|
|
19
19
|
from collections.abc import Mapping
|
|
20
20
|
from enum import Enum
|
|
21
|
-
from typing import TYPE_CHECKING, Final, Literal,
|
|
22
|
-
|
|
23
|
-
from typing_extensions import TypeAlias
|
|
21
|
+
from typing import TYPE_CHECKING, Final, Literal, TypeAlias
|
|
24
22
|
|
|
25
23
|
from streamlit.dataframe_util import DataFormat
|
|
26
24
|
from streamlit.elements.lib.column_types import ColumnConfig, ColumnType
|
|
@@ -109,6 +107,11 @@ _EDITING_COMPATIBILITY_MAPPING: Final[dict[ColumnType, list[ColumnDataKind]]] =
|
|
|
109
107
|
ColumnDataKind.STRING,
|
|
110
108
|
ColumnDataKind.EMPTY,
|
|
111
109
|
],
|
|
110
|
+
"multiselect": [
|
|
111
|
+
ColumnDataKind.LIST,
|
|
112
|
+
ColumnDataKind.STRING,
|
|
113
|
+
ColumnDataKind.EMPTY,
|
|
114
|
+
],
|
|
112
115
|
}
|
|
113
116
|
|
|
114
117
|
|
|
@@ -400,16 +403,14 @@ def determine_dataframe_schema(
|
|
|
400
403
|
|
|
401
404
|
|
|
402
405
|
# A mapping of column names/IDs to column configs.
|
|
403
|
-
ColumnConfigMapping: TypeAlias = dict[
|
|
404
|
-
Union[IndexIdentifierType, str, int], ColumnConfig
|
|
405
|
-
]
|
|
406
|
+
ColumnConfigMapping: TypeAlias = dict[IndexIdentifierType | str | int, ColumnConfig]
|
|
406
407
|
ColumnConfigMappingInput: TypeAlias = Mapping[
|
|
407
408
|
# TODO(lukasmasuch): This should also use int here to
|
|
408
409
|
# correctly type the support for positional index. However,
|
|
409
410
|
# allowing int here leads mypy to complain about simple dict[str, ...]
|
|
410
411
|
# as input -> which seems like a mypy bug.
|
|
411
|
-
|
|
412
|
-
|
|
412
|
+
IndexIdentifierType | str,
|
|
413
|
+
ColumnConfig | None | str,
|
|
413
414
|
]
|
|
414
415
|
|
|
415
416
|
|