API reference#
The public surface, in the order a caller meets it. This page is curated: it lists the names you import, not every symbol in the package. The task pages explain how they compose, and Errors and limits states what each failure means.
import tenspec loads neither array library. Import tenspec.numpy or tenspec.torch for the
declarations of the library you use.
The three entry points#
- tenspec.checked(function)#
Check the declared arguments and the declared return of one call.
One scope spans the argument checks, the body, and the return check, so the operands and the return share their dimension, dtype and device bindings. Every call starts fresh bindings, so a later call can use other extents. Tenspec replaces an accepted array only when its declaration names a transform. A caller’s own validator keeps its own behavior.
Place it under an ordinary @classmethod or @staticmethod, closest to the function. A method that returns Self is checked against the class that received the call.
- Returns:
A function with the same signature, which checks its declarations on every call.
- Raises:
AnnotationError – for an async function, and for a callable that is not a plain Python function. This release checks neither.
- Parameters:
function (Callable[[P], R])
- Return type:
Callable[[P], R]
- class tenspec.TensorContracts#
Check the tensor fields of one model together, in one boundary.
Place it first among the bases, as class M(TensorContracts, ProjectBase). It adds no base of its own and keeps the caller’s model configuration, strictness, and validators. The caller permits arbitrary types for the array fields.
Each model validation opens its own boundary, so a nested model binds independently. Tenspec replaces a field value only when its declaration names a transform, such as CopyReadOnly, and it copies or seals nothing else. A caller’s own validator keeps its own behavior. A tensor field checks its default the way it checks a supplied value.
Preparation runs where Pydantic builds the model’s schema, so Pydantic has already resolved every annotation in the caller’s own namespace. A declaration written as a string, as a name local to the function that defines the model, or as a name defined after the model, therefore reaches preparation as a real type. The subclass keeps what the caller declared in __tensor_declarations__.
- tenspec.validate(value, annotation)#
Check value against annotation, and return the accepted value.
One call owns one boundary, so every tensor inside a tuple declaration shares the dimension and dtype bindings. Two calls never relate their names. No binding stays active after the call returns.
A repeated annotation reuses its prepared schema. Every declared check still runs against this call’s own values, inside this call’s own bindings.
validate((a, b), tuple[Float[Shape[“rows”]], Float[Shape[“rows”]]]) refuses two arrays whose first axis disagrees.
- Returns:
The accepted value. Tenspec returns an array by identity unless its declaration names a transform. A caller’s own validator can still replace it.
- Parameters:
value (Any)
annotation (TypeForm)
- Return type:
T
Backend aliases#
Each alias takes a shape first, then any transforms, then any properties:
Float[Shape["rows features"]], or Float[Shape["rows features"], Finite] with a property. The
spellings are the same in tenspec.numpy and tenspec.torch.
Alias |
Accepts |
|---|---|
|
any supported floating format |
|
any supported signed integer format |
|
any supported unsigned integer format |
|
any supported complex format |
|
the boolean format |
|
that exact floating format |
|
that exact signed integer format |
|
that exact unsigned integer format |
|
that exact complex format |
tenspec.torch adds BFloat16, and the device markers a placement requirement names. See
Dtype for what each backend
represents, and Device for why NumPy takes no device.
These are parameterized type aliases. The table gives the spelling a caller writes, because the expanded form carries Tenspec’s internal declaration rather than anything a caller types.
Writing a shape#
tenspec.Shape is a public re-export of typing.Literal. A shape is its single string
argument, as Shape["rows features"].
Write from typing import Literal as Shape in your own code. The re-export works at run time,
but Ruff 0.16.6 does not follow it and reports a false diagnostic against the shape text. See
Ruff and the Shape alias.
Common operations#
A property checks a value and changes nothing. A transform replaces the value every later operation and the caller receive. A declaration names every transform before its properties.
- class tenspec.Finite#
Every element of the array is a finite number.
- class tenspec.NonEmpty#
The array holds at least one element.
- class tenspec.Contiguous#
The array uses C-contiguous storage.
- class tenspec.Materialized#
The array holds actual data, rather than metadata alone.
These four import from tenspec and work on either backend. The next two are NumPy’s own,
because they read a NumPy writeable flag:
- class tenspec.numpy.ReadOnly#
This array’s own writeable flag is off.
It is a check of that flag. It is not a seal, and it never promises that another reference to the same storage cannot change the values.
- class tenspec.numpy.CopyReadOnly#
Replace the array with a read-only copy of it, on separate storage.
The caller’s array keeps its own values and its writeable flag. The stored copy shares no memory with it, and refuses an ordinary write. This is not immutability: a caller who reaches the copy’s base array can still change the values.
It copies on every validation, so it costs time and memory each time.
CopyReadOnly against ReadOnly states what neither
one promises.
Reading a bound axis#
- tenspec.axis_size(name)#
The extent bound to name in the boundary that is validating now.
Call it inside a checked function, a model validator, or a standalone validation. It reads a bound dimension and never selects a tensor or reparses a shape.
- Returns:
The extent bound to the name.
- Raises:
BindingError – with reason NO_SCOPE outside a scope, UNBOUND for a name this boundary never bound, and NOT_SCALAR for a variadic group.
- Parameters:
name (str)
- Return type:
int
Errors#
- class tenspec.AnnotationError#
A declaration is invalid, unsupported, or never reached Tenspec’s preparation.
- class tenspec.ConstraintEvaluationError#
A backend cannot inspect a declared property of this value.
This never reports a measured mismatch. It reports that the check could not run.
- class tenspec.errors.TensorMismatch(*, requirement, observed, bindings=None)#
An array disagrees with its declared requirement.
The requirement, the observed fact, and the relevant bindings stay readable on the error. The error holds no array.
- Parameters:
requirement (str)
observed (str)
bindings (Mapping[str, int | tuple[int, ...]] | None)
- class tenspec.errors.BindingError(reason, name=None)#
A required dimension was unavailable, rather than different.
The reason says which lookup failed. The name is the dimension the caller asked for.
- Parameters:
reason (BindingReason)
name (str | None)
- class tenspec.errors.TransformContractError(*, transform, fact, before, after)#
A transform changed a structural fact that it had to preserve.
This reports a defect in the transform, not invalid caller input, so it is a RuntimeError and never a ValueError. It names the transform and the one fact that changed. Every field is text, so the error holds no array.
- Parameters:
transform (str)
fact (str)
before (str)
after (str)
The error classes says where each one is raised, and which of them a caller normally catches.
The advanced interfaces#
Each lower name keeps its defining module. Use them to check arrays outside an annotation, or to add a backend.
Name |
Module |
What it is |
|---|---|---|
|
|
one resolved declaration, prepared once and reused for many arrays |
|
|
the one-shot form of the same check |
|
|
the whole requirement: shape, dtype, device, transforms, properties |
|
|
the shape string as a |
|
|
the facts one boundary holds, and the boundary itself |
|
|
a declaration as an ordinary Pydantic annotation |
|
|
the seam a new array library implements |
import numpy as np
from tenspec.numpy import NUMPY_ARRAYS
from tenspec.runtime.bindings import validation_scope
from tenspec.runtime.validation import ArrayValidator
from tenspec.types.dtypes import DTypeFamily, DTypeRequirement
from tenspec.types.properties import NonEmpty
from tenspec.types.shapes import parse_shape
from tenspec.types.tensor import TensorType
requirement = TensorType(
shape=parse_shape("rows cols"),
dtype=DTypeRequirement(permitted=DTypeFamily.FLOATING),
properties=(NonEmpty,),
)
validator = ArrayValidator(tensor_type=requirement, backend=NUMPY_ARRAYS)
with validation_scope() as bindings:
validator.validate(np.ones((3, 4)), bindings=bindings)
print(dict(bindings.dimensions))
ArrayValidator resolves its operations once, at construction, and refuses a requirement the
backend cannot answer there. It holds no array and no bindings, so one validator serves many
calls. validate_array does the same work in one call, and pays the preparation each time.
An ArrayBackend reads facts and compares nothing: shape, dtype, device, native_dtype
and operation_formats. It receives neither a TensorType nor a Bindings, so a backend
cannot read a declaration or decide whether two values agree. The runtime does that.