Skip to content

Commit

Permalink
Better error messages
Browse files Browse the repository at this point in the history
  • Loading branch information
flying-sheep committed Jan 13, 2025
1 parent 977ae5d commit d6fb426
Show file tree
Hide file tree
Showing 16 changed files with 58 additions and 53 deletions.
6 changes: 3 additions & 3 deletions src/scanpy/_utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1076,7 +1076,7 @@ def __init__(self, adata: AnnData, key=None):
self._dists_key = "distances"
else:
if key not in adata.uns:
msg = f'No "{key}" in .uns'
msg = f"No {key!r} in .uns"
raise KeyError(msg)

Check warning on line 1080 in src/scanpy/_utils/__init__.py

View check run for this annotation

Codecov / codecov/patch

src/scanpy/_utils/__init__.py#L1079-L1080

Added lines #L1079 - L1080 were not covered by tests
self._neighbors_dict = adata.uns[key]
self._conns_key = self._neighbors_dict["connectivities_key"]
Expand Down Expand Up @@ -1110,12 +1110,12 @@ def __getitem__(self, key: Literal["connectivities_key"]) -> str: ...
def __getitem__(self, key: str):
if key == "distances":
if "distances" not in self:
msg = f'No "{self._dists_key}" in .obsp'
msg = f"No {self._dists_key!r} in .obsp"
raise KeyError(msg)

Check warning on line 1114 in src/scanpy/_utils/__init__.py

View check run for this annotation

Codecov / codecov/patch

src/scanpy/_utils/__init__.py#L1113-L1114

Added lines #L1113 - L1114 were not covered by tests
return self._distances
elif key == "connectivities":
if "connectivities" not in self:
msg = f'No "{self._conns_key}" in .obsp'
msg = f"No {self._conns_key!r} in .obsp"
raise KeyError(msg)

Check warning on line 1119 in src/scanpy/_utils/__init__.py

View check run for this annotation

Codecov / codecov/patch

src/scanpy/_utils/__init__.py#L1118-L1119

Added lines #L1118 - L1119 were not covered by tests
return self._connectivities
elif key == "connectivities_key":
Expand Down
6 changes: 3 additions & 3 deletions src/scanpy/get/get.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,23 +170,23 @@ def _check_indices(
if key in dim_df.columns:
col_keys.append(key)
if key in alt_names.index:
msg = f"The key '{key}' is found in both adata.{dim} and {alt_repr}.{alt_search_repr}."
msg = f"The key {key!r} is found in both adata.{dim} and {alt_repr}.{alt_search_repr}."
raise KeyError(msg)
elif key in alt_names.index:
val = alt_names[key]
if isinstance(val, pd.Series):
# while var_names must be unique, adata.var[gene_symbols] does not
# It's still ambiguous to refer to a duplicated entry though.
assert alias_index is not None
msg = f"Found duplicate entries for '{key}' in {alt_repr}.{alt_search_repr}."
msg = f"Found duplicate entries for {key!r} in {alt_repr}.{alt_search_repr}."
raise KeyError(msg)
index_keys.append(val)
index_aliases.append(key)
else:
not_found.append(key)
if len(not_found) > 0:
msg = (
f"Could not find keys '{not_found}' in columns of `adata.{dim}` or in"
f"Could not find keys {not_found!r} in columns of `adata.{dim}` or in"
f" {alt_repr}.{alt_search_repr}."
)
raise KeyError(msg)
Expand Down
31 changes: 19 additions & 12 deletions src/scanpy/plotting/_anndata.py
Original file line number Diff line number Diff line change
Expand Up @@ -2284,20 +2284,12 @@ def _reorder_categories_after_dendrogram(
'var_group_labels', and 'var_group_positions'
"""

dendrogram_key = _get_dendrogram_key(adata, dendrogram_key, groupby)

if isinstance(groupby, str):
groupby = [groupby]

dendro_info = adata.uns[dendrogram_key]
if groupby != dendro_info["groupby"]:
msg = (
"Incompatible observations. The precomputed dendrogram contains "
f"information for the observation: '{groupby}' while the plot is "
f"made for the observation: '{dendro_info['groupby']}. "
"Please run `sc.tl.dendrogram` using the right observation.'"
)
raise ValueError(msg)
dendro_info = adata.uns[
_get_dendrogram_key(adata, dendrogram_key, groupby, validate_groupby=True)
]

if categories is None:
categories = adata.obs[dendro_info["groupby"]].cat.categories
Expand Down Expand Up @@ -2371,7 +2363,11 @@ def _format_first_three_categories(categories):


def _get_dendrogram_key(
adata: AnnData, dendrogram_key: str | None, groupby: str | Sequence[str]
adata: AnnData,
dendrogram_key: str | None,
groupby: str | Sequence[str],
*,
validate_groupby: bool = False,
) -> str:
# the `dendrogram_key` can be a bool an NoneType or the name of the
# dendrogram key. By default the name of the dendrogram key is 'dendrogram'
Expand Down Expand Up @@ -2401,6 +2397,17 @@ def _get_dendrogram_key(
)
raise ValueError(msg)

Check warning on line 2398 in src/scanpy/plotting/_anndata.py

View check run for this annotation

Codecov / codecov/patch

src/scanpy/plotting/_anndata.py#L2398

Added line #L2398 was not covered by tests

if validate_groupby:
existing_groupby = adata.uns[dendrogram_key]["groupby"]
if groupby != existing_groupby:
msg = (

Check warning on line 2403 in src/scanpy/plotting/_anndata.py

View check run for this annotation

Codecov / codecov/patch

src/scanpy/plotting/_anndata.py#L2403

Added line #L2403 was not covered by tests
"Incompatible observations. The precomputed dendrogram contains "
f"information for the observation: {groupby!r} while the plot is "
f"made for the observation: {existing_groupby!r}. "
"Please run `sc.tl.dendrogram` using the right observation.'"
)
raise ValueError(msg)

Check warning on line 2409 in src/scanpy/plotting/_anndata.py

View check run for this annotation

Codecov / codecov/patch

src/scanpy/plotting/_anndata.py#L2409

Added line #L2409 was not covered by tests

return dendrogram_key


Expand Down
14 changes: 4 additions & 10 deletions src/scanpy/plotting/_baseplot_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -899,17 +899,11 @@ def _format_first_three_categories(_categories):
_categories = _categories[:3] + ["etc."]
return ", ".join(_categories)

key = _get_dendrogram_key(self.adata, dendrogram_key, self.groupby)

dendro_info = self.adata.uns[key]
if self.groupby != dendro_info["groupby"]:
msg = (
"Incompatible observations. The precomputed dendrogram contains "
f"information for the observation: '{self.groupby}' while the plot is "
f"made for the observation: '{dendro_info['groupby']}. "
"Please run `sc.tl.dendrogram` using the right observation.'"
dendro_info = self.adata.uns[
_get_dendrogram_key(
self.adata, dendrogram_key, self.groupby, validate_groupby=True
)
raise ValueError(msg)
]

# order of groupby categories
categories_idx_ordered = dendro_info["categories_idx_ordered"]
Expand Down
4 changes: 2 additions & 2 deletions src/scanpy/plotting/_tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ def rank_genes_groups(
if n_genes < 1:
msg = (

Check warning on line 401 in src/scanpy/plotting/_tools/__init__.py

View check run for this annotation

Codecov / codecov/patch

src/scanpy/plotting/_tools/__init__.py#L401

Added line #L401 was not covered by tests
"Specifying a negative number for n_genes has not been implemented for "
f"this plot. Received n_genes={n_genes}."
f"this plot. Received {n_genes=!r}."
)
raise NotImplementedError(msg)

Check warning on line 405 in src/scanpy/plotting/_tools/__init__.py

View check run for this annotation

Codecov / codecov/patch

src/scanpy/plotting/_tools/__init__.py#L405

Added line #L405 was not covered by tests

Expand Down Expand Up @@ -1535,7 +1535,7 @@ def embedding_density(

if f"X_{basis}" not in adata.obsm_keys():
msg = (

Check warning on line 1537 in src/scanpy/plotting/_tools/__init__.py

View check run for this annotation

Codecov / codecov/patch

src/scanpy/plotting/_tools/__init__.py#L1537

Added line #L1537 was not covered by tests
f"Cannot find the embedded representation `adata.obsm[X_{basis!r}]`. "
f"Cannot find the embedded representation `adata.obsm['X_{basis}']`. "
"Compute the embedding first."
)
raise ValueError(msg)

Check warning on line 1541 in src/scanpy/plotting/_tools/__init__.py

View check run for this annotation

Codecov / codecov/patch

src/scanpy/plotting/_tools/__init__.py#L1541

Added line #L1541 was not covered by tests
Expand Down
2 changes: 1 addition & 1 deletion src/scanpy/plotting/_tools/paga.py
Original file line number Diff line number Diff line change
Expand Up @@ -1166,7 +1166,7 @@ def moving_average(a):
if node not in groups_names_set:
msg = (

Check warning on line 1167 in src/scanpy/plotting/_tools/paga.py

View check run for this annotation

Codecov / codecov/patch

src/scanpy/plotting/_tools/paga.py#L1167

Added line #L1167 was not covered by tests
f"Each node/group needs to be in {groups_names.tolist()} "
f"(`groups_key`={groups_key!r}) not {node!r}."
f"({groups_key=!r}) not {node!r}."
)
raise ValueError(msg)

Check warning on line 1171 in src/scanpy/plotting/_tools/paga.py

View check run for this annotation

Codecov / codecov/patch

src/scanpy/plotting/_tools/paga.py#L1171

Added line #L1171 was not covered by tests
nodes_ints.append(groups_names.get_loc(node))
Expand Down
6 changes: 3 additions & 3 deletions src/scanpy/plotting/_tools/scatterplots.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ def embedding(
use_raw = layer is None and adata.raw is not None
if use_raw and layer is not None:
msg = (
"Cannot use both a layer and the raw representation. Was passed:"
f"use_raw={use_raw}, layer={layer}."
"Cannot use both a layer and the raw representation. "
f"Was passed: {use_raw=!r}, {layer=!r}."
)
raise ValueError(msg)
if use_raw and adata.raw is None:
Expand Down Expand Up @@ -1167,7 +1167,7 @@ def _get_basis(adata: AnnData, basis: str) -> np.ndarray:
elif f"X_{basis}" in adata.obsm:
return adata.obsm[f"X_{basis}"]
else:
msg = f"Could not find '{basis}' or 'X_{basis}' in .obsm"
msg = f"Could not find {basis!r} or 'X_{basis}' in .obsm"
raise KeyError(msg)

Check warning on line 1171 in src/scanpy/plotting/_tools/scatterplots.py

View check run for this annotation

Codecov / codecov/patch

src/scanpy/plotting/_tools/scatterplots.py#L1170-L1171

Added lines #L1170 - L1171 were not covered by tests


Expand Down
4 changes: 2 additions & 2 deletions src/scanpy/plotting/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ def _validate_palette(adata: AnnData, key: str) -> None:
else:
logg.warning(
f"The following color value found in adata.uns['{key}_colors'] "
f"is not valid: '{color}'. Default colors will be used instead."
f"is not valid: {color!r}. Default colors will be used instead."
)
_set_default_colors_for_categorical_obs(adata, key)
_palette = None
Expand Down Expand Up @@ -633,7 +633,7 @@ def scatter_group(

color = rgb2hex(adata.uns[key + "_colors"][cat_code])
if not is_color_like(color):
msg = f'"{color}" is not a valid matplotlib color.'
msg = f"{color!r} is not a valid matplotlib color."
raise ValueError(msg)

Check warning on line 637 in src/scanpy/plotting/_utils.py

View check run for this annotation

Codecov / codecov/patch

src/scanpy/plotting/_utils.py#L636-L637

Added lines #L636 - L637 were not covered by tests
data = [Y[mask_obs, 0], Y[mask_obs, 1]]
if projection == "3d":
Expand Down
2 changes: 1 addition & 1 deletion src/scanpy/preprocessing/_highly_variable_genes.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def _highly_variable_genes_seurat_v3(

if check_values and not check_nonnegative_integers(data):
warnings.warn(
f"`flavor='{flavor}'` expects raw count data, but non-integers were found.",
f"`{flavor=!r}` expects raw count data, but non-integers were found.",
UserWarning,
)

Expand Down
2 changes: 1 addition & 1 deletion src/scanpy/preprocessing/_pca/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ def pca(
min_dim = min(adata_comp.n_vars, adata_comp.n_obs)
n_comps = min_dim - 1 if min_dim <= settings.N_PCS else settings.N_PCS

logg.info(f" with n_comps={n_comps}")
logg.info(f" with {n_comps=}")

X = _get_obs_rep(adata_comp, layer=layer)
if is_backed_type(X) and layer is not None:
Expand Down
2 changes: 1 addition & 1 deletion src/scanpy/preprocessing/_qc.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def _choose_mtx_rep(adata, *, use_raw: bool = False, layer: str | None = None):
if use_raw and is_layer:
msg = (

Check warning on line 35 in src/scanpy/preprocessing/_qc.py

View check run for this annotation

Codecov / codecov/patch

src/scanpy/preprocessing/_qc.py#L35

Added line #L35 was not covered by tests
"Cannot use expression from both layer and raw. You provided:"
f"'use_raw={use_raw}' and 'layer={layer}'"
f"{use_raw=!r} and {layer=!r}"
)
raise ValueError(msg)

Check warning on line 39 in src/scanpy/preprocessing/_qc.py

View check run for this annotation

Codecov / codecov/patch

src/scanpy/preprocessing/_qc.py#L39

Added line #L39 was not covered by tests
if is_layer:
Expand Down
22 changes: 13 additions & 9 deletions src/scanpy/readwrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from ._utils import _empty

if TYPE_CHECKING:
from datetime import datetime
from typing import BinaryIO, Literal

from ._utils import Empty
Expand Down Expand Up @@ -221,7 +222,7 @@ def read_10x_h5(
if genome:
if genome not in adata.var["genome"].values:
msg = (
f"Could not find data corresponding to genome '{genome}' in '{filename}'. "
f"Could not find data corresponding to genome {genome!r} in {filename}. "
f"Available genomes are: {list(adata.var['genome'].unique())}."
)
raise ValueError(msg)
Expand All @@ -231,29 +232,32 @@ def read_10x_h5(
if adata.is_view:
adata = adata.copy()
else:
adata = _read_legacy_10x_h5(filename, genome=genome, start=start)
adata = _read_legacy_10x_h5(Path(filename), genome=genome, start=start)
return adata


def _read_legacy_10x_h5(filename, *, genome=None, start=None):
def _read_legacy_10x_h5(
path: Path, *, genome: str | None = None, start: datetime | None = None
):
"""
Read hdf5 file from Cell Ranger v2 or earlier versions.
"""
with h5py.File(str(filename), "r") as f:
with h5py.File(str(path), "r") as f:
try:
children = list(f.keys())
if not genome:
if len(children) > 1:
msg = (
f"'{filename}' contains more than one genome. For legacy 10x h5 "
"files you must specify the genome if more than one is present. "
f"{path} contains more than one genome. "
"For legacy 10x h5 files you must specify the genome "
"if more than one is present. "
f"Available genomes are: {children}"
)
raise ValueError(msg)
genome = children[0]
elif genome not in children:
msg = (
f"Could not find genome '{genome}' in '{filename}'. "
f"Could not find genome {genome!r} in {path}. "
f"Available genomes are: {children}"
)
raise ValueError(msg)
Expand Down Expand Up @@ -475,10 +479,10 @@ def read_visium(
if not f.exists():
if any(x in str(f) for x in ["hires_image", "lowres_image"]):
logg.warning(
f"You seem to be missing an image file.\nCould not find '{f}'."
f"You seem to be missing an image file.\nCould not find {f}."
)
else:
msg = f"Could not find '{f}'"
msg = f"Could not find {f}"
raise OSError(msg)

Check warning on line 486 in src/scanpy/readwrite.py

View check run for this annotation

Codecov / codecov/patch

src/scanpy/readwrite.py#L485-L486

Added lines #L485 - L486 were not covered by tests

adata.uns["spatial"][library_id]["images"] = dict()
Expand Down
4 changes: 2 additions & 2 deletions src/scanpy/tools/_dpt.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@


def _diffmap(adata, n_comps=15, neighbors_key=None, random_state=0):
start = logg.info(f"computing Diffusion Maps using n_comps={n_comps}(=n_dcs)")
start = logg.info(f"computing Diffusion Maps using {n_comps=}(=n_dcs)")
dpt = DPT(adata, neighbors_key=neighbors_key)
dpt.compute_transitions()
dpt.compute_eigen(n_comps=n_comps, random_state=random_state)
Expand Down Expand Up @@ -153,7 +153,7 @@ def dpt(
allow_kendall_tau_shift=allow_kendall_tau_shift,
neighbors_key=neighbors_key,
)
start = logg.info(f"computing Diffusion Pseudotime using n_dcs={n_dcs}")
start = logg.info(f"computing Diffusion Pseudotime using {n_dcs=}")
if n_branchings > 1:
logg.info(" this uses a hierarchical implementation")
if dpt.iroot is not None:
Expand Down
2 changes: 1 addition & 1 deletion src/scanpy/tools/_ingest.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ def __init__(self, dim, axis=0, vals=None):
def __setitem__(self, key, value):
if value.shape[self._axis] != self._dim:
msg = (

Check warning on line 191 in src/scanpy/tools/_ingest.py

View check run for this annotation

Codecov / codecov/patch

src/scanpy/tools/_ingest.py#L191

Added line #L191 was not covered by tests
f"Value passed for key '{key}' is of incorrect shape. "
f"Value passed for key {key!r} is of incorrect shape. "
f"Value has shape {value.shape[self._axis]} "
f"for dimension {self._axis} while "
f"it should have {self._dim}."
Expand Down
2 changes: 1 addition & 1 deletion src/scanpy/tools/_leiden.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def leiden(
"""
if flavor not in {"igraph", "leidenalg"}:
msg = (
f"flavor must be either 'igraph' or 'leidenalg', but '{flavor}' was passed"
f"flavor must be either 'igraph' or 'leidenalg', but {flavor!r} was passed"
)
raise ValueError(msg)
_utils.ensure_igraph()
Expand Down
2 changes: 1 addition & 1 deletion src/scanpy/tools/_utils_clustering.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def restrict_adjacency(
raise ValueError(msg)

Check warning on line 41 in src/scanpy/tools/_utils_clustering.py

View check run for this annotation

Codecov / codecov/patch

src/scanpy/tools/_utils_clustering.py#L40-L41

Added lines #L40 - L41 were not covered by tests
for c in restrict_categories:
if c not in adata.obs[restrict_key].cat.categories:
msg = f"'{c}' is not a valid category for '{restrict_key}'"
msg = f"{c!r} is not a valid category for {restrict_key!r}"
raise ValueError(msg)

Check warning on line 45 in src/scanpy/tools/_utils_clustering.py

View check run for this annotation

Codecov / codecov/patch

src/scanpy/tools/_utils_clustering.py#L44-L45

Added lines #L44 - L45 were not covered by tests
restrict_indices = adata.obs[restrict_key].isin(restrict_categories).values
adjacency = adjacency[restrict_indices, :]
Expand Down

0 comments on commit d6fb426

Please sign in to comment.