]> granicus.if.org Git - python/commitdiff
bpo-24329: allow __qualname__ and __classcell__ in __slots__ (GH-495)
authorXiang Zhang <angwerzx@126.com>
Wed, 8 Mar 2017 03:18:49 +0000 (11:18 +0800)
committerGitHub <noreply@github.com>
Wed, 8 Mar 2017 03:18:49 +0000 (11:18 +0800)
Lib/test/test_descr.py
Objects/typeobject.c

index 5da7ae598c628bf5c915b89a1f0fe76ae0a641e7..92553e3573be44a50192ec753f4f233b4cdc20fc 100644 (file)
@@ -1322,6 +1322,46 @@ order (MRO) for bases """
         a.foo = 42
         self.assertEqual(a.__dict__, {"foo": 42})
 
+    def test_slots_special2(self):
+        # Testing __qualname__ and __classcell__ in __slots__
+        class Meta(type):
+            def __new__(cls, name, bases, namespace, attr):
+                self.assertIn(attr, namespace)
+                return super().__new__(cls, name, bases, namespace)
+
+        class C1:
+            def __init__(self):
+                self.b = 42
+        class C2(C1, metaclass=Meta, attr="__classcell__"):
+            __slots__ = ["__classcell__"]
+            def __init__(self):
+                super().__init__()
+        self.assertIsInstance(C2.__dict__["__classcell__"],
+                              types.MemberDescriptorType)
+        c = C2()
+        self.assertEqual(c.b, 42)
+        self.assertNotHasAttr(c, "__classcell__")
+        c.__classcell__ = 42
+        self.assertEqual(c.__classcell__, 42)
+        with self.assertRaises(TypeError):
+            class C3:
+                __classcell__ = 42
+                __slots__ = ["__classcell__"]
+
+        class Q1(metaclass=Meta, attr="__qualname__"):
+            __slots__ = ["__qualname__"]
+        self.assertEqual(Q1.__qualname__, C1.__qualname__[:-2] + "Q1")
+        self.assertIsInstance(Q1.__dict__["__qualname__"],
+                              types.MemberDescriptorType)
+        q = Q1()
+        self.assertNotHasAttr(q, "__qualname__")
+        q.__qualname__ = "q"
+        self.assertEqual(q.__qualname__, "q")
+        with self.assertRaises(TypeError):
+            class Q2:
+                __qualname__ = object()
+                __slots__ = ["__qualname__"]
+
     def test_slots_descriptor(self):
         # Issue2115: slot descriptors did not correctly check
         # the type of the given object
index 18b67c83258dcbcb05e7211d70bd145da3930777..d70ced04bfed7ce2e2944d6a63022aeb92167b6e 100644 (file)
@@ -2478,11 +2478,17 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
             }
             PyList_SET_ITEM(newslots, j, tmp);
             if (PyDict_GetItem(dict, tmp)) {
-                PyErr_Format(PyExc_ValueError,
-                             "%R in __slots__ conflicts with class variable",
-                             tmp);
-                Py_DECREF(newslots);
-                goto error;
+                /* CPython inserts __qualname__ and __classcell__ (when needed)
+                   into the namespace when creating a class.  They will be deleted
+                   below so won't act as class variables. */
+                if (!_PyUnicode_EqualToASCIIId(tmp, &PyId___qualname__) &&
+                    !_PyUnicode_EqualToASCIIId(tmp, &PyId___classcell__)) {
+                    PyErr_Format(PyExc_ValueError,
+                                 "%R in __slots__ conflicts with class variable",
+                                 tmp);
+                    Py_DECREF(newslots);
+                    goto error;
+                }
             }
             j++;
         }