In [25]:
import numpy as np
from collections.abc import Iterable
from typing import List
from pydantic import (
    BaseModel, validator, root_validator,
    PositiveFloat, confloat
)

Intro

Pydantized python


In [18]:
class A(BaseModel):
    x: int
class B(A):
    y: int

In [19]:
class Vector(BaseModel):
    r: PositiveFloat
    θ: confloat(gt=0, lt=2*np.pi)

In [20]:
Vector.schema()


Out[20]:
{'title': 'Vector',
 'type': 'object',
 'properties': {'r': {'title': 'R', 'type': 'number', 'exclusiveMinimum': 0},
  'θ': {'title': 'Θ',
   'type': 'number',
   'exclusiveMinimum': 0,
   'exclusiveMaximum': 6.283185307179586}},
 'required': ['r', 'θ']}

In [21]:
class Complex(Vector):
    @validator('θ', pre=True)
    def standardize_θ(θ):
        return θ % (2*np.pi)
    def conj(self):
        return Complex(r=self.r, θ=-self.θ)
      
z = Complex(r=1, θ=0.75)
z.conj()


Out[21]:
Complex(r=1.0, θ=5.533185307179586)

In [22]:
class VectorBasis(BaseModel):
    e :List[Vector]

basis = VectorBasis(e=[z, z.conj()])
basis


Out[22]:
VectorBasis(e=[Complex(r=1.0, θ=0.75), Complex(r=1.0, θ=5.533185307179586)])

Normal Python


In [13]:
class A:
    def __init__(self, x:int):
        self.x = x
class B(A):
    def __init__(self, x:int, y:int):
        super().__init__(x)
            # Remove y from signature
        self.y

In [14]:
class Vector:
    def __init__(self, r:float, θ:float):
        if r <= 0:
            raise ValueError("Negative radius")
        if θ < 0 or 2*np.pi < θ:
            raise ValueError("Angle outside [0,2π]")
        self.r = r
        self.θ = θ

In [15]:
class Complex(Vector):
    def __init__(self, r:float, θ:float):
        θ = θ % (2*np.pi)
        super().__init__(r, θ)
    def conj(self):
        return Complex(self.r, -self.θ)
      
z = Complex(r=1, θ=0.75)
z.conj()


Out[15]:
<__main__.Complex at 0x7f476d343040>

In [16]:
class VectorBasis:
    def __init__(self, e):
        if not isinstance(e, Iterable):
            raise TypeError(
              "e is not iterable")
        if not isinstance(e, list):
            e = list(e)
        for ei in e:
            if not isinstance(ei, Vector):
                raise TypeError(
                  "e must be composed of Vector objects")
        self.e = e
    
basis = VectorBasis([z, z.conj()])
basis


Out[16]:
<__main__.VectorBasis at 0x7f476d343b80>

Example validation error


In [23]:
Vector(r=-1, θ=99)


---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
<ipython-input-23-3198a3192d14> in <module>
----> 1 Vector(r=-1, θ=99)

~/usr/local/miniconda3/envs/sinn/lib/python3.8/site-packages/pydantic/main.cpython-38-x86_64-linux-gnu.so in pydantic.main.BaseModel.__init__()

ValidationError: 2 validation errors for Vector
r
  ensure this value is greater than 0 (type=value_error.number.not_gt; limit_value=0)
θ
  ensure this value is less than 6.283185307179586 (type=value_error.number.not_lt; limit_value=6.283185307179586)

Execution order


In [26]:
class Foo(BaseModel):
    a: int
    b: int
    @root_validator
    def root_val_post(cls, values):
        print("root val post")
        return values
    @root_validator(pre=True)
    def root_val_pre(cls, values):
        print("root val pre")
        return values
    @validator('a')
    def val_post_a(cls, a):
        print("val post a")
        return a
    @validator('b', pre=True)
    def val_pre_b(cls, b):
        print("val pre b")
        return b
    @validator('a', pre=True)
    def val_pre_a(cls, a):
        print("val pre a")
        return a
    @validator('b')
    def val_post_b(cls, b):
        print("val post b")
        return b

In [28]:
Foo(a=1, b=1)


root val pre
val pre a
val post a
val pre b
val post b
root val post
Out[28]:
Foo(a=1, b=1)

In [29]:
Foo()


root val pre
root val post
---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
<ipython-input-29-fdea65d60c59> in <module>
----> 1 Foo()

~/usr/local/miniconda3/envs/sinn/lib/python3.8/site-packages/pydantic/main.cpython-38-x86_64-linux-gnu.so in pydantic.main.BaseModel.__init__()

ValidationError: 2 validation errors for Foo
a
  field required (type=value_error.missing)
b
  field required (type=value_error.missing)

In [30]:
Foo(a=[1,2], b=1)


root val pre
val pre a
val pre b
val post b
root val post
---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
<ipython-input-30-e67f50384fc4> in <module>
----> 1 Foo(a=[1,2], b=1)

~/usr/local/miniconda3/envs/sinn/lib/python3.8/site-packages/pydantic/main.cpython-38-x86_64-linux-gnu.so in pydantic.main.BaseModel.__init__()

ValidationError: 1 validation error for Foo
a
  value is not a valid integer (type=type_error.integer)

In [31]:
Foo(a=1, b=[1,2])


root val pre
val pre a
val post a
val pre b
root val post
---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
<ipython-input-31-ca8e22466f32> in <module>
----> 1 Foo(a=1, b=[1,2])

~/usr/local/miniconda3/envs/sinn/lib/python3.8/site-packages/pydantic/main.cpython-38-x86_64-linux-gnu.so in pydantic.main.BaseModel.__init__()

ValidationError: 1 validation error for Foo
b
  value is not a valid integer (type=type_error.integer)

In [32]:
Foo(a=[1,2], b=[1,2])


root val pre
val pre a
val pre b
root val post
---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
<ipython-input-32-fe07bec27756> in <module>
----> 1 Foo(a=[1,2], b=[1,2])

~/usr/local/miniconda3/envs/sinn/lib/python3.8/site-packages/pydantic/main.cpython-38-x86_64-linux-gnu.so in pydantic.main.BaseModel.__init__()

ValidationError: 2 validation errors for Foo
a
  value is not a valid integer (type=type_error.integer)
b
  value is not a valid integer (type=type_error.integer)

In [ ]: