]> granicus.if.org Git - python/commitdiff
use __qualname__ to compute bound method repr (closes #21389)
authorBenjamin Peterson <benjamin@python.org>
Wed, 20 Aug 2014 23:41:57 +0000 (18:41 -0500)
committerBenjamin Peterson <benjamin@python.org>
Wed, 20 Aug 2014 23:41:57 +0000 (18:41 -0500)
Patch from Steven Barker.

Lib/test/test_defaultdict.py
Lib/test/test_descr.py
Misc/NEWS
Objects/classobject.c

index 532d535981b36c1efbbea0c2dd4cd6ede7caffef..48d1cb537bc132a76204c115a691e6be09c32143 100644 (file)
@@ -157,8 +157,9 @@ class TestDefaultDict(unittest.TestCase):
             def _factory(self):
                 return []
         d = sub()
-        self.assertTrue(repr(d).startswith(
-            "defaultdict(<bound method sub._factory of defaultdict(..."))
+        self.assertRegex(repr(d),
+            r"defaultdict\(<bound method .*sub\._factory "
+            r"of defaultdict\(\.\.\., \{\}\)>, \{\}\)")
 
         # NOTE: printing a subclass of a builtin type does not call its
         # tp_print slot. So this part is essentially the same test as above.
index 634ba7e5dfeb8a39766cf0ec5ff8799c1c1a550f..39782a41f79e5fa1c2bbd3469c63b8c1cba8ff42 100644 (file)
@@ -4423,6 +4423,61 @@ order (MRO) for bases """
         self.assertIn("__dict__", Base.__dict__)
         self.assertNotIn("__dict__", Sub.__dict__)
 
+    def test_bound_method_repr(self):
+        class Foo:
+            def method(self):
+                pass
+        self.assertRegex(repr(Foo().method),
+            r"<bound method .*Foo\.method of <.*Foo object at .*>>")
+
+
+        class Base:
+            def method(self):
+                pass
+        class Derived1(Base):
+            pass
+        class Derived2(Base):
+            def method(self):
+                pass
+        base = Base()
+        derived1 = Derived1()
+        derived2 = Derived2()
+        super_d2 = super(Derived2, derived2)
+        self.assertRegex(repr(base.method),
+            r"<bound method .*Base\.method of <.*Base object at .*>>")
+        self.assertRegex(repr(derived1.method),
+            r"<bound method .*Base\.method of <.*Derived1 object at .*>>")
+        self.assertRegex(repr(derived2.method),
+            r"<bound method .*Derived2\.method of <.*Derived2 object at .*>>")
+        self.assertRegex(repr(super_d2.method),
+            r"<bound method .*Base\.method of <.*Derived2 object at .*>>")
+
+        class Foo:
+            @classmethod
+            def method(cls):
+                pass
+        foo = Foo()
+        self.assertRegex(repr(foo.method), # access via instance
+            r"<bound method .*Foo\.method of <class '.*Foo'>>")
+        self.assertRegex(repr(Foo.method), # access via the class
+            r"<bound method .*Foo\.method of <class '.*Foo'>>")
+
+
+        class MyCallable:
+            def __call__(self, arg):
+                pass
+        func = MyCallable() # func has no __name__ or __qualname__ attributes
+        instance = object()
+        method = types.MethodType(func, instance)
+        self.assertRegex(repr(method),
+            r"<bound method \? of <object object at .*>>")
+        func.__name__ = "name"
+        self.assertRegex(repr(method),
+            r"<bound method name of <object object at .*>>")
+        func.__qualname__ = "qualname"
+        self.assertRegex(repr(method),
+            r"<bound method qualname of <object object at .*>>")
+
 
 class DictProxyTests(unittest.TestCase):
     def setUp(self):
index ed7865ef8b6cbca3e1061bcfac06603ce838464e..e6e09edea6d9d4e47d521b6758ac9f8b72009d9f 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@ Release date: TBA
 Core and Builtins
 -----------------
 
+- Issue #21389: Displaying the __qualname__ of the underlying function in the
+  repr of a bound method.
+
 - Issue #22206: Using pthread, PyThread_create_key() now sets errno to ENOMEM
   and returns -1 (error) on integer overflow.
 
index 0c0bd47fbb76274e0c9643840aa328752fb0bff8..07cb6395b414ffe77411d1b31030d28fb77a0eb4 100644 (file)
@@ -15,6 +15,7 @@ static int numfree = 0;
 #endif
 
 _Py_IDENTIFIER(__name__);
+_Py_IDENTIFIER(__qualname__);
 
 PyObject *
 PyMethod_Function(PyObject *im)
@@ -243,51 +244,33 @@ method_repr(PyMethodObject *a)
 {
     PyObject *self = a->im_self;
     PyObject *func = a->im_func;
-    PyObject *klass;
-    PyObject *funcname = NULL ,*klassname = NULL, *result = NULL;
-    char *defname = "?";
+    PyObject *funcname = NULL, *result = NULL;
+    const char *defname = "?";
 
-    if (self == NULL) {
-        PyErr_BadInternalCall();
-        return NULL;
-    }
-    klass = (PyObject*)Py_TYPE(self);
-
-    funcname = _PyObject_GetAttrId(func, &PyId___name__);
+    funcname = _PyObject_GetAttrId(func, &PyId___qualname__);
     if (funcname == NULL) {
         if (!PyErr_ExceptionMatches(PyExc_AttributeError))
             return NULL;
         PyErr_Clear();
-    }
-    else if (!PyUnicode_Check(funcname)) {
-        Py_DECREF(funcname);
-        funcname = NULL;
-    }
 
-    if (klass == NULL)
-        klassname = NULL;
-    else {
-        klassname = _PyObject_GetAttrId(klass, &PyId___name__);
-        if (klassname == NULL) {
-            if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
-                Py_XDECREF(funcname);
+        funcname = _PyObject_GetAttrId(func, &PyId___name__);
+        if (funcname == NULL) {
+            if (!PyErr_ExceptionMatches(PyExc_AttributeError))
                 return NULL;
-            }
             PyErr_Clear();
         }
-        else if (!PyUnicode_Check(klassname)) {
-            Py_DECREF(klassname);
-            klassname = NULL;
-        }
+    }
+    
+    if (funcname != NULL && !PyUnicode_Check(funcname)) {
+        Py_DECREF(funcname);
+        funcname = NULL;
     }
 
     /* XXX Shouldn't use repr()/%R here! */
-    result = PyUnicode_FromFormat("<bound method %V.%V of %R>",
-                                  klassname, defname,
+    result = PyUnicode_FromFormat("<bound method %V of %R>",
                                   funcname, defname, self);
 
     Py_XDECREF(funcname);
-    Py_XDECREF(klassname);
     return result;
 }