Skip to content

Commit 4d82f62

Browse files
encukouabalkinMatzeB
authored
gh-47146: Soft-deprecate structmember.h, expose its contents via Python.h (GH-99014)
The ``structmember.h`` header is deprecated, though it continues to be available and there are no plans to remove it. There are no deprecation warnings. Old code can stay unchanged (unless the extra include and non-namespaced macros bother you greatly). Specifically, no uses in CPython are updated -- that would just be unnecessary churn. The ``structmember.h`` header is deprecated, though it continues to be available and there are no plans to remove it. Its contents are now available just by including ``Python.h``, with a ``Py`` prefix added if it was missing: - `PyMemberDef`, `PyMember_GetOne` and`PyMember_SetOne` - Type macros like `Py_T_INT`, `Py_T_DOUBLE`, etc. (previously ``T_INT``, ``T_DOUBLE``, etc.) - The flags `Py_READONLY` (previously ``READONLY``) and `Py_AUDIT_READ` (previously all uppercase) Several items are not exposed from ``Python.h``: - `T_OBJECT` (use `Py_T_OBJECT_EX`) - `T_NONE` (previously undocumented, and pretty quirky) - The macro ``WRITE_RESTRICTED`` which does nothing. - The macros ``RESTRICTED`` and ``READ_RESTRICTED``, equivalents of `Py_AUDIT_READ`. - In some configurations, ``<stddef.h>`` is not included from ``Python.h``. It should be included manually when using ``offsetof()``. The deprecated header continues to provide its original contents under the original names. Your old code can stay unchanged, unless the extra include and non-namespaced macros bother you greatly. There is discussion on the issue to rename `T_PYSSIZET` to `PY_T_SSIZE` or similar. I chose not to do that -- users will probably copy/paste that with any spelling, and not renaming it makes migration docs simpler. Co-Authored-By: Alexander Belopolsky <[email protected]> Co-Authored-By: Matthias Braun <[email protected]>
1 parent 1bf983c commit 4d82f62

File tree

19 files changed

+667
-345
lines changed

19 files changed

+667
-345
lines changed

Doc/c-api/structures.rst

Lines changed: 184 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -385,100 +385,223 @@ Accessing attributes of extension types
385385
.. c:type:: PyMemberDef
386386
387387
Structure which describes an attribute of a type which corresponds to a C
388-
struct member. Its fields are:
388+
struct member. Its fields are, in order:
389389
390-
.. c:member:: const char* PyMemberDef.name
390+
.. c:member:: const char* name
391391
392-
Name of the member
392+
Name of the member.
393+
A NULL value marks the end of a ``PyMemberDef[]`` array.
393394
394-
.. c:member:: int PyMemberDef.type
395-
396-
The type of the member in the C struct.
395+
The string should be static, no copy is made of it.
397396
398397
.. c:member:: Py_ssize_t PyMemberDef.offset
399398
400399
The offset in bytes that the member is located on the type’s object struct.
401400
402-
.. c:member:: int PyMemberDef.flags
403-
404-
Flag bits indicating if the field should be read-only or writable.
405-
406-
.. c:member:: const char* PyMemberDef.doc
407-
408-
Points to the contents of the docstring.
409-
410-
:c:member:`PyMemberDef.type` can be one of many ``T_`` macros corresponding to various C
411-
types. When the member is accessed in Python, it will be converted to the
412-
equivalent Python type.
413-
414-
=============== ==================
415-
Macro name C type
416-
=============== ==================
417-
T_SHORT short
418-
T_INT int
419-
T_LONG long
420-
T_FLOAT float
421-
T_DOUBLE double
422-
T_STRING const char \*
423-
T_OBJECT PyObject \*
424-
T_OBJECT_EX PyObject \*
425-
T_CHAR char
426-
T_BYTE char
427-
T_UBYTE unsigned char
428-
T_UINT unsigned int
429-
T_USHORT unsigned short
430-
T_ULONG unsigned long
431-
T_BOOL char
432-
T_LONGLONG long long
433-
T_ULONGLONG unsigned long long
434-
T_PYSSIZET Py_ssize_t
435-
=============== ==================
436-
437-
:c:macro:`T_OBJECT` and :c:macro:`T_OBJECT_EX` differ in that
438-
:c:macro:`T_OBJECT` returns ``None`` if the member is ``NULL`` and
439-
:c:macro:`T_OBJECT_EX` raises an :exc:`AttributeError`. Try to use
440-
:c:macro:`T_OBJECT_EX` over :c:macro:`T_OBJECT` because :c:macro:`T_OBJECT_EX`
441-
handles use of the :keyword:`del` statement on that attribute more correctly
442-
than :c:macro:`T_OBJECT`.
443-
444-
:c:member:`PyMemberDef.flags` can be ``0`` for write and read access or :c:macro:`READONLY` for
445-
read-only access. Using :c:macro:`T_STRING` for :attr:`type` implies
446-
:c:macro:`READONLY`. :c:macro:`T_STRING` data is interpreted as UTF-8.
447-
Only :c:macro:`T_OBJECT` and :c:macro:`T_OBJECT_EX`
448-
members can be deleted. (They are set to ``NULL``).
401+
.. c:member:: int type
402+
403+
The type of the member in the C struct.
404+
See :ref:`PyMemberDef-types` for the possible values.
405+
406+
.. c:member:: int flags
407+
408+
Zero or more of the :ref:`PyMemberDef-flags`, combined using bitwise OR.
409+
410+
.. c:member:: const char* doc
411+
412+
The docstring, or NULL.
413+
The string should be static, no copy is made of it.
414+
Typically, it is defined using :c:macro:`PyDoc_STR`.
415+
416+
By default (when :c:member:`flags` is ``0``), members allow
417+
both read and write access.
418+
Use the :c:macro:`Py_READONLY` flag for read-only access.
419+
Certain types, like :c:macro:`Py_T_STRING`, imply :c:macro:`Py_READONLY`.
420+
Only :c:macro:`Py_T_OBJECT_EX` (and legacy :c:macro:`T_OBJECT`) members can
421+
be deleted.
449422
450423
.. _pymemberdef-offsets:
451424
452-
Heap allocated types (created using :c:func:`PyType_FromSpec` or similar),
453-
``PyMemberDef`` may contain definitions for the special member
454-
``__vectorcalloffset__``, corresponding to
425+
For heap-allocated types (created using :c:func:`PyType_FromSpec` or similar),
426+
``PyMemberDef`` may contain a definition for the special member
427+
``"__vectorcalloffset__"``, corresponding to
455428
:c:member:`~PyTypeObject.tp_vectorcall_offset` in type objects.
456-
These must be defined with ``T_PYSSIZET`` and ``READONLY``, for example::
429+
These must be defined with ``Py_T_PYSSIZET`` and ``Py_READONLY``, for example::
457430
458431
static PyMemberDef spam_type_members[] = {
459-
{"__vectorcalloffset__", T_PYSSIZET, offsetof(Spam_object, vectorcall), READONLY},
432+
{"__vectorcalloffset__", Py_T_PYSSIZET,
433+
offsetof(Spam_object, vectorcall), Py_READONLY},
460434
{NULL} /* Sentinel */
461435
};
462436
437+
(You may need to ``#include <stddef.h>`` for :c:func:`!offsetof`.)
438+
463439
The legacy offsets :c:member:`~PyTypeObject.tp_dictoffset` and
464-
:c:member:`~PyTypeObject.tp_weaklistoffset` are still supported, but extensions are
465-
strongly encouraged to use ``Py_TPFLAGS_MANAGED_DICT`` and
466-
``Py_TPFLAGS_MANAGED_WEAKREF`` instead.
440+
:c:member:`~PyTypeObject.tp_weaklistoffset` can be defined similarly using
441+
``"__dictoffset__"`` and ``"__weaklistoffset__"`` members, but extensions
442+
are strongly encouraged to use :const:`Py_TPFLAGS_MANAGED_DICT` and
443+
:const:`Py_TPFLAGS_MANAGED_WEAKREF` instead.
467444
445+
.. versionchanged:: 3.12
446+
447+
``PyMemberDef`` is always available.
448+
Previously, it required including ``"structmember.h"``.
468449
469450
.. c:function:: PyObject* PyMember_GetOne(const char *obj_addr, struct PyMemberDef *m)
470451
471452
Get an attribute belonging to the object at address *obj_addr*. The
472453
attribute is described by ``PyMemberDef`` *m*. Returns ``NULL``
473454
on error.
474455
456+
.. versionchanged:: 3.12
457+
458+
``PyMember_GetOne`` is always available.
459+
Previously, it required including ``"structmember.h"``.
475460
476461
.. c:function:: int PyMember_SetOne(char *obj_addr, struct PyMemberDef *m, PyObject *o)
477462
478463
Set an attribute belonging to the object at address *obj_addr* to object *o*.
479464
The attribute to set is described by ``PyMemberDef`` *m*. Returns ``0``
480465
if successful and a negative value on failure.
481466
467+
.. versionchanged:: 3.12
468+
469+
``PyMember_SetOne`` is always available.
470+
Previously, it required including ``"structmember.h"``.
471+
472+
.. _PyMemberDef-flags:
473+
474+
Member flags
475+
^^^^^^^^^^^^
476+
477+
The following flags can be used with :c:member:`PyMemberDef.flags`:
478+
479+
.. c:macro:: Py_READONLY
480+
481+
Not writable.
482+
483+
.. c:macro:: Py_AUDIT_READ
484+
485+
Emit an ``object.__getattr__`` :ref:`audit event <audit-events>`
486+
before reading.
487+
488+
.. index::
489+
single: READ_RESTRICTED
490+
single: WRITE_RESTRICTED
491+
single: RESTRICTED
492+
493+
.. versionchanged:: 3.10
494+
495+
The :const:`!RESTRICTED`, :const:`!READ_RESTRICTED` and
496+
:const:`!WRITE_RESTRICTED` macros available with
497+
``#include "structmember.h"`` are deprecated.
498+
:const:`!READ_RESTRICTED` and :const:`!RESTRICTED` are equivalent to
499+
:const:`Py_AUDIT_READ`; :const:`!WRITE_RESTRICTED` does nothing.
500+
501+
.. index::
502+
single: READONLY
503+
504+
.. versionchanged:: 3.12
505+
506+
The :const:`!READONLY` macro was renamed to :const:`Py_READONLY`.
507+
The :const:`!PY_AUDIT_READ` macro was renamed with the ``Py_`` prefix.
508+
The new names are now always available.
509+
Previously, these required ``#include "structmember.h"``.
510+
The header is still available and it provides the old names.
511+
512+
.. _PyMemberDef-types:
513+
514+
Member types
515+
^^^^^^^^^^^^
516+
517+
:c:member:`PyMemberDef.type` can be one of the following macros corresponding
518+
to various C types.
519+
When the member is accessed in Python, it will be converted to the
520+
equivalent Python type.
521+
When it is set from Python, it will be converted back to the C type.
522+
If that is not possible, an exception such as :exc:`TypeError` or
523+
:exc:`ValueError` is raised.
524+
525+
Unless marked (D), attributes defined this way cannot be deleted
526+
using e.g. :keyword:`del` or :py:func:`delattr`.
527+
528+
================================ ============================= ======================
529+
Macro name C type Python type
530+
================================ ============================= ======================
531+
.. c:macro:: Py_T_BYTE :c:expr:`char` :py:class:`int`
532+
.. c:macro:: Py_T_SHORT :c:expr:`short` :py:class:`int`
533+
.. c:macro:: Py_T_INT :c:expr:`int` :py:class:`int`
534+
.. c:macro:: Py_T_LONG :c:expr:`long` :py:class:`int`
535+
.. c:macro:: Py_T_LONGLONG :c:expr:`long long` :py:class:`int`
536+
.. c:macro:: Py_T_UBYTE :c:expr:`unsigned char` :py:class:`int`
537+
.. c:macro:: Py_T_UINT :c:expr:`unsigned int` :py:class:`int`
538+
.. c:macro:: Py_T_USHORT :c:expr:`unsigned short` :py:class:`int`
539+
.. c:macro:: Py_T_ULONG :c:expr:`unsigned long` :py:class:`int`
540+
.. c:macro:: Py_T_ULONGLONG :c:expr:`unsigned long long` :py:class:`int`
541+
.. c:macro:: Py_T_PYSSIZET :c:expr:`Py_ssize_t` :py:class:`int`
542+
.. c:macro:: Py_T_FLOAT :c:expr:`float` :py:class:`float`
543+
.. c:macro:: Py_T_DOUBLE :c:expr:`double` :py:class:`float`
544+
.. c:macro:: Py_T_BOOL :c:expr:`char` :py:class:`bool`
545+
(written as 0 or 1)
546+
.. c:macro:: Py_T_STRING :c:expr:`const char *` (*) :py:class:`str` (RO)
547+
.. c:macro:: Py_T_STRING_INPLACE :c:expr:`const char[]` (*) :py:class:`str` (RO)
548+
.. c:macro:: Py_T_CHAR :c:expr:`char` (0-127) :py:class:`str` (**)
549+
.. c:macro:: Py_T_OBJECT_EX :c:expr:`PyObject *` :py:class:`object` (D)
550+
================================ ============================= ======================
551+
552+
(*): Zero-terminated, UTF8-encoded C string.
553+
With :c:macro:`!Py_T_STRING` the C representation is a pointer;
554+
with :c:macro:`!Py_T_STRING_INLINE` the string is stored directly
555+
in the structure.
556+
557+
(**): String of length 1. Only ASCII is accepted.
558+
559+
(RO): Implies :c:macro:`Py_READONLY`.
560+
561+
(D): Can be deleted, in which case the pointer is set to ``NULL``.
562+
Reading a ``NULL`` pointer raises :py:exc:`AttributeError`.
563+
564+
.. index::
565+
single: T_BYTE
566+
single: T_SHORT
567+
single: T_INT
568+
single: T_LONG
569+
single: T_LONGLONG
570+
single: T_UBYTE
571+
single: T_USHORT
572+
single: T_UINT
573+
single: T_ULONG
574+
single: T_ULONGULONG
575+
single: T_PYSSIZET
576+
single: T_FLOAT
577+
single: T_DOUBLE
578+
single: T_BOOL
579+
single: T_CHAR
580+
single: T_STRING
581+
single: T_STRING_INPLACE
582+
single: T_OBJECT_EX
583+
single: structmember.h
584+
585+
.. versionadded:: 3.12
586+
587+
In previous versions, the macros were only available with
588+
``#include "structmember.h"`` and were named without the ``Py_`` prefix
589+
(e.g. as ``T_INT``).
590+
The header is still available and contains the old names, along with
591+
the following deprecated types:
592+
593+
.. c:macro:: T_OBJECT
594+
595+
Like ``Py_T_OBJECT_EX``, but ``NULL`` is converted to ``None``.
596+
This results in surprising behavior in Python: deleting the attribute
597+
effectively sets it to ``None``.
598+
599+
.. c:macro:: T_NONE
600+
601+
Always ``None``. Must be used with :c:macro:`Py_READONLY`.
602+
603+
Defining Getters and Setters
604+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
482605
483606
.. c:type:: PyGetSetDef
484607

Doc/data/stable_abi.dat

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

Doc/extending/newtypes.rst

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -286,36 +286,11 @@ be read-only or read-write. The structures in the table are defined as::
286286

287287
For each entry in the table, a :term:`descriptor` will be constructed and added to the
288288
type which will be able to extract a value from the instance structure. The
289-
:attr:`type` field should contain one of the type codes defined in the
290-
:file:`structmember.h` header; the value will be used to determine how to
289+
:attr:`type` field should contain a type code like :c:macro:`Py_T_INT` or
290+
:c:macro:`Py_T_DOUBLE`; the value will be used to determine how to
291291
convert Python values to and from C values. The :attr:`flags` field is used to
292-
store flags which control how the attribute can be accessed.
293-
294-
The following flag constants are defined in :file:`structmember.h`; they may be
295-
combined using bitwise-OR.
296-
297-
+---------------------------+----------------------------------------------+
298-
| Constant | Meaning |
299-
+===========================+==============================================+
300-
| :const:`READONLY` | Never writable. |
301-
+---------------------------+----------------------------------------------+
302-
| :const:`PY_AUDIT_READ` | Emit an ``object.__getattr__`` |
303-
| | :ref:`audit events <audit-events>` before |
304-
| | reading. |
305-
+---------------------------+----------------------------------------------+
306-
307-
.. versionchanged:: 3.10
308-
:const:`RESTRICTED`, :const:`READ_RESTRICTED` and :const:`WRITE_RESTRICTED`
309-
are deprecated. However, :const:`READ_RESTRICTED` is an alias for
310-
:const:`PY_AUDIT_READ`, so fields that specify either :const:`RESTRICTED`
311-
or :const:`READ_RESTRICTED` will also raise an audit event.
312-
313-
.. index::
314-
single: READONLY
315-
single: READ_RESTRICTED
316-
single: WRITE_RESTRICTED
317-
single: RESTRICTED
318-
single: PY_AUDIT_READ
292+
store flags which control how the attribute can be accessed: you can set it to
293+
:c:macro:`Py_READONLY` to prevent Python code from setting it.
319294

320295
An interesting advantage of using the :c:member:`~PyTypeObject.tp_members` table to build
321296
descriptors that are used at runtime is that any attribute defined this way can

Doc/extending/newtypes_tutorial.rst

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -239,13 +239,6 @@ adds these capabilities:
239239

240240
This version of the module has a number of changes.
241241

242-
We've added an extra include::
243-
244-
#include <structmember.h>
245-
246-
This include provides declarations that we use to handle attributes, as
247-
described a bit later.
248-
249242
The :class:`Custom` type now has three data attributes in its C struct,
250243
*first*, *last*, and *number*. The *first* and *last* variables are Python
251244
strings containing first and last names. The *number* attribute is a C integer.
@@ -436,11 +429,11 @@ We want to expose our instance variables as attributes. There are a
436429
number of ways to do that. The simplest way is to define member definitions::
437430

438431
static PyMemberDef Custom_members[] = {
439-
{"first", T_OBJECT_EX, offsetof(CustomObject, first), 0,
432+
{"first", Py_T_OBJECT_EX, offsetof(CustomObject, first), 0,
440433
"first name"},
441-
{"last", T_OBJECT_EX, offsetof(CustomObject, last), 0,
434+
{"last", Py_T_OBJECT_EX, offsetof(CustomObject, last), 0,
442435
"last name"},
443-
{"number", T_INT, offsetof(CustomObject, number), 0,
436+
{"number", Py_T_INT, offsetof(CustomObject, number), 0,
444437
"custom number"},
445438
{NULL} /* Sentinel */
446439
};
@@ -609,7 +602,7 @@ above. In this case, we aren't using a closure, so we just pass ``NULL``.
609602
We also remove the member definitions for these attributes::
610603

611604
static PyMemberDef Custom_members[] = {
612-
{"number", T_INT, offsetof(CustomObject, number), 0,
605+
{"number", Py_T_INT, offsetof(CustomObject, number), 0,
613606
"custom number"},
614607
{NULL} /* Sentinel */
615608
};

Doc/includes/custom2.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#define PY_SSIZE_T_CLEAN
22
#include <Python.h>
3-
#include "structmember.h"
3+
#include <stddef.h> /* for offsetof() */
44

55
typedef struct {
66
PyObject_HEAD
@@ -63,11 +63,11 @@ Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
6363
}
6464

6565
static PyMemberDef Custom_members[] = {
66-
{"first", T_OBJECT_EX, offsetof(CustomObject, first), 0,
66+
{"first", Py_T_OBJECT_EX, offsetof(CustomObject, first), 0,
6767
"first name"},
68-
{"last", T_OBJECT_EX, offsetof(CustomObject, last), 0,
68+
{"last", Py_T_OBJECT_EX, offsetof(CustomObject, last), 0,
6969
"last name"},
70-
{"number", T_INT, offsetof(CustomObject, number), 0,
70+
{"number", Py_T_INT, offsetof(CustomObject, number), 0,
7171
"custom number"},
7272
{NULL} /* Sentinel */
7373
};

0 commit comments

Comments
 (0)