diff --git a/src/graphql/error/format_error.py b/src/graphql/error/format_error.py index d813ad2f..9b768cf5 100644 --- a/src/graphql/error/format_error.py +++ b/src/graphql/error/format_error.py @@ -17,7 +17,11 @@ def format_error(error: "GraphQLError") -> Dict[str, Any]: raise TypeError("Received no error object.") formatted: Dict[str, Any] = dict( # noqa: E701 (pycqa/flake8#394) message=error.message or "An unknown error occurred.", - locations=error.locations, + locations=( + [location.formatted for location in error.locations] + if error.locations is not None + else None + ), path=error.path, ) if error.extensions: diff --git a/src/graphql/language/location.py b/src/graphql/language/location.py index d59c9c44..a0334fee 100644 --- a/src/graphql/language/location.py +++ b/src/graphql/language/location.py @@ -12,6 +12,18 @@ class SourceLocation(NamedTuple): line: int column: int + @property + def formatted(self): + return dict(line=self.line, column=self.column) + + def __eq__(self, other): + if isinstance(other, dict): + return other == self.formatted + return super().__eq__(other) + + def __ne__(self, other): + return not self == other + def get_location(source: "Source", position: int) -> SourceLocation: """Get the line and column for a character position in the source. diff --git a/tests/error/test_format_error.py b/tests/error/test_format_error.py index 7f98fcb7..beac8e61 100644 --- a/tests/error/test_format_error.py +++ b/tests/error/test_format_error.py @@ -30,9 +30,10 @@ def format_graphql_error(): ValueError("original"), extensions=extensions, ) - assert error == { + formatted = format_error(error) + assert formatted == { "message": "test message", - "locations": [(2, 14), (3, 20)], + "locations": [{"line": 2, "column": 14}, {"line": 3, "column": 20}], "path": path, "extensions": extensions, } diff --git a/tests/execution/test_abstract.py b/tests/execution/test_abstract.py index 9ed24ee4..3c4b8996 100644 --- a/tests/execution/test_abstract.py +++ b/tests/execution/test_abstract.py @@ -254,7 +254,7 @@ def resolve_type_on_interface_yields_useful_error(): assert format_error(result.errors[0]) == { "message": "Runtime Object type 'Human'" " is not a possible type for 'Pet'.", - "locations": [(3, 15)], + "locations": [{"line": 3, "column": 15}], "path": ["pets", 2], } @@ -330,7 +330,7 @@ def resolve_type_on_union_yields_useful_error(): assert format_error(result.errors[0]) == { "message": "Runtime Object type 'Human'" " is not a possible type for 'Pet'.", - "locations": [(3, 15)], + "locations": [{"line": 3, "column": 15}], "path": ["pets", 2], } diff --git a/tests/execution/test_abstract_async.py b/tests/execution/test_abstract_async.py index 456933cc..69a4578d 100644 --- a/tests/execution/test_abstract_async.py +++ b/tests/execution/test_abstract_async.py @@ -182,12 +182,12 @@ async def is_type_of_with_async_error(): assert list(map(format_error, result.errors)) == [ # type: ignore { "message": "We are testing this error", - "locations": [(3, 15)], + "locations": [{"line": 3, "column": 15}], "path": ["pets", 0], }, { "message": "We are testing this error", - "locations": [(3, 15)], + "locations": [{"line": 3, "column": 15}], "path": ["pets", 1], }, ] @@ -334,7 +334,7 @@ async def resolve_type_on_interface_yields_useful_error(): assert format_error(result.errors[0]) == { "message": "Runtime Object type 'Human'" " is not a possible type for 'Pet'.", - "locations": [(3, 15)], + "locations": [{"line": 3, "column": 15}], "path": ["pets", 2], } @@ -411,7 +411,7 @@ async def resolve_type_on_union_yields_useful_error(): assert format_error(result.errors[0]) == { "message": "Runtime Object type 'Human'" " is not a possible type for 'Pet'.", - "locations": [(3, 15)], + "locations": [{"line": 3, "column": 15}], "path": ["pets", 2], } diff --git a/tests/language/test_location.py b/tests/language/test_location.py new file mode 100644 index 00000000..0fd85a44 --- /dev/null +++ b/tests/language/test_location.py @@ -0,0 +1,12 @@ +from graphql import SourceLocation + + +def describe_SourceLocation(): + def equals_with_itself(): + assert SourceLocation(1, 2) == SourceLocation(1, 2) + assert (SourceLocation(1, 2) != SourceLocation(1, 2)) is False + + def equals_with_formatted_form(): + sl = SourceLocation(1, 2) + assert SourceLocation(1, 2) == sl.formatted + assert (SourceLocation(1, 2) != sl.formatted) is False