Skip to content

Commit f2175f5

Browse files
committed
Improved exception handling
1 parent cec4c68 commit f2175f5

File tree

12 files changed

+142
-115
lines changed

12 files changed

+142
-115
lines changed

graphql/error.py

Lines changed: 0 additions & 52 deletions
This file was deleted.

graphql/error/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from .base import GraphQLError
2+
from .located_error import GraphQLLocatedError
3+
from .syntax_error import GraphQLSyntaxError
4+
from .format_error import format_error
5+
6+
__all__ = ['GraphQLError', 'GraphQLLocatedError', 'GraphQLSyntaxError', 'format_error']

graphql/error/base.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from ..language.location import get_location
2+
3+
4+
class GraphQLError(Exception):
5+
__slots__ = 'message', 'nodes', 'stack', 'original_error', '_source', '_positions'
6+
7+
def __init__(self, message, nodes=None, stack=None, source=None, positions=None):
8+
super(GraphQLError, self).__init__(message)
9+
self.message = message
10+
self.nodes = nodes
11+
self.stack = stack
12+
self._source = source
13+
self._positions = positions
14+
15+
@property
16+
def source(self):
17+
if self._source:
18+
return self._source
19+
if self.nodes:
20+
node = self.nodes[0]
21+
return node and node.loc and node.loc.source
22+
23+
@property
24+
def positions(self):
25+
if self._positions:
26+
return self._positions
27+
if self.nodes is not None:
28+
node_positions = [node.loc and node.loc.start for node in self.nodes]
29+
if any(node_positions):
30+
return node_positions
31+
32+
@property
33+
def locations(self):
34+
source = self.source
35+
if self.positions and source:
36+
return [get_location(source, pos) for pos in self.positions]

graphql/error/format_error.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
def format_error(error):
2+
formatted_error = {
3+
'message': error.message,
4+
}
5+
if error.locations is not None:
6+
formatted_error['locations'] = [
7+
{'line': loc.line, 'column': loc.column}
8+
for loc in error.locations
9+
]
10+
11+
return formatted_error

graphql/error/located_error.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import sys
2+
3+
from .base import GraphQLError
4+
5+
__all__ = ['GraphQLLocatedError']
6+
7+
8+
class GraphQLLocatedError(GraphQLError):
9+
10+
def __init__(self, nodes, original_error=None):
11+
if original_error:
12+
message = str(original_error)
13+
else:
14+
message = 'An unknown error occurred.'
15+
16+
if isinstance(original_error, GraphQLError):
17+
stack = original_error.stack
18+
else:
19+
stack = sys.exc_info()[2]
20+
21+
super(GraphQLLocatedError, self).__init__(
22+
message=message,
23+
nodes=nodes,
24+
stack=stack
25+
)
26+
self.original_error = original_error

graphql/language/error.py renamed to graphql/error/syntax_error.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
from ..error import GraphQLError
2-
from .location import get_location
1+
from ..language.location import get_location
2+
from .base import GraphQLError
33

4-
__all__ = ['LanguageError']
4+
__all__ = ['GraphQLSyntaxError']
55

66

7-
class LanguageError(GraphQLError):
7+
class GraphQLSyntaxError(GraphQLError):
88

99
def __init__(self, source, position, description):
1010
location = get_location(source, position)
11-
super(LanguageError, self).__init__(
11+
super(GraphQLSyntaxError, self).__init__(
1212
message=u'Syntax Error {} ({}:{}) {}\n\n{}'.format(
1313
source.name,
1414
location.line,

graphql/execution/executor.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from promise import Promise, is_thenable, promise_for_dict, promisify
66

7-
from ..error import GraphQLError
7+
from ..error import GraphQLError, GraphQLLocatedError
88
from ..pyutils.default_ordered_dict import DefaultOrderedDict
99
from ..type import (GraphQLEnumType, GraphQLInterfaceType, GraphQLList,
1010
GraphQLNonNull, GraphQLObjectType, GraphQLScalarType,
@@ -226,11 +226,11 @@ def complete_value(exe_context, return_type, field_asts, info, result):
226226
info,
227227
resolved
228228
),
229-
lambda error: Promise.rejected(GraphQLError(error and str(error), field_asts, error))
229+
lambda error: Promise.rejected(GraphQLLocatedError(field_asts, original_error=error))
230230
)
231231

232232
if isinstance(result, Exception):
233-
raise GraphQLError(str(result), field_asts, result)
233+
raise GraphQLLocatedError(field_asts, original_error=result)
234234

235235
if isinstance(return_type, GraphQLNonNull):
236236
completed = complete_value(

graphql/language/lexer.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from six import unichr
44

5-
from .error import LanguageError
5+
from ..error import GraphQLSyntaxError
66

77
__all__ = ['Token', 'Lexer', 'TokenKind',
88
'get_token_desc', 'get_token_kind_desc']
@@ -156,7 +156,7 @@ def read_token(source, from_position):
156156
code = char_code_at(body, position)
157157

158158
if code < 0x0020 and code not in (0x0009, 0x000A, 0x000D):
159-
raise LanguageError(
159+
raise GraphQLSyntaxError(
160160
source, position,
161161
u'Invalid character {}.'.format(print_char_code(code))
162162
)
@@ -179,7 +179,7 @@ def read_token(source, from_position):
179179
elif code == 34: # "
180180
return read_string(source, position)
181181

182-
raise LanguageError(
182+
raise GraphQLSyntaxError(
183183
source, position,
184184
u'Unexpected character {}.'.format(print_char_code(code)))
185185

@@ -241,7 +241,7 @@ def read_number(source, start, first_code):
241241
code = char_code_at(body, position)
242242

243243
if code is not None and 48 <= code <= 57:
244-
raise LanguageError(
244+
raise GraphQLSyntaxError(
245245
source,
246246
position,
247247
u'Invalid number, unexpected digit after 0: {}.'.format(print_char_code(code))
@@ -291,7 +291,7 @@ def read_digits(source, start, first_code):
291291

292292
return position
293293

294-
raise LanguageError(
294+
raise GraphQLSyntaxError(
295295
source,
296296
position,
297297
u'Invalid number, expected digit but got: {}.'.format(print_char_code(code))
@@ -338,7 +338,7 @@ def read_string(source, start):
338338
break
339339

340340
if code < 0x0020 and code != 0x0009:
341-
raise LanguageError(
341+
raise GraphQLSyntaxError(
342342
source,
343343
position,
344344
u'Invalid character within String: {}.'.format(print_char_code(code))
@@ -362,15 +362,15 @@ def read_string(source, start):
362362
)
363363

364364
if char_code < 0:
365-
raise LanguageError(
365+
raise GraphQLSyntaxError(
366366
source, position,
367367
u'Invalid character escape sequence: \\u{}.'.format(body[position + 1: position + 5])
368368
)
369369

370370
append(unichr(char_code))
371371
position += 4
372372
else:
373-
raise LanguageError(
373+
raise GraphQLSyntaxError(
374374
source, position,
375375
u'Invalid character escape sequence: \\{}.'.format(unichr(code))
376376
)
@@ -379,7 +379,7 @@ def read_string(source, start):
379379
chunk_start = position
380380

381381
if code != 34: # Quote (")
382-
raise LanguageError(source, position, 'Unterminated string')
382+
raise GraphQLSyntaxError(source, position, 'Unterminated string')
383383

384384
append(body[chunk_start:position])
385385
return Token(TokenKind.STRING, start, position + 1, u''.join(value))

graphql/language/parser.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from six import string_types
22

33
from . import ast
4-
from .error import LanguageError
4+
from ..error import GraphQLSyntaxError
55
from .lexer import Lexer, TokenKind, get_token_desc, get_token_kind_desc
66
from .source import Source
77

@@ -109,7 +109,7 @@ def expect(parser, kind):
109109
advance(parser)
110110
return token
111111

112-
raise LanguageError(
112+
raise GraphQLSyntaxError(
113113
parser.source,
114114
token.start,
115115
u'Expected {}, found {}'.format(
@@ -128,7 +128,7 @@ def expect_keyword(parser, value):
128128
advance(parser)
129129
return token
130130

131-
raise LanguageError(
131+
raise GraphQLSyntaxError(
132132
parser.source,
133133
token.start,
134134
u'Expected "{}", found {}'.format(value, get_token_desc(token))
@@ -139,7 +139,7 @@ def unexpected(parser, at_token=None):
139139
"""Helper function for creating an error when an unexpected lexed token
140140
is encountered."""
141141
token = at_token or parser.token
142-
return LanguageError(
142+
return GraphQLSyntaxError(
143143
parser.source,
144144
token.start,
145145
u'Unexpected {}'.format(get_token_desc(token))

0 commit comments

Comments
 (0)