Repository: pallets/werkzeug

Issue:
deprecate utils.validate/bind_arguments
All the functionality can be replaced with `inspect.signature` and `Signature.bind`. If we want to keep it, at least replace the implementation to use `inspect`.


Discussion:
I'll get started on this. But just out of genuine curiosity, why would we choose to keep it as opposed to only using signature and bind? Any pros that I'm not seeing here?
I more meant if someone speaks up with a good reason Werkzeug needs to keep this, we might keep it or let them maintain a separate package. Either way, we should use the built-in APIs.
So, I was looking into this and apparently these functions aren't being used anywhere internally. I have marked the implementations as deprecated and added the warnings but I'm no sure about reimplementing code that will most likely be removed later. I would rather spend my time on other open issues here, but if we decide to keep them, I would gladly do it. That being said, `_internal._parse_signature` and `utils.ArgumentValidationError` are being used, exclusively, by these two functions. Considering how _internal._parse_signature accomplishes almost the same thing as `inspect.signature` and how `Signature.bind` already comes with [TypeError exceptions](https://docs.python.org/3/library/exceptions.html#TypeError) I see no reason to keep such code (might be wrong here) and have marked them to be removed at 2.1 too. Any thoughts on this?

Resolving patch:
diff --git a/src/werkzeug/_internal.py b/src/werkzeug/_internal.py
--- a/src/werkzeug/_internal.py
+++ b/src/werkzeug/_internal.py
@@ -161,7 +161,11 @@ def _log(type, message, *args, **kwargs):
 
 
 def _parse_signature(func):
-    """Return a signature object for the function."""
+    """Return a signature object for the function.
+
+    .. deprecated:: 2.0
+        Will be removed in 2.1 along with utils.bind/validate_arguments
+    """
     # if we have a cached validator for this function, return it
     parse = _signature_cache.get(func)
     if parse is not None:
diff --git a/src/werkzeug/utils.py b/src/werkzeug/utils.py
--- a/src/werkzeug/utils.py
+++ b/src/werkzeug/utils.py
@@ -628,7 +628,16 @@ def proxy(request):
     :param drop_extra: set to `False` if you don't want extra arguments
                        to be silently dropped.
     :return: tuple in the form ``(args, kwargs)``.
+
+    .. deprecated:: 2.0
+        Will be removed in 2.1. Use :func:`inspect.signature` instead.
     """
+    warnings.warn(
+        "'utils.validate_arguments' is deprecated and will be removed"
+        " in 2.1. Use 'inspect.signature' instead.",
+        DeprecationWarning,
+        stacklevel=2,
+    )
     parser = _parse_signature(func)
     args, kwargs, missing, extra, extra_positional = parser(args, kwargs)[:5]
     if missing:
@@ -649,7 +658,16 @@ def bind_arguments(func, args, kwargs):
     :param args: tuple of positional arguments.
     :param kwargs: a dict of keyword arguments.
     :return: a :class:`dict` of bound keyword arguments.
+
+    .. deprecated:: 2.0
+        Will be removed in 2.1. Use :meth:`Signature.bind` instead.
     """
+    warnings.warn(
+        "'utils.bind_arguments' is deprecated and will be removed in"
+        " 2.1. Use 'Signature.bind' instead.",
+        DeprecationWarning,
+        stacklevel=2,
+    )
     (
         args,
         kwargs,
@@ -680,8 +698,11 @@ def bind_arguments(func, args, kwargs):
 
 
 class ArgumentValidationError(ValueError):
+    """Raised if :func:`validate_arguments` fails to validate
 
-    """Raised if :func:`validate_arguments` fails to validate"""
+    .. deprecated:: 2.0
+        Will be removed in 2.1 along with utils.bind/validate_arguments.
+    """
 
     def __init__(self, missing=None, extra=None, extra_positional=None):
         self.missing = set(missing or ())


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -279,44 +279,6 @@ def test_html_builder():
     )
 
 
-def test_validate_arguments():
-    def take_none():
-        pass
-
-    def take_two(a, b):
-        pass
-
-    def take_two_one_default(a, b=0):
-        pass
-
-    assert utils.validate_arguments(take_two, (1, 2), {}) == ((1, 2), {})
-    assert utils.validate_arguments(take_two, (1,), {"b": 2}) == ((1, 2), {})
-    assert utils.validate_arguments(take_two_one_default, (1,), {}) == ((1, 0), {})
-    assert utils.validate_arguments(take_two_one_default, (1, 2), {}) == ((1, 2), {})
-
-    pytest.raises(
-        utils.ArgumentValidationError, utils.validate_arguments, take_two, (), {}
-    )
-
-    assert utils.validate_arguments(take_none, (1, 2), {"c": 3}) == ((), {})
-    pytest.raises(
-        utils.ArgumentValidationError,
-        utils.validate_arguments,
-        take_none,
-        (1,),
-        {},
-        drop_extra=False,
-    )
-    pytest.raises(
-        utils.ArgumentValidationError,
-        utils.validate_arguments,
-        take_none,
-        (),
-        {"a": 1},
-        drop_extra=False,
-    )
-
-
 def test_header_set_duplication_bug():
     headers = Headers([("Content-Type", "text/html"), ("Foo", "bar"), ("Blub", "blah")])
     headers["blub"] = "hehe"