Commit 0ddbf479 authored by Antoine Pitrou's avatar Antoine Pitrou

Issue #22462: Fix pyexpat's creation of a dummy frame to make it appear in exception tracebacks.

Initial patch by Mark Shannon.
parent b2fdafe3
...@@ -24,6 +24,7 @@ PyAPI_FUNC(int) PyTraceBack_Here(struct _frame *); ...@@ -24,6 +24,7 @@ PyAPI_FUNC(int) PyTraceBack_Here(struct _frame *);
PyAPI_FUNC(int) PyTraceBack_Print(PyObject *, PyObject *); PyAPI_FUNC(int) PyTraceBack_Print(PyObject *, PyObject *);
#ifndef Py_LIMITED_API #ifndef Py_LIMITED_API
PyAPI_FUNC(int) _Py_DisplaySourceLine(PyObject *, PyObject *, int, int); PyAPI_FUNC(int) _Py_DisplaySourceLine(PyObject *, PyObject *, int, int);
PyAPI_FUNC(void) _PyTraceback_Add(char *, char *, int);
#endif #endif
/* Reveal traceback type so we can typecheck traceback objects */ /* Reveal traceback type so we can typecheck traceback objects */
......
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
# handler, are obscure and unhelpful. # handler, are obscure and unhelpful.
from io import BytesIO from io import BytesIO
import os
import unittest import unittest
import traceback
from xml.parsers import expat from xml.parsers import expat
from xml.parsers.expat import errors from xml.parsers.expat import errors
...@@ -419,7 +421,11 @@ class HandlerExceptionTest(unittest.TestCase): ...@@ -419,7 +421,11 @@ class HandlerExceptionTest(unittest.TestCase):
def StartElementHandler(self, name, attrs): def StartElementHandler(self, name, attrs):
raise RuntimeError(name) raise RuntimeError(name)
def test(self): def check_traceback_entry(self, entry, filename, funcname):
self.assertEqual(os.path.basename(entry[0]), filename)
self.assertEqual(entry[2], funcname)
def test_exception(self):
parser = expat.ParserCreate() parser = expat.ParserCreate()
parser.StartElementHandler = self.StartElementHandler parser.StartElementHandler = self.StartElementHandler
try: try:
...@@ -429,6 +435,16 @@ class HandlerExceptionTest(unittest.TestCase): ...@@ -429,6 +435,16 @@ class HandlerExceptionTest(unittest.TestCase):
self.assertEqual(e.args[0], 'a', self.assertEqual(e.args[0], 'a',
"Expected RuntimeError for element 'a', but" + \ "Expected RuntimeError for element 'a', but" + \
" found %r" % e.args[0]) " found %r" % e.args[0])
# Check that the traceback contains the relevant line in pyexpat.c
entries = traceback.extract_tb(e.__traceback__)
self.assertEqual(len(entries), 3)
self.check_traceback_entry(entries[0],
"test_pyexpat.py", "test_exception")
self.check_traceback_entry(entries[1],
"pyexpat.c", "StartElement")
self.check_traceback_entry(entries[2],
"test_pyexpat.py", "StartElementHandler")
self.assertIn('call_with_frame("StartElement"', entries[1][3])
# Test Current* members: # Test Current* members:
......
...@@ -24,6 +24,9 @@ Core and Builtins ...@@ -24,6 +24,9 @@ Core and Builtins
Library Library
------- -------
- Issue #22462: Fix pyexpat's creation of a dummy frame to make it
appear in exception tracebacks.
- Issue #21173: Fix len() on a WeakKeyDictionary when .clear() was called - Issue #21173: Fix len() on a WeakKeyDictionary when .clear() was called
with an iterator alive. with an iterator alive.
......
...@@ -92,49 +92,6 @@ PrintError(char *msg, ...) ...@@ -92,49 +92,6 @@ PrintError(char *msg, ...)
} }
/* after code that pyrex generates */
void _ctypes_add_traceback(char *funcname, char *filename, int lineno)
{
PyObject *py_globals = 0;
PyCodeObject *py_code = 0;
PyFrameObject *py_frame = 0;
PyObject *exception, *value, *tb;
/* (Save and) Clear the current exception. Python functions must not be
called with an exception set. Calling Python functions happens when
the codec of the filesystem encoding is implemented in pure Python. */
PyErr_Fetch(&exception, &value, &tb);
py_globals = PyDict_New();
if (!py_globals)
goto bad;
py_code = PyCode_NewEmpty(filename, funcname, lineno);
if (!py_code)
goto bad;
py_frame = PyFrame_New(
PyThreadState_Get(), /*PyThreadState *tstate,*/
py_code, /*PyCodeObject *code,*/
py_globals, /*PyObject *globals,*/
0 /*PyObject *locals*/
);
if (!py_frame)
goto bad;
py_frame->f_lineno = lineno;
PyErr_Restore(exception, value, tb);
PyTraceBack_Here(py_frame);
Py_DECREF(py_globals);
Py_DECREF(py_code);
Py_DECREF(py_frame);
return;
bad:
Py_XDECREF(py_globals);
Py_XDECREF(py_code);
Py_XDECREF(py_frame);
}
#ifdef MS_WIN32 #ifdef MS_WIN32
/* /*
* We must call AddRef() on non-NULL COM pointers we receive as arguments * We must call AddRef() on non-NULL COM pointers we receive as arguments
...@@ -254,7 +211,7 @@ static void _CallPythonObject(void *mem, ...@@ -254,7 +211,7 @@ static void _CallPythonObject(void *mem,
} }
#define CHECK(what, x) \ #define CHECK(what, x) \
if (x == NULL) _ctypes_add_traceback(what, "_ctypes/callbacks.c", __LINE__ - 1), PyErr_Print() if (x == NULL) _PyTraceback_Add(what, "_ctypes/callbacks.c", __LINE__ - 1), PyErr_Print()
if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) { if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) {
error_object = _ctypes_get_errobj(&space); error_object = _ctypes_get_errobj(&space);
......
...@@ -919,7 +919,7 @@ static PyObject *GetResult(PyObject *restype, void *result, PyObject *checker) ...@@ -919,7 +919,7 @@ static PyObject *GetResult(PyObject *restype, void *result, PyObject *checker)
v = PyObject_CallFunctionObjArgs(checker, retval, NULL); v = PyObject_CallFunctionObjArgs(checker, retval, NULL);
if (v == NULL) if (v == NULL)
_ctypes_add_traceback("GetResult", "_ctypes/callproc.c", __LINE__-2); _PyTraceback_Add("GetResult", "_ctypes/callproc.c", __LINE__-2);
Py_DECREF(retval); Py_DECREF(retval);
return v; return v;
} }
......
...@@ -353,8 +353,6 @@ extern char *_ctypes_conversion_errors; ...@@ -353,8 +353,6 @@ extern char *_ctypes_conversion_errors;
extern void _ctypes_free_closure(void *); extern void _ctypes_free_closure(void *);
extern void *_ctypes_alloc_closure(void); extern void *_ctypes_alloc_closure(void);
extern void _ctypes_add_traceback(char *, char *, int);
extern PyObject *PyCData_FromBaseObj(PyObject *type, PyObject *base, Py_ssize_t index, char *adr); extern PyObject *PyCData_FromBaseObj(PyObject *type, PyObject *base, Py_ssize_t index, char *adr);
extern char *_ctypes_alloc_format_string(const char *prefix, const char *suffix); extern char *_ctypes_alloc_format_string(const char *prefix, const char *suffix);
extern char *_ctypes_alloc_format_string_with_shape(int ndim, extern char *_ctypes_alloc_format_string_with_shape(int ndim,
......
...@@ -8,8 +8,6 @@ ...@@ -8,8 +8,6 @@
#define XML_COMBINED_VERSION (10000*XML_MAJOR_VERSION+100*XML_MINOR_VERSION+XML_MICRO_VERSION) #define XML_COMBINED_VERSION (10000*XML_MAJOR_VERSION+100*XML_MINOR_VERSION+XML_MICRO_VERSION)
#define FIX_TRACE
static XML_Memory_Handling_Suite ExpatMemoryHandler = { static XML_Memory_Handling_Suite ExpatMemoryHandler = {
PyObject_Malloc, PyObject_Realloc, PyObject_Free}; PyObject_Malloc, PyObject_Realloc, PyObject_Free};
...@@ -210,121 +208,17 @@ flag_error(xmlparseobject *self) ...@@ -210,121 +208,17 @@ flag_error(xmlparseobject *self)
error_external_entity_ref_handler); error_external_entity_ref_handler);
} }
static PyCodeObject*
getcode(enum HandlerTypes slot, char* func_name, int lineno)
{
if (handler_info[slot].tb_code == NULL) {
handler_info[slot].tb_code =
PyCode_NewEmpty(__FILE__, func_name, lineno);
}
return handler_info[slot].tb_code;
}
#ifdef FIX_TRACE
static int
trace_frame(PyThreadState *tstate, PyFrameObject *f, int code, PyObject *val)
{
int result = 0;
if (!tstate->use_tracing || tstate->tracing)
return 0;
if (tstate->c_profilefunc != NULL) {
tstate->tracing++;
result = tstate->c_profilefunc(tstate->c_profileobj,
f, code , val);
tstate->use_tracing = ((tstate->c_tracefunc != NULL)
|| (tstate->c_profilefunc != NULL));
tstate->tracing--;
if (result)
return result;
}
if (tstate->c_tracefunc != NULL) {
tstate->tracing++;
result = tstate->c_tracefunc(tstate->c_traceobj,
f, code , val);
tstate->use_tracing = ((tstate->c_tracefunc != NULL)
|| (tstate->c_profilefunc != NULL));
tstate->tracing--;
}
return result;
}
static int
trace_frame_exc(PyThreadState *tstate, PyFrameObject *f)
{
PyObject *type, *value, *traceback, *arg;
int err;
if (tstate->c_tracefunc == NULL)
return 0;
PyErr_Fetch(&type, &value, &traceback);
if (value == NULL) {
value = Py_None;
Py_INCREF(value);
}
arg = PyTuple_Pack(3, type, value, traceback);
if (arg == NULL) {
PyErr_Restore(type, value, traceback);
return 0;
}
err = trace_frame(tstate, f, PyTrace_EXCEPTION, arg);
Py_DECREF(arg);
if (err == 0)
PyErr_Restore(type, value, traceback);
else {
Py_XDECREF(type);
Py_XDECREF(value);
Py_XDECREF(traceback);
}
return err;
}
#endif
static PyObject* static PyObject*
call_with_frame(PyCodeObject *c, PyObject* func, PyObject* args, call_with_frame(char *funcname, int lineno, PyObject* func, PyObject* args,
xmlparseobject *self) xmlparseobject *self)
{ {
PyThreadState *tstate = PyThreadState_GET(); PyObject *res;
PyFrameObject *f;
PyObject *res, *globals;
if (c == NULL)
return NULL;
globals = PyEval_GetGlobals();
if (globals == NULL) {
return NULL;
}
f = PyFrame_New(tstate, c, globals, NULL);
if (f == NULL)
return NULL;
tstate->frame = f;
#ifdef FIX_TRACE
if (trace_frame(tstate, f, PyTrace_CALL, Py_None) < 0) {
return NULL;
}
#endif
res = PyEval_CallObject(func, args); res = PyEval_CallObject(func, args);
if (res == NULL) { if (res == NULL) {
if (tstate->curexc_traceback == NULL) _PyTraceback_Add(funcname, __FILE__, lineno);
PyTraceBack_Here(f);
XML_StopParser(self->itself, XML_FALSE); XML_StopParser(self->itself, XML_FALSE);
#ifdef FIX_TRACE
if (trace_frame_exc(tstate, f) < 0) {
return NULL;
}
} }
else {
if (trace_frame(tstate, f, PyTrace_RETURN, res) < 0) {
Py_CLEAR(res);
}
}
#else
}
#endif
tstate->frame = f->f_back;
Py_DECREF(f);
return res; return res;
} }
...@@ -376,7 +270,7 @@ call_character_handler(xmlparseobject *self, const XML_Char *buffer, int len) ...@@ -376,7 +270,7 @@ call_character_handler(xmlparseobject *self, const XML_Char *buffer, int len)
PyTuple_SET_ITEM(args, 0, temp); PyTuple_SET_ITEM(args, 0, temp);
/* temp is now a borrowed reference; consider it unused. */ /* temp is now a borrowed reference; consider it unused. */
self->in_callback = 1; self->in_callback = 1;
temp = call_with_frame(getcode(CharacterData, "CharacterData", __LINE__), temp = call_with_frame("CharacterData", __LINE__,
self->handlers[CharacterData], args, self); self->handlers[CharacterData], args, self);
/* temp is an owned reference again, or NULL */ /* temp is an owned reference again, or NULL */
self->in_callback = 0; self->in_callback = 0;
...@@ -508,7 +402,7 @@ my_StartElementHandler(void *userData, ...@@ -508,7 +402,7 @@ my_StartElementHandler(void *userData,
} }
/* Container is now a borrowed reference; ignore it. */ /* Container is now a borrowed reference; ignore it. */
self->in_callback = 1; self->in_callback = 1;
rv = call_with_frame(getcode(StartElement, "StartElement", __LINE__), rv = call_with_frame("StartElement", __LINE__,
self->handlers[StartElement], args, self); self->handlers[StartElement], args, self);
self->in_callback = 0; self->in_callback = 0;
Py_DECREF(args); Py_DECREF(args);
...@@ -537,7 +431,7 @@ my_##NAME##Handler PARAMS {\ ...@@ -537,7 +431,7 @@ my_##NAME##Handler PARAMS {\
args = Py_BuildValue PARAM_FORMAT ;\ args = Py_BuildValue PARAM_FORMAT ;\
if (!args) { flag_error(self); return RETURN;} \ if (!args) { flag_error(self); return RETURN;} \
self->in_callback = 1; \ self->in_callback = 1; \
rv = call_with_frame(getcode(NAME,#NAME,__LINE__), \ rv = call_with_frame(#NAME,__LINE__, \
self->handlers[NAME], args, self); \ self->handlers[NAME], args, self); \
self->in_callback = 0; \ self->in_callback = 0; \
Py_DECREF(args); \ Py_DECREF(args); \
...@@ -669,7 +563,7 @@ my_ElementDeclHandler(void *userData, ...@@ -669,7 +563,7 @@ my_ElementDeclHandler(void *userData,
goto finally; goto finally;
} }
self->in_callback = 1; self->in_callback = 1;
rv = call_with_frame(getcode(ElementDecl, "ElementDecl", __LINE__), rv = call_with_frame("ElementDecl", __LINE__,
self->handlers[ElementDecl], args, self); self->handlers[ElementDecl], args, self);
self->in_callback = 0; self->in_callback = 0;
if (rv == NULL) { if (rv == NULL) {
......
...@@ -142,6 +142,39 @@ PyTraceBack_Here(PyFrameObject *frame) ...@@ -142,6 +142,39 @@ PyTraceBack_Here(PyFrameObject *frame)
return 0; return 0;
} }
/* Insert a frame into the traceback for (funcname, filename, lineno). */
void _PyTraceback_Add(char *funcname, char *filename, int lineno)
{
PyObject *globals = NULL;
PyCodeObject *code = NULL;
PyFrameObject *frame = NULL;
PyObject *exception, *value, *tb;
/* Save and clear the current exception. Python functions must not be
called with an exception set. Calling Python functions happens when
the codec of the filesystem encoding is implemented in pure Python. */
PyErr_Fetch(&exception, &value, &tb);
globals = PyDict_New();
if (!globals)
goto done;
code = PyCode_NewEmpty(filename, funcname, lineno);
if (!code)
goto done;
frame = PyFrame_New(PyThreadState_Get(), code, globals, NULL);
if (!frame)
goto done;
frame->f_lineno = lineno;
PyErr_Restore(exception, value, tb);
PyTraceBack_Here(frame);
done:
Py_XDECREF(globals);
Py_XDECREF(code);
Py_XDECREF(frame);
}
static PyObject * static PyObject *
_Py_FindSourceFile(PyObject *filename, char* namebuf, size_t namelen, PyObject *io) _Py_FindSourceFile(PyObject *filename, char* namebuf, size_t namelen, PyObject *io)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment