import sys from enum import Enum from typing import Any, Callable, ClassVar, List, Mapping, Optional, Type from uuid import UUID, uuid4 import pytest from pydantic import BaseModel, Extra, Field, NoneBytes, NoneStr, Required, ValidationError, constr def test_success(): # same as below but defined here so class definition occurs inside the test class Model(BaseModel): a: float b: int = 10 m = Model(a=10.2) assert m.a == 10.2 assert m.b == 10 class UltraSimpleModel(BaseModel): a: float b: int = 10 def test_ultra_simple_missing(): with pytest.raises(ValidationError) as exc_info: UltraSimpleModel() assert exc_info.value.errors() == [{'loc': ('a',), 'msg': 'field required', 'type': 'value_error.missing'}] def test_ultra_simple_failed(): with pytest.raises(ValidationError) as exc_info: UltraSimpleModel(a='x', b='x') assert exc_info.value.errors() == [ {'loc': ('a',), 'msg': 'value is not a valid float', 'type': 'type_error.float'}, {'loc': ('b',), 'msg': 'value is not a valid integer', 'type': 'type_error.integer'}, ] def test_ultra_simple_repr(): m = UltraSimpleModel(a=10.2) assert str(m) == 'a=10.2 b=10' assert repr(m) == 'UltraSimpleModel(a=10.2, b=10)' assert repr(m.__fields__['a']) == "ModelField(name='a', type=float, required=True)" assert repr(m.__fields__['b']) == "ModelField(name='b', type=int, required=False, default=10)" assert dict(m) == {'a': 10.2, 'b': 10} assert m.dict() == {'a': 10.2, 'b': 10} assert m.json() == '{"a": 10.2, "b": 10}' with pytest.raises(DeprecationWarning, match=r'`model.to_string\(\)` method is deprecated'): assert m.to_string() == 'a=10.2 b=10' def test_default_dict_repr(): def myfunc(): return 1 class Model(BaseModel): a: int = Field(default_factory=myfunc) b = Field(default_factory=myfunc) m = Model() assert str(m) == 'a=1 b=1' assert repr(m) == 'Model(a=1, b=1)' assert ( repr(m.__fields__['a']) == "ModelField(name='a', type=int, required=False, default_factory='')" ) assert ( repr(m.__fields__['b']) == "ModelField(name='b', type=int, required=False, default_factory='')" ) assert dict(m) == {'a': 1, 'b': 1} assert m.dict() == {'a': 1, 'b': 1} assert m.json() == '{"a": 1, "b": 1}' def test_comparing(): m = UltraSimpleModel(a=10.2, b='100') assert m == {'a': 10.2, 'b': 100} assert m == UltraSimpleModel(a=10.2, b=100) def test_nullable_strings_success(): class NoneCheckModel(BaseModel): existing_str_value = 'foo' required_str_value: str = ... required_str_none_value: NoneStr = ... existing_bytes_value = b'foo' required_bytes_value: bytes = ... required_bytes_none_value: NoneBytes = ... m = NoneCheckModel( required_str_value='v1', required_str_none_value=None, required_bytes_value='v2', required_bytes_none_value=None ) assert m.required_str_value == 'v1' assert m.required_str_none_value is None assert m.required_bytes_value == b'v2' assert m.required_bytes_none_value is None def test_nullable_strings_fails(): class NoneCheckModel(BaseModel): existing_str_value = 'foo' required_str_value: str = ... required_str_none_value: NoneStr = ... existing_bytes_value = b'foo' required_bytes_value: bytes = ... required_bytes_none_value: NoneBytes = ... with pytest.raises(ValidationError) as exc_info: NoneCheckModel( required_str_value=None, required_str_none_value=None, required_bytes_value=None, required_bytes_none_value=None, ) assert exc_info.value.errors() == [ {'loc': ('required_str_value',), 'msg': 'none is not an allowed value', 'type': 'type_error.none.not_allowed'}, { 'loc': ('required_bytes_value',), 'msg': 'none is not an allowed value', 'type': 'type_error.none.not_allowed', }, ] class RecursiveModel(BaseModel): grape: bool = ... banana: UltraSimpleModel = ... def test_recursion(): m = RecursiveModel(grape=1, banana={'a': 1}) assert m.grape is True assert m.banana.a == 1.0 assert m.banana.b == 10 assert repr(m) == 'RecursiveModel(grape=True, banana=UltraSimpleModel(a=1.0, b=10))' def test_recursion_fails(): with pytest.raises(ValidationError): RecursiveModel(grape=1, banana=123) def test_not_required(): class Model(BaseModel): a: float = None assert Model(a=12.2).a == 12.2 assert Model().a is None assert Model(a=None).a is None def test_infer_type(): class Model(BaseModel): a = False b = '' c = 0 assert Model().a is False assert Model().b == '' assert Model().c == 0 def test_allow_extra(): class Model(BaseModel): a: float = ... class Config: extra = Extra.allow assert Model(a='10.2', b=12).dict() == {'a': 10.2, 'b': 12} def test_forbidden_extra_success(): class ForbiddenExtra(BaseModel): foo = 'whatever' class Config: extra = Extra.forbid m = ForbiddenExtra() assert m.foo == 'whatever' m = ForbiddenExtra(foo=1) assert m.foo == '1' def test_forbidden_extra_fails(): class ForbiddenExtra(BaseModel): foo = 'whatever' class Config: extra = Extra.forbid with pytest.raises(ValidationError) as exc_info: ForbiddenExtra(foo='ok', bar='wrong', spam='xx') assert exc_info.value.errors() == [ {'loc': ('bar',), 'msg': 'extra fields not permitted', 'type': 'value_error.extra'}, {'loc': ('spam',), 'msg': 'extra fields not permitted', 'type': 'value_error.extra'}, ] def test_disallow_mutation(): class Model(BaseModel): a: float model = Model(a=0.2) with pytest.raises(ValueError, match='"Model" object has no field "b"'): model.b = 2 def test_extra_allowed(): class Model(BaseModel): a: float class Config: extra = Extra.allow model = Model(a=0.2, b=0.1) assert model.b == 0.1 assert not hasattr(model, 'c') model.c = 1 assert hasattr(model, 'c') assert model.c == 1 def test_extra_ignored(): class Model(BaseModel): a: float class Config: extra = Extra.ignore model = Model(a=0.2, b=0.1) assert not hasattr(model, 'b') with pytest.raises(ValueError, match='"Model" object has no field "c"'): model.c = 1 def test_set_attr(): m = UltraSimpleModel(a=10.2) assert m.dict() == {'a': 10.2, 'b': 10} m.b = 20 assert m.dict() == {'a': 10.2, 'b': 20} def test_set_attr_invalid(): class UltraSimpleModel(BaseModel): a: float = ... b: int = 10 m = UltraSimpleModel(a=10.2) assert m.dict() == {'a': 10.2, 'b': 10} with pytest.raises(ValueError) as exc_info: m.c = 20 assert '"UltraSimpleModel" object has no field "c"' in exc_info.value.args[0] def test_any(): class AnyModel(BaseModel): a: Any = 10 assert AnyModel().a == 10 assert AnyModel(a='foobar').a == 'foobar' def test_alias(): class SubModel(BaseModel): c = 'barfoo' class Config: fields = {'c': {'alias': '_c'}} class Model(BaseModel): a = 'foobar' b: SubModel = SubModel() class Config: fields = {'a': {'alias': '_a'}} assert Model().a == 'foobar' assert Model().b.c == 'barfoo' assert Model().dict() == {'a': 'foobar', 'b': {'c': 'barfoo'}} assert Model(_a='different').a == 'different' assert Model(b={'_c': 'different'}).b.c == 'different' assert Model(_a='different', b={'_c': 'different'}).dict() == {'a': 'different', 'b': {'c': 'different'}} assert Model(_a='different', b={'_c': 'different'}).dict(by_alias=True) == { '_a': 'different', 'b': {'_c': 'different'}, } def test_population_by_field_name(): class Model(BaseModel): a: str class Config: allow_population_by_field_name = True fields = {'a': {'alias': '_a'}} assert Model(a='different').a == 'different' assert Model(a='different').dict() == {'a': 'different'} assert Model(a='different').dict(by_alias=True) == {'_a': 'different'} def test_field_order(): class Model(BaseModel): c: float b: int = 10 a: str d: dict = {} assert list(Model.__fields__.keys()) == ['c', 'b', 'a', 'd'] def test_required(): # same as below but defined here so class definition occurs inside the test class Model(BaseModel): a: float = Required b: int = 10 m = Model(a=10.2) assert m.dict() == dict(a=10.2, b=10) with pytest.raises(ValidationError) as exc_info: Model() assert exc_info.value.errors() == [{'loc': ('a',), 'msg': 'field required', 'type': 'value_error.missing'}] def test_not_immutability(): class TestModel(BaseModel): a: int = 10 class Config: allow_mutation = True extra = Extra.forbid m = TestModel() assert m.a == 10 m.a = 11 assert m.a == 11 with pytest.raises(ValueError) as exc_info: m.b = 11 assert '"TestModel" object has no field "b"' in exc_info.value.args[0] def test_immutability(): class TestModel(BaseModel): a: int = 10 class Config: allow_mutation = False extra = Extra.forbid m = TestModel() assert m.a == 10 with pytest.raises(TypeError) as exc_info: m.a = 11 assert '"TestModel" is immutable and does not support item assignment' in exc_info.value.args[0] with pytest.raises(ValueError) as exc_info: m.b = 11 assert '"TestModel" object has no field "b"' in exc_info.value.args[0] def test_const_validates(): class Model(BaseModel): a: int = Field(3, const=True) m = Model(a=3) assert m.a == 3 def test_const_uses_default(): class Model(BaseModel): a: int = Field(3, const=True) m = Model() assert m.a == 3 def test_const_with_wrong_value(): class Model(BaseModel): a: int = Field(3, const=True) with pytest.raises(ValidationError) as exc_info: Model(a=4) assert exc_info.value.errors() == [ { 'loc': ('a',), 'msg': 'unexpected value; permitted: 3', 'type': 'value_error.const', 'ctx': {'given': 4, 'permitted': [3]}, } ] def test_const_list(): class SubModel(BaseModel): b: int class Model(BaseModel): a: List[SubModel] = Field([SubModel(b=1), SubModel(b=2), SubModel(b=3)], const=True) b: List[SubModel] = Field([{'b': 4}, {'b': 5}, {'b': 6}], const=True) m = Model() assert m.a == [SubModel(b=1), SubModel(b=2), SubModel(b=3)] assert m.b == [SubModel(b=4), SubModel(b=5), SubModel(b=6)] assert m.schema() == { 'definitions': { 'SubModel': { 'properties': {'b': {'title': 'B', 'type': 'integer'}}, 'required': ['b'], 'title': 'SubModel', 'type': 'object', } }, 'properties': { 'a': { 'const': [SubModel(b=1), SubModel(b=2), SubModel(b=3)], 'items': {'$ref': '#/definitions/SubModel'}, 'title': 'A', 'type': 'array', }, 'b': { 'const': [{'b': 4}, {'b': 5}, {'b': 6}], 'items': {'$ref': '#/definitions/SubModel'}, 'title': 'B', 'type': 'array', }, }, 'title': 'Model', 'type': 'object', } def test_const_list_with_wrong_value(): class SubModel(BaseModel): b: int class Model(BaseModel): a: List[SubModel] = Field([SubModel(b=1), SubModel(b=2), SubModel(b=3)], const=True) b: List[SubModel] = Field([{'b': 4}, {'b': 5}, {'b': 6}], const=True) with pytest.raises(ValidationError) as exc_info: Model(a=[{'b': 3}, {'b': 1}, {'b': 2}], b=[{'b': 6}, {'b': 5}]) assert exc_info.value.errors() == [ { 'ctx': { 'given': [{'b': 3}, {'b': 1}, {'b': 2}], 'permitted': [[SubModel(b=1), SubModel(b=2), SubModel(b=3)]], }, 'loc': ('a',), 'msg': 'unexpected value; permitted: [SubModel(b=1), SubModel(b=2), SubModel(b=3)]', 'type': 'value_error.const', }, { 'ctx': {'given': [{'b': 6}, {'b': 5}], 'permitted': [[{'b': 4}, {'b': 5}, {'b': 6}]]}, 'loc': ('b',), 'msg': "unexpected value; permitted: [{'b': 4}, {'b': 5}, {'b': 6}]", 'type': 'value_error.const', }, ] assert exc_info.value.json().startswith('[') with pytest.raises(ValidationError) as exc_info: Model(a=[SubModel(b=3), SubModel(b=1), SubModel(b=2)], b=[SubModel(b=3), SubModel(b=1)]) assert exc_info.value.errors() == [ { 'ctx': { 'given': [SubModel(b=3), SubModel(b=1), SubModel(b=2)], 'permitted': [[SubModel(b=1), SubModel(b=2), SubModel(b=3)]], }, 'loc': ('a',), 'msg': 'unexpected value; permitted: [SubModel(b=1), SubModel(b=2), SubModel(b=3)]', 'type': 'value_error.const', }, { 'ctx': {'given': [SubModel(b=3), SubModel(b=1)], 'permitted': [[{'b': 4}, {'b': 5}, {'b': 6}]]}, 'loc': ('b',), 'msg': "unexpected value; permitted: [{'b': 4}, {'b': 5}, {'b': 6}]", 'type': 'value_error.const', }, ] assert exc_info.value.json().startswith('[') def test_const_validation_json_serializable(): class SubForm(BaseModel): field: int class Form(BaseModel): field1: SubForm = Field({'field': 2}, const=True) field2: List[SubForm] = Field([{'field': 2}], const=True) with pytest.raises(ValidationError) as exc_info: # Fails Form(field1={'field': 1}, field2=[{'field': 1}]) # This should not raise an Json error exc_info.value.json() class ValidateAssignmentModel(BaseModel): a: int = 2 b: constr(min_length=1) class Config: validate_assignment = True def test_validating_assignment_pass(): p = ValidateAssignmentModel(a=5, b='hello') p.a = 2 assert p.a == 2 assert p.dict() == {'a': 2, 'b': 'hello'} p.b = 'hi' assert p.b == 'hi' assert p.dict() == {'a': 2, 'b': 'hi'} def test_validating_assignment_fail(): p = ValidateAssignmentModel(a=5, b='hello') with pytest.raises(ValidationError) as exc_info: p.a = 'b' assert exc_info.value.errors() == [ {'loc': ('a',), 'msg': 'value is not a valid integer', 'type': 'type_error.integer'} ] with pytest.raises(ValidationError) as exc_info: p.b = '' assert exc_info.value.errors() == [ { 'loc': ('b',), 'msg': 'ensure this value has at least 1 characters', 'type': 'value_error.any_str.min_length', 'ctx': {'limit_value': 1}, } ] def test_enum_values(): FooEnum = Enum('FooEnum', {'foo': 'foo', 'bar': 'bar'}) class Model(BaseModel): foo: FooEnum = None class Config: use_enum_values = True m = Model(foo='foo') # this is the actual value, so has not "values" field assert not isinstance(m.foo, FooEnum) assert m.foo == 'foo' def test_enum_raw(): FooEnum = Enum('FooEnum', {'foo': 'foo', 'bar': 'bar'}) class Model(BaseModel): foo: FooEnum = None m = Model(foo='foo') assert isinstance(m.foo, FooEnum) assert m.foo != 'foo' assert m.foo.value == 'foo' def test_set_tuple_values(): class Model(BaseModel): foo: set bar: tuple m = Model(foo=['a', 'b'], bar=['c', 'd']) assert m.foo == {'a', 'b'} assert m.bar == ('c', 'd') assert m.dict() == {'foo': {'a', 'b'}, 'bar': ('c', 'd')} def test_default_copy(): class User(BaseModel): friends: List[int] = [] u1 = User() u2 = User() assert u1.friends is not u2.friends class ArbitraryType: pass def test_arbitrary_type_allowed_validation_success(): class ArbitraryTypeAllowedModel(BaseModel): t: ArbitraryType class Config: arbitrary_types_allowed = True arbitrary_type_instance = ArbitraryType() m = ArbitraryTypeAllowedModel(t=arbitrary_type_instance) assert m.t == arbitrary_type_instance def test_arbitrary_type_allowed_validation_fails(): class ArbitraryTypeAllowedModel(BaseModel): t: ArbitraryType class Config: arbitrary_types_allowed = True class C: pass with pytest.raises(ValidationError) as exc_info: ArbitraryTypeAllowedModel(t=C()) assert exc_info.value.errors() == [ { 'loc': ('t',), 'msg': 'instance of ArbitraryType expected', 'type': 'type_error.arbitrary_type', 'ctx': {'expected_arbitrary_type': 'ArbitraryType'}, } ] def test_arbitrary_types_not_allowed(): with pytest.raises(RuntimeError) as exc_info: class ArbitraryTypeNotAllowedModel(BaseModel): t: ArbitraryType assert exc_info.value.args[0].startswith('no validator found for') def test_type_type_validation_success(): class ArbitraryClassAllowedModel(BaseModel): t: Type[ArbitraryType] arbitrary_type_class = ArbitraryType m = ArbitraryClassAllowedModel(t=arbitrary_type_class) assert m.t == arbitrary_type_class def test_type_type_subclass_validation_success(): class ArbitraryClassAllowedModel(BaseModel): t: Type[ArbitraryType] class ArbitrarySubType(ArbitraryType): pass arbitrary_type_class = ArbitrarySubType m = ArbitraryClassAllowedModel(t=arbitrary_type_class) assert m.t == arbitrary_type_class def test_type_type_validation_fails_for_instance(): class ArbitraryClassAllowedModel(BaseModel): t: Type[ArbitraryType] class C: pass with pytest.raises(ValidationError) as exc_info: ArbitraryClassAllowedModel(t=C) assert exc_info.value.errors() == [ { 'loc': ('t',), 'msg': 'subclass of ArbitraryType expected', 'type': 'type_error.subclass', 'ctx': {'expected_class': 'ArbitraryType'}, } ] def test_type_type_validation_fails_for_basic_type(): class ArbitraryClassAllowedModel(BaseModel): t: Type[ArbitraryType] with pytest.raises(ValidationError) as exc_info: ArbitraryClassAllowedModel(t=1) assert exc_info.value.errors() == [ { 'loc': ('t',), 'msg': 'subclass of ArbitraryType expected', 'type': 'type_error.subclass', 'ctx': {'expected_class': 'ArbitraryType'}, } ] def test_bare_type_type_validation_success(): class ArbitraryClassAllowedModel(BaseModel): t: Type arbitrary_type_class = ArbitraryType m = ArbitraryClassAllowedModel(t=arbitrary_type_class) assert m.t == arbitrary_type_class def test_bare_type_type_validation_fails(): class ArbitraryClassAllowedModel(BaseModel): t: Type arbitrary_type = ArbitraryType() with pytest.raises(ValidationError) as exc_info: ArbitraryClassAllowedModel(t=arbitrary_type) assert exc_info.value.errors() == [{'loc': ('t',), 'msg': 'a class is expected', 'type': 'type_error.class'}] def test_annotation_field_name_shadows_attribute(): with pytest.raises(NameError): # When defining a model that has an attribute with the name of a built-in attribute, an exception is raised class BadModel(BaseModel): schema: str # This conflicts with the BaseModel's schema() class method def test_value_field_name_shadows_attribute(): # When defining a model that has an attribute with the name of a built-in attribute, an exception is raised with pytest.raises(NameError): class BadModel(BaseModel): schema = 'abc' # This conflicts with the BaseModel's schema() class method def test_class_var(): class MyModel(BaseModel): a: ClassVar b: ClassVar[int] = 1 c: int = 2 assert list(MyModel.__fields__.keys()) == ['c'] def test_fields_set(): class MyModel(BaseModel): a: int b: int = 2 m = MyModel(a=5) assert m.__fields_set__ == {'a'} m.b = 2 assert m.__fields_set__ == {'a', 'b'} m = MyModel(a=5, b=2) assert m.__fields_set__ == {'a', 'b'} def test_exclude_unset_dict(): class MyModel(BaseModel): a: int b: int = 2 m = MyModel(a=5) assert m.dict(exclude_unset=True) == {'a': 5} m = MyModel(a=5, b=3) assert m.dict(exclude_unset=True) == {'a': 5, 'b': 3} def test_exclude_unset_recursive(): class ModelA(BaseModel): a: int b: int = 1 class ModelB(BaseModel): c: int d: int = 2 e: ModelA m = ModelB(c=5, e={'a': 0}) assert m.dict() == {'c': 5, 'd': 2, 'e': {'a': 0, 'b': 1}} assert m.dict(exclude_unset=True) == {'c': 5, 'e': {'a': 0}} assert dict(m) == {'c': 5, 'd': 2, 'e': {'a': 0, 'b': 1}} def test_dict_exclude_unset_populated_by_alias(): class MyModel(BaseModel): a: str = Field('default', alias='alias_a') b: str = Field('default', alias='alias_b') class Config: allow_population_by_field_name = True m = MyModel(alias_a='a') assert m.dict(exclude_unset=True) == {'a': 'a'} assert m.dict(exclude_unset=True, by_alias=True) == {'alias_a': 'a'} def test_dict_exclude_unset_populated_by_alias_with_extra(): class MyModel(BaseModel): a: str = Field('default', alias='alias_a') b: str = Field('default', alias='alias_b') class Config: extra = 'allow' m = MyModel(alias_a='a', c='c') assert m.dict(exclude_unset=True) == {'a': 'a', 'c': 'c'} assert m.dict(exclude_unset=True, by_alias=True) == {'alias_a': 'a', 'c': 'c'} def test_dir_fields(): class MyModel(BaseModel): attribute_a: int attribute_b: int = 2 m = MyModel(attribute_a=5) assert 'dict' in dir(m) assert 'json' in dir(m) assert 'attribute_a' in dir(m) assert 'attribute_b' in dir(m) def test_dict_with_extra_keys(): class MyModel(BaseModel): a: str = Field(None, alias='alias_a') class Config: extra = Extra.allow m = MyModel(extra_key='extra') assert m.dict() == {'a': None, 'extra_key': 'extra'} assert m.dict(by_alias=True) == {'alias_a': None, 'extra_key': 'extra'} def test_root(): class MyModel(BaseModel): __root__: str m = MyModel(__root__='a') assert m.dict() == {'__root__': 'a'} assert m.__root__ == 'a' def test_root_list(): class MyModel(BaseModel): __root__: List[str] m = MyModel(__root__=['a']) assert m.dict() == {'__root__': ['a']} assert m.__root__ == ['a'] def test_root_failed(): with pytest.raises(ValueError, match='__root__ cannot be mixed with other fields'): class MyModel(BaseModel): __root__: str a: str def test_root_undefined_failed(): class MyModel(BaseModel): a: List[str] with pytest.raises(ValidationError) as exc_info: MyModel(__root__=['a']) assert exc_info.value.errors() == [{'loc': ('a',), 'msg': 'field required', 'type': 'value_error.missing'}] def test_parse_root_as_mapping(): class MyModel(BaseModel): __root__: Mapping[str, str] assert MyModel.parse_obj({1: 2}).__root__ == {'1': '2'} with pytest.raises(ValidationError) as exc_info: MyModel.parse_obj({'__root__': {'1': '2'}}) assert exc_info.value.errors() == [ {'loc': ('__root__', '__root__'), 'msg': 'str type expected', 'type': 'type_error.str'} ] def test_parse_obj_non_mapping_root(): class MyModel(BaseModel): __root__: List[str] assert MyModel.parse_obj(['a']).__root__ == ['a'] assert MyModel.parse_obj({'__root__': ['a']}).__root__ == ['a'] with pytest.raises(ValidationError) as exc_info: MyModel.parse_obj({'__not_root__': ['a']}) assert exc_info.value.errors() == [ {'loc': ('__root__',), 'msg': 'value is not a valid list', 'type': 'type_error.list'} ] with pytest.raises(ValidationError): MyModel.parse_obj({'__root__': ['a'], 'other': 1}) assert exc_info.value.errors() == [ {'loc': ('__root__',), 'msg': 'value is not a valid list', 'type': 'type_error.list'} ] def test_untouched_types(): from pydantic import BaseModel class _ClassPropertyDescriptor: def __init__(self, getter): self.getter = getter def __get__(self, instance, owner): return self.getter(owner) classproperty = _ClassPropertyDescriptor class Model(BaseModel): class Config: keep_untouched = (classproperty,) @classproperty def class_name(cls) -> str: return cls.__name__ assert Model.class_name == 'Model' assert Model().class_name == 'Model' def test_custom_types_fail_without_keep_untouched(): from pydantic import BaseModel class _ClassPropertyDescriptor: def __init__(self, getter): self.getter = getter def __get__(self, instance, owner): return self.getter(owner) classproperty = _ClassPropertyDescriptor with pytest.raises(RuntimeError) as e: class Model(BaseModel): @classproperty def class_name(cls) -> str: return cls.__name__ Model.class_name assert str(e.value) == ( "no validator found for ." "_ClassPropertyDescriptor'>, see `arbitrary_types_allowed` in Config" ) class Model(BaseModel): class Config: arbitrary_types_allowed = True @classproperty def class_name(cls) -> str: return cls.__name__ with pytest.raises(AttributeError) as e: Model.class_name assert str(e.value) == "type object 'Model' has no attribute 'class_name'" def test_model_iteration(): class Foo(BaseModel): a: int = 1 b: int = 2 class Bar(BaseModel): c: int d: Foo m = Bar(c=3, d={}) assert m.dict() == {'c': 3, 'd': {'a': 1, 'b': 2}} assert list(m) == [('c', 3), ('d', Foo())] assert dict(m) == {'c': 3, 'd': Foo()} def test_custom_init_subclass_params(): class DerivedModel(BaseModel): def __init_subclass__(cls, something): cls.something = something # if this raises a TypeError, then there is a regression of issue 867: # pydantic.main.MetaModel.__new__ should include **kwargs at the end of the # method definition and pass them on to the super call at the end in order # to allow the special method __init_subclass__ to be defined with custom # parameters on extended BaseModel classes. class NewModel(DerivedModel, something=2): something = 1 assert NewModel.something == 2 def test_update_forward_refs_does_not_modify_module_dict(): class MyModel(BaseModel): field: Optional['MyModel'] MyModel.update_forward_refs() assert 'MyModel' not in sys.modules[MyModel.__module__].__dict__ def test_two_defaults(): with pytest.raises(ValueError, match='^cannot specify both default and default_factory$'): class Model(BaseModel): a: int = Field(default=3, default_factory=lambda: 3) def test_default_factory(): class ValueModel(BaseModel): uid: UUID = uuid4() m1 = ValueModel() m2 = ValueModel() assert m1.uid == m2.uid class DynamicValueModel(BaseModel): uid: UUID = Field(default_factory=uuid4) m1 = DynamicValueModel() m2 = DynamicValueModel() assert isinstance(m1.uid, UUID) assert m1.uid != m2.uid # With a callable: we still should be able to set callables as defaults class FunctionModel(BaseModel): a: int = 1 uid: Callable[[], UUID] = Field(uuid4) m = FunctionModel() assert m.uid is uuid4