[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

AltStyle によって変換されたページ (->オリジナル) /