Skip to content

Commit 5969925

Browse files
committed
Add support for TypedDict for **kwargs
1 parent bc0c97a commit 5969925

File tree

3 files changed

+126
-1
lines changed

3 files changed

+126
-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: 3 additions & 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};
@@ -561,6 +562,7 @@ pub fn build_validator(
561562
callable::CallableValidator,
562563
// arguments
563564
arguments::ArgumentsValidator,
565+
var_kwargs::VarKwargsValidator,
564566
// default value
565567
with_default::WithDefaultValidator,
566568
// chain validators
@@ -716,6 +718,7 @@ pub enum CombinedValidator {
716718
Callable(callable::CallableValidator),
717719
// arguments
718720
Arguments(arguments::ArgumentsValidator),
721+
VarKwargs(var_kwargs::VarKwargsValidator),
719722
// default value
720723
WithDefault(with_default::WithDefaultValidator),
721724
// chain validators

src/validators/var_kwargs.rs

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

0 commit comments

Comments
 (0)