Skip to content

Commit 000a1c3

Browse files
committed
[GR-21836] Improve exception and traceback handling
PullRequest: graalpython/910
2 parents d376938 + 67231df commit 000a1c3

File tree

112 files changed

+3315
-1447
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

112 files changed

+3315
-1447
lines changed

graalpython/com.oracle.graal.python.test/src/graalpytest.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -380,14 +380,16 @@ def run(self):
380380
ThreadPool.shutdown()
381381
print("\n\nRan %d tests (%d passes, %d failures)" % (self.passed + self.failed, self.passed, self.failed))
382382
for e in self.exceptions:
383-
print(e)
383+
msg, exc = e
384+
print(msg)
384385
if verbose:
385-
msg, exc = e
386386
try:
387387
import traceback
388-
traceback.print_tb(exc.__traceback__)
389-
except BaseException:
388+
traceback.print_exception(type(exc), exc, exc.__traceback__)
389+
except Exception:
390390
pass
391+
else:
392+
print(exc)
391393

392394
if self.exceptions or self.failed:
393395
os._exit(1)

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_err.py

Lines changed: 111 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -62,6 +62,52 @@ def _reference_fetch(args):
6262
return sys.exc_info()[0]
6363

6464

65+
def compare_tracebacks(tb1, tb2):
66+
while tb1 and tb2:
67+
if tb1.tb_frame.f_code != tb2.tb_frame.f_code:
68+
print(f"\ntb_next: {tb1.tb_frame.f_code} != {tb2.tb_frame.f_code}\n")
69+
return False
70+
tb1 = tb1.tb_next
71+
tb2 = tb2.tb_next
72+
return tb1 is None and tb2 is None
73+
74+
75+
def compare_frame_f_back_chain(f1, f2):
76+
while f1 and f2:
77+
if f1.f_code != f2.f_code:
78+
print(f"\nframe: {f1.f_code} != {f2.f_code}\n")
79+
return False
80+
f1 = f1.f_back
81+
f2 = f2.f_back
82+
return f1 is None and f2 is None
83+
84+
85+
def _reference_fetch_tb_from_python(args):
86+
try:
87+
args[0]()
88+
except:
89+
tb = sys.exc_info()[2]
90+
return tb.tb_next # PyErr_Fetch doesn't contain the current frame
91+
92+
93+
def _reference_fetch_tb_f_back(args):
94+
try:
95+
args[0]()
96+
except:
97+
return sys.exc_info()[2].tb_frame.f_back
98+
99+
100+
def _raise_exception():
101+
def inner():
102+
raise OSError
103+
def reraise(e):
104+
raise e
105+
try:
106+
inner()
107+
except Exception as e:
108+
reraise(e)
109+
110+
65111
def _is_exception_class(exc):
66112
return isinstance(exc, type) and issubclass(exc, BaseException)
67113

@@ -89,11 +135,11 @@ class Dummy:
89135
pass
90136

91137

92-
class TestPyNumber(CPyExtTestCase):
138+
class TestPyErr(CPyExtTestCase):
93139

94140
def compile_module(self, name):
95141
type(self).mro()[1].__dict__["test_%s" % name].create_module(name)
96-
super(TestPyNumber, self).compile_module(name)
142+
super(TestPyErr, self).compile_module(name)
97143

98144
test_PyErr_SetString = CPyExtFunctionVoid(
99145
_reference_setstring,
@@ -312,7 +358,6 @@ def compile_module(self, name):
312358
PyObject* typ = NULL;
313359
PyObject* val = NULL;
314360
PyObject* tb = NULL;
315-
Py_ssize_t size = 3;
316361
PyErr_SetNone(exception_type);
317362
PyErr_Fetch(&typ, &val, &tb);
318363
return typ;
@@ -324,3 +369,65 @@ def compile_module(self, name):
324369
callfunction="wrap_PyErr_Fetch",
325370
cmpfunc=unhandled_error_compare
326371
)
372+
373+
test_PyErr_Fetch_tb_from_c = CPyExtFunctionVoid(
374+
lambda args: None,
375+
lambda: [(1,)],
376+
code="""PyObject* wrap_PyErr_Fetch_tb_from_c() {
377+
PyErr_SetString(PyExc_ArithmeticError, "test");
378+
PyObject* typ = NULL;
379+
PyObject* val = NULL;
380+
PyObject* tb = NULL;
381+
PyErr_Fetch(&typ, &val, &tb);
382+
return tb == NULL? Py_None: tb;
383+
}
384+
""",
385+
resultspec="O",
386+
callfunction="wrap_PyErr_Fetch_tb_from_c",
387+
cmpfunc=compare_tracebacks,
388+
)
389+
390+
test_PyErr_Fetch_tb_from_python = CPyExtFunction(
391+
_reference_fetch_tb_from_python,
392+
lambda: (
393+
(lambda: 1 / 0,),
394+
(_raise_exception,),
395+
),
396+
code="""PyObject* wrap_PyErr_Fetch_tb_from_python(PyObject* fn) {
397+
PyObject_CallFunction(fn, NULL);
398+
PyObject* typ = NULL;
399+
PyObject* val = NULL;
400+
PyObject* tb = NULL;
401+
PyErr_Fetch(&typ, &val, &tb);
402+
return tb;
403+
}
404+
""",
405+
resultspec="O",
406+
argspec='O',
407+
arguments=["PyObject* fn"],
408+
callfunction="wrap_PyErr_Fetch_tb_from_python",
409+
cmpfunc=compare_tracebacks,
410+
)
411+
412+
# GR-22089
413+
# test_PyErr_Fetch_tb_f_back = CPyExtFunction(
414+
# _reference_fetch_tb_f_back,
415+
# lambda: (
416+
# (lambda: 1 / 0,),
417+
# (_raise_exception,),
418+
# ),
419+
# code="""PyObject* wrap_PyErr_Fetch_tb_f_back(PyObject* fn) {
420+
# PyObject_CallFunction(fn, NULL);
421+
# PyObject* typ = NULL;
422+
# PyObject* val = NULL;
423+
# PyObject* tb = NULL;
424+
# PyErr_Fetch(&typ, &val, &tb);
425+
# return PyObject_GetAttrString(PyObject_GetAttrString(tb, "tb_frame"), "f_back");
426+
# }
427+
# """,
428+
# resultspec="O",
429+
# argspec='O',
430+
# arguments=["PyObject* fn"],
431+
# callfunction="wrap_PyErr_Fetch_tb_f_back",
432+
# cmpfunc=compare_frame_f_back_chain,
433+
# )

0 commit comments

Comments
 (0)