mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
Merge b38b8b8b50 into 7099af8f5e
This commit is contained in:
commit
9688fa5dda
9 changed files with 70 additions and 14 deletions
|
|
@ -2633,7 +2633,7 @@ Notes on using *__slots__*:
|
|||
* :exc:`TypeError` will be raised if *__slots__* other than *__dict__* and
|
||||
*__weakref__* are defined for a class derived from a
|
||||
:c:member:`"variable-length" built-in type <PyTypeObject.tp_itemsize>` such as
|
||||
:class:`int`, :class:`bytes`, and :class:`tuple`.
|
||||
:class:`int`, :class:`bytes`, and :class:`type`, except :class:`tuple`.
|
||||
|
||||
* Any non-string :term:`iterable` may be assigned to *__slots__*.
|
||||
|
||||
|
|
@ -2658,6 +2658,7 @@ Notes on using *__slots__*:
|
|||
|
||||
.. versionchanged:: next
|
||||
Allowed defining the *__dict__* and *__weakref__* *__slots__* for any class.
|
||||
Allowed defining any *__slots__* for a class derived from :class:`tuple`.
|
||||
|
||||
|
||||
.. _class-customization:
|
||||
|
|
|
|||
|
|
@ -381,6 +381,10 @@ Other language changes
|
|||
for any class.
|
||||
(Contributed by Serhiy Storchaka in :gh:`41779`.)
|
||||
|
||||
* Allowed defining any :ref:`__slots__ <slots>` for a class derived from
|
||||
:class:`tuple` (including classes created by :func:`collections.namedtuple`).
|
||||
(Contributed by Serhiy Storchaka in :gh:`41779`.)
|
||||
|
||||
|
||||
New modules
|
||||
===========
|
||||
|
|
|
|||
|
|
@ -80,10 +80,14 @@ struct PyMemberDef {
|
|||
#define _Py_T_NONE 20 // Deprecated. Value is always None.
|
||||
|
||||
/* Flags */
|
||||
#define Py_READONLY 1
|
||||
#define Py_AUDIT_READ 2 // Added in 3.10, harmless no-op before that
|
||||
#define _Py_WRITE_RESTRICTED 4 // Deprecated, no-op. Do not reuse the value.
|
||||
#define Py_RELATIVE_OFFSET 8
|
||||
#define Py_READONLY (1 << 0)
|
||||
#define Py_AUDIT_READ (1 << 1) // Added in 3.10, harmless no-op before that
|
||||
#define _Py_WRITE_RESTRICTED (1 << 2) // Deprecated, no-op. Do not reuse the value.
|
||||
#define Py_RELATIVE_OFFSET (1 << 3)
|
||||
|
||||
#ifndef Py_LIMITED_API
|
||||
# define _Py_AFTER_ITEMS (1 << 4) // For internal use.
|
||||
#endif
|
||||
|
||||
PyAPI_FUNC(PyObject *) PyMember_GetOne(const char *, PyMemberDef *);
|
||||
PyAPI_FUNC(int) PyMember_SetOne(char *, PyMemberDef *, PyObject *);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ typedef propertyobject _PyPropertyObject;
|
|||
|
||||
extern PyTypeObject _PyMethodWrapper_Type;
|
||||
|
||||
extern void *_PyMember_GetOffset(PyObject *, PyMemberDef *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1320,6 +1320,18 @@ class X(object):
|
|||
with self.assertRaisesRegex(AttributeError, "'X' object has no attribute 'a'"):
|
||||
X().a
|
||||
|
||||
def test_slots_after_items(self):
|
||||
class C(tuple):
|
||||
__slots__ = ['a']
|
||||
x = C((1, 2, 3))
|
||||
self.assertNotHasAttr(x, "__dict__")
|
||||
self.assertNotHasAttr(x, "a")
|
||||
x.a = 42
|
||||
self.assertEqual(x.a, 42)
|
||||
del x.a
|
||||
self.assertNotHasAttr(x, "a")
|
||||
self.assertEqual(x, (1, 2, 3))
|
||||
|
||||
def test_slots_special(self):
|
||||
# Testing __dict__ and __weakref__ in __slots__...
|
||||
class D(object):
|
||||
|
|
@ -1422,6 +1434,9 @@ class W(base):
|
|||
self.assertIs(weakref.ref(a)(), a)
|
||||
self.assertEqual(a, base(arg))
|
||||
|
||||
@support.subTests('base', [int, bytes] +
|
||||
([_testcapi.HeapCCollection] if _testcapi else []))
|
||||
def test_unsupported_slots(self, base):
|
||||
with self.assertRaises(TypeError):
|
||||
class X(base):
|
||||
__slots__ = ['x']
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
Allowed defining any :ref:`__slots__ <slots>` for a class derived from
|
||||
:class:`tuple` (including classes created by
|
||||
:func:`collections.namedtuple`).
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
#include "pycore_abstract.h" // _PySequence_IterSearch()
|
||||
#include "pycore_call.h" // _PyObject_VectorcallTstate()
|
||||
#include "pycore_code.h" // CO_FAST_FREE
|
||||
#include "pycore_descrobject.h" // _PyMember_GetOffset()
|
||||
#include "pycore_dict.h" // _PyDict_KeysSize()
|
||||
#include "pycore_function.h" // _PyFunction_GetVersionForCurrentState()
|
||||
#include "pycore_interpframe.h" // _PyInterpreterFrame
|
||||
|
|
@ -2578,7 +2579,7 @@ traverse_slots(PyTypeObject *type, PyObject *self, visitproc visit, void *arg)
|
|||
mp = _PyHeapType_GET_MEMBERS((PyHeapTypeObject *)type);
|
||||
for (i = 0; i < n; i++, mp++) {
|
||||
if (mp->type == Py_T_OBJECT_EX) {
|
||||
char *addr = (char *)self + mp->offset;
|
||||
void *addr = _PyMember_GetOffset(self, mp);
|
||||
PyObject *obj = *(PyObject **)addr;
|
||||
if (obj != NULL) {
|
||||
int err = visit(obj, arg);
|
||||
|
|
@ -2653,7 +2654,7 @@ clear_slots(PyTypeObject *type, PyObject *self)
|
|||
mp = _PyHeapType_GET_MEMBERS((PyHeapTypeObject *)type);
|
||||
for (i = 0; i < n; i++, mp++) {
|
||||
if (mp->type == Py_T_OBJECT_EX && !(mp->flags & Py_READONLY)) {
|
||||
char *addr = (char *)self + mp->offset;
|
||||
void *addr = _PyMember_GetOffset(self, mp);
|
||||
PyObject *obj = *(PyObject **)addr;
|
||||
if (obj != NULL) {
|
||||
*(PyObject **)addr = NULL;
|
||||
|
|
@ -4641,7 +4642,11 @@ type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type, PyObject *dict
|
|||
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) {
|
||||
int after_items = (ctx->base->tp_itemsize != 0 &&
|
||||
!(ctx->base->tp_flags & Py_TPFLAGS_ITEMS_AT_END));
|
||||
if (ctx->base->tp_itemsize != 0 &&
|
||||
!(ctx->base->tp_flags & Py_TPFLAGS_TUPLE_SUBCLASS))
|
||||
{
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"arbitrary __slots__ not supported for subtype of '%s'",
|
||||
ctx->base->tp_name);
|
||||
|
|
@ -4655,6 +4660,9 @@ type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type, PyObject *dict
|
|||
}
|
||||
mp->type = Py_T_OBJECT_EX;
|
||||
mp->offset = slotoffset;
|
||||
if (after_items) {
|
||||
mp->flags |= _Py_AFTER_ITEMS;
|
||||
}
|
||||
|
||||
/* __dict__ and __weakref__ are already filtered out */
|
||||
assert(strcmp(mp->name, "__dict__") != 0);
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters
|
|||
#define SPEC_FAIL_ATTR_METACLASS_OVERRIDDEN 34
|
||||
#define SPEC_FAIL_ATTR_SPLIT_DICT 35
|
||||
#define SPEC_FAIL_ATTR_DESCR_NOT_DEFERRED 36
|
||||
#define SPEC_FAIL_ATTR_SLOT_AFTER_ITEMS 37
|
||||
|
||||
/* Binary subscr and store subscr */
|
||||
|
||||
|
|
@ -810,6 +811,10 @@ do_specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject*
|
|||
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_EXPECTED_ERROR);
|
||||
return -1;
|
||||
}
|
||||
if (dmem->flags & _Py_AFTER_ITEMS) {
|
||||
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_SLOT_AFTER_ITEMS);
|
||||
return -1;
|
||||
}
|
||||
if (dmem->flags & Py_AUDIT_READ) {
|
||||
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_AUDITED_SLOT);
|
||||
return -1;
|
||||
|
|
@ -1004,6 +1009,10 @@ _Py_Specialize_StoreAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *na
|
|||
SPECIALIZATION_FAIL(STORE_ATTR, SPEC_FAIL_EXPECTED_ERROR);
|
||||
goto fail;
|
||||
}
|
||||
if (dmem->flags & _Py_AFTER_ITEMS) {
|
||||
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_SLOT_AFTER_ITEMS);
|
||||
goto fail;
|
||||
}
|
||||
if (dmem->flags & Py_READONLY) {
|
||||
SPECIALIZATION_FAIL(STORE_ATTR, SPEC_FAIL_ATTR_READ_ONLY);
|
||||
goto fail;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "Python.h"
|
||||
#include "pycore_abstract.h" // _PyNumber_Index()
|
||||
#include "pycore_descrobject.h" // _PyMember_GetOffset()
|
||||
#include "pycore_long.h" // _PyLong_IsNegative()
|
||||
#include "pycore_object.h" // _Py_TryIncrefCompare(), FT_ATOMIC_*()
|
||||
#include "pycore_critical_section.h"
|
||||
|
|
@ -20,6 +21,17 @@ member_get_object(const char *addr, const char *obj_addr, PyMemberDef *l)
|
|||
return v;
|
||||
}
|
||||
|
||||
void *
|
||||
_PyMember_GetOffset(PyObject *obj, PyMemberDef *mp)
|
||||
{
|
||||
unsigned char *addr = (unsigned char *)obj + mp->offset;
|
||||
if (mp->flags & _Py_AFTER_ITEMS) {
|
||||
PyTypeObject *type = Py_TYPE(obj);
|
||||
addr += _Py_SIZE_ROUND_UP(Py_SIZE(obj) * type->tp_itemsize, SIZEOF_VOID_P);
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
PyMember_GetOne(const char *obj_addr, PyMemberDef *l)
|
||||
{
|
||||
|
|
@ -31,7 +43,7 @@ PyMember_GetOne(const char *obj_addr, PyMemberDef *l)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
const char* addr = obj_addr + l->offset;
|
||||
const void *addr = _PyMember_GetOffset((PyObject *)obj_addr, l);
|
||||
switch (l->type) {
|
||||
case Py_T_BOOL:
|
||||
v = PyBool_FromLong(FT_ATOMIC_LOAD_CHAR_RELAXED(*(char*)addr));
|
||||
|
|
@ -80,7 +92,7 @@ PyMember_GetOne(const char *obj_addr, PyMemberDef *l)
|
|||
v = PyUnicode_FromString((char*)addr);
|
||||
break;
|
||||
case Py_T_CHAR: {
|
||||
char char_val = FT_ATOMIC_LOAD_CHAR_RELAXED(*addr);
|
||||
char char_val = FT_ATOMIC_LOAD_CHAR_RELAXED(*(char*)addr);
|
||||
v = PyUnicode_FromStringAndSize(&char_val, 1);
|
||||
break;
|
||||
}
|
||||
|
|
@ -151,10 +163,8 @@ PyMember_SetOne(char *addr, PyMemberDef *l, PyObject *v)
|
|||
return -1;
|
||||
}
|
||||
|
||||
#ifdef Py_GIL_DISABLED
|
||||
PyObject *obj = (PyObject *) addr;
|
||||
#endif
|
||||
addr += l->offset;
|
||||
PyObject *obj = (PyObject *)addr;
|
||||
addr = _PyMember_GetOffset(obj, l);
|
||||
|
||||
if ((l->flags & Py_READONLY))
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue