From 7295c6a871f9cc42a4209a8eebe2e0974194a2a3 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 1 May 2012 09:51:09 -0400 Subject: [PATCH] fix calling the classmethod descriptor directly (closes #14699) --- Lib/test/test_descr.py | 16 +++++++++++++++ Misc/NEWS | 2 ++ Objects/descrobject.c | 44 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 2289f6e5776..cdaf7d2aca0 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -1458,6 +1458,22 @@ order (MRO) for bases """ self.assertEqual(x, spam.spamlist) self.assertEqual(a, a1) self.assertEqual(d, d1) + spam_cm = spam.spamlist.__dict__['classmeth'] + x2, a2, d2 = spam_cm(spam.spamlist, *a, **d) + self.assertEqual(x2, spam.spamlist) + self.assertEqual(a2, a1) + self.assertEqual(d2, d1) + class SubSpam(spam.spamlist): pass + x2, a2, d2 = spam_cm(SubSpam, *a, **d) + self.assertEqual(x2, SubSpam) + self.assertEqual(a2, a1) + self.assertEqual(d2, d1) + with self.assertRaises(TypeError): + spam_cm() + with self.assertRaises(TypeError): + spam_cm(spam.spamlist()) + with self.assertRaises(TypeError): + spam_cm(list) def test_staticmethods(self): # Testing static methods... diff --git a/Misc/NEWS b/Misc/NEWS index c9752f6aa2d..c24caacd38a 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,8 @@ What's New in Python 3.2.4 Core and Builtins ----------------- +- Issue #14699: Fix calling the classmethod descriptor directly. + - Issue #14433: Prevent msvcrt crash in interactive prompt when stdin is closed. diff --git a/Objects/descrobject.c b/Objects/descrobject.c index a786bae1434..9934f23387a 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -256,14 +256,52 @@ static PyObject * classmethoddescr_call(PyMethodDescrObject *descr, PyObject *args, PyObject *kwds) { - PyObject *func, *result; + Py_ssize_t argc; + PyObject *self, *func, *result; - func = PyCFunction_New(descr->d_method, (PyObject *)PyDescr_TYPE(descr)); + /* Make sure that the first argument is acceptable as 'self' */ + assert(PyTuple_Check(args)); + argc = PyTuple_GET_SIZE(args); + if (argc < 1) { + PyErr_Format(PyExc_TypeError, + "descriptor '%V' of '%.100s' " + "object needs an argument", + descr_name((PyDescrObject *)descr), "?", + PyDescr_TYPE(descr)->tp_name); + return NULL; + } + self = PyTuple_GET_ITEM(args, 0); + if (!PyType_Check(self)) { + PyErr_Format(PyExc_TypeError, + "descriptor '%V' requires a type " + "but received a '%.100s'", + descr_name((PyDescrObject *)descr), "?", + PyDescr_TYPE(descr)->tp_name, + self->ob_type->tp_name); + return NULL; + } + if (!PyType_IsSubtype((PyTypeObject *)self, PyDescr_TYPE(descr))) { + PyErr_Format(PyExc_TypeError, + "descriptor '%V' " + "requires a subtype of '%.100s' " + "but received '%.100s", + descr_name((PyDescrObject *)descr), "?", + PyDescr_TYPE(descr)->tp_name, + self->ob_type->tp_name); + return NULL; + } + + func = PyCFunction_New(descr->d_method, self); if (func == NULL) return NULL; - + args = PyTuple_GetSlice(args, 1, argc); + if (args == NULL) { + Py_DECREF(func); + return NULL; + } result = PyEval_CallObjectWithKeywords(func, args, kwds); Py_DECREF(func); + Py_DECREF(args); return result; }