Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 40ee301

Browse files
committed
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.
1 parent 2617199 commit 40ee301

7 files changed

Lines changed: 170 additions & 23 deletions

File tree

Doc/library/inspect.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,16 @@ attributes:
159159
| | | arguments and local |
160160
| | | variables |
161161
+-----------+-----------------+---------------------------+
162+
| generator | __name__ | name |
163+
+-----------+-----------------+---------------------------+
164+
| | __qualname__ | qualified name |
165+
+-----------+-----------------+---------------------------+
166+
| | gi_frame | frame |
167+
+-----------+-----------------+---------------------------+
168+
| | gi_running | is the generator running? |
169+
+-----------+-----------------+---------------------------+
170+
| | gi_code | code |
171+
+-----------+-----------------+---------------------------+
162172
| builtin | __doc__ | documentation string |
163173
+-----------+-----------------+---------------------------+
164174
| | __name__ | original name of this |
@@ -169,6 +179,10 @@ attributes:
169179
| | | ``None`` |
170180
+-----------+-----------------+---------------------------+
171181

182+
.. versionchanged:: 3.5
183+
184+
Add ``__qualname__`` attribute to generators.
185+
172186

173187
.. function:: getmembers(object[, predicate])
174188

Doc/whatsnew/3.5.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,12 @@ Changes in the Python API
304304
or :exc:`ssl.SSLWantWriteError` on a non-blocking socket if the operation
305305
would block. Previously, it would return 0. See :issue:`20951`.
306306

307+
* The ``__name__`` attribute of generator is now set from the function name,
308+
instead of being set from the code name. Use ``gen.gi_code.co_name`` to
309+
retrieve the code name. Generators also have a new ``__qualname__``
310+
attribute, the qualified name, which is now used for the representation
311+
of a generator (``repr(gen)``). See :issue:`21205`.
312+
307313
Changes in the C API
308314
--------------------
309315

Include/genobject.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ typedef struct {
2525

2626
/* List of weak reference. */
2727
PyObject *gi_weakreflist;
28+
29+
/* Name of the generator. */
30+
PyObject *gi_name;
31+
32+
/* Qualified name of the generator. */
33+
PyObject *gi_qualname;
2834
} PyGenObject;
2935

3036
PyAPI_DATA(PyTypeObject) PyGen_Type;
@@ -33,6 +39,8 @@ PyAPI_DATA(PyTypeObject) PyGen_Type;
3339
#define PyGen_CheckExact(op) (Py_TYPE(op) == &PyGen_Type)
3440

3541
PyAPI_FUNC(PyObject *) PyGen_New(struct _frame *);
42+
PyAPI_FUNC(PyObject *) PyGen_NewWithQualName(struct _frame *,
43+
PyObject *name, PyObject *qualname);
3644
PyAPI_FUNC(int) PyGen_NeedsFinalizing(PyGenObject *);
3745
PyAPI_FUNC(int) _PyGen_FetchStopIterationValue(PyObject **);
3846
PyObject *_PyGen_Send(PyGenObject *, PyObject *);

Lib/test/test_generators.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,45 @@ def gen():
5050
self.assertEqual(gc.garbage, old_garbage)
5151

5252

53+
class GeneratorTest(unittest.TestCase):
54+
55+
def test_name(self):
56+
def func():
57+
yield 1
58+
59+
# check generator names
60+
gen = func()
61+
self.assertEqual(gen.__name__, "func")
62+
self.assertEqual(gen.__qualname__,
63+
"GeneratorTest.test_name.<locals>.func")
64+
65+
# modify generator names
66+
gen.__name__ = "name"
67+
gen.__qualname__ = "qualname"
68+
self.assertEqual(gen.__name__, "name")
69+
self.assertEqual(gen.__qualname__, "qualname")
70+
71+
# generator names must be a string and cannot be deleted
72+
self.assertRaises(TypeError, setattr, gen, '__name__', 123)
73+
self.assertRaises(TypeError, setattr, gen, '__qualname__', 123)
74+
self.assertRaises(TypeError, delattr, gen, '__name__')
75+
self.assertRaises(TypeError, delattr, gen, '__qualname__')
76+
77+
# modify names of the function creating the generator
78+
func.__qualname__ = "func_qualname"
79+
func.__name__ = "func_name"
80+
gen = func()
81+
self.assertEqual(gen.__name__, "func_name")
82+
self.assertEqual(gen.__qualname__, "func_qualname")
83+
84+
# unnamed generator
85+
gen = (x for x in range(10))
86+
self.assertEqual(gen.__name__,
87+
"<genexpr>")
88+
self.assertEqual(gen.__qualname__,
89+
"GeneratorTest.test_name.<locals>.<genexpr>")
90+
91+
5392
tutorial_tests = """
5493
Let's try a simple generator:
5594

Misc/NEWS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ Release date: TBA
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #21205: Add a new ``__qualname__`` attribute to generator, the
14+
qualified name, and use it in the representation of a generator
15+
(``repr(gen)``). The default name of the generator (``__name__`` attribute)
16+
is now get from the function instead of the code. Use ``gen.gi_code.co_name``
17+
to get the name of the code.
18+
1319
- Issue #21669: With the aid of heuristics in SyntaxError.__init__, the
1420
parser now attempts to generate more meaningful (or at least more search
1521
engine friendly) error messages when "exec" and "print" are used as

Objects/genobject.c

Lines changed: 75 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ gen_traverse(PyGenObject *gen, visitproc visit, void *arg)
1212
{
1313
Py_VISIT((PyObject *)gen->gi_frame);
1414
Py_VISIT(gen->gi_code);
15+
Py_VISIT(gen->gi_name);
16+
Py_VISIT(gen->gi_qualname);
1517
return 0;
1618
}
1719

@@ -58,6 +60,8 @@ gen_dealloc(PyGenObject *gen)
5860
_PyObject_GC_UNTRACK(self);
5961
Py_CLEAR(gen->gi_frame);
6062
Py_CLEAR(gen->gi_code);
63+
Py_CLEAR(gen->gi_name);
64+
Py_CLEAR(gen->gi_qualname);
6165
PyObject_GC_Del(gen);
6266
}
6367

@@ -418,33 +422,73 @@ static PyObject *
418422
gen_repr(PyGenObject *gen)
419423
{
420424
return PyUnicode_FromFormat("<generator object %S at %p>",
421-
((PyCodeObject *)gen->gi_code)->co_name,
422-
gen);
425+
gen->gi_qualname, gen);
423426
}
424427

428+
static PyObject *
429+
gen_get_name(PyGenObject *op)
430+
{
431+
Py_INCREF(op->gi_name);
432+
return op->gi_name;
433+
}
434+
435+
static int
436+
gen_set_name(PyGenObject *op, PyObject *value)
437+
{
438+
PyObject *tmp;
439+
440+
/* Not legal to del gen.gi_name or to set it to anything
441+
* other than a string object. */
442+
if (value == NULL || !PyUnicode_Check(value)) {
443+
PyErr_SetString(PyExc_TypeError,
444+
"__name__ must be set to a string object");
445+
return -1;
446+
}
447+
tmp = op->gi_name;
448+
Py_INCREF(value);
449+
op->gi_name = value;
450+
Py_DECREF(tmp);
451+
return 0;
452+
}
425453

426454
static PyObject *
427-
gen_get_name(PyGenObject *gen)
455+
gen_get_qualname(PyGenObject *op)
428456
{
429-
PyObject *name = ((PyCodeObject *)gen->gi_code)->co_name;
430-
Py_INCREF(name);
431-
return name;
457+
Py_INCREF(op->gi_qualname);
458+
return op->gi_qualname;
432459
}
433460

461+
static int
462+
gen_set_qualname(PyGenObject *op, PyObject *value)
463+
{
464+
PyObject *tmp;
434465

435-
PyDoc_STRVAR(gen__name__doc__,
436-
"Return the name of the generator's associated code object.");
466+
/* Not legal to del gen.__qualname__ or to set it to anything
467+
* other than a string object. */
468+
if (value == NULL || !PyUnicode_Check(value)) {
469+
PyErr_SetString(PyExc_TypeError,
470+
"__qualname__ must be set to a string object");
471+
return -1;
472+
}
473+
tmp = op->gi_qualname;
474+
Py_INCREF(value);
475+
op->gi_qualname = value;
476+
Py_DECREF(tmp);
477+
return 0;
478+
}
437479

438480
static PyGetSetDef gen_getsetlist[] = {
439-
{"__name__", (getter)gen_get_name, NULL, gen__name__doc__},
440-
{NULL}
481+
{"__name__", (getter)gen_get_name, (setter)gen_set_name,
482+
PyDoc_STR("name of the generator")},
483+
{"__qualname__", (getter)gen_get_qualname, (setter)gen_set_qualname,
484+
PyDoc_STR("qualified name of the generator")},
485+
{NULL} /* Sentinel */
441486
};
442487

443-
444488
static PyMemberDef gen_memberlist[] = {
445-
{"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), READONLY},
446-
{"gi_running", T_BOOL, offsetof(PyGenObject, gi_running), READONLY},
447-
{"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY},
489+
{"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), READONLY},
490+
{"gi_running", T_BOOL, offsetof(PyGenObject, gi_running), READONLY},
491+
{"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY},
448492
{NULL} /* Sentinel */
449493
};
450494

@@ -510,7 +554,7 @@ PyTypeObject PyGen_Type = {
510554
};
511555

512556
PyObject *
513-
PyGen_New(PyFrameObject *f)
557+
PyGen_NewWithQualName(PyFrameObject *f, PyObject *name, PyObject *qualname)
514558
{
515559
PyGenObject *gen = PyObject_GC_New(PyGenObject, &PyGen_Type);
516560
if (gen == NULL) {
@@ -523,10 +567,26 @@ PyGen_New(PyFrameObject *f)
523567
gen->gi_code = (PyObject *)(f->f_code);
524568
gen->gi_running = 0;
525569
gen->gi_weakreflist = NULL;
570+
if (name != NULL)
571+
gen->gi_name = name;
572+
else
573+
gen->gi_name = ((PyCodeObject *)gen->gi_code)->co_name;
574+
Py_INCREF(gen->gi_name);
575+
if (qualname != NULL)
576+
gen->gi_qualname = qualname;
577+
else
578+
gen->gi_qualname = gen->gi_name;
579+
Py_INCREF(gen->gi_qualname);
526580
_PyObject_GC_TRACK(gen);
527581
return (PyObject *)gen;
528582
}
529583

584+
PyObject *
585+
PyGen_New(PyFrameObject *f)
586+
{
587+
return PyGen_NewWithQualName(f, NULL, NULL);
588+
}
589+
530590
int
531591
PyGen_NeedsFinalizing(PyGenObject *gen)
532592
{

Python/ceval.c

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3401,10 +3401,11 @@ too_many_positional(PyCodeObject *co, int given, int defcount, PyObject **fastlo
34013401
PyEval_EvalFrame() and PyEval_EvalCodeEx() you will need to adjust
34023402
the test in the if statements in Misc/gdbinit (pystack and pystackv). */
34033403

3404-
PyObject *
3405-
PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
3404+
static PyObject *
3405+
_PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
34063406
PyObject **args, int argcount, PyObject **kws, int kwcount,
3407-
PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure)
3407+
PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure,
3408+
PyObject *name, PyObject *qualname)
34083409
{
34093410
PyCodeObject* co = (PyCodeObject*)_co;
34103411
PyFrameObject *f;
@@ -3596,7 +3597,7 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
35963597

35973598
/* Create a new generator that owns the ready to run frame
35983599
* and return that as the value. */
3599-
return PyGen_New(f);
3600+
return PyGen_NewWithQualName(f, name, qualname);
36003601
}
36013602

36023603
retval = PyEval_EvalFrameEx(f,0);
@@ -3615,6 +3616,16 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
36153616
return retval;
36163617
}
36173618

3619+
PyObject *
3620+
PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
3621+
PyObject **args, int argcount, PyObject **kws, int kwcount,
3622+
PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure)
3623+
{
3624+
return _PyEval_EvalCodeWithName(_co, globals, locals,
3625+
args, argcount, kws, kwcount,
3626+
defs, defcount, kwdefs, closure,
3627+
NULL, NULL);
3628+
}
36183629

36193630
static PyObject *
36203631
special_lookup(PyObject *o, _Py_Identifier *id)
@@ -4313,6 +4324,8 @@ fast_function(PyObject *func, PyObject ***pp_stack, int n, int na, int nk)
43134324
PyObject *globals = PyFunction_GET_GLOBALS(func);
43144325
PyObject *argdefs = PyFunction_GET_DEFAULTS(func);
43154326
PyObject *kwdefs = PyFunction_GET_KW_DEFAULTS(func);
4327+
PyObject *name = ((PyFunctionObject *)func) -> func_name;
4328+
PyObject *qualname = ((PyFunctionObject *)func) -> func_qualname;
43164329
PyObject **d = NULL;
43174330
int nd = 0;
43184331

@@ -4355,10 +4368,11 @@ fast_function(PyObject *func, PyObject ***pp_stack, int n, int na, int nk)
43554368
d = &PyTuple_GET_ITEM(argdefs, 0);
43564369
nd = Py_SIZE(argdefs);
43574370
}
4358-
return PyEval_EvalCodeEx((PyObject*)co, globals,
4359-
(PyObject *)NULL, (*pp_stack)-n, na,
4360-
(*pp_stack)-2*nk, nk, d, nd, kwdefs,
4361-
PyFunction_GET_CLOSURE(func));
4371+
return _PyEval_EvalCodeWithName((PyObject*)co, globals,
4372+
(PyObject *)NULL, (*pp_stack)-n, na,
4373+
(*pp_stack)-2*nk, nk, d, nd, kwdefs,
4374+
PyFunction_GET_CLOSURE(func),
4375+
name, qualname);
43624376
}
43634377

43644378
static PyObject *

0 commit comments

Comments
 (0)