prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Typing validations should be ignored for tests
  ## Description of the issue
  https://github.com/frappe/frappe/blob/010aa4636ace30a9df4c09f0ca991169f34274b9/frappe/utils/typing_validations.py#L164
  
  If you're writing Frappe tests using the `unittest.mock` module, there might be cases where the argument object is replaced with a `Mock` or `MagicMock` object. This breaks typing validations when running CI tests using the `develop` branch.
  
  I think a reasonable approach could be to either ignore all validations during tests, and/or allow configuring this behaviour per-test (with the default being "ignore").
  
  ## Context
  
  **Output of `bench version`**
  ```
  frappe 14.14.2
  ```
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `frappe/utils/typing_validations.py`
  Content:
  ```
  1 from collections.abc import Callable
  2 from functools import lru_cache, wraps
  3 from inspect import _empty, isclass, signature
  4 from types import EllipsisType
  5 from typing import ForwardRef, TypeVar, Union
  6 
  7 from pydantic import ConfigDict
  8 
  9 from frappe.exceptions import FrappeTypeError
  10 
  11 SLACK_DICT = {
  12 	bool: (int, bool, float),
  13 }
  14 T = TypeVar("T")
  15 
  16 
  17 FrappePydanticConfig = ConfigDict(arbitrary_types_allowed=True)
  18 
  19 
  20 def validate_argument_types(func: Callable, apply_condition: Callable = lambda: True):
  21 	@wraps(func)
  22 	def wrapper(*args, **kwargs):
  23 		"""Validate argument types of whitelisted functions.
  24 
  25 		:param args: Function arguments.
  26 		:param kwargs: Function keyword arguments."""
  27 
  28 		if apply_condition():
  29 			args, kwargs = transform_parameter_types(func, args, kwargs)
  30 
  31 		return func(*args, **kwargs)
  32 
  33 	return wrapper
  34 
  35 
  36 def qualified_name(obj) -> str:
  37 	"""
  38 	Return the qualified name (e.g. package.module.Type) for the given object.
  39 
  40 	Builtins and types from the :mod:typing package get special treatment by having the module
  41 	name stripped from the generated name.
  42 
  43 	"""
  44 	discovered_type = obj if isclass(obj) else type(obj)
  45 	module, qualname = discovered_type.__module__, discovered_type.__qualname__
  46 
  47 	if module in {"typing", "types"}:
  48 		return obj
  49 	elif module in {"builtins"}:
  50 		return qualname
  51 	else:
  52 		return f"{module}.{qualname}"
  53 
  54 
  55 def raise_type_error(
  56 	arg_name: str, arg_type: type, arg_value: object, current_exception: Exception | None = None
  57 ):
  58 	"""
  59 	Raise a TypeError with a message that includes the name of the argument, the expected type
  60 	and the actual type of the value passed.
  61 
  62 	"""
  63 	raise FrappeTypeError(
  64 		f"Argument '{arg_name}' should be of type '{qualified_name(arg_type)}' but got "
  65 		f"'{qualified_name(arg_value)}' instead."
  66 	) from current_exception
  67 
  68 
  69 @lru_cache(maxsize=2048)
  70 def TypeAdapter(type_):
  71 	from pydantic import TypeAdapter as PyTypeAdapter
  72 
  73 	return PyTypeAdapter(type_, config=FrappePydanticConfig)
  74 
  75 
  76 def transform_parameter_types(func: Callable, args: tuple, kwargs: dict):
  77 	"""
  78 	Validate the types of the arguments passed to a function with the type annotations
  79 	defined on the function.
  80 
  81 	"""
  82 	if not (args or kwargs) or not func.__annotations__:
  83 		return args, kwargs
  84 
  85 	from pydantic import ValidationError as PyValidationError
  86 
  87 	annotations = func.__annotations__
  88 	new_args, new_kwargs = list(args), kwargs
  89 
  90 	# generate kwargs dict from args
  91 	arg_names = func.__code__.co_varnames[: func.__code__.co_argcount]
  92 
  93 	if not args:
  94 		prepared_args = kwargs
  95 
  96 	elif kwargs:
  97 		arg_values = args or func.__defaults__ or []
  98 		prepared_args = dict(zip(arg_names, arg_values, strict=False))
  99 		prepared_args.update(kwargs)
  100 
  101 	else:
  102 		prepared_args = dict(zip(arg_names, args, strict=False))
  103 
  104 	# check if type hints dont match the default values
  105 	func_signature = signature(func)
  106 	func_params = dict(func_signature.parameters)
  107 
  108 	# check if the argument types are correct
  109 	for current_arg, current_arg_type in annotations.items():
  110 		if current_arg not in prepared_args:
  111 			continue
  112 
  113 		current_arg_value = prepared_args[current_arg]
  114 
  115 		# if the type is a ForwardRef or str, ignore it
  116 		if isinstance(current_arg_type, ForwardRef | str):
  117 			continue
  118 		elif any(isinstance(x, ForwardRef | str) for x in getattr(current_arg_type, "__args__", [])):
  119 			continue
  120 
  121 		# allow slack for Frappe types
  122 		if current_arg_type in SLACK_DICT:
  123 			current_arg_type = SLACK_DICT[current_arg_type]
  124 
  125 		param_def = func_params.get(current_arg)
  126 
  127 		# add default value's type in acceptable types
  128 		if param_def.default is not _empty:
  129 			if isinstance(current_arg_type, tuple):
  130 				if type(param_def.default) not in current_arg_type:
  131 					current_arg_type += (type(param_def.default),)
  132 				current_arg_type = Union[current_arg_type]  # noqa: UP007
  133 
  134 			elif param_def.default != current_arg_type:
  135 				current_arg_type = Union[current_arg_type, type(param_def.default)]  # noqa: UP007
  136 		elif isinstance(current_arg_type, tuple):
  137 			current_arg_type = Union[current_arg_type]  # noqa: UP007
  138 
  139 		# validate the type set using pydantic - raise a TypeError if Validation is raised or Ellipsis is returned
  140 		try:
  141 			current_arg_value_after = TypeAdapter(current_arg_type).validate_python(current_arg_value)
  142 		except (TypeError, PyValidationError) as e:
  143 			raise_type_error(current_arg, current_arg_type, current_arg_value, current_exception=e)
  144 
  145 		if isinstance(current_arg_value_after, EllipsisType):
  146 			raise_type_error(current_arg, current_arg_type, current_arg_value)
  147 
  148 		# update the args and kwargs with possibly casted value
  149 		if current_arg in kwargs:
  150 			new_kwargs[current_arg] = current_arg_value_after
  151 		else:
  152 			new_args[arg_names.index(current_arg)] = current_arg_value_after
  153 
  154 	return new_args, new_kwargs
  155
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/frappe/utils/typing_validations.py b/frappe/utils/typing_validations.py
  --- a/frappe/utils/typing_validations.py
  +++ b/frappe/utils/typing_validations.py
  @@ -3,6 +3,7 @@
   from inspect import _empty, isclass, signature
   from types import EllipsisType
   from typing import ForwardRef, TypeVar, Union
  +from unittest import mock
   
   from pydantic import ConfigDict
   
  @@ -77,8 +78,8 @@
   	"""
   	Validate the types of the arguments passed to a function with the type annotations
   	defined on the function.
  -
   	"""
  +
   	if not (args or kwargs) or not func.__annotations__:
   		return args, kwargs
   
  @@ -117,6 +118,9 @@
   			continue
   		elif any(isinstance(x, ForwardRef | str) for x in getattr(current_arg_type, "__args__", [])):
   			continue
  +		# ignore unittest.mock objects
  +		elif isinstance(current_arg_value, mock.Mock):
  +			continue
   
   		# allow slack for Frappe types
   		if current_arg_type in SLACK_DICT:
  
verification_info: {"golden_diff": "diff --git a/frappe/utils/typing_validations.py b/frappe/utils/typing_validations.py\n--- a/frappe/utils/typing_validations.py\n+++ b/frappe/utils/typing_validations.py\n@@ -3,6 +3,7 @@\n from inspect import _empty, isclass, signature\n from types import EllipsisType\n from typing import ForwardRef, TypeVar, Union\n+from unittest import mock\n \n from pydantic import ConfigDict\n \n@@ -77,8 +78,8 @@\n \t\"\"\"\n \tValidate the types of the arguments passed to a function with the type annotations\n \tdefined on the function.\n-\n \t\"\"\"\n+\n \tif not (args or kwargs) or not func.__annotations__:\n \t\treturn args, kwargs\n \n@@ -117,6 +118,9 @@\n \t\t\tcontinue\n \t\telif any(isinstance(x, ForwardRef | str) for x in getattr(current_arg_type, \"__args__\", [])):\n \t\t\tcontinue\n+\t\t# ignore unittest.mock objects\n+\t\telif isinstance(current_arg_value, mock.Mock):\n+\t\t\tcontinue\n \n \t\t# allow slack for Frappe types\n \t\tif current_arg_type in SLACK_DICT:\n", "issue": "Typing validations should be ignored for tests\n## Description of the issue\r\nhttps://github.com/frappe/frappe/blob/010aa4636ace30a9df4c09f0ca991169f34274b9/frappe/utils/typing_validations.py#L164\r\n\r\nIf you're writing Frappe tests using the `unittest.mock` module, there might be cases where the argument object is replaced with a `Mock` or `MagicMock` object. This breaks typing validations when running CI tests using the `develop` branch.\r\n\r\nI think a reasonable approach could be to either ignore all validations during tests, and/or allow configuring this behaviour per-test (with the default being \"ignore\").\r\n\r\n## Context\r\n\r\n**Output of `bench version`**\r\n```\r\nfrappe 14.14.2\r\n```\r\n\n", "before_files": [{"content": "from collections.abc import Callable\nfrom functools import lru_cache, wraps\nfrom inspect import _empty, isclass, signature\nfrom types import EllipsisType\nfrom typing import ForwardRef, TypeVar, Union\n\nfrom pydantic import ConfigDict\n\nfrom frappe.exceptions import FrappeTypeError\n\nSLACK_DICT = {\n\tbool: (int, bool, float),\n}\nT = TypeVar(\"T\")\n\n\nFrappePydanticConfig = ConfigDict(arbitrary_types_allowed=True)\n\n\ndef validate_argument_types(func: Callable, apply_condition: Callable = lambda: True):\n\t@wraps(func)\n\tdef wrapper(*args, **kwargs):\n\t\t\"\"\"Validate argument types of whitelisted functions.\n\n\t\t:param args: Function arguments.\n\t\t:param kwargs: Function keyword arguments.\"\"\"\n\n\t\tif apply_condition():\n\t\t\targs, kwargs = transform_parameter_types(func, args, kwargs)\n\n\t\treturn func(*args, **kwargs)\n\n\treturn wrapper\n\n\ndef qualified_name(obj) -> str:\n\t\"\"\"\n\tReturn the qualified name (e.g. package.module.Type) for the given object.\n\n\tBuiltins and types from the :mod:typing package get special treatment by having the module\n\tname stripped from the generated name.\n\n\t\"\"\"\n\tdiscovered_type = obj if isclass(obj) else type(obj)\n\tmodule, qualname = discovered_type.__module__, discovered_type.__qualname__\n\n\tif module in {\"typing\", \"types\"}:\n\t\treturn obj\n\telif module in {\"builtins\"}:\n\t\treturn qualname\n\telse:\n\t\treturn f\"{module}.{qualname}\"\n\n\ndef raise_type_error(\n\targ_name: str, arg_type: type, arg_value: object, current_exception: Exception | None = None\n):\n\t\"\"\"\n\tRaise a TypeError with a message that includes the name of the argument, the expected type\n\tand the actual type of the value passed.\n\n\t\"\"\"\n\traise FrappeTypeError(\n\t\tf\"Argument '{arg_name}' should be of type '{qualified_name(arg_type)}' but got \"\n\t\tf\"'{qualified_name(arg_value)}' instead.\"\n\t) from current_exception\n\n\n@lru_cache(maxsize=2048)\ndef TypeAdapter(type_):\n\tfrom pydantic import TypeAdapter as PyTypeAdapter\n\n\treturn PyTypeAdapter(type_, config=FrappePydanticConfig)\n\n\ndef transform_parameter_types(func: Callable, args: tuple, kwargs: dict):\n\t\"\"\"\n\tValidate the types of the arguments passed to a function with the type annotations\n\tdefined on the function.\n\n\t\"\"\"\n\tif not (args or kwargs) or not func.__annotations__:\n\t\treturn args, kwargs\n\n\tfrom pydantic import ValidationError as PyValidationError\n\n\tannotations = func.__annotations__\n\tnew_args, new_kwargs = list(args), kwargs\n\n\t# generate kwargs dict from args\n\targ_names = func.__code__.co_varnames[: func.__code__.co_argcount]\n\n\tif not args:\n\t\tprepared_args = kwargs\n\n\telif kwargs:\n\t\targ_values = args or func.__defaults__ or []\n\t\tprepared_args = dict(zip(arg_names, arg_values, strict=False))\n\t\tprepared_args.update(kwargs)\n\n\telse:\n\t\tprepared_args = dict(zip(arg_names, args, strict=False))\n\n\t# check if type hints dont match the default values\n\tfunc_signature = signature(func)\n\tfunc_params = dict(func_signature.parameters)\n\n\t# check if the argument types are correct\n\tfor current_arg, current_arg_type in annotations.items():\n\t\tif current_arg not in prepared_args:\n\t\t\tcontinue\n\n\t\tcurrent_arg_value = prepared_args[current_arg]\n\n\t\t# if the type is a ForwardRef or str, ignore it\n\t\tif isinstance(current_arg_type, ForwardRef | str):\n\t\t\tcontinue\n\t\telif any(isinstance(x, ForwardRef | str) for x in getattr(current_arg_type, \"__args__\", [])):\n\t\t\tcontinue\n\n\t\t# allow slack for Frappe types\n\t\tif current_arg_type in SLACK_DICT:\n\t\t\tcurrent_arg_type = SLACK_DICT[current_arg_type]\n\n\t\tparam_def = func_params.get(current_arg)\n\n\t\t# add default value's type in acceptable types\n\t\tif param_def.default is not _empty:\n\t\t\tif isinstance(current_arg_type, tuple):\n\t\t\t\tif type(param_def.default) not in current_arg_type:\n\t\t\t\t\tcurrent_arg_type += (type(param_def.default),)\n\t\t\t\tcurrent_arg_type = Union[current_arg_type]  # noqa: UP007\n\n\t\t\telif param_def.default != current_arg_type:\n\t\t\t\tcurrent_arg_type = Union[current_arg_type, type(param_def.default)]  # noqa: UP007\n\t\telif isinstance(current_arg_type, tuple):\n\t\t\tcurrent_arg_type = Union[current_arg_type]  # noqa: UP007\n\n\t\t# validate the type set using pydantic - raise a TypeError if Validation is raised or Ellipsis is returned\n\t\ttry:\n\t\t\tcurrent_arg_value_after = TypeAdapter(current_arg_type).validate_python(current_arg_value)\n\t\texcept (TypeError, PyValidationError) as e:\n\t\t\traise_type_error(current_arg, current_arg_type, current_arg_value, current_exception=e)\n\n\t\tif isinstance(current_arg_value_after, EllipsisType):\n\t\t\traise_type_error(current_arg, current_arg_type, current_arg_value)\n\n\t\t# update the args and kwargs with possibly casted value\n\t\tif current_arg in kwargs:\n\t\t\tnew_kwargs[current_arg] = current_arg_value_after\n\t\telse:\n\t\t\tnew_args[arg_names.index(current_arg)] = current_arg_value_after\n\n\treturn new_args, new_kwargs\n", "path": "frappe/utils/typing_validations.py"}], "after_files": [{"content": "from collections.abc import Callable\nfrom functools import lru_cache, wraps\nfrom inspect import _empty, isclass, signature\nfrom types import EllipsisType\nfrom typing import ForwardRef, TypeVar, Union\nfrom unittest import mock\n\nfrom pydantic import ConfigDict\n\nfrom frappe.exceptions import FrappeTypeError\n\nSLACK_DICT = {\n\tbool: (int, bool, float),\n}\nT = TypeVar(\"T\")\n\n\nFrappePydanticConfig = ConfigDict(arbitrary_types_allowed=True)\n\n\ndef validate_argument_types(func: Callable, apply_condition: Callable = lambda: True):\n\t@wraps(func)\n\tdef wrapper(*args, **kwargs):\n\t\t\"\"\"Validate argument types of whitelisted functions.\n\n\t\t:param args: Function arguments.\n\t\t:param kwargs: Function keyword arguments.\"\"\"\n\n\t\tif apply_condition():\n\t\t\targs, kwargs = transform_parameter_types(func, args, kwargs)\n\n\t\treturn func(*args, **kwargs)\n\n\treturn wrapper\n\n\ndef qualified_name(obj) -> str:\n\t\"\"\"\n\tReturn the qualified name (e.g. package.module.Type) for the given object.\n\n\tBuiltins and types from the :mod:typing package get special treatment by having the module\n\tname stripped from the generated name.\n\n\t\"\"\"\n\tdiscovered_type = obj if isclass(obj) else type(obj)\n\tmodule, qualname = discovered_type.__module__, discovered_type.__qualname__\n\n\tif module in {\"typing\", \"types\"}:\n\t\treturn obj\n\telif module in {\"builtins\"}:\n\t\treturn qualname\n\telse:\n\t\treturn f\"{module}.{qualname}\"\n\n\ndef raise_type_error(\n\targ_name: str, arg_type: type, arg_value: object, current_exception: Exception | None = None\n):\n\t\"\"\"\n\tRaise a TypeError with a message that includes the name of the argument, the expected type\n\tand the actual type of the value passed.\n\n\t\"\"\"\n\traise FrappeTypeError(\n\t\tf\"Argument '{arg_name}' should be of type '{qualified_name(arg_type)}' but got \"\n\t\tf\"'{qualified_name(arg_value)}' instead.\"\n\t) from current_exception\n\n\n@lru_cache(maxsize=2048)\ndef TypeAdapter(type_):\n\tfrom pydantic import TypeAdapter as PyTypeAdapter\n\n\treturn PyTypeAdapter(type_, config=FrappePydanticConfig)\n\n\ndef transform_parameter_types(func: Callable, args: tuple, kwargs: dict):\n\t\"\"\"\n\tValidate the types of the arguments passed to a function with the type annotations\n\tdefined on the function.\n\t\"\"\"\n\n\tif not (args or kwargs) or not func.__annotations__:\n\t\treturn args, kwargs\n\n\tfrom pydantic import ValidationError as PyValidationError\n\n\tannotations = func.__annotations__\n\tnew_args, new_kwargs = list(args), kwargs\n\n\t# generate kwargs dict from args\n\targ_names = func.__code__.co_varnames[: func.__code__.co_argcount]\n\n\tif not args:\n\t\tprepared_args = kwargs\n\n\telif kwargs:\n\t\targ_values = args or func.__defaults__ or []\n\t\tprepared_args = dict(zip(arg_names, arg_values, strict=False))\n\t\tprepared_args.update(kwargs)\n\n\telse:\n\t\tprepared_args = dict(zip(arg_names, args, strict=False))\n\n\t# check if type hints dont match the default values\n\tfunc_signature = signature(func)\n\tfunc_params = dict(func_signature.parameters)\n\n\t# check if the argument types are correct\n\tfor current_arg, current_arg_type in annotations.items():\n\t\tif current_arg not in prepared_args:\n\t\t\tcontinue\n\n\t\tcurrent_arg_value = prepared_args[current_arg]\n\n\t\t# if the type is a ForwardRef or str, ignore it\n\t\tif isinstance(current_arg_type, ForwardRef | str):\n\t\t\tcontinue\n\t\telif any(isinstance(x, ForwardRef | str) for x in getattr(current_arg_type, \"__args__\", [])):\n\t\t\tcontinue\n\t\t# ignore unittest.mock objects\n\t\telif isinstance(current_arg_value, mock.Mock):\n\t\t\tcontinue\n\n\t\t# allow slack for Frappe types\n\t\tif current_arg_type in SLACK_DICT:\n\t\t\tcurrent_arg_type = SLACK_DICT[current_arg_type]\n\n\t\tparam_def = func_params.get(current_arg)\n\n\t\t# add default value's type in acceptable types\n\t\tif param_def.default is not _empty:\n\t\t\tif isinstance(current_arg_type, tuple):\n\t\t\t\tif type(param_def.default) not in current_arg_type:\n\t\t\t\t\tcurrent_arg_type += (type(param_def.default),)\n\t\t\t\tcurrent_arg_type = Union[current_arg_type]  # noqa: UP007\n\n\t\t\telif param_def.default != current_arg_type:\n\t\t\t\tcurrent_arg_type = Union[current_arg_type, type(param_def.default)]  # noqa: UP007\n\t\telif isinstance(current_arg_type, tuple):\n\t\t\tcurrent_arg_type = Union[current_arg_type]  # noqa: UP007\n\n\t\t# validate the type set using pydantic - raise a TypeError if Validation is raised or Ellipsis is returned\n\t\ttry:\n\t\t\tcurrent_arg_value_after = TypeAdapter(current_arg_type).validate_python(current_arg_value)\n\t\texcept (TypeError, PyValidationError) as e:\n\t\t\traise_type_error(current_arg, current_arg_type, current_arg_value, current_exception=e)\n\n\t\tif isinstance(current_arg_value_after, EllipsisType):\n\t\t\traise_type_error(current_arg, current_arg_type, current_arg_value)\n\n\t\t# update the args and kwargs with possibly casted value\n\t\tif current_arg in kwargs:\n\t\t\tnew_kwargs[current_arg] = current_arg_value_after\n\t\telse:\n\t\t\tnew_args[arg_names.index(current_arg)] = current_arg_value_after\n\n\treturn new_args, new_kwargs\n", "path": "frappe/utils/typing_validations.py"}]}