[Python-checkins] cpython: use __qualname__ to compute bound method repr (closes #21389)

benjamin.peterson python-checkins at python.org
Thu Aug 21 01:42:12 CEST 2014


http://hg.python.org/cpython/rev/92dcee426014
changeset:   92170:92dcee426014
parent:      92162:10d0a692b1b6
user:        Benjamin Peterson <benjamin at python.org>
date:        Wed Aug 20 18:41:57 2014 -0500
summary:
  use __qualname__ to compute bound method repr (closes #21389)

Patch from Steven Barker.

files:
  Lib/test/test_defaultdict.py |   5 +-
  Lib/test/test_descr.py       |  55 ++++++++++++++++++++++++
  Misc/NEWS                    |   3 +
  Objects/classobject.c        |  45 ++++++-------------
  4 files changed, 75 insertions(+), 33 deletions(-)


diff --git a/Lib/test/test_defaultdict.py b/Lib/test/test_defaultdict.py
--- a/Lib/test/test_defaultdict.py
+++ b/Lib/test/test_defaultdict.py
@@ -157,8 +157,9 @@
             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.
diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py
--- a/Lib/test/test_descr.py
+++ b/Lib/test/test_descr.py
@@ -4423,6 +4423,61 @@
         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):
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@
 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.
 
diff --git a/Objects/classobject.c b/Objects/classobject.c
--- a/Objects/classobject.c
+++ b/Objects/classobject.c
@@ -15,6 +15,7 @@
 #endif
 
 _Py_IDENTIFIER(__name__);
+_Py_IDENTIFIER(__qualname__);
 
 PyObject *
 PyMethod_Function(PyObject *im)
@@ -243,51 +244,33 @@
 {
     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();
+
+        funcname = _PyObject_GetAttrId(func, &PyId___name__);
+        if (funcname == NULL) {
+            if (!PyErr_ExceptionMatches(PyExc_AttributeError))
+                return NULL;
+            PyErr_Clear();
+        }
     }
-    else if (!PyUnicode_Check(funcname)) {
+    
+    if (funcname != NULL && !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);
-                return NULL;
-            }
-            PyErr_Clear();
-        }
-        else if (!PyUnicode_Check(klassname)) {
-            Py_DECREF(klassname);
-            klassname = 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;
 }
 

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list