Commit 0c19e215 authored by Stefan Behnel's avatar Stefan Behnel

add __name__ and __qualname__ properties to generators

parent 7cb37ff0
...@@ -9,6 +9,10 @@ Latest ...@@ -9,6 +9,10 @@ Latest
Features added Features added
-------------- --------------
* Generators have new properties ``__name__`` and ``__qualname__``
that provide the plain/qualified name of the generator function
(following CPython 3.5).
* The ``inline`` function modifier is available as a decorator * The ``inline`` function modifier is available as a decorator
``@cython.inline`` in pure mode. ``@cython.inline`` in pure mode.
......
...@@ -3792,11 +3792,13 @@ class GeneratorDefNode(DefNode): ...@@ -3792,11 +3792,13 @@ class GeneratorDefNode(DefNode):
def generate_function_body(self, env, code): def generate_function_body(self, env, code):
body_cname = self.gbody.entry.func_cname body_cname = self.gbody.entry.func_cname
name = code.intern_identifier(self.name)
qualname = code.intern_identifier(self.qualname)
code.putln('{') code.putln('{')
code.putln('__pyx_GeneratorObject *gen = __Pyx_Generator_New(' code.putln('__pyx_GeneratorObject *gen = __Pyx_Generator_New('
'(__pyx_generator_body_t) %s, (PyObject *) %s); %s' % ( '(__pyx_generator_body_t) %s, (PyObject *) %s, %s, %s); %s' % (
body_cname, Naming.cur_scope_cname, body_cname, Naming.cur_scope_cname, name, qualname,
code.error_goto_if_null('gen', self.pos))) code.error_goto_if_null('gen', self.pos)))
code.put_decref(Naming.cur_scope_cname, py_object_type) code.put_decref(Naming.cur_scope_cname, py_object_type)
if self.requires_classobj: if self.requires_classobj:
......
...@@ -1847,8 +1847,6 @@ class CalculateQualifiedNamesTransform(EnvTransform): ...@@ -1847,8 +1847,6 @@ class CalculateQualifiedNamesTransform(EnvTransform):
qualname = self.qualified_name qualname = self.qualified_name
node.qualname = EncodedString('.'.join(qualname)) node.qualname = EncodedString('.'.join(qualname))
node.module_name = self.module_name node.module_name = self.module_name
self.visitchildren(node)
return node
def _append_entry(self, entry): def _append_entry(self, entry):
if entry.is_pyglobal and not entry.is_pyclass_attr: if entry.is_pyglobal and not entry.is_pyclass_attr:
...@@ -1857,16 +1855,23 @@ class CalculateQualifiedNamesTransform(EnvTransform): ...@@ -1857,16 +1855,23 @@ class CalculateQualifiedNamesTransform(EnvTransform):
self.qualified_name.append(entry.name) self.qualified_name.append(entry.name)
def visit_ClassNode(self, node): def visit_ClassNode(self, node):
return self._set_qualname(node, node.name) self._set_qualname(node, node.name)
self.visitchildren(node)
return node
def visit_PyClassNamespaceNode(self, node): def visit_PyClassNamespaceNode(self, node):
# class name was already added by parent node # class name was already added by parent node
return self._set_qualname(node) self._set_qualname(node)
self.visitchildren(node)
return node
def visit_PyCFunctionNode(self, node): def visit_PyCFunctionNode(self, node):
return self._set_qualname(node, node.def_node.name) self._set_qualname(node, node.def_node.name)
self.visitchildren(node)
return node
def visit_FuncDefNode(self, node): def visit_FuncDefNode(self, node):
self._set_qualname(node, node.name)
orig_qualified_name = self.qualified_name[:] orig_qualified_name = self.qualified_name[:]
if getattr(node, 'name', None) == '<lambda>': if getattr(node, 'name', None) == '<lambda>':
self.qualified_name.append('<lambda>') self.qualified_name.append('<lambda>')
......
...@@ -37,13 +37,15 @@ typedef struct { ...@@ -37,13 +37,15 @@ typedef struct {
PyObject *gi_weakreflist; PyObject *gi_weakreflist;
PyObject *classobj; PyObject *classobj;
PyObject *yieldfrom; PyObject *yieldfrom;
PyObject *gi_name;
PyObject *gi_qualname;
int resume_label; int resume_label;
// using T_BOOL for property below requires char value // using T_BOOL for property below requires char value
char is_running; char is_running;
} __pyx_GeneratorObject; } __pyx_GeneratorObject;
static __pyx_GeneratorObject *__Pyx_Generator_New(__pyx_generator_body_t body, static __pyx_GeneratorObject *__Pyx_Generator_New(__pyx_generator_body_t body,
PyObject *closure); PyObject *closure, PyObject *name, PyObject *qualname);
static int __pyx_Generator_init(void); static int __pyx_Generator_init(void);
static int __Pyx_Generator_clear(PyObject* self); static int __Pyx_Generator_clear(PyObject* self);
...@@ -536,12 +538,70 @@ static void __Pyx_Generator_del(PyObject *self) { ...@@ -536,12 +538,70 @@ static void __Pyx_Generator_del(PyObject *self) {
#endif #endif
} }
static PyObject *
__Pyx_Generator_get_name(__pyx_GeneratorObject *self)
{
Py_INCREF(self->gi_name);
return self->gi_name;
}
static int
__Pyx_Generator_set_name(__pyx_GeneratorObject *self, PyObject *value)
{
PyObject *tmp;
#if PY_MAJOR_VERSION >= 3
if (unlikely(value == NULL || !PyUnicode_Check(value))) {
#else
if (unlikely(value == NULL || !PyString_Check(value))) {
#endif
PyErr_SetString(PyExc_TypeError,
"__name__ must be set to a string object");
return -1;
}
tmp = self->gi_name;
Py_INCREF(value);
self->gi_name = value;
Py_XDECREF(tmp);
return 0;
}
static PyObject *
__Pyx_Generator_get_qualname(__pyx_GeneratorObject *self)
{
Py_INCREF(self->gi_qualname);
return self->gi_qualname;
}
static int
__Pyx_Generator_set_qualname(__pyx_GeneratorObject *self, PyObject *value)
{
PyObject *tmp;
#if PY_MAJOR_VERSION >= 3
if (unlikely(value == NULL || !PyUnicode_Check(value))) {
#else
if (unlikely(value == NULL || !PyString_Check(value))) {
#endif
PyErr_SetString(PyExc_TypeError,
"__qualname__ must be set to a string object");
return -1;
}
tmp = self->gi_qualname;
Py_INCREF(value);
self->gi_qualname = value;
Py_XDECREF(tmp);
return 0;
}
static PyGetSetDef __pyx_Generator_getsets[] = {
{(char *) "__name__", (getter)__Pyx_Generator_get_name, (setter)__Pyx_Generator_set_name, 0, 0},
{(char *) "__qualname__", (getter)__Pyx_Generator_get_qualname, (setter)__Pyx_Generator_set_qualname, 0, 0},
{0, 0, 0, 0, 0}
};
static PyMemberDef __pyx_Generator_memberlist[] = { static PyMemberDef __pyx_Generator_memberlist[] = {
{(char *) "gi_running", {(char *) "gi_running", T_BOOL, offsetof(__pyx_GeneratorObject, is_running), READONLY, NULL},
T_BOOL,
offsetof(__pyx_GeneratorObject, is_running),
READONLY,
NULL},
{0, 0, 0, 0, 0} {0, 0, 0, 0, 0}
}; };
...@@ -586,7 +646,7 @@ static PyTypeObject __pyx_GeneratorType_type = { ...@@ -586,7 +646,7 @@ static PyTypeObject __pyx_GeneratorType_type = {
(iternextfunc) __Pyx_Generator_Next, /*tp_iternext*/ (iternextfunc) __Pyx_Generator_Next, /*tp_iternext*/
__pyx_Generator_methods, /*tp_methods*/ __pyx_Generator_methods, /*tp_methods*/
__pyx_Generator_memberlist, /*tp_members*/ __pyx_Generator_memberlist, /*tp_members*/
0, /*tp_getset*/ __pyx_Generator_getsets, /*tp_getset*/
0, /*tp_base*/ 0, /*tp_base*/
0, /*tp_dict*/ 0, /*tp_dict*/
0, /*tp_descr_get*/ 0, /*tp_descr_get*/
...@@ -614,7 +674,7 @@ static PyTypeObject __pyx_GeneratorType_type = { ...@@ -614,7 +674,7 @@ static PyTypeObject __pyx_GeneratorType_type = {
}; };
static __pyx_GeneratorObject *__Pyx_Generator_New(__pyx_generator_body_t body, static __pyx_GeneratorObject *__Pyx_Generator_New(__pyx_generator_body_t body,
PyObject *closure) { PyObject *closure, PyObject *name, PyObject *qualname) {
__pyx_GeneratorObject *gen = __pyx_GeneratorObject *gen =
PyObject_GC_New(__pyx_GeneratorObject, &__pyx_GeneratorType_type); PyObject_GC_New(__pyx_GeneratorObject, &__pyx_GeneratorType_type);
...@@ -632,6 +692,8 @@ static __pyx_GeneratorObject *__Pyx_Generator_New(__pyx_generator_body_t body, ...@@ -632,6 +692,8 @@ static __pyx_GeneratorObject *__Pyx_Generator_New(__pyx_generator_body_t body,
gen->exc_value = NULL; gen->exc_value = NULL;
gen->exc_traceback = NULL; gen->exc_traceback = NULL;
gen->gi_weakreflist = NULL; gen->gi_weakreflist = NULL;
gen->gi_qualname = qualname;
gen->gi_name = name;
PyObject_GC_Track(gen); PyObject_GC_Track(gen);
return gen; return gen;
......
...@@ -23,6 +23,7 @@ def very_simple(): ...@@ -23,6 +23,7 @@ def very_simple():
>>> next(x) >>> next(x)
Traceback (most recent call last): Traceback (most recent call last):
StopIteration StopIteration
>>> x = very_simple() >>> x = very_simple()
>>> x.send(1) >>> x.send(1)
Traceback (most recent call last): Traceback (most recent call last):
...@@ -31,6 +32,35 @@ def very_simple(): ...@@ -31,6 +32,35 @@ def very_simple():
yield 1 yield 1
def attributes():
"""
>>> x = attributes()
>>> x.__name__
'attributes'
>>> x.__qualname__
'attributes'
>>> x.gi_running # before next()
False
>>> inner = next(x)
>>> x.gi_running # after next()
False
>>> next(x)
Traceback (most recent call last):
StopIteration
>>> x.gi_running # after termination
False
>>> y = inner()
>>> y.__name__
'<lambda>'
>>> y.__qualname__
'attributes.<locals>.inner.<locals>.<lambda>'
"""
def inner():
return (lambda : (yield 1))
yield inner()
def simple(): def simple():
""" """
>>> x = simple() >>> x = simple()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment