Skip to content

Commit 5b62942

Browse files
committed
Issue python#13577: Built-in methods and functions now have a __qualname__.
Patch by sbt.
1 parent d22983d commit 5b62942

5 files changed

Lines changed: 73 additions & 7 deletions

File tree

‎Include/methodobject.h‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ PyAPI_FUNC(int) PyCFunction_GetFlags(PyObject *);
3030
#define PyCFunction_GET_FUNCTION(func) \
3131
(((PyCFunctionObject *)func) -> m_ml -> ml_meth)
3232
#define PyCFunction_GET_SELF(func) \
33-
(((PyCFunctionObject *)func) -> m_self)
33+
(((PyCFunctionObject *)func) -> m_ml -> ml_flags & METH_STATIC ? \
34+
NULL : ((PyCFunctionObject *)func) -> m_self)
3435
#define PyCFunction_GET_FLAGS(func) \
3536
(((PyCFunctionObject *)func) -> m_ml -> ml_flags)
3637
#endif

‎Lib/test/test_funcattrs.py‎

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,11 +342,37 @@ def f():
342342
self.assertTrue(s.__func__ is f)
343343

344344

345+
class BuiltinFunctionPropertiesTest(unittest.TestCase):
346+
# XXX Not sure where this should really go since I can't find a
347+
# test module specifically for builtin_function_or_method.
348+
349+
def test_builtin__qualname__(self):
350+
import time
351+
352+
# builtin function:
353+
self.assertEqual(len.__qualname__, 'len')
354+
self.assertEqual(time.time.__qualname__, 'time')
355+
356+
# builtin classmethod:
357+
self.assertEqual(dict.fromkeys.__qualname__, 'dict.fromkeys')
358+
self.assertEqual(float.__getformat__.__qualname__,
359+
'float.__getformat__')
360+
361+
# builtin staticmethod:
362+
self.assertEqual(str.maketrans.__qualname__, 'str.maketrans')
363+
self.assertEqual(bytes.maketrans.__qualname__, 'bytes.maketrans')
364+
365+
# builtin bound instance method:
366+
self.assertEqual([1, 2, 3].append.__qualname__, 'list.append')
367+
self.assertEqual({'foo': 'bar'}.pop.__qualname__, 'dict.pop')
368+
369+
345370
def test_main():
346371
support.run_unittest(FunctionPropertiesTest, InstancemethodAttrTest,
347372
ArbitraryFunctionAttrTest, FunctionDictsTest,
348373
FunctionDocstringTest, CellTest,
349-
StaticMethodAttrsTest)
374+
StaticMethodAttrsTest,
375+
BuiltinFunctionPropertiesTest)
350376

351377
if __name__ == "__main__":
352378
test_main()

‎Misc/NEWS‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ What's New in Python 3.3 Alpha 1?
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #13577: Built-in methods and functions now have a __qualname__.
14+
Patch by sbt.
15+
1316
- Issue #6695: Full garbage collection runs now clear the freelist of set
1417
objects. Initial patch by Matthias Troffaes.
1518

‎Objects/methodobject.c‎

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ PyCFunction_GetFunction(PyObject *op)
4444
PyErr_BadInternalCall();
4545
return NULL;
4646
}
47-
return ((PyCFunctionObject *)op) -> m_ml -> ml_meth;
47+
return PyCFunction_GET_FUNCTION(op);
4848
}
4949

5050
PyObject *
@@ -54,7 +54,7 @@ PyCFunction_GetSelf(PyObject *op)
5454
PyErr_BadInternalCall();
5555
return NULL;
5656
}
57-
return ((PyCFunctionObject *)op) -> m_self;
57+
return PyCFunction_GET_SELF(op);
5858
}
5959

6060
int
@@ -64,7 +64,7 @@ PyCFunction_GetFlags(PyObject *op)
6464
PyErr_BadInternalCall();
6565
return -1;
6666
}
67-
return ((PyCFunctionObject *)op) -> m_ml -> ml_flags;
67+
return PyCFunction_GET_FLAGS(op);
6868
}
6969

7070
PyObject *
@@ -151,6 +151,41 @@ meth_get__name__(PyCFunctionObject *m, void *closure)
151151
return PyUnicode_FromString(m->m_ml->ml_name);
152152
}
153153

154+
static PyObject *
155+
meth_get__qualname__(PyCFunctionObject *m, void *closure)
156+
{
157+
/* If __self__ is a module or NULL, return m.__name__
158+
(e.g. len.__qualname__ == 'len')
159+
160+
If __self__ is a type, return m.__self__.__qualname__ + '.' + m.__name__
161+
(e.g. dict.fromkeys.__qualname__ == 'dict.fromkeys')
162+
163+
Otherwise return type(m.__self__).__qualname__ + '.' + m.__name__
164+
(e.g. [].append.__qualname__ == 'list.append') */
165+
PyObject *type, *type_qualname, *res;
166+
_Py_IDENTIFIER(__qualname__);
167+
168+
if (m->m_self == NULL || PyModule_Check(m->m_self))
169+
return PyUnicode_FromString(m->m_ml->ml_name);
170+
171+
type = PyType_Check(m->m_self) ? m->m_self : (PyObject*)Py_TYPE(m->m_self);
172+
173+
type_qualname = _PyObject_GetAttrId(type, &PyId___qualname__);
174+
if (type_qualname == NULL)
175+
return NULL;
176+
177+
if (!PyUnicode_Check(type_qualname)) {
178+
PyErr_SetString(PyExc_TypeError, "<method>.__class__."
179+
"__qualname__ is not a unicode object");
180+
Py_XDECREF(type_qualname);
181+
return NULL;
182+
}
183+
184+
res = PyUnicode_FromFormat("%S.%s", type_qualname, m->m_ml->ml_name);
185+
Py_DECREF(type_qualname);
186+
return res;
187+
}
188+
154189
static int
155190
meth_traverse(PyCFunctionObject *m, visitproc visit, void *arg)
156191
{
@@ -164,7 +199,7 @@ meth_get__self__(PyCFunctionObject *m, void *closure)
164199
{
165200
PyObject *self;
166201

167-
self = m->m_self;
202+
self = PyCFunction_GET_SELF(m);
168203
if (self == NULL)
169204
self = Py_None;
170205
Py_INCREF(self);
@@ -174,6 +209,7 @@ meth_get__self__(PyCFunctionObject *m, void *closure)
174209
static PyGetSetDef meth_getsets [] = {
175210
{"__doc__", (getter)meth_get__doc__, NULL, NULL},
176211
{"__name__", (getter)meth_get__name__, NULL, NULL},
212+
{"__qualname__", (getter)meth_get__qualname__, NULL, NULL},
177213
{"__self__", (getter)meth_get__self__, NULL, NULL},
178214
{0}
179215
};

‎Objects/typeobject.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3726,7 +3726,7 @@ add_methods(PyTypeObject *type, PyMethodDef *meth)
37263726
descr = PyDescr_NewClassMethod(type, meth);
37273727
}
37283728
else if (meth->ml_flags & METH_STATIC) {
3729-
PyObject *cfunc = PyCFunction_New(meth, NULL);
3729+
PyObject *cfunc = PyCFunction_New(meth, (PyObject*)type);
37303730
if (cfunc == NULL)
37313731
return -1;
37323732
descr = PyStaticMethod_New(cfunc);

0 commit comments

Comments
 (0)