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
|
import pickle
from typing import Union
import pytest
from pydantic_core import MISSING, PydanticSerializationUnexpectedValue
from pydantic import BaseModel, TypeAdapter, ValidationError
def test_missing_sentinel_model() -> None:
class Model(BaseModel):
f: Union[int, MISSING] = MISSING
g: MISSING = MISSING
m1 = Model()
assert m1.model_dump() == {}
assert m1.model_dump_json() == '{}'
m2 = Model.model_validate({'f': MISSING, 'g': MISSING})
assert m2.f is MISSING
assert m2.g is MISSING
m3 = Model(f=1)
assert m3.model_dump() == {'f': 1}
assert m3.model_dump_json() == '{"f":1}'
def test_missing_sentinel_type_adapter() -> None:
"""Note that this usage isn't explicitly supported (and useless in practice)."""
# TODO Remove annotation with PEP 747:
ta: TypeAdapter[object] = TypeAdapter(MISSING)
assert ta.validate_python(MISSING) is MISSING
with pytest.raises(ValidationError) as exc_info:
ta.validate_python(1)
assert exc_info.value.errors()[0]['type'] == 'missing_sentinel_error'
assert ta.dump_python(MISSING) is MISSING
with pytest.raises(PydanticSerializationUnexpectedValue):
ta.dump_python(1)
# Defined in module to be picklable:
class ModelPickle(BaseModel):
f: Union[int, MISSING] = MISSING
@pytest.mark.xfail(reason="PEP 661 sentinels aren't picklable yet in the experimental typing-extensions implementation")
def test_missing_sentinel_pickle() -> None:
m = ModelPickle()
m_reconstructed = pickle.loads(pickle.dumps(m))
assert m_reconstructed.f is MISSING
def test_missing_sentinel_json_schema() -> None:
class Model(BaseModel):
f: Union[int, MISSING] = MISSING
g: MISSING = MISSING
h: MISSING
assert Model.model_json_schema()['properties'] == {
'f': {'title': 'F', 'type': 'integer'},
}
def test_model_construct_with_missing_default_does_not_crash() -> None:
class M(BaseModel):
a: Union[int, MISSING] = MISSING
# Should not raise
m = M.model_construct()
assert hasattr(m, 'a')
# Keep sentinel by identity
assert getattr(m, 'a') is MISSING
|