From d6c33fbd346765c6a8654dccacb2338006bf2b47 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 29 Jan 2021 13:24:55 +0000 Subject: bpo-42990: Introduce 'frame constructor' struct to simplify API for PyEval_CodeEval and friends (GH-24298) * Introduce 'frame constructor' to simplify API for frame creation * Embed struct using a macro to conform to PEP 7 --- Include/cpython/frameobject.h | 4 +- Include/funcobject.h | 26 +++++--- Include/internal/pycore_ceval.h | 7 +-- Lib/test/test_sys.py | 2 +- Objects/call.c | 33 +++------- Objects/frameobject.c | 85 ++++++++++---------------- Objects/funcobject.c | 13 +++- Python/ceval.c | 130 +++++++++++++++++++++------------------- 8 files changed, 144 insertions(+), 156 deletions(-) diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h index 2817061..f162e24 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -72,7 +72,7 @@ PyAPI_FUNC(PyFrameObject *) PyFrame_New(PyThreadState *, PyCodeObject *, /* only internal use */ PyFrameObject* _PyFrame_New_NoTrack(PyThreadState *, PyCodeObject *, - PyObject *, PyObject *); + PyObject *, PyObject *, PyObject *); /* The rest of the interface is specific for frame objects */ @@ -92,3 +92,5 @@ PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *); PyAPI_FUNC(void) _PyFrame_DebugMallocStats(FILE *out); PyAPI_FUNC(PyFrameObject *) PyFrame_GetBack(PyFrameObject *frame); + +PyObject *_PyEval_BuiltinsFromGlobals(PyObject *globals); diff --git a/Include/funcobject.h b/Include/funcobject.h index c5cc9d2..d7acd18 100644 --- a/Include/funcobject.h +++ b/Include/funcobject.h @@ -7,6 +7,21 @@ extern "C" { #endif + +#define COMMON_FIELDS(PREFIX) \ + PyObject *PREFIX ## globals; \ + PyObject *PREFIX ## builtins; \ + PyObject *PREFIX ## name; \ + PyObject *PREFIX ## qualname; \ + PyObject *PREFIX ## code; /* A code object, the __code__ attribute */ \ + PyObject *PREFIX ## defaults; /* NULL or a tuple */ \ + PyObject *PREFIX ## kwdefaults; /* NULL or a dict */ \ + PyObject *PREFIX ## closure; /* NULL or a tuple of cell objects */ + +typedef struct { + COMMON_FIELDS(fc_) +} PyFrameConstructor; + /* Function objects and code objects should not be confused with each other: * * Function objects are created by the execution of the 'def' statement. @@ -20,18 +35,12 @@ extern "C" { typedef struct { PyObject_HEAD - PyObject *func_code; /* A code object, the __code__ attribute */ - PyObject *func_globals; /* A dictionary (other mappings won't do) */ - PyObject *func_defaults; /* NULL or a tuple */ - PyObject *func_kwdefaults; /* NULL or a dict */ - PyObject *func_closure; /* NULL or a tuple of cell objects */ + COMMON_FIELDS(func_) PyObject *func_doc; /* The __doc__ attribute, can be anything */ - PyObject *func_name; /* The __name__ attribute, a string object */ PyObject *func_dict; /* The __dict__ attribute, a dict or NULL */ PyObject *func_weakreflist; /* List of weak references */ PyObject *func_module; /* The __module__ attribute, can be anything */ PyObject *func_annotations; /* Annotations, a dict or NULL */ - PyObject *func_qualname; /* The qualified name */ vectorcallfunc vectorcall; /* Invariant: @@ -84,6 +93,9 @@ PyAPI_FUNC(PyObject *) _PyFunction_Vectorcall( #define PyFunction_GET_ANNOTATIONS(func) \ (((PyFunctionObject *)func) -> func_annotations) +#define PyFunction_AS_FRAME_CONSTRUCTOR(func) \ + ((PyFrameConstructor *)&((PyFunctionObject *)(func))->func_globals) + /* The classmethod and staticmethod types lives here, too */ PyAPI_DATA(PyTypeObject) PyClassMethod_Type; PyAPI_DATA(PyTypeObject) PyStaticMethod_Type; diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index 38fd681..a9da8b8 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -42,13 +42,10 @@ _PyEval_EvalFrame(PyThreadState *tstate, PyFrameObject *f, int throwflag) extern PyObject *_PyEval_EvalCode( PyThreadState *tstate, - PyObject *_co, PyObject *globals, PyObject *locals, + PyFrameConstructor *desc, PyObject *locals, PyObject *const *args, Py_ssize_t argcount, PyObject *const *kwnames, PyObject *const *kwargs, - Py_ssize_t kwcount, int kwstep, - PyObject *const *defs, Py_ssize_t defcount, - PyObject *kwdefs, PyObject *closure, - PyObject *name, PyObject *qualname); + Py_ssize_t kwcount, int kwstep); #ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS extern int _PyEval_ThreadsInitialized(PyInterpreterState *interp); diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index c4e0535..fca05e6 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1280,7 +1280,7 @@ class SizeofTest(unittest.TestCase): check(x, vsize('4Pi2c4P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P')) # function def func(): pass - check(func, size('13P')) + check(func, size('14P')) class c(): @staticmethod def foo(): diff --git a/Objects/call.c b/Objects/call.c index 1fb85ef..7972693 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -331,16 +331,16 @@ PyCFunction_Call(PyObject *callable, PyObject *args, PyObject *kwargs) static PyObject* _Py_HOT_FUNCTION function_code_fastcall(PyThreadState *tstate, PyCodeObject *co, PyObject *const *args, Py_ssize_t nargs, - PyObject *globals) + PyFunctionObject *func) { assert(tstate != NULL); - assert(globals != NULL); + assert(func != NULL); /* XXX Perhaps we should create a specialized _PyFrame_New_NoTrack() that doesn't take locals, but does take builtins without sanity checking them. */ - PyFrameObject *f = _PyFrame_New_NoTrack(tstate, co, globals, NULL); + PyFrameObject *f = _PyFrame_New_NoTrack(tstate, co, func->func_globals, func->func_builtins, NULL); if (f == NULL) { return NULL; } @@ -381,14 +381,13 @@ _PyFunction_Vectorcall(PyObject *func, PyObject* const* stack, PyThreadState *tstate = _PyThreadState_GET(); PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func); - PyObject *globals = PyFunction_GET_GLOBALS(func); PyObject *argdefs = PyFunction_GET_DEFAULTS(func); if (co->co_kwonlyargcount == 0 && nkwargs == 0 && (co->co_flags & ~PyCF_MASK) == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { if (argdefs == NULL && co->co_argcount == nargs) { - return function_code_fastcall(tstate, co, stack, nargs, globals); + return function_code_fastcall(tstate, co, stack, nargs, (PyFunctionObject *)func); } else if (nargs == 0 && argdefs != NULL && co->co_argcount == PyTuple_GET_SIZE(argdefs)) { @@ -397,34 +396,16 @@ _PyFunction_Vectorcall(PyObject *func, PyObject* const* stack, stack = _PyTuple_ITEMS(argdefs); return function_code_fastcall(tstate, co, stack, PyTuple_GET_SIZE(argdefs), - globals); + (PyFunctionObject *)func); } } - PyObject *kwdefs = PyFunction_GET_KW_DEFAULTS(func); - PyObject *closure = PyFunction_GET_CLOSURE(func); - PyObject *name = ((PyFunctionObject *)func) -> func_name; - PyObject *qualname = ((PyFunctionObject *)func) -> func_qualname; - - PyObject **d; - Py_ssize_t nd; - if (argdefs != NULL) { - d = _PyTuple_ITEMS(argdefs); - nd = PyTuple_GET_SIZE(argdefs); - assert(nd <= INT_MAX); - } - else { - d = NULL; - nd = 0; - } return _PyEval_EvalCode(tstate, - (PyObject*)co, globals, (PyObject *)NULL, + PyFunction_AS_FRAME_CONSTRUCTOR(func), (PyObject *)NULL, stack, nargs, nkwargs ? _PyTuple_ITEMS(kwnames) : NULL, stack + nargs, - nkwargs, 1, - d, (int)nd, kwdefs, - closure, name, qualname); + nkwargs, 1); } diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 4c5eaa2..45a275b 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -22,7 +22,6 @@ static PyMemberDef frame_memberlist[] = { {NULL} /* Sentinel */ }; - static struct _Py_frame_state * get_frame_state(void) { @@ -816,54 +815,12 @@ frame_alloc(PyCodeObject *code) } -static inline PyObject * -frame_get_builtins(PyFrameObject *back, PyObject *globals) -{ - PyObject *builtins; - - if (back != NULL && back->f_globals == globals) { - /* If we share the globals, we share the builtins. - Save a lookup and a call. */ - builtins = back->f_builtins; - assert(builtins != NULL); - Py_INCREF(builtins); - return builtins; - } - - builtins = _PyDict_GetItemIdWithError(globals, &PyId___builtins__); - if (builtins != NULL && PyModule_Check(builtins)) { - builtins = PyModule_GetDict(builtins); - assert(builtins != NULL); - } - if (builtins != NULL) { - Py_INCREF(builtins); - return builtins; - } - - if (PyErr_Occurred()) { - return NULL; - } - - /* No builtins! Make up a minimal one. - Give them 'None', at least. */ - builtins = PyDict_New(); - if (builtins == NULL) { - return NULL; - } - if (PyDict_SetItemString(builtins, "None", Py_None) < 0) { - Py_DECREF(builtins); - return NULL; - } - return builtins; -} - - PyFrameObject* _Py_HOT_FUNCTION _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code, - PyObject *globals, PyObject *locals) + PyObject *globals, PyObject *builtins, PyObject *locals) { #ifdef Py_DEBUG - if (code == NULL || globals == NULL || !PyDict_Check(globals) || + if (code == NULL || globals == NULL || builtins == NULL || (locals != NULL && !PyMapping_Check(locals))) { PyErr_BadInternalCall(); return NULL; @@ -871,18 +828,14 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code, #endif PyFrameObject *back = tstate->frame; - PyObject *builtins = frame_get_builtins(back, globals); - if (builtins == NULL) { - return NULL; - } PyFrameObject *f = frame_alloc(code); if (f == NULL) { - Py_DECREF(builtins); return NULL; } f->f_stackdepth = 0; + Py_INCREF(builtins); f->f_builtins = builtins; Py_XINCREF(back); f->f_back = back; @@ -902,8 +855,9 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code, f->f_locals = locals; } else { - if (locals == NULL) + if (locals == NULL) { locals = globals; + } Py_INCREF(locals); f->f_locals = locals; } @@ -925,7 +879,9 @@ PyFrameObject* PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, PyObject *locals) { - PyFrameObject *f = _PyFrame_New_NoTrack(tstate, code, globals, locals); + PyObject *builtins = _PyEval_BuiltinsFromGlobals(globals); + PyFrameObject *f = _PyFrame_New_NoTrack(tstate, code, globals, builtins, locals); + Py_DECREF(builtins); if (f) _PyObject_GC_TRACK(f); return f; @@ -1223,3 +1179,28 @@ PyFrame_GetBack(PyFrameObject *frame) Py_XINCREF(back); return back; } + +PyObject *_PyEval_BuiltinsFromGlobals(PyObject *globals) { + PyObject *builtins = _PyDict_GetItemIdWithError(globals, &PyId___builtins__); + if (builtins) { + if (PyModule_Check(builtins)) { + builtins = PyModule_GetDict(builtins); + assert(builtins != NULL); + } + } + if (builtins == NULL) { + if (PyErr_Occurred()) { + return NULL; + } + /* No builtins! Make up a minimal one + Give them 'None', at least. */ + builtins = PyDict_New(); + if (builtins == NULL || + PyDict_SetItemString( + builtins, "None", Py_None) < 0) + return NULL; + } + else + Py_INCREF(builtins); + return builtins; +} diff --git a/Objects/funcobject.c b/Objects/funcobject.c index e7961b3..f839d7b 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -3,6 +3,7 @@ #include "Python.h" #include "pycore_object.h" +#include "frameobject.h" #include "code.h" #include "structmember.h" // PyMemberDef @@ -40,8 +41,14 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname op->func_weakreflist = NULL; Py_INCREF(code); op->func_code = code; + assert(globals != NULL); Py_INCREF(globals); op->func_globals = globals; + PyObject *builtins = _PyEval_BuiltinsFromGlobals(globals); + if (builtins == NULL) { + return NULL; + } + op->func_builtins = builtins; op->func_name = ((PyCodeObject *)code)->co_name; Py_INCREF(op->func_name); op->func_defaults = NULL; /* No default arguments */ @@ -592,15 +599,16 @@ func_clear(PyFunctionObject *op) { Py_CLEAR(op->func_code); Py_CLEAR(op->func_globals); - Py_CLEAR(op->func_module); + Py_CLEAR(op->func_builtins); Py_CLEAR(op->func_name); + Py_CLEAR(op->func_qualname); + Py_CLEAR(op->func_module); Py_CLEAR(op->func_defaults); Py_CLEAR(op->func_kwdefaults); Py_CLEAR(op->func_doc); Py_CLEAR(op->func_dict); Py_CLEAR(op->func_closure); Py_CLEAR(op->func_annotations); - Py_CLEAR(op->func_qualname); return 0; } @@ -627,6 +635,7 @@ func_traverse(PyFunctionObject *f, visitproc visit, void *arg) { Py_VISIT(f->func_code); Py_VISIT(f->func_globals); + Py_VISIT(f->func_builtins); Py_VISIT(f->func_module); Py_VISIT(f->func_defaults); Py_VISIT(f->func_kwdefaults); diff --git a/Python/ceval.c b/Python/ceval.c index 5e2a160..be9ea24 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3888,7 +3888,7 @@ main_loop: if (oparg & 0x08) { assert(PyTuple_CheckExact(TOP())); - func ->func_closure = POP(); + func->func_closure = POP(); } if (oparg & 0x04) { assert(PyTuple_CheckExact(TOP())); @@ -4233,7 +4233,7 @@ missing_arguments(PyThreadState *tstate, PyCodeObject *co, static void too_many_positional(PyThreadState *tstate, PyCodeObject *co, - Py_ssize_t given, Py_ssize_t defcount, + Py_ssize_t given, PyObject *defaults, PyObject **fastlocals, PyObject *qualname) { int plural; @@ -4249,6 +4249,7 @@ too_many_positional(PyThreadState *tstate, PyCodeObject *co, kwonly_given++; } } + Py_ssize_t defcount = defaults == NULL ? 0 : PyTuple_GET_SIZE(defaults); if (defcount) { Py_ssize_t atleast = co_argcount - defcount; plural = 1; @@ -4356,41 +4357,20 @@ fail: PyObject * _PyEval_EvalCode(PyThreadState *tstate, - PyObject *_co, PyObject *globals, PyObject *locals, + PyFrameConstructor *con, PyObject *locals, PyObject *const *args, Py_ssize_t argcount, PyObject *const *kwnames, PyObject *const *kwargs, - Py_ssize_t kwcount, int kwstep, - PyObject *const *defs, Py_ssize_t defcount, - PyObject *kwdefs, PyObject *closure, - PyObject *name, PyObject *qualname) + Py_ssize_t kwcount, int kwstep) { assert(is_tstate_valid(tstate)); - PyCodeObject *co = (PyCodeObject*)_co; - - if (!name) { - name = co->co_name; - } - assert(name != NULL); - assert(PyUnicode_Check(name)); - - if (!qualname) { - qualname = name; - } - assert(qualname != NULL); - assert(PyUnicode_Check(qualname)); - + PyCodeObject *co = (PyCodeObject*)con->fc_code; + assert(con->fc_defaults == NULL || PyTuple_CheckExact(con->fc_defaults)); PyObject *retval = NULL; const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount; - if (globals == NULL) { - _PyErr_SetString(tstate, PyExc_SystemError, - "PyEval_EvalCodeEx: NULL globals"); - return NULL; - } - /* Create the frame */ - PyFrameObject *f = _PyFrame_New_NoTrack(tstate, co, globals, locals); + PyFrameObject *f = _PyFrame_New_NoTrack(tstate, co, con->fc_globals, con->fc_builtins, locals); if (f == NULL) { return NULL; } @@ -4448,7 +4428,7 @@ _PyEval_EvalCode(PyThreadState *tstate, if (keyword == NULL || !PyUnicode_Check(keyword)) { _PyErr_Format(tstate, PyExc_TypeError, "%U() keywords must be strings", - qualname); + con->fc_qualname); goto fail; } @@ -4480,14 +4460,14 @@ _PyEval_EvalCode(PyThreadState *tstate, if (co->co_posonlyargcount && positional_only_passed_as_keyword(tstate, co, kwcount, kwnames, - qualname)) + con->fc_qualname)) { goto fail; } _PyErr_Format(tstate, PyExc_TypeError, "%U() got an unexpected keyword argument '%S'", - qualname, keyword); + con->fc_qualname, keyword); goto fail; } @@ -4500,7 +4480,7 @@ _PyEval_EvalCode(PyThreadState *tstate, if (GETLOCAL(j) != NULL) { _PyErr_Format(tstate, PyExc_TypeError, "%U() got multiple values for argument '%S'", - qualname, keyword); + con->fc_qualname, keyword); goto fail; } Py_INCREF(value); @@ -4509,13 +4489,14 @@ _PyEval_EvalCode(PyThreadState *tstate, /* Check the number of positional arguments */ if ((argcount > co->co_argcount) && !(co->co_flags & CO_VARARGS)) { - too_many_positional(tstate, co, argcount, defcount, fastlocals, - qualname); + too_many_positional(tstate, co, argcount, con->fc_defaults, fastlocals, + con->fc_qualname); goto fail; } /* Add missing positional arguments (copy default values from defs) */ if (argcount < co->co_argcount) { + Py_ssize_t defcount = con->fc_defaults == NULL ? 0 : PyTuple_GET_SIZE(con->fc_defaults); Py_ssize_t m = co->co_argcount - defcount; Py_ssize_t missing = 0; for (i = argcount; i < m; i++) { @@ -4525,18 +4506,21 @@ _PyEval_EvalCode(PyThreadState *tstate, } if (missing) { missing_arguments(tstate, co, missing, defcount, fastlocals, - qualname); + con->fc_qualname); goto fail; } if (n > m) i = n - m; else i = 0; - for (; i < defcount; i++) { - if (GETLOCAL(m+i) == NULL) { - PyObject *def = defs[i]; - Py_INCREF(def); - SETLOCAL(m+i, def); + if (defcount) { + PyObject **defs = &PyTuple_GET_ITEM(con->fc_defaults, 0); + for (; i < defcount; i++) { + if (GETLOCAL(m+i) == NULL) { + PyObject *def = defs[i]; + Py_INCREF(def); + SETLOCAL(m+i, def); + } } } } @@ -4548,8 +4532,8 @@ _PyEval_EvalCode(PyThreadState *tstate, if (GETLOCAL(i) != NULL) continue; PyObject *varname = PyTuple_GET_ITEM(co->co_varnames, i); - if (kwdefs != NULL) { - PyObject *def = PyDict_GetItemWithError(kwdefs, varname); + if (con->fc_kwdefaults != NULL) { + PyObject *def = PyDict_GetItemWithError(con->fc_kwdefaults, varname); if (def) { Py_INCREF(def); SETLOCAL(i, def); @@ -4563,7 +4547,7 @@ _PyEval_EvalCode(PyThreadState *tstate, } if (missing) { missing_arguments(tstate, co, missing, -1, fastlocals, - qualname); + con->fc_qualname); goto fail; } } @@ -4590,7 +4574,7 @@ _PyEval_EvalCode(PyThreadState *tstate, /* Copy closure variables to free variables */ for (i = 0; i < PyTuple_GET_SIZE(co->co_freevars); ++i) { - PyObject *o = PyTuple_GET_ITEM(closure, i); + PyObject *o = PyTuple_GET_ITEM(con->fc_closure, i); Py_INCREF(o); freevars[PyTuple_GET_SIZE(co->co_cellvars) + i] = o; } @@ -4607,11 +4591,11 @@ _PyEval_EvalCode(PyThreadState *tstate, /* Create a new generator that owns the ready to run frame * and return that as the value. */ if (is_coro) { - gen = PyCoro_New(f, name, qualname); + gen = PyCoro_New(f, con->fc_name, con->fc_qualname); } else if (co->co_flags & CO_ASYNC_GENERATOR) { - gen = PyAsyncGen_New(f, name, qualname); + gen = PyAsyncGen_New(f, con->fc_name, con->fc_qualname); } else { - gen = PyGen_NewWithQualName(f, name, qualname); + gen = PyGen_NewWithQualName(f, con->fc_name, con->fc_qualname); } if (gen == NULL) { return NULL; @@ -4643,7 +4627,7 @@ fail: /* Jump here from prelude on failure */ return retval; } - +/* Legacy API */ PyObject * _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, PyObject *const *args, Py_ssize_t argcount, @@ -4653,16 +4637,36 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, PyObject *kwdefs, PyObject *closure, PyObject *name, PyObject *qualname) { + PyObject *defaults = _PyTuple_FromArray(defs, defcount); + if (defaults == NULL) { + return NULL; + } + PyObject *builtins = _PyEval_BuiltinsFromGlobals(globals); + if (builtins == NULL) { + Py_DECREF(defaults); + return NULL; + } + PyFrameConstructor constr = { + .fc_globals = globals, + .fc_builtins = builtins, + .fc_name = name, + .fc_qualname = qualname, + .fc_code = _co, + .fc_defaults = defaults, + .fc_kwdefaults = kwdefs, + .fc_closure = closure + }; PyThreadState *tstate = _PyThreadState_GET(); - return _PyEval_EvalCode(tstate, _co, globals, locals, - args, argcount, - kwnames, kwargs, - kwcount, kwstep, - defs, defcount, - kwdefs, closure, - name, qualname); + PyObject *res = _PyEval_EvalCode(tstate, &constr, locals, + args, argcount, + kwnames, kwargs, + kwcount, kwstep); + Py_DECREF(defaults); + Py_DECREF(builtins); + return res; } +/* Legacy API */ PyObject * PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, PyObject *const *args, int argcount, @@ -4670,13 +4674,15 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, PyObject *const *defs, int defcount, PyObject *kwdefs, PyObject *closure) { - return _PyEval_EvalCodeWithName(_co, globals, locals, - args, argcount, - kws, kws != NULL ? kws + 1 : NULL, - kwcount, 2, - defs, defcount, - kwdefs, closure, - NULL, NULL); + return _PyEval_EvalCodeWithName( + _co, globals, locals, + args, argcount, + kws, kws != NULL ? kws + 1 : NULL, + kwcount, 2, + defs, defcount, + kwdefs, closure, + ((PyCodeObject *)_co)->co_name, + ((PyCodeObject *)_co)->co_name); } static PyObject * -- cgit v0.12