diff -r 99db0ca554d9 Lib/test/test_descr.py --- a/Lib/test/test_descr.py Mon Apr 14 13:13:01 2014 -0400 +++ b/Lib/test/test_descr.py Wed Apr 16 17:09:51 2014 -0400 @@ -4997,11 +4997,99 @@ self.assertLess(sys.getsizeof(vars(b)), sys.getsizeof({})) +class TestDescriptorsCallable(unittest.TestCase): + """Test that every possible descriptor is callable. + + We check all combinations of: + * an instance method, + * a class method, and + * a static method. + + Accessing them as: + * as an instance attribute, + * as a class attribute, and + * through the class __dict__. + + And we call: + * one user method and + * one built-in method. + + See http://bugs.python.org/issue20309 for the history behind these tests. + """ + + class C: + def normal_method(self, a, b): + print(self, a, b) + + @classmethod + def class_method(cls, a, b): + print(cls, a, b) + + @staticmethod + def static_method(a, b): + print(a, b) + + def test_userclass_instance_instancemethod(self): + self.assertTrue(callable(self.C().normal_method)) + + def test_builtinclass_instance_instancemethod(self): + self.assertTrue(callable((123).__add__)) + + def test_userclass_instance_classmethod(self): + self.assertTrue(callable(self.C().class_method)) + + def test_builtinclass__instance_classmethod(self): + self.assertTrue(callable({}.fromkeys)) + + def test_userclass_instance_staticmethod(self): + self.assertTrue(callable(self.C().static_method)) + + def test_builtinclass_instance_staticmethod(self): + self.assertTrue(callable('abc'.maketrans)) + + def test_userclass_class_instancemethod(self): + self.assertTrue(callable(C.normal_method)) + + def test_builtinclass_class_instancemethod(self): + self.assertTrue(callable(int.__add__)) + + def test_userclass_class_classmethod(self): + self.assertTrue(callable(C.class_method)) + + def test_builtinclass_class_classmethod(self): + self.assertTrue(callable(dict.fromkeys)) + + def test_userclass_class_staticmethod(self): + self.assertTrue(callable(C.static_method)) + + def test_builtinclass_class_staticmethod(self): + self.assertTrue(callable(str.maketrans)) + + def test_userclass_classdict_instancemethod(self): + self.assertTrue(callable(C.__dict__['normal_method'])) + + def test_builtinclass_classdict_instancemethod(self): + self.assertTrue(callable(int.__dict__['__add__'])) + + def test_userclass_classdict_classmethod(self): + self.assertTrue(callable(C.__dict__['class_method'])) + + def test_builtinclass_classdict_instancemethod(self): + self.assertTrue(callable(dict.__dict__['fromkeys'])) + + def test_userclass_classdict_staticmethod(self): + self.assertTrue(callable(C.__dict__['static_method'])) + + def test_builtinclass__classdict_staticmethod(self): + self.assertTrue(callable(str.__dict__['maketrans'])) + + def test_main(): # Run all local test cases, with PTypesLongInitTest first. support.run_unittest(PTypesLongInitTest, OperatorsTest, ClassPropertiesAndMethods, DictProxyTests, - MiscTests, PicklingTests, SharedKeyTests) + MiscTests, PicklingTests, SharedKeyTests, + TestDescriptorsCallable) if __name__ == "__main__": test_main() diff -r 99db0ca554d9 Objects/funcobject.c --- a/Objects/funcobject.c Mon Apr 14 13:13:01 2014 -0400 +++ b/Objects/funcobject.c Wed Apr 16 17:09:51 2014 -0400 @@ -776,6 +776,21 @@ return 0; } +static PyObject * +cm_call(PyObject *func, PyObject *arg, PyObject *kw) +{ + classmethod *cm = (classmethod *)func; + + if (cm->cm_callable == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "uninitialized classmethod object"); + return NULL; + } + + // XXX Use PyObject_Call + return function_call(cm->cm_callable, arg, kw); +} + static PyMemberDef cm_memberlist[] = { {"__func__", T_OBJECT, offsetof(classmethod, cm_callable), READONLY}, {NULL} /* Sentinel */ @@ -839,7 +854,7 @@ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ - 0, /* tp_call */ + cm_call, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ @@ -940,6 +955,21 @@ return sm->sm_callable; } +static PyObject * +sm_call(PyObject *func, PyObject *arg, PyObject *kw) +{ + staticmethod *sm = (staticmethod *)func; + + if (sm->sm_callable == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "uninitialized staticmethod object"); + return NULL; + } + + // XXX Use PyObject_Call + return function_call(sm->sm_callable, arg, kw); +} + static int sm_init(PyObject *self, PyObject *args, PyObject *kwds) { @@ -1015,7 +1045,7 @@ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ - 0, /* tp_call */ + sm_call, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */