[Python-checkins] cpython: Issue #21205: Add a new ``__qualname__`` attribute to generator, the qualified

victor.stinner python-checkins at python.org
Mon Jun 16 16:00:18 CEST 2014


http://hg.python.org/cpython/rev/aa85e8d729ae
changeset: 91211:aa85e8d729ae
parent: 91209:36057f357537
user: Victor Stinner <victor.stinner at gmail.com>
date: Mon Jun 16 15:59:28 2014 +0200
summary:
 Issue #21205: Add a new ``__qualname__`` attribute to generator, the qualified
name, and use it in the representation of a generator (``repr(gen)``). The
default name of the generator (``__name__`` attribute) is now get from the
function instead of the code. Use ``gen.gi_code.co_name`` to get the name of
the code.
files:
 Doc/library/inspect.rst | 14 +++
 Doc/whatsnew/3.5.rst | 6 +
 Include/genobject.h | 8 ++
 Lib/test/test_generators.py | 39 ++++++++++
 Misc/NEWS | 6 +
 Objects/genobject.c | 90 ++++++++++++++++++++----
 Python/ceval.c | 30 ++++++--
 7 files changed, 170 insertions(+), 23 deletions(-)
diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst
--- a/Doc/library/inspect.rst
+++ b/Doc/library/inspect.rst
@@ -159,6 +159,16 @@
 | | | 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 @@
 | | | ``None`` |
 +-----------+-----------------+---------------------------+
 
+.. versionchanged:: 3.5
+
+ Add ``__qualname__`` attribute to generators.
+
 
 .. function:: getmembers(object[, predicate])
 
diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst
--- a/Doc/whatsnew/3.5.rst
+++ b/Doc/whatsnew/3.5.rst
@@ -304,6 +304,12 @@
 or :exc:`ssl.SSLWantWriteError` on a non-blocking socket if the operation
 would block. Previously, it would return 0. See :issue:`20951`.
 
+* The ``__name__`` attribute of generator is now set from the function name,
+ instead of being set from the code name. Use ``gen.gi_code.co_name`` to
+ retrieve the code name. Generators also have a new ``__qualname__``
+ attribute, the qualified name, which is now used for the representation
+ of a generator (``repr(gen)``). See :issue:`21205`.
+
 Changes in the C API
 --------------------
 
diff --git a/Include/genobject.h b/Include/genobject.h
--- a/Include/genobject.h
+++ b/Include/genobject.h
@@ -25,6 +25,12 @@
 
 /* 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 @@
 #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 --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py
--- a/Lib/test/test_generators.py
+++ b/Lib/test/test_generators.py
@@ -50,6 +50,45 @@
 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.<locals>.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__,
+ "<genexpr>")
+ self.assertEqual(gen.__qualname__,
+ "GeneratorTest.test_name.<locals>.<genexpr>")
+
+
 tutorial_tests = """
 Let's try a simple generator:
 
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,12 @@
 Core and Builtins
 -----------------
 
+- Issue #21205: Add a new ``__qualname__`` attribute to generator, the
+ qualified name, and use it in the representation of a generator
+ (``repr(gen)``). The default name of the generator (``__name__`` attribute)
+ is now get from the function instead of the code. Use ``gen.gi_code.co_name``
+ to get the name of the code.
+
 - Issue #21669: With the aid of heuristics in SyntaxError.__init__, the
 parser now attempts to generate more meaningful (or at least more search
 engine friendly) error messages when "exec" and "print" are used as
diff --git a/Objects/genobject.c b/Objects/genobject.c
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -12,6 +12,8 @@
 {
 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 @@
 _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 @@
 gen_repr(PyGenObject *gen)
 {
 return PyUnicode_FromFormat("<generator object %S at %p>",
- ((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 @@
 };
 
 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 @@
 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 --git a/Python/ceval.c b/Python/ceval.c
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -3401,10 +3401,11 @@
 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 @@
 
 /* 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 @@
 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 @@
 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 @@
 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 *
-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /