]> granicus.if.org Git - python/commitdiff
fix calling the classmethod descriptor directly (closes #14699)
authorBenjamin Peterson <benjamin@python.org>
Tue, 1 May 2012 13:51:09 +0000 (09:51 -0400)
committerBenjamin Peterson <benjamin@python.org>
Tue, 1 May 2012 13:51:09 +0000 (09:51 -0400)
Lib/test/test_descr.py
Misc/NEWS
Objects/descrobject.c

index b66849e369d3ec9b9ef32e8dc0ae62b38cb93584..bfb3552f55ae42fc53f32b175aa099b8e02fc120 100644 (file)
@@ -1419,6 +1419,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...
index 4398e436f1695eaca44b7bbd0851686cff3cb6e4..b7c22c790e9904f92b57079e298e1baaace3b766 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -9,6 +9,8 @@ What's New in Python 2.7.4
 Core and Builtins
 -----------------
 
+- Issue #14699: Fix calling the classmethod descriptor directly.
+
 - Issue #11603 (again): Setting __repr__ to __str__ now raises a RuntimeError
   when repr() or str() is called on such an object.
 
index 8e7ea7d25f4bdc9b32286ee588c41045189528ae..15109dbfa31b7070766fe9239a905eeb5f93092d 100644 (file)
@@ -254,14 +254,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 *)descr->d_type);
-    if (func == NULL)
+    /* 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), "?",
+                     descr->d_type->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), "?",
+                     descr->d_type->tp_name,
+                     self->ob_type->tp_name);
+        return NULL;
+    }
+    if (!PyType_IsSubtype((PyTypeObject *)self, descr->d_type)) {
+        PyErr_Format(PyExc_TypeError,
+                     "descriptor '%V' "
+                     "requires a subtype of '%.100s' "
+                     "but received '%.100s",
+                     descr_name((PyDescrObject *)descr), "?",
+                     descr->d_type->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;
 }