diff --git a/changes/880-koxudaxi.md b/changes/880-koxudaxi.md new file mode 100644 index 0000000000..0ee8dd6f9b --- /dev/null +++ b/changes/880-koxudaxi.md @@ -0,0 +1 @@ +Fix field of a type that has a default value. \ No newline at end of file diff --git a/pydantic/main.py b/pydantic/main.py index 5221650d3d..176c7007c4 100644 --- a/pydantic/main.py +++ b/pydantic/main.py @@ -18,7 +18,7 @@ from .schema import model_schema from .types import PyObject, StrBytes from .typing import AnyCallable, AnyType, ForwardRef, is_classvar, resolve_annotations, update_field_forward_refs -from .utils import GetterDict, ValueItems, truncate, validate_field_name +from .utils import GetterDict, ValueItems, lenient_issubclass, truncate, validate_field_name if TYPE_CHECKING: from .class_validators import ValidatorListDict @@ -175,7 +175,11 @@ def __new__(mcs, name, bases, namespace, **kwargs): elif is_valid_field(ann_name): validate_field_name(bases, ann_name) value = namespace.get(ann_name, ...) - if isinstance(value, untouched_types) and ann_type != PyObject: + if ( + isinstance(value, untouched_types) + and ann_type != PyObject + and not lenient_issubclass(getattr(ann_type, '__origin__', None), Type) + ): continue fields[ann_name] = ModelField.infer( name=ann_name, diff --git a/tests/test_edge_cases.py b/tests/test_edge_cases.py index ed32b08dc6..6c97c9a8b8 100644 --- a/tests/test_edge_cases.py +++ b/tests/test_edge_cases.py @@ -1,7 +1,7 @@ import re from decimal import Decimal from enum import Enum -from typing import Any, Dict, List, Optional, Set, Tuple, Union +from typing import Any, Dict, List, Optional, Set, Tuple, Type, Union import pytest @@ -957,15 +957,50 @@ def __init__(self, **data) -> None: Foobar(x=1) -def test_ignored_type(): - def foobar(): +def test_type_on_annotation(): + class FooBar: pass class Model(BaseModel): - a: int = foobar - b: int + a: int = int + b: Type[int] + c: Type[int] = int + d: FooBar = FooBar + e: Type[FooBar] + f: Type[FooBar] = FooBar + + assert Model.__fields__.keys() == {'b', 'c', 'e', 'f'} + + +def test_assign_type(): + class Parent: + def echo(self): + return 'parent' - assert Model.__fields__.keys() == {'b'} + class Child(Parent): + def echo(self): + return 'child' + + class Different: + def echo(self): + return 'different' + + class Model(BaseModel): + v: Type[Parent] = Parent + + assert Model(v=Parent).v().echo() == 'parent' + assert Model().v().echo() == 'parent' + assert Model(v=Child).v().echo() == 'child' + with pytest.raises(ValidationError) as exc_info: + Model(v=Different) + assert exc_info.value.errors() == [ + { + 'loc': ('v',), + 'msg': 'subclass of Parent expected', + 'type': 'type_error.subclass', + 'ctx': {'expected_class': 'Parent'}, + } + ] def test_optional_subfields():