Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
python pydantic typed dict validation
from functools import wraps
from typing import TypedDict
from pydantic import BaseModel, validate_arguments
def replace_typed_dict_annotations(annotations):
new_annotations = {}
for k, T in annotations.items():
if type(T).__name__ == '_TypedDictMeta':
class M(TDict):
__annotations__ = replace_typed_dict_annotations(T.__annotations__)
M.__name__ = T.__name__
new_annotations[k] = M
new_annotations[k] = T
return new_annotations
def validate_args(fn):
def f(*args, **kwargs):
def wrapper(*args, **kwargs):
f(*args, **kwargs)
return fn(*args, **kwargs)
wrapper.__annotations__ = replace_typed_dict_annotations(fn.__annotations__)
return validate_arguments(wrapper)
class TDict(BaseModel):
class Config:
extra = 'forbid'
arbitrary_types_allowed = True
def __get_validators__(cls):
yield from super().__get_validators__()
yield cls.validate
def validate(cls, v):
if not isinstance(v, dict):
raise TypeError('dict type expected')
return cls(**v).dict()
class D(TypedDict):
z: int
class A(TypedDict):
x: int
y: str
d: D
def foo(a: A) -> int:
return a
print(foo({'x': 1, 'y': '2', 'd': {'z': 3}}))
print(foo({'x': 1, 'y': [], 'z': 22, 'd': []}))
# OUT:
# <class 'dict'>
# {'x': 1, 'y': '2', 'd': {'z': 3}}
# ...
# pydantic.error_wrappers.ValidationError: 3 validation errors for Foo
# a -> y
# str type expected (type=type_error.str)
# a -> d
# dict type expected (type=type_error)
# a -> z
# extra fields not permitted (type=value_error.extra)

This comment has been minimized.

Copy link
Owner Author

@bofm bofm commented Jan 13, 2021

workaround for samuelcolvin/pydantic#760

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment