Skip to content

Probatio is a modern, maintained data validation library for Python, and a clean-room reimplementation of voluptuous: the same API, written fresh. Change the import, keep your schemas, and get a library that is actively maintained, MIT licensed, and validated against voluptuous’s own behavior. Then, when you want more, it is already there.

The model stays the same as voluptuous: a schema is data, and data describes data. You compose schemas from plain Python types, dicts, lists, and small helpers, then call the schema with a value to validate it.

Created and written by Franck Nijhof, also known as Frenck.

Drop-in compatible

The public API mirrors voluptuous: Schema, Required, Optional, All, Any, Coerce, Invalid, and the rest. Change the import, keep your schemas. voluptuous’s own 0.16.0 test suite runs against it, and every divergence is a deliberate, documented deviation.

More than a replacement

It also clears voluptuous’s backlog: cross-field rules, dataclass and TypedDict schemas, network and format validators, and errors that carry a path and suggest the key you meant. Several are voluptuous’s own open requests, now implemented.

Proven, not promised

100% line and branch coverage, two type checkers, and continuous fuzzing on every untrusted-input surface. It is also safer than the original: leaks and RecursionError edges voluptuous has are fixed, and documented.

Typed and modern

Ships py.typed, checked under mypy and ty, pure Python with no native build. Exports JSON Schema and OpenAPI from a schema, and reads them back in.

It is also fast for pure Python: roughly two to three times voluptuous’s speed on a representative config schema, and about seven times once a schema compiles itself. The numbers, and how to reproduce them, are on the performance page.

Define a schema, then call it with a value to validate it:

from probatio import Schema, Required, Optional
schema = Schema(
{
Required("name"): str,
Optional("age"): int,
}
)
schema({"name": "Frenck", "age": 40})
# {'name': 'Frenck', 'age': 40}

When a value does not match, the schema raises Invalid (or MultipleInvalid) with a path to the offending value, however deep it sits:

from probatio import Schema, Invalid
schema = Schema({"server": {"port": int}})
try:
schema({"server": {"port": "nope"}})
except Invalid as err:
print(err)
# expected int at 'server.port'

It validates untrusted input, config, an API body, a device response, so the bar is whether you would hand it exactly that. The evidence, not the adjectives:

  • voluptuous 0.16.0 test suite: runs against Probatio, with every divergence a deliberate, documented deviation. voluptuous’s own authors’ notion of the contract, checked.
  • Home Assistant config_validation: the full suite passes, with voluptuous swapped out for Probatio.
  • 100% line and branch coverage, type-checked under both mypy and ty, in CI.
  • Fuzzed on every untrusted-input surface. The first fuzzing pass found hundreds of exception leaks on hostile input. All fixed; none since.
  • Safer than the original. A built-in validator only ever raises Invalid, never a raw exception, and that is enforced, not hoped for.

Probatio aims to be a drop-in replacement. In most cases the migration is a one-line import change. See migrating from voluptuous for the details and the current compatibility status.