summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/c-api/code.rst50
-rw-r--r--Doc/c-api/concrete.rst1
-rw-r--r--Include/code.h5
-rw-r--r--Lib/test/test_code.py16
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/_ctypes/callbacks.c35
-rw-r--r--Modules/_testcapimodule.c16
-rw-r--r--Modules/pyexpat.c43
-rw-r--r--Objects/codeobject.c46
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__':
diff --git a/Misc/NEWS b/Misc/NEWS
index e097b43..ba721e7 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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)