diff --git a/src/scanpy/external/exporting.py b/src/scanpy/external/exporting.py index 60605a3330..5c8db72580 100644 --- a/src/scanpy/external/exporting.py +++ b/src/scanpy/external/exporting.py @@ -577,11 +577,8 @@ def cellbrowser( # noqa: PLR0913 """ try: import cellbrowser.cellbrowser as cb - except ImportError: - logg.error( - "The package cellbrowser is not installed. " - "Install with 'pip install cellbrowser' and retry." - ) + except ImportError as e: + e.add_note("Please install `cellbrowser` and try again.") raise data_dir = str(data_dir) diff --git a/src/scanpy/external/pp/_bbknn.py b/src/scanpy/external/pp/_bbknn.py index feda84542a..1621153fc5 100644 --- a/src/scanpy/external/pp/_bbknn.py +++ b/src/scanpy/external/pp/_bbknn.py @@ -133,8 +133,8 @@ def bbknn( # noqa: PLR0913 try: from bbknn import bbknn except ImportError as e: - msg = "Please install bbknn: `pip install bbknn`." - raise ImportError(msg) from e + e.add_note("Please install `bbknn` and try again.") + raise return bbknn( adata=adata, batch_key=batch_key, diff --git a/src/scanpy/external/pp/_dca.py b/src/scanpy/external/pp/_dca.py index 2973da752f..12d93c2c53 100644 --- a/src/scanpy/external/pp/_dca.py +++ b/src/scanpy/external/pp/_dca.py @@ -180,8 +180,8 @@ def dca( # noqa: PLR0913 try: from dca.api import dca except ImportError as e: - msg = "Please install dca package (>= 0.2.1) via `pip install dca`" - raise ImportError(msg) from e + e.add_note("Please install `dca` (≥ 0.2.1) and try again.") + raise e return dca( adata, diff --git a/src/scanpy/external/pp/_harmony_integrate.py b/src/scanpy/external/pp/_harmony_integrate.py index 4fd908d955..d4e9982fef 100644 --- a/src/scanpy/external/pp/_harmony_integrate.py +++ b/src/scanpy/external/pp/_harmony_integrate.py @@ -90,8 +90,8 @@ def harmony_integrate( try: import harmonypy except ImportError as e: - msg = "\nplease install harmonypy:\n\n\tpip install harmonypy" - raise ImportError(msg) from e + e.add_note("Please install `harmonypy` and try again.") + raise x = adata.obsm[basis].astype(np.float64) diff --git a/src/scanpy/external/pp/_magic.py b/src/scanpy/external/pp/_magic.py index a30a7964b2..bd6ed74046 100644 --- a/src/scanpy/external/pp/_magic.py +++ b/src/scanpy/external/pp/_magic.py @@ -140,8 +140,8 @@ def magic( # noqa: PLR0913 try: from magic import MAGIC except ImportError as e: - msg = "Please install magic package via `pip install magic-impute`" - raise ImportError(msg) from e + e.add_note("Please install `magic-impute` and try again.") + raise else: if pkg_version("magic-impute") < Version(MIN_VERSION): msg = ( diff --git a/src/scanpy/external/pp/_mnn_correct.py b/src/scanpy/external/pp/_mnn_correct.py index 53fbd93170..1bef795ef0 100644 --- a/src/scanpy/external/pp/_mnn_correct.py +++ b/src/scanpy/external/pp/_mnn_correct.py @@ -133,8 +133,8 @@ def mnn_correct( # noqa: PLR0913 import mnnpy from mnnpy import mnn_correct except ImportError as e: - msg = "Please install the package mnnpy (https://github.com/chriscainx/mnnpy). " - raise ImportError(msg) from e + e.add_note("Please install `mnnpy` and try again.") + raise n_jobs = settings.n_jobs if n_jobs is None else n_jobs diff --git a/src/scanpy/external/pp/_scanorama_integrate.py b/src/scanpy/external/pp/_scanorama_integrate.py index c02c351c1f..1809491570 100644 --- a/src/scanpy/external/pp/_scanorama_integrate.py +++ b/src/scanpy/external/pp/_scanorama_integrate.py @@ -109,8 +109,8 @@ def scanorama_integrate( try: import scanorama except ImportError as e: - msg = "\nplease install Scanorama:\n\n\tpip install scanorama" - raise ImportError(msg) from e + e.add_note("Please install `scanorama` and try again.") + raise # Get batch indices in linear time. curr_batch = None diff --git a/src/scanpy/external/tl/_harmony_timeseries.py b/src/scanpy/external/tl/_harmony_timeseries.py index be2da92169..2e2e3e6e02 100644 --- a/src/scanpy/external/tl/_harmony_timeseries.py +++ b/src/scanpy/external/tl/_harmony_timeseries.py @@ -135,8 +135,8 @@ def harmony_timeseries( try: import harmony except ImportError as e: - msg = "\nplease install harmony:\n\n\tpip install harmonyTS" - raise ImportError(msg) from e + e.add_note("Please install `harmonyTS` and try again.") + raise adata = adata.copy() if copy else adata logg.info("Harmony augmented affinity matrix") diff --git a/src/scanpy/external/tl/_palantir.py b/src/scanpy/external/tl/_palantir.py index 1d4e580687..5fb46bb09d 100644 --- a/src/scanpy/external/tl/_palantir.py +++ b/src/scanpy/external/tl/_palantir.py @@ -333,9 +333,9 @@ def palantir_results( return pr_res -def _check_import(): +def _check_import() -> None: try: import palantir # noqa: F401 except ImportError as e: - msg = "\nplease install palantir:\n\tpip install palantir" - raise ImportError(msg) from e + e.add_note("Please install `palantir` and try again.") + raise diff --git a/src/scanpy/external/tl/_phate.py b/src/scanpy/external/tl/_phate.py index 16905273a8..9986260709 100644 --- a/src/scanpy/external/tl/_phate.py +++ b/src/scanpy/external/tl/_phate.py @@ -152,11 +152,8 @@ def phate( # noqa: PLR0913 try: import phate except ImportError as e: - msg = ( - "You need to install the package `phate`: please run `pip install " - "--user phate` in a terminal." - ) - raise ImportError(msg) from e + e.add_note("Please install `phate` and try again.") + raise x_phate = phate.PHATE( n_components=n_components, k=k, diff --git a/src/scanpy/external/tl/_sam.py b/src/scanpy/external/tl/_sam.py index e805d4b26f..eed58ba6a0 100644 --- a/src/scanpy/external/tl/_sam.py +++ b/src/scanpy/external/tl/_sam.py @@ -206,13 +206,8 @@ def sam( # noqa: PLR0913 try: from samalg import SAM except ImportError as e: - msg = ( - "\nplease install sam-algorithm: \n\n" - "\tgit clone git://github.com/atarashansky/self-assembling-manifold.git\n" - "\tcd self-assembling-manifold\n" - "\tpip install ." - ) - raise ImportError(msg) from e + e.add_note("Please install `sc-sam` and try again.") + raise logg.info("Self-assembling manifold") diff --git a/src/scanpy/external/tl/_trimap.py b/src/scanpy/external/tl/_trimap.py index b670863f13..5b3d9859ed 100644 --- a/src/scanpy/external/tl/_trimap.py +++ b/src/scanpy/external/tl/_trimap.py @@ -103,8 +103,8 @@ def trimap( # noqa: PLR0913 try: from trimap import TRIMAP except ImportError as e: - msg = "\nplease install trimap: \n\n\tsudo pip install trimap" - raise ImportError(msg) from e + e.add_note("Please install `trimap` and try again.") + raise adata = adata.copy() if copy else adata start = logg.info("computing TriMap") adata = adata.copy() if copy else adata diff --git a/src/scanpy/external/tl/_wishbone.py b/src/scanpy/external/tl/_wishbone.py index 0fff78dec2..fe18b65efc 100644 --- a/src/scanpy/external/tl/_wishbone.py +++ b/src/scanpy/external/tl/_wishbone.py @@ -103,8 +103,8 @@ def wishbone( try: from wishbone.core import wishbone as c_wishbone except ImportError as e: - msg = "\nplease install wishbone:\n\n\thttps://github.com/dpeerlab/wishbone" - raise ImportError(msg) from e + e.add_note("Please install `wishbone` and try again.") + raise # Start cell index s = np.where(adata.obs_names == start_cell)[0] diff --git a/src/scanpy/metrics/_metrics.py b/src/scanpy/metrics/_metrics.py index d6df6c252d..d6ac63a439 100644 --- a/src/scanpy/metrics/_metrics.py +++ b/src/scanpy/metrics/_metrics.py @@ -205,8 +205,11 @@ def modularity_array( try: import igraph as ig except ImportError as e: # pragma: no cover - msg = "igraph is require for computing modularity" - raise ImportError(msg) from e + e.add_note( + "`igraph` is required for computing modularity. " + "Please install `igraph` and try again." + ) + raise igraph_mode: str = ig.ADJ_DIRECTED if is_directed else ig.ADJ_UNDIRECTED graph: ig.Graph = ig.Graph.Weighted_Adjacency(connectivities, mode=igraph_mode) return graph.modularity(_codes(labels)) diff --git a/src/scanpy/plotting/_utils.py b/src/scanpy/plotting/_utils.py index 91c79ccd3b..05e159f158 100644 --- a/src/scanpy/plotting/_utils.py +++ b/src/scanpy/plotting/_utils.py @@ -1132,12 +1132,12 @@ def _create_white_to_color_gradient( popt = np.get_printoptions() try: import colour - except ImportError: - msg = ( - "Please install the `colour-science` package to use `group_colors`: " - "`pip install colour-science` or `pip install scanpy[plotting]`" + except ImportError as e: + e.add_note( + "`colour-science` is required for using `group_colors`. " + "Please install `scanpy[plotting]` (or `colour-science` directly) and try again." ) - raise ImportError(msg) from None + raise finally: # https://github.com/colour-science/colour/issues/1388 np.set_printoptions(legacy=popt["legacy"]) diff --git a/src/scanpy/preprocessing/_highly_variable_genes.py b/src/scanpy/preprocessing/_highly_variable_genes.py index 26d5a7763e..f980ac2533 100644 --- a/src/scanpy/preprocessing/_highly_variable_genes.py +++ b/src/scanpy/preprocessing/_highly_variable_genes.py @@ -157,8 +157,8 @@ def _highly_variable_genes_seurat_v3( # noqa: PLR0912, PLR0915 try: from skmisc.loess import loess except ImportError as e: - msg = "Please install skmisc package via `pip install --user scikit-misc" - raise ImportError(msg) from e + e.add_note("Please install `scikit-misc` and try again.") + raise df = pd.DataFrame(index=adata.var_names) data = _get_obs_rep(adata, layer=layer) raise_if_dask_feature_axis_chunked(data) diff --git a/src/scanpy/preprocessing/_scrublet/__init__.py b/src/scanpy/preprocessing/_scrublet/__init__.py index b17821c723..b8ea1a267c 100644 --- a/src/scanpy/preprocessing/_scrublet/__init__.py +++ b/src/scanpy/preprocessing/_scrublet/__init__.py @@ -181,7 +181,10 @@ def scrublet( # noqa: PLR0913 if threshold is None and not find_spec("skimage"): # pragma: no cover # Scrublet.call_doublets requires `skimage` with `threshold=None` but PCA # is called early, which is wasteful if there is not `skimage` - msg = "threshold is None and thus scrublet requires skimage, but skimage is not installed." + msg = ( + "`threshold` is None and thus scrublet requires `scikit-image`. " + "Please install `scanpy[scrublet]` (or `scikit-image` directly) and try again." + ) raise ValueError(msg) if copy: diff --git a/src/scanpy/queries/_queries.py b/src/scanpy/queries/_queries.py index 22e9a63603..791cc00505 100644 --- a/src/scanpy/queries/_queries.py +++ b/src/scanpy/queries/_queries.py @@ -67,8 +67,8 @@ def simple_query( try: from pybiomart import Server except ImportError as e: - msg = "This method requires the `pybiomart` module to be installed." - raise ImportError(msg) from e + e.add_note("Please install `pybiomart` and try again.") + raise server = Server(host, use_cache=use_cache) dataset = server.marts["ENSEMBL_MART_ENSEMBL"].datasets[f"{org}_gene_ensembl"] res = dataset.query(attributes=attrs, filters=filters, use_attr_names=True) @@ -278,8 +278,8 @@ def enrich( try: from gprofiler import GProfiler except ImportError as e: - msg = "This method requires the `gprofiler-official` module to be installed." - raise ImportError(msg) from e + e.add_note("Please install `gprofiler-official` and try again.") + raise gprofiler = GProfiler(user_agent="scanpy", return_dataframe=True) gprofiler_kwargs = dict(gprofiler_kwargs) for k in ["organism"]: diff --git a/src/scanpy/tools/_leiden.py b/src/scanpy/tools/_leiden.py index 4d8f954895..61967f2ce8 100644 --- a/src/scanpy/tools/_leiden.py +++ b/src/scanpy/tools/_leiden.py @@ -236,8 +236,10 @@ def _validate_flavor( try: import leidenalg # noqa: F401 except ImportError as e: - msg = "Please install the leiden algorithm: `conda install -c conda-forge leidenalg` or `pip install leidenalg`." - raise ImportError(msg) from e + e.add_note( + "Please install `scanpy[leiden]` (or `leidenalg` directly) and try again." + ) + raise flavor = "leidenalg" case _: msg = f"flavor must be either 'igraph' or 'leidenalg', but {flavor!r} was passed" diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 73f558766b..af35236c6a 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -1916,7 +1916,7 @@ def test_dotplot_group_colors_raises_error_on_missing_dep( markers = ["CD79A"] group_colors = {"CD19+ B": "blue"} - with pytest.raises(ImportError, match="pip install colour-science"): + with pytest.raises(ImportError, match=r"colour-science.*required"): sc.pl.dotplot( adata, markers,