mirror of
https://github.com/python/cpython.git
synced 2025-11-25 21:11:09 +00:00
GH-95245: Document use of MANAGED flags instead of offsets. (GH-96044)
This commit is contained in:
parent
07f12b5c15
commit
0f733fffe8
4 changed files with 78 additions and 79 deletions
|
|
@ -469,19 +469,21 @@ Accessing attributes of extension types
|
||||||
.. _pymemberdef-offsets:
|
.. _pymemberdef-offsets:
|
||||||
|
|
||||||
Heap allocated types (created using :c:func:`PyType_FromSpec` or similar),
|
Heap allocated types (created using :c:func:`PyType_FromSpec` or similar),
|
||||||
``PyMemberDef`` may contain definitions for the special members
|
``PyMemberDef`` may contain definitions for the special member
|
||||||
``__dictoffset__``, ``__weaklistoffset__`` and ``__vectorcalloffset__``,
|
``__vectorcalloffset__``, corresponding to
|
||||||
corresponding to
|
|
||||||
:c:member:`~PyTypeObject.tp_dictoffset`,
|
|
||||||
:c:member:`~PyTypeObject.tp_weaklistoffset` and
|
|
||||||
:c:member:`~PyTypeObject.tp_vectorcall_offset` in type objects.
|
:c:member:`~PyTypeObject.tp_vectorcall_offset` in type objects.
|
||||||
These must be defined with ``T_PYSSIZET`` and ``READONLY``, for example::
|
These must be defined with ``T_PYSSIZET`` and ``READONLY``, for example::
|
||||||
|
|
||||||
static PyMemberDef spam_type_members[] = {
|
static PyMemberDef spam_type_members[] = {
|
||||||
{"__dictoffset__", T_PYSSIZET, offsetof(Spam_object, dict), READONLY},
|
{"__vectorcalloffset__", T_PYSSIZET, offsetof(Spam_object, vectorcall), READONLY},
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
The legacy offsets :c:member:`~PyTypeObject.tp_dictoffset` and
|
||||||
|
:c:member:`~PyTypeObject.tp_weaklistoffset` are still supported, but extensions are
|
||||||
|
strongly encouraged to use ``Py_TPFLAGS_MANAGED_DICT`` and
|
||||||
|
``Py_TPFLAGS_MANAGED_WEAKREF`` instead.
|
||||||
|
|
||||||
|
|
||||||
.. c:function:: PyObject* PyMember_GetOne(const char *obj_addr, struct PyMemberDef *m)
|
.. c:function:: PyObject* PyMember_GetOne(const char *obj_addr, struct PyMemberDef *m)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -327,9 +327,9 @@ The following functions and structs are used to create
|
||||||
* :c:member:`~PyTypeObject.tp_weaklist`
|
* :c:member:`~PyTypeObject.tp_weaklist`
|
||||||
* :c:member:`~PyTypeObject.tp_vectorcall`
|
* :c:member:`~PyTypeObject.tp_vectorcall`
|
||||||
* :c:member:`~PyTypeObject.tp_weaklistoffset`
|
* :c:member:`~PyTypeObject.tp_weaklistoffset`
|
||||||
(see :ref:`PyMemberDef <pymemberdef-offsets>`)
|
(use :const:`Py_TPFLAGS_MANAGED_WEAKREF` instead)
|
||||||
* :c:member:`~PyTypeObject.tp_dictoffset`
|
* :c:member:`~PyTypeObject.tp_dictoffset`
|
||||||
(see :ref:`PyMemberDef <pymemberdef-offsets>`)
|
(use :const:`Py_TPFLAGS_MANAGED_DICT` instead)
|
||||||
* :c:member:`~PyTypeObject.tp_vectorcall_offset`
|
* :c:member:`~PyTypeObject.tp_vectorcall_offset`
|
||||||
(see :ref:`PyMemberDef <pymemberdef-offsets>`)
|
(see :ref:`PyMemberDef <pymemberdef-offsets>`)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ Quick Reference
|
||||||
| | | __gt__, | | | | |
|
| | | __gt__, | | | | |
|
||||||
| | | __ge__ | | | | |
|
| | | __ge__ | | | | |
|
||||||
+------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+
|
+------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+
|
||||||
| :c:member:`~PyTypeObject.tp_weaklistoffset` | :c:type:`Py_ssize_t` | | | X | | ? |
|
| (:c:member:`~PyTypeObject.tp_weaklistoffset`) | :c:type:`Py_ssize_t` | | | X | | ? |
|
||||||
+------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+
|
+------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+
|
||||||
| :c:member:`~PyTypeObject.tp_iter` | :c:type:`getiterfunc` | __iter__ | | | | X |
|
| :c:member:`~PyTypeObject.tp_iter` | :c:type:`getiterfunc` | __iter__ | | | | X |
|
||||||
+------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+
|
+------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+
|
||||||
|
|
@ -117,7 +117,7 @@ Quick Reference
|
||||||
| :c:member:`~PyTypeObject.tp_descr_set` | :c:type:`descrsetfunc` | __set__, | | | | X |
|
| :c:member:`~PyTypeObject.tp_descr_set` | :c:type:`descrsetfunc` | __set__, | | | | X |
|
||||||
| | | __delete__ | | | | |
|
| | | __delete__ | | | | |
|
||||||
+------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+
|
+------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+
|
||||||
| :c:member:`~PyTypeObject.tp_dictoffset` | :c:type:`Py_ssize_t` | | | X | | ? |
|
| (:c:member:`~PyTypeObject.tp_dictoffset`) | :c:type:`Py_ssize_t` | | | X | | ? |
|
||||||
+------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+
|
+------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+
|
||||||
| :c:member:`~PyTypeObject.tp_init` | :c:type:`initproc` | __init__ | X | X | | X |
|
| :c:member:`~PyTypeObject.tp_init` | :c:type:`initproc` | __init__ | X | X | | X |
|
||||||
+------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+
|
+------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+
|
||||||
|
|
@ -1018,7 +1018,6 @@ and :c:type:`PyType_Type` effectively act as defaults.)
|
||||||
:const:`Py_TPFLAGS_HAVE_GC` flag bit is clear in the subtype and the
|
:const:`Py_TPFLAGS_HAVE_GC` flag bit is clear in the subtype and the
|
||||||
:c:member:`~PyTypeObject.tp_traverse` and :c:member:`~PyTypeObject.tp_clear` fields in the subtype exist and have
|
:c:member:`~PyTypeObject.tp_traverse` and :c:member:`~PyTypeObject.tp_clear` fields in the subtype exist and have
|
||||||
``NULL`` values.
|
``NULL`` values.
|
||||||
|
|
||||||
.. XXX are most flag bits *really* inherited individually?
|
.. XXX are most flag bits *really* inherited individually?
|
||||||
|
|
||||||
**Default:**
|
**Default:**
|
||||||
|
|
@ -1135,6 +1134,33 @@ and :c:type:`PyType_Type` effectively act as defaults.)
|
||||||
:const:`Py_TPFLAGS_IMMUTABLETYPE` flag set. For extension types, it is
|
:const:`Py_TPFLAGS_IMMUTABLETYPE` flag set. For extension types, it is
|
||||||
inherited whenever :c:member:`~PyTypeObject.tp_descr_get` is inherited.
|
inherited whenever :c:member:`~PyTypeObject.tp_descr_get` is inherited.
|
||||||
|
|
||||||
|
.. data:: Py_TPFLAGS_MANAGED_DICT
|
||||||
|
|
||||||
|
This bit indicates that instances of the class have a ``__dict___``
|
||||||
|
attribute, and that the space for the dictionary is managed by the VM.
|
||||||
|
|
||||||
|
If this flag is set, :const:`Py_TPFLAGS_HAVE_GC` should also be set.
|
||||||
|
|
||||||
|
.. versionadded:: 3.12
|
||||||
|
|
||||||
|
**Inheritance:**
|
||||||
|
|
||||||
|
This flag is inherited unless the
|
||||||
|
:c:member:`~PyTypeObject.tp_dictoffset` field is set in a superclass.
|
||||||
|
|
||||||
|
|
||||||
|
.. data:: Py_TPFLAGS_MANAGED_WEAKREF
|
||||||
|
|
||||||
|
This bit indicates that instances of the class should be weakly
|
||||||
|
referenceable.
|
||||||
|
|
||||||
|
.. versionadded:: 3.12
|
||||||
|
|
||||||
|
**Inheritance:**
|
||||||
|
|
||||||
|
This flag is inherited unless the
|
||||||
|
:c:member:`~PyTypeObject.tp_weaklistoffset` field is set in a superclass.
|
||||||
|
|
||||||
|
|
||||||
.. XXX Document more flags here?
|
.. XXX Document more flags here?
|
||||||
|
|
||||||
|
|
@ -1487,6 +1513,9 @@ and :c:type:`PyType_Type` effectively act as defaults.)
|
||||||
|
|
||||||
.. c:member:: Py_ssize_t PyTypeObject.tp_weaklistoffset
|
.. c:member:: Py_ssize_t PyTypeObject.tp_weaklistoffset
|
||||||
|
|
||||||
|
While this field is still supported, :const:`Py_TPFLAGS_MANAGED_WEAKREF`
|
||||||
|
should be used instead, if at all possible.
|
||||||
|
|
||||||
If the instances of this type are weakly referenceable, this field is greater
|
If the instances of this type are weakly referenceable, this field is greater
|
||||||
than zero and contains the offset in the instance structure of the weak
|
than zero and contains the offset in the instance structure of the weak
|
||||||
reference list head (ignoring the GC header, if present); this offset is used by
|
reference list head (ignoring the GC header, if present); this offset is used by
|
||||||
|
|
@ -1497,6 +1526,9 @@ and :c:type:`PyType_Type` effectively act as defaults.)
|
||||||
Do not confuse this field with :c:member:`~PyTypeObject.tp_weaklist`; that is the list head for
|
Do not confuse this field with :c:member:`~PyTypeObject.tp_weaklist`; that is the list head for
|
||||||
weak references to the type object itself.
|
weak references to the type object itself.
|
||||||
|
|
||||||
|
It is an error to set both the :const:`Py_TPFLAGS_MANAGED_WEAKREF` bit and
|
||||||
|
:c:member:`~PyTypeObject.tp_weaklist`.
|
||||||
|
|
||||||
**Inheritance:**
|
**Inheritance:**
|
||||||
|
|
||||||
This field is inherited by subtypes, but see the rules listed below. A subtype
|
This field is inherited by subtypes, but see the rules listed below. A subtype
|
||||||
|
|
@ -1504,19 +1536,12 @@ and :c:type:`PyType_Type` effectively act as defaults.)
|
||||||
reference list head than the base type. Since the list head is always found via
|
reference list head than the base type. Since the list head is always found via
|
||||||
:c:member:`~PyTypeObject.tp_weaklistoffset`, this should not be a problem.
|
:c:member:`~PyTypeObject.tp_weaklistoffset`, this should not be a problem.
|
||||||
|
|
||||||
When a type defined by a class statement has no :attr:`~object.__slots__` declaration,
|
**Default:**
|
||||||
and none of its base types are weakly referenceable, the type is made weakly
|
|
||||||
referenceable by adding a weak reference list head slot to the instance layout
|
|
||||||
and setting the :c:member:`~PyTypeObject.tp_weaklistoffset` of that slot's offset.
|
|
||||||
|
|
||||||
When a type's :attr:`__slots__` declaration contains a slot named
|
If the :const:`Py_TPFLAGS_MANAGED_WEAKREF` bit is set in the
|
||||||
:attr:`__weakref__`, that slot becomes the weak reference list head for
|
:c:member:`~PyTypeObject.tp_dict` field, then
|
||||||
instances of the type, and the slot's offset is stored in the type's
|
:c:member:`~PyTypeObject.tp_weaklistoffset` will be set to a negative value,
|
||||||
:c:member:`~PyTypeObject.tp_weaklistoffset`.
|
to indicate that it is unsafe to use this field.
|
||||||
|
|
||||||
When a type's :attr:`__slots__` declaration does not contain a slot named
|
|
||||||
:attr:`__weakref__`, the type inherits its :c:member:`~PyTypeObject.tp_weaklistoffset` from its
|
|
||||||
base type.
|
|
||||||
|
|
||||||
|
|
||||||
.. c:member:: getiterfunc PyTypeObject.tp_iter
|
.. c:member:: getiterfunc PyTypeObject.tp_iter
|
||||||
|
|
@ -1695,6 +1720,9 @@ and :c:type:`PyType_Type` effectively act as defaults.)
|
||||||
|
|
||||||
.. c:member:: Py_ssize_t PyTypeObject.tp_dictoffset
|
.. c:member:: Py_ssize_t PyTypeObject.tp_dictoffset
|
||||||
|
|
||||||
|
While this field is still supported, :const:`Py_TPFLAGS_MANAGED_DICT` should be
|
||||||
|
used instead, if at all possible.
|
||||||
|
|
||||||
If the instances of this type have a dictionary containing instance variables,
|
If the instances of this type have a dictionary containing instance variables,
|
||||||
this field is non-zero and contains the offset in the instances of the type of
|
this field is non-zero and contains the offset in the instances of the type of
|
||||||
the instance variable dictionary; this offset is used by
|
the instance variable dictionary; this offset is used by
|
||||||
|
|
@ -1703,17 +1731,7 @@ and :c:type:`PyType_Type` effectively act as defaults.)
|
||||||
Do not confuse this field with :c:member:`~PyTypeObject.tp_dict`; that is the dictionary for
|
Do not confuse this field with :c:member:`~PyTypeObject.tp_dict`; that is the dictionary for
|
||||||
attributes of the type object itself.
|
attributes of the type object itself.
|
||||||
|
|
||||||
If the value of this field is greater than zero, it specifies the offset from
|
The value specifies the offset of the dictionary from the start of the instance structure.
|
||||||
the start of the instance structure. If the value is less than zero, it
|
|
||||||
specifies the offset from the *end* of the instance structure. A negative
|
|
||||||
offset is more expensive to use, and should only be used when the instance
|
|
||||||
structure contains a variable-length part. This is used for example to add an
|
|
||||||
instance variable dictionary to subtypes of :class:`str` or :class:`tuple`. Note
|
|
||||||
that the :c:member:`~PyTypeObject.tp_basicsize` field should account for the dictionary added to
|
|
||||||
the end in that case, even though the dictionary is not included in the basic
|
|
||||||
object layout. On a system with a pointer size of 4 bytes,
|
|
||||||
:c:member:`~PyTypeObject.tp_dictoffset` should be set to ``-4`` to indicate that the dictionary is
|
|
||||||
at the very end of the structure.
|
|
||||||
|
|
||||||
The :c:member:`~PyTypeObject.tp_dictoffset` should be regarded as write-only.
|
The :c:member:`~PyTypeObject.tp_dictoffset` should be regarded as write-only.
|
||||||
To get the pointer to the dictionary call :c:func:`PyObject_GenericGetDict`.
|
To get the pointer to the dictionary call :c:func:`PyObject_GenericGetDict`.
|
||||||
|
|
@ -1721,30 +1739,26 @@ and :c:type:`PyType_Type` effectively act as defaults.)
|
||||||
dictionary, so it is may be more efficient to call :c:func:`PyObject_GetAttr`
|
dictionary, so it is may be more efficient to call :c:func:`PyObject_GetAttr`
|
||||||
when accessing an attribute on the object.
|
when accessing an attribute on the object.
|
||||||
|
|
||||||
|
It is an error to set both the :const:`Py_TPFLAGS_MANAGED_WEAKREF` bit and
|
||||||
|
:c:member:`~PyTypeObject.tp_dictoffset`.
|
||||||
|
|
||||||
**Inheritance:**
|
**Inheritance:**
|
||||||
|
|
||||||
This field is inherited by subtypes, but see the rules listed below. A subtype
|
This field is inherited by subtypes. A subtype should not override this offset;
|
||||||
may override this offset; this means that the subtype instances store the
|
doing so could be unsafe, if C code tries to access the dictionary at the
|
||||||
dictionary at a difference offset than the base type. Since the dictionary is
|
previous offset.
|
||||||
always found via :c:member:`~PyTypeObject.tp_dictoffset`, this should not be a problem.
|
To properly support inheritance, use :const:`Py_TPFLAGS_MANAGED_DICT`.
|
||||||
|
|
||||||
When a type defined by a class statement has no :attr:`~object.__slots__` declaration,
|
|
||||||
and none of its base types has an instance variable dictionary, a dictionary
|
|
||||||
slot is added to the instance layout and the :c:member:`~PyTypeObject.tp_dictoffset` is set to
|
|
||||||
that slot's offset.
|
|
||||||
|
|
||||||
When a type defined by a class statement has a :attr:`__slots__` declaration,
|
|
||||||
the type inherits its :c:member:`~PyTypeObject.tp_dictoffset` from its base type.
|
|
||||||
|
|
||||||
(Adding a slot named :attr:`~object.__dict__` to the :attr:`__slots__` declaration does
|
|
||||||
not have the expected effect, it just causes confusion. Maybe this should be
|
|
||||||
added as a feature just like :attr:`__weakref__` though.)
|
|
||||||
|
|
||||||
**Default:**
|
**Default:**
|
||||||
|
|
||||||
This slot has no default. For :ref:`static types <static-types>`, if the
|
This slot has no default. For :ref:`static types <static-types>`, if the
|
||||||
field is ``NULL`` then no :attr:`__dict__` gets created for instances.
|
field is ``NULL`` then no :attr:`__dict__` gets created for instances.
|
||||||
|
|
||||||
|
If the :const:`Py_TPFLAGS_MANAGED_DICT` bit is set in the
|
||||||
|
:c:member:`~PyTypeObject.tp_dict` field, then
|
||||||
|
:c:member:`~PyTypeObject.tp_dictoffset` will be set to ``-1``, to indicate
|
||||||
|
that it is unsafe to use this field.
|
||||||
|
|
||||||
|
|
||||||
.. c:member:: initproc PyTypeObject.tp_init
|
.. c:member:: initproc PyTypeObject.tp_init
|
||||||
|
|
||||||
|
|
@ -2663,8 +2677,6 @@ A type that supports weakrefs, instance dicts, and hashing::
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
const char *data;
|
const char *data;
|
||||||
PyObject *inst_dict;
|
|
||||||
PyObject *weakreflist;
|
|
||||||
} MyObject;
|
} MyObject;
|
||||||
|
|
||||||
static PyTypeObject MyObject_Type = {
|
static PyTypeObject MyObject_Type = {
|
||||||
|
|
@ -2672,9 +2684,9 @@ A type that supports weakrefs, instance dicts, and hashing::
|
||||||
.tp_name = "mymod.MyObject",
|
.tp_name = "mymod.MyObject",
|
||||||
.tp_basicsize = sizeof(MyObject),
|
.tp_basicsize = sizeof(MyObject),
|
||||||
.tp_doc = PyDoc_STR("My objects"),
|
.tp_doc = PyDoc_STR("My objects"),
|
||||||
.tp_weaklistoffset = offsetof(MyObject, weakreflist),
|
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
|
||||||
.tp_dictoffset = offsetof(MyObject, inst_dict),
|
Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_MANAGED_DICT |
|
||||||
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
|
Py_TPFLAGS_MANAGED_WEAKREF,
|
||||||
.tp_new = myobj_new,
|
.tp_new = myobj_new,
|
||||||
.tp_traverse = (traverseproc)myobj_traverse,
|
.tp_traverse = (traverseproc)myobj_traverse,
|
||||||
.tp_clear = (inquiry)myobj_clear,
|
.tp_clear = (inquiry)myobj_clear,
|
||||||
|
|
|
||||||
|
|
@ -570,43 +570,28 @@ performance-critical objects (such as numbers).
|
||||||
.. seealso::
|
.. seealso::
|
||||||
Documentation for the :mod:`weakref` module.
|
Documentation for the :mod:`weakref` module.
|
||||||
|
|
||||||
For an object to be weakly referencable, the extension type must do two things:
|
For an object to be weakly referencable, the extension type must set the
|
||||||
|
``Py_TPFLAGS_MANAGED_WEAKREF`` bit of the :c:member:`~PyTypeObject.tp_flags`
|
||||||
|
field. The legacy :c:member:`~PyTypeObject.tp_weaklistoffset` field should
|
||||||
|
be left as zero.
|
||||||
|
|
||||||
#. Include a :c:type:`PyObject\*` field in the C object structure dedicated to
|
Concretely, here is how the statically declared type object would look::
|
||||||
the weak reference mechanism. The object's constructor should leave it
|
|
||||||
``NULL`` (which is automatic when using the default
|
|
||||||
:c:member:`~PyTypeObject.tp_alloc`).
|
|
||||||
|
|
||||||
#. Set the :c:member:`~PyTypeObject.tp_weaklistoffset` type member
|
|
||||||
to the offset of the aforementioned field in the C object structure,
|
|
||||||
so that the interpreter knows how to access and modify that field.
|
|
||||||
|
|
||||||
Concretely, here is how a trivial object structure would be augmented
|
|
||||||
with the required field::
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
PyObject_HEAD
|
|
||||||
PyObject *weakreflist; /* List of weak references */
|
|
||||||
} TrivialObject;
|
|
||||||
|
|
||||||
And the corresponding member in the statically declared type object::
|
|
||||||
|
|
||||||
static PyTypeObject TrivialType = {
|
static PyTypeObject TrivialType = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
/* ... other members omitted for brevity ... */
|
/* ... other members omitted for brevity ... */
|
||||||
.tp_weaklistoffset = offsetof(TrivialObject, weakreflist),
|
.tp_flags = Py_TPFLAGS_MANAGED_WEAKREF | ...,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
The only further addition is that ``tp_dealloc`` needs to clear any weak
|
The only further addition is that ``tp_dealloc`` needs to clear any weak
|
||||||
references (by calling :c:func:`PyObject_ClearWeakRefs`) if the field is
|
references (by calling :c:func:`PyObject_ClearWeakRefs`)::
|
||||||
non-``NULL``::
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
Trivial_dealloc(TrivialObject *self)
|
Trivial_dealloc(TrivialObject *self)
|
||||||
{
|
{
|
||||||
/* Clear weakrefs first before calling any destructors */
|
/* Clear weakrefs first before calling any destructors */
|
||||||
if (self->weakreflist != NULL)
|
PyObject_ClearWeakRefs((PyObject *) self);
|
||||||
PyObject_ClearWeakRefs((PyObject *) self);
|
|
||||||
/* ... remainder of destruction code omitted for brevity ... */
|
/* ... remainder of destruction code omitted for brevity ... */
|
||||||
Py_TYPE(self)->tp_free((PyObject *) self);
|
Py_TYPE(self)->tp_free((PyObject *) self);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue