Skip to content

Commit 84a574c

Browse files
committed
Add new arguments schema
1 parent 9975030 commit 84a574c

File tree

7 files changed

+732
-0
lines changed

7 files changed

+732
-0
lines changed

python/pydantic_core/core_schema.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3489,6 +3489,112 @@ def arguments_schema(
34893489
)
34903490

34913491

3492+
class ArgumentsV3Parameter(TypedDict, total=False):
3493+
name: Required[str]
3494+
schema: Required[CoreSchema]
3495+
mode: Literal[
3496+
'positional_only',
3497+
'positional_or_keyword',
3498+
'keyword_only',
3499+
'var_args',
3500+
'var_kwargs_uniform',
3501+
'var_kwargs_unpacked_typed_dict',
3502+
] # default positional_or_keyword
3503+
alias: Union[str, list[Union[str, int]], list[list[Union[str, int]]]]
3504+
3505+
3506+
def arguments_v3_parameter(
3507+
name: str,
3508+
schema: CoreSchema,
3509+
*,
3510+
mode: Literal[
3511+
'positional_only',
3512+
'positional_or_keyword',
3513+
'keyword_only',
3514+
'var_args',
3515+
'var_kwargs_uniform',
3516+
'var_kwargs_unpacked_typed_dict',
3517+
]
3518+
| None = None,
3519+
alias: str | list[str | int] | list[list[str | int]] | None = None,
3520+
) -> ArgumentsV3Parameter:
3521+
"""
3522+
Returns a schema that matches an argument parameter, e.g.:
3523+
3524+
```py
3525+
from pydantic_core import SchemaValidator, core_schema
3526+
3527+
param = core_schema.arguments_v3_parameter(
3528+
name='a', schema=core_schema.str_schema(), mode='positional_only'
3529+
)
3530+
schema = core_schema.arguments_v3_schema([param])
3531+
v = SchemaValidator(schema)
3532+
assert v.validate_python({'a': 'hello'}) == (('hello',), {})
3533+
```
3534+
3535+
Args:
3536+
name: The name to use for the argument parameter
3537+
schema: The schema to use for the argument parameter
3538+
mode: The mode to use for the argument parameter
3539+
alias: The alias to use for the argument parameter
3540+
"""
3541+
return _dict_not_none(name=name, schema=schema, mode=mode, alias=alias)
3542+
3543+
3544+
class ArgumentsV3Schema(TypedDict, total=False):
3545+
type: Required[Literal['arguments-v3']]
3546+
arguments_schema: Required[list[ArgumentsV3Parameter]]
3547+
populate_by_name: bool
3548+
var_args_schema: CoreSchema
3549+
var_kwargs_mode: VarKwargsMode
3550+
var_kwargs_schema: CoreSchema
3551+
ref: str
3552+
metadata: dict[str, Any]
3553+
serialization: SerSchema
3554+
3555+
3556+
def arguments_v3_schema(
3557+
arguments: list[ArgumentsV3Parameter],
3558+
*,
3559+
populate_by_name: bool | None = None,
3560+
ref: str | None = None,
3561+
metadata: dict[str, Any] | None = None,
3562+
serialization: SerSchema | None = None,
3563+
) -> ArgumentsV3Schema:
3564+
"""
3565+
Returns a schema that matches an arguments schema, e.g.:
3566+
3567+
```py
3568+
from pydantic_core import SchemaValidator, core_schema
3569+
3570+
param_a = core_schema.arguments_v3_parameter(
3571+
name='a', schema=core_schema.str_schema(), mode='positional_only'
3572+
)
3573+
param_b = core_schema.arguments_v3_parameter(
3574+
name='kwargs', schema=core_schema.bool_schema(), mode='var_kwargs_uniform'
3575+
)
3576+
schema = core_schema.arguments_v3_schema([param_a, param_b])
3577+
v = SchemaValidator(schema)
3578+
assert v.validate_python({'a': 'hello', 'kwargs': {'extra': True}}) == (('hello',), {'extra': True})
3579+
```
3580+
3581+
Args:
3582+
arguments: The arguments to use for the arguments schema
3583+
populate_by_name: Whether to populate by name
3584+
ref: optional unique identifier of the schema, used to reference the schema in other places
3585+
metadata: Any other information you want to include with the schema, not used by pydantic-core
3586+
serialization: Custom serialization schema
3587+
"""
3588+
return _dict_not_none(
3589+
type='arguments-v2',
3590+
arguments_schema=arguments,
3591+
populate_by_name=populate_by_name,
3592+
ref=ref,
3593+
metadata=metadata,
3594+
serialization=serialization,
3595+
)
3596+
3597+
34923598
class CallSchema(TypedDict, total=False):
34933599
type: Required[Literal['call']]
34943600
arguments_schema: Required[CoreSchema]

src/input/input_abstract.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ pub trait Input<'py>: fmt::Debug {
8181

8282
fn validate_args(&self) -> ValResult<Self::Arguments<'_>>;
8383

84+
fn validate_args_v3(&self) -> ValResult<Self::Arguments<'_>>;
85+
8486
fn validate_dataclass_args<'a>(&'a self, dataclass_name: &str) -> ValResult<Self::Arguments<'a>>;
8587

8688
fn validate_str(&self, strict: bool, coerce_numbers_to_str: bool) -> ValMatch<EitherString<'_>>;

src/input/input_json.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ impl<'py, 'data> Input<'py> for JsonValue<'data> {
8484
}
8585
}
8686

87+
#[cfg_attr(has_coverage_attribute, coverage(off))]
88+
fn validate_args_v3(&self) -> ValResult<Self::Arguments<'_>> {
89+
Err(ValError::new(ErrorTypeDefaults::ArgumentsType, self))
90+
}
91+
8792
fn validate_dataclass_args<'a>(&'a self, class_name: &str) -> ValResult<JsonArgs<'a, 'data>> {
8893
match self {
8994
JsonValue::Object(object) => Ok(JsonArgs::new(None, Some(object))),
@@ -375,6 +380,11 @@ impl<'py> Input<'py> for str {
375380
Err(ValError::new(ErrorTypeDefaults::ArgumentsType, self))
376381
}
377382

383+
#[cfg_attr(has_coverage_attribute, coverage(off))]
384+
fn validate_args_v3(&self) -> ValResult<Never> {
385+
Err(ValError::new(ErrorTypeDefaults::ArgumentsType, self))
386+
}
387+
378388
#[cfg_attr(has_coverage_attribute, coverage(off))]
379389
fn validate_dataclass_args(&self, class_name: &str) -> ValResult<Never> {
380390
let class_name = class_name.to_string();

src/input/input_python.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,16 @@ impl<'py> Input<'py> for Bound<'py, PyAny> {
117117
}
118118
}
119119

120+
fn validate_args_v3(&self) -> ValResult<PyArgs<'py>> {
121+
if let Ok(args_kwargs) = self.extract::<ArgsKwargs>() {
122+
let args = args_kwargs.args.into_bound(self.py());
123+
let kwargs = args_kwargs.kwargs.map(|d| d.into_bound(self.py()));
124+
Ok(PyArgs::new(Some(args), kwargs))
125+
} else {
126+
Err(ValError::new(ErrorTypeDefaults::ArgumentsType, self))
127+
}
128+
}
129+
120130
fn validate_dataclass_args<'a>(&'a self, class_name: &str) -> ValResult<PyArgs<'py>> {
121131
if let Ok(dict) = self.downcast::<PyDict>() {
122132
Ok(PyArgs::new(None, Some(dict.clone())))

src/input/input_string.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ impl<'py> Input<'py> for StringMapping<'py> {
8989
Err(ValError::new(ErrorTypeDefaults::ArgumentsType, self))
9090
}
9191

92+
fn validate_args_v3(&self) -> ValResult<Self::Arguments<'_>> {
93+
// do we want to support this?
94+
Err(ValError::new(ErrorTypeDefaults::ArgumentsType, self))
95+
}
96+
9297
fn validate_dataclass_args<'a>(&'a self, _dataclass_name: &str) -> ValResult<StringMappingDict<'py>> {
9398
match self {
9499
StringMapping::String(_) => Err(ValError::new(ErrorTypeDefaults::ArgumentsType, self)),

0 commit comments

Comments
 (0)