Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions Doc/c-api/code.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,33 @@ bound into a function.

Return the number of free variables in *co*.

.. c:function:: PyCodeObject* PyCode_New(int argcount, int kwonlyargcount, 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)
.. c:function:: PyCodeObject* PyCode_New(int argcount, int kwonlyargcount, 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 *linetable, PyObject *exceptiontable)

Return a new code object. If you need a dummy code object to create a frame,
use :c:func:`PyCode_NewEmpty` instead. Calling :c:func:`PyCode_New` directly
can bind you to a precise Python version since the definition of the bytecode
changes often.
will bind you to a precise Python version since the definition of the bytecode
changes often. The many arguments of this function are inter-dependent in complex
ways, meaning that subtle changes to values are likely to result in incorrect
execution or VM crashes. Use this function only with extreme care.

.. c:function:: PyCodeObject* PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, 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)
.. versionchanged:: 3.11
Added ``exceptiontable`` parameter.

.. c:function:: PyCodeObject* PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, 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 *linetable, PyObject *exceptiontable)

Similar to :c:func:`PyCode_New`, but with an extra "posonlyargcount" for positional-only arguments.
The same caveats that apply to ``PyCode_New`` also apply to this function.

.. versionadded:: 3.8

.. versionchanged:: 3.11
Added ``exceptiontable`` parameter.

.. c:function:: PyCodeObject* 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
:func:`exec` or :func:`eval` the resulting code object.
function name, and first line number. The resulting code
object will raise an ``Exception`` if executed.

.. c:function:: int PyCode_Addr2Line(PyCodeObject *co, int byte_offset)

Expand Down
6 changes: 6 additions & 0 deletions Doc/whatsnew/3.11.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1159,6 +1159,12 @@ C API Changes
as its second parameter, instead of ``PyFrameObject*``.
See :pep:`523` for more details of how to use this function pointer type.

* :c:func:`PyCode_New` and :c:func:`PyCode_NewWithPosOnlyArgs` now take
an additional ``exception_table`` argument.
Using these functions should be avoided, if at all possible.
To get a custom code object: create a code object using the compiler,
then get a modified version with the ``replace`` method.

New Features
------------

Expand Down
3 changes: 3 additions & 0 deletions Lib/test/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ def test_newempty(self):
self.assertEqual(co.co_filename, "filename")
self.assertEqual(co.co_name, "funcname")
self.assertEqual(co.co_firstlineno, 15)
#Empty code object should raise, but not crash the VM
with self.assertRaises(Exception):
exec(co)

@cpython_only
def test_closure_injection(self):
Expand Down
16 changes: 15 additions & 1 deletion Objects/codeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -626,12 +626,20 @@ PyCode_New(int argcount, int kwonlyargcount,
exceptiontable);
}

static const char assert0[4] = {
LOAD_ASSERTION_ERROR,
0,
RAISE_VARARGS,
1
};

PyCodeObject *
PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno)
{
PyObject *nulltuple = NULL;
PyObject *filename_ob = NULL;
PyObject *funcname_ob = NULL;
PyObject *code_ob = NULL;
PyCodeObject *result = NULL;

nulltuple = PyTuple_New(0);
Expand All @@ -646,27 +654,33 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno)
if (filename_ob == NULL) {
goto failed;
}
code_ob = PyBytes_FromStringAndSize(assert0, 4);
if (code_ob == NULL) {
goto failed;
}

#define emptystring (PyObject *)&_Py_SINGLETON(bytes_empty)
struct _PyCodeConstructor con = {
.filename = filename_ob,
.name = funcname_ob,
.qualname = funcname_ob,
.code = emptystring,
.code = code_ob,
.firstlineno = firstlineno,
.linetable = emptystring,
.consts = nulltuple,
.names = nulltuple,
.localsplusnames = nulltuple,
.localspluskinds = emptystring,
.exceptiontable = emptystring,
.stacksize = 1,
};
result = _PyCode_New(&con);

failed:
Py_XDECREF(nulltuple);
Py_XDECREF(funcname_ob);
Py_XDECREF(filename_ob);
Py_XDECREF(code_ob);
return result;
}

Expand Down