From: Stefan Behnel Date: Fri, 4 Jan 2013 15:39:01 +0000 (+0100) Subject: implement __qualname__ special attribute on Python functions/classes (PEP 3155) X-Git-Tag: 0.18b1~25 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=7a91570aefb8ad6542f157a4d1a5d760affda1e0;p=platform%2Fupstream%2Fpython-cython.git implement __qualname__ special attribute on Python functions/classes (PEP 3155) --- diff --git a/CHANGES.rst b/CHANGES.rst index 4277183..6aa349f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -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. diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 0990da8..3c4d865 100755 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -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))) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 909c512..27487b2 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -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); @@ -128,6 +129,34 @@ __Pyx_CyFunction_set_name(__pyx_CyFunctionObject *op, PyObject *value) } 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) { PyObject *self; @@ -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("", - func_name, (void *)op); + op->func_qualname, (void *)op); #else return PyString_FromFormat("", - 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); diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 5ffd5e3..2da2148 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -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__"); diff --git a/tests/run/cyfunction.pyx b/tests/run/cyfunction.pyx index 2185f09..2c3e024 100644 --- a/tests/run/cyfunction.pyx +++ b/tests/run/cyfunction.pyx @@ -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__ diff --git a/tests/run/metaclass.pyx b/tests/run/metaclass.pyx index 3247e2f..81c7a7a 100644 --- a/tests/run/metaclass.pyx +++ b/tests/run/metaclass.pyx @@ -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