Files
pydantic/tests/test_parse.py
T
Samuel Colvin 594effa279 Switching to pydantic_core (#4516)
* working on core schema generation

* adapting main.py

* getting tests to run

* fix tests

* disable pyright, fix mypy

* moving to class-based model generation

* working on validators

* change how models are created

* start fixing test_main.py

* fixing mypy

* SelfType

* recursive models working, more tests fixed

* fix tests on <3.10

* get docs build to pass

* starting to cleanup types.py

* starting works on custom types

* working on using annotated-types

* using annoated types for constraints

* lots of cleanup, fixing network tests

* network tests passing 🎉

* working on types

* working on types and cleanup

* fixing UUID type, restructing again

* more types and newer pydantic-core

* working on Iterable

* more test_types tests

* support newer pydantic-core, fixing more test_types.py

* working through more test_types.py

* test_types.py at last passing locally 🎉

* fixing more tests in test_types.py

* fix datetime_parse tests and linting

* get tests running again, rename to test_datetime.py

* renaming internal modules

* working through mypy errors

* fixing mypy

* refactoring _generate_schema.py

* test_main.py passing

* uprev deps

* fix conftest and linting?

* importing Annotated

* ltining

* import Annotated from typing_extensions

* fixing 3.7 compatibility

* fixing tests on 3.9

* fix linting

* fixing SecretField and 3.9 tests

* customising get_type_hints

* ignore warnings on 3.11

* spliting repr out of utils

* removing unused bits of _repr, fix tests for 3.7

* more cleanup, removing many type aliases

* clean up repr

* support namedtuples and typeddicts

* test is_union

* removing errors, uprev pydantic-core

* fix tests on 3.8

* fixing private attributes and model_post_init

* renaming and cleanup

* remove unnecessary PydanticMetadata inheritance

* fixing forward refs and mypy tests

* fix signatures, change how xfail works

* revert mypy tests to 3.7 syntax

* correct model title

* try to fix tests

* fixing ClassVar forward refs

* uprev pydantic-core, new error format

* add "force" argument to model_rebuild

* Apply suggestions from code review

Suggestions from @tiangolo and @hramezani 🙏

Co-authored-by: Hasan Ramezani <hasan.r67@gmail.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>

* more suggestions from @tiangolo

* extra -> json_schema_extra on Field

Co-authored-by: Hasan Ramezani <hasan.r67@gmail.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2022-11-02 12:01:17 +00:00

194 lines
5.0 KiB
Python

import json
import pickle
from typing import List, Tuple, Union
import pytest
from pydantic import BaseModel, Field, ValidationError, parse_obj_as
pytestmark = pytest.mark.xfail(reason='working on V2', strict=False)
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_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)