diff --git a/python/pydantic_core/core_schema.py b/python/pydantic_core/core_schema.py index c4a26041a..c7ca7cfd8 100644 --- a/python/pydantic_core/core_schema.py +++ b/python/pydantic_core/core_schema.py @@ -2882,6 +2882,7 @@ class TypedDictSchema(TypedDict, total=False): type: Required[Literal['typed-dict']] fields: Required[dict[str, TypedDictField]] cls: type[Any] + cls_name: str computed_fields: list[ComputedField] strict: bool extras_schema: CoreSchema @@ -2898,6 +2899,7 @@ def typed_dict_schema( fields: dict[str, TypedDictField], *, cls: type[Any] | None = None, + cls_name: str | None = None, computed_fields: list[ComputedField] | None = None, strict: bool | None = None, extras_schema: CoreSchema | None = None, @@ -2929,6 +2931,8 @@ class MyTypedDict(TypedDict): Args: fields: The fields to use for the typed dict cls: The class to use for the typed dict + cls_name: The name to use in error locations. Falls back to `cls.__name__`, or the validator name if no class + is provided. computed_fields: Computed fields to use when serializing the model, only applies when directly inside a model strict: Whether the typed dict is strict extras_schema: The extra validator to use for the typed dict @@ -2942,6 +2946,7 @@ class MyTypedDict(TypedDict): type='typed-dict', fields=fields, cls=cls, + cls_name=cls_name, computed_fields=computed_fields, strict=strict, extras_schema=extras_schema, diff --git a/src/validators/typed_dict.rs b/src/validators/typed_dict.rs index 999926762..013db4d69 100644 --- a/src/validators/typed_dict.rs +++ b/src/validators/typed_dict.rs @@ -1,6 +1,6 @@ use pyo3::intern; use pyo3::prelude::*; -use pyo3::types::{PyDict, PyString}; +use pyo3::types::{PyDict, PyString, PyType}; use crate::build_tools::py_schema_err; use crate::build_tools::{is_strict, schema_or_config, ExtraBehavior}; @@ -37,6 +37,7 @@ pub struct TypedDictValidator { loc_by_alias: bool, validate_by_alias: Option, validate_by_name: Option, + cls_name: Option, } impl BuildValidator for TypedDictValidator { @@ -69,6 +70,14 @@ impl BuildValidator for TypedDictValidator { let fields_dict: Bound<'_, PyDict> = schema.get_as_req(intern!(py, "fields"))?; let mut fields: Vec = Vec::with_capacity(fields_dict.len()); + let cls_name: Option = match schema.get_as_req::(intern!(py, "cls_name")) { + Ok(name) => Some(name), + Err(_) => match schema.get_as_req::>(intern!(py, "cls")) { + Ok(class) => Some(class.getattr(intern!(py, "__name__"))?.extract()?), + Err(_) => None, + }, + }; + for (key, value) in fields_dict { let field_info = value.downcast::()?; let field_name_py = key.downcast_into::()?; @@ -128,6 +137,7 @@ impl BuildValidator for TypedDictValidator { loc_by_alias: config.get_as(intern!(py, "loc_by_alias"))?.unwrap_or(true), validate_by_alias: config.get_as(intern!(py, "validate_by_alias"))?, validate_by_name: config.get_as(intern!(py, "validate_by_name"))?, + cls_name, } .into()) } @@ -367,6 +377,6 @@ impl Validator for TypedDictValidator { } fn get_name(&self) -> &str { - Self::EXPECTED_TYPE + self.cls_name.as_deref().unwrap_or(Self::EXPECTED_TYPE) } }