helpers

class InterfaceCase[source]

An interface test helper that allows a given interface to generate and validate arrays in one of its formats.

Each instance of “interface test case” should be considered one of the potentially multiple realizations of a given interface. If an interface has multiple formats (eg. zarr’s different store s), then it should have several test helpers.

abstract property interface: Interface

The interface that this helper is for

classmethod array_from_case(case: ValidationCase, path: Path | None = None) NDArrayType | None[source]

Generate an array from the given validation case.

Returns None if an array can’t be generated for a specific case.

abstractmethod classmethod make_array(shape: tuple[int, ...]=(10, 10), dtype: str | type | Any | generic = <class 'float'>, path: Path | None = None, array: NDArrayType | None = None) NDArrayType | None[source]

Make an array from a shape and dtype, and a path if needed

Parameters:
  • shape – shape of the array

  • dtype – dtype of the array

  • path – Path, if needed to generate on disk

  • array – Rather than passing shape and dtype, pass a literal arraylike thing

classmethod validate_case(case: ValidationCase, path: Path) bool[source]

Validate a generated array against the annotation in the validation case.

Kept in the InterfaceCase in case an interface has specific needs aside from just validating against a model, but typically left as is.

If an array can’t be generated for a given case, returns None so that the calling function can know to skip rather than fail the case.

Raises exceptions if validation fails (or succeeds when it shouldn’t)

Parameters:
  • case (ValidationCase) – The validation case to validate.

  • path (Path) – Path to generate arrays into, if any.

Returns:

True if array is valid and was supposed to be,

or invalid and wasn’t supposed to be

classmethod skip(shape: tuple[int, ...], dtype: str | type | Any | generic) bool[source]

Whether a given interface should be skipped for the case

class ValidationCase(*, id: str | None = None, annotation_shape: tuple[int | str, ...] | tuple[tuple[int | str, ...], ...] = (10, 10, '*', '*'), annotation_dtype: str | type | ~typing.Any | ~numpy.generic | ~collections.abc.Sequence[str | type | ~typing.Any | ~numpy.generic] = (<class 'numpy.float16'>, <class 'numpy.float32'>, <class 'numpy.float64'>, <class 'numpy.float32'>, <class 'numpy.float64'>), shape: tuple[int, ...] = (10, 10, 2, 2), dtype: type | ~numpy.dtype = <class 'float'>, passes: bool = False, interface: type[~numpydantic.testing.helpers.InterfaceCase] | None = None, path: ~pathlib.Path | None = None, marks: set[str] = <factory>)[source]

Test case for validating an array.

Contains both the validating model and the parameterization for an array to test in a given interface

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

id: str | None

String identifying the validation case

annotation_shape: tuple[int | str, ...] | tuple[tuple[int | str, ...], ...]

Shape to use in computed annotation used to validate against

annotation_dtype: str | type | Any | generic | Sequence[str | type | Any | generic]

Dtype to use in computed annotation used to validate against

shape: tuple[int, ...]

Shape of the array to validate

dtype: type | dtype

Dtype of the array to validate

passes: bool

Whether the validation should pass or not

interface: type[InterfaceCase] | None

The interface test case to generate and validate the array with

path: Path | None

The path to generate arrays into, if any.

marks: set[str]

pytest marks to set for this test case

model_config = {'arbitrary_types_allowed': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

property annotation: NDArray[Any, Any]

Annotation used in the model we validate against

property model: type[BaseModel]

A model with a field array with the given annotation

property pytest_marks: list[MarkDecorator]

Instantiated pytest marks from ValidationCase.marks plus the interface name.

validate_case(path: Path | None = None) bool[source]

Whether the generated array correctly validated against the annotation, given the interface

Parameters:

path (pathlib.Path) – Directory to generate array into, if on disk.

Raises:

ValueError – if an interface is missing

array(path: Path) NDArrayType[source]

Generate an array for the validation case if we have an interface to do so

merge(other: ValidationCase | Sequence[ValidationCase]) ValidationCase[source]

Merge two validation cases

Dump both, excluding any unset fields, and merge, preferring other.

valid is True if and only if it is True in both.

skip() bool[source]

Whether this case should be skipped (eg. due to the interface case being incompatible with the requested dtype or shape)

emit_mypy_source() str | None[source]

Full .py source for one mypy test case, assigning the dtype/shape annotation to a variable annotated with the annotation_dtype/annotation_shape.

Returns None when the interface has no typing class, and thus has not opted-in to static typing.

merge_cases(*args: ValidationCase) ValidationCase[source]

Merge multiple validation cases

merged_product(*args: Sequence[ValidationCase], conditions: dict = None) list[ValidationCase][source]

Generator for the product of the iterators of validation cases, merging each tuple, and respecting if they should be ValidationCase.skip() or not.

Examples

shape_cases = [
    ValidationCase(shape=(10, 10, 10), passes=True, id="valid shape"),
    ValidationCase(shape=(10, 10), passes=False, id="missing dimension"),
]
dtype_cases = [
    ValidationCase(dtype=float, passes=True, id="float"),
    ValidationCase(dtype=int, passes=False, id="int"),
]

iterator = merged_product(shape_cases, dtype_cases))
next(iterator)
# ValidationCase(
#     shape=(10, 10, 10),
#     dtype=float,
#     passes=True,
#     id="valid shape-float"
# )
next(iterator)
# ValidationCase(
#     shape=(10, 10, 10),
#     dtype=int,
#     passes=False,
#     id="valid shape-int"
# )