# HG changeset patch # Parent aed9ccb56c523d3a86988038ca07e5380c2613ef diff -r aed9ccb56c52 Include/methodobject.h --- a/Include/methodobject.h Tue Dec 13 12:46:11 2011 +0000 +++ b/Include/methodobject.h Tue Dec 13 12:53:32 2011 +0000 @@ -30,6 +30,9 @@ #define PyCFunction_GET_FUNCTION(func) \ (((PyCFunctionObject *)func) -> m_ml -> ml_meth) #define PyCFunction_GET_SELF(func) \ + (((PyCFunctionObject *)func) -> m_ml -> ml_flags & METH_STATIC ? \ + NULL : ((PyCFunctionObject *)func) -> m_self) +#define _PyCFunction_GET_RAW_SELF(func) \ (((PyCFunctionObject *)func) -> m_self) #define PyCFunction_GET_FLAGS(func) \ (((PyCFunctionObject *)func) -> m_ml -> ml_flags) diff -r aed9ccb56c52 Lib/test/test_funcattrs.py --- a/Lib/test/test_funcattrs.py Tue Dec 13 12:46:11 2011 +0000 +++ b/Lib/test/test_funcattrs.py Tue Dec 13 12:53:32 2011 +0000 @@ -342,11 +342,44 @@ 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, FunctionDocstringTest, CellTest, - StaticMethodAttrsTest) + StaticMethodAttrsTest, + BuiltinFunctionPropertiesTest) if __name__ == "__main__": test_main() diff -r aed9ccb56c52 Objects/methodobject.c --- a/Objects/methodobject.c Tue Dec 13 12:46:11 2011 +0000 +++ b/Objects/methodobject.c Tue Dec 13 12:53:32 2011 +0000 @@ -54,6 +54,8 @@ PyErr_BadInternalCall(); return NULL; } + if (((PyCFunctionObject *)op) -> m_ml -> ml_flags | METH_STATIC) + return NULL; return ((PyCFunctionObject *)op) -> m_self; } @@ -72,10 +74,12 @@ { PyCFunctionObject* f = (PyCFunctionObject*)func; PyCFunction meth = PyCFunction_GET_FUNCTION(func); - PyObject *self = PyCFunction_GET_SELF(func); + int flags = PyCFunction_GET_FLAGS(func); + PyObject *self = flags & METH_STATIC ? NULL : + _PyCFunction_GET_RAW_SELF(func); Py_ssize_t size; - switch (PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST)) { + switch (flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST)) { case METH_VARARGS: if (kw == NULL || PyDict_Size(kw) == 0) return (*meth)(self, arg); @@ -151,6 +155,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) { @@ -165,7 +204,7 @@ PyObject *self; self = m->m_self; - if (self == NULL) + if (self == NULL || PyCFunction_GET_FLAGS(m) & METH_STATIC) self = Py_None; Py_INCREF(self); return self; @@ -174,6 +213,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 aed9ccb56c52 Objects/typeobject.c --- a/Objects/typeobject.c Tue Dec 13 12:46:11 2011 +0000 +++ b/Objects/typeobject.c Tue Dec 13 12:53:32 2011 +0000 @@ -3724,7 +3724,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); diff -r aed9ccb56c52 Python/ceval.c --- a/Python/ceval.c Tue Dec 13 12:46:11 2011 +0000 +++ b/Python/ceval.c Tue Dec 13 12:53:32 2011 +0000 @@ -3982,7 +3982,8 @@ PCALL(PCALL_CFUNCTION); if (flags & (METH_NOARGS | METH_O)) { PyCFunction meth = PyCFunction_GET_FUNCTION(func); - PyObject *self = PyCFunction_GET_SELF(func); + PyObject *self = flags & METH_STATIC ? NULL : + _PyCFunction_GET_RAW_SELF(func); if (flags & METH_NOARGS && na == 0) { C_TRACE(x, (*meth)(self,NULL)); }