Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ lint.select = [
"C4", # flake8-comprehensions
"D", # pydocstyle
"E", # Error detected by Pycodestyle
"EM", # Error messages
"F", # Errors detected by Pyflakes
"I", # isort
"RUF100", # Report unused noqa directives
Expand Down
29 changes: 14 additions & 15 deletions src/scverse_misc/_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,14 @@ def _check_namespace_signature(ns_class: type, cls: type, canonical_instance_nam

# Ensure there are at least two parameters (self and mdata)
if len(params) < 2:
raise TypeError(f"Namespace initializer must accept a {cls.__name__} instance as the second parameter.")
msg = f"Namespace initializer must accept a {cls.__name__} instance as the second parameter."
raise TypeError(msg)

# Get the second parameter (expected to be `canonical_instance_name`)
[_, param, *_] = params.values()
if param.annotation is inspect.Parameter.empty:
raise AttributeError(
f"Namespace initializer's second parameter must be annotated as the {cls.__name__!r} class, got empty annotation."
)
msg = f"Namespace initializer's second parameter must be annotated as the {cls.__name__!r} class, got empty annotation."
raise AttributeError(msg)

name_ok = param.name == canonical_instance_name

Expand All @@ -107,30 +107,28 @@ def _check_namespace_signature(ns_class: type, cls: type, canonical_instance_nam
type_hints = get_type_hints(ns_class.__init__) # type: ignore[misc] # https://github.com/python/mypy/issues/21236
resolved_type = type_hints.get(param.name, param.annotation)
except NameError as e:
raise NameError(
f"Namespace initializer's second parameter must be named {canonical_instance_name!r}, got '{param.name}'."
) from e
msg = f"Namespace initializer's second parameter must be named {canonical_instance_name!r}, got {param.name!r}."
raise NameError(msg) from e

type_ok = resolved_type is cls

match (name_ok, type_ok):
case (True, True):
return # Signature is correct.
case (False, True):
raise TypeError(
f"Namespace initializer's second parameter must be named {canonical_instance_name!r}, got {param.name!r}."
)
msg = f"Namespace initializer's second parameter must be named {canonical_instance_name!r}, got {param.name!r}."
raise TypeError(msg)
case (True, False):
type_repr = getattr(resolved_type, "__name__", str(resolved_type))
raise TypeError(
f"Namespace initializer's second parameter must be annotated as the {cls.__name__!r} class, got {type_repr!r}."
)
msg = f"Namespace initializer's second parameter must be annotated as the {cls.__name__!r} class, got {type_repr!r}."
raise TypeError(msg)
case _:
type_repr = getattr(resolved_type, "__name__", str(resolved_type))
raise TypeError(
msg = (
f"Namespace initializer's second parameter must be named {canonical_instance_name!r}, got {param.name!r}. "
f"And must be annotated as {cls.__name__!r}, got {type_repr!r}."
)
raise TypeError(msg)


def _create_namespace[NameSpT: ExtensionNamespace](
Expand All @@ -141,7 +139,8 @@ def _create_namespace[NameSpT: ExtensionNamespace](
def namespace(ns_class: type[NameSpT]) -> type[NameSpT]:
_check_namespace_signature(ns_class, cls, canonical_instance_name) # Perform the runtime signature check
if name in reserved_namespaces:
raise AttributeError(f"cannot override reserved attribute {name!r}")
msg = f"cannot override reserved attribute {name!r}"
raise AttributeError(msg)
elif hasattr(cls, name):
warnings.warn(
f"Overriding existing custom namespace {name!r} (on {cls.__name__!r})", UserWarning, stacklevel=2
Expand Down
9 changes: 6 additions & 3 deletions src/scverse_misc/datasets/_fetcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,12 @@ def download(file: FileEntry, /, dest: Path | None = None, processor: Processor
return download(replace(file, url=fallback, fallback_urls=None), dest=dest, processor=processor)
except (OSError, ValueError) as e:
exceptions.append(e)
raise ExceptionGroup(f"Could not download {file.name}", exceptions) from None
msg = f"Could not download {file.name}"
raise ExceptionGroup(msg, exceptions) from None

if entry.type not in _LOADERS:
raise KeyError(f"No loader registered for type {entry.type!r}. Available: {available_loaders()}")
msg = f"No loader registered for type {entry.type!r}. Available: {available_loaders()}"
raise KeyError(msg)
return cast("Loader[T]", _LOADERS[entry.type])(entry, target, download, **kwargs)


Expand All @@ -151,5 +153,6 @@ def _load_spatialdata(entry: DatasetEntry, target: Path, download: DownloadCB, /
download(entry.file(suffix=".zip"), dest=dest, processor=pooch.Unzip(extract_dir="."))
zarrs = sorted(dest.glob("*.zarr"))
if len(zarrs) != 1:
raise RuntimeError(f"Expected exactly one .zarr extracted under {dest}, found {len(zarrs)}: {zarrs}.")
msg = f"Expected exactly one .zarr extracted under {dest}, found {len(zarrs)}: {zarrs}."
raise RuntimeError(msg)
return sd.read_zarr(zarrs[0], **cast("dict[str, Any]", kwargs))
9 changes: 6 additions & 3 deletions src/scverse_misc/datasets/_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ def resolve_url(self, base_url: str | None = None) -> str:
return self.url
if base_url and self.s3_key:
return f"{base_url.rstrip('/')}/{self.s3_key}"
raise ValueError(f"FileEntry {self.name!r} has neither `url` nor `s3_key` (with a registry `base_url`).")
msg = f"FileEntry {self.name!r} has neither `url` nor `s3_key` (with a registry `base_url`)."
raise ValueError(msg)


@dataclass(frozen=True, slots=True)
Expand Down Expand Up @@ -68,9 +69,11 @@ def file(self, *, name: str | None = None, suffix: str | None = None) -> FileEnt
matches = [f for f in self.files if f.name.endswith(suffix)]
crit = f"suffix={suffix!r}"
else:
raise ValueError("Pass exactly one of `name` or `suffix`.")
msg = "Pass exactly one of `name` or `suffix`."
raise ValueError(msg)
if len(matches) != 1:
raise ValueError(f"Expected exactly one file with {crit} in {self.name!r}, found {len(matches)}.")
msg = f"Expected exactly one file with {crit} in {self.name!r}, found {len(matches)}."
raise ValueError(msg)
return matches[0]


Expand Down
Loading