diff --git a/DEPRECATIONS.md b/DEPRECATIONS.md index 5e917fa1..2ca30d7c 100644 --- a/DEPRECATIONS.md +++ b/DEPRECATIONS.md @@ -8,12 +8,6 @@ uv run python -m cellpy._deprecation | Name | Replacement | Introduced | Removal | | --- | --- | --- | --- | -| `Batch.plot(backend="seaborn")` | `backend="matplotlib"` | 2.0 | 2.1 | -| `cycle_info_plot(interactive=...)` | `backend="plotly"|"matplotlib"` | 2.0 | 2.1 | -| `cycles_plot(interactive=...)` | `backend="plotly"|"matplotlib"` | 2.0 | 2.1 | -| `cycles_plot(xlim=...)` | `cycles_plot(x_range=...)` | 2.0 | 2.1 | -| `cycles_plot(ylim=...)` | `cycles_plot(y_range=...)` | 2.0 | 2.1 | -| `dva_plot(interactive=...)` | `backend="plotly"|"matplotlib"` | 2.0 | 2.1 | | `ica.Converter` | `cellpy.ica.transform_half_cycle with IcaOptions` | 2.0 | 2.1 | | `ica.dqdv(cycle=...)` | `cellpy.ica.dqdv(cycles=...)` | 2.0 | 2.1 | | `ica.dqdv(label_direction=...)` | `the direction column, which the specced frame always carries` | 2.0 | 2.1 | @@ -21,10 +15,6 @@ uv run python -m cellpy._deprecation | `ica.dqdv_cycle` | `cellpy.ica.dqdv (returns the specced long frame)` | 2.0 | 2.1 | | `ica.dqdv_cycles` | `cellpy.ica.dqdv (returns the specced long frame)` | 2.0 | 2.1 | | `ica.dqdv_np` | `cellpy.ica.transform_half_cycle with IcaOptions` | 2.0 | 2.1 | -| `ica_plot(interactive=...)` | `backend="plotly"|"matplotlib"` | 2.0 | 2.1 | | `legacy header attribute access (headers_normal / _summary / _step_table)` | `c.schema.raw / c.schema.steps / c.schema.summary` | 2.0 | 2.1 | | `make_new_cell` | `CellpyCell.vacant` | 2.0 | 2.1 | -| `plotutils.summary_plot_legacy` | `cellpy.utils.plotutils.summary_plot (same figures, same options)` | 2.0 | 2.1 | -| `raw_plot(interactive=...)` | `backend="plotly"|"matplotlib"` | 2.0 | 2.1 | -| `summary_plot(interactive=...)` | `backend="plotly"|"matplotlib"` | 2.0 | 2.1 | | `the 'dq' column of the ica output frame` | `the 'dqdv' column of the same frame` | 2.0 | 2.1 | diff --git a/cellpy/_deprecation.py b/cellpy/_deprecation.py index e4ae4101..edf85ffe 100644 --- a/cellpy/_deprecation.py +++ b/cellpy/_deprecation.py @@ -147,64 +147,8 @@ def _seed_known_deprecations() -> None: removal="2.1", ) - # Plotting redesign (#567). The old implementation behind this name was - # unconditionally broken (its first statement unpacked a None); the name - # now delegates to summary_plot and goes away in 2.1. - _register( - "plotutils.summary_plot_legacy", - "cellpy.utils.plotutils.summary_plot (same figures, same options)", - removal="2.1", - ) - # Stage 1 (#639): interactive= is a warn_once alias for backend=. - _register( - "summary_plot(interactive=...)", - 'backend="plotly"|"matplotlib"', - removal="2.1", - ) - # Stage 2 (#646): cycles_plot backend= + range spelling. - _register( - "cycles_plot(interactive=...)", - 'backend="plotly"|"matplotlib"', - removal="2.1", - ) - _register( - "cycles_plot(xlim=...)", - "cycles_plot(x_range=...)", - removal="2.1", - ) - _register( - "cycles_plot(ylim=...)", - "cycles_plot(y_range=...)", - removal="2.1", - ) - # Stage 2 (#647): raw_plot / cycle_info_plot backend=. - _register( - "raw_plot(interactive=...)", - 'backend="plotly"|"matplotlib"', - removal="2.1", - ) - _register( - "cycle_info_plot(interactive=...)", - 'backend="plotly"|"matplotlib"', - removal="2.1", - ) - # Stage 2 (#648): ica_plot / dva_plot backend=. - _register( - "ica_plot(interactive=...)", - 'backend="plotly"|"matplotlib"', - removal="2.1", - ) - _register( - "dva_plot(interactive=...)", - 'backend="plotly"|"matplotlib"', - removal="2.1", - ) - # Stage 3 (#658): Batch.plot backend triage — seaborn alias, bokeh removed. - _register( - 'Batch.plot(backend="seaborn")', - 'backend="matplotlib"', - removal="2.1", - ) + # Plotting shims (interactive=, xlim/ylim, backend="seaborn", summary_plot_legacy) + # were removed in 2.1 (E1, #713) -- no longer registered here. if __name__ == "__main__": diff --git a/cellpy/plotting/batch_summary.py b/cellpy/plotting/batch_summary.py index ed761279..2ef97e1a 100644 --- a/cellpy/plotting/batch_summary.py +++ b/cellpy/plotting/batch_summary.py @@ -2,8 +2,7 @@ Relocated from ``cellpy.utils.batch_tools.batch_plotters`` so ``Batch.plot`` delegates into ``cellpy.plotting``. Public backends: ``plotly`` (primary) and -``matplotlib``. ``seaborn`` is a deprecated alias for ``matplotlib``; ``bokeh`` -raises. +``matplotlib``. ``seaborn`` and ``bokeh`` were removed in 2.1 and now raise. """ from __future__ import annotations @@ -34,22 +33,13 @@ def resolve_batch_plot_backend(backend: Optional[str]) -> str: - """Normalize Batch.plot backend names (triage for #658).""" - from cellpy._deprecation import warn_once - + """Normalize Batch.plot backend names.""" if backend is None: backend = getattr(config.batch, "backend", None) or "plotly" key = str(backend).strip().lower() - if key == "seaborn": - warn_once( - 'Batch.plot(backend="seaborn")', - 'backend="matplotlib"', - stacklevel=3, - ) - key = "matplotlib" - if key == "bokeh": + if key in ("seaborn", "bokeh"): raise ValueError( - 'Batch.plot backend "bokeh" was removed; use backend="plotly" ' + f'Batch.plot backend "{key}" was removed; use backend="plotly" ' 'or backend="matplotlib".' ) if key not in SUPPORTED_BATCH_PLOT_BACKENDS: diff --git a/cellpy/utils/plotutils.py b/cellpy/utils/plotutils.py index 778ee215..ba2f2e16 100644 --- a/cellpy/utils/plotutils.py +++ b/cellpy/utils/plotutils.py @@ -607,7 +607,6 @@ class SummaryPlotConfig: hover_columns: Optional[list] = None auto_convert_legend_labels: bool = True backend: Optional[str] = None - interactive: Optional[bool] = None share_y: bool = False rangeslider: bool = False @@ -920,97 +919,6 @@ def _create_col_info(self, c: Any) -> tuple[tuple, dict, dict, dict]: -def summary_plot_legacy( - c, - x: Optional[str] = None, - y: str = "capacities_gravimetric_coulombic_efficiency", - height: Optional[int] = None, - width: int = 900, - markers: bool = True, - title: Optional[str] = None, - x_range: Optional[list] = None, - y_range: Optional[list] = None, - ce_range: Optional[list] = None, - norm_range: Optional[list] = None, - cv_share_range: Optional[list] = None, - split: bool = True, - auto_convert_legend_labels: bool = True, - interactive: bool = True, - share_y: bool = False, - rangeslider: bool = False, - return_data: bool = False, - verbose: bool = False, - plotly_template: Optional[str] = None, - seaborn_palette: str = "deep", - seaborn_style: str = "dark", - formation_cycles: int = 3, - show_formation: bool = True, - show_legend: bool = True, - x_axis_domain_formation_fraction: float = 0.2, - column_separator: float = 0.01, - reset_losses: bool = True, - link_capacity_scales: bool = False, - fullcell_standard_normalization_type: str = "on-max", - fullcell_standard_normalization_factor: Optional[float] = None, - fullcell_standard_normalization_scaler: float = 1.0, - seaborn_line_hooks: Optional[list[tuple[str, list, dict]]] = None, - **kwargs, -) -> Any: - """Deprecated. Use [`summary_plot`][cellpy.utils.plotutils.summary_plot]. - - The 1 400-line implementation that used to live here was **broken, not - merely redundant**: its first statement unpacked the return value of - ``SummaryPlotInfo._create_col_info``, which stores its results as - attributes and returns ``None`` — so every call raised ``TypeError`` - regardless of the cell or the runtime (#567). Nothing in the repo called - it, and nothing outside it could have called it successfully either. - - Delegates to ``summary_plot``, which draws the same figures through the - builder pipeline. Removal in 2.1. - """ - warn_once( - "plotutils.summary_plot_legacy", - "cellpy.utils.plotutils.summary_plot (same figures, same options)", - removal="2.1", - ) - return summary_plot( - c, - x=x, - y=y, - height=height, - width=width, - markers=markers, - title=title, - x_range=x_range, - y_range=y_range, - ce_range=ce_range, - norm_range=norm_range, - cv_share_range=cv_share_range, - split=split, - auto_convert_legend_labels=auto_convert_legend_labels, - interactive=interactive, - share_y=share_y, - rangeslider=rangeslider, - return_data=return_data, - verbose=verbose, - plotly_template=plotly_template, - seaborn_palette=seaborn_palette, - seaborn_style=seaborn_style, - formation_cycles=formation_cycles, - show_formation=show_formation, - show_legend=show_legend, - x_axis_domain_formation_fraction=x_axis_domain_formation_fraction, - column_separator=column_separator, - reset_losses=reset_losses, - link_capacity_scales=link_capacity_scales, - fullcell_standard_normalization_type=fullcell_standard_normalization_type, - fullcell_standard_normalization_factor=fullcell_standard_normalization_factor, - fullcell_standard_normalization_scaler=fullcell_standard_normalization_scaler, - seaborn_line_hooks=seaborn_line_hooks, - **kwargs, - ) - - @notebook_docstring_printer def summary_plot( c, @@ -1029,7 +937,6 @@ def summary_plot( hover_columns: Optional[list] = None, auto_convert_legend_labels: bool = True, backend: Optional[str] = None, - interactive: Optional[bool] = None, share_y: bool = False, rangeslider: bool = False, return_data: bool = False, @@ -1081,8 +988,6 @@ def summary_plot( auto_convert_legend_labels: convert the legend labels to a nicer format. backend: plotting backend (``"plotly"`` or ``"matplotlib"``; default ``"plotly"``) - interactive: deprecated alias for backend selection - (``True`` → ``"plotly"``, ``False`` → ``"matplotlib"``); removal 2.1 rangeslider: add a range slider to the x-axis (only for plotly) share_y: share y-axis (only for plotly) return_data: return the data used for plotting @@ -1219,7 +1124,6 @@ def summary_plot( hover_columns=hover_columns, auto_convert_legend_labels=auto_convert_legend_labels, backend=backend, - interactive=interactive, share_y=share_y, rangeslider=rangeslider, return_data=return_data, @@ -1250,18 +1154,7 @@ def summary_plot( config.x = _resolve_summary_column(c, config.x) config.hover_columns = _resolve_summary_columns(c, config.hover_columns) - # Resolve backend= vs deprecated interactive= (#639). - resolved_backend = config.backend - if config.interactive is not None: - warn_once( - "summary_plot(interactive=...)", - 'backend="plotly"|"matplotlib"', - removal="2.1", - ) - if resolved_backend is None: - resolved_backend = "plotly" if config.interactive else "matplotlib" - if resolved_backend is None: - resolved_backend = "plotly" + resolved_backend = config.backend or "plotly" config.backend = resolved_backend if resolved_backend == "plotly" and not plotly_available: @@ -1367,7 +1260,6 @@ def raw_plot( x_label=None, title=None, backend: Optional[str] = None, - interactive: Optional[bool] = None, plot_type="voltage-current", double_y=True, **kwargs, @@ -1384,8 +1276,6 @@ def raw_plot( x_label (str): label for x-axis title (str): title of the plot backend (str, optional): ``"plotly"`` (default) or ``"matplotlib"``. - interactive (bool, optional): Deprecated alias for backend selection - (``True``→plotly, ``False``→matplotlib; removal 2.1). plot_type (str): type of plot (defaults to "voltage-current") (overrides given y if y is not None), currently only "voltage-current", "raw", "capacity", "capacity-current", and "full" is supported. double_y (bool): use double y-axis (only for matplotlib and when plot_type with 2 rows is used) @@ -1395,17 +1285,7 @@ def raw_plot( ``matplotlib`` figure or ``plotly`` figure """ - resolved_backend = backend - if interactive is not None: - warn_once( - "raw_plot(interactive=...)", - 'backend="plotly"|"matplotlib"', - removal="2.1", - ) - if resolved_backend is None: - resolved_backend = "plotly" if interactive else "matplotlib" - if resolved_backend is None: - resolved_backend = "plotly" + resolved_backend = backend or "plotly" if resolved_backend == "plotly" and not plotly_available: warnings.warn("Can not perform interactive plotting. Plotly is not available.") @@ -1442,7 +1322,6 @@ def cycle_info_plot( cycle=None, get_axes=False, backend: Optional[str] = None, - interactive: Optional[bool] = None, t_unit="hours", v_unit="V", i_unit="mA", @@ -1457,8 +1336,6 @@ def cycle_info_plot( cycle (int or list or tuple): cycle(s) to select (must be int for matplotlib) get_axes (bool): return axes (for matplotlib) or figure (for plotly) backend (str, optional): ``"plotly"`` (default) or ``"matplotlib"``. - interactive (bool, optional): Deprecated alias for backend selection - (``True``→plotly, ``False``→matplotlib; removal 2.1). t_unit (str): unit for x-axis (default: "hours") v_unit (str): unit for y-axis (default: "V") i_unit (str): unit for current (default: "mA") @@ -1467,17 +1344,7 @@ def cycle_info_plot( Returns: ``matplotlib.axes`` or None (or a figure when ``get_axes`` / backend semantics require it) """ - resolved_backend = backend - if interactive is not None: - warn_once( - "cycle_info_plot(interactive=...)", - 'backend="plotly"|"matplotlib"', - removal="2.1", - ) - if resolved_backend is None: - resolved_backend = "plotly" if interactive else "matplotlib" - if resolved_backend is None: - resolved_backend = "plotly" + resolved_backend = backend or "plotly" if resolved_backend == "plotly" and not plotly_available: warnings.warn("Can not perform interactive plotting. Plotly is not available.") @@ -1536,10 +1403,7 @@ def cycles_plot( figsize=(6, 4), x_range=None, y_range=None, - xlim=None, - ylim=None, backend: Optional[str] = None, - interactive: Optional[bool] = None, return_figure=None, width=800, height=600, @@ -1580,11 +1444,7 @@ def cycles_plot( figsize (tuple, optional): Size of the figure for matplotlib. Default is (6, 4). x_range (list, optional): Limits for the x-axis. y_range (list, optional): Limits for the y-axis. - xlim (list, optional): Deprecated alias for ``x_range`` (removal 2.1). - ylim (list, optional): Deprecated alias for ``y_range`` (removal 2.1). backend (str, optional): ``"plotly"`` (default) or ``"matplotlib"``. - interactive (bool, optional): Deprecated alias for backend selection - (``True``→plotly, ``False``→matplotlib; removal 2.1). return_figure (bool, optional): Whether to return the figure object. Default is ``True`` for matplotlib and ``False`` for plotly (``fig.show()``). width (int, optional): Width of the figure for Plotly. Default is 800. @@ -1614,18 +1474,7 @@ def cycles_plot( Else: None: The plot is shown in the default browser. """ - # Resolve backend= vs deprecated interactive= (#646 / same as #639). - resolved_backend = backend - if interactive is not None: - warn_once( - "cycles_plot(interactive=...)", - 'backend="plotly"|"matplotlib"', - removal="2.1", - ) - if resolved_backend is None: - resolved_backend = "plotly" if interactive else "matplotlib" - if resolved_backend is None: - resolved_backend = "plotly" + resolved_backend = backend or "plotly" if resolved_backend == "plotly" and not plotly_available: warnings.warn("Can not perform interactive plotting. Plotly is not available.") @@ -1634,24 +1483,6 @@ def cycles_plot( if return_figure is None: return_figure = resolved_backend != "plotly" - # Canonical range spelling: x_range/y_range; xlim/ylim are warn_once aliases. - if xlim is not None: - warn_once( - "cycles_plot(xlim=...)", - "cycles_plot(x_range=...)", - removal="2.1", - ) - if x_range is None: - x_range = xlim - if ylim is not None: - warn_once( - "cycles_plot(ylim=...)", - "cycles_plot(y_range=...)", - removal="2.1", - ) - if y_range is None: - y_range = ylim - seaborn_context = kwargs.pop("seaborn_context", "notebook") seaborn_facecolor = kwargs.pop("seaborn_facecolor", "#EAEAF2") seaborn_edgecolor = kwargs.pop("seaborn_edgecolor", "black") @@ -1712,24 +1543,9 @@ def cycles_plot( return None -def _resolve_plot_backend( - *, - backend: Optional[str], - interactive: Optional[bool], - deprecation_site: str, -) -> str: - """Resolve ``backend=`` vs deprecated ``interactive=`` for plot entry points.""" - resolved = backend - if interactive is not None: - warn_once( - deprecation_site, - 'backend="plotly"|"matplotlib"', - removal="2.1", - ) - if resolved is None: - resolved = "plotly" if interactive else "matplotlib" - if resolved is None: - resolved = "plotly" +def _resolve_plot_backend(backend: Optional[str]) -> str: + """Resolve the plotting backend for plot entry points (default ``plotly``).""" + resolved = backend or "plotly" if resolved == "plotly" and not plotly_available: warnings.warn("Can not perform interactive plotting. Plotly is not available.") resolved = "matplotlib" @@ -1743,7 +1559,6 @@ def ica_plot( options=None, *, backend: Optional[str] = None, - interactive: Optional[bool] = None, title=None, colormap="viridis", width=800, @@ -1767,7 +1582,6 @@ def ica_plot( direction: ``"charge"``, ``"discharge"``, or ``"both"``. options: Optional [`IcaOptions`][cellpy.ica.IcaOptions]. backend: ``"plotly"`` (default) or ``"matplotlib"``. - interactive: Deprecated alias for backend selection (removal 2.1). title: Figure title. colormap: Cycle colour map. width, height: Plotly figure size. @@ -1785,11 +1599,7 @@ def ica_plot( from cellpy.plotting.context import from_source from cellpy.plotting.prepare.ica import IcaPrepareConfig, prepare as prepare_ica - resolved_backend = _resolve_plot_backend( - backend=backend, - interactive=interactive, - deprecation_site="ica_plot(interactive=...)", - ) + resolved_backend = _resolve_plot_backend(backend) option_keys = { "voltage_resolution", @@ -1850,7 +1660,6 @@ def dva_plot( options=None, *, backend: Optional[str] = None, - interactive: Optional[bool] = None, title=None, colormap="viridis", width=800, @@ -1875,7 +1684,6 @@ def dva_plot( options: Optional [`IcaOptions`][cellpy.ica.IcaOptions] (defaults to DVA-oriented options inside ``dvdq``). backend: ``"plotly"`` (default) or ``"matplotlib"``. - interactive: Deprecated alias for backend selection (removal 2.1). title: Figure title. colormap: Cycle colour map. width, height: Plotly figure size. @@ -1893,11 +1701,7 @@ def dva_plot( from cellpy.plotting.context import from_source from cellpy.plotting.prepare.ica import IcaPrepareConfig, prepare as prepare_ica - resolved_backend = _resolve_plot_backend( - backend=backend, - interactive=interactive, - deprecation_site="dva_plot(interactive=...)", - ) + resolved_backend = _resolve_plot_backend(backend) option_keys = { "voltage_resolution", @@ -1984,11 +1788,11 @@ def _check_plotter_plotly(): c = cellpy.get(p) fig = cycles_plot( c, - ylim=[0.0, 1.0], + y_range=[0.0, 1.0], show_formation=False, cut_colorbar=False, title="My nice plot", - interactive=True, + backend="plotly", return_figure=True, ) print("saving figure") @@ -2011,11 +1815,11 @@ def _check_plotter_matplotlib(): c = cellpy.get(p) fig = cycles_plot( c, - ylim=[0.0, 1.0], + y_range=[0.0, 1.0], show_formation=False, cut_colorbar=False, title="My nice plot", - interactive=False, + backend="matplotlib", return_figure=True, ) print("saving figure") @@ -2044,7 +1848,7 @@ def _check_summary_plotter_plotly(): # cut_colorbar=False, # split=True, title="My nice plot", - interactive=True, # rangeslider=True, + backend="plotly", # rangeslider=True, show_formation=True, # return_data=False, ) @@ -2078,7 +1882,7 @@ def _check_summary_plotter_seaborn(): # cut_colorbar=False, # split=True, title="My nice plot", - interactive=False, # rangeslider=True, + backend="matplotlib", # rangeslider=True, show_formation=True, # return_figure=True, ) # print("saving figure") @@ -2098,7 +1902,7 @@ def _check_cycles_plotter_plotly(): c, y="capacities_gravimetric", cycles=[1, 2, 3, 4, 5, 20, 40, 60], - interactive=True, + backend="plotly", return_figure=True, ) save_image_files(fig, out / "test_plot_cycles_plotly", backend="plotly") @@ -2114,7 +1918,7 @@ def _check_cycles_plotter_matplotlib(): fig = cycles_plot( c, y="capacities_gravimetric", - interactive=False, + backend="matplotlib", return_figure=True, ) save_image_files(fig, out / "test_plot_cycles_matplotlib", backend="matplotlib") diff --git a/tests/test_batch.py b/tests/test_batch.py index b7ea4924..9c6f95bb 100644 --- a/tests/test_batch.py +++ b/tests/test_batch.py @@ -799,13 +799,13 @@ def test_batch_update(parameters, batch_instance): @pytest.mark.essential def test_batch_plot_backend_triage(): - """seaborn → matplotlib (warn); bokeh → ValueError (#658).""" + """plotly/matplotlib supported; seaborn + bokeh removed in 2.1 (E1, #713).""" from cellpy.plotting.batch_summary import resolve_batch_plot_backend assert resolve_batch_plot_backend("plotly") == "plotly" assert resolve_batch_plot_backend("matplotlib") == "matplotlib" - with pytest.warns(DeprecationWarning, match="seaborn"): - assert resolve_batch_plot_backend("seaborn") == "matplotlib" + with pytest.raises(ValueError, match="seaborn"): + resolve_batch_plot_backend("seaborn") with pytest.raises(ValueError, match="bokeh"): resolve_batch_plot_backend("bokeh") with pytest.raises(ValueError, match="not supported"): diff --git a/tests/test_cycles_prepare.py b/tests/test_cycles_prepare.py index cef8f29d..b79aefd5 100644 --- a/tests/test_cycles_prepare.py +++ b/tests/test_cycles_prepare.py @@ -41,42 +41,17 @@ def test_cycles_plot_backend_matplotlib(cell): @pytest.mark.essential -def test_cycles_plot_interactive_alias_warns(cell): - from cellpy import _deprecation - - _deprecation._WARNED_SITES.clear() - with warnings.catch_warnings(record=True) as caught: - warnings.simplefilter("always", DeprecationWarning) - fig = cycles_plot(cell, interactive=False, return_figure=True) - assert fig is not None - messages = [ - str(w.message) - for w in caught - if issubclass(w.category, DeprecationWarning) - and "interactive" in str(w.message) - ] - assert messages - assert "backend=" in messages[0] or "matplotlib" in messages[0] - - -@pytest.mark.essential -def test_cycles_plot_xlim_ylim_alias_warns(cell): - from cellpy import _deprecation - - _deprecation._WARNED_SITES.clear() - with warnings.catch_warnings(record=True) as caught: - warnings.simplefilter("always", DeprecationWarning) - fig = cycles_plot( - cell, - backend="matplotlib", - xlim=[0, 1], - ylim=[0, 2], - return_figure=True, - ) +def test_cycles_plot_interactive_and_range_shims_removed(cell): + # interactive=/xlim/ylim were removed in 2.1 (E1, #713); canonical spellings only. + import inspect + + params = inspect.signature(cycles_plot).parameters + assert "interactive" not in params + assert "xlim" not in params and "ylim" not in params + fig = cycles_plot( + cell, backend="matplotlib", x_range=[0, 1], y_range=[0, 2], return_figure=True + ) assert fig is not None - texts = [str(w.message) for w in caught if issubclass(w.category, DeprecationWarning)] - assert any("xlim" in t for t in texts) - assert any("ylim" in t for t in texts) @pytest.mark.essential diff --git a/tests/test_figure_specs.py b/tests/test_figure_specs.py index d94f58ff..ad2d1071 100644 --- a/tests/test_figure_specs.py +++ b/tests/test_figure_specs.py @@ -178,7 +178,7 @@ def test_plotting_does_not_mutate_the_cell(cell): from cellpy.utils.plotutils import summary_plot summary_plot( - cell, y="capacities_gravimetric_split_constant_voltage", interactive=False + cell, y="capacities_gravimetric_split_constant_voltage", backend="matplotlib" ) assert list(cell.data.summary.columns) == summary_before diff --git a/tests/test_ica_plot_prepare.py b/tests/test_ica_plot_prepare.py index 988fba86..5bb1c491 100644 --- a/tests/test_ica_plot_prepare.py +++ b/tests/test_ica_plot_prepare.py @@ -71,37 +71,10 @@ def test_dva_plot_backend_matplotlib(cell): @pytest.mark.essential -def test_ica_plot_interactive_alias_warns(cell): - from cellpy import _deprecation +def test_ica_dva_interactive_removed(cell): + import inspect - _deprecation._WARNED_SITES.clear() - with warnings.catch_warnings(record=True) as caught: - warnings.simplefilter("always", DeprecationWarning) - fig = ica_plot(cell, cycles=1, interactive=False) - assert fig is not None - messages = [ - str(w.message) - for w in caught - if issubclass(w.category, DeprecationWarning) - and "interactive" in str(w.message) - ] - assert messages - assert "backend=" in messages[0] or "matplotlib" in messages[0] - - -@pytest.mark.essential -def test_dva_plot_interactive_alias_warns(cell): - from cellpy import _deprecation - - _deprecation._WARNED_SITES.clear() - with warnings.catch_warnings(record=True) as caught: - warnings.simplefilter("always", DeprecationWarning) - fig = dva_plot(cell, cycles=1, interactive=False) - assert fig is not None - messages = [ - str(w.message) - for w in caught - if issubclass(w.category, DeprecationWarning) - and "interactive" in str(w.message) - ] - assert messages + assert "interactive" not in inspect.signature(ica_plot).parameters + assert "interactive" not in inspect.signature(dva_plot).parameters + assert ica_plot(cell, cycles=1, backend="matplotlib") is not None + assert dva_plot(cell, cycles=1, backend="matplotlib") is not None diff --git a/tests/test_mpl_backend.py b/tests/test_mpl_backend.py index a38c3b73..31c0f25f 100644 --- a/tests/test_mpl_backend.py +++ b/tests/test_mpl_backend.py @@ -34,29 +34,18 @@ def test_seaborn_plot_builder_is_gone(): @pytest.mark.essential -def test_interactive_alias_warns_and_maps(cell): - from cellpy import _deprecation +def test_interactive_kwarg_removed(cell): + # interactive= was removed in 2.1 (E1, #713); backend= is canonical. + import inspect - # warn_once is once-per-call-site across the whole process; reset so this - # test is order-independent in the essential suite. - _deprecation._WARNED_SITES.clear() - with warnings.catch_warnings(record=True) as caught: - warnings.simplefilter("always", DeprecationWarning) - fig = summary_plot( - cell, - y="capacities_gravimetric", - interactive=False, - show_formation=False, - ) + assert "interactive" not in inspect.signature(summary_plot).parameters + fig = summary_plot( + cell, + y="capacities_gravimetric", + backend="matplotlib", + show_formation=False, + ) assert fig is not None - messages = [ - str(w.message) - for w in caught - if issubclass(w.category, DeprecationWarning) - and "interactive" in str(w.message) - ] - assert messages, "expected DeprecationWarning for interactive=" - assert "backend=" in messages[0] or "matplotlib" in messages[0] @pytest.mark.essential diff --git a/tests/test_plotutils_headers.py b/tests/test_plotutils_headers.py index c82a1618..df206c09 100644 --- a/tests/test_plotutils_headers.py +++ b/tests/test_plotutils_headers.py @@ -102,12 +102,12 @@ def test_normalized_cycle_index_is_dialect_invariant(): @pytest.mark.essential -@pytest.mark.parametrize("interactive", [False, True]) -def test_raw_plot_runs_on_a_native_cell(cell, interactive): +@pytest.mark.parametrize("backend", ["matplotlib", "plotly"]) +def test_raw_plot_runs_on_a_native_cell(cell, backend): """`raw_plot` raised KeyError: 'voltage' on every cellpy 2 cell.""" - if interactive and not plotly_available: + if backend == "plotly" and not plotly_available: pytest.skip("plotly not installed") - figure = plotutils.raw_plot(cell, interactive=interactive) + figure = plotutils.raw_plot(cell, backend=backend) assert figure is not None @@ -115,17 +115,17 @@ def test_raw_plot_runs_on_a_native_cell(cell, interactive): @pytest.mark.parametrize("plot_type", ["voltage-current", "capacity", "raw"]) def test_raw_plot_predefined_types_run(cell, plot_type): """Each plot_type reaches a different set of raw columns.""" - figure = plotutils.raw_plot(cell, interactive=False, plot_type=plot_type) + figure = plotutils.raw_plot(cell, backend="matplotlib", plot_type=plot_type) assert figure is not None @pytest.mark.essential -@pytest.mark.parametrize("interactive", [False, True]) -def test_cycle_info_plot_runs_on_a_native_cell(cell, interactive): +@pytest.mark.parametrize("backend", ["matplotlib", "plotly"]) +def test_cycle_info_plot_runs_on_a_native_cell(cell, backend): """`cycle_info_plot` raised on both the raw and the step frame.""" - if interactive and not plotly_available: + if backend == "plotly" and not plotly_available: pytest.skip("plotly not installed") - plotutils.cycle_info_plot(cell, cycle=3, interactive=interactive) + plotutils.cycle_info_plot(cell, cycle=3, backend=backend) # --- user-supplied column names ---------------------------------------------- @@ -136,7 +136,7 @@ def test_cycle_info_plot_runs_on_a_native_cell(cell, interactive): def test_summary_plot_accepts_the_legacy_x_spelling(cell): """`x="cycle_index"` is what this module's own docstring tells you to pass.""" figure = plotutils.summary_plot( - cell, y="capacities_gravimetric", x="cycle_index", interactive=False + cell, y="capacities_gravimetric", x="cycle_index", backend="matplotlib" ) assert figure is not None @@ -145,7 +145,7 @@ def test_summary_plot_accepts_the_legacy_x_spelling(cell): @pytest.mark.skipif(not seaborn_available, reason="seaborn not installed") def test_summary_plot_accepts_the_native_x_spelling(cell): figure = plotutils.summary_plot( - cell, y="capacities_gravimetric", x="cycle_num", interactive=False + cell, y="capacities_gravimetric", x="cycle_num", backend="matplotlib" ) assert figure is not None @@ -175,50 +175,18 @@ def test_cv_split_plot_leaves_the_summary_frame_alone(cell): """ before = list(cell.data.summary.columns) plotutils.summary_plot( - cell, y="capacities_gravimetric_split_constant_voltage", interactive=False + cell, y="capacities_gravimetric_split_constant_voltage", backend="matplotlib" ) assert list(cell.data.summary.columns) == before assert cell.data.summary.index.name != "cycle_num" -# --- summary_plot_legacy is a delegate now ------------------------------------ +# --- summary_plot_legacy was removed in 2.1 (E1, #713) ------------------------ @pytest.mark.essential -@pytest.mark.skipif(not seaborn_available, reason="seaborn not installed") -def test_summary_plot_legacy_delegates_and_warns(cell): - """The old implementation could not draw anything at all. - - Its first statement unpacked the return value of - `SummaryPlotInfo._create_col_info`, which stores its results as attributes - and returns None — so every call raised TypeError, on any cell, since the - refactor that introduced the class (#567). The name now delegates to - summary_plot, which is strictly better than the TypeError callers got. - """ - import warnings as _warnings - - from tests.figure_spec_support import describe_figure - - with _warnings.catch_warnings(record=True) as caught: - _warnings.simplefilter("always") - via_legacy = plotutils.summary_plot_legacy( - cell, y="capacities_gravimetric", interactive=False - ) - assert any( - issubclass(w.category, DeprecationWarning) - and "summary_plot_legacy" in str(w.message) - for w in caught - ) - - direct = plotutils.summary_plot( - cell, y="capacities_gravimetric", interactive=False - ) - assert describe_figure(via_legacy) == describe_figure(direct) - - import matplotlib.pyplot as plt - - plt.close(via_legacy) - plt.close(direct) +def test_summary_plot_legacy_removed(): + assert not hasattr(plotutils, "summary_plot_legacy") @pytest.mark.essential diff --git a/tests/test_raw_cycle_info_prepare.py b/tests/test_raw_cycle_info_prepare.py index c17783e8..02a9f66e 100644 --- a/tests/test_raw_cycle_info_prepare.py +++ b/tests/test_raw_cycle_info_prepare.py @@ -64,36 +64,10 @@ def test_cycle_info_plot_backend_matplotlib(cell): @pytest.mark.essential -def test_raw_plot_interactive_alias_warns(cell): - from cellpy import _deprecation +def test_raw_and_cycle_info_interactive_removed(cell): + import inspect - _deprecation._WARNED_SITES.clear() - with warnings.catch_warnings(record=True) as caught: - warnings.simplefilter("always", DeprecationWarning) - fig = raw_plot(cell, interactive=False) - assert fig is not None - messages = [ - str(w.message) - for w in caught - if issubclass(w.category, DeprecationWarning) - and "interactive" in str(w.message) - ] - assert messages - assert "backend=" in messages[0] or "matplotlib" in messages[0] - - -@pytest.mark.essential -def test_cycle_info_plot_interactive_alias_warns(cell): - from cellpy import _deprecation - - _deprecation._WARNED_SITES.clear() - with warnings.catch_warnings(record=True) as caught: - warnings.simplefilter("always", DeprecationWarning) - cycle_info_plot(cell, cycle=3, interactive=False) - messages = [ - str(w.message) - for w in caught - if issubclass(w.category, DeprecationWarning) - and "interactive" in str(w.message) - ] - assert messages + assert "interactive" not in inspect.signature(raw_plot).parameters + assert "interactive" not in inspect.signature(cycle_info_plot).parameters + assert raw_plot(cell, backend="matplotlib") is not None + cycle_info_plot(cell, cycle=3, backend="matplotlib")