diff options
-rw-r--r-- | Doc/c-api/code.rst | 50 | ||||
-rw-r--r-- | Doc/c-api/concrete.rst | 1 | ||||
-rw-r--r-- | Include/code.h | 5 | ||||
-rw-r--r-- | Lib/test/test_code.py | 16 | ||||
-rw-r--r-- | Misc/NEWS | 3 | ||||
-rw-r--r-- | Modules/_ctypes/callbacks.c | 35 | ||||
-rw-r--r-- | Modules/_testcapimodule.c | 16 | ||||
-rw-r--r-- | Modules/pyexpat.c | 43 | ||||
-rw-r--r-- | Objects/codeobject.c | 46 |
9 files changed, 139 insertions, 76 deletions
diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst new file mode 100644 index 0000000..c6ca8c5 --- /dev/null +++ b/Doc/c-api/code.rst @@ -0,0 +1,50 @@ +.. highlightlang:: c + +.. _codeobjects: + +Code Objects +------------ + +.. sectionauthor:: Jeffrey Yasskin <jyasskin@gmail.com> + + +.. index:: + object: code + +Code objects are a low-level detail of the CPython implementation. +Each one represents a chunk of executable code that hasn't yet been +bound into a function. + +.. ctype:: PyCodeObject + + The C structure of the objects used to describe code objects. The + fields of this type are subject to change at any time. + + +.. cvar:: PyTypeObject PyCode_Type + + This is an instance of :ctype:`PyTypeObject` representing the Python + :class:`code` type. + + +.. cfunction:: int PyCode_Check(PyObject *co) + + Return true if *co* is a :class:`code` object + +.. cfunction:: int PyCode_GetNumFree(PyObject *co) + + Return the number of free variables in *co*. + +.. cfunction:: PyCodeObject *PyCode_New(int argcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *lnotab) + + Return a new code object. If you need a dummy code object to + create a frame, use :cfunc:`PyCode_NewEmpty` instead. Calling + :cfunc:`PyCode_New` directly can bind you to a precise Python + version since the definition of the bytecode changes often. + + +.. cfunction:: int PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) + + Return a new empty code object with the specified filename, + function name, and first line number. It is illegal to + :keyword:`exec` or :func:`eval` the resulting code object. diff --git a/Doc/c-api/concrete.rst b/Doc/c-api/concrete.rst index 6958646..0595788 100644 --- a/Doc/c-api/concrete.rst +++ b/Doc/c-api/concrete.rst @@ -105,3 +105,4 @@ Other Objects gen.rst datetime.rst set.rst + code.rst diff --git a/Include/code.h b/Include/code.h index 8c00700..0167ad4 100644 --- a/Include/code.h +++ b/Include/code.h @@ -70,6 +70,11 @@ PyAPI_FUNC(PyCodeObject *) PyCode_New( int, int, int, int, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, int, PyObject *); /* same as struct above */ + +/* Creates a new empty code object with the specified source location. */ +PyAPI_FUNC(PyCodeObject *) +PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno); + PyAPI_FUNC(int) PyCode_Addr2Line(PyCodeObject *, int); /* for internal use only */ diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index b69223d..4a88e60 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -80,6 +80,9 @@ consts: ("'doc string'", 'None') """ +import unittest +import _testcapi + def consts(t): """Yield a doctest-safe sequence of object reprs.""" for elt in t: @@ -96,10 +99,21 @@ def dump(co): print "%s: %s" % (attr, getattr(co, "co_" + attr)) print "consts:", tuple(consts(co.co_consts)) + +class CodeTest(unittest.TestCase): + + def test_newempty(self): + co = _testcapi.code_newempty("filename", "funcname", 15) + self.assertEquals(co.co_filename, "filename") + self.assertEquals(co.co_name, "funcname") + self.assertEquals(co.co_firstlineno, 15) + + def test_main(verbose=None): - from test.test_support import run_doctest + from test.test_support import run_doctest, run_unittest from test import test_code run_doctest(test_code, verbose) + run_unittest(CodeTest) if __name__ == '__main__': @@ -927,6 +927,9 @@ Build C-API ----- +- Issue #5959: Add a PyCode_NewEmpty() function to create a new empty code + object at a specified file, function, and line number. + - Issue #1419652: Change the first argument to PyImport_AppendInittab() to ``const char *`` as the string is stored beyond the call. diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index 1720169..c7bfa21 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -99,40 +99,13 @@ PrintError(char *msg, ...) /* after code that pyrex generates */ void _ctypes_add_traceback(char *funcname, char *filename, int lineno) { - PyObject *py_srcfile = 0; - PyObject *py_funcname = 0; PyObject *py_globals = 0; - PyObject *empty_tuple = 0; - PyObject *empty_string = 0; PyCodeObject *py_code = 0; PyFrameObject *py_frame = 0; - - py_srcfile = PyString_FromString(filename); - if (!py_srcfile) goto bad; - py_funcname = PyString_FromString(funcname); - if (!py_funcname) goto bad; + py_globals = PyDict_New(); if (!py_globals) goto bad; - empty_tuple = PyTuple_New(0); - if (!empty_tuple) goto bad; - empty_string = PyString_FromString(""); - if (!empty_string) goto bad; - py_code = PyCode_New( - 0, /*int argcount,*/ - 0, /*int nlocals,*/ - 0, /*int stacksize,*/ - 0, /*int flags,*/ - empty_string, /*PyObject *code,*/ - empty_tuple, /*PyObject *consts,*/ - empty_tuple, /*PyObject *names,*/ - empty_tuple, /*PyObject *varnames,*/ - empty_tuple, /*PyObject *freevars,*/ - empty_tuple, /*PyObject *cellvars,*/ - py_srcfile, /*PyObject *filename,*/ - py_funcname, /*PyObject *name,*/ - lineno, /*int firstlineno,*/ - empty_string /*PyObject *lnotab*/ - ); + py_code = PyCode_NewEmpty(filename, funcname, lineno); if (!py_code) goto bad; py_frame = PyFrame_New( PyThreadState_Get(), /*PyThreadState *tstate,*/ @@ -145,10 +118,6 @@ void _ctypes_add_traceback(char *funcname, char *filename, int lineno) PyTraceBack_Here(py_frame); bad: Py_XDECREF(py_globals); - Py_XDECREF(py_srcfile); - Py_XDECREF(py_funcname); - Py_XDECREF(empty_tuple); - Py_XDECREF(empty_string); Py_XDECREF(py_code); Py_XDECREF(py_frame); } diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 0cccca3..fb5cf73 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -988,6 +988,21 @@ traceback_print(PyObject *self, PyObject *args) Py_RETURN_NONE; } +/* To test that the result of PyCode_NewEmpty has the right members. */ +static PyObject * +code_newempty(PyObject *self, PyObject *args) +{ + const char *filename; + const char *funcname; + int firstlineno; + + if (!PyArg_ParseTuple(args, "ssi:code_newempty", + &filename, &funcname, &firstlineno)) + return NULL; + + return (PyObject *)PyCode_NewEmpty(filename, funcname, firstlineno); +} + static PyMethodDef TestMethods[] = { {"raise_exception", raise_exception, METH_VARARGS}, {"test_config", (PyCFunction)test_config, METH_NOARGS}, @@ -1033,6 +1048,7 @@ static PyMethodDef TestMethods[] = { {"_pending_threadfunc", pending_threadfunc, METH_VARARGS}, #endif {"traceback_print", traceback_print, METH_VARARGS}, + {"code_newempty", code_newempty, METH_VARARGS}, {NULL, NULL} /* sentinel */ }; diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c index 01971b7..47ef186 100644 --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -261,52 +261,11 @@ flag_error(xmlparseobject *self) static PyCodeObject* getcode(enum HandlerTypes slot, char* func_name, int lineno) { - PyObject *code = NULL; - PyObject *name = NULL; - PyObject *nulltuple = NULL; - PyObject *filename = NULL; - if (handler_info[slot].tb_code == NULL) { - code = PyString_FromString(""); - if (code == NULL) - goto failed; - name = PyString_FromString(func_name); - if (name == NULL) - goto failed; - nulltuple = PyTuple_New(0); - if (nulltuple == NULL) - goto failed; - filename = PyString_FromString(__FILE__); handler_info[slot].tb_code = - PyCode_New(0, /* argcount */ - 0, /* nlocals */ - 0, /* stacksize */ - 0, /* flags */ - code, /* code */ - nulltuple, /* consts */ - nulltuple, /* names */ - nulltuple, /* varnames */ -#if PYTHON_API_VERSION >= 1010 - nulltuple, /* freevars */ - nulltuple, /* cellvars */ -#endif - filename, /* filename */ - name, /* name */ - lineno, /* firstlineno */ - code /* lnotab */ - ); - if (handler_info[slot].tb_code == NULL) - goto failed; - Py_DECREF(code); - Py_DECREF(nulltuple); - Py_DECREF(filename); - Py_DECREF(name); + PyCode_NewEmpty(__FILE__, func_name, lineno); } return handler_info[slot].tb_code; - failed: - Py_XDECREF(code); - Py_XDECREF(name); - return NULL; } #ifdef FIX_TRACE diff --git a/Objects/codeobject.c b/Objects/codeobject.c index e94b4cc..55f3fb8 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -107,6 +107,52 @@ PyCode_New(int argcount, int nlocals, int stacksize, int flags, return co; } +PyCodeObject * +PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) +{ + static PyObject *emptystring = NULL; + static PyObject *nulltuple = NULL; + PyObject *filename_ob = NULL; + PyObject *funcname_ob = NULL; + PyCodeObject *result = NULL; + if (emptystring == NULL) { + emptystring = PyString_FromString(""); + if (emptystring == NULL) + goto failed; + } + if (nulltuple == NULL) { + nulltuple = PyTuple_New(0); + if (nulltuple == NULL) + goto failed; + } + funcname_ob = PyString_FromString(funcname); + if (funcname_ob == NULL) + goto failed; + filename_ob = PyString_FromString(filename); + if (filename_ob == NULL) + goto failed; + + result = PyCode_New(0, /* argcount */ + 0, /* nlocals */ + 0, /* stacksize */ + 0, /* flags */ + emptystring, /* code */ + nulltuple, /* consts */ + nulltuple, /* names */ + nulltuple, /* varnames */ + nulltuple, /* freevars */ + nulltuple, /* cellvars */ + filename_ob, /* filename */ + funcname_ob, /* name */ + firstlineno, /* firstlineno */ + emptystring /* lnotab */ + ); + +failed: + Py_XDECREF(funcname_ob); + Py_XDECREF(filename_ob); + return result; +} #define OFF(x) offsetof(PyCodeObject, x) |