Repository: marshmallow-code/webargs

Issue:
Include failing argument name for validation errors
When a validation occurs I want to be able to return the name of the failing argument. At the moment, this is not possible since the validation error contains just a string. An example response:

``` json
{
    "message": "A validation error occured.",
    "params": {
        "foo": "Longer than 10"
    }
}
```

Is this something you feel is worth adding?



Discussion:
@alexmic I agree that this would be useful. :+1: Pull requests welcome!


Resolving patch:
diff --git a/webargs/core.py b/webargs/core.py
--- a/webargs/core.py
+++ b/webargs/core.py
@@ -40,9 +40,10 @@ class ValidationError(WebargsError):
 
         Store status_code and additonal data.
     """
-    def __init__(self, error, status_code=400, **data):
+    def __init__(self, error, status_code=400, arg_name=None, **data):
         self.message = text_type(error)
         self.status_code = status_code
+        self.arg_name = arg_name
         self.data = data
         super(ValidationError, self).__init__(self.message)
 
@@ -220,12 +221,12 @@ def _validate(self, name, value):
             __type_map__.get(type(ret), type(ret).__name__)
         )
         if ret is None and self.type in __non_nullable_types__:
-            raise ValidationError(self.error or msg)
+            raise ValidationError(self.error or msg, arg_name=name)
 
         try:
             ret = self.type(ret)
         except (ValueError, TypeError):
-            raise ValidationError(self.error or msg)
+            raise ValidationError(self.error or msg, arg_name=name)
 
         # Then call validation functions
         for validator in self.validators:
@@ -233,7 +234,7 @@ def _validate(self, name, value):
                 msg = u'Validator {0}({1}) is not True'.format(
                     validator.__name__, ret
                 )
-                raise ValidationError(self.error or msg)
+                raise ValidationError(self.error or msg, arg_name=name)
 
         if self._has_nesting:
             # Filter out extra args
@@ -363,7 +364,7 @@ def parse_arg(self, name, argobj, req, locations=None):
                     value = argobj.default
             if argobj.required:
                 raise RequiredArgMissingError(
-                    'Required parameter "{0}" not found.'.format(name)
+                    'Required parameter "{0}" not found.'.format(name), arg_name=name
                 )
         return value
 
@@ -403,7 +404,7 @@ def parse(self, argmap, req, locations=None, validate=None, force_all=False):
                     msg = u'Validator {0}({1}) is not True'.format(
                         validator.__name__, parsed
                     )
-                    raise ValidationError(self.error or msg)
+                    raise ValidationError(self.error or msg, arg_name=argname)
         except ValidationError as error:
             if self.error_callback:
                 self.error_callback(error)


Test patch:
diff --git a/tests/test_core.py b/tests/test_core.py
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -62,6 +62,12 @@ def test_validated(self):
         with pytest.raises(ValidationError):
             arg.validated('foo', 32)
 
+    def test_validated_stores_the_arg_name(self):
+        arg = Arg(validate=lambda t: False)
+        with pytest.raises(ValidationError) as excinfo:
+            arg.validated('our_arg_name', True)
+        assert 'our_arg_name' == excinfo.value.arg_name
+
     def test_validated_with_nonascii_input(self):
         arg = Arg(validate=lambda t: False)
         text = u'øˆ∆´ƒº'