Skip to content

Commit 0761adb

Browse files
authored
Add cls_name to typed-dict schema (#1659)
This aligns with other schemas, like the dataclass one where the class name is used as the validator name
1 parent 72350a1 commit 0761adb

File tree

2 files changed

+17
-2
lines changed

2 files changed

+17
-2
lines changed

python/pydantic_core/core_schema.py

+5
Original file line numberDiff line numberDiff line change
@@ -2882,6 +2882,7 @@ class TypedDictSchema(TypedDict, total=False):
28822882
type: Required[Literal['typed-dict']]
28832883
fields: Required[dict[str, TypedDictField]]
28842884
cls: type[Any]
2885+
cls_name: str
28852886
computed_fields: list[ComputedField]
28862887
strict: bool
28872888
extras_schema: CoreSchema
@@ -2898,6 +2899,7 @@ def typed_dict_schema(
28982899
fields: dict[str, TypedDictField],
28992900
*,
29002901
cls: type[Any] | None = None,
2902+
cls_name: str | None = None,
29012903
computed_fields: list[ComputedField] | None = None,
29022904
strict: bool | None = None,
29032905
extras_schema: CoreSchema | None = None,
@@ -2929,6 +2931,8 @@ class MyTypedDict(TypedDict):
29292931
Args:
29302932
fields: The fields to use for the typed dict
29312933
cls: The class to use for the typed dict
2934+
cls_name: The name to use in error locations. Falls back to `cls.__name__`, or the validator name if no class
2935+
is provided.
29322936
computed_fields: Computed fields to use when serializing the model, only applies when directly inside a model
29332937
strict: Whether the typed dict is strict
29342938
extras_schema: The extra validator to use for the typed dict
@@ -2942,6 +2946,7 @@ class MyTypedDict(TypedDict):
29422946
type='typed-dict',
29432947
fields=fields,
29442948
cls=cls,
2949+
cls_name=cls_name,
29452950
computed_fields=computed_fields,
29462951
strict=strict,
29472952
extras_schema=extras_schema,

src/validators/typed_dict.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use pyo3::intern;
22
use pyo3::prelude::*;
3-
use pyo3::types::{PyDict, PyString};
3+
use pyo3::types::{PyDict, PyString, PyType};
44

55
use crate::build_tools::py_schema_err;
66
use crate::build_tools::{is_strict, schema_or_config, ExtraBehavior};
@@ -37,6 +37,7 @@ pub struct TypedDictValidator {
3737
loc_by_alias: bool,
3838
validate_by_alias: Option<bool>,
3939
validate_by_name: Option<bool>,
40+
cls_name: Option<String>,
4041
}
4142

4243
impl BuildValidator for TypedDictValidator {
@@ -69,6 +70,14 @@ impl BuildValidator for TypedDictValidator {
6970
let fields_dict: Bound<'_, PyDict> = schema.get_as_req(intern!(py, "fields"))?;
7071
let mut fields: Vec<TypedDictField> = Vec::with_capacity(fields_dict.len());
7172

73+
let cls_name: Option<String> = match schema.get_as_req::<String>(intern!(py, "cls_name")) {
74+
Ok(name) => Some(name),
75+
Err(_) => match schema.get_as_req::<Bound<'_, PyType>>(intern!(py, "cls")) {
76+
Ok(class) => Some(class.getattr(intern!(py, "__name__"))?.extract()?),
77+
Err(_) => None,
78+
},
79+
};
80+
7281
for (key, value) in fields_dict {
7382
let field_info = value.downcast::<PyDict>()?;
7483
let field_name_py = key.downcast_into::<PyString>()?;
@@ -128,6 +137,7 @@ impl BuildValidator for TypedDictValidator {
128137
loc_by_alias: config.get_as(intern!(py, "loc_by_alias"))?.unwrap_or(true),
129138
validate_by_alias: config.get_as(intern!(py, "validate_by_alias"))?,
130139
validate_by_name: config.get_as(intern!(py, "validate_by_name"))?,
140+
cls_name,
131141
}
132142
.into())
133143
}
@@ -367,6 +377,6 @@ impl Validator for TypedDictValidator {
367377
}
368378

369379
fn get_name(&self) -> &str {
370-
Self::EXPECTED_TYPE
380+
self.cls_name.as_deref().unwrap_or(Self::EXPECTED_TYPE)
371381
}
372382
}

0 commit comments

Comments
 (0)