Commit 7a91570a authored by Stefan Behnel's avatar Stefan Behnel

implement __qualname__ special attribute on Python functions/classes (PEP 3155)

parent 8c941d9d
......@@ -8,6 +8,9 @@ Cython Changelog
Features added
--------------
* Python functions/classes provide the special attribute "__qualname__"
as defined by PEP 3155.
* Added a directive ``overflowcheck`` which raises an OverflowException when
arithmetic with C ints overflow. This has a modest performance penalty, but
is much faster than using Python ints.
......
......@@ -6017,13 +6017,22 @@ class DictItemNode(ExprNode):
class ModuleNameMixin(object):
def set_mod_name(self, env):
def set_qualified_name(self, env, self_name):
self.module_name = env.global_scope().qualified_name
prefix = env.qualified_name[len(self.module_name)+1:]
if prefix:
self_name = prefix + '.' + self_name
self.qualname = StringEncoding.EncodedString(self_name)
def get_py_mod_name(self, code):
return code.get_py_string_const(
self.module_name, identifier=True)
def get_py_qualified_name(self, code):
return code.get_py_string_const(
self.qualname, identifier=True)
class ClassNode(ExprNode, ModuleNameMixin):
# Helper class used in the implementation of Python
# class definitions. Constructs a class object given
......@@ -6046,7 +6055,7 @@ class ClassNode(ExprNode, ModuleNameMixin):
self.is_temp = 1
env.use_utility_code(UtilityCode.load_cached("CreateClass", "ObjectHandling.c"))
#TODO(craig,haoyu) This should be moved to a better place
self.set_mod_name(env)
self.set_qualified_name(env, self.name)
def may_be_none(self):
return True
......@@ -6062,12 +6071,14 @@ class ClassNode(ExprNode, ModuleNameMixin):
self.dict.py_result(),
self.doc.py_result()))
py_mod_name = self.get_py_mod_name(code)
qualname = self.get_py_qualified_name(code)
code.putln(
'%s = __Pyx_CreateClass(%s, %s, %s, %s); %s' % (
'%s = __Pyx_CreateClass(%s, %s, %s, %s, %s); %s' % (
self.result(),
self.bases.py_result(),
self.dict.py_result(),
cname,
qualname,
py_mod_name,
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result())
......@@ -6264,7 +6275,7 @@ class PyClassNamespaceNode(ExprNode, ModuleNameMixin):
self.type = py_object_type
self.is_temp = 1
#TODO(craig,haoyu) This should be moved to a better place
self.set_mod_name(env)
self.set_qualified_name(env, self.name)
def may_be_none(self):
return True
......@@ -6272,16 +6283,18 @@ class PyClassNamespaceNode(ExprNode, ModuleNameMixin):
def generate_result_code(self, code):
cname = code.intern_identifier(self.name)
py_mod_name = self.get_py_mod_name(code)
qualname = self.get_py_qualified_name(code)
if self.doc:
doc_code = self.doc.result()
else:
doc_code = '(PyObject *) NULL'
code.putln(
"%s = __Pyx_Py3MetaclassPrepare(%s, %s, %s, %s, %s, %s); %s" % (
"%s = __Pyx_Py3MetaclassPrepare(%s, %s, %s, %s, %s, %s, %s); %s" % (
self.result(),
self.metaclass.result(),
self.bases.result(),
cname,
qualname,
self.mkw.result(),
py_mod_name,
doc_code,
......@@ -6449,7 +6462,7 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
self.analyse_default_args(env)
#TODO(craig,haoyu) This should be moved to a better place
self.set_mod_name(env)
self.set_qualified_name(env, self.def_node.name)
def analyse_default_args(self, env):
"""
......@@ -6575,15 +6588,15 @@ class PyCFunctionNode(ExprNode, ModuleNameMixin):
else:
flags = '0'
py_mod_name = self.get_py_mod_name(code)
code.putln(
'%s = %s(&%s, %s, %s, %s, %s); %s' % (
'%s = %s(&%s, %s, %s, %s, %s, %s); %s' % (
self.result(),
constructor,
self.pymethdef_cname,
flags,
self.get_py_qualified_name(code),
self.self_result_code(),
py_mod_name,
self.get_py_mod_name(code),
code_object_result,
code.error_goto_if_null(self.result(), self.pos)))
......
......@@ -25,6 +25,7 @@ typedef struct {
PyObject *func_dict;
PyObject *func_weakreflist;
PyObject *func_name;
PyObject *func_qualname;
PyObject *func_doc;
PyObject *func_code;
PyObject *func_closure;
......@@ -41,11 +42,11 @@ typedef struct {
static PyTypeObject *__pyx_CyFunctionType = 0;
#define __Pyx_CyFunction_NewEx(ml, flags, self, module, code) \
__Pyx_CyFunction_New(__pyx_CyFunctionType, ml, flags, self, module, code)
#define __Pyx_CyFunction_NewEx(ml, flags, qualname, self, module, code) \
__Pyx_CyFunction_New(__pyx_CyFunctionType, ml, flags, qualname, self, module, code)
static PyObject *__Pyx_CyFunction_New(PyTypeObject *,
PyMethodDef *ml, int flags,
static PyObject *__Pyx_CyFunction_New(PyTypeObject *, PyMethodDef *ml,
int flags, PyObject* qualname,
PyObject *self, PyObject *module,
PyObject* code);
......@@ -127,6 +128,34 @@ __Pyx_CyFunction_set_name(__pyx_CyFunctionObject *op, PyObject *value)
return 0;
}
static PyObject *
__Pyx_CyFunction_get_qualname(__pyx_CyFunctionObject *op)
{
Py_INCREF(op->func_qualname);
return op->func_qualname;
}
static int
__Pyx_CyFunction_set_qualname(__pyx_CyFunctionObject *op, PyObject *value)
{
PyObject *tmp;
#if PY_MAJOR_VERSION >= 3
if (value == NULL || !PyUnicode_Check(value)) {
#else
if (value == NULL || !PyString_Check(value)) {
#endif
PyErr_SetString(PyExc_TypeError,
"__qualname__ must be set to a string object");
return -1;
}
tmp = op->func_qualname;
Py_INCREF(value);
op->func_qualname = value;
Py_XDECREF(tmp);
return 0;
}
static PyObject *
__Pyx_CyFunction_get_self(__pyx_CyFunctionObject *m, CYTHON_UNUSED void *closure)
{
......@@ -230,6 +259,7 @@ static PyGetSetDef __pyx_CyFunction_getsets[] = {
{(char *) "__doc__", (getter)__Pyx_CyFunction_get_doc, (setter)__Pyx_CyFunction_set_doc, 0, 0},
{(char *) "func_name", (getter)__Pyx_CyFunction_get_name, (setter)__Pyx_CyFunction_set_name, 0, 0},
{(char *) "__name__", (getter)__Pyx_CyFunction_get_name, (setter)__Pyx_CyFunction_set_name, 0, 0},
{(char *) "__qualname__", (getter)__Pyx_CyFunction_get_qualname, (setter)__Pyx_CyFunction_set_qualname, 0, 0},
{(char *) "__self__", (getter)__Pyx_CyFunction_get_self, 0, 0, 0},
{(char *) "func_dict", (getter)__Pyx_CyFunction_get_dict, (setter)__Pyx_CyFunction_set_dict, 0, 0},
{(char *) "__dict__", (getter)__Pyx_CyFunction_get_dict, (setter)__Pyx_CyFunction_set_dict, 0, 0},
......@@ -269,7 +299,7 @@ static PyMethodDef __pyx_CyFunction_methods[] = {
};
static PyObject *__Pyx_CyFunction_New(PyTypeObject *type, PyMethodDef *ml, int flags,
static PyObject *__Pyx_CyFunction_New(PyTypeObject *type, PyMethodDef *ml, int flags, PyObject* qualname,
PyObject *closure, PyObject *module, PyObject* code) {
__pyx_CyFunctionObject *op = PyObject_GC_New(__pyx_CyFunctionObject, type);
if (op == NULL)
......@@ -284,6 +314,8 @@ static PyObject *__Pyx_CyFunction_New(PyTypeObject *type, PyMethodDef *ml, int f
op->func.m_module = module;
op->func_dict = NULL;
op->func_name = NULL;
Py_INCREF(qualname);
op->func_qualname = qualname;
op->func_doc = NULL;
op->func_classobj = NULL;
Py_XINCREF(code);
......@@ -304,6 +336,7 @@ __Pyx_CyFunction_clear(__pyx_CyFunctionObject *m)
Py_CLEAR(m->func.m_module);
Py_CLEAR(m->func_dict);
Py_CLEAR(m->func_name);
Py_CLEAR(m->func_qualname);
Py_CLEAR(m->func_doc);
Py_CLEAR(m->func_code);
Py_CLEAR(m->func_classobj);
......@@ -338,6 +371,7 @@ static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit,
Py_VISIT(m->func.m_module);
Py_VISIT(m->func_dict);
Py_VISIT(m->func_name);
Py_VISIT(m->func_qualname);
Py_VISIT(m->func_doc);
Py_VISIT(m->func_code);
Py_VISIT(m->func_classobj);
......@@ -378,14 +412,12 @@ static PyObject *__Pyx_CyFunction_descr_get(PyObject *func, PyObject *obj, PyObj
static PyObject*
__Pyx_CyFunction_repr(__pyx_CyFunctionObject *op)
{
PyObject *func_name = __Pyx_CyFunction_get_name(op);
#if PY_MAJOR_VERSION >= 3
return PyUnicode_FromFormat("<cyfunction %U at %p>",
func_name, (void *)op);
op->func_qualname, (void *)op);
#else
return PyString_FromFormat("<cyfunction %s at %p>",
PyString_AsString(func_name), (void *)op);
PyString_AsString(op->func_qualname), (void *)op);
#endif
}
......@@ -555,11 +587,11 @@ typedef struct {
PyObject *self;
} __pyx_FusedFunctionObject;
#define __pyx_FusedFunction_NewEx(ml, flags, self, module, code) \
__pyx_FusedFunction_New(__pyx_FusedFunctionType, ml, flags, self, module, code)
#define __pyx_FusedFunction_NewEx(ml, flags, qualname, self, module, code) \
__pyx_FusedFunction_New(__pyx_FusedFunctionType, ml, flags, qualname, self, module, code)
static PyObject *__pyx_FusedFunction_New(PyTypeObject *type,
PyMethodDef *ml, int flags,
PyObject *self, PyObject *module,
PyObject *qualname, PyObject *self, PyObject *module,
PyObject *code);
static PyTypeObject *__pyx_FusedFunctionType = NULL;
......@@ -571,11 +603,12 @@ static int __pyx_FusedFunction_init(void);
//@requires: CythonFunction
static PyObject *
__pyx_FusedFunction_New(PyTypeObject *type, PyMethodDef *ml, int flags, PyObject *self,
__pyx_FusedFunction_New(PyTypeObject *type, PyMethodDef *ml, int flags,
PyObject *qualname, PyObject *self,
PyObject *module, PyObject *code)
{
__pyx_FusedFunctionObject *fusedfunc =
(__pyx_FusedFunctionObject *) __Pyx_CyFunction_New(type, ml, flags,
(__pyx_FusedFunctionObject *) __Pyx_CyFunction_New(type, ml, flags, qualname,
self, module, code);
if (!fusedfunc)
return NULL;
......@@ -632,6 +665,7 @@ __pyx_FusedFunction_descr_get(PyObject *self, PyObject *obj, PyObject *type)
meth = (__pyx_FusedFunctionObject *) __pyx_FusedFunction_NewEx(
((PyCFunctionObject *) func)->m_ml,
((__pyx_CyFunctionObject *) func)->flags,
((__pyx_CyFunctionObject *) func)->func_qualname,
((__pyx_CyFunctionObject *) func)->func_closure,
((PyCFunctionObject *) func)->m_module,
((__pyx_CyFunctionObject *) func)->func_code);
......
......@@ -443,18 +443,20 @@ static PyObject *__Pyx_Py3MetaclassGet(PyObject *bases, PyObject *mkw) {
/////////////// CreateClass.proto ///////////////
static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *name,
PyObject *modname); /*proto*/
PyObject *qualname, PyObject *modname); /*proto*/
/////////////// CreateClass ///////////////
//@requires: FindPy2Metaclass
static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *name,
PyObject *modname) {
PyObject *qualname, PyObject *modname) {
PyObject *result;
PyObject *metaclass;
if (PyDict_SetItemString(dict, "__module__", modname) < 0)
return NULL;
if (PyDict_SetItemString(dict, "__qualname__", qualname) < 0)
return NULL;
/* Python2 __metaclass__ */
metaclass = PyDict_GetItemString(dict, "__metaclass__");
......@@ -470,13 +472,13 @@ static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *na
/////////////// Py3ClassCreate.proto ///////////////
static PyObject *__Pyx_Py3MetaclassPrepare(PyObject *metaclass, PyObject *bases, PyObject *name, PyObject *mkw, PyObject *modname, PyObject *doc); /*proto*/
static PyObject *__Pyx_Py3MetaclassPrepare(PyObject *metaclass, PyObject *bases, PyObject *name, PyObject *qualname, PyObject *mkw, PyObject *modname, PyObject *doc); /*proto*/
static PyObject *__Pyx_Py3ClassCreate(PyObject *metaclass, PyObject *name, PyObject *bases, PyObject *dict, PyObject *mkw); /*proto*/
/////////////// Py3ClassCreate ///////////////
static PyObject *__Pyx_Py3MetaclassPrepare(PyObject *metaclass, PyObject *bases, PyObject *name,
PyObject *mkw, PyObject *modname, PyObject *doc) {
PyObject *qualname, PyObject *mkw, PyObject *modname, PyObject *doc) {
PyObject *prep;
PyObject *pargs;
PyObject *ns;
......@@ -519,6 +521,24 @@ static PyObject *__Pyx_Py3MetaclassPrepare(PyObject *metaclass, PyObject *bases,
return NULL;
}
Py_DECREF(str);
#if PY_MAJOR_VERSION >= 3
str = PyUnicode_FromString("__qualname__");
#else
str = PyString_FromString("__qualname__");
#endif
if (!str) {
Py_DECREF(ns);
return NULL;
}
if (PyObject_SetItem(ns, str, qualname) < 0) {
Py_DECREF(ns);
Py_DECREF(str);
return NULL;
}
Py_DECREF(str);
if (doc) {
#if PY_MAJOR_VERSION >= 3
str = PyUnicode_FromString("__doc__");
......
......@@ -31,6 +31,38 @@ def test_name():
'foo'
"""
def test_qualname():
"""
>>> test_qualname.__qualname__
'test_qualname'
>>> test_qualname.__qualname__ = 123 #doctest:+ELLIPSIS
Traceback (most recent call last):
TypeError: __qualname__ must be set to a ... object
>>> test_qualname.__qualname__ = 'foo'
>>> test_qualname.__qualname__
'foo'
"""
def test_nested_qualname():
"""
>>> func = test_nested_qualname()
>>> func().__qualname__
'test_nested_qualname.outer.Test'
>>> func().test.__qualname__
'test_nested_qualname.outer.Test.test'
>>> func()().test.__qualname__
'test_nested_qualname.outer.Test.test'
"""
def outer():
class Test(object):
def test(self):
return 123
return Test
return outer
def test_doc():
"""
>>> del test_doc.__doc__
......
......@@ -49,7 +49,7 @@ class Py3ClassMCOnly(object, metaclass=Py3MetaclassPlusAttr):
>>> obj.metaclass_was_here
True
>>> obj._order
['__module__', '__doc__', 'bar', 'metaclass_was_here']
['__module__', '__qualname__', '__doc__', 'bar', 'metaclass_was_here']
"""
bar = 321
......@@ -76,7 +76,7 @@ class Py3Foo(object, metaclass=Py3Base, foo=123):
>>> obj.bar
321
>>> obj._order
['__module__', '__doc__', 'bar', 'foo']
['__module__', '__qualname__', '__doc__', 'bar', 'foo']
"""
bar = 321
......
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