Coverage for pydantic/_internal/_validate_call.py: 100.00%

69 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-07-10 09:28 +0000

1from __future__ import annotations as _annotations 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

2 

3import functools 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

4import inspect 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

5from collections.abc import Awaitable 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

6from functools import partial 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

7from typing import Any, Callable 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

8 

9import pydantic_core 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

10 

11from ..config import ConfigDict 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

12from ..plugin._schema_validator import create_schema_validator 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

13from ._config import ConfigWrapper 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

14from ._generate_schema import GenerateSchema, ValidateCallSupportedTypes 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

15from ._namespace_utils import MappingNamespace, NsResolver, ns_for_function 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

16 

17 

18def extract_function_name(func: ValidateCallSupportedTypes) -> str: 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

19 """Extract the name of a `ValidateCallSupportedTypes` object.""" 

20 return f'partial({func.func.__name__})' if isinstance(func, functools.partial) else func.__name__ 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

21 

22 

23def extract_function_qualname(func: ValidateCallSupportedTypes) -> str: 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

24 """Extract the qualname of a `ValidateCallSupportedTypes` object.""" 

25 return f'partial({func.func.__qualname__})' if isinstance(func, functools.partial) else func.__qualname__ 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

26 

27 

28def update_wrapper_attributes(wrapped: ValidateCallSupportedTypes, wrapper: Callable[..., Any]): 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

29 """Update the `wrapper` function with the attributes of the `wrapped` function. Return the updated function.""" 

30 if inspect.iscoroutinefunction(wrapped): 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

31 

32 @functools.wraps(wrapped) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

33 async def wrapper_function(*args, **kwargs): # type: ignore 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

34 return await wrapper(*args, **kwargs) 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

35 else: 

36 

37 @functools.wraps(wrapped) 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

38 def wrapper_function(*args, **kwargs): 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

39 return wrapper(*args, **kwargs) 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

40 

41 # We need to manually update this because `partial` object has no `__name__` and `__qualname__`. 

42 wrapper_function.__name__ = extract_function_name(wrapped) 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

43 wrapper_function.__qualname__ = extract_function_qualname(wrapped) 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

44 wrapper_function.raw_function = wrapped # type: ignore 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

45 

46 return wrapper_function 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

47 

48 

49class ValidateCallWrapper: 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

50 """This is a wrapper around a function that validates the arguments passed to it, and optionally the return value.""" 

51 

52 __slots__ = ( 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

53 'function', 

54 'validate_return', 

55 'schema_type', 

56 'module', 

57 'qualname', 

58 'ns_resolver', 

59 'config_wrapper', 

60 '__pydantic_complete__', 

61 '__pydantic_validator__', 

62 '__return_pydantic_validator__', 

63 ) 

64 

65 def __init__( 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

66 self, 

67 function: ValidateCallSupportedTypes, 

68 config: ConfigDict | None, 

69 validate_return: bool, 

70 parent_namespace: MappingNamespace | None, 

71 ) -> None: 

72 self.function = function 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

73 self.validate_return = validate_return 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

74 if isinstance(function, partial): 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

75 self.schema_type = function.func 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

76 self.module = function.func.__module__ 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

77 else: 

78 self.schema_type = function 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

79 self.module = function.__module__ 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

80 self.qualname = extract_function_qualname(function) 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

81 

82 self.ns_resolver = NsResolver( 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

83 namespaces_tuple=ns_for_function(self.schema_type, parent_namespace=parent_namespace) 

84 ) 

85 self.config_wrapper = ConfigWrapper(config) 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

86 if not self.config_wrapper.defer_build: 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

87 self._create_validators() 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

88 else: 

89 self.__pydantic_complete__ = False 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

90 

91 def _create_validators(self) -> None: 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

92 gen_schema = GenerateSchema(self.config_wrapper, self.ns_resolver) 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

93 schema = gen_schema.clean_schema(gen_schema.generate_schema(self.function)) 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

94 core_config = self.config_wrapper.core_config(title=self.qualname) 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

95 

96 self.__pydantic_validator__ = create_schema_validator( 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

97 schema, 

98 self.schema_type, 

99 self.module, 

100 self.qualname, 

101 'validate_call', 

102 core_config, 

103 self.config_wrapper.plugin_settings, 

104 ) 

105 if self.validate_return: 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

106 signature = inspect.signature(self.function) 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

107 return_type = signature.return_annotation if signature.return_annotation is not signature.empty else Any 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

108 gen_schema = GenerateSchema(self.config_wrapper, self.ns_resolver) 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

109 schema = gen_schema.clean_schema(gen_schema.generate_schema(return_type)) 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

110 validator = create_schema_validator( 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

111 schema, 

112 self.schema_type, 

113 self.module, 

114 self.qualname, 

115 'validate_call', 

116 core_config, 

117 self.config_wrapper.plugin_settings, 

118 ) 

119 if inspect.iscoroutinefunction(self.function): 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

120 

121 async def return_val_wrapper(aw: Awaitable[Any]) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

122 return validator.validate_python(await aw) 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

123 

124 self.__return_pydantic_validator__ = return_val_wrapper 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL

125 else: 

126 self.__return_pydantic_validator__ = validator.validate_python 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

127 else: 

128 self.__return_pydantic_validator__ = None 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

129 

130 self.__pydantic_complete__ = True 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

131 

132 def __call__(self, *args: Any, **kwargs: Any) -> Any: 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

133 if not self.__pydantic_complete__: 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

134 self._create_validators() 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

135 

136 res = self.__pydantic_validator__.validate_python(pydantic_core.ArgsKwargs(args, kwargs)) 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

137 if self.__return_pydantic_validator__: 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

138 return self.__return_pydantic_validator__(res) 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO

139 else: 

140 return res 1abcdefghijklMmnopqrstuvwxyzNABCDEFGHIJKLO