Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0ddbf47

Browse files
committed
Issue #22462: Fix pyexpat's creation of a dummy frame to make it appear in exception tracebacks.
Initial patch by Mark Shannon.
1 parent b2fdafe commit 0ddbf47

File tree

8 files changed

+63
-161
lines changed

8 files changed

+63
-161
lines changed

Include/traceback.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ PyAPI_FUNC(int) PyTraceBack_Here(struct _frame *);
2424
PyAPI_FUNC(int) PyTraceBack_Print(PyObject *, PyObject *);
2525
#ifndef Py_LIMITED_API
2626
PyAPI_FUNC(int) _Py_DisplaySourceLine(PyObject *, PyObject *, int, int);
27+
PyAPI_FUNC(void) _PyTraceback_Add(char *, char *, int);
2728
#endif
2829

2930
/* Reveal traceback type so we can typecheck traceback objects */

Lib/test/test_pyexpat.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
# handler, are obscure and unhelpful.
33

44
from io import BytesIO
5+
import os
56
import unittest
7+
import traceback
68

79
from xml.parsers import expat
810
from xml.parsers.expat import errors
@@ -419,7 +421,11 @@ class HandlerExceptionTest(unittest.TestCase):
419421
def StartElementHandler(self, name, attrs):
420422
raise RuntimeError(name)
421423

422-
def test(self):
424+
def check_traceback_entry(self, entry, filename, funcname):
425+
self.assertEqual(os.path.basename(entry[0]), filename)
426+
self.assertEqual(entry[2], funcname)
427+
428+
def test_exception(self):
423429
parser = expat.ParserCreate()
424430
parser.StartElementHandler = self.StartElementHandler
425431
try:
@@ -429,6 +435,16 @@ def test(self):
429435
self.assertEqual(e.args[0], 'a',
430436
"Expected RuntimeError for element 'a', but" + \
431437
" found %r" % e.args[0])
438+
# Check that the traceback contains the relevant line in pyexpat.c
439+
entries = traceback.extract_tb(e.__traceback__)
440+
self.assertEqual(len(entries), 3)
441+
self.check_traceback_entry(entries[0],
442+
"test_pyexpat.py", "test_exception")
443+
self.check_traceback_entry(entries[1],
444+
"pyexpat.c", "StartElement")
445+
self.check_traceback_entry(entries[2],
446+
"test_pyexpat.py", "StartElementHandler")
447+
self.assertIn('call_with_frame("StartElement"', entries[1][3])
432448

433449

434450
# Test Current* members:

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ Core and Builtins
2424
Library
2525
-------
2626

27+
- Issue #22462: Fix pyexpat's creation of a dummy frame to make it
28+
appear in exception tracebacks.
29+
2730
- Issue #21173: Fix len() on a WeakKeyDictionary when .clear() was called
2831
with an iterator alive.
2932

Modules/_ctypes/callbacks.c

Lines changed: 1 addition & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -92,49 +92,6 @@ PrintError(char *msg, ...)
9292
}
9393

9494

95-
/* after code that pyrex generates */
96-
void _ctypes_add_traceback(char *funcname, char *filename, int lineno)
97-
{
98-
PyObject *py_globals = 0;
99-
PyCodeObject *py_code = 0;
100-
PyFrameObject *py_frame = 0;
101-
PyObject *exception, *value, *tb;
102-
103-
/* (Save and) Clear the current exception. Python functions must not be
104-
called with an exception set. Calling Python functions happens when
105-
the codec of the filesystem encoding is implemented in pure Python. */
106-
PyErr_Fetch(&exception, &value, &tb);
107-
108-
py_globals = PyDict_New();
109-
if (!py_globals)
110-
goto bad;
111-
py_code = PyCode_NewEmpty(filename, funcname, lineno);
112-
if (!py_code)
113-
goto bad;
114-
py_frame = PyFrame_New(
115-
PyThreadState_Get(), /*PyThreadState *tstate,*/
116-
py_code, /*PyCodeObject *code,*/
117-
py_globals, /*PyObject *globals,*/
118-
0 /*PyObject *locals*/
119-
);
120-
if (!py_frame)
121-
goto bad;
122-
py_frame->f_lineno = lineno;
123-
124-
PyErr_Restore(exception, value, tb);
125-
PyTraceBack_Here(py_frame);
126-
127-
Py_DECREF(py_globals);
128-
Py_DECREF(py_code);
129-
Py_DECREF(py_frame);
130-
return;
131-
132-
bad:
133-
Py_XDECREF(py_globals);
134-
Py_XDECREF(py_code);
135-
Py_XDECREF(py_frame);
136-
}
137-
13895
#ifdef MS_WIN32
13996
/*
14097
* We must call AddRef() on non-NULL COM pointers we receive as arguments
@@ -254,7 +211,7 @@ static void _CallPythonObject(void *mem,
254211
}
255212

256213
#define CHECK(what, x) \
257-
if (x == NULL) _ctypes_add_traceback(what, "_ctypes/callbacks.c", __LINE__ - 1), PyErr_Print()
214+
if (x == NULL) _PyTraceback_Add(what, "_ctypes/callbacks.c", __LINE__ - 1), PyErr_Print()
258215

259216
if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) {
260217
error_object = _ctypes_get_errobj(&space);

Modules/_ctypes/callproc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,7 @@ static PyObject *GetResult(PyObject *restype, void *result, PyObject *checker)
919919

920920
v = PyObject_CallFunctionObjArgs(checker, retval, NULL);
921921
if (v == NULL)
922-
_ctypes_add_traceback("GetResult", "_ctypes/callproc.c", __LINE__-2);
922+
_PyTraceback_Add("GetResult", "_ctypes/callproc.c", __LINE__-2);
923923
Py_DECREF(retval);
924924
return v;
925925
}

Modules/_ctypes/ctypes.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -353,8 +353,6 @@ extern char *_ctypes_conversion_errors;
353353
extern void _ctypes_free_closure(void *);
354354
extern void *_ctypes_alloc_closure(void);
355355

356-
extern void _ctypes_add_traceback(char *, char *, int);
357-
358356
extern PyObject *PyCData_FromBaseObj(PyObject *type, PyObject *base, Py_ssize_t index, char *adr);
359357
extern char *_ctypes_alloc_format_string(const char *prefix, const char *suffix);
360358
extern char *_ctypes_alloc_format_string_with_shape(int ndim,

Modules/pyexpat.c

Lines changed: 7 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88

99
#define XML_COMBINED_VERSION (10000*XML_MAJOR_VERSION+100*XML_MINOR_VERSION+XML_MICRO_VERSION)
1010

11-
#define FIX_TRACE
12-
1311
static XML_Memory_Handling_Suite ExpatMemoryHandler = {
1412
PyObject_Malloc, PyObject_Realloc, PyObject_Free};
1513

@@ -210,121 +208,17 @@ flag_error(xmlparseobject *self)
210208
error_external_entity_ref_handler);
211209
}
212210

213-
static PyCodeObject*
214-
getcode(enum HandlerTypes slot, char* func_name, int lineno)
215-
{
216-
if (handler_info[slot].tb_code == NULL) {
217-
handler_info[slot].tb_code =
218-
PyCode_NewEmpty(__FILE__, func_name, lineno);
219-
}
220-
return handler_info[slot].tb_code;
221-
}
222-
223-
#ifdef FIX_TRACE
224-
static int
225-
trace_frame(PyThreadState *tstate, PyFrameObject *f, int code, PyObject *val)
226-
{
227-
int result = 0;
228-
if (!tstate->use_tracing || tstate->tracing)
229-
return 0;
230-
if (tstate->c_profilefunc != NULL) {
231-
tstate->tracing++;
232-
result = tstate->c_profilefunc(tstate->c_profileobj,
233-
f, code , val);
234-
tstate->use_tracing = ((tstate->c_tracefunc != NULL)
235-
|| (tstate->c_profilefunc != NULL));
236-
tstate->tracing--;
237-
if (result)
238-
return result;
239-
}
240-
if (tstate->c_tracefunc != NULL) {
241-
tstate->tracing++;
242-
result = tstate->c_tracefunc(tstate->c_traceobj,
243-
f, code , val);
244-
tstate->use_tracing = ((tstate->c_tracefunc != NULL)
245-
|| (tstate->c_profilefunc != NULL));
246-
tstate->tracing--;
247-
}
248-
return result;
249-
}
250-
251-
static int
252-
trace_frame_exc(PyThreadState *tstate, PyFrameObject *f)
253-
{
254-
PyObject *type, *value, *traceback, *arg;
255-
int err;
256-
257-
if (tstate->c_tracefunc == NULL)
258-
return 0;
259-
260-
PyErr_Fetch(&type, &value, &traceback);
261-
if (value == NULL) {
262-
value = Py_None;
263-
Py_INCREF(value);
264-
}
265-
arg = PyTuple_Pack(3, type, value, traceback);
266-
if (arg == NULL) {
267-
PyErr_Restore(type, value, traceback);
268-
return 0;
269-
}
270-
err = trace_frame(tstate, f, PyTrace_EXCEPTION, arg);
271-
Py_DECREF(arg);
272-
if (err == 0)
273-
PyErr_Restore(type, value, traceback);
274-
else {
275-
Py_XDECREF(type);
276-
Py_XDECREF(value);
277-
Py_XDECREF(traceback);
278-
}
279-
return err;
280-
}
281-
#endif
282-
283211
static PyObject*
284-
call_with_frame(PyCodeObject *c, PyObject* func, PyObject* args,
212+
call_with_frame(char *funcname, int lineno, PyObject* func, PyObject* args,
285213
xmlparseobject *self)
286214
{
287-
PyThreadState *tstate = PyThreadState_GET();
288-
PyFrameObject *f;
289-
PyObject *res, *globals;
215+
PyObject *res;
290216

291-
if (c == NULL)
292-
return NULL;
293-
294-
globals = PyEval_GetGlobals();
295-
if (globals == NULL) {
296-
return NULL;
297-
}
298-
299-
f = PyFrame_New(tstate, c, globals, NULL);
300-
if (f == NULL)
301-
return NULL;
302-
tstate->frame = f;
303-
#ifdef FIX_TRACE
304-
if (trace_frame(tstate, f, PyTrace_CALL, Py_None) < 0) {
305-
return NULL;
306-
}
307-
#endif
308217
res = PyEval_CallObject(func, args);
309218
if (res == NULL) {
310-
if (tstate->curexc_traceback == NULL)
311-
PyTraceBack_Here(f);
219+
_PyTraceback_Add(funcname, __FILE__, lineno);
312220
XML_StopParser(self->itself, XML_FALSE);
313-
#ifdef FIX_TRACE
314-
if (trace_frame_exc(tstate, f) < 0) {
315-
return NULL;
316-
}
317221
}
318-
else {
319-
if (trace_frame(tstate, f, PyTrace_RETURN, res) < 0) {
320-
Py_CLEAR(res);
321-
}
322-
}
323-
#else
324-
}
325-
#endif
326-
tstate->frame = f->f_back;
327-
Py_DECREF(f);
328222
return res;
329223
}
330224

@@ -376,7 +270,7 @@ call_character_handler(xmlparseobject *self, const XML_Char *buffer, int len)
376270
PyTuple_SET_ITEM(args, 0, temp);
377271
/* temp is now a borrowed reference; consider it unused. */
378272
self->in_callback = 1;
379-
temp = call_with_frame(getcode(CharacterData, "CharacterData", __LINE__),
273+
temp = call_with_frame("CharacterData", __LINE__,
380274
self->handlers[CharacterData], args, self);
381275
/* temp is an owned reference again, or NULL */
382276
self->in_callback = 0;
@@ -508,7 +402,7 @@ my_StartElementHandler(void *userData,
508402
}
509403
/* Container is now a borrowed reference; ignore it. */
510404
self->in_callback = 1;
511-
rv = call_with_frame(getcode(StartElement, "StartElement", __LINE__),
405+
rv = call_with_frame("StartElement", __LINE__,
512406
self->handlers[StartElement], args, self);
513407
self->in_callback = 0;
514408
Py_DECREF(args);
@@ -537,7 +431,7 @@ my_##NAME##Handler PARAMS {\
537431
args = Py_BuildValue PARAM_FORMAT ;\
538432
if (!args) { flag_error(self); return RETURN;} \
539433
self->in_callback = 1; \
540-
rv = call_with_frame(getcode(NAME,#NAME,__LINE__), \
434+
rv = call_with_frame(#NAME,__LINE__, \
541435
self->handlers[NAME], args, self); \
542436
self->in_callback = 0; \
543437
Py_DECREF(args); \
@@ -669,7 +563,7 @@ my_ElementDeclHandler(void *userData,
669563
goto finally;
670564
}
671565
self->in_callback = 1;
672-
rv = call_with_frame(getcode(ElementDecl, "ElementDecl", __LINE__),
566+
rv = call_with_frame("ElementDecl", __LINE__,
673567
self->handlers[ElementDecl], args, self);
674568
self->in_callback = 0;
675569
if (rv == NULL) {

Python/traceback.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,39 @@ PyTraceBack_Here(PyFrameObject *frame)
142142
return 0;
143143
}
144144

145+
/* Insert a frame into the traceback for (funcname, filename, lineno). */
146+
void _PyTraceback_Add(char *funcname, char *filename, int lineno)
147+
{
148+
PyObject *globals = NULL;
149+
PyCodeObject *code = NULL;
150+
PyFrameObject *frame = NULL;
151+
PyObject *exception, *value, *tb;
152+
153+
/* Save and clear the current exception. Python functions must not be
154+
called with an exception set. Calling Python functions happens when
155+
the codec of the filesystem encoding is implemented in pure Python. */
156+
PyErr_Fetch(&exception, &value, &tb);
157+
158+
globals = PyDict_New();
159+
if (!globals)
160+
goto done;
161+
code = PyCode_NewEmpty(filename, funcname, lineno);
162+
if (!code)
163+
goto done;
164+
frame = PyFrame_New(PyThreadState_Get(), code, globals, NULL);
165+
if (!frame)
166+
goto done;
167+
frame->f_lineno = lineno;
168+
169+
PyErr_Restore(exception, value, tb);
170+
PyTraceBack_Here(frame);
171+
172+
done:
173+
Py_XDECREF(globals);
174+
Py_XDECREF(code);
175+
Py_XDECREF(frame);
176+
}
177+
145178
static PyObject *
146179
_Py_FindSourceFile(PyObject *filename, char* namebuf, size_t namelen, PyObject *io)
147180
{

0 commit comments

Comments
 (0)