Validating Function Arguments and Calls
Function validation in this codebase is split into two primary concerns: verifying that a value is a callable entity and validating the arguments passed to a function during an actual call.
The core logic resides in pydantic_core.core_schema, which provides schemas for defining function signatures, handling variadic arguments, and executing functions with validated inputs.
Validating Callables
The simplest form of validation is checking if a value is callable. The CallableSchema (defined in pydantic_core/core_schema.py) performs a check equivalent to Python's callable() function.
from pydantic_core import SchemaValidator, core_schema as cs
v = SchemaValidator(cs.callable_schema())
def my_func():
pass
assert v.validate_python(my_func) == my_func
# v.validate_python(123) # Raises ValidationError
Defining Function Signatures
To validate the arguments themselves, the codebase uses ArgumentsSchema. This schema maps input data—which can be a tuple of positional arguments, a dict of keyword arguments, or a specialized ArgsKwargs object—into a validated (args, kwargs) tuple.
ArgumentsParameter and Modes
The ArgumentsSchema relies on a list of ArgumentsParameter objects. Each parameter defines how a specific argument should be validated and which "mode" it uses:
positional_only: The argument must be provided positionally.positional_or_keyword: The argument can be provided either way (default).keyword_only: The argument must be provided as a keyword.
from pydantic_core.core_schema import ArgumentsParameter
# Example of a parameter definition
param = ArgumentsParameter(
name='a',
mode='positional_or_keyword',
schema={'type': 'int'},
alias='alpha'
)
ArgumentsSchema Structure
The ArgumentsSchema handles the mapping logic. It can be configured to validate by name or alias and can handle variadic arguments (*args) and keyword arguments (**kwargs) via var_args_schema and var_kwargs_schema.
from pydantic_core import SchemaValidator, core_schema as cs
v = SchemaValidator({
'type': 'arguments',
'arguments_schema': [
{'name': 'a', 'mode': 'positional_only', 'schema': {'type': 'int'}},
{'name': 'b', 'mode': 'keyword_only', 'schema': {'type': 'str'}},
],
})
# Validating a mix of positional and keyword inputs
# Returns (args_tuple, kwargs_dict)
args, kwargs = v.validate_python(((1,), {'b': 'hello'}))
assert args == (1,)
assert kwargs == {'b': 'hello'}
Executing Validated Calls
The CallSchema integrates an ArgumentsSchema with a specific Python function. When validate_python is called on a CallSchema, the validator:
- Validates the input against the
arguments_schema. - Calls the provided
functionusing the validated(args, kwargs). - Optionally validates the return value of the function against a
return_schema.
This pattern is used extensively in tests like pydantic-core/tests/validators/test_call.py.
from pydantic_core import SchemaValidator, core_schema as cs
def add(a: int, b: int) -> int:
return a + b
v = SchemaValidator({
'type': 'call',
'function': add,
'arguments_schema': {
'type': 'arguments',
'arguments_schema': [
{'name': 'a', 'mode': 'positional_or_keyword', 'schema': {'type': 'int'}},
{'name': 'b', 'mode': 'positional_or_keyword', 'schema': {'type': 'int'}},
],
},
'return_schema': {'type': 'int', 'lt': 100},
})
# The result of validation is the result of the function call
assert v.validate_python({'a': 10, 'b': 20}) == 30
If the function call fails or the return value does not match the return_schema, a ValidationError is raised. The function_name field in CallSchema can be used to customize the error location; otherwise, it defaults to function.__name__.
Advanced Signatures with ArgumentsV3
The codebase includes an experimental ArgumentsV3Schema and ArgumentsV3Parameter. This version simplifies the definition of complex signatures by including variadic arguments directly in the main arguments_schema list using specialized modes.
ArgumentsV3Parameter supports additional modes:
var_args: Equivalent to*args.var_kwargs_uniform: Equivalent to**kwargswhere all values share a schema.var_kwargs_unpacked_typed_dict: Used for PEP 646 style unpacked TypedDicts.
Example from pydantic-core/tests/validators/arguments_v3/test_general.py:
from pydantic_core import core_schema as cs, ArgsKwargs
v = SchemaValidator(
cs.arguments_v3_schema(
arguments_schema=[
cs.arguments_v3_parameter(name='a', schema=cs.int_schema(), mode='positional_only'),
cs.arguments_v3_parameter(name='args', schema=cs.int_schema(), mode='var_args'),
cs.arguments_v3_parameter(name='kwargs', schema=cs.str_schema(), mode='var_kwargs_uniform'),
]
)
)
# Validating against a mix of positional and variadic inputs
input_data = ArgsKwargs((1, 2, 3), {'extra': 'value'})
validated = v.validate_python(input_data)
# Result: ((1, 2, 3), {'extra': 'value'})
Unlike the standard ArgumentsSchema, ArgumentsV3Schema uses extra_behavior (set to 'forbid' or 'ignore') to control how unexpected keyword arguments are handled, providing stricter control over function invocation.