Skip to content

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.

NameStatusNotes
SchemaSupportedSame calling convention, including positional required/extra.
SchemableSupportedThe type alias for anything Schema accepts.
ALLOW_EXTRASupportedExtra-key policy constant.
PREVENT_EXTRASupportedThe default extra-key policy.
REMOVE_EXTRASupportedExtra-key policy constant.
UNDEFINEDSupportedThe “no value” sentinel for marker defaults.
UndefinedSupportedThe type of UNDEFINED.
default_factorySupportedWraps a callable as a marker default factory.
NameStatusNotes
MarkerSupportedBase class for all markers.
RequiredSupportedThe key must be present.
OptionalSupportedThe key may be present; default fills it in.
RemoveSupportedDrop matching keys from the output.
ExtraSupportedThe catch-all key.
InclusiveSupportedKeys in a group appear together or not at all.
ExclusiveSupportedAt most one key from a group.
SelfSupported, with deviationRecursive schema reference. See Intentional deviations.
NameStatusNotes
AllSupportedEvery validator must pass, output feeding the next.
AnySupportedFirst accepting validator wins.
AndSupportedAlias of All.
OrSupportedAlias of Any.
UnionSupportedLike Any, with an optional discriminant.
SwitchSupportedAlias of Union.
SomeOfSupportedPass between min_valid and max_valid of the validators.
NameStatusNotes
CoerceSupportedConvert with type(value).
BooleanSupportedFactory (Boolean()), as in voluptuous. Reads truthy/falsy strings as bool.
LiteralSupportedRequire equality with a literal.
EqualSupportedRequire equality with a target.
InSupportedMembership in a container.
NotInSupportedNon-membership in a container.
ContainsSupportedA collection must contain an item.
MatchSupportedMatch a regular expression.
ReplaceSupportedReplace regex matches in a string.
RangeSupportedNumeric bounds, inclusive by default.
ClampSupportedPin a value into a range.
NumberSupportedValidate a numeric string, optionally precision and scale.
LengthSupportedBound the length of a sized value.
UniqueSupportedRequire distinct items.
SetSupportedConvert an iterable into a set.
ExactSequenceSupportedValidate a fixed-length sequence by position.
UnorderedSupportedValidate a sequence in any order.
ObjectSupportedValidate an object’s attributes like a mapping.
MaybeSupportedAllow None, otherwise validate.
LowerSupportedLowercase transform.
UpperSupportedUppercase transform.
CapitalizeSupportedCapitalize transform.
TitleSupportedTitle-case transform.
StripSupportedWhitespace-strip transform.
EmailSupportedBacktracking-safe email format check.
UrlSupportedBacktracking-safe URL format check.
FqdnUrlSupportedURL with a fully qualified domain.
DatetimeSupportedValidate a datetime string (default %Y-%m-%dT%H:%M:%S.%fZ).
DateSupportedValidate a date string (default %Y-%m-%d).
IsDirSupportedAn existing directory path.
IsFileSupportedAn existing file path.
PathExistsSupportedA path that exists.
IsTrueSupportedThe value must be truthy.
IsFalseSupportedThe value must be falsy.
DefaultToSupportedReplace None with a default.
SetToSupportedAlways produce a fixed value.
MsgSupportedReplace a validator’s failure message.
NameStatusNotes
ErrorSupportedBase for everything Probatio raises.
SchemaErrorSupportedThe schema definition itself is invalid.
InvalidSupportedA single validation failure, with a path.
MultipleInvalidSupported, with deviationCollects several errors and proxies the first. See note below.
RequiredFieldInvalidSupportedSemantic subclass of Invalid.
ObjectInvalidSupportedSemantic subclass of Invalid.
DictInvalidSupportedSemantic subclass of Invalid.
SequenceTypeInvalidSupportedSemantic subclass of Invalid.
TypeInvalidSupportedSemantic subclass of Invalid.
ValueInvalidSupportedSemantic subclass of Invalid.
ScalarInvalidSupportedSemantic subclass of Invalid.
LiteralInvalidSupportedSemantic subclass of Invalid.
CoerceInvalidSupportedSemantic subclass of Invalid.
AnyInvalidSupportedSemantic subclass of Invalid.
AllInvalidSupportedSemantic subclass of Invalid.
MatchInvalidSupportedSemantic subclass of Invalid.
RangeInvalidSupportedSemantic subclass of Invalid.
LengthInvalidSupportedSemantic subclass of Invalid.
InInvalidSupportedSemantic subclass of Invalid.
NotInInvalidSupportedSemantic subclass of Invalid.
ContainsInvalidSupportedSemantic subclass of Invalid.
ExactSequenceInvalidSupportedSemantic subclass of Invalid.
ExclusiveInvalidSupportedSemantic subclass of Invalid.
InclusiveInvalidSupportedSemantic subclass of Invalid.
TrueInvalidSupportedSemantic subclass of Invalid.
FalseInvalidSupportedSemantic subclass of Invalid.
BooleanInvalidSupportedSemantic subclass of Invalid.
UrlInvalidSupportedSemantic subclass of Invalid.
EmailInvalidSupportedSemantic subclass of Invalid.
DirInvalidSupportedSemantic subclass of Invalid.
FileInvalidSupportedSemantic subclass of Invalid.
PathInvalidSupportedSemantic subclass of Invalid.
DatetimeInvalidSupportedSemantic subclass of Invalid.
DateInvalidSupportedSemantic subclass of Invalid.
NotEnoughValidSupportedRaised by SomeOf.
TooManyValidSupportedRaised by SomeOf.
NameStatusNotes
validateSupportedDecorator that validates a function’s arguments and __return__.
messageSupportedDecorator turning a ValueError-raising function into a configurable validator factory.
truthSupportedDecorator turning a predicate into a validator that returns the value when truthy.
raisesSupportedContext manager asserting a block raises a given exception.
humanize_errorSupportedIn probatio.humanize. Render an error against the data.
validate_with_humanized_errorsSupportedIn probatio.humanize. Validate, raising a humanized message.

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 a Union with a hand-written discriminant, and the direct equivalent of Home Assistant’s cv.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 with Coerce for the parsed object). NormalizeMacAddress returns a MAC in canonical form, and Port returns an int. Common across ecosystems, reinvented across Home Assistant and ESPHome.
  • Time, Duration, TimeZoneInfo, TimeZone: time-of-day (the sibling of Date/Datetime), duration validation, IANA zone validation, and UTC offset validation, each returning the value unchanged. Time is voluptuous issue #335; the others recur across ecosystems.
  • AsDatetime, AsDate, AsTime, AsTimedelta, AsTimezone: the object-returning siblings of Datetime/Date/Time/Duration/TimeZone, parsing to a datetime/date/time/timedelta/datetime.timezone instead of validating and passing the value through. The date and time ones parse ISO 8601 by default (standard library only, for deterministic results), or a strptime format; AsDatetime can require a timezone-aware result. (TimeZoneInfo has no As* 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 UTC datetime. 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 (Percentage validates and returns the value; FromPercentage parses it to a float). 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 Arduino map(), or the whole affine transform in one Scale). Readable, composable stand-ins for Coerce(lambda ...).
  • CollapseWhitespace, RemovePrefix, RemoveSuffix, Truncate: string normalizers beyond the plain transforms (squeeze internal whitespace, strip a known prefix or suffix, cap the length). Unlike Lower/Strip, they reject a non-string rather than coercing it.
  • Map, EmptyToNone: Map translates 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; EmptyToNone turns an empty string or container into None (the “empty means unset” shape), the near-reverse of DefaultTo.
  • Split, Join, Sort, Dedupe, First, Last, Without: collection shapers. Split/Join convert between a delimited string and a list; Sort and Dedupe are the doing siblings of the Sorted and Unique validators; First/Last pick an element; Without prunes listed values (so Without(None) clears the holes).
  • JSONString, FromJSONString: validate a JSON string (and optionally the decoded value against an inner schema); JSONString returns the string unchanged, while FromJSONString returns the decoded value.
  • IsSymlink, IsSocket, IsFifo, IsBlockDevice: filesystem predicates for the special file types, alongside IsDir/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 with All), 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 of Inclusive/Exclusive. They reject a non-mapping by default (require_mapping=False to opt out). Home Assistant and ESPHome roll their own has_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’s context), rejecting a change to an immutable field or a second write to a write-once one. voluptuous has no equivalent.
  • ExtraKeysInvalid with .candidates: an unknown key under PREVENT_EXTRA reports 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 bare isinstance check when that type is a schema. EnumInvalid is the error its built-in consumer (an enum class) raises. voluptuous has no such hook.
  • current_context with schema(data, context=...): an optional call argument that hands per-call state to validators that read current_context(), so one compiled schema validates against state known only at call time. Additive (a plain schema(data) is unchanged). voluptuous has no equivalent.

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 int in a float schema (Schema(float)(5), ADR-017). voluptuous validates float by isinstance, so an int is rejected. Probatio honors the PEP 484 numeric tower: an int is a valid float value, so it is accepted and normalized to float (5 becomes 5.0), while bool, a string, and None are still rejected. This aligns the validator with the {"type": "number"} that to_json_schema already emits. A strict widening: only a value voluptuous would have rejected is affected, and a float matcher now matches an int too (so Remove(float) in a list drops ints, and a {float: ...} type key matches an int key).
  • The rendered error string, str(error) (ADR-015). voluptuous renders expected int for dictionary value @ data['server']['port']. Probatio renders the same error as expected int at 'server.port': the path is a dotted trail (sequence indices as [n], so at '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 any Mapping, and the message now says so). The attributes are unchanged: path, msg, error_message, and error_type keep their voluptuous semantics, so code that reads them (rather than parsing the rendered string) is unaffected. Code that string-matches str(error) should switch to path and error_message, or to as_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. Literal reads expected {lit} instead of {value} not match for {lit}. Unordered reads “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”). Boolean reads “expected a boolean”. Classes, paths, and translation keys are unaffected; only the sentence changed.
  • Recursive Self on cyclic or very deep data. voluptuous crashes with RecursionError. Probatio raises a clean Invalid with a path, caught with the rest of your errors. The depth limit tracks a fraction of the interpreter’s recursion limit, so raising sys.setrecursionlimit allows 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_schema on untrusted input. voluptuous can hang or overflow the stack. Probatio refuses a catastrophically backtracking pattern and a pathologically deep document with SchemaError.
  • 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 Mapping input (a MappingProxyType, a multidict, a custom mapping). voluptuous rejects it with “expected a dictionary”. Probatio validates any object implementing the Mapping protocol and returns a plain dict. A genuine dict subclass, on the other hand, is preserved as its own class, the same as voluptuous, so a NodeDictClass and 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”. A ValueError with no message stays bare.
  • An enum member as a schema or mapping key ({Color.RED: str}). voluptuous rejects it with SchemaError (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, with Required/Optional, and as a value.
  • An enum class as a schema (Schema(Color)). voluptuous treats it as a type and validates by isinstance, 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 get Color.RED back. 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 an IntEnum takes a numeric equivalent (1.0, True) and an IntFlag builds a combined value from any integer (Schema(Perm)(3) is Perm.R | Perm.W), the same as Coerce of that enum. Use an explicit In([...]) when you need to accept only the exact listed members.
  • Coerce(T) on a value already of type T whose constructor rejects it (Coerce(uuid.UUID)(a_uuid)). uuid.UUID(a_uuid) raises, so voluptuous reports a CoerceInvalid. Probatio returns the value unchanged, making Coerce idempotent, 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 a T, 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 raw TypeError/ValueError from the underlying call (fixed upstream in PR #540 and PR #539, not yet released). Probatio raises a clean Invalid (a MatchInvalid for Replace, an Invalid for Number).
  • A signaling Decimal (Decimal("sNaN")) compared by a numeric validator (Range, In, Equal, MultipleOf). A signaling NaN raises decimal.InvalidOperation on any comparison, which voluptuous leaks. Probatio catches the ArithmeticError and reports a clean Invalid (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’s Email does not (it validates the common unquoted form with plain string checks, no backtracking regex). The quoted form is effectively never used in configuration.
  • extend with another Schema (base.extend(other_schema)). voluptuous raises AssertionError (added upstream in PR #538, not yet released). Probatio merges the extension’s keys with its required intent preserved (recursively); its extra must match the result’s, so pass its .schema dict 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 a Coerce actually coerces.
  • A failing Any whose 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”) as AnyInvalid. A branch that is an arbitrary validator has no label, so Any still surfaces that branch’s error.
  • An unknown key under PREVENT_EXTRA ({"name": str} given {"nmae": ...}). voluptuous raises a bare Invalid("extra keys not allowed"). Probatio raises ExtraKeysInvalid("not a valid option, did you mean 'name'?"), carrying the close matches on .candidates. The stable code stays extra_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, Strip take value (voluptuous v); Marker/Remove take schema (voluptuous schema_); Msg takes validator (voluptuous schema); DefaultTo takes default (voluptuous default_value). Positional calls are unaffected; only keyword callers using the old names need to update.
  • Inclusive positional argument order. Probatio is Inclusive(key, group, msg=None, default=UNDEFINED, description=None), matching the other markers; voluptuous puts description before default. Pass default and description by keyword to be safe across both.
  • discriminant on All/Any. A tagged-union discriminant is honored on Union (alias Switch), not on All/Any; passing it to All/Any has no effect. Use Union for a discriminated union.
  • An ExactSequence element 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.

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-library enum.Enum, leaked into the namespace. Reach for import enum.
  • Decimal, InvalidOperation: the standard-library decimal types. Reach for from decimal import Decimal.
  • Generator: collections.abc.Generator. Reach for from 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 of voluptuous.error. Use the error submodule 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.

The submodules resolve too, so code that imports voluptuous internals has a Probatio counterpart.

voluptuous moduleProbatio module
voluptuousprobatio
voluptuous.errorprobatio.error
voluptuous.validatorsprobatio.validators
voluptuous.humanizeprobatio.humanize
voluptuous.schema_builderprobatio.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.