diff --git a/.coveragerc b/.coveragerc index bcf28e09c..f65ab1441 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,9 +1,18 @@ [run] branch = True source = nibabel -include = */nibabel/* omit = */externals/* */benchmarks/* - */tests/* nibabel/_version.py + +[report] +exclude_also = + def __repr__ + if (ty\.|typing\.)?TYPE_CHECKING: + class .*\((ty\.|typing\.)Protocol\): + @(ty\.|typing\.)overload + if 0: + if __name__ == .__main__.: + @(abc\.)?abstractmethod + raise NotImplementedError diff --git a/nibabel/_compression.py b/nibabel/_compression.py index eeb66f36b..f697fa54c 100644 --- a/nibabel/_compression.py +++ b/nibabel/_compression.py @@ -17,7 +17,7 @@ from .optpkg import optional_package -if ty.TYPE_CHECKING: # pragma: no cover +if ty.TYPE_CHECKING: import indexed_gzip # type: ignore[import] import pyzstd diff --git a/nibabel/arrayproxy.py b/nibabel/arrayproxy.py index 4bf5bd470..ed2310519 100644 --- a/nibabel/arrayproxy.py +++ b/nibabel/arrayproxy.py @@ -57,7 +57,7 @@ KEEP_FILE_OPEN_DEFAULT = False -if ty.TYPE_CHECKING: # pragma: no cover +if ty.TYPE_CHECKING: import numpy.typing as npt from typing_extensions import Self # PY310 @@ -75,19 +75,17 @@ class ArrayLike(ty.Protocol): shape: tuple[int, ...] @property - def ndim(self) -> int: ... # pragma: no cover + def ndim(self) -> int: ... # If no dtype is passed, any dtype might be returned, depending on the array-like @ty.overload - def __array__( - self, dtype: None = ..., / - ) -> np.ndarray[ty.Any, np.dtype[ty.Any]]: ... # pragma: no cover + def __array__(self, dtype: None = ..., /) -> np.ndarray[ty.Any, np.dtype[ty.Any]]: ... # Any dtype might be passed, and *that* dtype must be returned @ty.overload - def __array__(self, dtype: _DType, /) -> np.ndarray[ty.Any, _DType]: ... # pragma: no cover + def __array__(self, dtype: _DType, /) -> np.ndarray[ty.Any, _DType]: ... - def __getitem__(self, key, /) -> npt.NDArray: ... # pragma: no cover + def __getitem__(self, key, /) -> npt.NDArray: ... class ArrayProxy(ArrayLike): diff --git a/nibabel/dataobj_images.py b/nibabel/dataobj_images.py index e84ac8567..685059901 100644 --- a/nibabel/dataobj_images.py +++ b/nibabel/dataobj_images.py @@ -19,7 +19,7 @@ from .filebasedimages import FileBasedHeader, FileBasedImage from .fileholders import FileMap -if ty.TYPE_CHECKING: # pragma: no cover +if ty.TYPE_CHECKING: import numpy.typing as npt from .filename_parser import FileSpec diff --git a/nibabel/deprecated.py b/nibabel/deprecated.py index b8c378cee..15d3e5326 100644 --- a/nibabel/deprecated.py +++ b/nibabel/deprecated.py @@ -8,7 +8,7 @@ from .deprecator import Deprecator from .pkg_info import cmp_pkg_version -if ty.TYPE_CHECKING: # pragma: no cover +if ty.TYPE_CHECKING: P = ty.ParamSpec('P') diff --git a/nibabel/deprecator.py b/nibabel/deprecator.py index 010b1be23..83118dd53 100644 --- a/nibabel/deprecator.py +++ b/nibabel/deprecator.py @@ -9,7 +9,7 @@ import warnings from textwrap import dedent -if ty.TYPE_CHECKING: # pragma: no cover +if ty.TYPE_CHECKING: T = ty.TypeVar('T') P = ty.ParamSpec('P') diff --git a/nibabel/filebasedimages.py b/nibabel/filebasedimages.py index 4e0d06b64..c12644a2b 100644 --- a/nibabel/filebasedimages.py +++ b/nibabel/filebasedimages.py @@ -20,7 +20,7 @@ from .filename_parser import TypesFilenamesError, _stringify_path, splitext_addext, types_filenames from .openers import ImageOpener -if ty.TYPE_CHECKING: # pragma: no cover +if ty.TYPE_CHECKING: from .filename_parser import ExtensionSpec, FileSpec FileSniff = ty.Tuple[bytes, str] @@ -54,13 +54,13 @@ def from_header(klass: type[HdrT], header: FileBasedHeader | ty.Mapping | None = @classmethod def from_fileobj(klass: type[HdrT], fileobj: io.IOBase) -> HdrT: - raise NotImplementedError # pragma: no cover + raise NotImplementedError def write_to(self, fileobj: io.IOBase) -> None: - raise NotImplementedError # pragma: no cover + raise NotImplementedError def __eq__(self, other: object) -> bool: - raise NotImplementedError # pragma: no cover + raise NotImplementedError def __ne__(self, other: object) -> bool: return not self == other @@ -251,7 +251,7 @@ def from_filename(klass: type[ImgT], filename: FileSpec) -> ImgT: @classmethod def from_file_map(klass: type[ImgT], file_map: FileMap) -> ImgT: - raise NotImplementedError # pragma: no cover + raise NotImplementedError @classmethod def filespec_to_file_map(klass, filespec: FileSpec) -> FileMap: @@ -308,7 +308,7 @@ def to_filename(self, filename: FileSpec, **kwargs) -> None: self.to_file_map(**kwargs) def to_file_map(self, file_map: FileMap | None = None, **kwargs) -> None: - raise NotImplementedError # pragma: no cover + raise NotImplementedError @classmethod def make_file_map(klass, mapping: ty.Mapping[str, str | io.IOBase] | None = None) -> FileMap: @@ -373,7 +373,7 @@ def from_image(klass: type[ImgT], img: FileBasedImage) -> ImgT: img : ``FileBasedImage`` instance Image, of our own class """ - raise NotImplementedError # pragma: no cover + raise NotImplementedError @classmethod def _sniff_meta_for( diff --git a/nibabel/filename_parser.py b/nibabel/filename_parser.py index bdbca6a38..d2c23ae6e 100644 --- a/nibabel/filename_parser.py +++ b/nibabel/filename_parser.py @@ -14,7 +14,7 @@ import pathlib import typing as ty -if ty.TYPE_CHECKING: # pragma: no cover +if ty.TYPE_CHECKING: FileSpec = str | os.PathLike[str] ExtensionSpec = tuple[str, str | None] diff --git a/nibabel/loadsave.py b/nibabel/loadsave.py index 159d9bae8..e39aeceba 100644 --- a/nibabel/loadsave.py +++ b/nibabel/loadsave.py @@ -26,7 +26,7 @@ _compressed_suffixes = ('.gz', '.bz2', '.zst') -if ty.TYPE_CHECKING: # pragma: no cover +if ty.TYPE_CHECKING: from .filebasedimages import FileBasedImage from .filename_parser import FileSpec diff --git a/nibabel/onetime.py b/nibabel/onetime.py index fa1b2f992..5018ba90c 100644 --- a/nibabel/onetime.py +++ b/nibabel/onetime.py @@ -137,12 +137,10 @@ def __init__(self, func: ty.Callable[[InstanceT], T]) -> None: @ty.overload def __get__( self, obj: None, objtype: type[InstanceT] | None = None - ) -> ty.Callable[[InstanceT], T]: ... # pragma: no cover + ) -> ty.Callable[[InstanceT], T]: ... @ty.overload - def __get__( - self, obj: InstanceT, objtype: type[InstanceT] | None = None - ) -> T: ... # pragma: no cover + def __get__(self, obj: InstanceT, objtype: type[InstanceT] | None = None) -> T: ... def __get__( self, obj: InstanceT | None, objtype: type[InstanceT] | None = None diff --git a/nibabel/openers.py b/nibabel/openers.py index f84ccb706..c3fa9a478 100644 --- a/nibabel/openers.py +++ b/nibabel/openers.py @@ -18,7 +18,7 @@ from ._compression import HAVE_INDEXED_GZIP, IndexedGzipFile, pyzstd -if ty.TYPE_CHECKING: # pragma: no cover +if ty.TYPE_CHECKING: from types import TracebackType from _typeshed import WriteableBuffer @@ -36,9 +36,8 @@ @ty.runtime_checkable class Fileish(ty.Protocol): - def read(self, size: int = -1, /) -> bytes: ... # pragma: no cover - - def write(self, b: bytes, /) -> int | None: ... # pragma: no cover + def read(self, size: int = -1, /) -> bytes: ... + def write(self, b: bytes, /) -> int | None: ... class DeterministicGzipFile(gzip.GzipFile): diff --git a/nibabel/pointset.py b/nibabel/pointset.py index e39a4d418..70a802480 100644 --- a/nibabel/pointset.py +++ b/nibabel/pointset.py @@ -30,7 +30,7 @@ from nibabel.fileslice import strided_scalar from nibabel.spatialimages import SpatialImage -if ty.TYPE_CHECKING: # pragma: no cover +if ty.TYPE_CHECKING: from typing_extensions import Self _DType = ty.TypeVar('_DType', bound=np.dtype[ty.Any]) @@ -41,12 +41,10 @@ class CoordinateArray(ty.Protocol): shape: tuple[int, int] @ty.overload - def __array__( - self, dtype: None = ..., / - ) -> np.ndarray[ty.Any, np.dtype[ty.Any]]: ... # pragma: no cover + def __array__(self, dtype: None = ..., /) -> np.ndarray[ty.Any, np.dtype[ty.Any]]: ... @ty.overload - def __array__(self, dtype: _DType, /) -> np.ndarray[ty.Any, _DType]: ... # pragma: no cover + def __array__(self, dtype: _DType, /) -> np.ndarray[ty.Any, _DType]: ... @dataclass diff --git a/nibabel/spatialimages.py b/nibabel/spatialimages.py index 185694cd7..96f8115a2 100644 --- a/nibabel/spatialimages.py +++ b/nibabel/spatialimages.py @@ -154,7 +154,7 @@ except ImportError: # PY38 from functools import lru_cache as cache -if ty.TYPE_CHECKING: # pragma: no cover +if ty.TYPE_CHECKING: import numpy.typing as npt SpatialImgT = ty.TypeVar('SpatialImgT', bound='SpatialImage') @@ -162,18 +162,15 @@ class HasDtype(ty.Protocol): - def get_data_dtype(self) -> np.dtype: ... # pragma: no cover - - def set_data_dtype(self, dtype: npt.DTypeLike) -> None: ... # pragma: no cover + def get_data_dtype(self) -> np.dtype: ... + def set_data_dtype(self, dtype: npt.DTypeLike) -> None: ... @ty.runtime_checkable class SpatialProtocol(ty.Protocol): - def get_data_dtype(self) -> np.dtype: ... # pragma: no cover - - def get_data_shape(self) -> ty.Tuple[int, ...]: ... # pragma: no cover - - def get_zooms(self) -> ty.Tuple[float, ...]: ... # pragma: no cover + def get_data_dtype(self) -> np.dtype: ... + def get_data_shape(self) -> ty.Tuple[int, ...]: ... + def get_zooms(self) -> ty.Tuple[float, ...]: ... class HeaderDataError(Exception): diff --git a/nibabel/volumeutils.py b/nibabel/volumeutils.py index 379d654a3..29b954dbb 100644 --- a/nibabel/volumeutils.py +++ b/nibabel/volumeutils.py @@ -24,7 +24,7 @@ from .casting import OK_FLOATS, shared_range from .externals.oset import OrderedSet -if ty.TYPE_CHECKING: # pragma: no cover +if ty.TYPE_CHECKING: import numpy.typing as npt Scalar = np.number | float @@ -1191,13 +1191,13 @@ def _ftype4scaled_finite( @ty.overload def finite_range( arr: npt.ArrayLike, check_nan: ty.Literal[False] = False -) -> tuple[Scalar, Scalar]: ... # pragma: no cover +) -> tuple[Scalar, Scalar]: ... @ty.overload def finite_range( arr: npt.ArrayLike, check_nan: ty.Literal[True] -) -> tuple[Scalar, Scalar, bool]: ... # pragma: no cover +) -> tuple[Scalar, Scalar, bool]: ... def finite_range( diff --git a/nibabel/xmlutils.py b/nibabel/xmlutils.py index 5049a7641..5d079e117 100644 --- a/nibabel/xmlutils.py +++ b/nibabel/xmlutils.py @@ -20,7 +20,7 @@ class XmlSerializable: def _to_xml_element(self) -> Element: """Output should be a xml.etree.ElementTree.Element""" - raise NotImplementedError # pragma: no cover + raise NotImplementedError def to_xml(self, enc='utf-8', **kwargs) -> bytes: r"""Generate an XML bytestring with a given encoding. @@ -109,10 +109,10 @@ def parse(self, string=None, fname=None, fptr=None): parser.ParseFile(fptr) def StartElementHandler(self, name, attrs): - raise NotImplementedError # pragma: no cover + raise NotImplementedError def EndElementHandler(self, name): - raise NotImplementedError # pragma: no cover + raise NotImplementedError def CharacterDataHandler(self, data): - raise NotImplementedError # pragma: no cover + raise NotImplementedError diff --git a/pyproject.toml b/pyproject.toml index 4df5886d7..ff5168f9c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,11 +67,12 @@ doc = [ "tomli; python_version < '3.11'", ] test = [ - "pytest<8.1", # relax once pytest-doctestplus releases 1.2.0 + "pytest", "pytest-doctestplus", "pytest-cov", "pytest-httpserver", "pytest-xdist", + "coverage>=7.2", ] # Remaining: Simpler to centralize in tox dev = ["tox"]