Pydantic

Data validation using Python 3.6 type hinting

 

Samuel Colvin

from datetime import datetime
from typing import List
from pydantic import BaseModel

class User(BaseModel):
    id: int
    name = 'John Doe'
    signup_ts: datetime = None
    friends: List[int] = []

external_data = {
    'id': '123',
    'signup_ts': '2017-06-01 12:22',
    'friends': [1, '2', b'3']
}
user = User(**external_data)

print(user.id)              #> 123
print(repr(user.signup_ts)) #> datetime.datetime(2017, 6, 1, 12, 22)
print(friends)              #> [1, 2, 3]

Basic Usage

from datetime import datetime
from typing import List
from pydantic import BaseModel


class User(BaseModel):
    id: int
    name = 'John Doe'
    signup_ts: datetime = None
    friends: List[int] = []


User(friends=['not int'])

#>
Traceback (most recent call last):
  ...
    raise ValidationError(errors)
pydantic.exceptions.ValidationError: 2 errors validating input
id:
  field required (error_type=Missing)
friends:
  invalid literal for int() with 
     base 10: 'not int' (error_type=ValueError track=int)

Errors

from enum import Enum
from pydantic import BaseModel, validator


class FruitEnum(str, Enum):
    pear = 'pear'
    banana = 'banana'


class Foo(BaseModel):
    count: int
    size: float = None


class MyModel(BaseModel):
    fruit: FruitEnum = FruitEnum.pear
    foo: Foo
    name: str

    @validator('name')
    def name_must_contain_space(cls, v):
        if ' ' not in v:
            raise ValueError('must contain a space')
        return v.title()

Complex Types

Performance

Package Relative Perf. Validation Time
pydantic 24.8μs
toasted-marshmallow 1.6x slower 39.5μs
marshmallow 1.9x slower 47.4μs
trafaret 2.3x slower 56.1μs
django-restful-framework 16.0x slower 397.5μs
  • no brainfuck - no new schema definition micro-language to learn. 

  • python types - plays nicely with your IDE, linter and mypy

  • dual-use - settings (environment variable) validation and API data validation

  • Fast ...

Text

Rationale