Skip to content

qten.validations

Package reference for qten.validations.

validations

Validation decorators and symbolic validation helpers.

This package centralizes the runtime validation layer used across QTen dataclasses and symbolic objects. The core API lets a dataclass declare validators once, run them automatically after construction, run them manually on an existing object, or temporarily suppress construction-time validation when building intermediate states.

Core exports
  • need_validation() Attach validators to a dataclass and run them after __init__.
  • validate() Execute registered validators explicitly on an existing instance.
  • no_validate Temporarily suppress construction-time validation in the current thread.

Exported API

need_validation

need_validation(
    *validators: ValidatorFn,
) -> Callable[[_C], _C]

Decorate a dataclass so its __init__ runs inherited validators.

The decorator is dataclass-only. It materializes __validators__ on the decorated class by collecting validator lists from the MRO in base-to- derived order, appending any validators passed directly to the decorator, and deduplicating by validator object identity.

Validation happens after the original dataclass __init__ completes, which means dataclass __post_init__ normalization has already run. Use no_validate to suppress only automatic construction-time validation; explicit calls to validate() still run validators.

Validator contract

A validator is any callable with the signature validator(instance) -> None. It should return None when the instance is valid and raise a user-facing exception, usually TypeError or ValueError, when validation fails.

Parameters:

Name Type Description Default
*validators ValidatorFn

Validator callables to append to the validators inherited from base classes. Passing no validators is valid and preserves inherited validators on the decorated class.

()

Returns:

Type Description
Callable[[_C], _C]

A class decorator that attaches the combined validator chain to the dataclass and returns the same class object.

Raises:

Type Description
TypeError

If the decorator is applied to a class that is not a dataclass.

Examples:

from dataclasses import dataclass

from qten.validations import need_validation


def check_positive(instance: "PositiveValue") -> None:
    if instance.value <= 0:
        raise ValueError("value must be positive")


@need_validation(check_positive)
@dataclass(frozen=True)
class PositiveValue:
    value: int


PositiveValue(1)
Source code in src/qten/validations/_validations.py
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
def need_validation(
    *validators: ValidatorFn,
) -> Callable[[_C], _C]:
    """
    Decorate a dataclass so its ``__init__`` runs inherited validators.

    The decorator is dataclass-only. It materializes ``__validators__`` on the
    decorated class by collecting validator lists from the MRO in base-to-
    derived order, appending any validators passed directly to the decorator,
    and deduplicating by validator object identity.

    Validation happens after the original dataclass `__init__` completes, which
    means dataclass `__post_init__` normalization has already run. Use
    [`no_validate`][qten.validations.no_validate] to suppress only automatic
    construction-time validation; explicit calls to
    [`validate()`][qten.validations.validate] still run validators.

    Validator contract
    ------------------
    A validator is any callable with the signature `validator(instance) -> None`.
    It should return `None` when the instance is valid and raise a user-facing
    exception, usually `TypeError` or `ValueError`, when validation fails.

    Parameters
    ----------
    *validators : ValidatorFn
        Validator callables to append to the validators inherited from base
        classes. Passing no validators is valid and preserves inherited
        validators on the decorated class.

    Returns
    -------
    Callable[[_C], _C]
        A class decorator that attaches the combined validator chain to the
        dataclass and returns the same class object.

    Raises
    ------
    TypeError
        If the decorator is applied to a class that is not a dataclass.

    Examples
    --------
    ```python
    from dataclasses import dataclass

    from qten.validations import need_validation


    def check_positive(instance: "PositiveValue") -> None:
        if instance.value <= 0:
            raise ValueError("value must be positive")


    @need_validation(check_positive)
    @dataclass(frozen=True)
    class PositiveValue:
        value: int


    PositiveValue(1)
    ```
    """

    def decorator(cls: _C) -> _C:
        if not is_dataclass(cls):
            raise TypeError("@need_validation can only be applied to dataclasses.")

        inherited: List[ValidatorFn] = []
        for base in reversed(cls.__mro__[1:]):
            base_validators = cast(
                tuple[ValidatorFn, ...], getattr(base, "__validators__", ())
            )
            for validator in base_validators:
                if validator not in inherited:
                    inherited.append(validator)

        combined = inherited.copy()
        for validator in validators:
            if validator not in combined:
                combined.append(validator)
        setattr(cls, "__validators__", combined)

        original_init = cls.__init__

        def run_validators(self: Any) -> None:
            if _validation_disabled_depth() > 0:
                return
            for validator in _class_validators(cls):
                validator(self)

        @wraps(original_init)
        def wrapped(self: Any, *args: Any, **kwargs: Any) -> None:
            original_init(self, *args, **kwargs)
            run_validators(self)

        setattr(cls, "__init__", wrapped)
        return cls

    return decorator

no_validate

Bases: ContextDecorator

Temporarily disable validation in the current thread.

While this context manager is active, construction-time validators installed by need_validation() do not run. The suppression state is thread-local and nestable, so entering nested no_validate() blocks only reenables validation after the outermost block exits.

Because this class inherits from contextlib.ContextDecorator, it can also be used as a function decorator around construction helper functions.

Examples:

from dataclasses import dataclass

from qten.validations import need_validation, no_validate


def check_positive(instance: "PositiveValue") -> None:
    if instance.value <= 0:
        raise ValueError("value must be positive")


@need_validation(check_positive)
@dataclass(frozen=True)
class PositiveValue:
    value: int


with no_validate():
    instance = PositiveValue(0)

@no_validate()
def build_intermediate():
    return PositiveValue(0)

__enter__

__enter__() -> 'no_validate'
Source code in src/qten/validations/_validations.py
83
84
85
86
def __enter__(self) -> "no_validate":
    previous_depth = _validation_disabled_depth()
    _VALIDATION_STATE.disabled_depth = previous_depth + 1
    return self

__exit__

__exit__(
    exc_type: type[BaseException] | None,
    exc: BaseException | None,
    tb: Any,
) -> None
Source code in src/qten/validations/_validations.py
88
89
90
91
92
93
94
95
96
97
98
def __exit__(
    self,
    exc_type: type[BaseException] | None,
    exc: BaseException | None,
    tb: Any,
) -> None:
    previous_depth = _validation_disabled_depth() - 1
    if previous_depth == 0:
        delattr(_VALIDATION_STATE, "disabled_depth")
    else:
        _VALIDATION_STATE.disabled_depth = previous_depth

validate

validate(v: Any) -> None

Run the configured validator chain for v immediately.

Unlike construction-time validation, this function ignores no_validate and always executes the validators attached to type(v). Validators run in the same order used by construction-time validation: inherited validators first, followed by validators declared on the concrete class.

Parameters:

Name Type Description Default
v Any

The value to validate.

required

Raises:

Type Description
TypeError

If the type of v is not configured for validation.

Examples:

from dataclasses import dataclass

from qten.validations import need_validation, validate


def check_nonempty(instance: "Label") -> None:
    if not instance.name:
        raise ValueError("name must be non-empty")


@need_validation(check_nonempty)
@dataclass(frozen=True)
class Label:
    name: str


label = Label("site")
validate(label)
Source code in src/qten/validations/_validations.py
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
def validate(v: Any) -> None:
    """
    Run the configured validator chain for ``v`` immediately.

    Unlike construction-time validation, this function ignores
    [`no_validate`][qten.validations.no_validate] and always executes the
    validators attached to `type(v)`. Validators run in the same order used by
    construction-time validation: inherited validators first, followed by
    validators declared on the concrete class.

    Parameters
    ----------
    v : Any
        The value to validate.

    Raises
    ------
    TypeError
        If the type of ``v`` is not configured for validation.

    Examples
    --------
    ```python
    from dataclasses import dataclass

    from qten.validations import need_validation, validate


    def check_nonempty(instance: "Label") -> None:
        if not instance.name:
            raise ValueError("name must be non-empty")


    @need_validation(check_nonempty)
    @dataclass(frozen=True)
    class Label:
        name: str


    label = Label("site")
    validate(label)
    ```
    """
    cls = type(v)
    if not hasattr(cls, "__validators__"):
        raise TypeError(
            f"{cls.__name__} is not configured for validation. "
            "Decorate the dataclass with @need_validation(...) before calling validate()."
        )
    for validator in _class_validators(cls):
        validator(v)