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 <abalkin@users.noreply.github.com>
Co-Authored-By: Matthias Braun <MatzeB@users.noreply.github.com>
This commit is contained in:
Petr Viktorin 2022-11-22 08:25:43 +01:00 committed by GitHub
parent 1bf983ce7e
commit 4d82f628c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 659 additions and 337 deletions

View File

@ -385,86 +385,67 @@ Accessing attributes of extension types
.. c:type:: PyMemberDef
Structure which describes an attribute of a type which corresponds to a C
struct member. Its fields are:
struct member. Its fields are, in order:
.. c:member:: const char* PyMemberDef.name
.. c:member:: const char* name
Name of the member
Name of the member.
A NULL value marks the end of a ``PyMemberDef[]`` array.
.. c:member:: int PyMemberDef.type
The type of the member in the C struct.
The string should be static, no copy is made of it.
.. c:member:: Py_ssize_t PyMemberDef.offset
The offset in bytes that the member is located on the types object struct.
.. c:member:: int PyMemberDef.flags
.. c:member:: int type
Flag bits indicating if the field should be read-only or writable.
The type of the member in the C struct.
See :ref:`PyMemberDef-types` for the possible values.
.. c:member:: const char* PyMemberDef.doc
.. c:member:: int flags
Points to the contents of the docstring.
Zero or more of the :ref:`PyMemberDef-flags`, combined using bitwise OR.
:c:member:`PyMemberDef.type` can be one of many ``T_`` macros corresponding to various C
types. When the member is accessed in Python, it will be converted to the
equivalent Python type.
.. c:member:: const char* doc
=============== ==================
Macro name C type
=============== ==================
T_SHORT short
T_INT int
T_LONG long
T_FLOAT float
T_DOUBLE double
T_STRING const char \*
T_OBJECT PyObject \*
T_OBJECT_EX PyObject \*
T_CHAR char
T_BYTE char
T_UBYTE unsigned char
T_UINT unsigned int
T_USHORT unsigned short
T_ULONG unsigned long
T_BOOL char
T_LONGLONG long long
T_ULONGLONG unsigned long long
T_PYSSIZET Py_ssize_t
=============== ==================
The docstring, or NULL.
The string should be static, no copy is made of it.
Typically, it is defined using :c:macro:`PyDoc_STR`.
:c:macro:`T_OBJECT` and :c:macro:`T_OBJECT_EX` differ in that
:c:macro:`T_OBJECT` returns ``None`` if the member is ``NULL`` and
:c:macro:`T_OBJECT_EX` raises an :exc:`AttributeError`. Try to use
:c:macro:`T_OBJECT_EX` over :c:macro:`T_OBJECT` because :c:macro:`T_OBJECT_EX`
handles use of the :keyword:`del` statement on that attribute more correctly
than :c:macro:`T_OBJECT`.
:c:member:`PyMemberDef.flags` can be ``0`` for write and read access or :c:macro:`READONLY` for
read-only access. Using :c:macro:`T_STRING` for :attr:`type` implies
:c:macro:`READONLY`. :c:macro:`T_STRING` data is interpreted as UTF-8.
Only :c:macro:`T_OBJECT` and :c:macro:`T_OBJECT_EX`
members can be deleted. (They are set to ``NULL``).
By default (when :c:member:`flags` is ``0``), members allow
both read and write access.
Use the :c:macro:`Py_READONLY` flag for read-only access.
Certain types, like :c:macro:`Py_T_STRING`, imply :c:macro:`Py_READONLY`.
Only :c:macro:`Py_T_OBJECT_EX` (and legacy :c:macro:`T_OBJECT`) members can
be deleted.
.. _pymemberdef-offsets:
Heap allocated types (created using :c:func:`PyType_FromSpec` or similar),
``PyMemberDef`` may contain definitions for the special member
``__vectorcalloffset__``, corresponding to
For heap-allocated types (created using :c:func:`PyType_FromSpec` or similar),
``PyMemberDef`` may contain a definition for the special member
``"__vectorcalloffset__"``, corresponding to
: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 ``Py_T_PYSSIZET`` and ``Py_READONLY``, for example::
static PyMemberDef spam_type_members[] = {
{"__vectorcalloffset__", T_PYSSIZET, offsetof(Spam_object, vectorcall), READONLY},
{"__vectorcalloffset__", Py_T_PYSSIZET,
offsetof(Spam_object, vectorcall), Py_READONLY},
{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.
(You may need to ``#include <stddef.h>`` for :c:func:`!offsetof`.)
The legacy offsets :c:member:`~PyTypeObject.tp_dictoffset` and
:c:member:`~PyTypeObject.tp_weaklistoffset` can be defined similarly using
``"__dictoffset__"`` and ``"__weaklistoffset__"`` members, but extensions
are strongly encouraged to use :const:`Py_TPFLAGS_MANAGED_DICT` and
:const:`Py_TPFLAGS_MANAGED_WEAKREF` instead.
.. versionchanged:: 3.12
``PyMemberDef`` is always available.
Previously, it required including ``"structmember.h"``.
.. c:function:: PyObject* PyMember_GetOne(const char *obj_addr, struct PyMemberDef *m)
@ -472,6 +453,10 @@ Accessing attributes of extension types
attribute is described by ``PyMemberDef`` *m*. Returns ``NULL``
on error.
.. versionchanged:: 3.12
``PyMember_GetOne`` is always available.
Previously, it required including ``"structmember.h"``.
.. c:function:: int PyMember_SetOne(char *obj_addr, struct PyMemberDef *m, PyObject *o)
@ -479,6 +464,144 @@ Accessing attributes of extension types
The attribute to set is described by ``PyMemberDef`` *m*. Returns ``0``
if successful and a negative value on failure.
.. versionchanged:: 3.12
``PyMember_SetOne`` is always available.
Previously, it required including ``"structmember.h"``.
.. _PyMemberDef-flags:
Member flags
^^^^^^^^^^^^
The following flags can be used with :c:member:`PyMemberDef.flags`:
.. c:macro:: Py_READONLY
Not writable.
.. c:macro:: Py_AUDIT_READ
Emit an ``object.__getattr__`` :ref:`audit event <audit-events>`
before reading.
.. index::
single: READ_RESTRICTED
single: WRITE_RESTRICTED
single: RESTRICTED
.. versionchanged:: 3.10
The :const:`!RESTRICTED`, :const:`!READ_RESTRICTED` and
:const:`!WRITE_RESTRICTED` macros available with
``#include "structmember.h"`` are deprecated.
:const:`!READ_RESTRICTED` and :const:`!RESTRICTED` are equivalent to
:const:`Py_AUDIT_READ`; :const:`!WRITE_RESTRICTED` does nothing.
.. index::
single: READONLY
.. versionchanged:: 3.12
The :const:`!READONLY` macro was renamed to :const:`Py_READONLY`.
The :const:`!PY_AUDIT_READ` macro was renamed with the ``Py_`` prefix.
The new names are now always available.
Previously, these required ``#include "structmember.h"``.
The header is still available and it provides the old names.
.. _PyMemberDef-types:
Member types
^^^^^^^^^^^^
:c:member:`PyMemberDef.type` can be one of the following macros corresponding
to various C types.
When the member is accessed in Python, it will be converted to the
equivalent Python type.
When it is set from Python, it will be converted back to the C type.
If that is not possible, an exception such as :exc:`TypeError` or
:exc:`ValueError` is raised.
Unless marked (D), attributes defined this way cannot be deleted
using e.g. :keyword:`del` or :py:func:`delattr`.
================================ ============================= ======================
Macro name C type Python type
================================ ============================= ======================
.. c:macro:: Py_T_BYTE :c:expr:`char` :py:class:`int`
.. c:macro:: Py_T_SHORT :c:expr:`short` :py:class:`int`
.. c:macro:: Py_T_INT :c:expr:`int` :py:class:`int`
.. c:macro:: Py_T_LONG :c:expr:`long` :py:class:`int`
.. c:macro:: Py_T_LONGLONG :c:expr:`long long` :py:class:`int`
.. c:macro:: Py_T_UBYTE :c:expr:`unsigned char` :py:class:`int`
.. c:macro:: Py_T_UINT :c:expr:`unsigned int` :py:class:`int`
.. c:macro:: Py_T_USHORT :c:expr:`unsigned short` :py:class:`int`
.. c:macro:: Py_T_ULONG :c:expr:`unsigned long` :py:class:`int`
.. c:macro:: Py_T_ULONGLONG :c:expr:`unsigned long long` :py:class:`int`
.. c:macro:: Py_T_PYSSIZET :c:expr:`Py_ssize_t` :py:class:`int`
.. c:macro:: Py_T_FLOAT :c:expr:`float` :py:class:`float`
.. c:macro:: Py_T_DOUBLE :c:expr:`double` :py:class:`float`
.. c:macro:: Py_T_BOOL :c:expr:`char` :py:class:`bool`
(written as 0 or 1)
.. c:macro:: Py_T_STRING :c:expr:`const char *` (*) :py:class:`str` (RO)
.. c:macro:: Py_T_STRING_INPLACE :c:expr:`const char[]` (*) :py:class:`str` (RO)
.. c:macro:: Py_T_CHAR :c:expr:`char` (0-127) :py:class:`str` (**)
.. c:macro:: Py_T_OBJECT_EX :c:expr:`PyObject *` :py:class:`object` (D)
================================ ============================= ======================
(*): Zero-terminated, UTF8-encoded C string.
With :c:macro:`!Py_T_STRING` the C representation is a pointer;
with :c:macro:`!Py_T_STRING_INLINE` the string is stored directly
in the structure.
(**): String of length 1. Only ASCII is accepted.
(RO): Implies :c:macro:`Py_READONLY`.
(D): Can be deleted, in which case the pointer is set to ``NULL``.
Reading a ``NULL`` pointer raises :py:exc:`AttributeError`.
.. index::
single: T_BYTE
single: T_SHORT
single: T_INT
single: T_LONG
single: T_LONGLONG
single: T_UBYTE
single: T_USHORT
single: T_UINT
single: T_ULONG
single: T_ULONGULONG
single: T_PYSSIZET
single: T_FLOAT
single: T_DOUBLE
single: T_BOOL
single: T_CHAR
single: T_STRING
single: T_STRING_INPLACE
single: T_OBJECT_EX
single: structmember.h
.. versionadded:: 3.12
In previous versions, the macros were only available with
``#include "structmember.h"`` and were named without the ``Py_`` prefix
(e.g. as ``T_INT``).
The header is still available and contains the old names, along with
the following deprecated types:
.. c:macro:: T_OBJECT
Like ``Py_T_OBJECT_EX``, but ``NULL`` is converted to ``None``.
This results in surprising behavior in Python: deleting the attribute
effectively sets it to ``None``.
.. c:macro:: T_NONE
Always ``None``. Must be used with :c:macro:`Py_READONLY`.
Defining Getters and Setters
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. c:type:: PyGetSetDef

View File

@ -386,6 +386,8 @@ function,PyMem_Malloc,3.2,,
function,PyMem_Realloc,3.2,,
type,PyMemberDef,3.2,,full-abi
var,PyMemberDescr_Type,3.2,,
function,PyMember_GetOne,3.2,,
function,PyMember_SetOne,3.2,,
function,PyMemoryView_FromBuffer,3.11,,
function,PyMemoryView_FromMemory,3.7,,
function,PyMemoryView_FromObject,3.2,,

View File

@ -286,36 +286,11 @@ be read-only or read-write. The structures in the table are defined as::
For each entry in the table, a :term:`descriptor` will be constructed and added to the
type which will be able to extract a value from the instance structure. The
:attr:`type` field should contain one of the type codes defined in the
:file:`structmember.h` header; the value will be used to determine how to
:attr:`type` field should contain a type code like :c:macro:`Py_T_INT` or
:c:macro:`Py_T_DOUBLE`; the value will be used to determine how to
convert Python values to and from C values. The :attr:`flags` field is used to
store flags which control how the attribute can be accessed.
The following flag constants are defined in :file:`structmember.h`; they may be
combined using bitwise-OR.
+---------------------------+----------------------------------------------+
| Constant | Meaning |
+===========================+==============================================+
| :const:`READONLY` | Never writable. |
+---------------------------+----------------------------------------------+
| :const:`PY_AUDIT_READ` | Emit an ``object.__getattr__`` |
| | :ref:`audit events <audit-events>` before |
| | reading. |
+---------------------------+----------------------------------------------+
.. versionchanged:: 3.10
:const:`RESTRICTED`, :const:`READ_RESTRICTED` and :const:`WRITE_RESTRICTED`
are deprecated. However, :const:`READ_RESTRICTED` is an alias for
:const:`PY_AUDIT_READ`, so fields that specify either :const:`RESTRICTED`
or :const:`READ_RESTRICTED` will also raise an audit event.
.. index::
single: READONLY
single: READ_RESTRICTED
single: WRITE_RESTRICTED
single: RESTRICTED
single: PY_AUDIT_READ
store flags which control how the attribute can be accessed: you can set it to
:c:macro:`Py_READONLY` to prevent Python code from setting it.
An interesting advantage of using the :c:member:`~PyTypeObject.tp_members` table to build
descriptors that are used at runtime is that any attribute defined this way can

View File

@ -239,13 +239,6 @@ adds these capabilities:
This version of the module has a number of changes.
We've added an extra include::
#include <structmember.h>
This include provides declarations that we use to handle attributes, as
described a bit later.
The :class:`Custom` type now has three data attributes in its C struct,
*first*, *last*, and *number*. The *first* and *last* variables are Python
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
number of ways to do that. The simplest way is to define member definitions::
static PyMemberDef Custom_members[] = {
{"first", T_OBJECT_EX, offsetof(CustomObject, first), 0,
{"first", Py_T_OBJECT_EX, offsetof(CustomObject, first), 0,
"first name"},
{"last", T_OBJECT_EX, offsetof(CustomObject, last), 0,
{"last", Py_T_OBJECT_EX, offsetof(CustomObject, last), 0,
"last name"},
{"number", T_INT, offsetof(CustomObject, number), 0,
{"number", Py_T_INT, offsetof(CustomObject, number), 0,
"custom number"},
{NULL} /* Sentinel */
};
@ -609,7 +602,7 @@ above. In this case, we aren't using a closure, so we just pass ``NULL``.
We also remove the member definitions for these attributes::
static PyMemberDef Custom_members[] = {
{"number", T_INT, offsetof(CustomObject, number), 0,
{"number", Py_T_INT, offsetof(CustomObject, number), 0,
"custom number"},
{NULL} /* Sentinel */
};

View File

@ -1,6 +1,6 @@
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "structmember.h"
#include <stddef.h> /* for offsetof() */
typedef struct {
PyObject_HEAD
@ -63,11 +63,11 @@ Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
}
static PyMemberDef Custom_members[] = {
{"first", T_OBJECT_EX, offsetof(CustomObject, first), 0,
{"first", Py_T_OBJECT_EX, offsetof(CustomObject, first), 0,
"first name"},
{"last", T_OBJECT_EX, offsetof(CustomObject, last), 0,
{"last", Py_T_OBJECT_EX, offsetof(CustomObject, last), 0,
"last name"},
{"number", T_INT, offsetof(CustomObject, number), 0,
{"number", Py_T_INT, offsetof(CustomObject, number), 0,
"custom number"},
{NULL} /* Sentinel */
};

View File

@ -1,6 +1,6 @@
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "structmember.h"
#include <stddef.h> /* for offsetof() */
typedef struct {
PyObject_HEAD
@ -63,7 +63,7 @@ Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
}
static PyMemberDef Custom_members[] = {
{"number", T_INT, offsetof(CustomObject, number), 0,
{"number", Py_T_INT, offsetof(CustomObject, number), 0,
"custom number"},
{NULL} /* Sentinel */
};

View File

@ -1,6 +1,6 @@
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "structmember.h"
#include <stddef.h> /* for offsetof() */
typedef struct {
PyObject_HEAD
@ -79,7 +79,7 @@ Custom_init(CustomObject *self, PyObject *args, PyObject *kwds)
}
static PyMemberDef Custom_members[] = {
{"number", T_INT, offsetof(CustomObject, number), 0,
{"number", Py_T_INT, offsetof(CustomObject, number), 0,
"custom number"},
{NULL} /* Sentinel */
};

View File

@ -849,6 +849,37 @@ Deprecated
* Creating :c:data:`immutable types <Py_TPFLAGS_IMMUTABLETYPE>` with mutable
bases is deprecated and will be disabled in Python 3.14.
* 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:
- :c:struct:`PyMemberDef`, :c:func:`PyMember_GetOne` and
:c:func:`PyMember_SetOne`
- Type macros like :c:macro:`Py_T_INT`, :c:macro:`Py_T_DOUBLE`, etc.
(previously ``T_INT``, ``T_DOUBLE``, etc.)
- The flags :c:macro:`Py_READONLY` (previously ``READONLY``) and
:c:macro:`Py_AUDIT_READ` (previously all uppercase)
Several items are not exposed from ``Python.h``:
- :c:macro:`T_OBJECT` (use :c:macro:`Py_T_OBJECT_EX`)
- :c:macro:`T_NONE` (previously undocumented, and pretty quirky)
- The macro ``WRITE_RESTRICTED`` which does nothing.
- The macros ``RESTRICTED`` and ``READ_RESTRICTED``, equivalents of
:c:macro:`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.
(Contributed in :gh:`47146` by Petr Viktorin, based on
earlier work by Alexander Belopolsky and Matthias Braun.)
Removed
-------

View File

@ -32,6 +32,61 @@ PyAPI_FUNC(PyObject *) PyDescr_NewGetSet(PyTypeObject *, PyGetSetDef *);
PyAPI_FUNC(PyObject *) PyDictProxy_New(PyObject *);
PyAPI_FUNC(PyObject *) PyWrapper_New(PyObject *, PyObject *);
/* An array of PyMemberDef structures defines the name, type and offset
of selected members of a C structure. These can be read by
PyMember_GetOne() and set by PyMember_SetOne() (except if their READONLY
flag is set). The array must be terminated with an entry whose name
pointer is NULL. */
struct PyMemberDef {
const char *name;
int type;
Py_ssize_t offset;
int flags;
const char *doc;
};
// These constants used to be in structmember.h, not prefixed by Py_.
// (structmember.h now has aliases to the new names.)
/* Types */
#define Py_T_SHORT 0
#define Py_T_INT 1
#define Py_T_LONG 2
#define Py_T_FLOAT 3
#define Py_T_DOUBLE 4
#define Py_T_STRING 5
#define _Py_T_OBJECT 6 // Deprecated, use Py_T_OBJECT_EX instead
/* the ordering here is weird for binary compatibility */
#define Py_T_CHAR 7 /* 1-character string */
#define Py_T_BYTE 8 /* 8-bit signed int */
/* unsigned variants: */
#define Py_T_UBYTE 9
#define Py_T_USHORT 10
#define Py_T_UINT 11
#define Py_T_ULONG 12
/* Added by Jack: strings contained in the structure */
#define Py_T_STRING_INPLACE 13
/* Added by Lillo: bools contained in the structure (assumed char) */
#define Py_T_BOOL 14
#define Py_T_OBJECT_EX 16
#define Py_T_LONGLONG 17
#define Py_T_ULONGLONG 18
#define Py_T_PYSSIZET 19 /* Py_ssize_t */
#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.
PyAPI_FUNC(PyObject *) PyMember_GetOne(const char *, PyMemberDef *);
PyAPI_FUNC(int) PyMember_SetOne(char *, PyMemberDef *, PyObject *);
#ifndef Py_LIMITED_API
# define Py_CPYTHON_DESCROBJECT_H
# include "cpython/descrobject.h"

View File

@ -5,69 +5,50 @@ extern "C" {
#endif
/* Interface to map C struct members to Python object attributes */
/* Interface to map C struct members to Python object attributes
*
* This header is deprecated: new code should not use stuff from here.
* New definitions are in descrobject.h.
*
* However, there's nothing wrong with old code continuing to use it,
* and there's not much mainenance overhead in maintaining a few aliases.
* So, don't be too eager to convert old code.
*
* It uses names not prefixed with Py_.
* It is also *not* included from Python.h and must be included individually.
*/
#include <stddef.h> /* For offsetof */
/* An array of PyMemberDef structures defines the name, type and offset
of selected members of a C structure. These can be read by
PyMember_GetOne() and set by PyMember_SetOne() (except if their READONLY
flag is set). The array must be terminated with an entry whose name
pointer is NULL. */
struct PyMemberDef {
const char *name;
int type;
Py_ssize_t offset;
int flags;
const char *doc;
};
#include <stddef.h> /* For offsetof (not always provided by Python.h) */
/* Types */
#define T_SHORT 0
#define T_INT 1
#define T_LONG 2
#define T_FLOAT 3
#define T_DOUBLE 4
#define T_STRING 5
#define T_OBJECT 6
/* XXX the ordering here is weird for binary compatibility */
#define T_CHAR 7 /* 1-character string */
#define T_BYTE 8 /* 8-bit signed int */
/* unsigned variants: */
#define T_UBYTE 9
#define T_USHORT 10
#define T_UINT 11
#define T_ULONG 12
/* Added by Jack: strings contained in the structure */
#define T_STRING_INPLACE 13
/* Added by Lillo: bools contained in the structure (assumed char) */
#define T_BOOL 14
#define T_OBJECT_EX 16 /* Like T_OBJECT, but raises AttributeError
when the value is NULL, instead of
converting to None. */
#define T_LONGLONG 17
#define T_ULONGLONG 18
#define T_PYSSIZET 19 /* Py_ssize_t */
#define T_NONE 20 /* Value is always None */
#define T_SHORT Py_T_SHORT
#define T_INT Py_T_INT
#define T_LONG Py_T_LONG
#define T_FLOAT Py_T_FLOAT
#define T_DOUBLE Py_T_DOUBLE
#define T_STRING Py_T_STRING
#define T_OBJECT _Py_T_OBJECT
#define T_CHAR Py_T_CHAR
#define T_BYTE Py_T_BYTE
#define T_UBYTE Py_T_UBYTE
#define T_USHORT Py_T_USHORT
#define T_UINT Py_T_UINT
#define T_ULONG Py_T_ULONG
#define T_STRING_INPLACE Py_T_STRING_INPLACE
#define T_BOOL Py_T_BOOL
#define T_OBJECT_EX Py_T_OBJECT_EX
#define T_LONGLONG Py_T_LONGLONG
#define T_ULONGLONG Py_T_ULONGLONG
#define T_PYSSIZET Py_T_PYSSIZET
#define T_NONE _Py_T_NONE
/* Flags */
#define READONLY 1
#define READ_RESTRICTED 2
#define PY_WRITE_RESTRICTED 4
#define READONLY Py_READONLY
#define PY_AUDIT_READ Py_AUDIT_READ
#define READ_RESTRICTED Py_AUDIT_READ
#define PY_WRITE_RESTRICTED _Py_WRITE_RESTRICTED
#define RESTRICTED (READ_RESTRICTED | PY_WRITE_RESTRICTED)
#define PY_AUDIT_READ READ_RESTRICTED
/* Current API, use this */
PyAPI_FUNC(PyObject *) PyMember_GetOne(const char *, PyMemberDef *);
PyAPI_FUNC(int) PyMember_SetOne(char *, PyMemberDef *, PyObject *);
#ifdef __cplusplus
}

View File

@ -4,15 +4,21 @@ from test.support import warnings_helper
# Skip this test if the _testcapi module isn't available.
import_helper.import_module('_testcapi')
from _testcapi import _test_structmembersType, \
CHAR_MAX, CHAR_MIN, UCHAR_MAX, \
SHRT_MAX, SHRT_MIN, USHRT_MAX, \
INT_MAX, INT_MIN, UINT_MAX, \
LONG_MAX, LONG_MIN, ULONG_MAX, \
LLONG_MAX, LLONG_MIN, ULLONG_MAX, \
PY_SSIZE_T_MAX, PY_SSIZE_T_MIN
from _testcapi import (_test_structmembersType_OldAPI,
_test_structmembersType_NewAPI,
CHAR_MAX, CHAR_MIN, UCHAR_MAX,
SHRT_MAX, SHRT_MIN, USHRT_MAX,
INT_MAX, INT_MIN, UINT_MAX,
LONG_MAX, LONG_MIN, ULONG_MAX,
LLONG_MAX, LLONG_MIN, ULLONG_MAX,
PY_SSIZE_T_MAX, PY_SSIZE_T_MIN,
)
ts=_test_structmembersType(False, # T_BOOL
# There are two classes: one using <structmember.h> and another using
# `Py_`-prefixed API. They should behave the same in Python
def _make_test_object(cls):
return cls(False, # T_BOOL
1, # T_BYTE
2, # T_UBYTE
3, # T_SHORT
@ -24,12 +30,16 @@ ts=_test_structmembersType(False, # T_BOOL
23, # T_PYSSIZET
9.99999,# T_FLOAT
10.1010101010, # T_DOUBLE
"hi" # T_STRING_INPLACE
"hi", # T_STRING_INPLACE
)
class ReadWriteTests(unittest.TestCase):
class ReadWriteTests:
def setUp(self):
self.ts = _make_test_object(self.cls)
def test_bool(self):
ts = self.ts
ts.T_BOOL = True
self.assertEqual(ts.T_BOOL, True)
ts.T_BOOL = False
@ -37,6 +47,7 @@ class ReadWriteTests(unittest.TestCase):
self.assertRaises(TypeError, setattr, ts, 'T_BOOL', 1)
def test_byte(self):
ts = self.ts
ts.T_BYTE = CHAR_MAX
self.assertEqual(ts.T_BYTE, CHAR_MAX)
ts.T_BYTE = CHAR_MIN
@ -45,6 +56,7 @@ class ReadWriteTests(unittest.TestCase):
self.assertEqual(ts.T_UBYTE, UCHAR_MAX)
def test_short(self):
ts = self.ts
ts.T_SHORT = SHRT_MAX
self.assertEqual(ts.T_SHORT, SHRT_MAX)
ts.T_SHORT = SHRT_MIN
@ -53,6 +65,7 @@ class ReadWriteTests(unittest.TestCase):
self.assertEqual(ts.T_USHORT, USHRT_MAX)
def test_int(self):
ts = self.ts
ts.T_INT = INT_MAX
self.assertEqual(ts.T_INT, INT_MAX)
ts.T_INT = INT_MIN
@ -61,6 +74,7 @@ class ReadWriteTests(unittest.TestCase):
self.assertEqual(ts.T_UINT, UINT_MAX)
def test_long(self):
ts = self.ts
ts.T_LONG = LONG_MAX
self.assertEqual(ts.T_LONG, LONG_MAX)
ts.T_LONG = LONG_MIN
@ -69,13 +83,17 @@ class ReadWriteTests(unittest.TestCase):
self.assertEqual(ts.T_ULONG, ULONG_MAX)
def test_py_ssize_t(self):
ts = self.ts
ts.T_PYSSIZET = PY_SSIZE_T_MAX
self.assertEqual(ts.T_PYSSIZET, PY_SSIZE_T_MAX)
ts.T_PYSSIZET = PY_SSIZE_T_MIN
self.assertEqual(ts.T_PYSSIZET, PY_SSIZE_T_MIN)
@unittest.skipUnless(hasattr(ts, "T_LONGLONG"), "long long not present")
def test_longlong(self):
ts = self.ts
if not hasattr(ts, "T_LONGLONG"):
self.skipTest("long long not present")
ts.T_LONGLONG = LLONG_MAX
self.assertEqual(ts.T_LONGLONG, LLONG_MAX)
ts.T_LONGLONG = LLONG_MIN
@ -91,6 +109,7 @@ class ReadWriteTests(unittest.TestCase):
self.assertEqual(ts.T_ULONGLONG, 4)
def test_bad_assignments(self):
ts = self.ts
integer_attributes = [
'T_BOOL',
'T_BYTE', 'T_UBYTE',
@ -109,37 +128,57 @@ class ReadWriteTests(unittest.TestCase):
self.assertRaises(TypeError, setattr, ts, attr, nonint)
def test_inplace_string(self):
ts = self.ts
self.assertEqual(ts.T_STRING_INPLACE, "hi")
self.assertRaises(TypeError, setattr, ts, "T_STRING_INPLACE", "s")
self.assertRaises(TypeError, delattr, ts, "T_STRING_INPLACE")
class ReadWriteTests_OldAPI(ReadWriteTests, unittest.TestCase):
cls = _test_structmembersType_OldAPI
class TestWarnings(unittest.TestCase):
class ReadWriteTests_NewAPI(ReadWriteTests, unittest.TestCase):
cls = _test_structmembersType_NewAPI
class TestWarnings:
def setUp(self):
self.ts = _make_test_object(self.cls)
def test_byte_max(self):
ts = self.ts
with warnings_helper.check_warnings(('', RuntimeWarning)):
ts.T_BYTE = CHAR_MAX+1
def test_byte_min(self):
ts = self.ts
with warnings_helper.check_warnings(('', RuntimeWarning)):
ts.T_BYTE = CHAR_MIN-1
def test_ubyte_max(self):
ts = self.ts
with warnings_helper.check_warnings(('', RuntimeWarning)):
ts.T_UBYTE = UCHAR_MAX+1
def test_short_max(self):
ts = self.ts
with warnings_helper.check_warnings(('', RuntimeWarning)):
ts.T_SHORT = SHRT_MAX+1
def test_short_min(self):
ts = self.ts
with warnings_helper.check_warnings(('', RuntimeWarning)):
ts.T_SHORT = SHRT_MIN-1
def test_ushort_max(self):
ts = self.ts
with warnings_helper.check_warnings(('', RuntimeWarning)):
ts.T_USHORT = USHRT_MAX+1
class TestWarnings_OldAPI(TestWarnings, unittest.TestCase):
cls = _test_structmembersType_OldAPI
class TestWarnings_NewAPI(TestWarnings, unittest.TestCase):
cls = _test_structmembersType_NewAPI
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,5 @@
The ``structmember.h`` header is deprecated. Its non-deprecated contents are
now available just by including ``Python.h``, with a ``Py_`` prefix added if
it was missing. (Deprecated contents are :c:macro:`T_OBJECT`,
:c:macro:`T_NONE`, and no-op flags.) Patch by Petr Viktorin, based on
earlier work by Alexander Belopolsky and Matthias Braun.

View File

@ -94,7 +94,7 @@
added = '3.2'
struct_abi_kind = 'full-abi'
[struct.PyMemberDef]
added = '3.2'
added = '3.2' # Before 3.12, PyMemberDef required #include "structmember.h"
struct_abi_kind = 'full-abi'
[struct.PyGetSetDef]
added = '3.2'
@ -1777,11 +1777,9 @@
added = '3.2'
abi_only = true
[function.PyMember_GetOne]
added = '3.2'
abi_only = true
added = '3.2' # Before 3.12, available in "structmember.h"
[function.PyMember_SetOne]
added = '3.2'
abi_only = true
added = '3.2' # Before 3.12, available in "structmember.h"
# TLS api is deprecated; superseded by TSS API
@ -2303,3 +2301,44 @@
added = '3.12'
[typedef.releasebufferproc]
added = '3.12'
[const.Py_T_BYTE]
added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix
[const.Py_T_SHORT]
added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix
[const.Py_T_INT]
added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix
[const.Py_T_LONG]
added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix
[const.Py_T_LONGLONG]
added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix
[const.Py_T_UBYTE]
added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix
[const.Py_T_UINT]
added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix
[const.Py_T_USHORT]
added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix
[const.Py_T_ULONG]
added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix
[const.Py_T_ULONGLONG]
added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix
[const.Py_T_PYSSIZET]
added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix
[const.Py_T_FLOAT]
added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix
[const.Py_T_DOUBLE]
added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix
[const.Py_T_BOOL]
added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix
[const.Py_T_STRING]
added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix
[const.Py_T_STRING_INPLACE]
added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix
[const.Py_T_CHAR]
added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix
[const.Py_T_OBJECT_EX]
added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix
[const.Py_READONLY]
added = '3.12' # Before 3.12, available in "structmember.h" w/o Py_ prefix
[const.Py_AUDIT_READ]
added = '3.12' # Before 3.12, available in "structmember.h"

View File

@ -169,7 +169,7 @@
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
# Some testing modules MUST be built as shared libraries.

View File

@ -35,6 +35,7 @@ int _PyTestCapi_Init_Mem(PyObject *module);
int _PyTestCapi_Init_Watchers(PyObject *module);
int _PyTestCapi_Init_Long(PyObject *module);
int _PyTestCapi_Init_Float(PyObject *module);
int _PyTestCapi_Init_Structmember(PyObject *module);
#ifdef LIMITED_API_AVAILABLE
int _PyTestCapi_Init_VectorcallLimited(PyObject *module);

View File

@ -0,0 +1,217 @@
#define PY_SSIZE_T_CLEAN
#include "parts.h"
#include <stddef.h> // for offsetof()
// This defines two classes that contain all the simple member types, one
// using "new" Py_-prefixed API, and the other using "old" <structmember.h>.
// They should behave identically in Python.
typedef struct {
char bool_member;
char byte_member;
unsigned char ubyte_member;
short short_member;
unsigned short ushort_member;
int int_member;
unsigned int uint_member;
long long_member;
unsigned long ulong_member;
Py_ssize_t pyssizet_member;
float float_member;
double double_member;
char inplace_member[6];
long long longlong_member;
unsigned long long ulonglong_member;
} all_structmembers;
typedef struct {
PyObject_HEAD
all_structmembers structmembers;
} test_structmembers;
static struct PyMemberDef test_members_newapi[] = {
{"T_BOOL", Py_T_BOOL, offsetof(test_structmembers, structmembers.bool_member), 0, NULL},
{"T_BYTE", Py_T_BYTE, offsetof(test_structmembers, structmembers.byte_member), 0, NULL},
{"T_UBYTE", Py_T_UBYTE, offsetof(test_structmembers, structmembers.ubyte_member), 0, NULL},
{"T_SHORT", Py_T_SHORT, offsetof(test_structmembers, structmembers.short_member), 0, NULL},
{"T_USHORT", Py_T_USHORT, offsetof(test_structmembers, structmembers.ushort_member), 0, NULL},
{"T_INT", Py_T_INT, offsetof(test_structmembers, structmembers.int_member), 0, NULL},
{"T_UINT", Py_T_UINT, offsetof(test_structmembers, structmembers.uint_member), 0, NULL},
{"T_LONG", Py_T_LONG, offsetof(test_structmembers, structmembers.long_member), 0, NULL},
{"T_ULONG", Py_T_ULONG, offsetof(test_structmembers, structmembers.ulong_member), 0, NULL},
{"T_PYSSIZET", Py_T_PYSSIZET, offsetof(test_structmembers, structmembers.pyssizet_member), 0, NULL},
{"T_FLOAT", Py_T_FLOAT, offsetof(test_structmembers, structmembers.float_member), 0, NULL},
{"T_DOUBLE", Py_T_DOUBLE, offsetof(test_structmembers, structmembers.double_member), 0, NULL},
{"T_STRING_INPLACE", Py_T_STRING_INPLACE, offsetof(test_structmembers, structmembers.inplace_member), 0, NULL},
{"T_LONGLONG", Py_T_LONGLONG, offsetof(test_structmembers, structmembers.longlong_member), 0, NULL},
{"T_ULONGLONG", Py_T_ULONGLONG, offsetof(test_structmembers, structmembers.ulonglong_member), 0, NULL},
{NULL}
};
static PyObject *
test_structmembers_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
static char *keywords[] = {
"T_BOOL", "T_BYTE", "T_UBYTE", "T_SHORT", "T_USHORT",
"T_INT", "T_UINT", "T_LONG", "T_ULONG", "T_PYSSIZET",
"T_FLOAT", "T_DOUBLE", "T_STRING_INPLACE",
"T_LONGLONG", "T_ULONGLONG",
NULL};
static const char fmt[] = "|bbBhHiIlknfds#LK";
test_structmembers *ob;
const char *s = NULL;
Py_ssize_t string_len = 0;
ob = PyObject_New(test_structmembers, type);
if (ob == NULL) {
return NULL;
}
memset(&ob->structmembers, 0, sizeof(all_structmembers));
if (!PyArg_ParseTupleAndKeywords(args, kwargs, fmt, keywords,
&ob->structmembers.bool_member,
&ob->structmembers.byte_member,
&ob->structmembers.ubyte_member,
&ob->structmembers.short_member,
&ob->structmembers.ushort_member,
&ob->structmembers.int_member,
&ob->structmembers.uint_member,
&ob->structmembers.long_member,
&ob->structmembers.ulong_member,
&ob->structmembers.pyssizet_member,
&ob->structmembers.float_member,
&ob->structmembers.double_member,
&s, &string_len,
&ob->structmembers.longlong_member,
&ob->structmembers.ulonglong_member))
{
Py_DECREF(ob);
return NULL;
}
if (s != NULL) {
if (string_len > 5) {
Py_DECREF(ob);
PyErr_SetString(PyExc_ValueError, "string too long");
return NULL;
}
strcpy(ob->structmembers.inplace_member, s);
}
else {
strcpy(ob->structmembers.inplace_member, "");
}
return (PyObject *)ob;
}
static PyType_Slot test_structmembers_slots[] = {
{Py_tp_new, test_structmembers_new},
{Py_tp_members, test_members_newapi},
{0},
};
static PyType_Spec test_structmembers_spec = {
.name = "_testcapi._test_structmembersType_NewAPI",
.flags = Py_TPFLAGS_DEFAULT,
.basicsize = sizeof(test_structmembers),
.slots = test_structmembers_slots,
};
#include <structmember.h>
static struct PyMemberDef test_members[] = {
{"T_BOOL", T_BOOL, offsetof(test_structmembers, structmembers.bool_member), 0, NULL},
{"T_BYTE", T_BYTE, offsetof(test_structmembers, structmembers.byte_member), 0, NULL},
{"T_UBYTE", T_UBYTE, offsetof(test_structmembers, structmembers.ubyte_member), 0, NULL},
{"T_SHORT", T_SHORT, offsetof(test_structmembers, structmembers.short_member), 0, NULL},
{"T_USHORT", T_USHORT, offsetof(test_structmembers, structmembers.ushort_member), 0, NULL},
{"T_INT", T_INT, offsetof(test_structmembers, structmembers.int_member), 0, NULL},
{"T_UINT", T_UINT, offsetof(test_structmembers, structmembers.uint_member), 0, NULL},
{"T_LONG", T_LONG, offsetof(test_structmembers, structmembers.long_member), 0, NULL},
{"T_ULONG", T_ULONG, offsetof(test_structmembers, structmembers.ulong_member), 0, NULL},
{"T_PYSSIZET", T_PYSSIZET, offsetof(test_structmembers, structmembers.pyssizet_member), 0, NULL},
{"T_FLOAT", T_FLOAT, offsetof(test_structmembers, structmembers.float_member), 0, NULL},
{"T_DOUBLE", T_DOUBLE, offsetof(test_structmembers, structmembers.double_member), 0, NULL},
{"T_STRING_INPLACE", T_STRING_INPLACE, offsetof(test_structmembers, structmembers.inplace_member), 0, NULL},
{"T_LONGLONG", T_LONGLONG, offsetof(test_structmembers, structmembers.longlong_member), 0, NULL},
{"T_ULONGLONG", T_ULONGLONG, offsetof(test_structmembers, structmembers.ulonglong_member), 0, NULL},
{NULL}
};
static void
test_structmembers_free(PyObject *ob)
{
PyObject_Free(ob);
}
/* Designated initializers would work too, but this does test the *old* API */
static PyTypeObject test_structmembersType_OldAPI= {
PyVarObject_HEAD_INIT(NULL, 0)
"test_structmembersType_OldAPI",
sizeof(test_structmembers), /* tp_basicsize */
0, /* tp_itemsize */
test_structmembers_free, /* destructor tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
PyObject_GenericSetAttr, /* tp_setattro */
0, /* tp_as_buffer */
0, /* tp_flags */
"Type containing all structmember types",
0, /* traverseproc tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
test_members, /* tp_members */
0,
0,
0,
0,
0,
0,
0,
0,
test_structmembers_new, /* tp_new */
};
int
_PyTestCapi_Init_Structmember(PyObject *m)
{
int res;
res = PyType_Ready(&test_structmembersType_OldAPI);
if (res < 0) {
return -1;
}
res = PyModule_AddObject(
m,
"_test_structmembersType_OldAPI",
(PyObject *)&test_structmembersType_OldAPI);
if (res < 0) {
return -1;
}
PyObject *test_structmembersType_NewAPI = PyType_FromModuleAndSpec(
m, &test_structmembers_spec, NULL);
if (!test_structmembersType_NewAPI) {
return -1;
}
res = PyModule_AddType(m, (PyTypeObject*)test_structmembersType_NewAPI);
Py_DECREF(test_structmembersType_NewAPI);
if (res < 0) {
return -1;
}
return 0;
}

View File

@ -21,7 +21,7 @@
#include "Python.h"
#include "marshal.h" // PyMarshal_WriteLongToFile
#include "structmember.h" // PyMemberDef
#include "structmember.h" // for offsetof(), T_OBJECT
#include <float.h> // FLT_MAX
#include <signal.h>
@ -3371,147 +3371,6 @@ static PyMethodDef TestMethods[] = {
{NULL, NULL} /* sentinel */
};
typedef struct {
char bool_member;
char byte_member;
unsigned char ubyte_member;
short short_member;
unsigned short ushort_member;
int int_member;
unsigned int uint_member;
long long_member;
unsigned long ulong_member;
Py_ssize_t pyssizet_member;
float float_member;
double double_member;
char inplace_member[6];
long long longlong_member;
unsigned long long ulonglong_member;
} all_structmembers;
typedef struct {
PyObject_HEAD
all_structmembers structmembers;
} test_structmembers;
static struct PyMemberDef test_members[] = {
{"T_BOOL", T_BOOL, offsetof(test_structmembers, structmembers.bool_member), 0, NULL},
{"T_BYTE", T_BYTE, offsetof(test_structmembers, structmembers.byte_member), 0, NULL},
{"T_UBYTE", T_UBYTE, offsetof(test_structmembers, structmembers.ubyte_member), 0, NULL},
{"T_SHORT", T_SHORT, offsetof(test_structmembers, structmembers.short_member), 0, NULL},
{"T_USHORT", T_USHORT, offsetof(test_structmembers, structmembers.ushort_member), 0, NULL},
{"T_INT", T_INT, offsetof(test_structmembers, structmembers.int_member), 0, NULL},
{"T_UINT", T_UINT, offsetof(test_structmembers, structmembers.uint_member), 0, NULL},
{"T_LONG", T_LONG, offsetof(test_structmembers, structmembers.long_member), 0, NULL},
{"T_ULONG", T_ULONG, offsetof(test_structmembers, structmembers.ulong_member), 0, NULL},
{"T_PYSSIZET", T_PYSSIZET, offsetof(test_structmembers, structmembers.pyssizet_member), 0, NULL},
{"T_FLOAT", T_FLOAT, offsetof(test_structmembers, structmembers.float_member), 0, NULL},
{"T_DOUBLE", T_DOUBLE, offsetof(test_structmembers, structmembers.double_member), 0, NULL},
{"T_STRING_INPLACE", T_STRING_INPLACE, offsetof(test_structmembers, structmembers.inplace_member), 0, NULL},
{"T_LONGLONG", T_LONGLONG, offsetof(test_structmembers, structmembers.longlong_member), 0, NULL},
{"T_ULONGLONG", T_ULONGLONG, offsetof(test_structmembers, structmembers.ulonglong_member), 0, NULL},
{NULL}
};
static PyObject *
test_structmembers_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
static char *keywords[] = {
"T_BOOL", "T_BYTE", "T_UBYTE", "T_SHORT", "T_USHORT",
"T_INT", "T_UINT", "T_LONG", "T_ULONG", "T_PYSSIZET",
"T_FLOAT", "T_DOUBLE", "T_STRING_INPLACE",
"T_LONGLONG", "T_ULONGLONG",
NULL};
static const char fmt[] = "|bbBhHiIlknfds#LK";
test_structmembers *ob;
const char *s = NULL;
Py_ssize_t string_len = 0;
ob = PyObject_New(test_structmembers, type);
if (ob == NULL)
return NULL;
memset(&ob->structmembers, 0, sizeof(all_structmembers));
if (!PyArg_ParseTupleAndKeywords(args, kwargs, fmt, keywords,
&ob->structmembers.bool_member,
&ob->structmembers.byte_member,
&ob->structmembers.ubyte_member,
&ob->structmembers.short_member,
&ob->structmembers.ushort_member,
&ob->structmembers.int_member,
&ob->structmembers.uint_member,
&ob->structmembers.long_member,
&ob->structmembers.ulong_member,
&ob->structmembers.pyssizet_member,
&ob->structmembers.float_member,
&ob->structmembers.double_member,
&s, &string_len
, &ob->structmembers.longlong_member,
&ob->structmembers.ulonglong_member
)) {
Py_DECREF(ob);
return NULL;
}
if (s != NULL) {
if (string_len > 5) {
Py_DECREF(ob);
PyErr_SetString(PyExc_ValueError, "string too long");
return NULL;
}
strcpy(ob->structmembers.inplace_member, s);
}
else {
strcpy(ob->structmembers.inplace_member, "");
}
return (PyObject *)ob;
}
static void
test_structmembers_free(PyObject *ob)
{
PyObject_Free(ob);
}
static PyTypeObject test_structmembersType = {
PyVarObject_HEAD_INIT(NULL, 0)
"test_structmembersType",
sizeof(test_structmembers), /* tp_basicsize */
0, /* tp_itemsize */
test_structmembers_free, /* destructor tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
PyObject_GenericSetAttr, /* tp_setattro */
0, /* tp_as_buffer */
0, /* tp_flags */
"Type containing all structmember types",
0, /* traverseproc tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
test_members, /* tp_members */
0,
0,
0,
0,
0,
0,
0,
0,
test_structmembers_new, /* tp_new */
};
typedef struct {
PyObject_HEAD
@ -4064,11 +3923,6 @@ PyInit__testcapi(void)
Py_SET_TYPE(&_HashInheritanceTester_Type, &PyType_Type);
Py_SET_TYPE(&test_structmembersType, &PyType_Type);
Py_INCREF(&test_structmembersType);
/* don't use a name starting with "test", since we don't want
test_capi to automatically call this */
PyModule_AddObject(m, "_test_structmembersType", (PyObject *)&test_structmembersType);
if (PyType_Ready(&matmulType) < 0)
return NULL;
Py_INCREF(&matmulType);
@ -4197,6 +4051,9 @@ PyInit__testcapi(void)
if (_PyTestCapi_Init_Float(m) < 0) {
return NULL;
}
if (_PyTestCapi_Init_Structmember(m) < 0) {
return NULL;
}
#ifndef LIMITED_API_AVAILABLE
PyModule_AddObjectRef(m, "LIMITED_API_AVAILABLE", Py_False);

View File

@ -106,6 +106,7 @@
<ClCompile Include="..\Modules\_testcapi\watchers.c" />
<ClCompile Include="..\Modules\_testcapi\float.c" />
<ClCompile Include="..\Modules\_testcapi\long.c" />
<ClCompile Include="..\Modules\_testcapi\structmember.c" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\PC\python_nt.rc" />

View File

@ -48,6 +48,9 @@
<ClCompile Include="..\Modules\_testcapi\long.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\Modules\_testcapi\structmember.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\PC\python_nt.rc">