diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index ab0fca05064..7be0a90abf1 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -917,6 +917,17 @@ StopIteration >>> g.gi_code is f.func_code True + +Test the __name__ attribute and the repr() + +>>> def f(): +... yield 5 +... +>>> g = f() +>>> g.__name__ +'f' +>>> repr(g) # doctest: +ELLIPSIS +'' """ # conjoin is a simple backtracking generator, named in honor of Icon's diff --git a/Lib/test/test_genexps.py b/Lib/test/test_genexps.py index 2598a7913ad..0317955ab14 100644 --- a/Lib/test/test_genexps.py +++ b/Lib/test/test_genexps.py @@ -92,7 +92,7 @@ Verify that parenthesis are required when used as a keyword argument value Verify that parenthesis are required when used as a keyword argument value >>> dict(a = (i for i in xrange(10))) #doctest: +ELLIPSIS - {'a': } + {'a': < generator object at ...>} Verify early binding for the outermost for-expression diff --git a/Misc/NEWS b/Misc/NEWS index 829aac68d88..7d3dcff75ca 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,11 @@ What's New in Python 2.6 beta 1? Core and Builtins ----------------- +- Issue #2863: generators now have a ``gen.__name__`` attribute that equals + ``gen.gi_code.co_name``, like ``func.__name___`` that equals + ``func.func_code.co_name``. The repr() of a generator now also contains + this name. + - Issue #2831: enumerate() now has a ``start`` argument. - Issue #2801: fix bug in the float.is_integer method where a ValueError diff --git a/Objects/genobject.c b/Objects/genobject.c index 38682955c3a..3cd911d5f84 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -281,6 +281,36 @@ gen_iternext(PyGenObject *gen) } +static PyObject * +gen_repr(PyGenObject *gen) +{ + char *code_name; + code_name = PyString_AsString(((PyCodeObject *)gen->gi_code)->co_name); + if (code_name == NULL) + return NULL; + return PyString_FromFormat("<%.200s generator object at %p>", + code_name, gen); +} + + +static PyObject * +gen_get_name(PyGenObject *gen) +{ + PyObject *name = ((PyCodeObject *)gen->gi_code)->co_name; + Py_INCREF(name); + return name; +} + + +PyDoc_STRVAR(gen__name__doc__, +"Return the name of the generator's associated code object."); + +static PyGetSetDef gen_getsetlist[] = { + {"__name__", (getter)gen_get_name, NULL, NULL, gen__name__doc__}, + {NULL} +}; + + static PyMemberDef gen_memberlist[] = { {"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), RO}, {"gi_running", T_INT, offsetof(PyGenObject, gi_running), RO}, @@ -306,7 +336,7 @@ PyTypeObject PyGen_Type = { 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ - 0, /* tp_repr */ + (reprfunc)gen_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ @@ -326,7 +356,7 @@ PyTypeObject PyGen_Type = { (iternextfunc)gen_iternext, /* tp_iternext */ gen_methods, /* tp_methods */ gen_memberlist, /* tp_members */ - 0, /* tp_getset */ + gen_getsetlist, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */