# HG changeset patch # Parent a4afa956ffcde4eeca525dbfb68aebbe47f7b28f diff -r a4afa956ffcd Lib/test/test_funcattrs.py --- a/Lib/test/test_funcattrs.py Sun Dec 11 21:28:53 2011 +0000 +++ b/Lib/test/test_funcattrs.py Mon Dec 12 11:29:08 2011 +0000 @@ -342,6 +342,38 @@ 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') + + # XXX Note that it is not possible to use __qualname__ to + # distinguish a builtin bound instance method from its unbound + # equivalent. This matches the behaviour for methods + # implemented in Python. It might be better to instead raise + # AttributeError in the bound case, or give something like + # 'list().append' or 'list..append'. + + def test_main(): support.run_unittest(FunctionPropertiesTest, InstancemethodAttrTest, ArbitraryFunctionAttrTest, FunctionDictsTest, diff -r a4afa956ffcd Objects/methodobject.c --- a/Objects/methodobject.c Sun Dec 11 21:28:53 2011 +0000 +++ b/Objects/methodobject.c Mon Dec 12 11:29:08 2011 +0000 @@ -151,6 +151,41 @@ 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) { @@ -174,6 +209,7 @@ 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 -r a4afa956ffcd Objects/typeobject.c --- a/Objects/typeobject.c Sun Dec 11 21:28:53 2011 +0000 +++ b/Objects/typeobject.c Mon Dec 12 11:29:08 2011 +0000 @@ -3731,7 +3731,7 @@ 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);