Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitcd9a56c

Browse files
encukouarhadthedeverlend-aasland
authored
gh-103509: PEP 697 -- Limited C API for Extending Opaque Types (GH-103511)
Co-authored-by: Oleg Iarygin <oleg@arhadthedev.net>Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com>
1 parent35d2738 commitcd9a56c

30 files changed

+970
-19
lines changed

‎Doc/c-api/object.rst

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,3 +395,42 @@ Object Protocol
395395
returns ``NULL`` if the object cannot be iterated.
396396
397397
..versionadded::3.10
398+
399+
..c:function::void *PyObject_GetTypeData(PyObject *o, PyTypeObject *cls)
400+
401+
Get a pointer to subclass-specific data reserved for *cls*.
402+
403+
The object *o* must be an instance of *cls*, and *cls* must have been
404+
created using negative:c:member:`PyType_Spec.basicsize`.
405+
Python does not check this.
406+
407+
On error, set an exception and return ``NULL``.
408+
409+
..versionadded::3.12
410+
411+
..c:function:: Py_ssize_tPyType_GetTypeDataSize(PyTypeObject *cls)
412+
413+
Return the size of the instance memory space reserved for *cls*, i.e. the size of the
414+
memory:c:func:`PyObject_GetTypeData` returns.
415+
416+
This may be larger than requested using:c:member:`-PyType_Spec.basicsize <PyType_Spec.basicsize>`;
417+
it is safe to use this larger size (e.g. with:c:func:`!memset`).
418+
419+
The type *cls* **must** have been created using
420+
negative :c:member:`PyType_Spec.basicsize`.
421+
Python does not check this.
422+
423+
On error, set an exception and return a negative value.
424+
425+
.. versionadded:: 3.12
426+
427+
.. c:function:: void *PyObject_GetItemData(PyObject *o)
428+
429+
Get a pointer to per-item data for a class with
430+
:const:`Py_TPFLAGS_ITEMS_AT_END`.
431+
432+
On error, set an exception and return ``NULL``.
433+
:py:exc:`TypeError` is raised if *o* does not have
434+
:const:`Py_TPFLAGS_ITEMS_AT_END` set.
435+
436+
..versionadded::3.12

‎Doc/c-api/structures.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,22 @@ The following flags can be used with :c:member:`PyMemberDef.flags`:
486486
Emit an ``object.__getattr__``:ref:`audit event<audit-events>`
487487
before reading.
488488
489+
..c:macro:: Py_RELATIVE_OFFSET
490+
491+
Indicates that the:c:member:`~PyMemberDef.offset` of this ``PyMemberDef``
492+
entry indicates an offset from the subclass-specific data, rather than
493+
from ``PyObject``.
494+
495+
Can only be used as part of:c:member:`Py_tp_members <PyTypeObject.tp_members>`
496+
:c:type:`slot <PyTypeSlot>` when creating a class using negative
497+
:c:member:`~PyTypeDef.basicsize`.
498+
It is mandatory in that case.
499+
500+
This flag is only used in:c:type:`PyTypeSlot`.
501+
When setting:c:member:`~PyTypeObject.tp_members` during
502+
class creation, Python clears it and sets
503+
:c:member:`PyMemberDef.offset` to the offset from the ``PyObject`` struct.
504+
489505
..index::
490506
single: READ_RESTRICTED
491507
single: WRITE_RESTRICTED

‎Doc/c-api/type.rst

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -353,25 +353,57 @@ The following functions and structs are used to create
353353
354354
Structure defining a type's behavior.
355355
356-
..c:member::constchar*PyType_Spec.name
356+
..c:member::constchar* name
357357
358358
Name of the type, used to set:c:member:`PyTypeObject.tp_name`.
359359
360-
..c:member::int PyType_Spec.basicsize
361-
..c:member::int PyType_Spec.itemsize
360+
..c:member::int basicsize
362361
363-
Size of the instance in bytes, used to set
364-
:c:member:`PyTypeObject.tp_basicsize` and
365-
:c:member:`PyTypeObject.tp_itemsize`.
362+
If positive, specifies the size of the instance in bytes.
363+
It is used to set:c:member:`PyTypeObject.tp_basicsize`.
366364
367-
..c:member::int PyType_Spec.flags
365+
If zero, specifies that:c:member:`~PyTypeObject.tp_basicsize`
366+
should be inherited.
367+
368+
If negative, the absolute value specifies how much space instances of the
369+
class need *in addition* to the superclass.
370+
Use:c:func:`PyObject_GetTypeData` to get a pointer to subclass-specific
371+
memory reserved this way.
372+
373+
..versionchanged::3.12
374+
375+
Previously, this field could not be negative.
376+
377+
..c:member::int itemsize
378+
379+
Size of one element of a variable-size type, in bytes.
380+
Used to set:c:member:`PyTypeObject.tp_itemsize`.
381+
See ``tp_itemsize`` documentation for caveats.
382+
383+
If zero,:c:member:`~PyTypeObject.tp_itemsize` is inherited.
384+
Extending arbitrary variable-sized classes is dangerous,
385+
since some types use a fixed offset for variable-sized memory,
386+
which can then overlap fixed-sized memory used by a subclass.
387+
To help prevent mistakes, inheriting ``itemsize`` is only possible
388+
in the following situations:
389+
390+
- The base is not variable-sized (its
391+
:c:member:`~PyTypeObject.tp_itemsize`).
392+
- The requested :c:member:`PyType_Spec.basicsize` is positive,
393+
suggesting that the memory layout of the base class is known.
394+
- The requested :c:member:`PyType_Spec.basicsize` is zero,
395+
suggesting that the subclass does not access the instance's memory
396+
directly.
397+
- With the :const:`Py_TPFLAGS_ITEMS_AT_END` flag.
398+
399+
.. c:member:: unsigned int flags
368400
369401
Type flags, used to set :c:member:`PyTypeObject.tp_flags`.
370402
371403
If the ``Py_TPFLAGS_HEAPTYPE`` flag is not set,
372404
:c:func:`PyType_FromSpecWithBases` sets it automatically.
373405
374-
..c:member:: PyType_Slot *PyType_Spec.slots
406+
.. c:member:: PyType_Slot *slots
375407
376408
Array of :c:type:`PyType_Slot` structures.
377409
Terminated by the special slot value ``{0, NULL}``.

‎Doc/c-api/typeobj.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,6 +1171,26 @@ and :c:type:`PyType_Type` effectively act as defaults.)
11711171
:c:member:`~PyTypeObject.tp_weaklistoffset` field is set in a superclass.
11721172

11731173

1174+
..data::Py_TPFLAGS_ITEMS_AT_END
1175+
1176+
Only usable with variable-size types, i.e. ones with non-zero
1177+
:c:member:`~PyObject.tp_itemsize`.
1178+
1179+
Indicates that the variable-sized portion of an instance of this type is
1180+
at the end of the instance's memory area, at an offset of
1181+
:c:expr:`Py_TYPE(obj)->tp_basicsize` (which may be different in each
1182+
subclass).
1183+
1184+
When setting this flag, be sure that all superclasses either
1185+
use this memory layout, or are not variable-sized.
1186+
Python does not check this.
1187+
1188+
..versionadded::3.12
1189+
1190+
**Inheritance:**
1191+
1192+
This flag is inherited.
1193+
11741194
.. XXX Document more flags here?
11751195
11761196

‎Doc/data/stable_abi.dat

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎Doc/whatsnew/3.12.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,6 +1159,21 @@ New Features
11591159

11601160
(Contributed by Petr Viktorin in:gh:`101101`.)
11611161

1162+
*:pep:`697`: Added API for extending types whose instance memory layout is
1163+
opaque:
1164+
1165+
-:c:member:`PyType_Spec.basicsize` can be zero or negative to specify
1166+
inheriting or extending the base class size.
1167+
-:c:func:`PyObject_GetTypeData` and:c:func:`PyType_GetTypeDataSize`
1168+
added to allow access to subclass-specific instance data.
1169+
-:const:`Py_TPFLAGS_ITEMS_AT_END` and:c:func:`PyObject_GetItemData`
1170+
added to allow safely extending certain variable-sized types, including
1171+
:c:var:`PyType_Type`.
1172+
-:c:macro:`Py_RELATIVE_OFFSET` added to allow defining
1173+
:c:type:`members <PyMemberDef>` in terms of a subclass-specific struct.
1174+
1175+
(Contributed by Petr Viktorin in:gh:`103509`.)
1176+
11621177
* Added the new limited C API function:c:func:`PyType_FromMetaclass`,
11631178
which generalizes the existing:c:func:`PyType_FromModuleAndSpec` using
11641179
an additional metaclass argument.

‎Include/cpython/object.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,7 @@ Py_DEPRECATED(3.11) typedef int UsingDeprecatedTrashcanMacro;
553553
Py_TRASHCAN_END; \
554554
} while(0);
555555

556+
PyAPI_FUNC(void*)PyObject_GetItemData(PyObject*obj);
556557

557558
PyAPI_FUNC(int)_PyObject_VisitManagedDict(PyObject*obj,visitprocvisit,void*arg);
558559
PyAPI_FUNC(void)_PyObject_ClearManagedDict(PyObject*obj);

‎Include/descrobject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ struct PyMemberDef {
8383
#definePy_READONLY 1
8484
#definePy_AUDIT_READ 2 // Added in 3.10, harmless no-op before that
8585
#define_Py_WRITE_RESTRICTED 4 // Deprecated, no-op. Do not reuse the value.
86+
#definePy_RELATIVE_OFFSET 8
8687

8788
PyAPI_FUNC(PyObject*)PyMember_GetOne(constchar*,PyMemberDef*);
8889
PyAPI_FUNC(int)PyMember_SetOne(char*,PyMemberDef*,PyObject*);

‎Include/internal/pycore_object.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -389,11 +389,6 @@ extern PyObject ** _PyObject_ComputedDictPointer(PyObject *);
389389
externvoid_PyObject_FreeInstanceAttributes(PyObject*obj);
390390
externint_PyObject_IsInstanceDictEmpty(PyObject*);
391391

392-
// Access macro to the members which are floating "behind" the object
393-
staticinlinePyMemberDef*_PyHeapType_GET_MEMBERS(PyHeapTypeObject*etype) {
394-
return (PyMemberDef*)((char*)etype+Py_TYPE(etype)->tp_basicsize);
395-
}
396-
397392
PyAPI_FUNC(PyObject*)_PyObject_LookupSpecial(PyObject*,PyObject*);
398393

399394
/* C function call trampolines to mitigate bad function pointer casts.

‎Include/object.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,8 @@ PyAPI_FUNC(PyObject *) PyType_GetQualName(PyTypeObject *);
355355
#endif
356356
#if !defined(Py_LIMITED_API)||Py_LIMITED_API+0 >=0x030C0000
357357
PyAPI_FUNC(PyObject*)PyType_FromMetaclass(PyTypeObject*,PyObject*,PyType_Spec*,PyObject*);
358+
PyAPI_FUNC(void*)PyObject_GetTypeData(PyObject*obj,PyTypeObject*cls);
359+
PyAPI_FUNC(Py_ssize_t)PyType_GetTypeDataSize(PyTypeObject*cls);
358360
#endif
359361

360362
/* Generic type check */
@@ -521,6 +523,9 @@ given type object has a specified feature.
521523
// subject itself (rather than a mapped attribute on it):
522524
#define_Py_TPFLAGS_MATCH_SELF (1UL << 22)
523525

526+
/* Items (ob_size*tp_itemsize) are found at the end of an instance's memory */
527+
#definePy_TPFLAGS_ITEMS_AT_END (1UL << 23)
528+
524529
/* These flags are used to determine if a type is a subclass. */
525530
#definePy_TPFLAGS_LONG_SUBCLASS (1UL << 24)
526531
#definePy_TPFLAGS_LIST_SUBCLASS (1UL << 25)

‎Include/pyport.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,4 +765,15 @@ extern char * _getpty(int *, int, mode_t, int);
765765
#undef __bool__
766766
#endif
767767

768+
// Make sure we have maximum alignment, even if the current compiler
769+
// does not support max_align_t. Note that:
770+
// - Autoconf reports alignment of unknown types to 0.
771+
// - 'long double' has maximum alignment on *most* platforms,
772+
// looks like the best we can do for pre-C11 compilers.
773+
// - The value is tested, see test_alignof_max_align_t
774+
#if !defined(ALIGNOF_MAX_ALIGN_T) || ALIGNOF_MAX_ALIGN_T == 0
775+
#undef ALIGNOF_MAX_ALIGN_T
776+
#defineALIGNOF_MAX_ALIGN_T_Alignof(longdouble)
777+
#endif
778+
768779
#endif/* Py_PYPORT_H*/

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp