mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	use __qualname__ to compute bound method repr (closes #21389)
Patch from Steven Barker.
This commit is contained in:
		
							parent
							
								
									2b7ccbda90
								
							
						
					
					
						commit
						48ad7c0b01
					
				
					 4 changed files with 75 additions and 33 deletions
				
			
		| 
						 | 
				
			
			@ -157,8 +157,9 @@ def __init__(self):
 | 
			
		|||
            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.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4423,6 +4423,61 @@ class Sub(Base):
 | 
			
		|||
        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):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,7 @@ static int numfree = 0;
 | 
			
		|||
#endif
 | 
			
		||||
 | 
			
		||||
_Py_IDENTIFIER(__name__);
 | 
			
		||||
_Py_IDENTIFIER(__qualname__);
 | 
			
		||||
 | 
			
		||||
PyObject *
 | 
			
		||||
PyMethod_Function(PyObject *im)
 | 
			
		||||
| 
						 | 
				
			
			@ -243,15 +244,14 @@ 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();
 | 
			
		||||
    funcname = _PyObject_GetAttrId(func, &PyId___qualname__);
 | 
			
		||||
    if (funcname == NULL) {
 | 
			
		||||
        if (!PyErr_ExceptionMatches(PyExc_AttributeError))
 | 
			
		||||
            return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    klass = (PyObject*)Py_TYPE(self);
 | 
			
		||||
        PyErr_Clear();
 | 
			
		||||
 | 
			
		||||
        funcname = _PyObject_GetAttrId(func, &PyId___name__);
 | 
			
		||||
        if (funcname == NULL) {
 | 
			
		||||
| 
						 | 
				
			
			@ -259,35 +259,18 @@ method_repr(PyMethodObject *a)
 | 
			
		|||
                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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue