From 586c0444aadd46003e3ce5158c5906c6e99ecb39 Mon Sep 17 00:00:00 2001 From: James Tanner Date: Tue, 19 Mar 2024 18:05:10 -0400 Subject: [PATCH] Handle more edge cases. Signed-off-by: James Tanner --- rest_framework/exceptions.py | 4 +-- tests/test_validation_error.py | 56 ++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/rest_framework/exceptions.py b/rest_framework/exceptions.py index 6a95acad8..7bd756ded 100644 --- a/rest_framework/exceptions.py +++ b/rest_framework/exceptions.py @@ -177,9 +177,7 @@ class ValidationError(APIException): """Handle error messages with templates and placeholders.""" try: return detail % params - except KeyError: - return detail - except ValueError: + except (KeyError, ValueError, TypeError, IndexError, AttributeError): return detail diff --git a/tests/test_validation_error.py b/tests/test_validation_error.py index f264c28d6..cdc909e0e 100644 --- a/tests/test_validation_error.py +++ b/tests/test_validation_error.py @@ -214,6 +214,22 @@ class TestValidationErrorWithDjangoStyle(TestCase): with pytest.raises(ValidationError): raise ValidationError(errors) + def test_validation_error_without_params_digit(self): + """Ensure that substitutable errors can be emitted with a digit placeholder.""" + + # mimic the logic in fields.Field.run_validators by saving the exception + # detail into a list which will then be the detail for a new ValidationError. + # this should not throw a TypeError on the date format placeholders ... + errors = [] + try: + raise ValidationError(detail='%d') + except ValidationError as exc: + errors.extend(exc.detail) + + # ensure it raises the correct exception type as an input to a new ValidationError + with pytest.raises(ValidationError): + raise ValidationError(errors) + def test_validation_error_without_params_date_formatters(self): """Ensure that substitutable errors can be emitted with invalid template placeholders.""" @@ -229,3 +245,43 @@ class TestValidationErrorWithDjangoStyle(TestCase): # ensure it raises the correct exception type as an input to a new ValidationError with pytest.raises(ValidationError): raise ValidationError(errors) + + def test_validation_error_with_param_that_has_attribute_error(self): + """Ensure that substitutable errors can be emitted with a bad string repr.""" + + class FooBar: + def __str__(self): + raise AttributeError("i was poorly coded") + + # mimic the logic in fields.Field.run_validators by saving the exception + # detail into a list which will then be the detail for a new ValidationError. + # this should not throw a ValueError on the date format placeholders ... + errors = [] + try: + raise ValidationError(detail='%s', params=FooBar()) + except ValidationError as exc: + errors.extend(exc.detail) + + # ensure it raises the correct exception type as an input to a new ValidationError + with pytest.raises(ValidationError): + raise ValidationError(errors) + + def test_validation_error_with_param_that_has_index_error(self): + """Ensure that substitutable errors can be emitted with a bad string repr.""" + + class FooBar: + def __str__(self): + raise IndexError("i was poorly coded") + + # mimic the logic in fields.Field.run_validators by saving the exception + # detail into a list which will then be the detail for a new ValidationError. + # this should not throw a ValueError on the date format placeholders ... + errors = [] + try: + raise ValidationError(detail='%s', params=FooBar()) + except ValidationError as exc: + errors.extend(exc.detail) + + # ensure it raises the correct exception type as an input to a new ValidationError + with pytest.raises(ValidationError): + raise ValidationError(errors)