import json import pickle from typing import List, Tuple, Union import pytest from pydantic import BaseModel, Field, Protocol, ValidationError, parse_obj_as class Model(BaseModel): a: float b: int = 10 def test_obj(): m = Model.parse_obj(dict(a=10.2)) assert str(m) == 'a=10.2 b=10' def test_parse_obj_fails(): with pytest.raises(ValidationError) as exc_info: Model.parse_obj([1, 2, 3]) assert exc_info.value.errors() == [ {'loc': ('__root__',), 'msg': 'Model expected dict not list', 'type': 'type_error'} ] def test_parse_obj_submodel(): m = Model.parse_obj(Model(a=10.2)) assert m.dict() == {'a': 10.2, 'b': 10} def test_parse_obj_wrong_model(): class Foo(BaseModel): c = 123 with pytest.raises(ValidationError) as exc_info: Model.parse_obj(Foo()) assert exc_info.value.errors() == [{'loc': ('a',), 'msg': 'field required', 'type': 'value_error.missing'}] def test_parse_obj_root(): class MyModel(BaseModel): __root__: str m = MyModel.parse_obj('a') assert m.dict() == {'__root__': 'a'} assert m.__root__ == 'a' def test_parse_root_list(): class MyModel(BaseModel): __root__: List[str] m = MyModel.parse_obj(['a']) assert m.dict() == {'__root__': ['a']} assert m.__root__ == ['a'] def test_parse_nested_root_list(): class NestedData(BaseModel): id: str class NestedModel(BaseModel): __root__: List[NestedData] class MyModel(BaseModel): nested: NestedModel m = MyModel.parse_obj({'nested': [{'id': 'foo'}]}) assert isinstance(m.nested, NestedModel) assert isinstance(m.nested.__root__[0], NestedData) def test_parse_nested_root_tuple(): class NestedData(BaseModel): id: str class NestedModel(BaseModel): __root__: Tuple[int, NestedData] class MyModel(BaseModel): nested: List[NestedModel] data = [0, {'id': 'foo'}] m = MyModel.parse_obj({'nested': [data]}) assert isinstance(m.nested[0], NestedModel) assert isinstance(m.nested[0].__root__[1], NestedData) nested = parse_obj_as(NestedModel, data) assert isinstance(nested, NestedModel) def test_parse_nested_custom_root(): class NestedModel(BaseModel): __root__: List[str] class MyModel(BaseModel): __root__: NestedModel nested = ['foo', 'bar'] m = MyModel.parse_obj(nested) assert isinstance(m, MyModel) assert isinstance(m.__root__, NestedModel) assert isinstance(m.__root__.__root__, List) assert isinstance(m.__root__.__root__[0], str) def test_json(): assert Model.parse_raw('{"a": 12, "b": 8}') == Model(a=12, b=8) def test_json_ct(): assert Model.parse_raw('{"a": 12, "b": 8}', content_type='application/json') == Model(a=12, b=8) def test_pickle_ct(): data = pickle.dumps(dict(a=12, b=8)) assert Model.parse_raw(data, content_type='application/pickle', allow_pickle=True) == Model(a=12, b=8) def test_pickle_proto(): data = pickle.dumps(dict(a=12, b=8)) assert Model.parse_raw(data, proto=Protocol.pickle, allow_pickle=True) == Model(a=12, b=8) def test_pickle_not_allowed(): data = pickle.dumps(dict(a=12, b=8)) with pytest.raises(RuntimeError): Model.parse_raw(data, proto=Protocol.pickle) def test_bad_ct(): with pytest.raises(ValidationError) as exc_info: Model.parse_raw('{"a": 12, "b": 8}', content_type='application/missing') assert exc_info.value.errors() == [ {'loc': ('__root__',), 'msg': 'Unknown content-type: application/missing', 'type': 'type_error'} ] def test_bad_proto(): with pytest.raises(ValidationError) as exc_info: Model.parse_raw('{"a": 12, "b": 8}', proto='foobar') assert exc_info.value.errors() == [{'loc': ('__root__',), 'msg': 'Unknown protocol: foobar', 'type': 'type_error'}] def test_file_json(tmpdir): p = tmpdir.join('test.json') p.write('{"a": 12, "b": 8}') assert Model.parse_file(str(p)) == Model(a=12, b=8) def test_file_json_no_ext(tmpdir): p = tmpdir.join('test') p.write('{"a": 12, "b": 8}') assert Model.parse_file(str(p)) == Model(a=12, b=8) def test_file_json_loads(tmp_path): def custom_json_loads(*args, **kwargs): data = json.loads(*args, **kwargs) data['a'] = 99 return data class Example(BaseModel): a: int class Config: json_loads = custom_json_loads p = tmp_path / 'test_json_loads.json' p.write_text('{"a": 12}') assert Example.parse_file(p) == Example(a=99) def test_file_pickle(tmpdir): p = tmpdir.join('test.pkl') p.write_binary(pickle.dumps(dict(a=12, b=8))) assert Model.parse_file(str(p), allow_pickle=True) == Model(a=12, b=8) def test_file_pickle_no_ext(tmpdir): p = tmpdir.join('test') p.write_binary(pickle.dumps(dict(a=12, b=8))) assert Model.parse_file(str(p), content_type='application/pickle', allow_pickle=True) == Model(a=12, b=8) def test_const_differentiates_union(): class SubModelA(BaseModel): key: str = Field('A', const=True) foo: int class SubModelB(BaseModel): key: str = Field('B', const=True) foo: int class Model(BaseModel): a: Union[SubModelA, SubModelB] m = Model.parse_obj({'a': {'key': 'B', 'foo': 3}}) assert isinstance(m.a, SubModelB)