voluptuous compatibility
The drop-in promise is the point of Probatio. This page makes it checkable. Below
is voluptuous’s meaningful public surface, name by name, with its status in
Probatio and a short note. Every name in voluptuous 0.16.0’s public surface was
checked against Probatio with hasattr(probatio, name), not from memory.
The status column uses three values:
- Supported: same name, same behavior.
- Supported, with deviation: present and compatible, but Probatio behaves differently in one documented way. Each one is a deliberate improvement.
- Not mirrored: Probatio does not expose this name. For everything on this page that is “not mirrored”, the reason is that the name is not part of voluptuous’s intended public API. See What Probatio does not mirror.
For the narrative version, read Migrating from voluptuous and Compatibility.
Schema and constants
Section titled “Schema and constants”| Name | Status | Notes |
|---|---|---|
Schema | Supported | Same calling convention, including positional required/extra. |
Schemable | Supported | The type alias for anything Schema accepts. |
ALLOW_EXTRA | Supported | Extra-key policy constant. |
PREVENT_EXTRA | Supported | The default extra-key policy. |
REMOVE_EXTRA | Supported | Extra-key policy constant. |
UNDEFINED | Supported | The “no value” sentinel for marker defaults. |
Undefined | Supported | The type of UNDEFINED. |
default_factory | Supported | Wraps a callable as a marker default factory. |
Markers
Section titled “Markers”| Name | Status | Notes |
|---|---|---|
Marker | Supported | Base class for all markers. |
Required | Supported | The key must be present. |
Optional | Supported | The key may be present; default fills it in. |
Remove | Supported | Drop matching keys from the output. |
Extra | Supported | The catch-all key. |
Inclusive | Supported | Keys in a group appear together or not at all. |
Exclusive | Supported | At most one key from a group. |
Self | Supported, with deviation | Recursive schema reference. See Intentional deviations. |
Combinators
Section titled “Combinators”| Name | Status | Notes |
|---|---|---|
All | Supported | Every validator must pass, output feeding the next. |
Any | Supported | First accepting validator wins. |
And | Supported | Alias of All. |
Or | Supported | Alias of Any. |
Union | Supported | Like Any, with an optional discriminant. |
Switch | Supported | Alias of Union. |
SomeOf | Supported | Pass between min_valid and max_valid of the validators. |
Validators
Section titled “Validators”| Name | Status | Notes |
|---|---|---|
Coerce | Supported | Convert with type(value). |
Boolean | Supported | Factory (Boolean()), as in voluptuous. Reads truthy/falsy strings as bool. |
Literal | Supported | Require equality with a literal. |
Equal | Supported | Require equality with a target. |
In | Supported | Membership in a container. |
NotIn | Supported | Non-membership in a container. |
Contains | Supported | A collection must contain an item. |
Match | Supported | Match a regular expression. |
Replace | Supported | Replace regex matches in a string. |
Range | Supported | Numeric bounds, inclusive by default. |
Clamp | Supported | Pin a value into a range. |
Number | Supported | Validate a numeric string, optionally precision and scale. |
Length | Supported | Bound the length of a sized value. |
Unique | Supported | Require distinct items. |
Set | Supported | Convert an iterable into a set. |
ExactSequence | Supported | Validate a fixed-length sequence by position. |
Unordered | Supported | Validate a sequence in any order. |
Object | Supported | Validate an object’s attributes like a mapping. |
Maybe | Supported | Allow None, otherwise validate. |
Lower | Supported | Lowercase transform. |
Upper | Supported | Uppercase transform. |
Capitalize | Supported | Capitalize transform. |
Title | Supported | Title-case transform. |
Strip | Supported | Whitespace-strip transform. |
Email | Supported | Backtracking-safe email format check. |
Url | Supported | Backtracking-safe URL format check. |
FqdnUrl | Supported | URL with a fully qualified domain. |
Datetime | Supported | Validate a datetime string (default %Y-%m-%dT%H:%M:%S.%fZ). |
Date | Supported | Validate a date string (default %Y-%m-%d). |
IsDir | Supported | An existing directory path. |
IsFile | Supported | An existing file path. |
PathExists | Supported | A path that exists. |
IsTrue | Supported | The value must be truthy. |
IsFalse | Supported | The value must be falsy. |
DefaultTo | Supported | Replace None with a default. |
SetTo | Supported | Always produce a fixed value. |
Msg | Supported | Replace a validator’s failure message. |
Error classes
Section titled “Error classes”| Name | Status | Notes |
|---|---|---|
Error | Supported | Base for everything Probatio raises. |
SchemaError | Supported | The schema definition itself is invalid. |
Invalid | Supported | A single validation failure, with a path. |
MultipleInvalid | Supported, with deviation | Collects several errors and proxies the first. See note below. |
RequiredFieldInvalid | Supported | Semantic subclass of Invalid. |
ObjectInvalid | Supported | Semantic subclass of Invalid. |
DictInvalid | Supported | Semantic subclass of Invalid. |
SequenceTypeInvalid | Supported | Semantic subclass of Invalid. |
TypeInvalid | Supported | Semantic subclass of Invalid. |
ValueInvalid | Supported | Semantic subclass of Invalid. |
ScalarInvalid | Supported | Semantic subclass of Invalid. |
LiteralInvalid | Supported | Semantic subclass of Invalid. |
CoerceInvalid | Supported | Semantic subclass of Invalid. |
AnyInvalid | Supported | Semantic subclass of Invalid. |
AllInvalid | Supported | Semantic subclass of Invalid. |
MatchInvalid | Supported | Semantic subclass of Invalid. |
RangeInvalid | Supported | Semantic subclass of Invalid. |
LengthInvalid | Supported | Semantic subclass of Invalid. |
InInvalid | Supported | Semantic subclass of Invalid. |
NotInInvalid | Supported | Semantic subclass of Invalid. |
ContainsInvalid | Supported | Semantic subclass of Invalid. |
ExactSequenceInvalid | Supported | Semantic subclass of Invalid. |
ExclusiveInvalid | Supported | Semantic subclass of Invalid. |
InclusiveInvalid | Supported | Semantic subclass of Invalid. |
TrueInvalid | Supported | Semantic subclass of Invalid. |
FalseInvalid | Supported | Semantic subclass of Invalid. |
BooleanInvalid | Supported | Semantic subclass of Invalid. |
UrlInvalid | Supported | Semantic subclass of Invalid. |
EmailInvalid | Supported | Semantic subclass of Invalid. |
DirInvalid | Supported | Semantic subclass of Invalid. |
FileInvalid | Supported | Semantic subclass of Invalid. |
PathInvalid | Supported | Semantic subclass of Invalid. |
DatetimeInvalid | Supported | Semantic subclass of Invalid. |
DateInvalid | Supported | Semantic subclass of Invalid. |
NotEnoughValid | Supported | Raised by SomeOf. |
TooManyValid | Supported | Raised by SomeOf. |
Helpers
Section titled “Helpers”| Name | Status | Notes |
|---|---|---|
validate | Supported | Decorator that validates a function’s arguments and __return__. |
message | Supported | Decorator turning a ValueError-raising function into a configurable validator factory. |
truth | Supported | Decorator turning a predicate into a validator that returns the value when truthy. |
raises | Supported | Context manager asserting a block raises a given exception. |
humanize_error | Supported | In probatio.humanize. Render an error against the data. |
validate_with_humanized_errors | Supported | In probatio.humanize. Validate, raising a humanized message. |
Additions beyond voluptuous
Section titled “Additions beyond voluptuous”These names and options are not in voluptuous; they are Probatio additions, so they do not affect the drop-in promise (existing code keeps working). Each carries forward an upstream request.
Forbidden: a marker requiring a key to be absent ({Forbidden("password"): object}). Carries forward issue #193.TaggedUnion: a discriminated union that routes on one key’s value to the matching schema (TaggedUnion("type", {"grid": ..., "solar": ...})), reporting the chosen branch’s own errors and naming the valid tags on a miss. Cases are a{tag: schema}mapping, or a list of branches that each pin the tag as a literal (read once from the branch); the routing table is built once either way. The readable form of aUnionwith a hand-writtendiscriminant, and the direct equivalent of Home Assistant’scv.key_value_schemas.Alias: a key marker accepting a value under one or more alias names and emitting it under a canonical name ({Alias("user_name", "user-name"): str}). Multiple aliases, first-present-wins by declaration order, optional strict rename. voluptuous has no equivalent.DataclassSchema,create_dataclass_schema,is_dataclass: build a schema from a dataclass, validate a mapping, and construct an instance. Carries forward draft PR #533.TypedDictSchema,create_typeddict_schema: build a schema from a TypedDict and return the validated mapping typed as that TypedDict (nothing constructed, since a TypedDict is a plain dict at runtime). voluptuous has no equivalent.Exclusive(..., required=True): an exclusive group must hold exactly one key (an empty group is an error). Carries forward issue #115.Exclusive(..., default=...): fill a group member when the exclusive group is empty. Carries forward issue #245.IPv4Address,IPv6Address,IPAddress,IPNetwork,MacAddress,UUID,Hostname,Fqdn: network and identifier validators; they validate and return the value unchanged (wrap withCoercefor the parsed object).NormalizeMacAddressreturns a MAC in canonical form, andPortreturns anint. Common across ecosystems, reinvented across Home Assistant and ESPHome.Time,Duration,TimeZoneInfo,TimeZone: time-of-day (the sibling ofDate/Datetime), duration validation, IANA zone validation, and UTC offset validation, each returning the value unchanged.Timeis voluptuous issue #335; the others recur across ecosystems.AsDatetime,AsDate,AsTime,AsTimedelta,AsTimezone: the object-returning siblings ofDatetime/Date/Time/Duration/TimeZone, parsing to adatetime/date/time/timedelta/datetime.timezoneinstead of validating and passing the value through. The date and time ones parse ISO 8601 by default (standard library only, for deterministic results), or astrptimeformat;AsDatetimecan require a timezone-aware result. (TimeZoneInfohas noAs*sibling:Coerce(zoneinfo.ZoneInfo)gives the object, since the constructor takes the name.)FromEpoch: parse a Unix timestamp (seconds or milliseconds) into a timezone-aware UTCdatetime. Common in APIs and device payloads.EnsureList,Slug,Positive,Negative,NonNegative,MultipleOf,Percentage,FromPercentage: list-wrapping, slug format, sign conveniences, integer-multiple, and a 0 to 100 percentage (Percentagevalidates and returns the value;FromPercentageparses it to afloat). Common config helpers.Secret: a key marker that redacts the key’s value from validation error output (<redacted>instead of the value), so a rejected credential does not leak into rendered validation error output. Composes with the presence markers by nesting (Optional(Secret("password"))). Voluptuous has no equivalent.NonEmpty,Byte,SmallFloat,IsRegex: a non-empty check, 0 to 255 and 0 to 1 bounded numbers, and a “value is a compilable regex” check.Multiply,Divide,Offset,Round,RoundUp,RoundDown,Snap,Abs,Modulo,Remap,Scale: arithmetic mutators for the small calculations common on device and sensor fields (a gain, a unit divide, a shift, rounding up/down or to a nearest step, a magnitude, a modulo wrap-around, a range remap like the Arduinomap(), or the whole affine transform in oneScale). Readable, composable stand-ins forCoerce(lambda ...).CollapseWhitespace,RemovePrefix,RemoveSuffix,Truncate: string normalizers beyond the plain transforms (squeeze internal whitespace, strip a known prefix or suffix, cap the length). UnlikeLower/Strip, they reject a non-string rather than coercing it.Map,EmptyToNone:Maptranslates a value through a table you supply (a device status code to a name), the bring-your-own-mapping answer to a domain conversion so probatio never picks a disputed table;EmptyToNoneturns an empty string or container intoNone(the “empty means unset” shape), the near-reverse ofDefaultTo.Split,Join,Sort,Dedupe,First,Last,Without: collection shapers.Split/Joinconvert between a delimited string and a list;SortandDedupeare the doing siblings of theSortedandUniquevalidators;First/Lastpick an element;Withoutprunes listed values (soWithout(None)clears the holes).JSONString,FromJSONString: validate a JSON string (and optionally the decoded value against an inner schema);JSONStringreturns the string unchanged, whileFromJSONStringreturns the decoded value.IsSymlink,IsSocket,IsFifo,IsBlockDevice: filesystem predicates for the special file types, alongsideIsDir/IsFile.Alpha,Alphanumeric,ASCII,PrintableASCII,NoWhitespace,StartsWith,EndsWith,ByteLength,HexColor: character-class and affix string checks, UTF-8 byte length, and a hex color.Base64,Hex,Sorted,ULID,Latitude,Longitude: base64/hex validation, an ascending-order check, a ULID, and geographic coordinate bounds.CreditCard,IBAN,DataURI,E164: format and checksum validators (Luhn, ISO 13616 mod-97, RFC 2397 data URIs, international phone format). Pure checks, no network or extra dependency. voluptuous has none of these.RequiredWith,RequiredWithout,RequiredIf,Check: cross-field rules over a whole mapping (used after a dict schema withAll), for conditional requirements (on a key’s presence, absence, or value, combined with an any/all mode) and an arbitrary predicate. voluptuous has no equivalent.AtLeastOne,AtMostOne,ExactlyOne,AllOrNone: key-group presence rules over a mapping, for how many of a set of keys may or must appear. The dict-level form ofInclusive/Exclusive. They reject a non-mapping by default (require_mapping=Falseto opt out). Home Assistant and ESPHome roll their ownhas_at_least_one_key/has_none_or_all_keys; voluptuous has no equivalent.Immutable,WriteOnce: transition rules that compare new data against its previous value (passed as the call’scontext), rejecting a change to an immutable field or a second write to a write-once one. voluptuous has no equivalent.ExtraKeysInvalidwith.candidates: an unknown key underPREVENT_EXTRAreports the closest known keys (“did you mean …?”) and carries them on the error, instead of a bare “extra keys not allowed”. ESPHome forks voluptuous internals for this; first-class here, so any consumer gets the suggestion.__probatio_validate__: a protocol a type defines (a classmethod) to validate a raw value of itself, used in place of the bareisinstancecheck when that type is a schema.EnumInvalidis the error its built-in consumer (an enum class) raises. voluptuous has no such hook.current_contextwithschema(data, context=...): an optional call argument that hands per-call state to validators that readcurrent_context(), so one compiled schema validates against state known only at call time. Additive (a plainschema(data)is unchanged). voluptuous has no equivalent.
Intentional deviations
Section titled “Intentional deviations”Compatibility is the target, so this list is short. Each entry is a deliberate improvement over a sharp edge, not a regression.
Each entry reads the same way: the behavior, then what voluptuous does, then what Probatio does.
- An
intin afloatschema (Schema(float)(5), ADR-017). voluptuous validatesfloatbyisinstance, so anintis rejected. Probatio honors the PEP 484 numeric tower: anintis a validfloatvalue, so it is accepted and normalized tofloat(5becomes5.0), whilebool, a string, andNoneare still rejected. This aligns the validator with the{"type": "number"}thatto_json_schemaalready emits. A strict widening: only a value voluptuous would have rejected is affected, and afloatmatcher now matches aninttoo (soRemove(float)in a list drops ints, and a{float: ...}type key matches an int key). - The rendered error string,
str(error)(ADR-015). voluptuous rendersexpected int for dictionary value @ data['server']['port']. Probatio renders the same error asexpected int at 'server.port': the path is a dotted trail (sequence indices as[n], soat 'hosts[2].name'), and the error-type clause is not rendered. A non-mapping is rejected as “expected a mapping” instead of “expected a dictionary” (the engine accepts anyMapping, and the message now says so). The attributes are unchanged:path,msg,error_message, anderror_typekeep their voluptuous semantics, so code that reads them (rather than parsing the rendered string) is unaffected. Code that string-matchesstr(error)should switch topathanderror_message, or toas_dict(). - Reworded default messages (ADR-015). A handful of voluptuous default
messages were broken or developer-speak; Probatio words them for the person
reading them.
Literalreadsexpected {lit}instead of{value} not match for {lit}.Unorderedreads “expected a sequence”, “expected a sequence of N items”, and “item N (…) does not match any validator” instead of “Value … is not sequence!”, “List lengths differ, value:N != target:M”, and “Element #N (…) is not valid against any validator”. The filesystem predicates lose their stray capitals (“not a directory”, not “Not a directory”).Booleanreads “expected a boolean”. Classes, paths, and translation keys are unaffected; only the sentence changed. - Recursive
Selfon cyclic or very deep data. voluptuous crashes withRecursionError. Probatio raises a cleanInvalidwith a path, caught with the rest of your errors. The depth limit tracks a fraction of the interpreter’s recursion limit, so raisingsys.setrecursionlimitallows deeper data, up to the point where the operating system’s own stack is the real ceiling; past that a very high limit can still overflow, so keep the limit within reason. from_json_schemaon untrusted input. voluptuous can hang or overflow the stack. Probatio refuses a catastrophically backtrackingpatternand a pathologically deep document withSchemaError.MultipleInvalid.error_type. voluptuous has no such attribute. Probatio proxies the first error’s type. Additive, so existing code is unaffected.- Missing complex required key (
Required(Any("a", "b"))). voluptuous reports it twice (the “at least one of […]” error plus a redundant “required key not provided”). Probatio reports the single meaningful error; the first error matches voluptuous. - Non-dict
Mappinginput (aMappingProxyType, a multidict, a custom mapping). voluptuous rejects it with “expected a dictionary”. Probatio validates any object implementing theMappingprotocol and returns a plaindict. A genuinedictsubclass, on the other hand, is preserved as its own class, the same as voluptuous, so aNodeDictClassand similar metadata-carrying subclasses survive validation. A strict superset, so existing dict code is unaffected. - A callable validator raising
ValueError("reason"). voluptuous reports a bare “not a valid value”, dropping the reason. Probatio appends it: “not a valid value: reason”. AValueErrorwith no message stays bare. - An enum member as a schema or mapping key (
{Color.RED: str}). voluptuous rejects it withSchemaError(added upstream in PR #537, not yet released). Probatio accepts it: a member is matched by equality as a scalar, and works as a literal key, withRequired/Optional, and as a value. - An enum class as a schema (
Schema(Color)). voluptuous treats it as a type and validates byisinstance, so it accepts only an already-built member and rejects the member’s value. Probatio accepts a member unchanged or any value that maps to one, returning the member, so the string a loader hands you ("red") validates and you getColor.REDback. A strict widening: input that was valid before still is, and the rejection now carries the list of valid values. Acceptance is whatever the enum’s own constructor allows, so anIntEnumtakes a numeric equivalent (1.0,True) and anIntFlagbuilds a combined value from any integer (Schema(Perm)(3)isPerm.R | Perm.W), the same asCoerceof that enum. Use an explicitIn([...])when you need to accept only the exact listed members. Coerce(T)on a value already of typeTwhose constructor rejects it (Coerce(uuid.UUID)(a_uuid)).uuid.UUID(a_uuid)raises, so voluptuous reports aCoerceInvalid. Probatio returns the value unchanged, makingCoerceidempotent, so re-validating an already-coerced value does not fail. This only affects a value that voluptuous would have rejected (a value that converts, or is not already aT, is unchanged), so it is a strict widening.- A built-in validator on a wrong-typed value (
Replace("a", "b")(42),Number()(None)). voluptuous leaks a rawTypeError/ValueErrorfrom the underlying call (fixed upstream in PR #540 and PR #539, not yet released). Probatio raises a cleanInvalid(aMatchInvalidforReplace, anInvalidforNumber). - A signaling
Decimal(Decimal("sNaN")) compared by a numeric validator (Range,In,Equal,MultipleOf). A signaling NaN raisesdecimal.InvalidOperationon any comparison, which voluptuous leaks. Probatio catches theArithmeticErrorand reports a cleanInvalid(the value fails the check), so a crafted value cannot leak a raw exception. - A quoted email local part (
"a..b"@example.com). voluptuous’s email regex accepts the RFC quoted-string form of the local part; Probatio’sEmaildoes not (it validates the common unquoted form with plain string checks, no backtracking regex). The quoted form is effectively never used in configuration. extendwith anotherSchema(base.extend(other_schema)). voluptuous raisesAssertionError(added upstream in PR #538, not yet released). Probatio merges the extension’s keys with itsrequiredintent preserved (recursively); itsextramust match the result’s, so pass its.schemadict for a raw merge.- A list with several failing items (
[{"name": str}]against two bad items). voluptuous stops at the first failing nested item, reporting one error (open request, issue #171). Probatio reports every failing item, each with its index in the path. Existing error-iterating code is unaffected. - A set schema with a transforming element (
{Coerce(int)}). voluptuous returns the set with its elements untransformed (bug, issue #400). Probatio applies the element schema to set items like it does to a list, so aCoerceactually coerces. - A failing
Anywhose branches are all concrete (Any(int, str, None)). voluptuous reports only the first branch (“expected int”) or “not a valid value” (open request, issue #412). Probatio lists every expected branch (“expected int or str or None”) asAnyInvalid. A branch that is an arbitrary validator has no label, soAnystill surfaces that branch’s error. - An unknown key under
PREVENT_EXTRA({"name": str}given{"nmae": ...}). voluptuous raises a bareInvalid("extra keys not allowed"). Probatio raisesExtraKeysInvalid("not a valid option, did you mean 'name'?"), carrying the close matches on.candidates. The stablecodestaysextra_keys_not_allowed, so code matching on it is unaffected; only the rendered string changed. - Keyword parameter names. Probatio renames a few of voluptuous’s parameters
for clarity:
Lower,Upper,Capitalize,Title,Striptakevalue(voluptuousv);Marker/Removetakeschema(voluptuousschema_);Msgtakesvalidator(voluptuousschema);DefaultTotakesdefault(voluptuousdefault_value). Positional calls are unaffected; only keyword callers using the old names need to update. Inclusivepositional argument order. Probatio isInclusive(key, group, msg=None, default=UNDEFINED, description=None), matching the other markers; voluptuous putsdescriptionbeforedefault. Passdefaultanddescriptionby keyword to be safe across both.discriminantonAll/Any. A tagged-union discriminant is honored onUnion(aliasSwitch), not onAll/Any; passing it toAll/Anyhas no effect. UseUnionfor a discriminated union.- An
ExactSequenceelement error carries the element’s index in the path (at '[1]'), where voluptuous reports the bare parent path. Probatio’s path is more precise; existing path-walking code sees a deeper path. - The order of multiple missing-required-key errors is the schema’s definition order in Probatio, and hash (set) order in voluptuous. The first error still matches whenever any non-missing error is present; only the ordering among several all-missing keys differs.
These match what the Compatibility and Migrating from voluptuous pages describe. If you hit a difference that is not here, treat it as a bug.
What Probatio does not mirror
Section titled “What Probatio does not mirror”Voluptuous has no __all__, so dir(voluptuous) returns everything the module
happens to import: standard-library modules, regex constants, and internal
helpers, none of which are part of its intended public API. Probatio defines an
explicit __all__ and does not re-export these. Reaching for one of them was
already reaching into voluptuous internals.
The notable names, so you are not surprised:
Enum: the standard-libraryenum.Enum, leaked into the namespace. Reach forimport enum.Decimal,InvalidOperation: the standard-librarydecimaltypes. Reach forfrom decimal import Decimal.Generator:collections.abc.Generator. Reach forfrom collections.abc import Generator.collections,datetime,inspect,itertools,os,re,sys,typing,urlparse: imported standard-library modules. Import them yourself.DOMAIN_REGEX,USER_REGEX,primitive_types: internal regex and type constants. Not public; do not depend on them.DefaultFactory,VirtualPathComponent: internal type aliases and helpers. Not public.extra,cache,wraps,contextmanager,basestring: internal helpers and imported builtins. Not public.er: an alias ofvoluptuous.error. Use theerrorsubmodule directly.
None of these are validators or markers. If your code uses one, it is coupled to a voluptuous implementation detail, and you want to import the real thing directly.
Submodule imports
Section titled “Submodule imports”The submodules resolve too, so code that imports voluptuous internals has a Probatio counterpart.
| voluptuous module | Probatio module |
|---|---|
voluptuous | probatio |
voluptuous.error | probatio.error |
voluptuous.validators | probatio.validators |
voluptuous.humanize | probatio.humanize |
voluptuous.schema_builder | probatio.schema |
For dependencies that import voluptuous internals under the voluptuous name
(not Probatio), probatio.compat.install_as_voluptuous() aliases Probatio into
sys.modules so every later import voluptuous resolves to probatio. The
Compatibility page covers what it registers
and how and when to call it.