From 0f0acc6651e1b80715db742175196ab3a16d834e Mon Sep 17 00:00:00 2001 From: Sean Caso Date: Thu, 30 Jul 2026 14:51:05 -0400 Subject: [PATCH] Replace use_container_width with width, and pin streamlit Streamlit renamed this flag and scheduled the old name for removal on 2025-12-31, a date that has already passed. It still works and only logs a warning, but this package declared "streamlit>=1.30.0" with no upper bound, so any future release could drop the old name and stop every chart, table and full-width button from rendering. All 9 sites were use_container_width=True on st.plotly_chart, st.dataframe or st.button, the three widgets where width="stretch" is the documented replacement. Found with an AST walk rather than a grep, so a call spanning several lines could not be missed, and st.image was confirmed absent: width there used to mean a pixel count. The floor moves to 1.60.0 because width is not accepted by the older releases the old floor allowed, so swapping the flag without raising it would have broken installs that were previously fine. Verified against 1.60.0, which is what is installed here. Capped below 2.0 so a major release cannot silently change widget APIs again. 212 passed, including the 10 dashboard tests that render the real app through AppTest. --- pyproject.toml | 10 ++++++++-- src/perfect_choice/dashboard/views/detail.py | 8 ++++---- src/perfect_choice/dashboard/views/new_decision.py | 6 +++--- src/perfect_choice/dashboard/views/replay.py | 4 ++-- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1f58e61..4614818 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,10 @@ dependencies = [ [project.optional-dependencies] charts = ["plotext>=5.2.0"] dashboard = [ - "streamlit>=1.30.0", + # 1.60 is the floor because the dashboard passes width="stretch", the + # replacement for use_container_width, which older releases do not accept. + # Capped below 2.0 so a major release cannot silently change widget APIs. + "streamlit>=1.60.0,<2.0.0", "plotly>=5.18.0", ] dev = [ @@ -24,7 +27,10 @@ dev = [ "pyflakes>=3.2.0", "respx>=0.22.0", # The dashboard journey tests drive the real app through streamlit's AppTest. - "streamlit>=1.30.0", + # 1.60 is the floor because the dashboard passes width="stretch", the + # replacement for use_container_width, which older releases do not accept. + # Capped below 2.0 so a major release cannot silently change widget APIs. + "streamlit>=1.60.0,<2.0.0", "plotly>=5.18.0", # Terminal charts are an optional feature, but one test covers what happens # when drawing them fails, so the test install needs them present. diff --git a/src/perfect_choice/dashboard/views/detail.py b/src/perfect_choice/dashboard/views/detail.py index a48cd49..90fa6fd 100644 --- a/src/perfect_choice/dashboard/views/detail.py +++ b/src/perfect_choice/dashboard/views/detail.py @@ -73,7 +73,7 @@ def render() -> None: yaxis=dict(autorange="reversed"), height=max(250, len(decision.criteria) * 45), ) - st.plotly_chart(fig_weights, use_container_width=True) + st.plotly_chart(fig_weights, width="stretch") else: st.info("No criteria defined.") @@ -99,7 +99,7 @@ def render() -> None: yaxis_title="Score", height=350, ) - st.plotly_chart(fig_rank, use_container_width=True) + st.plotly_chart(fig_rank, width="stretch") else: st.info("No rankings computed.") @@ -119,7 +119,7 @@ def render() -> None: # Reorder columns by criteria order col_order = [c.name for c in decision.criteria if c.name in df.columns] df = df[col_order] - st.dataframe(df, use_container_width=True) + st.dataframe(df, width="stretch") else: st.info("No scores recorded.") @@ -217,4 +217,4 @@ def _render_sensitivity_chart(decision) -> None: yaxis=dict(autorange="reversed"), height=max(250, len(names) * 45), ) - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch") diff --git a/src/perfect_choice/dashboard/views/new_decision.py b/src/perfect_choice/dashboard/views/new_decision.py index abffbd8..52c4795 100644 --- a/src/perfect_choice/dashboard/views/new_decision.py +++ b/src/perfect_choice/dashboard/views/new_decision.py @@ -588,7 +588,7 @@ def _render_ranking_chart(rankings: dict[str, list[RankingResult]]) -> None: yaxis_title="Score", xaxis_title="Alternative", ) - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch") def _render_weight_donut(criteria: list[Criterion]) -> None: @@ -624,7 +624,7 @@ def _render_weight_donut(criteria: list[Criterion]) -> None: **PLOTLY_LAYOUT, title="Criteria Weights", ) - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch") def _render_sensitivity_chart(sensitivity: list) -> None: @@ -666,7 +666,7 @@ def _render_sensitivity_chart(sensitivity: list) -> None: xaxis_title="Weight Perturbation (%)", yaxis=dict(autorange="reversed"), ) - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch") # --------------------------------------------------------------------------- diff --git a/src/perfect_choice/dashboard/views/replay.py b/src/perfect_choice/dashboard/views/replay.py index 42fb90f..7d85e58 100644 --- a/src/perfect_choice/dashboard/views/replay.py +++ b/src/perfect_choice/dashboard/views/replay.py @@ -195,7 +195,7 @@ def _run_comparison(decision, new_criteria): yaxis_title="Weight (%)", height=350, ) - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch") def _render_ranking_chart(rankings, label): @@ -223,4 +223,4 @@ def _render_ranking_chart(rankings, label): yaxis_title="Score", height=300, ) - st.plotly_chart(fig, use_container_width=True) + st.plotly_chart(fig, width="stretch")