mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	Issue #22462: Fix pyexpat's creation of a dummy frame to make it appear in exception tracebacks.
Initial patch by Mark Shannon.
This commit is contained in:
		
							parent
							
								
									b2fdafe3d2
								
							
						
					
					
						commit
						0ddbf4795f
					
				
					 8 changed files with 63 additions and 161 deletions
				
			
		| 
						 | 
				
			
			@ -24,6 +24,7 @@ PyAPI_FUNC(int) PyTraceBack_Here(struct _frame *);
 | 
			
		|||
PyAPI_FUNC(int) PyTraceBack_Print(PyObject *, PyObject *);
 | 
			
		||||
#ifndef Py_LIMITED_API
 | 
			
		||||
PyAPI_FUNC(int) _Py_DisplaySourceLine(PyObject *, PyObject *, int, int);
 | 
			
		||||
PyAPI_FUNC(void) _PyTraceback_Add(char *, char *, int);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Reveal traceback type so we can typecheck traceback objects */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,9 @@
 | 
			
		|||
# handler, are obscure and unhelpful.
 | 
			
		||||
 | 
			
		||||
from io import BytesIO
 | 
			
		||||
import os
 | 
			
		||||
import unittest
 | 
			
		||||
import traceback
 | 
			
		||||
 | 
			
		||||
from xml.parsers import expat
 | 
			
		||||
from xml.parsers.expat import errors
 | 
			
		||||
| 
						 | 
				
			
			@ -419,7 +421,11 @@ class HandlerExceptionTest(unittest.TestCase):
 | 
			
		|||
    def StartElementHandler(self, name, attrs):
 | 
			
		||||
        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.StartElementHandler = self.StartElementHandler
 | 
			
		||||
        try:
 | 
			
		||||
| 
						 | 
				
			
			@ -429,6 +435,16 @@ def test(self):
 | 
			
		|||
            self.assertEqual(e.args[0], 'a',
 | 
			
		||||
                             "Expected RuntimeError for element 'a', but" + \
 | 
			
		||||
                             " 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:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,9 @@ Core and Builtins
 | 
			
		|||
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
 | 
			
		||||
  with an iterator alive.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
/*
 | 
			
		||||
 * We must call AddRef() on non-NULL COM pointers we receive as arguments
 | 
			
		||||
| 
						 | 
				
			
			@ -254,7 +211,7 @@ static void _CallPythonObject(void *mem,
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
#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)) {
 | 
			
		||||
        error_object = _ctypes_get_errobj(&space);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -919,7 +919,7 @@ static PyObject *GetResult(PyObject *restype, void *result, PyObject *checker)
 | 
			
		|||
 | 
			
		||||
    v = PyObject_CallFunctionObjArgs(checker, retval, NULL);
 | 
			
		||||
    if (v == NULL)
 | 
			
		||||
        _ctypes_add_traceback("GetResult", "_ctypes/callproc.c", __LINE__-2);
 | 
			
		||||
        _PyTraceback_Add("GetResult", "_ctypes/callproc.c", __LINE__-2);
 | 
			
		||||
    Py_DECREF(retval);
 | 
			
		||||
    return v;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -353,8 +353,6 @@ extern char *_ctypes_conversion_errors;
 | 
			
		|||
extern void _ctypes_free_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 char *_ctypes_alloc_format_string(const char *prefix, const char *suffix);
 | 
			
		||||
extern char *_ctypes_alloc_format_string_with_shape(int ndim,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,8 +8,6 @@
 | 
			
		|||
 | 
			
		||||
#define XML_COMBINED_VERSION (10000*XML_MAJOR_VERSION+100*XML_MINOR_VERSION+XML_MICRO_VERSION)
 | 
			
		||||
 | 
			
		||||
#define FIX_TRACE
 | 
			
		||||
 | 
			
		||||
static XML_Memory_Handling_Suite ExpatMemoryHandler = {
 | 
			
		||||
    PyObject_Malloc, PyObject_Realloc, PyObject_Free};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -210,121 +208,17 @@ flag_error(xmlparseobject *self)
 | 
			
		|||
                                    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*
 | 
			
		||||
call_with_frame(PyCodeObject *c, PyObject* func, PyObject* args,
 | 
			
		||||
call_with_frame(char *funcname, int lineno, PyObject* func, PyObject* args,
 | 
			
		||||
                xmlparseobject *self)
 | 
			
		||||
{
 | 
			
		||||
    PyThreadState *tstate = PyThreadState_GET();
 | 
			
		||||
    PyFrameObject *f;
 | 
			
		||||
    PyObject *res, *globals;
 | 
			
		||||
    PyObject *res;
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
    if (res == NULL) {
 | 
			
		||||
        if (tstate->curexc_traceback == NULL)
 | 
			
		||||
            PyTraceBack_Here(f);
 | 
			
		||||
        _PyTraceback_Add(funcname, __FILE__, lineno);
 | 
			
		||||
        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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -376,7 +270,7 @@ call_character_handler(xmlparseobject *self, const XML_Char *buffer, int len)
 | 
			
		|||
    PyTuple_SET_ITEM(args, 0, temp);
 | 
			
		||||
    /* temp is now a borrowed reference; consider it unused. */
 | 
			
		||||
    self->in_callback = 1;
 | 
			
		||||
    temp = call_with_frame(getcode(CharacterData, "CharacterData", __LINE__),
 | 
			
		||||
    temp = call_with_frame("CharacterData", __LINE__,
 | 
			
		||||
                           self->handlers[CharacterData], args, self);
 | 
			
		||||
    /* temp is an owned reference again, or NULL */
 | 
			
		||||
    self->in_callback = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -508,7 +402,7 @@ my_StartElementHandler(void *userData,
 | 
			
		|||
        }
 | 
			
		||||
        /* Container is now a borrowed reference; ignore it. */
 | 
			
		||||
        self->in_callback = 1;
 | 
			
		||||
        rv = call_with_frame(getcode(StartElement, "StartElement", __LINE__),
 | 
			
		||||
        rv = call_with_frame("StartElement", __LINE__,
 | 
			
		||||
                             self->handlers[StartElement], args, self);
 | 
			
		||||
        self->in_callback = 0;
 | 
			
		||||
        Py_DECREF(args);
 | 
			
		||||
| 
						 | 
				
			
			@ -537,7 +431,7 @@ my_##NAME##Handler PARAMS {\
 | 
			
		|||
        args = Py_BuildValue PARAM_FORMAT ;\
 | 
			
		||||
        if (!args) { flag_error(self); return RETURN;} \
 | 
			
		||||
        self->in_callback = 1; \
 | 
			
		||||
        rv = call_with_frame(getcode(NAME,#NAME,__LINE__), \
 | 
			
		||||
        rv = call_with_frame(#NAME,__LINE__, \
 | 
			
		||||
                             self->handlers[NAME], args, self); \
 | 
			
		||||
        self->in_callback = 0; \
 | 
			
		||||
        Py_DECREF(args); \
 | 
			
		||||
| 
						 | 
				
			
			@ -669,7 +563,7 @@ my_ElementDeclHandler(void *userData,
 | 
			
		|||
            goto finally;
 | 
			
		||||
        }
 | 
			
		||||
        self->in_callback = 1;
 | 
			
		||||
        rv = call_with_frame(getcode(ElementDecl, "ElementDecl", __LINE__),
 | 
			
		||||
        rv = call_with_frame("ElementDecl", __LINE__,
 | 
			
		||||
                             self->handlers[ElementDecl], args, self);
 | 
			
		||||
        self->in_callback = 0;
 | 
			
		||||
        if (rv == NULL) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -142,6 +142,39 @@ PyTraceBack_Here(PyFrameObject *frame)
 | 
			
		|||
    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 *
 | 
			
		||||
_Py_FindSourceFile(PyObject *filename, char* namebuf, size_t namelen, PyObject *io)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue