diff --git a/src/graphql/validation/validate.py b/src/graphql/validation/validate.py index 4d183510..e9b34cd0 100644 --- a/src/graphql/validation/validate.py +++ b/src/graphql/validation/validate.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any from ..error import GraphQLError from ..language import DocumentNode, ParallelVisitor, visit @@ -29,10 +29,17 @@ class ValidationAbortedError(GraphQLError): """Error when a validation has been aborted (error limit reached).""" + def __init__( + self, + message: str = ( + "Too many validation errors, error limit reached. Validation aborted." + ), + **kwargs: Any, + ) -> None: + super().__init__(message, **kwargs) -validation_aborted_error = ValidationAbortedError( - "Too many validation errors, error limit reached. Validation aborted." -) + +validation_aborted_error = ValidationAbortedError() # Per the specification, descriptions must not affect validation. @@ -113,7 +120,7 @@ def validate( def on_error(error: GraphQLError) -> None: if len(errors) >= max_errors: - raise validation_aborted_error + raise ValidationAbortedError errors.append(error) context = ValidationContext( @@ -131,8 +138,8 @@ def on_error(error: GraphQLError) -> None: TypeInfoVisitor(type_info, ParallelVisitor(visitors)), query_document_keys_to_validate, ) - except ValidationAbortedError: - errors.append(validation_aborted_error) + except ValidationAbortedError as error: + errors.append(error.with_traceback(None)) return errors diff --git a/tests/validation/test_validation.py b/tests/validation/test_validation.py index bd01a7b3..42e1c676 100644 --- a/tests/validation/test_validation.py +++ b/tests/validation/test_validation.py @@ -4,6 +4,7 @@ from graphql.language import parse from graphql.utilities import build_schema from graphql.validation import ValidationRule, validate +from graphql.validation.validate import ValidationAbortedError, validation_aborted_error from .harness import test_schema @@ -136,6 +137,16 @@ def enter_field(self, *_args): with pytest.raises(RuntimeError, match=r"^Error from custom rule!$"): validate(test_schema, doc, [CustomRule], max_errors=1) + def does_not_accumulate_traceback_on_aborted_error(): + first_error = _validate_document(max_errors=1)[-1] + second_error = _validate_document(max_errors=1)[-1] + assert isinstance(first_error, ValidationAbortedError) + assert isinstance(second_error, ValidationAbortedError) + assert first_error is not second_error + assert first_error.__traceback__ is None + assert second_error.__traceback__ is None + assert validation_aborted_error.__traceback__ is None + def describe_operation_and_variable_definition_descriptions(): def validates_operation_with_description_and_variable_descriptions():