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

add __name__ and __qualname__ properties to generators

parent 7cb37ff0
......@@ -9,6 +9,10 @@ Latest
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
``@cython.inline`` in pure mode.
......
......@@ -3792,11 +3792,13 @@ class GeneratorDefNode(DefNode):
def generate_function_body(self, env, code):
body_cname = self.gbody.entry.func_cname
name = code.intern_identifier(self.name)
qualname = code.intern_identifier(self.qualname)
code.putln('{')
code.putln('__pyx_GeneratorObject *gen = __Pyx_Generator_New('
'(__pyx_generator_body_t) %s, (PyObject *) %s); %s' % (
body_cname, Naming.cur_scope_cname,
'(__pyx_generator_body_t) %s, (PyObject *) %s, %s, %s); %s' % (
body_cname, Naming.cur_scope_cname, name, qualname,
code.error_goto_if_null('gen', self.pos)))
code.put_decref(Naming.cur_scope_cname, py_object_type)
if self.requires_classobj:
......
......@@ -1847,8 +1847,6 @@ class CalculateQualifiedNamesTransform(EnvTransform):
qualname = self.qualified_name
node.qualname = EncodedString('.'.join(qualname))
node.module_name = self.module_name
self.visitchildren(node)
return node
def _append_entry(self, entry):
if entry.is_pyglobal and not entry.is_pyclass_attr:
......@@ -1857,16 +1855,23 @@ class CalculateQualifiedNamesTransform(EnvTransform):
self.qualified_name.append(entry.name)
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):
# 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):
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):
self._set_qualname(node, node.name)
orig_qualified_name = self.qualified_name[:]
if getattr(node, 'name', None) == '<lambda>':
self.qualified_name.append('<lambda>')
......
......@@ -37,13 +37,15 @@ typedef struct {
PyObject *gi_weakreflist;
PyObject *classobj;
PyObject *yieldfrom;
PyObject *gi_name;
PyObject *gi_qualname;
int resume_label;
// using T_BOOL for property below requires char value
char is_running;
} __pyx_GeneratorObject;
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_clear(PyObject* self);
......@@ -536,12 +538,70 @@ static void __Pyx_Generator_del(PyObject *self) {
#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[] = {
{(char *) "gi_running",
T_BOOL,
offsetof(__pyx_GeneratorObject, is_running),
READONLY,
NULL},
{(char *) "gi_running", T_BOOL, offsetof(__pyx_GeneratorObject, is_running), READONLY, NULL},
{0, 0, 0, 0, 0}
};
......@@ -586,7 +646,7 @@ static PyTypeObject __pyx_GeneratorType_type = {
(iternextfunc) __Pyx_Generator_Next, /*tp_iternext*/
__pyx_Generator_methods, /*tp_methods*/
__pyx_Generator_memberlist, /*tp_members*/
0, /*tp_getset*/
__pyx_Generator_getsets, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
......@@ -614,7 +674,7 @@ static PyTypeObject __pyx_GeneratorType_type = {
};
static __pyx_GeneratorObject *__Pyx_Generator_New(__pyx_generator_body_t body,
PyObject *closure) {
PyObject *closure, PyObject *name, PyObject *qualname) {
__pyx_GeneratorObject *gen =
PyObject_GC_New(__pyx_GeneratorObject, &__pyx_GeneratorType_type);
......@@ -632,6 +692,8 @@ static __pyx_GeneratorObject *__Pyx_Generator_New(__pyx_generator_body_t body,
gen->exc_value = NULL;
gen->exc_traceback = NULL;
gen->gi_weakreflist = NULL;
gen->gi_qualname = qualname;
gen->gi_name = name;
PyObject_GC_Track(gen);
return gen;
......
......@@ -23,6 +23,7 @@ def very_simple():
>>> next(x)
Traceback (most recent call last):
StopIteration
>>> x = very_simple()
>>> x.send(1)
Traceback (most recent call last):
......@@ -31,6 +32,35 @@ def very_simple():
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():
"""
>>> 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