From 5b62942074f2f6ae57c0e1bd8e4993dff4f5997f Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Fri, 23 Dec 2011 12:40:16 +0100 Subject: [PATCH] Issue #13577: Built-in methods and functions now have a __qualname__. Patch by sbt. --- Include/methodobject.h | 3 ++- Lib/test/test_funcattrs.py | 28 +++++++++++++++++++++++- Misc/NEWS | 3 +++ Objects/methodobject.c | 44 ++++++++++++++++++++++++++++++++++---- Objects/typeobject.c | 2 +- 5 files changed, 73 insertions(+), 7 deletions(-) diff --git a/Include/methodobject.h b/Include/methodobject.h index 7e67c0bf9a..d798d134e2 100644 --- a/Include/methodobject.h +++ b/Include/methodobject.h @@ -30,7 +30,8 @@ PyAPI_FUNC(int) PyCFunction_GetFlags(PyObject *); #define PyCFunction_GET_FUNCTION(func) \ (((PyCFunctionObject *)func) -> m_ml -> ml_meth) #define PyCFunction_GET_SELF(func) \ - (((PyCFunctionObject *)func) -> m_self) + (((PyCFunctionObject *)func) -> m_ml -> ml_flags & METH_STATIC ? \ + NULL : ((PyCFunctionObject *)func) -> m_self) #define PyCFunction_GET_FLAGS(func) \ (((PyCFunctionObject *)func) -> m_ml -> ml_flags) #endif diff --git a/Lib/test/test_funcattrs.py b/Lib/test/test_funcattrs.py index f4a38b961d..c8ed83020e 100644 --- a/Lib/test/test_funcattrs.py +++ b/Lib/test/test_funcattrs.py @@ -342,11 +342,37 @@ class StaticMethodAttrsTest(unittest.TestCase): self.assertTrue(s.__func__ is f) +class BuiltinFunctionPropertiesTest(unittest.TestCase): + # XXX Not sure where this should really go since I can't find a + # test module specifically for builtin_function_or_method. + + def test_builtin__qualname__(self): + import time + + # builtin function: + self.assertEqual(len.__qualname__, 'len') + self.assertEqual(time.time.__qualname__, 'time') + + # builtin classmethod: + self.assertEqual(dict.fromkeys.__qualname__, 'dict.fromkeys') + self.assertEqual(float.__getformat__.__qualname__, + 'float.__getformat__') + + # builtin staticmethod: + self.assertEqual(str.maketrans.__qualname__, 'str.maketrans') + self.assertEqual(bytes.maketrans.__qualname__, 'bytes.maketrans') + + # builtin bound instance method: + self.assertEqual([1, 2, 3].append.__qualname__, 'list.append') + self.assertEqual({'foo': 'bar'}.pop.__qualname__, 'dict.pop') + + def test_main(): support.run_unittest(FunctionPropertiesTest, InstancemethodAttrTest, ArbitraryFunctionAttrTest, FunctionDictsTest, FunctionDocstringTest, CellTest, - StaticMethodAttrsTest) + StaticMethodAttrsTest, + BuiltinFunctionPropertiesTest) if __name__ == "__main__": test_main() diff --git a/Misc/NEWS b/Misc/NEWS index cff3140429..1dc721af9e 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ What's New in Python 3.3 Alpha 1? Core and Builtins ----------------- +- Issue #13577: Built-in methods and functions now have a __qualname__. + Patch by sbt. + - Issue #6695: Full garbage collection runs now clear the freelist of set objects. Initial patch by Matthias Troffaes. diff --git a/Objects/methodobject.c b/Objects/methodobject.c index a7334cfe88..c3a64098dc 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -44,7 +44,7 @@ PyCFunction_GetFunction(PyObject *op) PyErr_BadInternalCall(); return NULL; } - return ((PyCFunctionObject *)op) -> m_ml -> ml_meth; + return PyCFunction_GET_FUNCTION(op); } PyObject * @@ -54,7 +54,7 @@ PyCFunction_GetSelf(PyObject *op) PyErr_BadInternalCall(); return NULL; } - return ((PyCFunctionObject *)op) -> m_self; + return PyCFunction_GET_SELF(op); } int @@ -64,7 +64,7 @@ PyCFunction_GetFlags(PyObject *op) PyErr_BadInternalCall(); return -1; } - return ((PyCFunctionObject *)op) -> m_ml -> ml_flags; + return PyCFunction_GET_FLAGS(op); } PyObject * @@ -151,6 +151,41 @@ meth_get__name__(PyCFunctionObject *m, void *closure) return PyUnicode_FromString(m->m_ml->ml_name); } +static PyObject * +meth_get__qualname__(PyCFunctionObject *m, void *closure) +{ + /* If __self__ is a module or NULL, return m.__name__ + (e.g. len.__qualname__ == 'len') + + If __self__ is a type, return m.__self__.__qualname__ + '.' + m.__name__ + (e.g. dict.fromkeys.__qualname__ == 'dict.fromkeys') + + Otherwise return type(m.__self__).__qualname__ + '.' + m.__name__ + (e.g. [].append.__qualname__ == 'list.append') */ + PyObject *type, *type_qualname, *res; + _Py_IDENTIFIER(__qualname__); + + if (m->m_self == NULL || PyModule_Check(m->m_self)) + return PyUnicode_FromString(m->m_ml->ml_name); + + type = PyType_Check(m->m_self) ? m->m_self : (PyObject*)Py_TYPE(m->m_self); + + type_qualname = _PyObject_GetAttrId(type, &PyId___qualname__); + if (type_qualname == NULL) + return NULL; + + if (!PyUnicode_Check(type_qualname)) { + PyErr_SetString(PyExc_TypeError, ".__class__." + "__qualname__ is not a unicode object"); + Py_XDECREF(type_qualname); + return NULL; + } + + res = PyUnicode_FromFormat("%S.%s", type_qualname, m->m_ml->ml_name); + Py_DECREF(type_qualname); + return res; +} + static int meth_traverse(PyCFunctionObject *m, visitproc visit, void *arg) { @@ -164,7 +199,7 @@ meth_get__self__(PyCFunctionObject *m, void *closure) { PyObject *self; - self = m->m_self; + self = PyCFunction_GET_SELF(m); if (self == NULL) self = Py_None; Py_INCREF(self); @@ -174,6 +209,7 @@ meth_get__self__(PyCFunctionObject *m, void *closure) static PyGetSetDef meth_getsets [] = { {"__doc__", (getter)meth_get__doc__, NULL, NULL}, {"__name__", (getter)meth_get__name__, NULL, NULL}, + {"__qualname__", (getter)meth_get__qualname__, NULL, NULL}, {"__self__", (getter)meth_get__self__, NULL, NULL}, {0} }; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 02999d6e9d..508e35c4d2 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3726,7 +3726,7 @@ add_methods(PyTypeObject *type, PyMethodDef *meth) descr = PyDescr_NewClassMethod(type, meth); } else if (meth->ml_flags & METH_STATIC) { - PyObject *cfunc = PyCFunction_New(meth, NULL); + PyObject *cfunc = PyCFunction_New(meth, (PyObject*)type); if (cfunc == NULL) return -1; descr = PyStaticMethod_New(cfunc); -- 2.50.1