Failures and limits#
The error classes#
Error |
Import from |
Base |
Raised when |
|---|---|---|---|
|
|
|
a declaration is invalid, unsupported, or reached Pydantic with no entry point |
|
|
|
an array disagrees with its declared requirement |
|
|
|
a dimension lookup found no extent. Its |
|
|
|
a backend cannot inspect a declared property of this value, such as |
|
|
|
a transform returned an array that broke its preservation contract |
Where the error is raised decides how you catch it. Pydantic wraps a ValueError raised
inside its own validation callbacks, and Tenspec’s check runs in one of those. So a
TensorMismatch, and a BindingError raised while a value is being checked, arrive inside a
ValidationError. A model validator is one of those callbacks too. An error raised in the body
of a checked function is not: it propagates as itself. axis_size in a function body, or
outside every boundary, therefore raises BindingError directly, as
Axis scope and axis_size shows.
TensorMismatch keeps its parts readable, and it holds no array:
from typing import Literal as Shape
import numpy as np
from pydantic import ValidationError
from tenspec import NonEmpty, validate
from tenspec.errors import TensorMismatch
from tenspec.numpy import Float
try:
validate((np.ones(3), np.ones(5)), tuple[Float[Shape["rows"]], Float[Shape["rows"]]])
except ValidationError as refusal:
entry = refusal.errors()[0]
original = entry["ctx"]["error"]
print(entry["loc"], isinstance(original, TensorMismatch))
print(original.requirement, "|", original.observed, "|", original.bindings)
try:
validate(np.zeros((2, 0)), Float[Shape["rows cols"], NonEmpty])
except ValidationError as refusal:
print(refusal.errors()[0]["msg"])
What this release does not check#
Case |
What happens |
|---|---|
an |
|
a callable that is not a plain Python function |
|
a union mixing tensor and ordinary non-tensor alternatives |
|
a tensor alias that contains its own name |
|
a device requirement on NumPy |
|
a Tenspec declaration on a model without the mixin |
|
A declaration relates values inside one boundary. It does not relate the length of a Python sequence to an axis, and it does not relate an axis of a nested model to one of its parent. Write those by hand, as in A relation the declaration cannot express.
Strict JSON input for models#
TensorContracts owns a model’s binding scope through a Pydantic wrap validator. An upstream
Pydantic limitation means that strict model_validate_json can reject JSON representations of
tuples, datetimes, decimals, sets, frozen sets and bytes. Nested models can be affected even when
they declare no tensor. This behavior was reproduced with Pydantic 2.14.0.
The wrapper still reports JSON mode, but its handler receives Python objects. For example, a strict tuple field receives a list and refuses it. See Pydantic issue 9204.
Validation of native Python values remains available. Array serialization and deserialization need an explicit representation; Tenspec does not provide one. If an application already has a separate storage model, parse its metadata with ordinary Pydantic, reconstruct native values and arrays, then validate the computational model. Decoding JSON into a dictionary alone does not perform that conversion.
The scope wrapper releases its bindings on success and failure. Changing its lifetime to work around this JSON limitation must preserve that behavior.
What a declaration does not promise#
No immutability.
CopyReadOnlyandReadOnlycontrol a copy and a flag, not access through a base array.No promise about a later value. A check happens at its boundary. See An owned computed result.
Nothing about what an outer validator returns. An
AfterValidatorruns after the check and owns its own result.No byte order. A logical alias such as
Float64accepts either byte order. Transform preservation compares the native dtype, so a transform may not change it.No claim about operability. A backend that represents a format says nothing about whether your installed NumPy or Torch can compute with it.
No static shape check. No type checker compares two shapes.