From 6ccea2b290ba54a0c7e70df3fc748180064a3b4e Mon Sep 17 00:00:00 2001 From: Charles Duffy Date: Wed, 28 Aug 2024 12:40:29 -0500 Subject: [PATCH] Treat Pydantic dataclasses as objects that are not dataclasses at all (#728) --- msgspec/_core.c | 9 +++++++-- msgspec/_utils.py | 3 +++ msgspec/inspect.py | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/msgspec/_core.c b/msgspec/_core.c index 0f287814..73431eac 100644 --- a/msgspec/_core.c +++ b/msgspec/_core.c @@ -410,6 +410,7 @@ typedef struct { PyObject *str__field_defaults; PyObject *str___post_init__; PyObject *str___dataclass_fields__; + PyObject *str___pydantic_fields__; PyObject *str___attrs_attrs__; PyObject *str___supertype__; #if PY312_PLUS @@ -2162,7 +2163,8 @@ PyDoc_STRVAR(unset__doc__, "in an object needs to be treated differently than one containing an explicit\n" "``None`` value. In this case, you may use ``UNSET`` as the default value,\n" "rather than ``None`` when defining object schemas. This feature is supported\n" -"for any `msgspec.Struct`, `dataclasses` or `attrs` types.\n" +"for any `msgspec.Struct`, `dataclasses` or `attrs` types (excluding Pydantic\n" +"dataclasses).\n" "\n" "Examples\n" "--------\n" @@ -4767,7 +4769,9 @@ is_dataclass_or_attrs_class(TypeNodeCollectState *state, PyObject *t) { PyType_Check(t) && ( PyObject_HasAttr(t, state->mod->str___dataclass_fields__) || PyObject_HasAttr(t, state->mod->str___attrs_attrs__) - ) + ) && ! ( + PyObject_HasAttr(t, state->mod->str___pydantic_fields__) + ) ); } @@ -22231,6 +22235,7 @@ PyInit__core(void) CACHED_STRING(str__field_defaults, "_field_defaults"); CACHED_STRING(str___post_init__, "__post_init__"); CACHED_STRING(str___dataclass_fields__, "__dataclass_fields__"); + CACHED_STRING(str___pydantic_fields__, "__pydantic_fields__"); CACHED_STRING(str___attrs_attrs__, "__attrs_attrs__"); CACHED_STRING(str___supertype__, "__supertype__"); #if PY312_PLUS diff --git a/msgspec/_utils.py b/msgspec/_utils.py index ddf6f27c..7b910bd3 100644 --- a/msgspec/_utils.py +++ b/msgspec/_utils.py @@ -213,6 +213,9 @@ def get_dataclass_info(obj): if hasattr(cls, "__dataclass_fields__"): from dataclasses import _FIELD, _FIELD_INITVAR, MISSING + if hasattr(cls, "__pydantic_fields__"): + raise TypeError("Pydantic dataclasses are not supported") + for field in cls.__dataclass_fields__.values(): if field._field_type is not _FIELD: if field._field_type is _FIELD_INITVAR: diff --git a/msgspec/inspect.py b/msgspec/inspect.py index 2f5a804c..d31065b9 100644 --- a/msgspec/inspect.py +++ b/msgspec/inspect.py @@ -683,7 +683,7 @@ def _is_enum(t): def _is_dataclass(t): - return hasattr(t, "__dataclass_fields__") + return hasattr(t, "__dataclass_fields__") and not hasattr(t, "__pydantic_fields__") def _is_attrs(t):