| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2617,7 +2617,7 @@ Notes on using *__slots__*: | |||
| 2617 | 2617 | * :exc:`TypeError` will be raised if *__slots__* other than *__dict__* and | |
| 2618 | 2618 | *__weakref__* are defined for a class derived from a | |
| 2619 | 2619 | :c:member:`"variable-length" built-in type <PyTypeObject.tp_itemsize>` such as | |
| 2620 | - :class:`int`, :class:`bytes`, and :class:`tuple`. | ||
| 2620 | + :class:`int`, :class:`bytes`, and :class:`type`, except :class:`tuple`. | ||
| 2621 | 2621 | ||
| 2622 | 2622 | * Any non-string :term:`iterable` may be assigned to *__slots__*. | |
| 2623 | 2623 | ||
@@ -2642,6 +2642,7 @@ Notes on using *__slots__*: | |||
| 2642 | 2642 | ||
| 2643 | 2643 | .. versionchanged:: 3.15 | |
| 2644 | 2644 | Allowed defining the *__dict__* and *__weakref__* *__slots__* for any class. | |
| 2645 | + Allowed defining any *__slots__* for a class derived from :class:`tuple`. | ||
| 2645 | 2646 | ||
| 2646 | 2647 | ||
| 2647 | 2648 | .. _class-customization: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -398,6 +398,10 @@ Other language changes | |||
| 398 | 398 | for any class. | |
| 399 | 399 | (Contributed by Serhiy Storchaka in :gh:`41779`.) | |
| 400 | 400 | ||
| 401 | + * Allowed defining any :ref:`__slots__ <slots>` for a class derived from | ||
| 402 | + :class:`tuple` (including classes created by :func:`collections.namedtuple`). | ||
| 403 | + (Contributed by Serhiy Storchaka in :gh:`41779`.) | ||
| 404 | + | ||
| 401 | 405 | ||
| 402 | 406 | New modules | |
| 403 | 407 | =========== | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -80,10 +80,14 @@ struct PyMemberDef { | |||
| 80 | 80 | #define _Py_T_NONE 20 // Deprecated. Value is always None. | |
| 81 | 81 | ||
| 82 | 82 | /* Flags */ | |
| 83 | - #define Py_READONLY 1 | ||
| 84 | - #define Py_AUDIT_READ 2 // Added in 3.10, harmless no-op before that | ||
| 85 | - #define _Py_WRITE_RESTRICTED 4 // Deprecated, no-op. Do not reuse the value. | ||
| 86 | - #define Py_RELATIVE_OFFSET 8 | ||
| 83 | + #define Py_READONLY (1 << 0) | ||
| 84 | + #define Py_AUDIT_READ (1 << 1) // Added in 3.10, harmless no-op before that | ||
| 85 | + #define _Py_WRITE_RESTRICTED (1 << 2) // Deprecated, no-op. Do not reuse the value. | ||
| 86 | + #define Py_RELATIVE_OFFSET (1 << 3) | ||
| 87 | + | ||
| 88 | + #ifndef Py_LIMITED_API | ||
| 89 | + # define _Py_AFTER_ITEMS (1 << 4) // For internal use. | ||
| 90 | + #endif | ||
| 87 | 91 | ||
| 88 | 92 | PyAPI_FUNC(PyObject *) PyMember_GetOne(const char *, PyMemberDef *); | |
| 89 | 93 | PyAPI_FUNC(int) PyMember_SetOne(char *, PyMemberDef *, PyObject *); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -22,6 +22,8 @@ typedef propertyobject _PyPropertyObject; | |||
| 22 | 22 | ||
| 23 | 23 | extern PyTypeObject _PyMethodWrapper_Type; | |
| 24 | 24 | ||
| 25 | + extern void *_PyMember_GetOffset(PyObject *, PyMemberDef *); | ||
| 26 | + | ||
| 25 | 27 | #ifdef __cplusplus | |
| 26 | 28 | } | |
| 27 | 29 | #endif | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1320,6 +1320,18 @@ class X(object): | |||
| 1320 | 1320 | with self.assertRaisesRegex(AttributeError, "'X' object has no attribute 'a'"): | |
| 1321 | 1321 | X().a | |
| 1322 | 1322 | ||
| 1323 | + def test_slots_after_items(self): | ||
| 1324 | + class C(tuple): | ||
| 1325 | + __slots__ = ['a'] | ||
| 1326 | + x = C((1, 2, 3)) | ||
| 1327 | + self.assertNotHasAttr(x, "__dict__") | ||
| 1328 | + self.assertNotHasAttr(x, "a") | ||
| 1329 | + x.a = 42 | ||
| 1330 | + self.assertEqual(x.a, 42) | ||
| 1331 | + del x.a | ||
| 1332 | + self.assertNotHasAttr(x, "a") | ||
| 1333 | + self.assertEqual(x, (1, 2, 3)) | ||
| 1334 | + | ||
| 1323 | 1335 | def test_slots_special(self): | |
| 1324 | 1336 | # Testing __dict__ and __weakref__ in __slots__... | |
| 1325 | 1337 | class D(object): | |
@@ -1422,6 +1434,9 @@ class W(base): | |||
| 1422 | 1434 | self.assertIs(weakref.ref(a)(), a) | |
| 1423 | 1435 | self.assertEqual(a, base(arg)) | |
| 1424 | 1436 | ||
| 1437 | + @support.subTests('base', [int, bytes] + | ||
| 1438 | + ([_testcapi.HeapCCollection] if _testcapi else [])) | ||
| 1439 | + def test_unsupported_slots(self, base): | ||
| 1425 | 1440 | with self.assertRaises(TypeError): | |
| 1426 | 1441 | class X(base): | |
| 1427 | 1442 | __slots__ = ['x'] | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,3 @@ | |||
| 1 | + Allowed defining any :ref:`__slots__ <slots>` for a class derived from | ||
| 2 | + :class:`tuple` (including classes created by | ||
| 3 | + :func:`collections.namedtuple`). | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,6 +4,7 @@ | |||
| 4 | 4 | #include "pycore_abstract.h" // _PySequence_IterSearch() | |
| 5 | 5 | #include "pycore_call.h" // _PyObject_VectorcallTstate() | |
| 6 | 6 | #include "pycore_code.h" // CO_FAST_FREE | |
| 7 | + #include "pycore_descrobject.h" // _PyMember_GetOffset() | ||
| 7 | 8 | #include "pycore_dict.h" // _PyDict_KeysSize() | |
| 8 | 9 | #include "pycore_function.h" // _PyFunction_GetVersionForCurrentState() | |
| 9 | 10 | #include "pycore_interpframe.h" // _PyInterpreterFrame | |
@@ -2578,7 +2579,7 @@ traverse_slots(PyTypeObject *type, PyObject *self, visitproc visit, void *arg) | |||
| 2578 | 2579 | mp = _PyHeapType_GET_MEMBERS((PyHeapTypeObject *)type); | |
| 2579 | 2580 | for (i = 0; i < n; i++, mp++) { | |
| 2580 | 2581 | if (mp->type == Py_T_OBJECT_EX) { | |
| 2581 | - char *addr = (char *)self + mp->offset; | ||
| 2582 | + void *addr = _PyMember_GetOffset(self, mp); | ||
| 2582 | 2583 | PyObject *obj = *(PyObject **)addr; | |
| 2583 | 2584 | if (obj != NULL) { | |
| 2584 | 2585 | int err = visit(obj, arg); | |
@@ -2653,7 +2654,7 @@ clear_slots(PyTypeObject *type, PyObject *self) | |||
| 2653 | 2654 | mp = _PyHeapType_GET_MEMBERS((PyHeapTypeObject *)type); | |
| 2654 | 2655 | for (i = 0; i < n; i++, mp++) { | |
| 2655 | 2656 | if (mp->type == Py_T_OBJECT_EX && !(mp->flags & Py_READONLY)) { | |
| 2656 | - char *addr = (char *)self + mp->offset; | ||
| 2657 | + void *addr = _PyMember_GetOffset(self, mp); | ||
| 2657 | 2658 | PyObject *obj = *(PyObject **)addr; | |
| 2658 | 2659 | if (obj != NULL) { | |
| 2659 | 2660 | *(PyObject **)addr = NULL; | |
@@ -4641,7 +4642,11 @@ type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type, PyObject *dict | |||
| 4641 | 4642 | if (et->ht_slots != NULL) { | |
| 4642 | 4643 | PyMemberDef *mp = _PyHeapType_GET_MEMBERS(et); | |
| 4643 | 4644 | Py_ssize_t nslot = PyTuple_GET_SIZE(et->ht_slots); | |
| 4644 | - if (ctx->base->tp_itemsize != 0) { | ||
| 4645 | + int after_items = (ctx->base->tp_itemsize != 0 && | ||
| 4646 | + !(ctx->base->tp_flags & Py_TPFLAGS_ITEMS_AT_END)); | ||
| 4647 | + if (ctx->base->tp_itemsize != 0 && | ||
| 4648 | + !(ctx->base->tp_flags & Py_TPFLAGS_TUPLE_SUBCLASS)) | ||
| 4649 | + { | ||
| 4645 | 4650 | PyErr_Format(PyExc_TypeError, | |
| 4646 | 4651 | "arbitrary __slots__ not supported for subtype of '%s'", | |
| 4647 | 4652 | ctx->base->tp_name); | |
@@ -4655,6 +4660,9 @@ type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type, PyObject *dict | |||
| 4655 | 4660 | } | |
| 4656 | 4661 | mp->type = Py_T_OBJECT_EX; | |
| 4657 | 4662 | mp->offset = slotoffset; | |
| 4663 | + if (after_items) { | ||
| 4664 | + mp->flags |= _Py_AFTER_ITEMS; | ||
| 4665 | + } | ||
| 4658 | 4666 | ||
| 4659 | 4667 | /* __dict__ and __weakref__ are already filtered out */ | |
| 4660 | 4668 | assert(strcmp(mp->name, "__dict__") != 0); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -141,6 +141,7 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters | |||
| 141 | 141 | #define SPEC_FAIL_ATTR_METACLASS_OVERRIDDEN 34 | |
| 142 | 142 | #define SPEC_FAIL_ATTR_SPLIT_DICT 35 | |
| 143 | 143 | #define SPEC_FAIL_ATTR_DESCR_NOT_DEFERRED 36 | |
| 144 | + #define SPEC_FAIL_ATTR_SLOT_AFTER_ITEMS 37 | ||
| 144 | 145 | ||
| 145 | 146 | /* Binary subscr and store subscr */ | |
| 146 | 147 | ||
@@ -812,6 +813,10 @@ do_specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* | |||
| 812 | 813 | SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_EXPECTED_ERROR); | |
| 813 | 814 | return -1; | |
| 814 | 815 | } | |
| 816 | + if (dmem->flags & _Py_AFTER_ITEMS) { | ||
| 817 | + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_SLOT_AFTER_ITEMS); | ||
| 818 | + return -1; | ||
| 819 | + } | ||
| 815 | 820 | if (dmem->flags & Py_AUDIT_READ) { | |
| 816 | 821 | SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_AUDITED_SLOT); | |
| 817 | 822 | return -1; | |
@@ -1006,6 +1011,10 @@ _Py_Specialize_StoreAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *na | |||
| 1006 | 1011 | SPECIALIZATION_FAIL(STORE_ATTR, SPEC_FAIL_EXPECTED_ERROR); | |
| 1007 | 1012 | goto fail; | |
| 1008 | 1013 | } | |
| 1014 | + if (dmem->flags & _Py_AFTER_ITEMS) { | ||
| 1015 | + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_SLOT_AFTER_ITEMS); | ||
| 1016 | + goto fail; | ||
| 1017 | + } | ||
| 1009 | 1018 | if (dmem->flags & Py_READONLY) { | |
| 1010 | 1019 | SPECIALIZATION_FAIL(STORE_ATTR, SPEC_FAIL_ATTR_READ_ONLY); | |
| 1011 | 1020 | goto fail; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | 4 | #include "Python.h" | |
| 5 | 5 | #include "pycore_abstract.h" // _PyNumber_Index() | |
| 6 | + #include "pycore_descrobject.h" // _PyMember_GetOffset() | ||
| 6 | 7 | #include "pycore_long.h" // _PyLong_IsNegative() | |
| 7 | 8 | #include "pycore_object.h" // _Py_TryIncrefCompare(), FT_ATOMIC_*() | |
| 8 | 9 | #include "pycore_critical_section.h" | |
@@ -20,6 +21,17 @@ member_get_object(const char *addr, const char *obj_addr, PyMemberDef *l) | |||
| 20 | 21 | return v; | |
| 21 | 22 | } | |
| 22 | 23 | ||
| 24 | + void * | ||
| 25 | + _PyMember_GetOffset(PyObject *obj, PyMemberDef *mp) | ||
| 26 | + { | ||
| 27 | + unsigned char *addr = (unsigned char *)obj + mp->offset; | ||
| 28 | + if (mp->flags & _Py_AFTER_ITEMS) { | ||
| 29 | + PyTypeObject *type = Py_TYPE(obj); | ||
| 30 | + addr += _Py_SIZE_ROUND_UP(Py_SIZE(obj) * type->tp_itemsize, SIZEOF_VOID_P); | ||
| 31 | + } | ||
| 32 | + return addr; | ||
| 33 | + } | ||
| 34 | + | ||
| 23 | 35 | PyObject * | |
| 24 | 36 | PyMember_GetOne(const char *obj_addr, PyMemberDef *l) | |
| 25 | 37 | { | |
@@ -31,7 +43,7 @@ PyMember_GetOne(const char *obj_addr, PyMemberDef *l) | |||
| 31 | 43 | return NULL; | |
| 32 | 44 | } | |
| 33 | 45 | ||
| 34 | - const char* addr = obj_addr + l->offset; | ||
| 46 | + const void *addr = _PyMember_GetOffset((PyObject *)obj_addr, l); | ||
| 35 | 47 | switch (l->type) { | |
| 36 | 48 | case Py_T_BOOL: | |
| 37 | 49 | v = PyBool_FromLong(FT_ATOMIC_LOAD_CHAR_RELAXED(*(char*)addr)); | |
@@ -80,7 +92,7 @@ PyMember_GetOne(const char *obj_addr, PyMemberDef *l) | |||
| 80 | 92 | v = PyUnicode_FromString((char*)addr); | |
| 81 | 93 | break; | |
| 82 | 94 | case Py_T_CHAR: { | |
| 83 | - char char_val = FT_ATOMIC_LOAD_CHAR_RELAXED(*addr); | ||
| 95 | + char char_val = FT_ATOMIC_LOAD_CHAR_RELAXED(*(char*)addr); | ||
| 84 | 96 | v = PyUnicode_FromStringAndSize(&char_val, 1); | |
| 85 | 97 | break; | |
| 86 | 98 | } | |
@@ -151,10 +163,8 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v) | |||
| 151 | 163 | return -1; | |
| 152 | 164 | } | |
| 153 | 165 | ||
| 154 | - #ifdef Py_GIL_DISABLED | ||
| 155 | - PyObject *obj = (PyObject *) addr; | ||
| 156 | - #endif | ||
| 157 | - addr += l->offset; | ||
| 166 | + PyObject *obj = (PyObject *)addr; | ||
| 167 | + addr = _PyMember_GetOffset(obj, l); | ||
| 158 | 168 | ||
| 159 | 169 | if ((l->flags & Py_READONLY)) | |
| 160 | 170 | { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments