Merge pull request #33217 from sagarvora/perf-typing-validations
This commit is contained in:
commit
ede9e6ed97
1 changed files with 23 additions and 25 deletions
|
|
@ -6,20 +6,23 @@ from typing import ForwardRef, TypeVar, Union
|
|||
from unittest import mock
|
||||
|
||||
from pydantic import ConfigDict, PydanticUserError
|
||||
from pydantic import TypeAdapter as PyTypeAdapter
|
||||
from pydantic import TypeAdapter as PydanticTypeAdapter
|
||||
from pydantic import ValidationError as PydanticValidationError
|
||||
|
||||
import frappe
|
||||
from frappe.exceptions import FrappeTypeError
|
||||
|
||||
SLACK_DICT = {
|
||||
bool: (int, bool, float),
|
||||
}
|
||||
T = TypeVar("T")
|
||||
ForwardRefOrStr = ForwardRef | str
|
||||
|
||||
|
||||
FrappePydanticConfig = ConfigDict(arbitrary_types_allowed=True)
|
||||
|
||||
|
||||
def validate_argument_types(func: Callable, apply_condition: Callable = lambda: True):
|
||||
def validate_argument_types(func: Callable, apply_condition: Callable | None = None):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
"""Validate argument types of whitelisted functions.
|
||||
|
|
@ -27,7 +30,7 @@ def validate_argument_types(func: Callable, apply_condition: Callable = lambda:
|
|||
:param args: Function arguments.
|
||||
:param kwargs: Function keyword arguments."""
|
||||
|
||||
if apply_condition():
|
||||
if apply_condition is None or apply_condition():
|
||||
args, kwargs = transform_parameter_types(func, args, kwargs)
|
||||
|
||||
return func(*args, **kwargs)
|
||||
|
|
@ -76,11 +79,11 @@ def raise_type_error(
|
|||
@lru_cache(maxsize=2048)
|
||||
def TypeAdapter(type_):
|
||||
try:
|
||||
return PyTypeAdapter(type_, config=FrappePydanticConfig)
|
||||
return PydanticTypeAdapter(type_, config=FrappePydanticConfig)
|
||||
except PydanticUserError as e:
|
||||
# Cannot set config for types BaseModel, TypedDict and dataclass
|
||||
if e.code == "type-adapter-config-unused":
|
||||
return PyTypeAdapter(type_)
|
||||
return PydanticTypeAdapter(type_)
|
||||
|
||||
raise e
|
||||
|
||||
|
|
@ -91,34 +94,29 @@ def transform_parameter_types(func: Callable, args: tuple, kwargs: dict):
|
|||
defined on the function.
|
||||
"""
|
||||
|
||||
annotations = func.__annotations__
|
||||
|
||||
if (
|
||||
not (args or kwargs)
|
||||
or not func.__annotations__
|
||||
or not annotations
|
||||
# No input validations to perform
|
||||
or (len(func.__annotations__) == 1 and func.__annotations__.get("return"))
|
||||
or (len(annotations) == 1 and "return" in annotations)
|
||||
):
|
||||
return args, kwargs
|
||||
|
||||
from pydantic import ValidationError as PyValidationError
|
||||
|
||||
import frappe
|
||||
|
||||
annotations = func.__annotations__
|
||||
new_args, new_kwargs = list(args), kwargs
|
||||
|
||||
# generate kwargs dict from args
|
||||
arg_names = func.__code__.co_varnames[: func.__code__.co_argcount]
|
||||
if args:
|
||||
# generate kwargs dict from args
|
||||
arg_names = func.__code__.co_varnames[: func.__code__.co_argcount]
|
||||
prepared_args = dict(zip(arg_names, args, strict=False))
|
||||
|
||||
if not args:
|
||||
prepared_args = kwargs
|
||||
|
||||
elif kwargs:
|
||||
arg_values = args or func.__defaults__ or []
|
||||
prepared_args = dict(zip(arg_names, arg_values, strict=False))
|
||||
prepared_args.update(kwargs)
|
||||
if kwargs:
|
||||
# update prepared_args with kwargs
|
||||
prepared_args.update(kwargs)
|
||||
|
||||
else:
|
||||
prepared_args = dict(zip(arg_names, args, strict=False))
|
||||
prepared_args = kwargs
|
||||
|
||||
# check if type hints dont match the default values
|
||||
func_params = frappe._get_cached_signature_params(func)[0]
|
||||
|
|
@ -131,9 +129,9 @@ def transform_parameter_types(func: Callable, args: tuple, kwargs: dict):
|
|||
current_arg_value = prepared_args[current_arg]
|
||||
|
||||
# if the type is a ForwardRef or str, ignore it
|
||||
if isinstance(current_arg_type, ForwardRef | str):
|
||||
if isinstance(current_arg_type, ForwardRefOrStr):
|
||||
continue
|
||||
elif any(isinstance(x, ForwardRef | str) for x in getattr(current_arg_type, "__args__", [])):
|
||||
elif any(isinstance(x, ForwardRefOrStr) for x in getattr(current_arg_type, "__args__", [])):
|
||||
continue
|
||||
# ignore unittest.mock objects
|
||||
elif isinstance(current_arg_value, mock.Mock):
|
||||
|
|
@ -160,7 +158,7 @@ def transform_parameter_types(func: Callable, args: tuple, kwargs: dict):
|
|||
# validate the type set using pydantic - raise a TypeError if Validation is raised or Ellipsis is returned
|
||||
try:
|
||||
current_arg_value_after = TypeAdapter(current_arg_type).validate_python(current_arg_value)
|
||||
except (TypeError, PyValidationError) as e:
|
||||
except (TypeError, PydanticValidationError) as e:
|
||||
raise_type_error(func, current_arg, current_arg_type, current_arg_value, current_exception=e)
|
||||
|
||||
if isinstance(current_arg_value_after, EllipsisType):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue