Skip to content

Commit 08969cd

Browse files
committed
Add support for TypedDict for **kwargs
1 parent ba8eab4 commit 08969cd

File tree

3 files changed

+121
-1
lines changed

3 files changed

+121
-1
lines changed

python/pydantic_core/core_schema.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from collections.abc import Mapping
1111
from datetime import date, datetime, time, timedelta
1212
from decimal import Decimal
13-
from typing import TYPE_CHECKING, Any, Callable, Dict, Hashable, List, Pattern, Set, Tuple, Type, Union
13+
from typing import TYPE_CHECKING, Any, Callable, Dict, Hashable, List, Pattern, Set, Tuple, Type, Union, overload
1414

1515
from typing_extensions import deprecated
1616

@@ -3372,6 +3372,48 @@ def arguments_parameter(
33723372
return _dict_not_none(name=name, schema=schema, mode=mode, alias=alias)
33733373

33743374

3375+
class VarKwargsSchema(TypedDict):
3376+
type: Literal['var_kwargs']
3377+
mode: Literal['single', 'typed_dict']
3378+
schema: CoreSchema
3379+
3380+
3381+
@overload
3382+
def var_kwargs_schema(
3383+
*,
3384+
mode: Literal['single'],
3385+
schema: CoreSchema,
3386+
) -> VarKwargsSchema: ...
3387+
3388+
3389+
@overload
3390+
def var_kwargs_schema(
3391+
*,
3392+
mode: Literal['typed_dict'],
3393+
schema: TypedDictSchema,
3394+
) -> VarKwargsSchema: ...
3395+
3396+
3397+
def var_kwargs_schema(
3398+
*,
3399+
mode: Literal['single', 'typed_dict'],
3400+
schema: CoreSchema,
3401+
) -> VarKwargsSchema:
3402+
"""Returns a schema describing the variadic keyword arguments of a callable.
3403+
3404+
Args:
3405+
mode: The validation mode to use. If `'single'`, every value of the keyword arguments will
3406+
be validated against the core schema from the `schema` argument. If `'typed_dict'`, the
3407+
`schema` argument must be a [`typed_dict_schema`][pydantic_core.core_schema.typed_dict_schema].
3408+
"""
3409+
3410+
return _dict_not_none(
3411+
type='var_kwargs',
3412+
mode=mode,
3413+
schema=schema,
3414+
)
3415+
3416+
33753417
class ArgumentsSchema(TypedDict, total=False):
33763418
type: Required[Literal['arguments']]
33773419
arguments_schema: Required[List[ArgumentsParameter]]

src/validators/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ mod union;
6161
mod url;
6262
mod uuid;
6363
mod validation_state;
64+
mod var_kwargs;
6465
mod with_default;
6566

6667
pub use self::validation_state::{Exactness, ValidationState};

src/validators/var_kwargs.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
use std::str::FromStr;
2+
3+
use pyo3::intern;
4+
use pyo3::prelude::*;
5+
use pyo3::types::{PyDict, PyString};
6+
7+
use crate::input::Input;
8+
use crate::tools::SchemaDict;
9+
10+
use super::validation_state::ValidationState;
11+
use super::{build_validator, BuildValidator, CombinedValidator, DefinitionsBuilder, Validator};
12+
13+
enum VarKwargsMode {
14+
Single,
15+
TypedDict,
16+
}
17+
18+
impl FromStr for VarKwargsMode {
19+
type Err = ();
20+
21+
fn from_str(s: &str) -> Result<Self, Self::Err> {
22+
match s {
23+
"single" => Ok(VarKwargsMode::Single),
24+
"typed_dict" => Ok(VarKwargsMode::TypedDict),
25+
_ => Err(()),
26+
}
27+
}
28+
}
29+
30+
#[derive(Debug)]
31+
pub struct VarKwargsValidator {
32+
mode: VarKwargsMode,
33+
validator: Box<CombinedValidator>,
34+
}
35+
36+
impl BuildValidator for VarKwargsValidator {
37+
const EXPECTED_TYPE: &'static str = "var_kwargs";
38+
39+
fn build(
40+
schema: &Bound<'_, PyDict>,
41+
config: Option<&Bound<'_, PyDict>>,
42+
definitions: &mut DefinitionsBuilder<CombinedValidator>,
43+
) -> PyResult<CombinedValidator> {
44+
let py = schema.py();
45+
46+
let py_mode: Bound<PyString> = schema.get_as_req(intern!(py, "mode"))?;
47+
let mode = VarKwargsMode::from_str(py_mode.to_string().as_str())?;
48+
49+
let validator_schema: Bound<PyDict> = schema.get_as_req(intern!(py, "schema"))?;
50+
51+
Ok(Self {
52+
mode,
53+
validator: Box::new(build_validator(&validator_schema, config, definitions)?),
54+
}
55+
.into())
56+
}
57+
}
58+
59+
impl_py_gc_traverse!(VarKwargsValidator { mode, validator });
60+
61+
impl Validator for VarKwargsValidator {
62+
fn validate<'py>(
63+
&self,
64+
py: Python<'py>,
65+
input: &(impl Input<'py> + ?Sized),
66+
state: &mut ValidationState<'_, 'py>,
67+
) -> ValResult<PyObject> {
68+
match self.mode {
69+
VarKwargsMode::Single => {
70+
// TODO iter over kwargs
71+
}
72+
VarKwargsMode::TypedDict => {
73+
// TODO
74+
}
75+
}
76+
}
77+
}

0 commit comments

Comments
 (0)