diff --git a/frappe/utils/typing_validations.py b/frappe/utils/typing_validations.py index ade5fcaa06..a2a0331396 100644 --- a/frappe/utils/typing_validations.py +++ b/frappe/utils/typing_validations.py @@ -82,72 +82,76 @@ def transform_parameter_types(func: Callable, args: tuple, kwargs: dict): defined on the function. """ - if annotations := func.__annotations__: - new_args, new_kwargs = list(args), kwargs + if not (args or kwargs) or not func.__annotations__: + return args, kwargs - # generate kwargs dict from args - arg_names = func.__code__.co_varnames[: func.__code__.co_argcount] + annotations = func.__annotations__ + new_args, new_kwargs = list(args), kwargs - if not args: - prepared_args = kwargs + # generate kwargs dict from args + arg_names = func.__code__.co_varnames[: func.__code__.co_argcount] - elif kwargs: - arg_values = args or func.__defaults__ or [] - prepared_args = dict(zip(arg_names, arg_values)) - prepared_args.update(kwargs) + if not args: + prepared_args = kwargs + elif kwargs: + arg_values = args or func.__defaults__ or [] + prepared_args = dict(zip(arg_names, arg_values)) + prepared_args.update(kwargs) + + else: + prepared_args = dict(zip(arg_names, args)) + + # check if type hints dont match the default values + func_signature = signature(func) + func_params = dict(func_signature.parameters) + + # check if the argument types are correct + for current_arg, current_arg_type in annotations.items(): + if current_arg not in prepared_args: + continue + + current_arg_value = prepared_args[current_arg] + + # if the type is a ForwardRef or str, ignore it + if isinstance(current_arg_type, (ForwardRef, str)): + continue + elif any(isinstance(x, (ForwardRef, str)) for x in getattr(current_arg_type, "__args__", [])): + continue + + # allow slack for Frappe types + if current_arg_type in SLACK_DICT: + current_arg_type = SLACK_DICT[current_arg_type] + + param_def = func_params.get(current_arg) + + # add default value's type in acceptable types + if param_def.default is not _empty: + if isinstance(current_arg_type, tuple): + if type(param_def.default) not in current_arg_type: + current_arg_type += (type(param_def.default),) + current_arg_type = Union[current_arg_type] + + elif param_def.default != current_arg_type: + current_arg_type = Union[current_arg_type, type(param_def.default)] + elif isinstance(current_arg_type, tuple): + current_arg_type = Union[current_arg_type] + + # validate the type set using pydantic - raise a TypeError if Validation is raised or Ellipsis is returned + try: + current_arg_value_after = parse_obj_as( + current_arg_type, current_arg_value, type_name=current_arg, config=FrappePydanticConfig + ) + except PyValidationError as e: + raise_type_error(current_arg, current_arg_type, current_arg_value, current_exception=e) + + if isinstance(current_arg_value_after, EllipsisType): + raise_type_error(current_arg, current_arg_type, current_arg_value) + + # update the args and kwargs with possibly casted value + if current_arg in kwargs: + new_kwargs[current_arg] = current_arg_value_after else: - prepared_args = dict(zip(arg_names, args)) + new_args[arg_names.index(current_arg)] = current_arg_value_after - # check if type hints dont match the default values - func_signature = signature(func) - func_params = dict(func_signature.parameters) - - # check if the argument types are correct - for current_arg, current_arg_type in annotations.items(): - if current_arg not in prepared_args: - continue - - current_arg_value = prepared_args[current_arg] - - # if the type is a ForwardRef or str, ignore it - if isinstance(current_arg_type, (ForwardRef, str)): - continue - elif any(isinstance(x, (ForwardRef, str)) for x in getattr(current_arg_type, "__args__", [])): - continue - - # allow slack for Frappe types - if current_arg_type in SLACK_DICT: - current_arg_type = SLACK_DICT[current_arg_type] - - param_def = func_params.get(current_arg) - - # add default value's type in acceptable types - if param_def.default is not _empty: - if isinstance(current_arg_type, tuple): - if type(param_def.default) not in current_arg_type: - current_arg_type += (type(param_def.default),) - current_arg_type = Union[current_arg_type] - - elif param_def.default != current_arg_type: - current_arg_type = Union[current_arg_type, type(param_def.default)] - - try: - current_arg_value_after = parse_obj_as( - current_arg_type, current_arg_value, type_name=current_arg, config=FrappePydanticConfig - ) - except PyValidationError as e: - raise_type_error(current_arg, current_arg_type, current_arg_value, current_exception=e) - - if isinstance(current_arg_value_after, EllipsisType): - raise_type_error(current_arg, current_arg_type, current_arg_value) - - else: - if current_arg in kwargs: - new_kwargs[current_arg] = current_arg_value_after - else: - new_args[arg_names.index(current_arg)] = current_arg_value_after - - return new_args, new_kwargs - - return args, kwargs + return new_args, new_kwargs