1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
|
"""Tests related to deferred evaluation of annotations introduced in Python 3.14 by PEP 649 and 749."""
import sys
from dataclasses import field
from typing import Annotated
import pytest
from annotated_types import MaxLen
from pydantic import (
BaseModel,
Field,
ValidationError,
field_serializer,
field_validator,
model_serializer,
model_validator,
)
from pydantic.dataclasses import dataclass
pytestmark = pytest.mark.skipif(
sys.version_info < (3, 14), reason='Requires deferred evaluation of annotations introduced in Python 3.14'
)
def test_deferred_annotations_model() -> None:
class Model(BaseModel):
a: Int
b: Str = 'a'
Int = int
Str = str
inst = Model(a='1', b=b'test')
assert inst.a == 1
assert inst.b == 'test'
def test_deferred_annotations_nested_model() -> None:
def outer():
def inner():
class Model(BaseModel):
ann: Annotated[List[Dict[str, str]], MaxLen(1)]
Dict = dict
return Model
List = list
Model = inner()
return Model
Model = outer()
with pytest.raises(ValidationError) as exc_info:
Model(ann=[{'a': 'b'}, {'c': 'd'}])
assert exc_info.value.errors()[0]['type'] == 'too_long'
def test_deferred_annotations_pydantic_dataclass() -> None:
@dataclass
class A:
a: Int = field(default=1)
Int = int
assert A(a='1').a == 1
def test_deferred_annotations_pydantic_dataclass_pydantic_field() -> None:
"""When initial support for Python 3.14 was added, this failed as support for the Pydantic
`Field()` function was implemented by writing directly to `__annotations__`.
"""
@dataclass
class A:
a: Int = Field(default=1)
Int = int
assert A(a='1').a == 1
def test_deferred_annotations_return_values() -> None:
class Model(BaseModel):
a: int
@model_validator(mode='after')
def check(self) -> Model:
return self
@model_validator(mode='before')
def before(cls, data) -> MyDict:
return data
@model_serializer(mode='plain')
def ser(self) -> MyDict:
return {'a': self.a}
@field_validator('a', mode='before')
def validate_a(cls, v) -> MyInt:
return v
@field_serializer('a', mode='plain')
def serialize_a(self, v) -> MyInt:
return v
MyDict = dict
MyInt = int
|