diff -r dd8f48ff9480 Lib/test/test_builtin.py --- a/Lib/test/test_builtin.py Tue Apr 14 00:15:42 2015 -0700 +++ b/Lib/test/test_builtin.py Fri Apr 17 15:59:01 2015 -0400 @@ -276,6 +276,22 @@ c3 = C3() self.assertTrue(callable(c3)) + # C4 instances are not actually callable. + class C4(object): + @property + def __call__(self): + raise AttributeError('Go away!') + + self.assertFalse(callable(C3())) + + # C5 instances are callable because the descriptor returns a func. + class C5(object): + @property + def __call__(self): + return lambda: None + + self.assertTrue(callable(C4())) + def test_chr(self): self.assertEqual(chr(32), ' ') self.assertEqual(chr(65), 'A') diff -r dd8f48ff9480 Misc/NEWS --- a/Misc/NEWS Tue Apr 14 00:15:42 2015 -0700 +++ b/Misc/NEWS Fri Apr 17 15:59:01 2015 -0400 @@ -9,6 +9,10 @@ Core and Builtins ----------------- + +- Issue #23990: Makes the callable builtin function respect the descriptor + protocol. + - Issue #22631: Added Linux-specific socket constant CAN_RAW_FD_FRAMES. Patch courtesy of Joe Jevnik. diff -r dd8f48ff9480 Objects/object.c --- a/Objects/object.c Tue Apr 14 00:15:42 2015 -0700 +++ b/Objects/object.c Fri Apr 17 15:59:01 2015 -0400 @@ -13,6 +13,7 @@ _Py_IDENTIFIER(__dir__); _Py_IDENTIFIER(__isabstractmethod__); _Py_IDENTIFIER(builtins); +_Py_IDENTIFIER(__call__); #ifdef Py_REF_DEBUG Py_ssize_t _Py_RefTotal; @@ -1274,9 +1275,11 @@ int PyCallable_Check(PyObject *x) { - if (x == NULL) + if (x == NULL) { return 0; - return x->ob_type->tp_call != NULL; + } + + return Py_TYPE(x)->tp_call && _PyObject_HasAttrId(x, &PyId___call__); }