Skip to content

Commit e722efd

Browse files
committed
Fix interaction between PyErr_Fetch and PyTraceBack_Here
1 parent 0f9b538 commit e722efd

File tree

2 files changed

+113
-3
lines changed

2 files changed

+113
-3
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
2+
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3+
#
4+
# The Universal Permissive License (UPL), Version 1.0
5+
#
6+
# Subject to the condition set forth below, permission is hereby granted to any
7+
# person obtaining a copy of this software, associated documentation and/or
8+
# data (collectively the "Software"), free of charge and under any and all
9+
# copyright rights in the Software, and any and all patent rights owned or
10+
# freely licensable by each licensor hereunder covering either (i) the
11+
# unmodified Software as contributed to or provided by such licensor, or (ii)
12+
# the Larger Works (as defined below), to deal in both
13+
#
14+
# (a) the Software, and
15+
#
16+
# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
17+
# one is included with the Software each a "Larger Work" to which the Software
18+
# is contributed by such licensors),
19+
#
20+
# without restriction, including without limitation the rights to copy, create
21+
# derivative works of, display, perform, and distribute the Software and make,
22+
# use, sell, offer for sale, import, export, have made, and have sold the
23+
# Software and the Larger Work(s), and to sublicense the foregoing rights on
24+
# either these or other terms.
25+
#
26+
# This license is subject to the following condition:
27+
#
28+
# The above copyright notice and either this complete permission notice or at a
29+
# minimum a reference to the UPL must be included in all copies or substantial
30+
# portions of the Software.
31+
#
32+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38+
# SOFTWARE.
39+
40+
import sys
41+
42+
from tests.cpyext import CPyExtType
43+
44+
45+
class TestTraceback:
46+
def test_PyTraceBack_Here(self):
47+
Tester = CPyExtType(
48+
"TracebackTester1",
49+
"""
50+
PyObject* test(PyObject* unused, PyObject* frame) {
51+
Py_INCREF(frame);
52+
PyErr_SetString(PyExc_RuntimeError, "test error");
53+
if (PyTraceBack_Here((PyFrameObject*)frame)) {
54+
Py_DECREF(frame);
55+
return NULL;
56+
}
57+
return NULL;
58+
}
59+
""",
60+
tp_methods='{"test", test, METH_O | METH_STATIC, ""}',
61+
)
62+
63+
def fake_frame():
64+
return sys._getframe(0)
65+
66+
frame = fake_frame()
67+
68+
try:
69+
Tester.test(frame)
70+
except RuntimeError as e:
71+
assert e.__traceback__
72+
assert e.__traceback__.tb_next
73+
assert e.__traceback__.tb_next.tb_frame is frame
74+
assert e.__traceback__.tb_next.tb_next is None
75+
else:
76+
assert False, "Didn't raise exception"
77+
78+
def test_PyTraceBack_Here_and_fetch(self):
79+
Tester = CPyExtType(
80+
"TracebackTester2",
81+
"""
82+
PyObject* test(PyObject* unused, PyObject* frame) {
83+
Py_INCREF(frame);
84+
PyErr_SetString(PyExc_RuntimeError, "test error");
85+
if (PyTraceBack_Here((PyFrameObject*)frame)) {
86+
Py_DECREF(frame);
87+
return NULL;
88+
}
89+
PyObject *type, *value, *tb;
90+
PyErr_Fetch(&type, &value, &tb);
91+
Py_DECREF(type);
92+
Py_DECREF(value);
93+
return tb;
94+
}
95+
""",
96+
tp_methods='{"test", test, METH_O | METH_STATIC, ""}',
97+
)
98+
99+
def fake_frame():
100+
return sys._getframe(0)
101+
102+
frame = fake_frame()
103+
104+
tb = Tester.test(frame)
105+
assert tb.tb_frame is frame
106+
assert tb.tb_next is None

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextErrBuiltins.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
import com.oracle.graal.python.builtins.objects.function.PKeyword;
9595
import com.oracle.graal.python.builtins.objects.module.PythonModule;
9696
import com.oracle.graal.python.builtins.objects.traceback.LazyTraceback;
97+
import com.oracle.graal.python.builtins.objects.traceback.MaterializeLazyTracebackNode;
9798
import com.oracle.graal.python.builtins.objects.traceback.PTraceback;
9899
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
99100
import com.oracle.graal.python.builtins.objects.tuple.TupleBuiltins;
@@ -172,15 +173,18 @@ Object run(
172173
@Bind("this") Node inliningTarget,
173174
@Cached GetThreadStateNode getThreadStateNode,
174175
@Cached InlinedGetClassNode getClassNode,
175-
@Cached ExceptionNodes.GetTracebackNode getTracebackNode) {
176+
@Cached MaterializeLazyTracebackNode materializeTraceback) {
176177
PException currentException = getThreadStateNode.getCurrentException();
177178
Object result;
178179
if (currentException == null) {
179180
result = getNativeNull();
180181
} else {
181182
Object exception = currentException.getEscapedException();
182-
Object traceback = getTracebackNode.execute(inliningTarget, exception);
183-
if (traceback == PNone.NONE) {
183+
Object traceback = null;
184+
if (currentException.getTraceback() != null) {
185+
traceback = materializeTraceback.execute(currentException.getTraceback());
186+
}
187+
if (traceback == null) {
184188
traceback = getNativeNull();
185189
}
186190
result = factory().createTuple(new Object[]{getClassNode.execute(inliningTarget, exception), exception, traceback});

0 commit comments

Comments
 (0)