diff --git a/.gitignore b/.gitignore index bbe3c82e58..fb9e2a944b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Scanpy outfiles /data/ /write/ +/figures/ # Docs /docs/_build/ diff --git a/docs/release-notes/3675.docs.md b/docs/release-notes/3675.docs.md new file mode 100644 index 0000000000..a03f10dcc1 --- /dev/null +++ b/docs/release-notes/3675.docs.md @@ -0,0 +1 @@ +Update tutorial {doc}`/tutorials/experimental/dask` for {mod}`anndata` 0.12 (see {pr}`scverse/scanpy-tutorials#186`) {smaller}`I Gold` diff --git a/docs/release-notes/3675.misc.md b/docs/release-notes/3675.misc.md new file mode 100644 index 0000000000..31eb4535c3 --- /dev/null +++ b/docs/release-notes/3675.misc.md @@ -0,0 +1 @@ +Deprecate `save` parameter of plotting functions. {smaller}`zethson` diff --git a/notebooks b/notebooks index 897eef59b9..e67ca1796d 160000 --- a/notebooks +++ b/notebooks @@ -1 +1 @@ -Subproject commit 897eef59b945f88f9c86715656286796eea43853 +Subproject commit e67ca1796d3b9b244ca2a0086bd1157ea5ca8337 diff --git a/src/scanpy/_settings/__init__.py b/src/scanpy/_settings/__init__.py index 8c2ddfaed1..1553f85766 100644 --- a/src/scanpy/_settings/__init__.py +++ b/src/scanpy/_settings/__init__.py @@ -229,7 +229,7 @@ def datasetdir(cls, datasetdir: Path | str) -> None: @property def figdir(cls) -> Path: - """Directory for saving figures (default `'./figures/'`).""" + r"""Directory for `autosave`\ ing figures (default `'./figures/'`).""" return cls._figdir @figdir.setter diff --git a/src/scanpy/plotting/_anndata.py b/src/scanpy/plotting/_anndata.py index f78f437989..5f0f27f035 100755 --- a/src/scanpy/plotting/_anndata.py +++ b/src/scanpy/plotting/_anndata.py @@ -145,8 +145,9 @@ def scatter( # noqa: PLR0913 marker: str | Sequence[str] = ".", title: str | Collection[str] | None = None, show: bool | None = None, - save: str | bool | None = None, ax: Axes | None = None, + # deprecated + save: str | bool | None = None, ) -> Axes | list[Axes] | None: """Scatter plot along observations or variables axes. @@ -750,9 +751,9 @@ def violin( # noqa: PLR0912, PLR0913, PLR0915 ylabel: str | Sequence[str] | None = None, rotation: float | None = None, show: bool | None = None, - save: bool | str | None = None, ax: Axes | None = None, - # deprecatd + # deprecated + save: bool | str | None = None, scale: DensityNorm | Empty = _empty, **kwds, ) -> Axes | FacetGrid | None: @@ -1007,7 +1008,7 @@ def clustermap( *, use_raw: bool | None = None, show: bool | None = None, - save: bool | str | None = None, + save: bool | str | None = None, # deprecated **kwds, ) -> ClusterGrid | None: """Hierarchically-clustered heatmap. diff --git a/src/scanpy/plotting/_docs.py b/src/scanpy/plotting/_docs.py index 55c4c690c7..0f5f43f049 100644 --- a/src/scanpy/plotting/_docs.py +++ b/src/scanpy/plotting/_docs.py @@ -186,7 +186,8 @@ save If `True` or a `str`, save the figure. A string is appended to the default filename. - Infer the filetype if ending on {`'.pdf'`, `'.png'`, `'.svg'`}.\ + Infer the filetype if ending on {`'.pdf'`, `'.png'`, `'.svg'`}. + (deprecated in favour of `sc.pl.plot(show=False).figure.savefig()`).\ """ doc_show_save_ax = f"""\ diff --git a/src/scanpy/plotting/_preprocessing.py b/src/scanpy/plotting/_preprocessing.py index fdcdb8ae71..60356757e6 100644 --- a/src/scanpy/plotting/_preprocessing.py +++ b/src/scanpy/plotting/_preprocessing.py @@ -21,8 +21,9 @@ def highly_variable_genes( # noqa: PLR0912 *, log: bool = False, show: bool | None = None, - save: bool | str | None = None, highly_variable_genes: bool = True, + # deprecated + save: bool | str | None = None, ) -> None: """Plot dispersions or normalized variance versus means for genes. @@ -111,6 +112,7 @@ def filter_genes_dispersion( *, log: bool = False, show: bool | None = None, + # deprecated save: bool | str | None = None, ) -> None: """Plot dispersions versus means for genes. diff --git a/src/scanpy/plotting/_stacked_violin.py b/src/scanpy/plotting/_stacked_violin.py index 4063f0fe10..7e84409913 100644 --- a/src/scanpy/plotting/_stacked_violin.py +++ b/src/scanpy/plotting/_stacked_violin.py @@ -682,7 +682,6 @@ def stacked_violin( # noqa: PLR0913 categories_order: Sequence[str] | None = None, swap_axes: bool = False, show: bool | None = None, - save: bool | str | None = None, return_fig: bool | None = False, ax: _AxesSubplot | None = None, vmin: float | None = None, @@ -700,6 +699,7 @@ def stacked_violin( # noqa: PLR0913 # deprecated order: Sequence[str] | None | Empty = _empty, scale: DensityNorm | Empty = _empty, + save: bool | str | None = None, **kwds, ) -> StackedViolin | dict | None: """Stacked violin plots. diff --git a/src/scanpy/plotting/_tools/__init__.py b/src/scanpy/plotting/_tools/__init__.py index df09481c8d..13b74fc636 100644 --- a/src/scanpy/plotting/_tools/__init__.py +++ b/src/scanpy/plotting/_tools/__init__.py @@ -188,6 +188,7 @@ def pca_variance_ratio( *, log: bool = False, show: bool | None = None, + # deprecated save: bool | str | None = None, ): """Plot the variance ratio. @@ -230,9 +231,10 @@ def dpt_timeseries( *, color_map: str | Colormap | None = None, show: bool | None = None, - save: bool | None = None, as_heatmap: bool = True, marker: str | Sequence[str] = ".", + # deprecated + save: bool | None = None, ): """Heatmap of pseudotime series. @@ -277,9 +279,10 @@ def dpt_groups_pseudotime( color_map: str | Colormap | None = None, palette: Sequence[str] | Cycler | None = None, show: bool | None = None, - save: bool | str | None = None, marker: str | Sequence[str] = ".", return_fig: bool = False, + # deprecated + save: bool | str | None = None, ) -> Figure | None: """Plot groups and pseudotime. @@ -349,8 +352,8 @@ def rank_genes_groups( # noqa: PLR0912, PLR0913, PLR0915 ncols: int = 4, sharey: bool = True, show: bool | None = None, - save: bool | None = None, ax: Axes | None = None, + save: bool | None = None, # deprecated **kwds, ) -> list[Axes] | None: """Plot ranking of genes. @@ -503,7 +506,12 @@ def rank_genes_groups( # noqa: PLR0912, PLR0913, PLR0915 def _fig_show_save_or_axes( - plot_obj: BasePlot, *, return_fig: bool, show: bool | None, save: bool | None + plot_obj: BasePlot, + *, + return_fig: bool, + show: bool | None, + # deprecated + save: bool | None, ): """Decides what to return.""" if return_fig: @@ -528,9 +536,9 @@ def _rank_genes_groups_plot( # noqa: PLR0912, PLR0913, PLR0915 min_logfoldchange: float | None = None, key: str | None = None, show: bool | None = None, - save: bool | None = None, return_fig: bool = False, gene_symbols: str | None = None, + save: bool | None = None, # deprecated **kwds, ): """Call the different `rank_genes_groups_*` plots.""" @@ -703,7 +711,7 @@ def rank_genes_groups_heatmap( min_logfoldchange: float | None = None, key: str | None = None, show: bool | None = None, - save: bool | None = None, + save: bool | None = None, # deprecated **kwds, ): """Plot ranking of genes using heatmap plot (see :func:`~scanpy.pl.heatmap`). @@ -786,7 +794,7 @@ def rank_genes_groups_tracksplot( min_logfoldchange: float | None = None, key: str | None = None, show: bool | None = None, - save: bool | None = None, + save: bool | None = None, # deprecated **kwds, ): """Plot ranking of genes using heatmap plot (see :func:`~scanpy.pl.heatmap`). @@ -863,8 +871,8 @@ def rank_genes_groups_dotplot( # noqa: PLR0913 min_logfoldchange: float | None = None, key: str | None = None, show: bool | None = None, - save: bool | None = None, return_fig: bool = False, + save: bool | None = None, # deprecated **kwds, ): """Plot ranking of genes using dotplot plot (see :func:`~scanpy.pl.dotplot`). @@ -1002,8 +1010,8 @@ def rank_genes_groups_stacked_violin( # noqa: PLR0913 min_logfoldchange: float | None = None, key: str | None = None, show: bool | None = None, - save: bool | None = None, return_fig: bool = False, + save: bool | None = None, # deprecated **kwds, ): """Plot ranking of genes using stacked_violin plot. @@ -1090,8 +1098,8 @@ def rank_genes_groups_matrixplot( # noqa: PLR0913 min_logfoldchange: float | None = None, key: str | None = None, show: bool | None = None, - save: bool | None = None, return_fig: bool = False, + save: bool | None = None, # deprecated **kwds, ): """Plot ranking of genes using matrixplot plot (see :func:`~scanpy.pl.matrixplot`). @@ -1230,8 +1238,8 @@ def rank_genes_groups_violin( # noqa: PLR0913 size: int = 1, ax: Axes | None = None, show: bool | None = None, - save: bool | None = None, # deprecated + save: bool | None = None, scale: DensityNorm | Empty = _empty, ): """Plot ranking of genes for all tested comparisons. @@ -1348,8 +1356,9 @@ def sim( as_heatmap: bool = False, shuffle: bool = False, show: bool | None = None, - save: bool | str | None = None, marker: str | Sequence[str] = ".", + # deprecated + save: bool | str | None = None, ) -> None: """Plot results of simulation. @@ -1456,9 +1465,9 @@ def embedding_density( # noqa: PLR0912, PLR0913, PLR0915 wspace: None = None, title: str | None = None, show: bool | None = None, - save: bool | str | None = None, ax: Axes | None = None, return_fig: bool | None = None, + save: bool | str | None = None, # deprecated **kwargs, ) -> Figure | Axes | None: """Plot the density of cells in an embedding (per condition). diff --git a/src/scanpy/plotting/_tools/paga.py b/src/scanpy/plotting/_tools/paga.py index 5af2e50a52..5435ee08fd 100644 --- a/src/scanpy/plotting/_tools/paga.py +++ b/src/scanpy/plotting/_tools/paga.py @@ -341,8 +341,9 @@ def paga( # noqa: PLR0912, PLR0913, PLR0915 groups=None, # backwards compat plot: bool = True, show: bool | None = None, - save: bool | str | None = None, ax: Axes | None = None, + # deprecated + save: bool | str | None = None, ) -> Axes | list[Axes] | None: r"""Plot the PAGA graph through thresholding low-connectivity edges. @@ -1068,8 +1069,9 @@ def paga_path( # noqa: PLR0912, PLR0913, PLR0915 as_heatmap: bool = True, return_data: bool = False, show: bool | None = None, - save: bool | str | None = None, ax: Axes | None = None, + # deprecated + save: bool | str | None = None, ) -> tuple[Axes, pd.DataFrame] | Axes | pd.DataFrame | None: r"""Gene expression and annotation changes along paths in the abstracted graph. @@ -1360,6 +1362,7 @@ def paga_adjacency( as_heatmap: bool = True, color_map: str | Colormap | None = None, show: bool | None = None, + # deprecated save: bool | str | None = None, ) -> None: """Plot connectivity of paga groups.""" diff --git a/src/scanpy/plotting/_tools/scatterplots.py b/src/scanpy/plotting/_tools/scatterplots.py index cf172caf73..2b7f71b984 100644 --- a/src/scanpy/plotting/_tools/scatterplots.py +++ b/src/scanpy/plotting/_tools/scatterplots.py @@ -113,10 +113,10 @@ def embedding( # noqa: PLR0912, PLR0913, PLR0915 wspace: float | None = None, title: str | Sequence[str] | None = None, show: bool | None = None, - save: bool | str | None = None, ax: Axes | None = None, return_fig: bool | None = None, marker: str | Sequence[str] = ".", + save: bool | str | None = None, # deprecated **kwargs, ) -> Figure | Axes | list[Axes] | None: """Scatter plot for user specified embedding basis (e.g. umap, pca, etc). @@ -837,7 +837,7 @@ def pca( annotate_var_explained: bool = False, show: bool | None = None, return_fig: bool | None = None, - save: bool | str | None = None, + save: bool | str | None = None, # deprecated **kwargs, ) -> Figure | Axes | list[Axes] | None: """Scatter plot in PCA coordinates. @@ -953,7 +953,7 @@ def spatial( # noqa: PLR0913 na_color: ColorLike | None = None, show: bool | None = None, return_fig: bool | None = None, - save: bool | str | None = None, + save: bool | str | None = None, # deprecated **kwargs, ) -> Figure | Axes | list[Axes] | None: """Scatter plot in spatial coordinates. diff --git a/src/scanpy/plotting/_utils.py b/src/scanpy/plotting/_utils.py index 23b7c1835a..55d5da0e9f 100644 --- a/src/scanpy/plotting/_utils.py +++ b/src/scanpy/plotting/_utils.py @@ -94,8 +94,9 @@ def matrix( # noqa: PLR0913 colorbar_shrink: float = 0.5, color_map: str | Colormap | None = None, show: bool | None = None, - save: bool | str | None = None, ax: Axes | None = None, + # deprecated + save: bool | str | None = None, ) -> None: """Plot a matrix.""" if ax is None: @@ -341,13 +342,17 @@ def savefig_or_show( # append it writekey += save save = True - save = settings.autosave if save is None else save - show = settings.autoshow if show is None else show - if save: + if do_save := settings.autosave if save is None else save: + if save: # `save=True | "some-str"` argument has been used + msg = ( + "Argument `save` is deprecated and will be removed in a future version. " + "Use `sc.pl.plot(show=False).figure.savefig()` instead." + ) + warnings.warn(msg, category=FutureWarning, stacklevel=2) savefig(writekey, dpi=dpi, ext=ext) - if show: + if settings.autoshow if show is None else show: plt.show() - if save: + if do_save: plt.close() # clear figure diff --git a/tests/test_plotting_embedded/test_embeddings.py b/tests/test_plotting_embedded/test_embeddings.py index acfa6f9b06..e8d4331218 100644 --- a/tests/test_plotting_embedded/test_embeddings.py +++ b/tests/test_plotting_embedded/test_embeddings.py @@ -1,5 +1,6 @@ from __future__ import annotations +import uuid from functools import partial, wraps from pathlib import Path from typing import TYPE_CHECKING @@ -251,3 +252,20 @@ def test_embedding_colorbar_location(image_comparer): sc.pl.pca(adata, color="LDHB", colorbar_loc=None, show=False) save_and_compare_images("no_colorbar") + + +def test_raise_save_future_warning(tmp_path: Path) -> None: + adata = pbmc3k_processed() + + unique_id = str(uuid.uuid4())[:8] + test_filename = f"test_violin_{unique_id}.png" + + sc.settings.figdir, original_figdir = tmp_path, sc.settings.figdir + try: + with pytest.warns(FutureWarning, match=r"Argument `save` is deprecated"): + sc.pl.violin(adata, keys="louvain", save=test_filename, show=False) + + expected_file = tmp_path / f"violin{test_filename}" + assert expected_file.exists(), f"Expected file {expected_file} was not created" + finally: + sc.settings.figdir = original_figdir