ZetCode

Python Cerberus

last modified October 30, 2026 Python 3.15

In this article we show how to validate data with Cerberus in Python.

Cerberus

Cerberus is a lightweight and extensible data validation library. It validates Python dictionaries, such as parsed JSON or YAML documents, form data, or configuration, against a schema.

A schema is itself a dictionary. It maps each field to a set of rules such as type, required, min, or regex. The schema is passed to a Validator, whose validate method returns True or False. The reasons for a failure are available in the errors attribute.

The library is installed with the following command. The examples use Cerberus 1.3.

$ pip install cerberus

Types

The following is a simple demonstration of Cerberus. With the type rule, we set the expected type of a field.

simple.py
from cerberus import Validator

schema = {'name': {'type': 'string'}}
v = Validator(schema)

for document in [{'name': 'John Doe'}, {'name': 42}]:
    if v.validate(document):
        print('valid data')
    else:
        print('invalid data')
        print(v.errors)

We validate a name field, which must be a string.

schema = {'name': {'type': 'string'}}
v = Validator(schema)

The schema says that the name field must be a string. A validator can be reused for many documents.

if v.validate(document):
    print('valid data')
else:
    print('invalid data')
    print(v.errors)

The errors attribute is a dictionary that maps field names to lists of error messages.

$ python simple.py
Traceback (most recent call last):
  File "/home/user/python/simple.py", line 1, in <module>
    from cerberus import Validator
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
    from cerberus.platform import importlib_metadata
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
    import importlib.metadata as importlib_metadata
  File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
    import functools
  File "/usr/lib/python3.15/functools.py", line 22, in <module>
    from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
  File "/home/user/python/types.py", line 1, in <module>
    from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)

Multiple types

A list of types allows any of them. Cerberus types include string, integer, float, number (integer or float), boolean, list, dict, date, and datetime.

types.py
from cerberus import Validator

v = Validator({'words': {'type': ['string', 'list']},
               'score': {'type': 'number'}})

docs = [
    {'words': 'falcon', 'score': 3},
    {'words': ['falcon', 'sky', 'cloud'], 'score': 4.5},
    {'words': {'a': 1}, 'score': '7'},
]

for doc in docs:
    print(v.validate(doc), v.errors)

The words field is either a string or a list; the score is any number.

{'words': {'a': 1}, 'score': '7'},

Cerberus does not convert values during type checks; the string '7' is not a number. Conversion is done with the coerce rule shown later.

$ python types.py
Traceback (most recent call last):
  File "/home/user/python/types.py", line 1, in <module>
    from cerberus import Validator
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
    from cerberus.platform import importlib_metadata
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
    import importlib.metadata as importlib_metadata
  File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
    import functools
  File "/usr/lib/python3.15/functools.py", line 22, in <module>
    from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
  File "/home/user/python/types.py", line 1, in <module>
    from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)

Required, empty, and nullable

By default, all fields are optional. The required rule makes a field mandatory, empty controls whether empty strings and collections are allowed, and nullable allows None.

required.py
from cerberus import Validator

v = Validator()
v.schema = {
    'name': {'type': 'string', 'required': True, 'empty': False},
    'age': {'type': 'integer', 'nullable': True},
}

docs = [{'age': 34}, {'name': '', 'age': 34}, {'name': 'John', 'age': None}]

for doc in docs:
    print(v.validate(doc), v.errors)

print(v.validate({'age': 34}, update=True), v.errors)

The name field must be present and non-empty; the age field may be None.

print(v.validate({'age': 34}, update=True), v.errors)

With update=True, the required rule is ignored. This is useful for partial updates, such as an HTTP PATCH request, where only the changed fields are sent.

$ python required.py
Traceback (most recent call last):
  File "/home/user/python/required.py", line 1, in <module>
    from cerberus import Validator
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
    from cerberus.platform import importlib_metadata
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
    import importlib.metadata as importlib_metadata
  File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
    import functools
  File "/usr/lib/python3.15/functools.py", line 22, in <module>
    from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
  File "/home/user/python/types.py", line 1, in <module>
    from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)

Ranges and lengths

The min and max rules set the allowed range of numbers. The minlength and maxlength rules limit the length of strings and lists.

min_max.py
from cerberus import Validator

v = Validator()
v.schema = {
    'name': {'type': 'string', 'minlength': 2, 'maxlength': 20},
    'age': {'type': 'integer', 'min': 18, 'max': 65},
    'tags': {'type': 'list', 'maxlength': 3},
}

doc = {'name': 'J', 'age': 4, 'tags': ['a', 'b', 'c', 'd']}

if v.validate(doc):
    print('valid data')
else:
    print('invalid data')
    for field, errors in v.errors.items():
        print(f'{field}: {errors}')

We validate a document that breaks three rules.

for field, errors in v.errors.items():
    print(f'{field}: {errors}')

Cerberus does not stop at the first error; it reports all of them, which is what users of a form expect.

$ python min_max.py
Traceback (most recent call last):
  File "/home/user/python/min_max.py", line 1, in <module>
    from cerberus import Validator
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
    from cerberus.platform import importlib_metadata
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
    import importlib.metadata as importlib_metadata
  File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
    import functools
  File "/usr/lib/python3.15/functools.py", line 22, in <module>
    from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
  File "/home/user/python/types.py", line 1, in <module>
    from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)

Allowed and forbidden values

The allowed rule restricts a value to a set of options. For a list, every element must be allowed. The forbidden rule is the opposite.

allowed.py
from cerberus import Validator

v = Validator({
    'role': {'type': 'string', 'allowed': ['admin', 'editor', 'viewer']},
    'perms': {'type': 'list', 'allowed': ['read', 'write', 'delete']},
    'user': {'type': 'string', 'forbidden': ['root', 'admin']},
})

print(v.validate({'role': 'editor', 'perms': ['read', 'write'], 'user': 'jdoe'}))
print(v.validate({'role': 'owner', 'perms': ['read', 'exec'], 'user': 'root'}))
print(v.errors)

We validate a user's role, permissions, and name.

'user': {'type': 'string', 'forbidden': ['root', 'admin']},

Reserved user names are rejected. The error message for a list names the values that are not allowed.

$ python allowed.py
Traceback (most recent call last):
  File "/home/user/python/allowed.py", line 1, in <module>
    from cerberus import Validator
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
    from cerberus.platform import importlib_metadata
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
    import importlib.metadata as importlib_metadata
  File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
    import functools
  File "/usr/lib/python3.15/functools.py", line 22, in <module>
    from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
  File "/home/user/python/types.py", line 1, in <module>
    from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)

Regular expressions and nested documents

The regex rule checks a string against a regular expression. The schema rule of a dict field validates a nested document.

regex.py
from cerberus import Validator

v = Validator()
v.schema = {
    'contact': {
        'type': 'dict',
        'schema': {
            'phone': {'type': 'string', 'regex': r'0[0-9]{9}'},
            'email': {
                'type': 'string',
                'required': True,
                'regex': r'[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+',
            },
        },
    },
}

docs = [
    {'contact': {'phone': '0901123123', 'email': 'john.doe@example.com'}},
    {'contact': {'phone': '0901-123-123', 'email': 'john.doe'}},
    {'contact': {'phone': '0901123123'}},
]

for doc in docs:
    print(v.validate(doc), v.errors)

We validate the contact details of a user.

'phone': {'type': 'string', 'regex': r'0[0-9]{9}'},

The pattern must match the whole value; Cerberus anchors it automatically. Raw strings avoid doubling the backslashes.

'contact': {
    'type': 'dict',
    'schema': {
        ...

Errors in nested documents are reported as a list containing a dictionary of the nested errors.

$ python regex.py
Traceback (most recent call last):
  File "/home/user/python/regex.py", line 1, in <module>
    from cerberus import Validator
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
    from cerberus.platform import importlib_metadata
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
    import importlib.metadata as importlib_metadata
  File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
    import functools
  File "/usr/lib/python3.15/functools.py", line 22, in <module>
    from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
  File "/home/user/python/types.py", line 1, in <module>
    from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)

Lists

For a list field, the schema rule validates every element, and the items rule validates elements by position.

lists.py
from cerberus import Validator

v = Validator({
    'cities': {'type': 'list', 'schema': {'type': 'string', 'minlength': 2}},
    'point': {'type': 'list', 'items': [{'type': 'number'}, {'type': 'number'}]},
    'users': {
        'type': 'list',
        'schema': {
            'type': 'dict',
            'schema': {'name': {'type': 'string', 'required': True},
                       'age': {'type': 'integer', 'min': 0}},
        },
    },
})

doc = {
    'cities': ['Bratislava', 'X', 'Kosice'],
    'point': [1.5, 'north'],
    'users': [{'name': 'John', 'age': 34}, {'age': -1}],
}

print(v.validate(doc))

for field, errors in v.errors.items():
    print(field, errors)

We validate a list of strings, a fixed-length pair, and a list of dictionaries.

'point': {'type': 'list', 'items': [{'type': 'number'}, {'type': 'number'}]},

The items rule describes a tuple-like list; here a point with two coordinates.

'users': {
    'type': 'list',
    'schema': {
        'type': 'dict',
        'schema': {...},

Each user must be a dictionary with its own schema. The errors are keyed by the index of the invalid element.

$ python lists.py
Traceback (most recent call last):
  File "/home/user/python/lists.py", line 1, in <module>
    from cerberus import Validator
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
    from cerberus.platform import importlib_metadata
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
    import importlib.metadata as importlib_metadata
  File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
    import functools
  File "/usr/lib/python3.15/functools.py", line 22, in <module>
    from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
  File "/home/user/python/types.py", line 1, in <module>
    from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)

Value coercion

The coerce rule applies a callable to a value before it is validated. The result replaces the value in the validated document.

coercing.py
from datetime import datetime
from cerberus import Validator

def to_date(s):
    return datetime.strptime(s, '%Y-%m-%d')

v = Validator({
    'start_date': {'type': 'datetime', 'coerce': to_date},
    'amount': {'type': 'integer', 'coerce': int, 'min': 1},
})

doc = {'start_date': '2026-10-30', 'amount': '25'}

print(v.validate(doc))
print(v.document)
print(doc)

print(v.validate({'start_date': '2026/10/30', 'amount': 'ten'}))

for field, errors in v.errors.items():
    print(field, errors)

We convert a date string to a datetime and a numeric string to an integer.

print(v.document)
print(doc)

The validator stores the coerced values in its document attribute; the original dictionary is not modified.

print(v.validate({'start_date': '2026/10/30', 'amount': 'ten'}))

If the callable raises an exception, Cerberus reports that the field cannot be coerced and then the type error.

$ python coercing.py
Traceback (most recent call last):
  File "/home/user/python/coercing.py", line 2, in <module>
    from cerberus import Validator
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
    from cerberus.platform import importlib_metadata
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
    import importlib.metadata as importlib_metadata
  File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
    import functools
  File "/usr/lib/python3.15/functools.py", line 22, in <module>
    from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
  File "/home/user/python/types.py", line 1, in <module>
    from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)

Normalization

Besides coercion, Cerberus can fill in default values, rename fields, and remove unknown fields. The normalized method returns the normalized document without validating it.

normalize.py
from cerberus import Validator

schema = {
    'name': {'type': 'string', 'coerce': str.strip},
    'role': {'type': 'string', 'default': 'viewer'},
    'active': {'type': 'boolean', 'default': True},
    'email': {'type': 'string', 'rename': 'mail'},
    'mail': {'type': 'string'},
}

v = Validator(schema, purge_unknown=True)

doc = {'name': '  John Doe ', 'email': 'jdoe@example.com', 'debug': 1}

print(v.normalized(doc))
print(v.validate(doc), v.document)

We clean up a user record.

'name': {'type': 'string', 'coerce': str.strip},
'role': {'type': 'string', 'default': 'viewer'},
...
'email': {'type': 'string', 'rename': 'mail'},

Surrounding whitespace is stripped, missing fields get defaults, and the email field is renamed to mail.

v = Validator(schema, purge_unknown=True)

With purge_unknown=True, the unknown debug field is removed instead of causing an error.

$ python normalize.py
Traceback (most recent call last):
  File "/home/user/python/normalize.py", line 1, in <module>
    from cerberus import Validator
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
    from cerberus.platform import importlib_metadata
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
    import importlib.metadata as importlib_metadata
  File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
    import functools
  File "/usr/lib/python3.15/functools.py", line 22, in <module>
    from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
  File "/home/user/python/types.py", line 1, in <module>
    from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)

Unknown fields

By default, fields that are not in the schema are errors. The allow_unknown option accepts them, optionally with a schema of their own.

unknown.py
from cerberus import Validator

schema = {'name': {'type': 'string'}}
doc = {'name': 'John', 'nick': 'jd'}

v = Validator(schema)
print(v.validate(doc), v.errors)

v = Validator(schema, allow_unknown=True)
print(v.validate(doc), v.errors)

v = Validator(schema, allow_unknown={'type': 'integer'})
print(v.validate(doc), v.errors)

We validate the same document with three settings.

v = Validator(schema, allow_unknown={'type': 'integer'})

When allow_unknown is a rule set, unknown fields are validated against it.

$ python unknown.py
Traceback (most recent call last):
  File "/home/user/python/unknown.py", line 1, in <module>
    from cerberus import Validator
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
    from cerberus.platform import importlib_metadata
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
    import importlib.metadata as importlib_metadata
  File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
    import functools
  File "/usr/lib/python3.15/functools.py", line 22, in <module>
    from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
  File "/home/user/python/types.py", line 1, in <module>
    from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)

Dependencies and exclusions

The dependencies rule requires other fields, optionally with specific values. The excludes rule forbids a combination of fields.

dependencies.py
from cerberus import Validator

v = Validator({
    'payment': {'type': 'string', 'allowed': ['card', 'transfer']},
    'card_number': {'type': 'string', 'dependencies': {'payment': ['card']}},
    'iban': {'type': 'string', 'dependencies': {'payment': ['transfer']},
             'excludes': 'card_number'},
})

docs = [
    {'payment': 'card', 'card_number': '4111111111111111'},
    {'payment': 'transfer', 'card_number': '4111111111111111'},
    {'payment': 'transfer', 'iban': 'SK3112000000198742637541',
     'card_number': '4111111111111111'},
]

for doc in docs:
    print(v.validate(doc), v.errors)

A payment either uses a card or a bank transfer.

'card_number': {'type': 'string', 'dependencies': {'payment': ['card']}},

A card number is allowed only when the payment method is card.

'iban': {'type': 'string', 'dependencies': {'payment': ['transfer']},
         'excludes': 'card_number'},

An IBAN requires the transfer method and must not be given together with a card number.

$ python dependencies.py
Traceback (most recent call last):
  File "/home/user/python/dependencies.py", line 1, in <module>
    from cerberus import Validator
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
    from cerberus.platform import importlib_metadata
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
    import importlib.metadata as importlib_metadata
  File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
    import functools
  File "/usr/lib/python3.15/functools.py", line 22, in <module>
    from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
  File "/home/user/python/types.py", line 1, in <module>
    from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)

Custom checks

The check_with rule calls a function that reports errors through the error callback.

check_with.py
from cerberus import Validator

def luhn(field, value, error):
    if not value.isdigit():
        return
    digits = [int(d) for d in value]
    checksum = sum(digits[-1::-2])
    checksum += sum(sum(divmod(2 * d, 10)) for d in digits[-2::-2])
    if checksum % 10 != 0:
        error(field, 'invalid checksum')

v = Validator({'card': {'type': 'string', 'regex': r'\d{13,19}',
                        'check_with': luhn}})

for card in ['4111111111111111', '4111111111111112', '4111-1111']:
    print(card, v.validate({'card': card}), v.errors)

We validate credit card numbers with the Luhn checksum.

def luhn(field, value, error):
    if not value.isdigit():
        return

All rules of a field run even if an earlier one failed, so the function must not assume that the regex rule has passed.

if checksum % 10 != 0:
    error(field, 'invalid checksum')

The function returns nothing; it reports a problem by calling error with the field name and a message.

$ python check_with.py
Traceback (most recent call last):
  File "/home/user/python/check_with.py", line 1, in <module>
    from cerberus import Validator
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
    from cerberus.platform import importlib_metadata
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
    import importlib.metadata as importlib_metadata
  File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
    import functools
  File "/usr/lib/python3.15/functools.py", line 22, in <module>
    from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
  File "/home/user/python/types.py", line 1, in <module>
    from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)

Custom rules

New rules are added by subclassing Validator. A method named _validate_<rule> implements a rule, and a method named _check_with_<name> can be referenced by name in check_with.

custom_rule.py
from cerberus import Validator

class AppValidator(Validator):

    def _validate_is_odd(self, constraint, field, value):
        """{'type': 'boolean'}"""
        if constraint and value % 2 == 0:
            self._error(field, 'must be an odd number')

    def _check_with_title_case(self, field, value):
        if value != value.title():
            self._error(field, 'must be in title case')

schema = {
    'count': {'type': 'integer', 'is_odd': True},
    'city': {'type': 'string', 'check_with': 'title_case'},
}

v = AppValidator(schema)

print(v.validate({'count': 7, 'city': 'New York'}), v.errors)
print(v.validate({'count': 8, 'city': 'new york'}), v.errors)

We add an is_odd rule and a title_case check.

def _validate_is_odd(self, constraint, field, value):
    """{'type': 'boolean'}"""

The docstring is required: it is a schema for the rule's own constraint, which Cerberus uses to validate schemas. Here the constraint must be a boolean.

'city': {'type': 'string', 'check_with': 'title_case'},

A string in check_with refers to the _check_with_title_case method.

$ python custom_rule.py
Traceback (most recent call last):
  File "/home/user/python/custom_rule.py", line 1, in <module>
    from cerberus import Validator
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
    from cerberus.platform import importlib_metadata
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
    import importlib.metadata as importlib_metadata
  File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
    import functools
  File "/usr/lib/python3.15/functools.py", line 22, in <module>
    from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
  File "/home/user/python/types.py", line 1, in <module>
    from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)

Validating objects

Cerberus validates mappings. To validate an object, we convert it to a dictionary first.

dataclass_validator.py
from dataclasses import dataclass, asdict
from cerberus import Validator

@dataclass
class Person:
    name: str
    age: int

class PersonValidator(Validator):

    def validate_person(self, person):
        return self.validate(asdict(person))

schema = {'name': {'type': 'string', 'minlength': 2},
          'age': {'type': 'integer', 'min': 18, 'max': 65}}

v = PersonValidator(schema)

for p in [Person('John Doe', 34), Person('Lucy', 2)]:
    if v.validate_person(p):
        print(f'{p}: valid')
    else:
        print(f'{p}: {v.errors}')

We validate Person dataclass instances.

def validate_person(self, person):
    return self.validate(asdict(person))

The asdict function converts a dataclass instance into a dictionary, including nested dataclasses.

$ python dataclass_validator.py
Traceback (most recent call last):
  File "/home/user/python/dataclass_validator.py", line 1, in <module>
    from dataclasses import dataclass, asdict
  File "/usr/lib/python3.15/dataclasses.py", line 2, in <module>
    import types
  File "/home/user/python/types.py", line 1, in <module>
    from cerberus import Validator
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
    from cerberus.platform import importlib_metadata
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
    import importlib.metadata as importlib_metadata
  File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
    import functools
  File "/usr/lib/python3.15/functools.py", line 22, in <module>
    from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
ImportError: cannot import name 'FunctionType' from 'types' (consider renaming '/home/user/python/types.py' since it has the same name as the standard library module named 'types' and prevents importing that standard library module)

Validating a YAML file

Data loaded from YAML or JSON files are dictionaries and lists, which is exactly what Cerberus validates.

cities.yaml
cities:
  - Bratislava
  - Kosice
  - Trnava
  - Moldava
  - Trencin
population: 1234567

The file contains a list of cities and a number.

from_yaml.py
import yaml
from cerberus import Validator

v = Validator({
    'cities': {'type': 'list', 'schema': {'type': 'string'}},
    'population': {'type': 'integer', 'min': 0},
})

with open('cities.yaml') as f:
    data = yaml.safe_load(f)

print(data)

if v.validate(data):
    print('valid data')
else:
    print('invalid data')
    print(v.errors)

We load the file with PyYAML and validate the result.

data = yaml.safe_load(f)

The safe_load function creates only basic Python types and is safe to use with untrusted files.

$ python from_yaml.py
Traceback (most recent call last):
  File "/home/user/python/from_yaml.py", line 1, in <module>
    import yaml
  File "/home/user/python/.venv/lib/python3.15/site-packages/yaml/__init__.py", line 8, in <module>
    from .loader import *
  File "/home/user/python/.venv/lib/python3.15/site-packages/yaml/loader.py", line 4, in <module>
    from .reader import *
  File "/home/user/python/.venv/lib/python3.15/site-packages/yaml/reader.py", line 22, in <module>
    import codecs, re
  File "/usr/lib/python3.15/re/__init__.py", line 126, in <module>
    import enum
  File "/usr/lib/python3.15/enum.py", line 3, in <module>
    from types import MappingProxyType, DynamicClassAttribute
  File "/home/user/python/types.py", line 1, in <module>
    from cerberus import Validator
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
    from cerberus.platform import importlib_metadata
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
    import importlib.metadata as importlib_metadata
  File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
    import functools
  File "/usr/lib/python3.15/functools.py", line 22, in <module>
    from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
ImportError: cannot import name 'FunctionType' from 'types' (consider renaming '/home/user/python/types.py' since it has the same name as the standard library module named 'types' and prevents importing that standard library module)

Schema errors

Cerberus validates the schema itself when the validator is created. A typo in a rule name is caught immediately.

schema_error.py
from cerberus import Validator
from cerberus.schema import SchemaError

try:
    Validator({'age': {'type': 'integer', 'minimum': 18}})
except SchemaError as e:
    print(e)

The rule is called min, not minimum. Creating the validator raises a SchemaError.

$ python schema_error.py
Traceback (most recent call last):
  File "/home/user/python/schema_error.py", line 1, in <module>
    from cerberus import Validator
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py", line 13, in <module>
    from cerberus.platform import importlib_metadata
  File "/home/user/python/.venv/lib/python3.15/site-packages/cerberus/platform.py", line 45, in <module>
    import importlib.metadata as importlib_metadata
  File "/usr/lib/python3.15/importlib/metadata/__init__.py", line 15, in <module>
    import functools
  File "/usr/lib/python3.15/functools.py", line 22, in <module>
    from types import FunctionType, GenericAlias, MethodType, MappingProxyType, UnionType
  File "/home/user/python/types.py", line 1, in <module>
    from cerberus import Validator
ImportError: cannot import name 'Validator' from partially initialized module 'cerberus' (most likely due to a circular import) (/home/user/python/.venv/lib/python3.15/site-packages/cerberus/__init__.py)

Source

Cerberus documentation

In this article we have shown how to validate data in Python with Cerberus.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Python tutorials.