typing-validation
A library to perform runtime validation of Python objects using type hints.
Install
Install the latest release from PyPI:
$ pip install --upgrade typing-validation
Usage
Validate a value against a type hint. validate returns True on success and raises on failure:
>>> from typing_validation import validate
>>> validate([1, 2, 3], list[int])
True
The True return exists so that validation can be gated behind an assertion, and compiled out entirely under -O:
assert validate(val, t)
When a value does not conform, the error says where:
>>> validate({"a": [1, "b"]}, dict[str, list[int]])
Traceback (most recent call last):
...
typing_validation.errors.ValidationError: For type dict[str, list[int]]: a component failed
For type list[int] value at key 'a': a component failed
For type <class 'int'> index 1: not an instance, got 'b'
The structured explanation is on the exception, for reading programmatically:
>>> try:
... validate([1, "b"], list[int])
... except ValidationError as e:
... print(e.failure.causes[0].location.at)
1
Validating the same type repeatedly
validate analyses the type on every call. When you validate many values against one type, validator analyses it once and hands back a function:
>>> from typing_validation import validator
>>> check = validator(list[int])
>>> check([1, 2, 3])
True
Same contract, same verdict, 2.7× faster per call, and it repays the cost of building it within a handful of values.
And when the values keep coming in very large numbers, compiled_validator emits Python specialised to the type and compiles it:
>>> from typing_validation import compiled_validator
>>> check = compiled_validator(list[int])
>>> check([1, 2, 3])
True
That runs at 23 ns per type-node against a hand-written check's 23 — it is, to within the noise, the code you would have written yourself.
It costs more to build, and it only helps where there is structure to unroll: for a recursive alias or a NumPy array it stops unrolling and hands back a validator, and the table says never rather than pretending otherwise.
So: validate for one-off checks, validator when the type is fixed and the values keep coming, compiled_validator when there are very many of them.
| Type | validate |
validator |
compiled_validator |
hand-written |
|---|---|---|---|---|
list[int] (1000 items) |
115.6 µs | 42.9 µs | 22.5 µs | 23.1 µs |
list[int] (20 items) |
2.7 µs | 1.0 µs | 561 ns | 541 ns |
dict[str, int] (20 items) |
5.2 µs | 1.9 µs | 1.0 µs | 1.0 µs |
tuple[int, str] |
724 ns | 362 ns | 150 ns | 127 ns |
int |
95 ns | 92 ns | 89 ns | 56 ns |
benchmark/REPORT.md has the full table — every case, both outcomes, construction costs, and the break-even points that say exactly how many values each mechanism needs before it repays — with the machine it was measured on.
Absolute figures move a long way with that machine; the ratios between the columns move far less, and are what these rows are for.
It also measures the library against seven others, in ten configurations, which benchmark/PEER-COMPARISON.md reads and draws conclusions from.
Run python -m benchmark for your own numbers, or python -m benchmark --write to regenerate the report.
One difference, and it is deliberate. Both validator and compiled_validator analyse the whole type before seeing any value, so they reject an unsupported type immediately:
validator(list[Callable[[int], int]]) # UnsupportedTypeError, at once
validate([], list[Callable[[int], int]]) # True — no value reached the Callable
The rest of the surface
from typing_validation import is_valid, validated, validated_iter
is_valid([1, "a"], list[int]) # False — a boolean, at boolean prices
validated(payload, list[int]) # returns payload, for use in an expression
validated_iter(stream, Iterator[int]) # checks each item as it is yielded
is_valid deliberately builds no explanation: a caller who wants one calls validate and catches the exception.
validated_iter is not a convenience wrapper. Determining the items of a one-shot iterator consumes it, so Iterator[int] cannot check its items eagerly without destroying the value — checking them on the way past is the only honest way.
Asking about a type
Support is all-or-nothing: if any component of a type is unsupported, the whole type is. can_validate answers up front:
>>> from typing_validation import can_validate, inspect_type
>>> can_validate(list[int])
True
>>> can_validate(tuple[int, Callable[[int], int]]) # poisoned by the Callable
False
inspect_type returns the whole structure and names precisely what poisoned it, so "unsupported" is never opaque:
>>> node = inspect_type(tuple[int, Callable[[int], int]])
>>> [c.t for c in node.unsupported_components()]
[typing.Callable[[int], int]]
NumPy
NumPy array types are supported by an extension, which you enable by importing:
import typing_validation.numpy # required
from numpy.typing import NDArray
validate(np.array([1, 2], dtype=np.uint8), NDArray[np.uint8])
The import is required rather than automatic, so that the supported surface never depends on whether some unrelated dependency happened to import NumPy.
Extending
A parametrised class can say how its own type arguments are validated:
class Box[T]:
@classmethod
def __validate__(cls, val, args):
return is_valid(val.item, args[0])
For classes you do not own, use register_validator(cls, check).
API
The full API documentation is available at typing-validation.readthedocs.io.
Structure
typing_validation/— the package source.knowledge/— design documents: the architecture, and the catalogue of supported type forms.test/— the conformance suite, with the case corpus intest/cases.py.benchmark/— the benchmark suite; run it withpython -m benchmark. The machinery is inbenchmark/tools/, the generated numbers inbenchmark/REPORT.md, and the written synthesis inbenchmark/PEER-COMPARISON.md.docs/— the Sphinx documentation pipeline.
License
LGPL-3.0-or-later © Hashberg Ltd
Metadata
Release files for typing-validation 2.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| typing_validation-2.2.1.tar.gz | 61.6 kB | Without provenance | Details for typing_validation-2.2.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| typing_validation-2.2.1-py3-none-any.whl | Python 3 | none | any | Details for typing_validation-2.2.1-py3-none-any.whl |
Total release size: 132.7 kB
Release files / typing_validation-2.2.1.tar.gz
| Download URL | typing_validation-2.2.1.tar.gz |
|---|---|
| Size | 61.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
db33e27b0269098902677aa23eba07fcd13f076b6ef2ffc1bb341be5c0942c75
|
|
BLAKE2b-256 checksum How to use checksums |
600c1a8a4850f9b24fb8c8ac2f99394d3fb4fd929360ebfd1436e13ecebf5baf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.14
|
Release files / typing_validation-2.2.1-py3-none-any.whl
| Download URL | typing_validation-2.2.1-py3-none-any.whl |
|---|---|
| Size | 71.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7b6740a98ebd3ba73a542032406807cd4a9620d578b69040a99bd58bdde9da7c
|
|
BLAKE2b-256 checksum How to use checksums |
3f0cf44ed22596e22ec9b359d4bb99742ee3380aa433e9e161ff7acb824e5fac
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.14
|
Security for typing-validation 2.2.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.