Skip to content

Commit c393ee8

Browse files
authored
bpo-24329: allow __qualname__ and __classcell__ in __slots__ (GH-495)
1 parent d5d3249 commit c393ee8

File tree

2 files changed

+51
-5
lines changed

2 files changed

+51
-5
lines changed

Lib/test/test_descr.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,6 +1322,46 @@ class C2(D, W):
13221322
a.foo = 42
13231323
self.assertEqual(a.__dict__, {"foo": 42})
13241324

1325+
def test_slots_special2(self):
1326+
# Testing __qualname__ and __classcell__ in __slots__
1327+
class Meta(type):
1328+
def __new__(cls, name, bases, namespace, attr):
1329+
self.assertIn(attr, namespace)
1330+
return super().__new__(cls, name, bases, namespace)
1331+
1332+
class C1:
1333+
def __init__(self):
1334+
self.b = 42
1335+
class C2(C1, metaclass=Meta, attr="__classcell__"):
1336+
__slots__ = ["__classcell__"]
1337+
def __init__(self):
1338+
super().__init__()
1339+
self.assertIsInstance(C2.__dict__["__classcell__"],
1340+
types.MemberDescriptorType)
1341+
c = C2()
1342+
self.assertEqual(c.b, 42)
1343+
self.assertNotHasAttr(c, "__classcell__")
1344+
c.__classcell__ = 42
1345+
self.assertEqual(c.__classcell__, 42)
1346+
with self.assertRaises(TypeError):
1347+
class C3:
1348+
__classcell__ = 42
1349+
__slots__ = ["__classcell__"]
1350+
1351+
class Q1(metaclass=Meta, attr="__qualname__"):
1352+
__slots__ = ["__qualname__"]
1353+
self.assertEqual(Q1.__qualname__, C1.__qualname__[:-2] + "Q1")
1354+
self.assertIsInstance(Q1.__dict__["__qualname__"],
1355+
types.MemberDescriptorType)
1356+
q = Q1()
1357+
self.assertNotHasAttr(q, "__qualname__")
1358+
q.__qualname__ = "q"
1359+
self.assertEqual(q.__qualname__, "q")
1360+
with self.assertRaises(TypeError):
1361+
class Q2:
1362+
__qualname__ = object()
1363+
__slots__ = ["__qualname__"]
1364+
13251365
def test_slots_descriptor(self):
13261366
# Issue2115: slot descriptors did not correctly check
13271367
# the type of the given object

Objects/typeobject.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2478,11 +2478,17 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
24782478
}
24792479
PyList_SET_ITEM(newslots, j, tmp);
24802480
if (PyDict_GetItem(dict, tmp)) {
2481-
PyErr_Format(PyExc_ValueError,
2482-
"%R in __slots__ conflicts with class variable",
2483-
tmp);
2484-
Py_DECREF(newslots);
2485-
goto error;
2481+
/* CPython inserts __qualname__ and __classcell__ (when needed)
2482+
into the namespace when creating a class. They will be deleted
2483+
below so won't act as class variables. */
2484+
if (!_PyUnicode_EqualToASCIIId(tmp, &PyId___qualname__) &&
2485+
!_PyUnicode_EqualToASCIIId(tmp, &PyId___classcell__)) {
2486+
PyErr_Format(PyExc_ValueError,
2487+
"%R in __slots__ conflicts with class variable",
2488+
tmp);
2489+
Py_DECREF(newslots);
2490+
goto error;
2491+
}
24862492
}
24872493
j++;
24882494
}

0 commit comments

Comments
 (0)