Skip to content

2.12: datetime, date, time, and timedelta silently and incorrectly serializes types incorrectly when dumping in Python mode when used as part of a union type #12439

@astei

Description

@astei

Initial Checks

  • I confirm that I'm using Pydantic V2

Description

pydantic/pydantic-core@37ec6e7 introduced a regression in which objects on fields annotated as datetime, date, and time will be silently handled as if the object was of the incorrect type (falling through to infer_to_python unconditionally). This is most evident when using a union type, where I would expect that if serializing as a datetime fails, Pydantic would try the next available type. The logic seems to have been borrowed from the existing timedelta serialization which was also buggy in a similar way (verified on 2.11.7).

The actual issue seems to be in pydantic-core. The TypeSerializer used to downcast to ensure that the values are the correct type, but it does not do so now, only doing so whenever mode='json' is passed in. Indeed, the code works correctly if I pass mode="json" to BaseModel.model_dump().

This particular bug is insidious because it happens that most of the time, infer_to_python actually does the right thing. But with a specific user-defined type, one can see that we are silently doing the wrong thing.

I have a PR to fix this within pydantic-core.

Example Code

from datetime import date, datetime, time, timedelta

from pydantic import BaseModel
from pydantic_core import core_schema


class SampleType:
    @classmethod
    def __get_pydantic_core_schema__(cls, source, handler) -> core_schema.CoreSchema:
        serialization_schema = core_schema.plain_serializer_function_ser_schema(lambda v: None)
        json_validation_schema = core_schema.no_info_plain_validator_function(
            function=lambda v: v, serialization=serialization_schema
        )
        python_validation_schema = core_schema.any_schema()

        return core_schema.json_or_python_schema(
            json_schema=json_validation_schema,
            python_schema=python_validation_schema,
            serialization=serialization_schema,
        )


class SampleModel(BaseModel):
    d: datetime | SampleType
    dt: datetime | SampleType
    t: time | SampleType
    td: timedelta | SampleType


print(SampleModel(d='z', dt='z', t='z', td='z').model_dump())

# Expected: {'d': None, 'dt': None, 't': None, 'td': None} (from SampleType)
# Actual: {'d': 'z', 'dt': 'z', 't': 'z', 'td': 'z'} (from pydantic-core temporal serializers)

Python, Pydantic & OS Version

pydantic version: 2.13.0a0+dev
        pydantic-core version: 2.41.4
          pydantic-core build: profile=release pgo=false
               python version: 3.11.4 (main, Jul 22 2023, 23:52:07) [Clang 14.0.0 (clang-1400.0.29.202)]
                     platform: macOS-15.7.1-arm64-arm-64bit
             related packages: typing_extensions-4.14.1
                       commit: c8035874e

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug V2Bug related to Pydantic V2

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions