API reference

Validating objects

To validate an object against a schema one may use vtjson.validate(). If validation fails this throws a vtjson.ValidationError. A suitable written schema can be used as a Python type annotation. vtjson.safe_cast() verifies if a given object has a given type. vtjson.make_type() transforms a schema into a genuine Python type so that validation can be done using isinstance().

validate(schema, obj, name='object', strict=True, subs={})

Validates the given object against the given schema.

Parameters:
  • schema (object) – the given schema

  • obj (object) – the object to be validated

  • name (str) – common name for the object to be validated; used in non-validation messages

  • strict (bool) – indicates whether or not the object being validated is allowed to have keys/entries which are not in the schema

  • subs (Mapping[str, object]) – a dictionary whose keys are labels and whose values are substitution schemas for schemas with those labels

Raises:
  • ValidationError – exception thrown when the object does not validate; the exception message contains an explanation about what went wrong

  • SchemaError – exception thrown when the schema definition is found to contain an error

Return type:

None

safe_cast(schema, obj)

Validates the given object against the given schema and changes its type accordingly.

Parameters:
  • schema (Type[TypeVar(T)]) – the given schema

  • obj (Any) – the object to be validated

Return type:

TypeVar(T)

Returns:

the validated object with its type changed

Raises:
  • ValidationError – exception thrown when the object does not validate; the exception message contains an explanation about what went wrong

  • SchemaError – exception thrown when the schema definition is found to contain an error

make_type(schema, name=None, strict=True, debug=False, subs={})

Transforms a schema into a genuine Python type.

Parameters:
  • schema (object) – the given schema

  • name (str | None) – sets the __name__ attribute of the type; if it is not supplied then vtjson makes an educated guess

  • strict (bool) – indicates whether or not the object being validated is allowed to have keys/entries which are not in the schema

  • debug (bool) – print feedback on the console if validation fails

  • subs (Mapping[str, object]) – a dictionary whose keys are labels and whose values are substitution schemas for schemas with those labels

Raises:

SchemaError – exception thrown when the schema definition is found to contain an error

Return type:

_validate_meta

exception ValidationError

Raised if validation fails. The associated message explains what went wrong.

exception SchemaError

Raised if a schema contains an error.

Built-in schemas

Some built-in schemas take arguments. If no arguments are given then the parentheses can be omitted. So email is equivalent to email(). Some built-ins have an optional name argument. This is used in non-validation messages.

class regex(regex, name=None, fullmatch=True, flags=0)

Bases: compiled_schema

This matches the strings which match the given pattern.

Parameters:
  • regex (str) – the regular expression pattern

  • name (str | None) – common name for the pattern that will be used in non-validation messages

  • fullmatch (bool) – indicates whether or not the full string should be matched

  • flags (int) – the flags argument used when invoking re.compile

Raises:

SchemaError – exception thrown when the schema definition is found to contain an error

class glob(pattern, name=None)

Bases: compiled_schema

Unix style filename matching. This is implemented using pathlib.PurePath().match().

Parameters:
  • pattern (str) – the wild card pattern to match

  • name (str | None) – common name for the pattern that will be used in non-validation messages

Raises:

SchemaError – exception thrown when the schema definition is found to contain an error

class div(divisor, remainder=0, name=None)

Bases: compiled_schema

This matches the integers x such that (x - remainder) % divisor == 0.

Parameters:
  • divisor (int) – the divisor

  • remainder (int) – the remainer

  • name (str | None) – common name (e.g. even or odd) that will be used in non-validation messages

Raises:

SchemaError – exception thrown when the schema definition is found to contain an error

class close_to(x, rel_tol=None, abs_tol=None)

Bases: compiled_schema

This matches the real numbers that are close to x in the sense of math.isclose.

Parameters:
  • rel_tol (int | float | None) – the maximal allowed relative deviation

  • abs_tol (int | float | None) – the maximal allowed absolute deviation

Raises:

SchemaError – exception thrown when the schema definition is found to contain an error

class email(**kw)

Bases: compiled_schema

Checks if the object is a valid email address. This uses the package email_validator. The email schema accepts the same options as validate_email in loc. cit.

Parameters:

kw (Any) – optional keyword arguments to be forwarded to email_validator.validate_email

class ip_address(version=None)

Bases: compiled_schema

Matches ip addresses of the specified version which can be 4, 6 or None.

Parameters:

version (Literal[4, 6, None]) – the version of the ip protocol

Raises:

SchemaError – exception thrown when the schema definition is found to contain an error

class url

Bases: compiled_schema

Matches valid urls.

class domain_name(ascii_only=True, resolve=False)

Bases: compiled_schema

Checks if the object is a valid domain name.

Parameters:
  • ascii_only (bool) – if False then allow IDNA domain names

  • resolve (bool) – if True check if the domain names resolves

class date_time(format=None)

Bases: compiled_schema

Without argument this represents an ISO 8601 date-time. The format argument represents a format string for strftime.

Parameters:

format (str | None) – format string for strftime

class date

Bases: compiled_schema

Matches an ISO 8601 date.

class time

Bases: compiled_schema

Matches an ISO 8601 time.

class anything

Bases: compiled_schema

Matchess anything.

class nothing

Bases: compiled_schema

Matches nothing.

class float_

Bases: compiled_schema

Schema that only matches floats. Not ints.

Modifiers

class one_of(*args)

Bases: compiled_schema

This represents a dictionary with exactly one key among a collection of keys.

Parameters:

args (object) – a collection of keys

class at_least_one_of(*args)

Bases: compiled_schema

This represents a dictionary with a least one key among a collection of keys.

Parameters:

args (object) – a collection of keys

class at_most_one_of(*args)

Bases: compiled_schema

This represents an dictionary with at most one key among a collection of keys.

Parameters:

args (object) – a collection of keys

class keys(*args)

Bases: compiled_schema

This represents a dictionary containing all the keys in a collection of keys.

Parameters:

args (object) – a collection of keys

class interval(lb, ub, strict_lb=False, strict_ub=False)

Bases: compiled_schema

This checks if lb <= object <= ub, provided the comparisons make sense.

Parameters:
  • lb (comparable | EllipsisType) – lower bound; … (ellipsis) means no lower bound

  • ub (comparable | EllipsisType) – upper bound; … (ellipsis) means no upper bound

  • strict_lb (bool) – if True use a strict lower bound

  • strict_ub (bool) – if True use a strict upper bound

Raises:

SchemaError – exception thrown when the schema definition is found to contain an error

class gt(lb)

Bases: compiled_schema

This checks if object > lb.

Parameters:

lb (comparable) – the strict lower bound

Raises:

SchemaError – exception thrown when the schema definition is found to contain an error

class ge(lb)

Bases: compiled_schema

This checks if object >= lb.

Parameters:

lb (comparable) – the lower bound

Raises:

SchemaError – exception thrown when the schema definition is found to contain an error

class lt(ub)

Bases: compiled_schema

This checks if object < ub.

Parameters:

ub (comparable) – the strict upper bound

Raises:

SchemaError – exception thrown when the schema definition is found to contain an error

class le(ub)

Bases: compiled_schema

This checks if object <= ub.

Parameters:

ub (comparable) – the upper bound

Raises:

SchemaError – exception thrown when the schema definition is found to contain an error

class size(lb, ub=None)

Bases: compiled_schema

Matches the objects (which support len() such as strings or lists) whose length is in the interval [lb, ub].

Parameters:
  • lb (int) – the lower bound for the length

  • ub (int | EllipsisType | None) – the upper bound for the length; … (ellipsis) means that there is no upper bound; None means lb==ub

Raises:

SchemaError – exception thrown when the schema definition is found to contain an error

class fields(d)

Bases: wrapper, Generic[StringKeyType]

d is a dictionary {“field1”: schema1, …}. This matches Python objects with attributes field1, field2, …, fieldN whose corresponding values should validate against schema1, schema2, …, schemaN respectively.

Parameters:

d (Mapping[TypeVar(StringKeyType, bound= Union[str, optional_key[str]]), object]) – a dictionary associating fields with schemas

Raises:

SchemaError – exception thrown when the schema definition is found to contain an error

class magic(mime_type, name=None)

Bases: compiled_schema

Checks if a buffer (for example a string or a byte array) has the given mime type. This is implemented using the python-magic package.

Parameters:
  • mime_type (str) – the mime type

  • name (str | None) – common name to refer to this mime type

Raises:

SchemaError – exception thrown when the schema definition is found to contain an error

class filter(filter, schema, filter_name=None)

Bases: wrapper

Applies filter to the object and validates the result with schema. If the callable throws an exception then validation fails.

Parameters:
  • filter (Callable[[Any], object]) – the filter to apply to the object

  • schema (object) – the schema used for validation once the filter has been applied

  • filter_name (str | None) – common name to refer to the filter

Raises:

SchemaError – exception thrown when the schema definition is found to contain an error

Wrappers

Wrappers are schemas that contain references to other schemas.

class union(*schemas)

Bases: wrapper

An object matches the schema union(schema1, …, schemaN) if it matches one of the schemas schema1, …, schemaN.

Parameters:

schemas (object) – collection of schemas, one of which should match

class intersect(*schemas)

Bases: wrapper

An object matches the schema intersect(schema1, …, schemaN) if it matches all the schemas schema1, …, schemaN.

Parameters:

schemas (object) – collection of schemas, all of which should match

class complement(schema)

Bases: wrapper

An object matches the schema complement(schema) if it does not match schema.

Parameters:

schema (object) – the schema that should not be matched

class lax(schema)

Bases: wrapper

An object matches the schema lax(schema) if it matches schema when validated with strict=False.

Parameters:

schema (object) – schema that should be validated against with strict=False

class strict(schema)

Bases: wrapper

An object matches the schema strict(schema) if it matches schema when validated with strict=True.

Parameters:

schema (object) – schema that should be validated against with strict=True

class quote(schema)

Bases: wrapper

An object matches the schema quote(schema) if it is equal to schema. For example the schema str matches strings but the schema quote(str) matches the object str.

Parameters:

schema (object) – the schema to be quoted

class set_name(schema, name, reason=False)

Bases: wrapper

An object matches the schema set_name(schema, name) if it matches schema, but the name argument will be used in non-validation messages.

Parameters:
  • schema (object) – the original schema

  • name (str) – name for use in non-validation messages

  • reason (bool) – if True then the original non-validation message will not be suppressed

class protocol(schema, dict=False)

Bases: wrapper

An object matches the schema protocol(schema, dict=False) if schema is a class (or class like object) and its fields are annotated with schemas which validate the corresponding fields in the object.

Parameters:
  • schema (object) – a type annotated class (or class like object such as Protocol or TypedDict) serving as prototype

  • dict (bool) – if True parse the object as a dict

Raises:

SchemaError – exception thrown when the schema does not support type_hints

class set_label(schema, *labels, debug=False)

Bases: wrapper

An object matches the schema set_label(schema, label1, …, labelN, debug=False) if it matches schema, unless the schema is replaced by a different one via the subs argument to validate.

Parameters:
  • schema (object) – the schema that will be labeled

  • labels (str) – labels that will be attached to the schema

  • debug (bool) – it True print a message on the console if the schema was changed via substitution

Raises:

SchemaError – exception thrown when the schema definition is found to contain an error

Conditional schemas

class ifthen(if_schema, then_schema, else_schema=None)

Bases: wrapper

If the object matches the if_schema then it should also match the then_schema. If the object does not match the if_schema then it should match the else_schema, if present.

Parameters:
  • if_schema (object) – the if_schema

  • then_schema (object) – the then_schema

  • else_schema (object | None) – the else_schema

class cond(*args)

Bases: wrapper

Args is a list of tuples (if_schema, then_schema). An object is successively validated against if_schema1, if_schema2, … until a validation succeeds. When this happens the object should match the corresponding then_schema. If no if_schema succeeds then the object is considered to have been validated. If one sets if_schemaN equal to anything then this serves as a catch all.

Parameters:

args (tuple[object, object]) – list of tuples pairing if_schemas with then_schemas

Raises:

SchemaError – exception thrown when the schema definition is found to contain an error

Type annotations integration

Type annotations as schemas

vtjson recognizes the following type annotations as schemas.

Annotated, Mapping[K, V] and subtypes, Container[T] and subtypes, tuple[…] and Tuple[…], Protocol, NamedTuple, NewType, TypedDict, Union and the | operator, Any.

For example dict[str, str] is translated internally into the schema {str: str}. This is explained further below.

Annotated

  • More general vtjson schemas can work along Python type annotations by using the typing.Annotated contruct. The most naive way to do this is via

    Annotated[type_annotation, vtjson_schema, skip_first]
    

    For example

    Annotated[list[object], [int, str, float], skip_first]
    

    A type checker such as mypy will only see the type annotation (list[object] in the example), whereas vtjson will only see the vtjson schema ([int, str, float] in the example). skip_first is a built-in short hand for Apply(skip_first=True) (see below) which directs vtjson to ignore the first argument of an Annotated schema.

  • In some use cases a vtjon schema will meaningfully refine a Python type or type annotation. In that case one should not use skip_first. For example:

    Annotated[datetime, fields({"tzinfo": timezone.utc})]
    

    defines a datetime object whose time zone is utc.

  • The built-in schemas already check that an object has the correct type. So for those one should use skip_first. For example:

    Annotated[int, div(2), skip_first]
    

    matches even integers.

  • If one wants to pre-compile a schema and still use it as a type annotation (assuming it is valid as such) then one can do:

    schema = <schema definition>
    Schema = Annotated[schema, compile(schema), skip_first]
    

Supported type annotations

Note that Python imposes strong restrictions on what constitutes a valid type annotation but vtjson is much more lax about this. Enforcing the restrictions is left to the type checkers or the Python interpreter.

TypedDict

A TypedDict type annotation is, roughly speaking, translated into a dict schema. E.g.

class Movie(TypedDict):
  title: str
  price: float

internally becomes

set_name({"title": str, "price": float}, "Movie", reason=True)

vtjson supports the total option to TypedDict as well as the Required and NotRequired annotations of fields, if they are compatible with the Python version being used.

Protocol

A class implementing a protocol is translated into a vtjson.fields schema. E.g.

class Movie(Protocol):
  title: str
  price: float

internally becomes

set_name(fields({"title": str, "price": float}), "Movie", reason=True)
NamedTuple

A NamedTuple class is translated into an vtjson.intersect schema encompassing a tuple schema and a vtjson.fields schema. E.g.

class Movie(NamedTuple):
  title: str
  price: float

internally becomes

set_name(intersect(tuple, fields({"title": str, "price": float})), "Movie", reason=True)
Annotated

This has already been discussed in the section Annotated. It is translated into a suitable vtjson.intersect schema. The handling of Annotated schemas can be influenced by vtjson.Apply objects.

NewType

This is translated into a vtjson.set_name schema. E.g. NewType(‘Movie’, str) becomes set_name(str, ‘Movie’)

tuple[…] and Tuple[…]

These are translated into the equivalent tuple schemas.

Mapping[K, V] and subtypes

These validate those objects that are members of the origin type (a subclass of Mapping) and whose (key, value) pairs match (K, V).

Container[T] and subtypes

These validate those objects that are members of the origin type (a subclass of Container) and whose elements match T.

Union and the | operator

These are translated into vtjson.union.

Literal

This is also translated into vtjson.union.

Any

This is translated into vtjson.anything.

Apply objects

  • If the list of arguments of an Annotated schema includes vtjson.Apply objects then those modify the treatement of the arguments that come before them. We already encountered vtjson.skip_first which is a built-in alias for Apply(skip_first=True).

  • Multiple vtjson.Apply objects are allowed. E.g. the following contrived schema

    Annotated[int, str, skip_first, float, skip_first]
    

    is equivalent to float.

class Apply(skip_first=None, name=None, labels=None)

Bases: object

Modifies the treatement of the previous arguments in an Annotated schema.

Parameters:
  • skip_first (bool | None) – if True do not use the first argument (the Python type annotation) in an Annotated construct for validation because this is already covered by the other arguments

  • name (str | None) – apply the corresponding vtjson.set_name command to the previous arguments

  • labels (Sequence[str] | None) – apply the corresponding vtjson.set_label command to the previous arguments

vtjson.skip_first: vtjson.Apply = vtjson.Apply(skip_first=True)

Do not use the first argument (the Python type annotation) in an Annotated construct for validation (likely because it is already covered by the other arguments).

Schema format

A schema can be, in order of precedence:

  • An instance of the class vtjson.compiled_schema. The class vtjson.compiled_schema defines a single abstract method vtjson.compiled_schema.__validate__() with similar semantics as vtjson.validate().

  • A subclass of vtjson.compiled_schema with a no-argument constructor.

  • An object having a __validate__() attribute with the same signature as vtjson.compiled_schema.__validate__().

  • An instance of the class vtjson.wrapper. The class vtjson.wrapper defines a single abstract method vtjson.wrapper.__compile__() that should produce an instance of vtjson.compiled_schema.

  • A Python type annotation such as list[str]. See Type annotations integration.

  • A Python type. In that case validation is done by checking membership. For compatibility with Python type annotations, the schema float matches both ints and floats. Use vtjson.float_ if you want only floats.

  • A callable. Validation is done by applying the callable to the object. If applying the callable throws an exception then the corresponding message will be part of the non-validation message.

  • An instance of Sequence that is not an instance of str (e.g a list or a tuple). Validation is done by first checking membership of the schema type, and then performing validation for each of the entries of the object being validated against the corresponding entries of the schema.

  • An instance of Mapping. Validation is done by first checking membership of the schema type, and then performing validation for each of the values of the object being validated against the corresponding values of the schema. Keys are themselves considered as schemas. E.g. {str: str} represents a dictionary whose keys and values are both strings. For a more elaborate discussion of validation of mappings see Validating against Mapping schemas.

  • A set. A set validates an object if the object is a set and the elements of the object are validated by an element of the schema.

  • An arbitrary Python object. Validation is done by checking equality of the schema and the object, except when the schema is float, in which case math.isclose is used. Below we call such an object a const schema.

class compiled_schema

The result of compiling a schema. A compiled_schema is produced by the factory function vtjson.compile().

__validate__(obj, name, strict, subs)

Validates the given object against the given schema.

Parameters:
  • schema – the given schema

  • obj (object) – the object to be validated

  • name (str) – common name for the object to be validated; used in non-validation messages

  • strict (bool) – indicates whether or not the object being validated is allowed to have keys/entries which are not in the schema

  • subs (Mapping[str, object]) – a dictionary whose keys are labels and whose values are substitution schemas for schemas with those labels

Return type:

str

Returns:

an empty string if validation succeeds; otherwise an explanation about what went wrong

compile(schema)

Compiles a schema. Internally invokes vtjson._compile().

Parameters:

schema (object) – the schema that should be compiled

Raises:

SchemaError – exception thrown when the schema definition is found to contain an error

Return type:

compiled_schema

_compile(schema, _deferred_compiles=None)

Compiles a schema.

Parameters:
  • schema (object) – the schema that should be compiled

  • _deferred_compiles (_mapping | None) – an opaque data structure used for handling recursive schemas; it should be passed unmodifed to any internal invocations of vtjson._compile() or vtjson.wrapper.__compile__()

Raises:

SchemaError – exception thrown when the schema definition is found to contain an error

Return type:

compiled_schema

class wrapper

Base class for schemas that refer to other schemas.

Handling such schemas is somewhat delicate since vtjson allows them to be recursive.

__compile__(_deferred_compiles=None)

Compiles a schema.

Parameters:

_deferred_compiles (_mapping | None) – an opaque data structure used for handling recursive schemas; it should be passed unmodifed to any internal invocations of vtjson._compile() or vtjson.wrapper.__compile__()

Raises:

SchemaError – exception thrown when the schema definition is found to contain an error

Return type:

compiled_schema

Validating against Mapping schemas

For a Mapping schema containing only const keys (i.e. keys corresponding to a const schema) the interpretation is obvious. Below we discuss the validation of an object against a Mapping schema in the general case.

  • First we verify that the type of the object is a subtype of the type of the schema. If not then validation fails.

  • We verify that all non-optional const keys of the schema are also keys of the object. If this is not the case then validation fails.

  • Now we make a list of all the keys of the schema (both optional and non-optional). The result will be called the key list below.

  • The object will pass validation if all its keys pass validation. We next discuss how to validate a particular key of the object.

  • If none of the entries of the key list validate the given key and strict==True (the default) then the key fails validation. If on the other hand strict==False then the key passes.

  • Assuming the fate of the given key hasn’t been decided yet, we now match it against all entries of the key list. If it matches an entry and the corresponding value also validates then the key is validated. Otherwise we keep going through the key list.

  • If the entire key list is consumed then the key fails validation.

A consequence of this algorithm is that non-const keys are automatically optional. So applying the wrapper vtjson.optional_key() to them is meaningless and has no effect.

class optional_key(key, _optional=True)

Bases: Generic[K]

Make a key in a Mapping schema optional. In the common case that the key is a string, the same effect can be achieved by appending ?. If you absolutely positively need a non-optional string key that ends in ? you can quote the ? by preceding it with a backslash.

Parameters:
  • key (TypeVar(K)) – the key to be made optional

  • _optional (bool) – if False create a mandatory key; this is normally for internal use only

Pre-compiling a schema

An object matches the schema compile(schema) if it matches schema. vtjson compiles a schema before using it for validation, so pre-compiling is not necessary. However for large schemas it may gain some of performance as it needs to be done only once. Compiling is an idempotent operation. It does nothing for an already compiled schema.