diff --git a/Include/code.h b/Include/code.h --- a/Include/code.h +++ b/Include/code.h @@ -30,6 +30,7 @@ typedef struct { Objects/lnotab_notes.txt for details. */ void *co_zombieframe; /* for optimization only (see frameobject.c) */ PyObject *co_weakreflist; /* to support weakrefs to code objects */ + PyObject *co_qualname; /* unicode (PEP 3155 qualname) */ } PyCodeObject; /* Masks for co_flags above */ @@ -79,12 +80,13 @@ PyAPI_DATA(PyTypeObject) PyCode_Type; PyAPI_FUNC(PyCodeObject *) PyCode_New( int, int, int, int, int, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, - PyObject *, PyObject *, int, PyObject *); + PyObject *, PyObject *, int, PyObject *, 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); +PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno, + const char *qualname); /* Return the line number associated with the specified bytecode index in this code object. If you just need the line number of a frame, diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -16,6 +16,7 @@ cellvars: ('x',) freevars: () nlocals: 2 flags: 3 +qualname: f consts: ('None', '', "'f..g'") >>> dump(f(4).__code__) @@ -28,6 +29,7 @@ cellvars: () freevars: ('x',) nlocals: 1 flags: 19 +qualname: f..g consts: ('None',) >>> def h(x, y): @@ -47,6 +49,7 @@ cellvars: () freevars: () nlocals: 5 flags: 67 +qualname: h consts: ('None',) >>> def attrs(obj): @@ -64,6 +67,7 @@ cellvars: () freevars: () nlocals: 1 flags: 67 +qualname: attrs consts: ('None',) >>> def optimize_away(): @@ -82,6 +86,7 @@ cellvars: () freevars: () nlocals: 0 flags: 67 +qualname: optimize_away consts: ("'doc string'", 'None') >>> def keywordonly_args(a,b,*,k1): @@ -98,6 +103,7 @@ cellvars: () freevars: () nlocals: 3 flags: 67 +qualname: keywordonly_args consts: ('None',) """ @@ -119,7 +125,7 @@ def consts(t): def dump(co): """Print out a text representation of a code object.""" for attr in ["name", "argcount", "kwonlyargcount", "names", "varnames", - "cellvars", "freevars", "nlocals", "flags"]: + "cellvars", "freevars", "nlocals", "flags", "qualname"]: print("%s: %s" % (attr, getattr(co, "co_" + attr))) print("consts:", tuple(consts(co.co_consts))) @@ -127,10 +133,11 @@ def dump(co): class CodeTest(unittest.TestCase): def test_newempty(self): - co = _testcapi.code_newempty("filename", "funcname", 15) + co = _testcapi.code_newempty("filename", "funcname", 15, "funcname") self.assertEqual(co.co_filename, "filename") self.assertEqual(co.co_name, "funcname") self.assertEqual(co.co_firstlineno, 15) + self.assertEqual(co.co_qualname, "funcname") class CodeWeakRefTest(unittest.TestCase): diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -338,7 +338,7 @@ Stack size: 7 Flags: OPTIMIZED, NEWLOCALS, VARARGS, VARKEYWORDS, GENERATOR Constants: 0: None - 1: + 1: .f at (.*), file "(.*)", line (.*)> 2: 'tricky..f' Variable names: 0: x diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -666,7 +666,7 @@ class SizeofTest(unittest.TestCase): return inner check(get_cell().__closure__[0], size(h + 'P')) # code - check(get_cell().__code__, size(h + '5i9Pi3P')) + check(get_cell().__code__, size(h + '5i9Pi4P')) # complex check(complex(0,1), size(h + '2d')) # method_descriptor (descriptor object) diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -101,7 +101,7 @@ void _ctypes_add_traceback(char *funcnam py_globals = PyDict_New(); if (!py_globals) goto bad; - py_code = PyCode_NewEmpty(filename, funcname, lineno); + py_code = PyCode_NewEmpty(filename, funcname, lineno, funcname); if (!py_code) goto bad; py_frame = PyFrame_New( PyThreadState_Get(), /*PyThreadState *tstate,*/ diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -2326,13 +2326,15 @@ code_newempty(PyObject *self, PyObject * { const char *filename; const char *funcname; + const char *qualname; int firstlineno; - if (!PyArg_ParseTuple(args, "ssi:code_newempty", - &filename, &funcname, &firstlineno)) + if (!PyArg_ParseTuple(args, "ssis:code_newempty", + &filename, &funcname, &firstlineno, &qualname)) return NULL; - return (PyObject *)PyCode_NewEmpty(filename, funcname, firstlineno); + return (PyObject *)PyCode_NewEmpty(filename, funcname, firstlineno, + qualname); } /* Test PyErr_NewExceptionWithDoc (also exercise PyErr_NewException). diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c --- a/Modules/pyexpat.c +++ b/Modules/pyexpat.c @@ -212,7 +212,7 @@ getcode(enum HandlerTypes slot, char* fu { if (handler_info[slot].tb_code == NULL) { handler_info[slot].tb_code = - PyCode_NewEmpty(__FILE__, func_name, lineno); + PyCode_NewEmpty(__FILE__, func_name, lineno, func_name); } return handler_info[slot].tb_code; } diff --git a/Objects/codeobject.c b/Objects/codeobject.c --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -53,7 +53,7 @@ PyCode_New(int argcount, int kwonlyargco PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, - PyObject *lnotab) + PyObject *lnotab, PyObject *qualname) { PyCodeObject *co; unsigned char *cell2arg = NULL; @@ -142,6 +142,13 @@ PyCode_New(int argcount, int kwonlyargco co->co_filename = filename; Py_INCREF(name); co->co_name = name; + if (qualname) { + Py_INCREF(qualname); + co->co_qualname = qualname; + } else { + Py_INCREF(name); + co->co_qualname = name; + } co->co_firstlineno = firstlineno; Py_INCREF(lnotab); co->co_lnotab = lnotab; @@ -151,12 +158,14 @@ PyCode_New(int argcount, int kwonlyargco } PyCodeObject * -PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) +PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno, + const char *qualname) { static PyObject *emptystring = NULL; static PyObject *nulltuple = NULL; PyObject *filename_ob = NULL; PyObject *funcname_ob = NULL; + PyObject *qualname_ob = NULL; PyCodeObject *result = NULL; if (emptystring == NULL) { emptystring = PyBytes_FromString(""); @@ -174,6 +183,9 @@ PyCode_NewEmpty(const char *filename, co filename_ob = PyUnicode_DecodeFSDefault(filename); if (filename_ob == NULL) goto failed; + qualname_ob = PyUnicode_FromString(qualname); + if (qualname_ob == NULL) + goto failed; result = PyCode_New(0, /* argcount */ 0, /* kwonlyargcount */ @@ -189,7 +201,8 @@ PyCode_NewEmpty(const char *filename, co filename_ob, /* filename */ funcname_ob, /* name */ firstlineno, /* firstlineno */ - emptystring /* lnotab */ + emptystring, /* lnotab */ + qualname_ob /* qualname */ ); failed: @@ -214,6 +227,7 @@ static PyMemberDef code_memberlist[] = { {"co_cellvars", T_OBJECT, OFF(co_cellvars), READONLY}, {"co_filename", T_OBJECT, OFF(co_filename), READONLY}, {"co_name", T_OBJECT, OFF(co_name), READONLY}, + {"co_qualname", T_OBJECT, OFF(co_qualname), READONLY}, {"co_firstlineno", T_INT, OFF(co_firstlineno), READONLY}, {"co_lnotab", T_OBJECT, OFF(co_lnotab), READONLY}, {NULL} /* Sentinel */ @@ -264,7 +278,7 @@ validate_and_copy_tuple(PyObject *tup) PyDoc_STRVAR(code_doc, "code(argcount, kwonlyargcount, nlocals, stacksize, flags, codestring,\n\ constants, names, varnames, filename, name, firstlineno,\n\ - lnotab[, freevars[, cellvars]])\n\ + lnotab[, freevars[, cellvars[, qualname]]])\n\ \n\ Create a code object. Not for the faint of heart."); @@ -285,10 +299,11 @@ code_new(PyTypeObject *type, PyObject *a PyObject *cellvars = NULL, *ourcellvars = NULL; PyObject *filename; PyObject *name; + PyObject *qualname = NULL; int firstlineno; PyObject *lnotab; - if (!PyArg_ParseTuple(args, "iiiiiSO!O!O!UUiS|O!O!:code", + if (!PyArg_ParseTuple(args, "iiiiiSO!O!O!UUiS|O!O!U:code", &argcount, &kwonlyargcount, &nlocals, &stacksize, &flags, &code, @@ -298,7 +313,8 @@ code_new(PyTypeObject *type, PyObject *a &filename, &name, &firstlineno, &lnotab, &PyTuple_Type, &freevars, - &PyTuple_Type, &cellvars)) + &PyTuple_Type, &cellvars, + &qualname)) return NULL; if (argcount < 0) { @@ -344,7 +360,7 @@ code_new(PyTypeObject *type, PyObject *a nlocals, stacksize, flags, code, consts, ournames, ourvarnames, ourfreevars, ourcellvars, filename, - name, firstlineno, lnotab); + name, firstlineno, lnotab, qualname); cleanup: Py_XDECREF(ournames); Py_XDECREF(ourvarnames); @@ -364,6 +380,7 @@ code_dealloc(PyCodeObject *co) Py_XDECREF(co->co_cellvars); Py_XDECREF(co->co_filename); Py_XDECREF(co->co_name); + Py_XDECREF(co->co_qualname); Py_XDECREF(co->co_lnotab); if (co->co_cell2arg != NULL) PyMem_FREE(co->co_cell2arg); @@ -385,11 +402,11 @@ code_repr(PyCodeObject *co) if (co->co_filename && PyUnicode_Check(co->co_filename)) { return PyUnicode_FromFormat( "", - co->co_name, co, co->co_filename, lineno); + co->co_qualname, co, co->co_filename, lineno); } else { return PyUnicode_FromFormat( "", - co->co_name, co, lineno); + co->co_qualname, co, lineno); } } diff --git a/Python/compile.c b/Python/compile.c --- a/Python/compile.c +++ b/Python/compile.c @@ -1571,8 +1571,8 @@ compiler_function(struct compiler *c, st st = (stmt_ty)asdl_seq_GET(s->v.FunctionDef.body, i); VISIT_IN_SCOPE(c, stmt, st); } + qualname = compiler_scope_qualname(c); co = assemble(c, 1); - qualname = compiler_scope_qualname(c); compiler_exit_scope(c); if (qualname == NULL || co == NULL) { Py_XDECREF(qualname); @@ -1790,8 +1790,8 @@ compiler_lambda(struct compiler *c, expr else { ADDOP_IN_SCOPE(c, RETURN_VALUE); } + qualname = compiler_scope_qualname(c); co = assemble(c, 1); - qualname = compiler_scope_qualname(c); compiler_exit_scope(c); if (qualname == NULL || co == NULL) return 0; @@ -3051,8 +3051,8 @@ compiler_comprehension(struct compiler * ADDOP(c, RETURN_VALUE); } + qualname = compiler_scope_qualname(c); co = assemble(c, 1); - qualname = compiler_scope_qualname(c); compiler_exit_scope(c); if (qualname == NULL || co == NULL) goto error; @@ -4113,7 +4113,7 @@ makecode(struct compiler *c, struct asse freevars, cellvars, c->c_filename_obj, c->u->u_name, c->u->u_firstlineno, - a->a_lnotab); + a->a_lnotab, c->u->u_qualname); error: Py_XDECREF(consts); Py_XDECREF(names); diff --git a/Python/frozen.c b/Python/frozen.c --- a/Python/frozen.c +++ b/Python/frozen.c @@ -21,8 +21,8 @@ static unsigned char M___hello__[] = { 105,122,101,100,117,5,0,0,0,112,114,105,110,116,40,0, 0,0,0,40,0,0,0,0,40,0,0,0,0,117,7,0, 0,0,102,108,97,103,46,112,121,117,8,0,0,0,60,109, - 111,100,117,108,101,62,1,0,0,0,115,2,0,0,0,6, - 1, + 111,100,117,108,101,62,117,8,0,0,0,60,109,111,100,117, + 108,101,62,1,0,0,0,115,2,0,0,0,6,1, }; #define SIZE (int)sizeof(M___hello__) diff --git a/Python/marshal.c b/Python/marshal.c --- a/Python/marshal.c +++ b/Python/marshal.c @@ -403,6 +403,7 @@ w_object(PyObject *v, WFILE *p) w_object(co->co_cellvars, p); w_object(co->co_filename, p); w_object(co->co_name, p); + w_object(co->co_qualname, p); w_long(co->co_firstlineno, p); w_object(co->co_lnotab, p); } @@ -1024,6 +1025,7 @@ r_object(RFILE *p) PyObject *cellvars = NULL; PyObject *filename = NULL; PyObject *name = NULL; + PyObject *qualname = NULL; int firstlineno; PyObject *lnotab = NULL; @@ -1081,6 +1083,9 @@ r_object(RFILE *p) name = r_object(p); if (name == NULL) goto code_error; + qualname = r_object(p); + if (qualname == NULL) + goto code_error; firstlineno = (int)r_long(p); lnotab = r_object(p); if (lnotab == NULL) @@ -1091,7 +1096,7 @@ r_object(RFILE *p) nlocals, stacksize, flags, code, consts, names, varnames, freevars, cellvars, filename, name, - firstlineno, lnotab); + firstlineno, lnotab, qualname); code_error: Py_XDECREF(code); @@ -1102,6 +1107,7 @@ r_object(RFILE *p) Py_XDECREF(cellvars); Py_XDECREF(filename); Py_XDECREF(name); + Py_XDECREF(qualname); Py_XDECREF(lnotab); } retval = v;