self.assertNotHasAttr(a, "__weakref__")
a.foo = 42
self.assertEqual(a.__dict__, {"foo": 42})
+ with self.assertRaises(TypeError):
+ weakref.ref(a)
class W(object):
__slots__ = ["__weakref__"]
a = W()
self.assertHasAttr(a, "__weakref__")
self.assertNotHasAttr(a, "__dict__")
- try:
+ with self.assertRaises(AttributeError):
a.foo = 42
- except AttributeError:
- pass
- else:
- self.fail("shouldn't be allowed to set a.foo")
+ self.assertIs(weakref.ref(a)(), a)
class C1(W, D):
__slots__ = []
self.assertHasAttr(a, "__weakref__")
a.foo = 42
self.assertEqual(a.__dict__, {"foo": 42})
+ self.assertIs(weakref.ref(a)(), a)
class C2(D, W):
__slots__ = []
self.assertHasAttr(a, "__weakref__")
a.foo = 42
self.assertEqual(a.__dict__, {"foo": 42})
+ self.assertIs(weakref.ref(a)(), a)
+
+ @unittest.skipIf(_testcapi is None, 'need the _testcapi module')
+ def test_slots_special_before_items(self):
+ class D(_testcapi.HeapCCollection):
+ __slots__ = ["__dict__"]
+ a = D(1, 2, 3)
+ self.assertHasAttr(a, "__dict__")
+ self.assertNotHasAttr(a, "__weakref__")
+ a.foo = 42
+ self.assertEqual(a.__dict__, {"foo": 42})
+ with self.assertRaises(TypeError):
+ weakref.ref(a)
+ del a.__dict__
+ self.assertNotHasAttr(a, "foo")
+ self.assertEqual(a.__dict__, {})
+ self.assertEqual(list(a), [1, 2, 3])
+
+ class W(_testcapi.HeapCCollection):
+ __slots__ = ["__weakref__"]
+ a = W(1, 2, 3)
+ self.assertHasAttr(a, "__weakref__")
+ self.assertNotHasAttr(a, "__dict__")
+ with self.assertRaises(AttributeError):
+ a.foo = 42
+ self.assertIs(weakref.ref(a)(), a)
+
+ with self.assertRaises(TypeError):
+ class X(_testcapi.HeapCCollection):
+ __slots__ = ['x']
+
+ with self.assertRaises(TypeError):
+ class X(_testcapi.HeapCCollection):
+ __slots__ = ['__dict__', 'x']
+
+ @support.subTests(('base', 'arg'), [
+ (tuple, (1, 2, 3)),
+ (int, 9876543210**2),
+ (bytes, b'ab'),
+ ])
+ def test_slots_special_after_items(self, base, arg):
+ class D(base):
+ __slots__ = ["__dict__"]
+ a = D(arg)
+ self.assertHasAttr(a, "__dict__")
+ self.assertNotHasAttr(a, "__weakref__")
+ a.foo = 42
+ self.assertEqual(a.__dict__, {"foo": 42})
+ with self.assertRaises(TypeError):
+ weakref.ref(a)
+ del a.__dict__
+ self.assertNotHasAttr(a, "foo")
+ self.assertEqual(a.__dict__, {})
+ self.assertEqual(a, base(arg))
+
+ class W(base):
+ __slots__ = ["__weakref__"]
+ a = W(arg)
+ self.assertHasAttr(a, "__weakref__")
+ self.assertNotHasAttr(a, "__dict__")
+ with self.assertRaises(AttributeError):
+ a.foo = 42
+ self.assertIs(weakref.ref(a)(), a)
+ self.assertEqual(a, base(arg))
+
+ with self.assertRaises(TypeError):
+ class X(base):
+ __slots__ = ['x']
+ with self.assertRaises(TypeError):
+ class X(base):
+ __slots__ = ['__dict__', 'x']
def test_slots_special2(self):
# Testing __qualname__ and __classcell__ in __slots__
static int
type_new_slots_impl(type_new_ctx *ctx, PyObject *dict)
{
- /* Are slots allowed? */
- if (ctx->nslot > 0 && ctx->base->tp_itemsize != 0) {
- PyErr_Format(PyExc_TypeError,
- "nonempty __slots__ not supported for subtype of '%s'",
- ctx->base->tp_name);
- return -1;
- }
-
if (type_new_visit_slots(ctx) < 0) {
return -1;
}
ctx->add_dict = 0;
ctx->add_weak = 0;
ctx->may_add_dict = (ctx->base->tp_dictoffset == 0);
- ctx->may_add_weak = (ctx->base->tp_weaklistoffset == 0
- && ctx->base->tp_itemsize == 0);
+ ctx->may_add_weak = (ctx->base->tp_weaklistoffset == 0);
if (ctx->slots == NULL) {
if (ctx->may_add_dict) {
ctx->add_dict++;
}
- if (ctx->may_add_weak) {
+ if (ctx->may_add_weak && ctx->base->tp_itemsize == 0) {
ctx->add_weak++;
}
}
if (et->ht_slots != NULL) {
PyMemberDef *mp = _PyHeapType_GET_MEMBERS(et);
Py_ssize_t nslot = PyTuple_GET_SIZE(et->ht_slots);
+ if (ctx->base->tp_itemsize != 0) {
+ PyErr_Format(PyExc_TypeError,
+ "arbitrary __slots__ not supported for subtype of '%s'",
+ ctx->base->tp_name);
+ return -1;
+ }
for (Py_ssize_t i = 0; i < nslot; i++, mp++) {
mp->name = PyUnicode_AsUTF8(
PyTuple_GET_ITEM(et->ht_slots, i));
set_tp_dict(type, dict);
PyHeapTypeObject *et = (PyHeapTypeObject*)type;
- et->ht_slots = ctx->slots;
- ctx->slots = NULL;
+ if (ctx->slots && PyTuple_GET_SIZE(ctx->slots)) {
+ et->ht_slots = ctx->slots;
+ ctx->slots = NULL;
+ }
+ else {
+ et->ht_slots = NULL;
+ Py_CLEAR(ctx->slots);
+ }
return type;