[?1034hdiff -r 04cc9c32f3e3 Doc/library/inspect.rst --- a/Doc/library/inspect.rst Thu Jun 12 18:39:42 2014 +0200 +++ b/Doc/library/inspect.rst Fri Jun 13 10:02:58 2014 +0200 @@ -159,6 +159,16 @@ attributes: | | | arguments and local | | | | variables | +-----------+-----------------+---------------------------+ +| generator | __name__ | name | ++-----------+-----------------+---------------------------+ +| | __qualname__ | qualified name | ++-----------+-----------------+---------------------------+ +| | gi_frame | frame | ++-----------+-----------------+---------------------------+ +| | gi_running | is the generator running? | ++-----------+-----------------+---------------------------+ +| | gi_code | code | ++-----------+-----------------+---------------------------+ | builtin | __doc__ | documentation string | +-----------+-----------------+---------------------------+ | | __name__ | original name of this | @@ -169,6 +179,10 @@ attributes: | | | ``None`` | +-----------+-----------------+---------------------------+ +.. versionchanged:: 3.5 + + Add ``__qualname__`` attribute to generators. + .. function:: getmembers(object[, predicate]) diff -r 04cc9c32f3e3 Include/genobject.h --- a/Include/genobject.h Thu Jun 12 18:39:42 2014 +0200 +++ b/Include/genobject.h Fri Jun 13 10:02:58 2014 +0200 @@ -25,6 +25,12 @@ typedef struct { /* List of weak reference. */ PyObject *gi_weakreflist; + + /* Name of the generator. */ + PyObject *gi_name; + + /* Qualified name of the generator. */ + PyObject *gi_qualname; } PyGenObject; PyAPI_DATA(PyTypeObject) PyGen_Type; @@ -33,6 +39,8 @@ PyAPI_DATA(PyTypeObject) PyGen_Type; #define PyGen_CheckExact(op) (Py_TYPE(op) == &PyGen_Type) PyAPI_FUNC(PyObject *) PyGen_New(struct _frame *); +PyAPI_FUNC(PyObject *) PyGen_NewWithQualName(struct _frame *, + PyObject *name, PyObject *qualname); PyAPI_FUNC(int) PyGen_NeedsFinalizing(PyGenObject *); PyAPI_FUNC(int) _PyGen_FetchStopIterationValue(PyObject **); PyObject *_PyGen_Send(PyGenObject *, PyObject *); diff -r 04cc9c32f3e3 Lib/test/test_generators.py --- a/Lib/test/test_generators.py Thu Jun 12 18:39:42 2014 +0200 +++ b/Lib/test/test_generators.py Fri Jun 13 10:02:58 2014 +0200 @@ -50,6 +50,45 @@ class FinalizationTest(unittest.TestCase self.assertEqual(gc.garbage, old_garbage) +class GeneratorTest(unittest.TestCase): + + def test_name(self): + def func(): + yield 1 + + # check generator names + gen = func() + self.assertEqual(gen.__name__, "func") + self.assertEqual(gen.__qualname__, + "GeneratorTest.test_name..func") + + # modify generator names + gen.__name__ = "name" + gen.__qualname__ = "qualname" + self.assertEqual(gen.__name__, "name") + self.assertEqual(gen.__qualname__, "qualname") + + # generator names must be a string and cannot be deleted + self.assertRaises(TypeError, setattr, gen, '__name__', 123) + self.assertRaises(TypeError, setattr, gen, '__qualname__', 123) + self.assertRaises(TypeError, delattr, gen, '__name__') + self.assertRaises(TypeError, delattr, gen, '__qualname__') + + # modify names of the function creating the generator + func.__qualname__ = "func_qualname" + func.__name__ = "func_name" + gen = func() + self.assertEqual(gen.__name__, "func_name") + self.assertEqual(gen.__qualname__, "func_qualname") + + # unnamed generator + gen = (x for x in range(10)) + self.assertEqual(gen.__name__, + "") + self.assertEqual(gen.__qualname__, + "GeneratorTest.test_name..") + + tutorial_tests = """ Let's try a simple generator: diff -r 04cc9c32f3e3 Objects/genobject.c --- a/Objects/genobject.c Thu Jun 12 18:39:42 2014 +0200 +++ b/Objects/genobject.c Fri Jun 13 10:02:58 2014 +0200 @@ -12,6 +12,8 @@ gen_traverse(PyGenObject *gen, visitproc { Py_VISIT((PyObject *)gen->gi_frame); Py_VISIT(gen->gi_code); + Py_VISIT(gen->gi_name); + Py_VISIT(gen->gi_qualname); return 0; } @@ -58,6 +60,8 @@ gen_dealloc(PyGenObject *gen) _PyObject_GC_UNTRACK(self); Py_CLEAR(gen->gi_frame); Py_CLEAR(gen->gi_code); + Py_CLEAR(gen->gi_name); + Py_CLEAR(gen->gi_qualname); PyObject_GC_Del(gen); } @@ -418,33 +422,73 @@ static PyObject * gen_repr(PyGenObject *gen) { return PyUnicode_FromFormat("", - ((PyCodeObject *)gen->gi_code)->co_name, - gen); + gen->gi_qualname, gen); } +static PyObject * +gen_get_name(PyGenObject *op) +{ + Py_INCREF(op->gi_name); + return op->gi_name; +} + +static int +gen_set_name(PyGenObject *op, PyObject *value) +{ + PyObject *tmp; + + /* Not legal to del gen.gi_name or to set it to anything + * other than a string object. */ + if (value == NULL || !PyUnicode_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "__name__ must be set to a string object"); + return -1; + } + tmp = op->gi_name; + Py_INCREF(value); + op->gi_name = value; + Py_DECREF(tmp); + return 0; +} static PyObject * -gen_get_name(PyGenObject *gen) +gen_get_qualname(PyGenObject *op) { - PyObject *name = ((PyCodeObject *)gen->gi_code)->co_name; - Py_INCREF(name); - return name; + Py_INCREF(op->gi_qualname); + return op->gi_qualname; } +static int +gen_set_qualname(PyGenObject *op, PyObject *value) +{ + PyObject *tmp; -PyDoc_STRVAR(gen__name__doc__, -"Return the name of the generator's associated code object."); + /* Not legal to del gen.__qualname__ or to set it to anything + * other than a string object. */ + if (value == NULL || !PyUnicode_Check(value)) { + PyErr_SetString(PyExc_TypeError, + "__qualname__ must be set to a string object"); + return -1; + } + tmp = op->gi_qualname; + Py_INCREF(value); + op->gi_qualname = value; + Py_DECREF(tmp); + return 0; +} static PyGetSetDef gen_getsetlist[] = { - {"__name__", (getter)gen_get_name, NULL, gen__name__doc__}, - {NULL} + {"__name__", (getter)gen_get_name, (setter)gen_set_name, + PyDoc_STR("name of the generator")}, + {"__qualname__", (getter)gen_get_qualname, (setter)gen_set_qualname, + PyDoc_STR("qualified name of the generator")}, + {NULL} /* Sentinel */ }; - static PyMemberDef gen_memberlist[] = { - {"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), READONLY}, - {"gi_running", T_BOOL, offsetof(PyGenObject, gi_running), READONLY}, - {"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY}, + {"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), READONLY}, + {"gi_running", T_BOOL, offsetof(PyGenObject, gi_running), READONLY}, + {"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY}, {NULL} /* Sentinel */ }; @@ -510,7 +554,7 @@ PyTypeObject PyGen_Type = { }; PyObject * -PyGen_New(PyFrameObject *f) +PyGen_NewWithQualName(PyFrameObject *f, PyObject *name, PyObject *qualname) { PyGenObject *gen = PyObject_GC_New(PyGenObject, &PyGen_Type); if (gen == NULL) { @@ -523,10 +567,26 @@ PyGen_New(PyFrameObject *f) gen->gi_code = (PyObject *)(f->f_code); gen->gi_running = 0; gen->gi_weakreflist = NULL; + if (name != NULL) + gen->gi_name = name; + else + gen->gi_name = ((PyCodeObject *)gen->gi_code)->co_name; + Py_INCREF(gen->gi_name); + if (qualname != NULL) + gen->gi_qualname = qualname; + else + gen->gi_qualname = gen->gi_name; + Py_INCREF(gen->gi_qualname); _PyObject_GC_TRACK(gen); return (PyObject *)gen; } +PyObject * +PyGen_New(PyFrameObject *f) +{ + return PyGen_NewWithQualName(f, NULL, NULL); +} + int PyGen_NeedsFinalizing(PyGenObject *gen) { diff -r 04cc9c32f3e3 Python/ceval.c --- a/Python/ceval.c Thu Jun 12 18:39:42 2014 +0200 +++ b/Python/ceval.c Fri Jun 13 10:02:58 2014 +0200 @@ -3401,10 +3401,11 @@ too_many_positional(PyCodeObject *co, in PyEval_EvalFrame() and PyEval_EvalCodeEx() you will need to adjust the test in the if statements in Misc/gdbinit (pystack and pystackv). */ -PyObject * -PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, +static PyObject * +_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, PyObject **args, int argcount, PyObject **kws, int kwcount, - PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure) + PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure, + PyObject *name, PyObject *qualname) { PyCodeObject* co = (PyCodeObject*)_co; PyFrameObject *f; @@ -3596,7 +3597,7 @@ PyEval_EvalCodeEx(PyObject *_co, PyObjec /* Create a new generator that owns the ready to run frame * and return that as the value. */ - return PyGen_New(f); + return PyGen_NewWithQualName(f, name, qualname); } retval = PyEval_EvalFrameEx(f,0); @@ -3615,6 +3616,16 @@ fail: /* Jump here from prelude on failu return retval; } +PyObject * +PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, + PyObject **args, int argcount, PyObject **kws, int kwcount, + PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure) +{ + return _PyEval_EvalCodeWithName(_co, globals, locals, + args, argcount, kws, kwcount, + defs, defcount, kwdefs, closure, + NULL, NULL); +} static PyObject * special_lookup(PyObject *o, _Py_Identifier *id) @@ -4313,6 +4324,8 @@ fast_function(PyObject *func, PyObject * PyObject *globals = PyFunction_GET_GLOBALS(func); PyObject *argdefs = PyFunction_GET_DEFAULTS(func); PyObject *kwdefs = PyFunction_GET_KW_DEFAULTS(func); + PyObject *name = ((PyFunctionObject *)func) -> func_name; + PyObject *qualname = ((PyFunctionObject *)func) -> func_qualname; PyObject **d = NULL; int nd = 0; @@ -4355,10 +4368,11 @@ fast_function(PyObject *func, PyObject * d = &PyTuple_GET_ITEM(argdefs, 0); nd = Py_SIZE(argdefs); } - return PyEval_EvalCodeEx((PyObject*)co, globals, - (PyObject *)NULL, (*pp_stack)-n, na, - (*pp_stack)-2*nk, nk, d, nd, kwdefs, - PyFunction_GET_CLOSURE(func)); + return _PyEval_EvalCodeWithName((PyObject*)co, globals, + (PyObject *)NULL, (*pp_stack)-n, na, + (*pp_stack)-2*nk, nk, d, nd, kwdefs, + PyFunction_GET_CLOSURE(func), + name, qualname); } static PyObject *