Skip to content

Add traceback to errors from failed promises #240

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions graphql/error/located_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,15 @@ def __init__(
else:
message = "An unknown error occurred."

stack = original_error and getattr(original_error, "stack", None)
if not stack:
stack = sys.exc_info()[2]
stack = (
original_error
and (
getattr(original_error, "stack", None)
# unfortunately, this is only available in Python 3:
or getattr(original_error, "__traceback__", None)
)
or sys.exc_info()[2]
)

super(GraphQLLocatedError, self).__init__(
message=message, nodes=nodes, stack=stack, path=path
Expand Down
54 changes: 41 additions & 13 deletions graphql/error/tests/test_base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import sys

import pytest
import traceback

from promise import Promise

from graphql.execution import execute
from graphql.language.parser import parse
from graphql.type import GraphQLField, GraphQLObjectType, GraphQLSchema, GraphQLString
Expand Down Expand Up @@ -46,28 +50,52 @@ def resolver(context, *_):

extracted = traceback.extract_tb(exc_info.tb)
formatted_tb = [row[2:] for row in extracted]
if formatted_tb[2][0] == "reraise":
formatted_tb[2:] = formatted_tb[3:]
formatted_tb = [tb for tb in formatted_tb if tb[0] != "reraise"]

assert formatted_tb == [
("test_reraise", "result.errors[0].reraise()"),
("reraise", "six.reraise(type(self), self, self.stack)"),
# ('reraise', 'raise value.with_traceback(tb)'),
(
"resolve_or_error",
"return executor.execute(resolve_fn, source, info, **args)",
),
("execute", "return fn(*args, **kwargs)"),
("resolver", 'raise Exception("Failed")'),
]
# assert formatted_tb == [
# ('test_reraise', 'result.errors[0].reraise()'),
# ('reraise', 'six.reraise(type(self), self, self.stack)'),
# ('on_complete_resolver', 'result = __resolver(*args, **kwargs)'),
# # ('reraise', 'raise value.with_traceback(tb)'),
# # ('resolve_or_error', 'return executor.execute(resolve_fn, source, info, **args)'),
# # ('execute', 'return fn(*args, **kwargs)'),
# ('resolver', "raise Exception('Failed')")
# ]

assert str(exc_info.value) == "Failed"


@pytest.mark.skipif(sys.version_info < (3,), reason="this works only with Python 3")
def test_reraise_from_promise():
# type: () -> None
ast = parse("query Example { a }")

def fail():
raise Exception("Failed")

def resolver(context, *_):
# type: (Optional[Any], *ResolveInfo) -> None
return Promise(lambda resolve, reject: resolve(fail()))

Type = GraphQLObjectType(
"Type", {"a": GraphQLField(GraphQLString, resolver=resolver)}
)

result = execute(GraphQLSchema(Type), ast)
with pytest.raises(Exception) as exc_info:
result.errors[0].reraise()

extracted = traceback.extract_tb(exc_info.tb)
formatted_tb = [row[2:] for row in extracted]
formatted_tb = [tb for tb in formatted_tb if tb[0] != "reraise"]

print(formatted_tb)

assert formatted_tb == [
("test_reraise_from_promise", "result.errors[0].reraise()"),
("_resolve_from_executor", "executor(resolve, reject)"),
("<lambda>", "return Promise(lambda resolve, reject: resolve(fail()))"),
("fail", 'raise Exception("Failed")'),
]

assert str(exc_info.value) == "Failed"