Search code examples
pythonpydantic

Validate a function call without calling a function


I'd like to know if I can use pydantic to check whether arguments would be fit for calling a type hinted function, but without calling the function.

For example, given

kwargs = {'x': 1, 'y': 'hi'}

def foo(x: int, y: str, z: Optional[list] = None):
    pass

I want to check whether foo(**kwargs) would be fine according to the type hints. So basically, what pydantic.validate_call does, without calling foo.

My research led me to this github issue. The solution works, but it relies on validate_arguments, which is deprecated. It cannot be switched for validate_call, because its return value doesn't have a vd attribute, making the line validated_function = f.vd fail.


Solution

  • You can extract the annotations attribute from the function and use it to build a pydantic model using the type constructor:

    def form_validator_model(func: collections.abc.Callable) -> type[pydantic.BaseModel]:
        ann = func.__annotations__.copy()
        ann.pop('return', None) # Remove the return value annotation if it exists.
        return type(f'{func.__name__}_Validator', (pydantic.BaseModel,), {'__annotations__': ann})
    
    def func(a: str, b: int) -> str:
        return a * b
    
    model = form_validator_model(func)
    model(a='hi', b='bye') # raises ValidationError
    

    The downside is you can't call the arguments positionally.