mirror of
https://github.com/python/cpython.git
synced 2026-05-06 04:37:33 -04:00
gh-149044: Implement PEP 820 – PySlot: Unified slot system for the C API (GH-149055)
Co-authored-by: Peter Bierma <zintensitydev@gmail.com>
This commit is contained in:
+2
-1
@@ -84,6 +84,7 @@ Include/internal/pycore_uop_ids.h generated
|
||||
Include/internal/pycore_uop_metadata.h generated
|
||||
Include/opcode.h generated
|
||||
Include/opcode_ids.h generated
|
||||
Include/slots_generated.h generated
|
||||
Include/token.h generated
|
||||
Lib/_opcode_metadata.py generated
|
||||
Lib/idlelib/help.html generated
|
||||
@@ -99,7 +100,6 @@ Lib/token.py generated
|
||||
Misc/sbom.spdx.json generated
|
||||
Modules/_testinternalcapi/test_cases.c.h generated
|
||||
Modules/_testinternalcapi/test_targets.h generated
|
||||
Objects/typeslots.inc generated
|
||||
PC/python3dll.c generated
|
||||
Parser/parser.c generated
|
||||
Parser/token.c generated
|
||||
@@ -110,6 +110,7 @@ Python/generated_cases.c.h generated
|
||||
Python/optimizer_cases.c.h generated
|
||||
Python/opcode_targets.h generated
|
||||
Python/record_functions.c.h generated
|
||||
Python/slots_generated.c generated
|
||||
Python/stdlib_module_names.h generated
|
||||
Tools/peg_generator/pegen/grammar_parser.py generated
|
||||
aclocal.m4 generated
|
||||
|
||||
@@ -38,7 +38,7 @@ Extension export hook
|
||||
|
||||
The export hook must be an exported function with the following signature:
|
||||
|
||||
.. c:function:: PyModuleDef_Slot *PyModExport_modulename(void)
|
||||
.. c:function:: PySlot *PyModExport_modulename(void)
|
||||
|
||||
For modules with ASCII-only names, the :ref:`export hook <extension-export-hook>`
|
||||
must be named :samp:`PyModExport_{<name>}`,
|
||||
@@ -57,7 +57,7 @@ Python's *punycode* encoding with hyphens replaced by underscores. In Python:
|
||||
suffix = b'U_' + name.encode('punycode').replace(b'-', b'_')
|
||||
return b'PyModExport' + suffix
|
||||
|
||||
The export hook returns an array of :c:type:`PyModuleDef_Slot` entries,
|
||||
The export hook returns an array of :c:type:`PySlot` entries,
|
||||
terminated by an entry with a slot ID of ``0``.
|
||||
These slots describe how the module should be created and initialized.
|
||||
|
||||
@@ -75,7 +75,7 @@ It is recommended to define the export hook function using a helper macro:
|
||||
Declare an extension module export hook.
|
||||
This macro:
|
||||
|
||||
* specifies the :c:expr:`PyModuleDef_Slot*` return type,
|
||||
* specifies the :c:expr:`PySlot*` return type,
|
||||
* adds any special linkage declarations required by the platform, and
|
||||
* for C++, declares the function as ``extern "C"``.
|
||||
|
||||
@@ -83,12 +83,12 @@ For example, a module called ``spam`` would be defined like this::
|
||||
|
||||
PyABIInfo_VAR(abi_info);
|
||||
|
||||
static PyModuleDef_Slot spam_slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_name, "spam"},
|
||||
{Py_mod_init, spam_init_function},
|
||||
static PySlot spam_slots[] = {
|
||||
PySlot_STATIC_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_STATIC_DATA(Py_mod_name, "spam"),
|
||||
PySlot_FUNC(Py_mod_init, spam_init_function),
|
||||
...
|
||||
{0, NULL},
|
||||
PySlot_END
|
||||
};
|
||||
|
||||
PyMODEXPORT_FUNC
|
||||
|
||||
@@ -18,6 +18,7 @@ document the API functions in detail.
|
||||
refcounting.rst
|
||||
exceptions.rst
|
||||
extension-modules.rst
|
||||
slots.rst
|
||||
utilities.rst
|
||||
abstract.rst
|
||||
concrete.rst
|
||||
|
||||
+80
-43
@@ -133,14 +133,16 @@ Module Objects
|
||||
unencodable filenames, use :c:func:`PyModule_GetFilenameObject` instead.
|
||||
|
||||
|
||||
.. _c_module_slots:
|
||||
.. _pymoduledef_slot:
|
||||
|
||||
Module definition
|
||||
-----------------
|
||||
|
||||
Modules created using the C API are typically defined using an
|
||||
array of :dfn:`slots`.
|
||||
The slots provide a "description" of how a module should be created.
|
||||
array of :c:type:`PySlot` structs, which provides a "description" of how a
|
||||
module should be created.
|
||||
See :ref:`capi-slots` for more information on slots in general.
|
||||
|
||||
.. versionchanged:: 3.15
|
||||
|
||||
@@ -158,30 +160,12 @@ Unless specified otherwise, the same slot ID may not be repeated
|
||||
in an array of slots.
|
||||
|
||||
|
||||
.. c:type:: PyModuleDef_Slot
|
||||
|
||||
.. c:member:: int slot
|
||||
|
||||
A slot ID, chosen from the available ``Py_mod_*`` values explained below.
|
||||
|
||||
An ID of 0 marks the end of a :c:type:`!PyModuleDef_Slot` array.
|
||||
|
||||
.. c:member:: void* value
|
||||
|
||||
Value of the slot, whose meaning depends on the slot ID.
|
||||
|
||||
The value may not be NULL.
|
||||
To leave a slot out, omit the :c:type:`PyModuleDef_Slot` entry entirely.
|
||||
|
||||
.. versionadded:: 3.5
|
||||
|
||||
|
||||
Metadata slots
|
||||
..............
|
||||
|
||||
.. c:macro:: Py_mod_name
|
||||
|
||||
:c:type:`Slot ID <PyModuleDef_Slot.slot>` for the name of the new module,
|
||||
:c:member:`Slot ID <PySlot.sl_id>` for the name of the new module,
|
||||
as a NUL-terminated UTF8-encoded ``const char *``.
|
||||
|
||||
Note that modules are typically created using a
|
||||
@@ -196,7 +180,7 @@ Metadata slots
|
||||
|
||||
.. c:macro:: Py_mod_doc
|
||||
|
||||
:c:type:`Slot ID <PyModuleDef_Slot.slot>` for the docstring of the new
|
||||
:c:type:`Slot ID <PySlot.sl_id>` for the docstring of the new
|
||||
module, as a NUL-terminated UTF8-encoded ``const char *``.
|
||||
|
||||
Usually it is set to a variable created with :c:macro:`PyDoc_STRVAR`.
|
||||
@@ -211,7 +195,7 @@ Feature slots
|
||||
|
||||
.. c:macro:: Py_mod_abi
|
||||
|
||||
:c:type:`Slot ID <PyModuleDef_Slot.slot>` whose value points to
|
||||
:c:member:`Slot ID <PySlot.sl_id>` whose value points to
|
||||
a :c:struct:`PyABIInfo` structure describing the ABI that
|
||||
the extension is using.
|
||||
|
||||
@@ -222,8 +206,8 @@ Feature slots
|
||||
|
||||
PyABIInfo_VAR(abi_info);
|
||||
|
||||
static PyModuleDef_Slot mymodule_slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
static PySlot mymodule_slots[] = {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
...
|
||||
};
|
||||
|
||||
@@ -237,7 +221,7 @@ Feature slots
|
||||
|
||||
.. c:macro:: Py_mod_multiple_interpreters
|
||||
|
||||
:c:type:`Slot ID <PyModuleDef_Slot.slot>` whose value is one of:
|
||||
:c:member:`Slot ID <PySlot.sl_id>` whose value is one of:
|
||||
|
||||
.. c:namespace:: NULL
|
||||
|
||||
@@ -267,7 +251,7 @@ Feature slots
|
||||
|
||||
.. c:macro:: Py_mod_gil
|
||||
|
||||
:c:type:`Slot ID <PyModuleDef_Slot.slot>` whose value is one of:
|
||||
:c:member:`Slot ID <PySlot.sl_id>` whose value is one of:
|
||||
|
||||
.. c:namespace:: NULL
|
||||
|
||||
@@ -296,7 +280,7 @@ Creation and initialization slots
|
||||
|
||||
.. c:macro:: Py_mod_create
|
||||
|
||||
:c:type:`Slot ID <PyModuleDef_Slot.slot>` for a function that creates
|
||||
:c:member:`Slot ID <PySlot.sl_id>` for a function that creates
|
||||
the module object itself.
|
||||
The function must have the signature:
|
||||
|
||||
@@ -346,7 +330,7 @@ Creation and initialization slots
|
||||
|
||||
.. c:macro:: Py_mod_exec
|
||||
|
||||
:c:type:`Slot ID <PyModuleDef_Slot.slot>` for a function that will
|
||||
:c:member:`Slot ID <PySlot.sl_id>` for a function that will
|
||||
:dfn:`execute`, or initialize, the module.
|
||||
This function does the equivalent to executing the code of a Python module:
|
||||
typically, it adds classes and constants to the module.
|
||||
@@ -375,7 +359,7 @@ Creation and initialization slots
|
||||
|
||||
.. c:macro:: Py_mod_methods
|
||||
|
||||
:c:type:`Slot ID <PyModuleDef_Slot.slot>` for a table of module-level
|
||||
:c:member:`Slot ID <PySlot.sl_id>` for a table of module-level
|
||||
functions, as an array of :c:type:`PyMethodDef` values suitable as the
|
||||
*functions* argument to :c:func:`PyModule_AddFunctions`.
|
||||
|
||||
@@ -446,12 +430,12 @@ To retrieve the state from a given module, use the following functions:
|
||||
Slots for defining module state
|
||||
...............................
|
||||
|
||||
The following :c:member:`PyModuleDef_Slot.slot` IDs are available for
|
||||
The following :c:member:`slot IDs <PySlot.sl_id>` are available for
|
||||
defining the module state.
|
||||
|
||||
.. c:macro:: Py_mod_state_size
|
||||
|
||||
:c:type:`Slot ID <PyModuleDef_Slot.slot>` for the size of the module state,
|
||||
:c:member:`Slot ID <PySlot.sl_id>` for the size of the module state,
|
||||
in bytes.
|
||||
|
||||
Setting the value to a non-negative value means that the module can be
|
||||
@@ -468,7 +452,7 @@ defining the module state.
|
||||
|
||||
.. c:macro:: Py_mod_state_traverse
|
||||
|
||||
:c:type:`Slot ID <PyModuleDef_Slot.slot>` for a traversal function to call
|
||||
:c:member:`Slot ID <PySlot.sl_id>` for a traversal function to call
|
||||
during GC traversal of the module object.
|
||||
|
||||
The signature of the function, and meanings of the arguments,
|
||||
@@ -491,7 +475,7 @@ defining the module state.
|
||||
|
||||
.. c:macro:: Py_mod_state_clear
|
||||
|
||||
:c:type:`Slot ID <PyModuleDef_Slot.slot>` for a clear function to call
|
||||
:c:member:`Slot ID <PySlot.sl_id>` for a clear function to call
|
||||
during GC clearing of the module object.
|
||||
|
||||
The signature of the function is:
|
||||
@@ -519,7 +503,7 @@ defining the module state.
|
||||
|
||||
.. c:macro:: Py_mod_state_free
|
||||
|
||||
:c:type:`Slot ID <PyModuleDef_Slot.slot>` for a function to call during
|
||||
:c:member:`Slot ID <PySlot.sl_id>` for a function to call during
|
||||
deallocation of the module object.
|
||||
|
||||
The signature of the function is:
|
||||
@@ -578,7 +562,7 @@ A module's token -- and the *your_token* value to use in the above code -- is:
|
||||
|
||||
.. c:macro:: Py_mod_token
|
||||
|
||||
:c:type:`Slot ID <PyModuleDef_Slot.slot>` for the module token.
|
||||
:c:member:`Slot ID <PySlot.sl_id>` for the module token.
|
||||
|
||||
If you use this slot to set the module token (rather than rely on the
|
||||
default), you must ensure that:
|
||||
@@ -617,14 +601,14 @@ Creating extension modules dynamically
|
||||
The following functions may be used to create an extension module dynamically,
|
||||
rather than from an extension's :ref:`export hook <extension-export-hook>`.
|
||||
|
||||
.. c:function:: PyObject *PyModule_FromSlotsAndSpec(const PyModuleDef_Slot *slots, PyObject *spec)
|
||||
.. c:function:: PyObject *PyModule_FromSlotsAndSpec(const PySlot *slots, PyObject *spec)
|
||||
|
||||
Create a new module object, given an array of :ref:`slots <pymoduledef_slot>`
|
||||
and the :py:class:`~importlib.machinery.ModuleSpec` *spec*.
|
||||
|
||||
The *slots* argument must point to an array of :c:type:`PyModuleDef_Slot`
|
||||
The *slots* argument must point to an array of :c:type:`PySlot`
|
||||
structures, terminated by an entry with slot ID of 0
|
||||
(typically written as ``{0}`` or ``{0, NULL}`` in C).
|
||||
(typically written as :c:macro:`PySlot_END`).
|
||||
The array must include a :c:data:`Py_mod_abi` entry.
|
||||
|
||||
The *spec* argument may be any ``ModuleSpec``-like object, as described
|
||||
@@ -640,10 +624,6 @@ rather than from an extension's :ref:`export hook <extension-export-hook>`.
|
||||
must be called to fully initialize a module.
|
||||
(See also :ref:`multi-phase-initialization`.)
|
||||
|
||||
The *slots* array only needs to be valid for the duration of the
|
||||
:c:func:`!PyModule_FromSlotsAndSpec` call.
|
||||
In particular, it may be heap-allocated.
|
||||
|
||||
.. versionadded:: 3.15
|
||||
|
||||
.. c:function:: int PyModule_Exec(PyObject *module)
|
||||
@@ -738,6 +718,8 @@ remove it.
|
||||
.. c:member:: PyModuleDef_Slot* m_slots
|
||||
|
||||
An array of additional slots, terminated by a ``{0, NULL}`` entry.
|
||||
Note that the entries use the older :c:type:`PyModuleDef_Slot` structure,
|
||||
rather than :c:type:`PySlot`.
|
||||
|
||||
If the array contains slots corresponding to :c:type:`PyModuleDef`
|
||||
members, the values must match.
|
||||
@@ -752,6 +734,39 @@ remove it.
|
||||
|
||||
.. c:member:: inquiry m_reload
|
||||
|
||||
.. c:namespace:: NULL
|
||||
|
||||
.. c:type:: PyModuleDef_Slot
|
||||
|
||||
Older structure defining additional slots of a module.
|
||||
|
||||
Note that a :c:type:`!PyModuleDef_Slot` array may be included in a
|
||||
:c:type:`!PySlot` array using :c:macro:`Py_mod_slots`,
|
||||
and vice versa using :c:macro:`Py_slot_subslots`.
|
||||
|
||||
Each :c:type:`!PyModuleDef_Slot` structure ``modslot`` is interpreted
|
||||
as the following :c:type:`PySlot` structure::
|
||||
|
||||
(PySlot){
|
||||
.sl_id=modslot.slot,
|
||||
.sl_flags=PySlot_INTPTR | sub_static,
|
||||
.sl_ptr=modslot.value
|
||||
}
|
||||
|
||||
where ``sub_static`` is ``PySlot_STATIC`` if the slot requires
|
||||
the flag (such as for :c:macro:`Py_mod_methods`), or if this flag
|
||||
is present on the "parent" :c:macro:`!Py_mod_slots` slot (if any).
|
||||
|
||||
.. c:member:: int slot
|
||||
|
||||
Corresponds to :c:member:`PySlot.sl_id`.
|
||||
|
||||
.. c:member:: void* value
|
||||
|
||||
Corresponds to :c:member:`PySlot.sl_ptr`.
|
||||
|
||||
.. versionadded:: 3.5
|
||||
|
||||
.. c:member:: traverseproc m_traverse
|
||||
inquiry m_clear
|
||||
freefunc m_free
|
||||
@@ -774,6 +789,14 @@ remove it.
|
||||
The type of ``PyModuleDef`` objects.
|
||||
|
||||
|
||||
.. c:macro:: Py_mod_slots
|
||||
|
||||
:c:member:`Slot ID <PySlot.sl_id>` that works like
|
||||
:c:macro:`Py_slot_subslots`, except it specifies an array of
|
||||
:c:type:`PyModuleDef_Slot` structures.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
.. _moduledef-dynamic:
|
||||
|
||||
The following API can be used to create modules from a :c:type:`!PyModuleDef`
|
||||
@@ -813,6 +836,10 @@ struct:
|
||||
|
||||
.. versionadded:: 3.5
|
||||
|
||||
.. soft-deprecated:: next
|
||||
|
||||
Prefer :c:func:`PyModule_FromSlotsAndSpec` in new code.
|
||||
|
||||
.. c:function:: PyObject * PyModule_FromDefAndSpec2(PyModuleDef *def, PyObject *spec, int module_api_version)
|
||||
|
||||
Create a new module object, given the definition in *def* and the
|
||||
@@ -833,12 +860,22 @@ struct:
|
||||
|
||||
.. versionadded:: 3.5
|
||||
|
||||
.. soft-deprecated:: next
|
||||
|
||||
Prefer :c:func:`PyModule_FromSlotsAndSpec` in new code.
|
||||
|
||||
.. c:function:: int PyModule_ExecDef(PyObject *module, PyModuleDef *def)
|
||||
|
||||
Process any execution slots (:c:data:`Py_mod_exec`) given in *def*.
|
||||
|
||||
.. versionadded:: 3.5
|
||||
|
||||
.. soft-deprecated:: next
|
||||
|
||||
To run a module's own execution slots, prefer :c:func:`PyModule_Exec`,
|
||||
which works on modules that were not created from a
|
||||
:c:type:`PyModuleDef` structure.
|
||||
|
||||
.. c:macro:: PYTHON_API_VERSION
|
||||
PYTHON_API_STRING
|
||||
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
.. highlight:: c
|
||||
|
||||
.. _capi-slots:
|
||||
|
||||
Definition slots
|
||||
================
|
||||
|
||||
To define :ref:`module objects <moduleobjects>` and
|
||||
:ref:`classes <creating-heap-types>` using the C API, you may use
|
||||
an array of *slots* -- essentally, key-value pairs that describe features
|
||||
of the object to create.
|
||||
This decouples the data from the structures used at runtime, allowing CPython
|
||||
-- and other Python C API implementations -- to update the stuctures without
|
||||
breaking backwards compatibility.
|
||||
|
||||
This section documents slots in general.
|
||||
For object-specific behavior and slot values, see documentation for functions
|
||||
that apply slots:
|
||||
|
||||
- :c:func:`PyType_FromSlots` for types;
|
||||
- :c:func:`PyModule_FromSlotsAndSpec` and :ref:`extension-export-hook`
|
||||
for modules.
|
||||
|
||||
When slots are passed to a function that applies them, the function will
|
||||
not modify the slot array, nor any data it points to (recursively).
|
||||
After the function is done, the caller is allowed to modify or deallocate
|
||||
the array and any data it points to (recursively), except data
|
||||
explicitly marked with :c:macro:`PySlot_STATIC`.
|
||||
|
||||
Except when documented otherwise, multiple slots with the same ID
|
||||
(:c:member:`~PySlot.sl_id`) may not occur in a single slots array.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
Slot arrays generalize an earlier way of defining objects:
|
||||
using :c:type:`PyType_Spec` with :c:type:`PyType_Slot` for types, and
|
||||
:c:type:`PyModuleDef` with :c:type:`PyModuleDef_Slot` for modules.
|
||||
The earlier API is :term:`soft deprecated`; there are no plans to remove it.
|
||||
|
||||
Entries of the slots array use the following structure:
|
||||
|
||||
.. c:type:: PySlot
|
||||
|
||||
An entry in a slots array. Defined as:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
typedef struct {
|
||||
uint16_t sl_id;
|
||||
uint16_t sl_flags;
|
||||
uint32_t _reserved; // must be 0
|
||||
union {
|
||||
void *sl_ptr;
|
||||
void (*sl_func)(void);
|
||||
Py_ssize_t sl_size;
|
||||
int64_t sl_int64;
|
||||
uint64_t sl_uint64;
|
||||
};
|
||||
} PySlot;
|
||||
|
||||
.. c:member:: uint16_t sl_id
|
||||
|
||||
A slot ID, chosen from:
|
||||
|
||||
- ``Py_slot_*`` values documented in :ref:`pyslot-common-ids` below;
|
||||
- ``Py_mod_*`` values for modules, as documented in :ref:`c_module_slots`;
|
||||
- Values for types, as documented in :ref:`pyslot_type_slot_ids`.
|
||||
|
||||
A :c:member:`!sl_id` of zero (:c:macro:`Py_slot_end`) marks the end of a
|
||||
slots array.
|
||||
|
||||
.. c:member:: void *sl_ptr
|
||||
void (*sl_func)(void)
|
||||
Py_ssize_t sl_size
|
||||
int64_t sl_int64
|
||||
uint64_t sl_uint64
|
||||
|
||||
The data for the slot.
|
||||
These members are part of an anonymous union;
|
||||
the member to use depends on which data type is required by the slot ID:
|
||||
data pointer, function pointer, size, signed or unsigned
|
||||
integer, respectively.
|
||||
|
||||
Except when documented otherwise for a specific slot ID, pointers
|
||||
(that is :c:member:`!sl_ptr` and :c:member:`!sl_func`) may not be NULL.
|
||||
|
||||
.. c:member:: uint16_t sl_flags
|
||||
|
||||
Zero or more of the following flags, OR-ed together:
|
||||
|
||||
.. c:namespace:: NULL
|
||||
|
||||
.. c:macro:: PySlot_STATIC
|
||||
|
||||
All data the slot points to is statically allocated and constant.
|
||||
Thus, the interpreter does not need to copy the information.
|
||||
|
||||
This flag is implied for function pointers.
|
||||
|
||||
The flag applies even to data the slot points to “indirectly”,
|
||||
except for slots nested via :c:macro:`Py_slot_subslots` which may
|
||||
have their own :c:macro:`!PySlot_STATIC` flags.
|
||||
For example, if applied to a :c:macro:`Py_tp_members` slot that
|
||||
points to an array of :c:type:`PyMemberDef` structures,
|
||||
then the entire array, as well as the name and doc strings
|
||||
in its elements, must be static and constant.
|
||||
|
||||
.. c:macro:: PySlot_INTPTR
|
||||
|
||||
The data is stored in ``sl_ptr``; CPython will cast it to
|
||||
the appropriate type.
|
||||
|
||||
This flag can simplify porting from the older :c:type:`PyType_Slot`
|
||||
and :c:type:`PyModuleDef_Slot` structures.
|
||||
|
||||
.. c:macro:: PySlot_OPTIONAL
|
||||
|
||||
If the slot ID is unknown, the interpreter should ignore the
|
||||
slot, rather than fail.
|
||||
|
||||
For example, if Python 3.16 adds a new feature with a new slot ID,attr
|
||||
the corresponding slot may be marked :c:macro:`!PySlot_OPTIONAL`
|
||||
so that Python 3.15 ignores it.
|
||||
|
||||
Note that the "optionality" only applies to unknown slot IDs.
|
||||
This flag does not make Python skip invalid values of known slots.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
|
||||
Convenience macros
|
||||
------------------
|
||||
|
||||
.. c:macro:: PySlot_DATA(name, value)
|
||||
PySlot_FUNC(name, value)
|
||||
PySlot_SIZE(name, value)
|
||||
PySlot_INT64(name, value)
|
||||
PySlot_UINT64(name, value)
|
||||
PySlot_STATIC_DATA(name, value)
|
||||
|
||||
Convenience macros to define :c:type:`!PySlot` structures with
|
||||
:c:member:`~PySlot.sl_id` and a particular union member set.
|
||||
|
||||
:c:macro:`!PySlot_STATIC_DATA` sets the :c:macro:`PySlot_STATIC` flag;
|
||||
others set no flags.
|
||||
|
||||
Note that these macros use *designated initializers*, a C language feature
|
||||
that C++ added in the 2020 version of the standard.
|
||||
If your code needs to be compatible with C++11 or older,
|
||||
use :c:macro:`PySlot_PTR` instead.
|
||||
|
||||
Defined as::
|
||||
|
||||
#define PySlot_DATA(NAME, VALUE) \
|
||||
{.sl_id=NAME, .sl_ptr=(void*)(VALUE)}
|
||||
|
||||
#define PySlot_FUNC(NAME, VALUE) \
|
||||
{.sl_id=NAME, .sl_func=(VALUE)}
|
||||
|
||||
#define PySlot_SIZE(NAME, VALUE) \
|
||||
{.sl_id=NAME, .sl_size=(VALUE)}
|
||||
|
||||
#define PySlot_INT64(NAME, VALUE) \
|
||||
{.sl_id=NAME, .sl_int64=(VALUE)}
|
||||
|
||||
#define PySlot_UINT64(NAME, VALUE) \
|
||||
{.sl_id=NAME, .sl_uint64=(VALUE)}
|
||||
|
||||
#define PySlot_STATIC_DATA(NAME, VALUE) \
|
||||
{.sl_id=NAME, .sl_flags=PySlot_STATIC, .sl_ptr=(VALUE)}
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
.. c:macro:: PySlot_END
|
||||
|
||||
Convenience macro to mark the end of a :c:type:`!PySlot` array.
|
||||
|
||||
Defined as::
|
||||
|
||||
#define PySlot_END {0}
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
.. c:macro:: PySlot_PTR(name, value)
|
||||
PySlot_PTR_STATIC(name, value)
|
||||
|
||||
Convenience macros for use in C++11-compatible code.
|
||||
This version of C++ does not allow setting arbitrary union members in
|
||||
literals; instead, these macros set the :c:macro:`PySlot_INTPTR` flag and cast
|
||||
the value to ``(void*)``.
|
||||
|
||||
Defined as::
|
||||
|
||||
#define PySlot_PTR(NAME, VALUE) \
|
||||
{NAME, PySlot_INTPTR, {0}, {(void*)(VALUE)}}
|
||||
|
||||
#define PySlot_PTR_STATIC(NAME, VALUE) \
|
||||
{NAME, PySlot_INTPTR|Py_SLOT_STATIC, {0}, {(void*)(VALUE)}}
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
.. _pyslot-common-ids:
|
||||
|
||||
Common slot IDs
|
||||
---------------
|
||||
|
||||
The following slot IDs may be used in both type and module definitions.
|
||||
|
||||
.. c:macro:: Py_slot_end
|
||||
|
||||
Marks the end of a slots array.
|
||||
Defined as zero.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
.. c:macro:: Py_slot_subslots
|
||||
|
||||
Nested slots array.
|
||||
|
||||
The value (:c:member:`~PySlot.sl_ptr`) should point to an array of
|
||||
:c:type:`PySlot` structures.
|
||||
The slots in the array (up to but not including the zero-ID
|
||||
terminator) will be treated as if they were inserted if the current
|
||||
slot array, at the point :c:macro:`!Py_slot_subslots` appears.
|
||||
|
||||
Slot nesting depth is limited to 5 levels.
|
||||
This restriction may be lifted in the future.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
.. c:macro:: Py_slot_invalid
|
||||
|
||||
Reserved; will always be treated as an unknown slot ID.
|
||||
Defined as ``UINT16_MAX`` (``0xFFFF``).
|
||||
|
||||
When used with the :c:macro:`PySlot_OPTIONAL` flag, defines a slot with
|
||||
no effect.
|
||||
Without the flag, processing a slot with this ID will fail.
|
||||
|
||||
.. versionadded:: next
|
||||
+427
-190
@@ -3,7 +3,7 @@
|
||||
.. _typeobjects:
|
||||
|
||||
Type Objects
|
||||
------------
|
||||
============
|
||||
|
||||
.. index:: pair: object; type
|
||||
|
||||
@@ -384,36 +384,19 @@ Type Objects
|
||||
* :py:mod:`weakref`
|
||||
|
||||
|
||||
.. _creating-heap-types:
|
||||
|
||||
Creating Heap-Allocated Types
|
||||
.............................
|
||||
-----------------------------
|
||||
|
||||
The following functions and structs are used to create
|
||||
:ref:`heap types <heap-types>`.
|
||||
The following function is used to create :ref:`heap types <heap-types>`:
|
||||
|
||||
.. c:function:: PyObject* PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases)
|
||||
.. c:function:: PyObject *PyType_FromSlots(const PySlot *slots)
|
||||
|
||||
Create and return a :ref:`heap type <heap-types>` from the *spec*
|
||||
(see :c:macro:`Py_TPFLAGS_HEAPTYPE`).
|
||||
|
||||
The metaclass *metaclass* is used to construct the resulting type object.
|
||||
When *metaclass* is ``NULL``, the metaclass is derived from *bases*
|
||||
(or *Py_tp_base[s]* slots if *bases* is ``NULL``, see below).
|
||||
|
||||
Metaclasses that override :c:member:`~PyTypeObject.tp_new` are not
|
||||
supported, except if ``tp_new`` is ``NULL``.
|
||||
|
||||
The *bases* argument can be used to specify base classes; it can either
|
||||
be only one class or a tuple of classes.
|
||||
If *bases* is ``NULL``, the :c:data:`Py_tp_bases` slot is used instead.
|
||||
If that also is ``NULL``, the :c:data:`Py_tp_base` slot is used instead.
|
||||
If that also is ``NULL``, the new type derives from :class:`object`.
|
||||
|
||||
The *module* argument can be used to record the module in which the new
|
||||
class is defined. It must be a module object or ``NULL``.
|
||||
If not ``NULL``, the module is associated with the new type and can later be
|
||||
retrieved with :c:func:`PyType_GetModule`.
|
||||
The associated module is not inherited by subclasses; it must be specified
|
||||
for each class individually.
|
||||
Create and return a :ref:`heap type <heap-types>` from a :c:type:`!PySlot`
|
||||
array.
|
||||
See :ref:`capi-slots` for general information on slots,
|
||||
and :ref:`pyslot_type_slot_ids` for slots specific to type creation.
|
||||
|
||||
This function calls :c:func:`PyType_Ready` on the new type.
|
||||
|
||||
@@ -430,8 +413,376 @@ The following functions and structs are used to create
|
||||
* :py:meth:`~object.__init_subclass__` is not called on any bases.
|
||||
* :py:meth:`~object.__set_name__` is not called on new descriptors.
|
||||
|
||||
Slots are typically defined as a global static constant arrays.
|
||||
However, sometimes slot values are not statically known at compile time.
|
||||
For example, slots like :c:data:`Py_tp_bases`, :c:data:`Py_tp_metaclass`
|
||||
and :c:data:`Py_tp_module` require live Python objects.
|
||||
In this case, it is recommended to put such slots on the stack,
|
||||
and use :c:macro:`Py_slot_subslots` to refer to an array of static slots.
|
||||
For example::
|
||||
|
||||
static const PySlot my_slots[] = {
|
||||
PySlot_STATIC_DATA(Py_tp_name, "MyClass"),
|
||||
PySlot_FUNC(Py_tp_repr, my_repr_func),
|
||||
...
|
||||
PySlot_END
|
||||
};
|
||||
|
||||
PyObject *make_my_class(PyObject *module) {
|
||||
PySlot all_slots[] = {
|
||||
PySlot_STATIC_DATA(Py_slot_subslots, my_slots),
|
||||
PySlot_DATA(Py_tp_module, module),
|
||||
PySlot_END
|
||||
};
|
||||
return PyType_FromSlots(all_slots);
|
||||
}
|
||||
|
||||
Heap types created without the :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag may be
|
||||
modified, for example by setting attributes on them, as with classes defined
|
||||
in Python code.
|
||||
Sometimes, such modifications are necessary to fully initialize a type,
|
||||
but you may wish to prevent users from changing the type after
|
||||
the initialization is done:
|
||||
|
||||
.. c:function:: int PyType_Freeze(PyTypeObject *type)
|
||||
|
||||
Make a type immutable: set the :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag.
|
||||
|
||||
All base classes of *type* must be immutable.
|
||||
|
||||
On success, return ``0``.
|
||||
On error, set an exception and return ``-1``.
|
||||
|
||||
The type must not be used before it's made immutable. For example, type
|
||||
instances must not be created before the type is made immutable.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
|
||||
.. _pyslot_type_slot_ids:
|
||||
|
||||
Type slot IDs
|
||||
.............
|
||||
|
||||
Most type slot IDs are named like the field names of the structures
|
||||
:c:type:`PyTypeObject`, :c:type:`PyNumberMethods`,
|
||||
:c:type:`PySequenceMethods`, :c:type:`PyMappingMethods` and
|
||||
:c:type:`PyAsyncMethods` with an added ``Py_`` prefix.
|
||||
For example, use:
|
||||
|
||||
* :c:data:`Py_tp_dealloc` to set :c:member:`PyTypeObject.tp_dealloc`
|
||||
* :c:data:`Py_nb_add` to set :c:member:`PyNumberMethods.nb_add`
|
||||
* :c:data:`Py_sq_length` to set :c:member:`PySequenceMethods.sq_length`
|
||||
|
||||
The following slots need additional considerations when specified as slots:
|
||||
|
||||
* :c:data:`Py_tp_name`
|
||||
* :c:data:`Py_tp_basicsize` and :c:data:`Py_tp_extra_basicsize`
|
||||
* :c:data:`Py_tp_itemsize`
|
||||
* :c:data:`Py_tp_flags`
|
||||
|
||||
Additional slots do not directly correspond to a :c:type:`!PyTypeObject`
|
||||
struct field:
|
||||
|
||||
* :c:data:`Py_tp_token`
|
||||
* :c:data:`Py_tp_metaclass`
|
||||
* :c:data:`Py_tp_module`
|
||||
|
||||
The following “offset” fields cannot be set using :c:type:`PyType_Slot`:
|
||||
|
||||
* :c:member:`~PyTypeObject.tp_weaklistoffset`
|
||||
(use :c:macro:`Py_TPFLAGS_MANAGED_WEAKREF` instead if possible)
|
||||
* :c:member:`~PyTypeObject.tp_dictoffset`
|
||||
(use :c:macro:`Py_TPFLAGS_MANAGED_DICT` instead if possible)
|
||||
* :c:member:`~PyTypeObject.tp_vectorcall_offset`
|
||||
(use ``"__vectorcalloffset__"`` in :ref:`PyMemberDef <pymemberdef-offsets>`)
|
||||
|
||||
If it is not possible to switch to a ``MANAGED`` flag (for example,
|
||||
for vectorcall or to support Python older than 3.12), specify the
|
||||
offset in :c:data:`Py_tp_members`.
|
||||
See :ref:`PyMemberDef documentation <pymemberdef-offsets>`
|
||||
for details.
|
||||
|
||||
The following internal fields cannot be set at all when creating a heap
|
||||
type:
|
||||
|
||||
* :c:member:`~PyTypeObject.tp_dict`,
|
||||
:c:member:`~PyTypeObject.tp_mro`,
|
||||
:c:member:`~PyTypeObject.tp_cache`,
|
||||
:c:member:`~PyTypeObject.tp_subclasses`, and
|
||||
:c:member:`~PyTypeObject.tp_weaklist`.
|
||||
|
||||
The :c:data:`Py_tp_base` slot is equivalent to :c:data:`Py_tp_bases`;
|
||||
both may be set either to a type or a tuple of types.
|
||||
If both are specified, the value of :c:data:`Py_tp_bases`
|
||||
is used.
|
||||
|
||||
Slot values may not be ``NULL``, except for the following:
|
||||
|
||||
* :c:data:`Py_tp_doc`
|
||||
* :c:data:`Py_tp_token` (for clarity, prefer :c:data:`Py_TP_USE_SPEC`
|
||||
rather than ``NULL``)
|
||||
|
||||
.. versionchanged:: 3.9
|
||||
Slots in :c:type:`PyBufferProcs` may be set in the unlimited API.
|
||||
|
||||
.. versionchanged:: 3.11
|
||||
:c:member:`~PyBufferProcs.bf_getbuffer` and
|
||||
:c:member:`~PyBufferProcs.bf_releasebuffer` are now available
|
||||
under the :ref:`limited API <limited-c-api>`.
|
||||
|
||||
.. versionchanged:: 3.14
|
||||
The field :c:member:`~PyTypeObject.tp_vectorcall` can now be set
|
||||
using :c:data:`Py_tp_vectorcall`. See the field's documentation
|
||||
for details.
|
||||
|
||||
.. versionchanged:: 3.15
|
||||
The :c:data:`Py_tp_bases` slot may be set to a single type object,
|
||||
making it equivalent to the :c:data:`Py_tp_base` slot.
|
||||
Previously, a tuple of types was required.
|
||||
|
||||
The following slots correspond to fields in the underlying type structure,
|
||||
but need extra remarks for use as slots:
|
||||
|
||||
.. c:macro:: Py_tp_name
|
||||
|
||||
:c:member:`Slot ID <PySlot.sl_id>` for the name of the type,
|
||||
used to set :c:member:`PyTypeObject.tp_name`.
|
||||
|
||||
This slot (or :c:func:`PyType_Spec.name`) is required to create a type.
|
||||
|
||||
This may not be used in :c:member:`PyType_Spec.slots`.
|
||||
Use :c:func:`PyType_Spec.name` instead.
|
||||
|
||||
.. impl-detail::
|
||||
|
||||
CPython processes slots in order.
|
||||
It is recommended to put ``Py_tp_name`` at the beginning of the slots
|
||||
array, so that if processing of a later slots fails, error messages
|
||||
can include the name.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
.. c:macro:: Py_tp_basicsize
|
||||
|
||||
:c:member:`Slot ID <PySlot.sl_id>` for the size of the instance in bytes.
|
||||
It is used to set :c:member:`PyTypeObject.tp_basicsize`.
|
||||
|
||||
The value must be positive.
|
||||
|
||||
This may not be used in :c:member:`PyType_Spec.slots`.
|
||||
Use :c:func:`PyType_Spec.basicsize` instead.
|
||||
|
||||
This slot may not be used with :c:func:`PyType_GetSlot`.
|
||||
Use :c:member:`PyTypeObject.tp_basicsize` instead if needed, but be aware
|
||||
that a type's size is often considered an implementation detail.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
.. c:macro:: Py_tp_extra_basicsize
|
||||
|
||||
:c:member:`Slot ID <PySlot.sl_id>` for type data size in bytes, that is,
|
||||
how much space instances of the class need *in addition*
|
||||
to space needed for superclasses.
|
||||
|
||||
The value is used, together with the size of superclasses, to set
|
||||
:c:member:`PyTypeObject.tp_basicsize`.
|
||||
Python will insert padding as needed to meet
|
||||
:c:member:`!tp_basicsize`'s alignment requirements.
|
||||
|
||||
Use :c:func:`PyObject_GetTypeData` to get a pointer to subclass-specific
|
||||
memory reserved this way.
|
||||
|
||||
The value must be positive.
|
||||
To specify that instances need no additional size (that is, size should be
|
||||
inherited), omit the :c:macro:`!Py_tp_extra_basicsize` slot rather than
|
||||
set it to zero.
|
||||
|
||||
Specifying both :c:macro:`Py_tp_basicsize` and
|
||||
:c:macro:`!Py_tp_extra_basicsize` is an error.
|
||||
|
||||
This may not be used in :c:member:`PyType_Spec.slots`.
|
||||
Use negative :c:func:`PyType_Spec.basicsize` instead.
|
||||
|
||||
This slot may not be used with :c:func:`PyType_GetSlot`.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
.. c:macro:: Py_tp_itemsize
|
||||
|
||||
:c:member:`Slot ID <PySlot.sl_id>` for the size of one element of a
|
||||
variable-size type, in bytes.
|
||||
Used to set :c:member:`PyTypeObject.tp_itemsize`.
|
||||
See :c:member:`!tp_itemsize` documentation for caveats.
|
||||
|
||||
The value must be positive.
|
||||
|
||||
If this slot is missing, :c:member:`~PyTypeObject.tp_itemsize` is inherited.
|
||||
Extending arbitrary variable-sized classes is dangerous,
|
||||
since some types use a fixed offset for variable-sized memory,
|
||||
which can then overlap fixed-sized memory used by a subclass.
|
||||
To help prevent mistakes, inheriting ``itemsize`` is only possible
|
||||
in the following situations:
|
||||
|
||||
- The base is not variable-sized (its
|
||||
:c:member:`~PyTypeObject.tp_itemsize`).
|
||||
- The requested :c:member:`PyType_Spec.basicsize` is positive,
|
||||
suggesting that the memory layout of the base class is known.
|
||||
- The requested :c:member:`PyType_Spec.basicsize` is zero,
|
||||
suggesting that the subclass does not access the instance's memory
|
||||
directly.
|
||||
- With the :c:macro:`Py_TPFLAGS_ITEMS_AT_END` flag.
|
||||
|
||||
This may not be used in :c:member:`PyType_Spec.slots`.
|
||||
Use :c:func:`PyType_Spec.itemsize` instead.
|
||||
|
||||
This slot may not be used with :c:func:`PyType_GetSlot`.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
.. c:macro:: Py_tp_flags
|
||||
|
||||
:c:member:`Slot ID <PySlot.sl_id>` for type flags, used to set
|
||||
:c:member:`PyTypeObject.tp_flags`.
|
||||
|
||||
The ``Py_TPFLAGS_HEAPTYPE`` flag is not set,
|
||||
:c:func:`PyType_FromSpecWithBases` sets it automatically.
|
||||
|
||||
This may not be used in :c:member:`PyType_Spec.slots`.
|
||||
Use negative :c:func:`PyType_Spec.basicsize` instead.
|
||||
|
||||
This slot may not be used with :c:func:`PyType_GetSlot`.
|
||||
Use :c:func:`PyType_GetFlags` instead.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
The following slots do not correspond to public fields in the
|
||||
underlying structures:
|
||||
|
||||
.. c:macro:: Py_tp_metaclass
|
||||
|
||||
:c:member:`Slot ID <PySlot.sl_id>` for the metaclass used to construct
|
||||
the resulting type object.
|
||||
When omitted the metaclass is derived from bases
|
||||
(:c:macro:`Py_tp_bases` or the *bases* argument of
|
||||
:c:func:`PyType_FromMetaclass`).
|
||||
|
||||
Metaclasses that override :c:member:`~PyTypeObject.tp_new` are not
|
||||
supported, except if ``tp_new`` is ``NULL``.
|
||||
|
||||
This may not be used in :c:member:`PyType_Spec.slots`.
|
||||
Use :c:func:`PyType_FromMetaclass` to specify a metaclass with
|
||||
:c:type:`!PyType_Spec`.
|
||||
|
||||
This slot may not be used with :c:func:`PyType_GetSlot`.
|
||||
Use :c:func:`Py_TYPE` on the type object instead.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
.. c:macro:: Py_tp_module
|
||||
|
||||
:c:member:`Slot ID <PySlot.sl_id>` for recording the module in which
|
||||
the new class is defined.
|
||||
|
||||
The value must be a module object.
|
||||
The module is associated with the new type and can later be
|
||||
retrieved with :c:func:`PyType_GetModule`.
|
||||
The associated module is not inherited by subclasses; it must be specified
|
||||
for each class individually.
|
||||
|
||||
This may not be used in :c:member:`PyType_Spec.slots`.
|
||||
Use :c:func:`PyType_FromMetaclass` to specify a module with
|
||||
:c:type:`!PyType_Spec`.
|
||||
|
||||
This slot may not be used with :c:func:`PyType_GetSlot`.
|
||||
Use :c:func:`PyType_GetModule` instead.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
.. c:macro:: Py_tp_token
|
||||
|
||||
:c:member:`Slot ID <PySlot.sl_id>` for recording a static memory layout ID
|
||||
for a class.
|
||||
|
||||
If the class is defined using a :c:type:`PyType_Spec`, and that spec is
|
||||
statically allocated, the token can be set to the spec using the special
|
||||
value :c:data:`Py_TP_USE_SPEC`:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static PyType_Slot foo_slots[] = {
|
||||
{Py_tp_token, Py_TP_USE_SPEC},
|
||||
|
||||
It can also be set to an arbitrary pointer, but you must ensure that:
|
||||
|
||||
* The pointer outlives the class, so it's not reused for something else
|
||||
while the class exists.
|
||||
* It "belongs" to the extension module where the class lives, so it will not
|
||||
clash with other extensions.
|
||||
|
||||
Use :c:func:`PyType_GetBaseByToken` to check if a class's superclass has
|
||||
a given token -- that is, check whether the memory layout is compatible.
|
||||
|
||||
To get the token for a given class (without considering superclasses),
|
||||
use :c:func:`PyType_GetSlot` with ``Py_tp_token``.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
.. c:namespace:: NULL
|
||||
|
||||
.. c:macro:: Py_TP_USE_SPEC
|
||||
|
||||
Used as a value with :c:data:`Py_tp_token` to set the token to the
|
||||
class's :c:type:`PyType_Spec`.
|
||||
May only be used for classes defined using :c:type:`!PyType_Spec`.
|
||||
|
||||
Expands to ``NULL``.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
.. c:macro:: Py_tp_slots
|
||||
|
||||
:c:member:`Slot ID <PySlot.sl_id>` that works like
|
||||
:c:macro:`Py_slot_subslots`, except it specifies an array of
|
||||
:c:type:`PyType_Slot` structures.
|
||||
|
||||
.. versionadded:: next
|
||||
|
||||
|
||||
Soft-deprecated API
|
||||
-------------------
|
||||
|
||||
The following functions are :term:`soft deprecated`.
|
||||
They will continue to work, but new features will be added as slots for
|
||||
:c:func:`PyType_FromSlots`, not as arguments to new ``PyType_From*`` functions.
|
||||
|
||||
.. c:function:: PyObject* PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases)
|
||||
|
||||
Create and return a :ref:`heap type <heap-types>` from the *spec*
|
||||
(see :c:macro:`Py_TPFLAGS_HEAPTYPE`).
|
||||
|
||||
A non-``NULL`` *metaclass* argument corresponds to the
|
||||
:c:macro:`Py_tp_metaclass` slot.
|
||||
|
||||
A non-``NULL`` *bases* argument corresponds to the :c:data:`Py_tp_bases`
|
||||
slot, and takes precedence over :c:data:`Py_tp_bases` and
|
||||
:c:data:`Py_tp_bases` slots.
|
||||
|
||||
A non-``NULL`` *module* argument corresponds to the
|
||||
:c:macro:`Py_tp_module` slot.
|
||||
|
||||
This function calls :c:func:`PyType_Ready` on the new type.
|
||||
|
||||
Note that this function does *not* fully match the behavior of
|
||||
calling :py:class:`type() <type>` or using the :keyword:`class` statement.
|
||||
See the note in :c:func:`PyType_FromSlots` documentation for details.
|
||||
|
||||
.. versionadded:: 3.12
|
||||
|
||||
.. soft-deprecated:: next
|
||||
|
||||
Prefer :c:func:`PyType_FromSlots` in new code.
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases)
|
||||
|
||||
@@ -459,6 +810,10 @@ The following functions and structs are used to create
|
||||
Creating classes whose metaclass overrides
|
||||
:c:member:`~PyTypeObject.tp_new` is no longer allowed.
|
||||
|
||||
.. soft-deprecated:: next
|
||||
|
||||
Prefer :c:func:`PyType_FromSlots` in new code.
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)
|
||||
|
||||
@@ -481,6 +836,10 @@ The following functions and structs are used to create
|
||||
Creating classes whose metaclass overrides
|
||||
:c:member:`~PyTypeObject.tp_new` is no longer allowed.
|
||||
|
||||
.. soft-deprecated:: next
|
||||
|
||||
Prefer :c:func:`PyType_FromSlots` in new code.
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyType_FromSpec(PyType_Spec *spec)
|
||||
|
||||
@@ -502,20 +861,9 @@ The following functions and structs are used to create
|
||||
Creating classes whose metaclass overrides
|
||||
:c:member:`~PyTypeObject.tp_new` is no longer allowed.
|
||||
|
||||
.. soft-deprecated:: next
|
||||
|
||||
.. c:function:: int PyType_Freeze(PyTypeObject *type)
|
||||
|
||||
Make a type immutable: set the :c:macro:`Py_TPFLAGS_IMMUTABLETYPE` flag.
|
||||
|
||||
All base classes of *type* must be immutable.
|
||||
|
||||
On success, return ``0``.
|
||||
On error, set an exception and return ``-1``.
|
||||
|
||||
The type must not be used before it's made immutable. For example, type
|
||||
instances must not be created before the type is made immutable.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
Prefer :c:func:`PyType_FromSlots` in new code.
|
||||
|
||||
.. raw:: html
|
||||
|
||||
@@ -528,27 +876,23 @@ The following functions and structs are used to create
|
||||
|
||||
.. c:type:: PyType_Spec
|
||||
|
||||
Structure defining a type's behavior.
|
||||
Structure defining a type's behavior, used for soft-deprecated functions
|
||||
like :c:func:`PyType_FromMetaclass`.
|
||||
|
||||
This structure contains several members that can instead be specified
|
||||
as :ref:`slots <pyslot_type_slot_ids>` for :c:func:`PyType_FromSlots`,
|
||||
and an array of slot entries with a simpler structure.
|
||||
|
||||
.. c:member:: const char* name
|
||||
|
||||
Name of the type, used to set :c:member:`PyTypeObject.tp_name`.
|
||||
Corresponds to :c:macro:`Py_tp_name`.
|
||||
|
||||
.. c:member:: int basicsize
|
||||
|
||||
If positive, specifies the size of the instance in bytes.
|
||||
It is used to set :c:member:`PyTypeObject.tp_basicsize`.
|
||||
If positive, corresponds to :c:macro:`Py_tp_basicsize`.
|
||||
|
||||
If zero, specifies that :c:member:`~PyTypeObject.tp_basicsize`
|
||||
should be inherited.
|
||||
|
||||
If negative, the absolute value specifies how much space instances of the
|
||||
class need *in addition* to the superclass.
|
||||
Use :c:func:`PyObject_GetTypeData` to get a pointer to subclass-specific
|
||||
memory reserved this way.
|
||||
For negative :c:member:`!basicsize`, Python will insert padding when
|
||||
needed to meet :c:member:`~PyTypeObject.tp_basicsize`'s alignment
|
||||
requirements.
|
||||
If negative, corresponds to :c:macro:`Py_tp_extra_basicsize` set to
|
||||
the absolute value.
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
|
||||
@@ -556,160 +900,53 @@ The following functions and structs are used to create
|
||||
|
||||
.. c:member:: int itemsize
|
||||
|
||||
Size of one element of a variable-size type, in bytes.
|
||||
Used to set :c:member:`PyTypeObject.tp_itemsize`.
|
||||
See ``tp_itemsize`` documentation for caveats.
|
||||
|
||||
If zero, :c:member:`~PyTypeObject.tp_itemsize` is inherited.
|
||||
Extending arbitrary variable-sized classes is dangerous,
|
||||
since some types use a fixed offset for variable-sized memory,
|
||||
which can then overlap fixed-sized memory used by a subclass.
|
||||
To help prevent mistakes, inheriting ``itemsize`` is only possible
|
||||
in the following situations:
|
||||
|
||||
- The base is not variable-sized (its
|
||||
:c:member:`~PyTypeObject.tp_itemsize`).
|
||||
- The requested :c:member:`PyType_Spec.basicsize` is positive,
|
||||
suggesting that the memory layout of the base class is known.
|
||||
- The requested :c:member:`PyType_Spec.basicsize` is zero,
|
||||
suggesting that the subclass does not access the instance's memory
|
||||
directly.
|
||||
- With the :c:macro:`Py_TPFLAGS_ITEMS_AT_END` flag.
|
||||
Corresponds to :c:macro:`Py_tp_itemsize`.
|
||||
|
||||
.. c:member:: unsigned int flags
|
||||
|
||||
Type flags, used to set :c:member:`PyTypeObject.tp_flags`.
|
||||
|
||||
If the ``Py_TPFLAGS_HEAPTYPE`` flag is not set,
|
||||
:c:func:`PyType_FromSpecWithBases` sets it automatically.
|
||||
Corresponds to :c:macro:`Py_tp_flags`.
|
||||
|
||||
.. c:member:: PyType_Slot *slots
|
||||
|
||||
Array of :c:type:`PyType_Slot` structures.
|
||||
Terminated by the special slot value ``{0, NULL}``.
|
||||
Array of :c:type:`PyType_Slot` (not :c:type:`PySlot`) structures.
|
||||
|
||||
Terminated by the special slot value ``{0, NULL}``.
|
||||
Each slot ID should be specified at most once.
|
||||
|
||||
.. raw:: html
|
||||
.. c:namespace:: NULL
|
||||
|
||||
<!-- Keep old URL fragments working (see gh-97908) -->
|
||||
<span id='c.PyType_Slot.PyType_Slot.slot'></span>
|
||||
<span id='c.PyType_Slot.PyType_Slot.pfunc'></span>
|
||||
.. raw:: html
|
||||
|
||||
.. c:type:: PyType_Slot
|
||||
<!-- Keep old URL fragments working (see gh-97908) -->
|
||||
<span id='c.PyType_Slot.PyType_Slot.slot'></span>
|
||||
<span id='c.PyType_Slot.PyType_Slot.pfunc'></span>
|
||||
|
||||
Structure defining optional functionality of a type, containing a slot ID
|
||||
and a value pointer.
|
||||
.. c:type:: PyType_Slot
|
||||
|
||||
.. c:member:: int slot
|
||||
Structure defining optional functionality of a type, used for
|
||||
soft-deprecated functions like :c:func:`PyType_FromMetaclass`.
|
||||
|
||||
A slot ID.
|
||||
Note that a :c:type:`!PyType_Slot` array may be included in a
|
||||
:c:type:`!PySlot` array using :c:macro:`Py_tp_slots`,
|
||||
and vice versa using :c:macro:`Py_slot_subslots`.
|
||||
|
||||
Slot IDs are named like the field names of the structures
|
||||
:c:type:`PyTypeObject`, :c:type:`PyNumberMethods`,
|
||||
:c:type:`PySequenceMethods`, :c:type:`PyMappingMethods` and
|
||||
:c:type:`PyAsyncMethods` with an added ``Py_`` prefix.
|
||||
For example, use:
|
||||
Each :c:type:`!PyType_Slot` structure ``tpslot`` is interpreted
|
||||
as the following :c:type:`PySlot` structure::
|
||||
|
||||
* :c:data:`Py_tp_dealloc` to set :c:member:`PyTypeObject.tp_dealloc`
|
||||
* :c:data:`Py_nb_add` to set :c:member:`PyNumberMethods.nb_add`
|
||||
* :c:data:`Py_sq_length` to set :c:member:`PySequenceMethods.sq_length`
|
||||
(PySlot){
|
||||
.sl_id=tpslot.slot,
|
||||
.sl_flags=PySlot_INTPTR | sub_static,
|
||||
.sl_ptr=tpslot.func
|
||||
}
|
||||
|
||||
An additional slot is supported that does not correspond to a
|
||||
:c:type:`!PyTypeObject` struct field:
|
||||
where ``sub_static`` is ``PySlot_STATIC`` if the slot requires
|
||||
the flag (such as for :c:macro:`Py_tp_methods`), or if this flag
|
||||
is present on the "parent" :c:macro:`!Py_tp_slots` slot (if any).
|
||||
|
||||
* :c:data:`Py_tp_token`
|
||||
.. c:member:: int slot
|
||||
|
||||
The following “offset” fields cannot be set using :c:type:`PyType_Slot`:
|
||||
Corresponds to :c:member:`PySlot.sl_id`.
|
||||
|
||||
* :c:member:`~PyTypeObject.tp_weaklistoffset`
|
||||
(use :c:macro:`Py_TPFLAGS_MANAGED_WEAKREF` instead if possible)
|
||||
* :c:member:`~PyTypeObject.tp_dictoffset`
|
||||
(use :c:macro:`Py_TPFLAGS_MANAGED_DICT` instead if possible)
|
||||
* :c:member:`~PyTypeObject.tp_vectorcall_offset`
|
||||
(use ``"__vectorcalloffset__"`` in
|
||||
:ref:`PyMemberDef <pymemberdef-offsets>`)
|
||||
.. c:member:: void *pfunc
|
||||
|
||||
If it is not possible to switch to a ``MANAGED`` flag (for example,
|
||||
for vectorcall or to support Python older than 3.12), specify the
|
||||
offset in :c:data:`Py_tp_members`.
|
||||
See :ref:`PyMemberDef documentation <pymemberdef-offsets>`
|
||||
for details.
|
||||
|
||||
The following internal fields cannot be set at all when creating a heap
|
||||
type:
|
||||
|
||||
* :c:member:`~PyTypeObject.tp_dict`,
|
||||
:c:member:`~PyTypeObject.tp_mro`,
|
||||
:c:member:`~PyTypeObject.tp_cache`,
|
||||
:c:member:`~PyTypeObject.tp_subclasses`, and
|
||||
:c:member:`~PyTypeObject.tp_weaklist`.
|
||||
|
||||
Setting :c:data:`Py_tp_bases` or :c:data:`Py_tp_base` may be
|
||||
problematic on some platforms.
|
||||
To avoid issues, use the *bases* argument of
|
||||
:c:func:`PyType_FromSpecWithBases` instead.
|
||||
|
||||
.. versionchanged:: 3.9
|
||||
Slots in :c:type:`PyBufferProcs` may be set in the unlimited API.
|
||||
|
||||
.. versionchanged:: 3.11
|
||||
:c:member:`~PyBufferProcs.bf_getbuffer` and
|
||||
:c:member:`~PyBufferProcs.bf_releasebuffer` are now available
|
||||
under the :ref:`limited API <limited-c-api>`.
|
||||
|
||||
.. versionchanged:: 3.14
|
||||
The field :c:member:`~PyTypeObject.tp_vectorcall` can now be set
|
||||
using :c:data:`Py_tp_vectorcall`. See the field's documentation
|
||||
for details.
|
||||
|
||||
.. c:member:: void *pfunc
|
||||
|
||||
The desired value of the slot. In most cases, this is a pointer
|
||||
to a function.
|
||||
|
||||
*pfunc* values may not be ``NULL``, except for the following slots:
|
||||
|
||||
* :c:data:`Py_tp_doc`
|
||||
* :c:data:`Py_tp_token` (for clarity, prefer :c:data:`Py_TP_USE_SPEC`
|
||||
rather than ``NULL``)
|
||||
|
||||
|
||||
.. c:macro:: Py_tp_token
|
||||
|
||||
A :c:member:`~PyType_Slot.slot` that records a static memory layout ID
|
||||
for a class.
|
||||
|
||||
If the :c:type:`PyType_Spec` of the class is statically
|
||||
allocated, the token can be set to the spec using the special value
|
||||
:c:data:`Py_TP_USE_SPEC`:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static PyType_Slot foo_slots[] = {
|
||||
{Py_tp_token, Py_TP_USE_SPEC},
|
||||
|
||||
It can also be set to an arbitrary pointer, but you must ensure that:
|
||||
|
||||
* The pointer outlives the class, so it's not reused for something else
|
||||
while the class exists.
|
||||
* It "belongs" to the extension module where the class lives, so it will not
|
||||
clash with other extensions.
|
||||
|
||||
Use :c:func:`PyType_GetBaseByToken` to check if a class's superclass has
|
||||
a given token -- that is, check whether the memory layout is compatible.
|
||||
|
||||
To get the token for a given class (without considering superclasses),
|
||||
use :c:func:`PyType_GetSlot` with ``Py_tp_token``.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
.. c:namespace:: NULL
|
||||
|
||||
.. c:macro:: Py_TP_USE_SPEC
|
||||
|
||||
Used as a value with :c:data:`Py_tp_token` to set the token to the
|
||||
class's :c:type:`PyType_Spec`.
|
||||
Expands to ``NULL``.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
Corresponds to :c:member:`PySlot.sl_ptr`.
|
||||
|
||||
@@ -555,6 +555,9 @@ and :c:data:`PyType_Type` effectively act as defaults.)
|
||||
|
||||
.. c:member:: const char* PyTypeObject.tp_name
|
||||
|
||||
See :c:macro:`Py_tp_name` for the corresponding
|
||||
:c:member:`Slot ID <PySlot.sl_id>`.
|
||||
|
||||
Pointer to a NUL-terminated string containing the name of the type. For types
|
||||
that are accessible as module globals, the string should be the full module
|
||||
name, followed by a dot, followed by the type name; for built-in types, it
|
||||
@@ -594,6 +597,10 @@ and :c:data:`PyType_Type` effectively act as defaults.)
|
||||
|
||||
These fields allow calculating the size in bytes of instances of the type.
|
||||
|
||||
See :c:macro:`Py_tp_basicsize`, :c:macro:`Py_tp_extra_basicsize` and
|
||||
:c:macro:`Py_tp_itemsize` for the corresponding
|
||||
:c:member:`Slot IDs <PySlot.sl_id>`.
|
||||
|
||||
There are two kinds of types: types with fixed-length instances have a zero
|
||||
:c:member:`!tp_itemsize` field, types with variable-length instances have a non-zero
|
||||
:c:member:`!tp_itemsize` field. For a type with fixed-length instances, all
|
||||
@@ -1133,6 +1140,9 @@ and :c:data:`PyType_Type` effectively act as defaults.)
|
||||
|
||||
.. c:member:: unsigned long PyTypeObject.tp_flags
|
||||
|
||||
See :c:macro:`Py_tp_flags` for the corresponding
|
||||
:c:member:`Slot ID <PySlot.sl_id>`.
|
||||
|
||||
This field is a bit mask of various flags. Some flags indicate variant
|
||||
semantics for certain situations; others are used to indicate that certain
|
||||
fields in the type object (or in the extension structures referenced via
|
||||
|
||||
Generated
+26
@@ -676,6 +676,19 @@ func,PySlice_GetIndicesEx,3.2,,
|
||||
func,PySlice_New,3.2,,
|
||||
data,PySlice_Type,3.2,,
|
||||
func,PySlice_Unpack,3.7,,
|
||||
type,PySlot,3.15,,full-abi
|
||||
macro,PySlot_DATA,3.15,,
|
||||
macro,PySlot_END,3.15,,
|
||||
macro,PySlot_FUNC,3.15,,
|
||||
macro,PySlot_INT64,3.15,,
|
||||
macro,PySlot_INTPTR,3.15,,
|
||||
macro,PySlot_OPTIONAL,3.15,,
|
||||
macro,PySlot_PTR,3.15,,
|
||||
macro,PySlot_PTR_STATIC,3.15,,
|
||||
macro,PySlot_SIZE,3.15,,
|
||||
macro,PySlot_STATIC,3.15,,
|
||||
macro,PySlot_STATIC_DATA,3.15,,
|
||||
macro,PySlot_UINT64,3.15,,
|
||||
func,PyState_AddModule,3.3,,
|
||||
func,PyState_FindModule,3.2,,
|
||||
func,PyState_RemoveModule,3.3,,
|
||||
@@ -755,6 +768,7 @@ func,PyType_ClearCache,3.2,,
|
||||
func,PyType_Freeze,3.14,,
|
||||
func,PyType_FromMetaclass,3.12,,
|
||||
func,PyType_FromModuleAndSpec,3.10,,
|
||||
func,PyType_FromSlots,3.15,,
|
||||
func,PyType_FromSpec,3.2,,
|
||||
func,PyType_FromSpecWithBases,3.3,,
|
||||
func,PyType_GenericAlloc,3.2,,
|
||||
@@ -1018,6 +1032,7 @@ macro,Py_mod_gil,3.13,,
|
||||
macro,Py_mod_methods,3.15,,
|
||||
macro,Py_mod_multiple_interpreters,3.12,,
|
||||
macro,Py_mod_name,3.15,,
|
||||
macro,Py_mod_slots,3.15,,
|
||||
macro,Py_mod_state_clear,3.15,,
|
||||
macro,Py_mod_state_free,3.15,,
|
||||
macro,Py_mod_state_size,3.15,,
|
||||
@@ -1061,6 +1076,9 @@ macro,Py_nb_rshift,3.2,,
|
||||
macro,Py_nb_subtract,3.2,,
|
||||
macro,Py_nb_true_divide,3.2,,
|
||||
macro,Py_nb_xor,3.2,,
|
||||
macro,Py_slot_end,3.15,,
|
||||
macro,Py_slot_invalid,3.15,,
|
||||
macro,Py_slot_subslots,3.15,,
|
||||
macro,Py_sq_ass_item,3.2,,
|
||||
macro,Py_sq_concat,3.2,,
|
||||
macro,Py_sq_contains,3.2,,
|
||||
@@ -1073,6 +1091,7 @@ type,Py_ssize_t,3.2,,
|
||||
macro,Py_tp_alloc,3.2,,
|
||||
macro,Py_tp_base,3.2,,
|
||||
macro,Py_tp_bases,3.2,,
|
||||
macro,Py_tp_basicsize,3.15,,
|
||||
macro,Py_tp_call,3.2,,
|
||||
macro,Py_tp_clear,3.2,,
|
||||
macro,Py_tp_dealloc,3.2,,
|
||||
@@ -1080,7 +1099,9 @@ macro,Py_tp_del,3.2,,
|
||||
macro,Py_tp_descr_get,3.2,,
|
||||
macro,Py_tp_descr_set,3.2,,
|
||||
macro,Py_tp_doc,3.2,,
|
||||
macro,Py_tp_extra_basicsize,3.15,,
|
||||
macro,Py_tp_finalize,3.5,,
|
||||
macro,Py_tp_flags,3.15,,
|
||||
macro,Py_tp_free,3.2,,
|
||||
macro,Py_tp_getattr,3.2,,
|
||||
macro,Py_tp_getattro,3.2,,
|
||||
@@ -1088,15 +1109,20 @@ macro,Py_tp_getset,3.2,,
|
||||
macro,Py_tp_hash,3.2,,
|
||||
macro,Py_tp_init,3.2,,
|
||||
macro,Py_tp_is_gc,3.2,,
|
||||
macro,Py_tp_itemsize,3.15,,
|
||||
macro,Py_tp_iter,3.2,,
|
||||
macro,Py_tp_iternext,3.2,,
|
||||
macro,Py_tp_members,3.2,,
|
||||
macro,Py_tp_metaclass,3.15,,
|
||||
macro,Py_tp_methods,3.2,,
|
||||
macro,Py_tp_module,3.15,,
|
||||
macro,Py_tp_name,3.15,,
|
||||
macro,Py_tp_new,3.2,,
|
||||
macro,Py_tp_repr,3.2,,
|
||||
macro,Py_tp_richcompare,3.2,,
|
||||
macro,Py_tp_setattr,3.2,,
|
||||
macro,Py_tp_setattro,3.2,,
|
||||
macro,Py_tp_slots,3.15,,
|
||||
macro,Py_tp_str,3.2,,
|
||||
macro,Py_tp_token,3.14,,
|
||||
macro,Py_tp_traverse,3.2,,
|
||||
|
||||
@@ -259,21 +259,25 @@ Rather than ``NULL``, the export hook should return the information needed to
|
||||
create a module.
|
||||
Let's start with the basics: the name and docstring.
|
||||
|
||||
The information should be defined in a ``static`` array of
|
||||
:c:type:`PyModuleDef_Slot` entries, which are essentially key-value pairs.
|
||||
The information should be defined in an array of
|
||||
:c:type:`PySlot` entries, which are essentially key-value pairs.
|
||||
Define this array just before your export hook:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
PyABIInfo_VAR(abi_info);
|
||||
|
||||
static PyModuleDef_Slot spam_slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_name, "spam"},
|
||||
{Py_mod_doc, "A wonderful module with an example function"},
|
||||
{0, NULL}
|
||||
static PySlot spam_slots[] = {
|
||||
PySlot_STATIC_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_STATIC_DATA(Py_mod_name, "spam"),
|
||||
PySlot_STATIC_DATA(Py_mod_doc, "A wonderful module with an example function"),
|
||||
PySlot_END
|
||||
};
|
||||
|
||||
The :c:macro:`PySlot_STATIC_DATA` macro is used when the slot value
|
||||
(here: ``&abi_info``, ``"spam"``, and the docstring) is a pointer to constant,
|
||||
statically allocated data.
|
||||
|
||||
The ``PyABIInfo_VAR(abi_info);`` macro and the :c:data:`Py_mod_abi` slot
|
||||
are a bit of boilerplate that helps prevent extensions compiled for
|
||||
a different version of Python from crashing the interpreter.
|
||||
@@ -281,7 +285,8 @@ a different version of Python from crashing the interpreter.
|
||||
For both :c:data:`Py_mod_name` and :c:data:`Py_mod_doc`, the values are C
|
||||
strings -- that is, NUL-terminated, UTF-8 encoded byte arrays.
|
||||
|
||||
Note the zero-filled sentinel entry at the end.
|
||||
Note ``PySlot_END`` sentinel entry at the end.
|
||||
This marks the end of the array.
|
||||
If you forget it, you'll trigger undefined behavior.
|
||||
|
||||
The array is defined as ``static`` -- that is, not visible outside this ``.c`` file.
|
||||
|
||||
@@ -37,12 +37,12 @@ static PyMethodDef spam_methods[] = {
|
||||
|
||||
PyABIInfo_VAR(abi_info);
|
||||
|
||||
static PyModuleDef_Slot spam_slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_name, "spam"},
|
||||
{Py_mod_doc, "A wonderful module with an example function"},
|
||||
{Py_mod_methods, spam_methods},
|
||||
{0, NULL}
|
||||
static PySlot spam_slots[] = {
|
||||
PySlot_STATIC_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_STATIC_DATA(Py_mod_name, "spam"),
|
||||
PySlot_STATIC_DATA(Py_mod_doc, "A wonderful module with an example function"),
|
||||
PySlot_STATIC_DATA(Py_mod_methods, spam_methods),
|
||||
PySlot_END
|
||||
};
|
||||
|
||||
/// Export hook prototype
|
||||
|
||||
@@ -88,6 +88,7 @@ Summary -- Release highlights
|
||||
* :pep:`782`: :ref:`A new PyBytesWriter C API to create a Python bytes object
|
||||
<whatsnew315-pybyteswriter>`
|
||||
* :pep:`803`: :ref:`Stable ABI for Free-Threaded Builds <whatsnew315-abi3t>`
|
||||
* :pep:`820`: :ref:`PySlot: Unified slot system for the C API <whatsnew315-pyslot>`
|
||||
* :ref:`The JIT compiler has been significantly upgraded <whatsnew315-jit>`
|
||||
* :ref:`Improved error messages <whatsnew315-improved-error-messages>`
|
||||
* :ref:`The official Windows 64-bit binaries now use the tail-calling interpreter
|
||||
@@ -2213,6 +2214,43 @@ New features
|
||||
It should only be used for debugging.
|
||||
(Contributed by Victor Stinner in :gh:`141070`.)
|
||||
|
||||
.. _whatsnew315-pyslot:
|
||||
|
||||
* Implement :pep:`820`: ``PySlot`` -- Unified slot system for the C API.
|
||||
See :ref:`capi-slots` for documentation.
|
||||
|
||||
This adds:
|
||||
|
||||
* The :c:type:`PySlot` struct;
|
||||
* the :c:func:`PyType_FromSlots` function;
|
||||
* new slot IDs: :c:macro:`Py_slot_end`, :c:macro:`Py_slot_invalid`;
|
||||
:c:macro:`Py_slot_subslots`, :c:macro:`Py_tp_slots`
|
||||
:c:macro:`Py_mod_slots`;
|
||||
:c:macro:`Py_tp_name`, :c:macro:`Py_tp_basicsize`,
|
||||
:c:macro:`Py_tp_extra_basicsize`, :c:macro:`Py_tp_itemsize`,
|
||||
:c:macro:`Py_tp_flags`, :c:macro:`Py_tp_metaclass`,
|
||||
:c:macro:`Py_tp_module`, :c:macro:`Py_tp_flags`;
|
||||
* convenience macros: :c:macro:`PySlot_DATA`, :c:macro:`PySlot_FUNC`,
|
||||
:c:macro:`PySlot_SIZE` :c:macro:`PySlot_INT64`, :c:macro:`PySlot_UINT64`,
|
||||
:c:macro:`PySlot_STATIC_DATA`, :c:macro:`PySlot_END`,
|
||||
:c:macro:`PySlot_PTR`, :c:macro:`PySlot_PTR_STATIC`.
|
||||
|
||||
The :c:func:`PyModule_FromSlotsAndSpec` function and
|
||||
``PyModExport`` :ref:`module export hook <extension-export-hook>` also
|
||||
use the new :c:type:`!PySlot` struct.
|
||||
|
||||
These following functions are :term:`soft deprecated`:
|
||||
|
||||
* :c:func:`PyType_FromSpec`
|
||||
* :c:func:`PyType_FromSpecWithBases`
|
||||
* :c:func:`PyType_FromModuleAndSpec`
|
||||
* :c:func:`PyType_FromMetaclass`
|
||||
* :c:func:`PyModule_FromDefAndSpec`
|
||||
* :c:func:`PyModule_FromDefAndSpec2`
|
||||
* :c:func:`PyModule_ExecDef`
|
||||
|
||||
(Contributed by Petr Viktorin in :gh:`149044`.)
|
||||
|
||||
* Add :c:func:`PyUnstable_ThreadState_SetStackProtection` and
|
||||
:c:func:`PyUnstable_ThreadState_ResetStackProtection` functions to set
|
||||
the stack protection base address and stack protection size of a Python
|
||||
|
||||
+2
-1
@@ -79,7 +79,8 @@ __pragma(warning(disable: 4201))
|
||||
#include "object.h"
|
||||
#include "refcount.h"
|
||||
#include "objimpl.h"
|
||||
#include "typeslots.h"
|
||||
#include "slots.h"
|
||||
#include "slots_generated.h"
|
||||
#include "pyhash.h"
|
||||
#include "cpython/pydebug.h"
|
||||
#include "bytearrayobject.h"
|
||||
|
||||
+1
-1
@@ -103,7 +103,7 @@
|
||||
#define PyMODINIT_FUNC _PyINIT_FUNC_DECLSPEC PyObject*
|
||||
#endif
|
||||
#ifndef PyMODEXPORT_FUNC
|
||||
#define PyMODEXPORT_FUNC _PyINIT_FUNC_DECLSPEC PyModuleDef_Slot*
|
||||
#define PyMODEXPORT_FUNC _PyINIT_FUNC_DECLSPEC PySlot*
|
||||
#endif
|
||||
|
||||
#endif /* Py_EXPORTS_H */
|
||||
|
||||
@@ -124,7 +124,7 @@ extern void _Py_ext_module_loader_result_apply_error(
|
||||
|
||||
/* The module init function. */
|
||||
typedef PyObject *(*PyModInitFunction)(void);
|
||||
typedef PyModuleDef_Slot *(*PyModExportFunction)(void);
|
||||
typedef PySlot *(*PyModExportFunction)(void);
|
||||
#ifdef HAVE_DYNAMIC_LOADING
|
||||
extern int _PyImport_GetModuleExportHooks(
|
||||
struct _Py_ext_module_loader_info *info,
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
#ifndef _Py_PYCORE_SLOTS_H
|
||||
#define _Py_PYCORE_SLOTS_H
|
||||
|
||||
#ifndef Py_BUILD_CORE
|
||||
# error "this header requires Py_BUILD_CORE define"
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Slot data type */
|
||||
typedef enum _PySlot_DTYPE {
|
||||
_PySlot_DTYPE_VOID,
|
||||
_PySlot_DTYPE_FUNC,
|
||||
_PySlot_DTYPE_PTR,
|
||||
_PySlot_DTYPE_SIZE,
|
||||
_PySlot_DTYPE_INT64,
|
||||
_PySlot_DTYPE_UINT64,
|
||||
}_PySlot_DTYPE;
|
||||
|
||||
/* Slot kind, used to identify:
|
||||
* - the thing the slot initializes (type/module/special)
|
||||
* - the struct type (PySlot/PyType_Slot/PyModuleDef_Slot)
|
||||
*/
|
||||
typedef enum _PySlot_KIND {
|
||||
_PySlot_KIND_TYPE,
|
||||
_PySlot_KIND_MOD,
|
||||
_PySlot_KIND_COMPAT,
|
||||
_PySlot_KIND_SLOT,
|
||||
} _PySlot_KIND;
|
||||
|
||||
typedef enum _PySlot_PROBLEM_HANDLING {
|
||||
_PySlot_PROBLEM_ALLOW,
|
||||
_PySlot_PROBLEM_DEPRECATED,
|
||||
_PySlot_PROBLEM_REJECT,
|
||||
} _PySlot_PROBLEM_HANDLING;
|
||||
|
||||
PyAPI_DATA(const char *const) _PySlot_names[];
|
||||
|
||||
#define _PySlot_MAX_NESTING 5
|
||||
|
||||
/* State for one nesting level of a slots iterator */
|
||||
typedef struct _PySlotIterator_state {
|
||||
union {
|
||||
// tagged by slot_struct_kind:
|
||||
const PySlot *slot; // with _PySlot_KIND_SLOT
|
||||
const PyType_Slot *tp_slot; // with _PySlot_KIND_TYPE
|
||||
const PyModuleDef_Slot *mod_slot; // with _PySlot_KIND_MOD
|
||||
const void *any_slot;
|
||||
};
|
||||
_PySlot_KIND slot_struct_kind;
|
||||
} _PySlotIterator_state;
|
||||
|
||||
#define _PySlot_SEEN_ENTRY_BITS (8 * sizeof(unsigned int))
|
||||
|
||||
/* State for a slots iterator */
|
||||
typedef struct {
|
||||
_PySlotIterator_state *state;
|
||||
_PySlotIterator_state states[_PySlot_MAX_NESTING];
|
||||
unsigned int seen[_Py_slot_COUNT / _PySlot_SEEN_ENTRY_BITS + 1];
|
||||
_PySlot_KIND kind;
|
||||
uint8_t recursion_level;
|
||||
bool is_at_end :1;
|
||||
bool is_first_run :1;
|
||||
|
||||
// Name of the object (type/module) being defined, NULL if unknown.
|
||||
// Must be set by the callers as soon as it's known.
|
||||
const char *name;
|
||||
|
||||
/* Output information: */
|
||||
|
||||
// The slot. Always a copy; may be modified by caller of the iterator.
|
||||
PySlot current;
|
||||
|
||||
} _PySlotIterator;
|
||||
|
||||
/* Initialize an iterator using a PySlot array */
|
||||
PyAPI_FUNC(void)
|
||||
_PySlotIterator_Init(_PySlotIterator *it, const PySlot *slots,
|
||||
_PySlot_KIND result_kind);
|
||||
|
||||
/* Initialize an iterator using a legacy slot array */
|
||||
PyAPI_FUNC(void)
|
||||
_PySlotIterator_InitLegacy(_PySlotIterator *it, const void *slots,
|
||||
_PySlot_KIND kind);
|
||||
|
||||
/* Reset a *successfully exhausted* iterator to the beginning.
|
||||
* The *slots* must be the same as for the previous
|
||||
* `_PySlotIterator_InitWithKind` call.
|
||||
* (Unlike creating a new iterator, we can skip some validation after Rewind.)
|
||||
*/
|
||||
PyAPI_FUNC(void) _PySlotIterator_Rewind(_PySlotIterator *it, const void *slots);
|
||||
|
||||
/* Iteration function.
|
||||
*
|
||||
* Return false at the end (when successfully exhausted).
|
||||
* Otherwise (even on error), fill output information in `it` and return true.
|
||||
*
|
||||
* On error, set an exception and set `it->current.sl_id` to `Py_slot_invalid`.
|
||||
*/
|
||||
PyAPI_FUNC(bool) _PySlotIterator_Next(_PySlotIterator *it);
|
||||
|
||||
/* Return 1 if given slot was "seen" by an earlier _PySlotIterator_Next call.
|
||||
* (This state is not reset by rewinding.)
|
||||
*/
|
||||
PyAPI_FUNC(bool) _PySlotIterator_SawSlot(_PySlotIterator *, int);
|
||||
|
||||
static inline const char *
|
||||
_PySlot_GetName(uint16_t id)
|
||||
{
|
||||
if (id >= _Py_slot_COUNT) {
|
||||
return "<unknown_slot>";
|
||||
}
|
||||
if (id == Py_slot_invalid) {
|
||||
return "Py_slot_invalid";
|
||||
}
|
||||
return _PySlot_names[id];
|
||||
}
|
||||
|
||||
static inline void
|
||||
_PySlot_err_bad_slot(char *kind, uint16_t id)
|
||||
{
|
||||
if (id < _Py_slot_COUNT) {
|
||||
PyErr_Format(PyExc_SystemError, "invalid %s slot %d (%s)",
|
||||
kind, (unsigned int)id, _PySlot_names[id]);
|
||||
}
|
||||
else if (id == Py_slot_invalid) {
|
||||
PyErr_Format(PyExc_SystemError, "invalid slot (Py_slot_invalid, %u)",
|
||||
(unsigned int)id);
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_SystemError, "unknown %s slot ID %u",
|
||||
kind, (unsigned int)id);
|
||||
}
|
||||
}
|
||||
|
||||
#include "internal/pycore_slots_generated.h"
|
||||
|
||||
#endif // _Py_PYCORE_SLOTS_H
|
||||
+958
@@ -0,0 +1,958 @@
|
||||
/* Generated by Tools/build/generate_slots.py */
|
||||
|
||||
#ifndef _PY_HAVE_INTERNAL_SLOTS_GENERATED_H
|
||||
#define _PY_HAVE_INTERNAL_SLOTS_GENERATED_H
|
||||
|
||||
static inline uint16_t
|
||||
_PySlot_resolve_type_slot(uint16_t slot_id)
|
||||
{
|
||||
switch (slot_id) {
|
||||
case 1:
|
||||
return Py_bf_getbuffer;
|
||||
case 2:
|
||||
return Py_bf_releasebuffer;
|
||||
case 3:
|
||||
return Py_mp_ass_subscript;
|
||||
case 4:
|
||||
return Py_mp_length;
|
||||
case Py_slot_end:
|
||||
case Py_mp_subscript:
|
||||
case Py_nb_absolute:
|
||||
case Py_nb_add:
|
||||
case Py_nb_and:
|
||||
case Py_nb_bool:
|
||||
case Py_nb_divmod:
|
||||
case Py_nb_float:
|
||||
case Py_nb_floor_divide:
|
||||
case Py_nb_index:
|
||||
case Py_nb_inplace_add:
|
||||
case Py_nb_inplace_and:
|
||||
case Py_nb_inplace_floor_divide:
|
||||
case Py_nb_inplace_lshift:
|
||||
case Py_nb_inplace_multiply:
|
||||
case Py_nb_inplace_or:
|
||||
case Py_nb_inplace_power:
|
||||
case Py_nb_inplace_remainder:
|
||||
case Py_nb_inplace_rshift:
|
||||
case Py_nb_inplace_subtract:
|
||||
case Py_nb_inplace_true_divide:
|
||||
case Py_nb_inplace_xor:
|
||||
case Py_nb_int:
|
||||
case Py_nb_invert:
|
||||
case Py_nb_lshift:
|
||||
case Py_nb_multiply:
|
||||
case Py_nb_negative:
|
||||
case Py_nb_or:
|
||||
case Py_nb_positive:
|
||||
case Py_nb_power:
|
||||
case Py_nb_remainder:
|
||||
case Py_nb_rshift:
|
||||
case Py_nb_subtract:
|
||||
case Py_nb_true_divide:
|
||||
case Py_nb_xor:
|
||||
case Py_sq_ass_item:
|
||||
case Py_sq_concat:
|
||||
case Py_sq_contains:
|
||||
case Py_sq_inplace_concat:
|
||||
case Py_sq_inplace_repeat:
|
||||
case Py_sq_item:
|
||||
case Py_sq_length:
|
||||
case Py_sq_repeat:
|
||||
case Py_tp_alloc:
|
||||
case Py_tp_base:
|
||||
case Py_tp_bases:
|
||||
case Py_tp_call:
|
||||
case Py_tp_clear:
|
||||
case Py_tp_dealloc:
|
||||
case Py_tp_del:
|
||||
case Py_tp_descr_get:
|
||||
case Py_tp_descr_set:
|
||||
case Py_tp_doc:
|
||||
case Py_tp_getattr:
|
||||
case Py_tp_getattro:
|
||||
case Py_tp_hash:
|
||||
case Py_tp_init:
|
||||
case Py_tp_is_gc:
|
||||
case Py_tp_iter:
|
||||
case Py_tp_iternext:
|
||||
case Py_tp_methods:
|
||||
case Py_tp_new:
|
||||
case Py_tp_repr:
|
||||
case Py_tp_richcompare:
|
||||
case Py_tp_setattr:
|
||||
case Py_tp_setattro:
|
||||
case Py_tp_str:
|
||||
case Py_tp_traverse:
|
||||
case Py_tp_members:
|
||||
case Py_tp_getset:
|
||||
case Py_tp_free:
|
||||
case Py_nb_matrix_multiply:
|
||||
case Py_nb_inplace_matrix_multiply:
|
||||
case Py_am_await:
|
||||
case Py_am_aiter:
|
||||
case Py_am_anext:
|
||||
case Py_tp_finalize:
|
||||
case Py_am_send:
|
||||
case Py_tp_vectorcall:
|
||||
case Py_tp_token:
|
||||
case Py_bf_getbuffer:
|
||||
case Py_bf_releasebuffer:
|
||||
case Py_mp_ass_subscript:
|
||||
case Py_mp_length:
|
||||
case Py_slot_subslots:
|
||||
case Py_tp_slots:
|
||||
case Py_tp_name:
|
||||
case Py_tp_basicsize:
|
||||
case Py_tp_extra_basicsize:
|
||||
case Py_tp_itemsize:
|
||||
case Py_tp_flags:
|
||||
case Py_tp_metaclass:
|
||||
case Py_tp_module:
|
||||
return slot_id;
|
||||
default:
|
||||
return Py_slot_invalid;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint16_t
|
||||
_PySlot_resolve_mod_slot(uint16_t slot_id)
|
||||
{
|
||||
switch (slot_id) {
|
||||
case 1:
|
||||
return Py_mod_create;
|
||||
case 2:
|
||||
return Py_mod_exec;
|
||||
case 3:
|
||||
return Py_mod_multiple_interpreters;
|
||||
case 4:
|
||||
return Py_mod_gil;
|
||||
case Py_slot_end:
|
||||
case Py_mod_create:
|
||||
case Py_mod_exec:
|
||||
case Py_mod_multiple_interpreters:
|
||||
case Py_mod_gil:
|
||||
case Py_slot_subslots:
|
||||
case Py_mod_slots:
|
||||
case Py_mod_name:
|
||||
case Py_mod_doc:
|
||||
case Py_mod_state_size:
|
||||
case Py_mod_methods:
|
||||
case Py_mod_state_traverse:
|
||||
case Py_mod_state_clear:
|
||||
case Py_mod_state_free:
|
||||
case Py_mod_abi:
|
||||
case Py_mod_token:
|
||||
return slot_id;
|
||||
default:
|
||||
return Py_slot_invalid;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void*
|
||||
_PySlot_type_getslot(PyTypeObject *tp, uint16_t slot_id)
|
||||
{
|
||||
switch (slot_id) {
|
||||
case Py_mp_subscript:
|
||||
if (!(tp->tp_as_mapping)) return NULL;
|
||||
return (void*)tp->tp_as_mapping->mp_subscript;
|
||||
case Py_nb_absolute:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_absolute;
|
||||
case Py_nb_add:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_add;
|
||||
case Py_nb_and:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_and;
|
||||
case Py_nb_bool:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_bool;
|
||||
case Py_nb_divmod:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_divmod;
|
||||
case Py_nb_float:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_float;
|
||||
case Py_nb_floor_divide:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_floor_divide;
|
||||
case Py_nb_index:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_index;
|
||||
case Py_nb_inplace_add:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_inplace_add;
|
||||
case Py_nb_inplace_and:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_inplace_and;
|
||||
case Py_nb_inplace_floor_divide:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_inplace_floor_divide;
|
||||
case Py_nb_inplace_lshift:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_inplace_lshift;
|
||||
case Py_nb_inplace_multiply:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_inplace_multiply;
|
||||
case Py_nb_inplace_or:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_inplace_or;
|
||||
case Py_nb_inplace_power:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_inplace_power;
|
||||
case Py_nb_inplace_remainder:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_inplace_remainder;
|
||||
case Py_nb_inplace_rshift:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_inplace_rshift;
|
||||
case Py_nb_inplace_subtract:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_inplace_subtract;
|
||||
case Py_nb_inplace_true_divide:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_inplace_true_divide;
|
||||
case Py_nb_inplace_xor:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_inplace_xor;
|
||||
case Py_nb_int:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_int;
|
||||
case Py_nb_invert:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_invert;
|
||||
case Py_nb_lshift:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_lshift;
|
||||
case Py_nb_multiply:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_multiply;
|
||||
case Py_nb_negative:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_negative;
|
||||
case Py_nb_or:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_or;
|
||||
case Py_nb_positive:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_positive;
|
||||
case Py_nb_power:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_power;
|
||||
case Py_nb_remainder:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_remainder;
|
||||
case Py_nb_rshift:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_rshift;
|
||||
case Py_nb_subtract:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_subtract;
|
||||
case Py_nb_true_divide:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_true_divide;
|
||||
case Py_nb_xor:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_xor;
|
||||
case Py_sq_ass_item:
|
||||
if (!(tp->tp_as_sequence)) return NULL;
|
||||
return (void*)tp->tp_as_sequence->sq_ass_item;
|
||||
case Py_sq_concat:
|
||||
if (!(tp->tp_as_sequence)) return NULL;
|
||||
return (void*)tp->tp_as_sequence->sq_concat;
|
||||
case Py_sq_contains:
|
||||
if (!(tp->tp_as_sequence)) return NULL;
|
||||
return (void*)tp->tp_as_sequence->sq_contains;
|
||||
case Py_sq_inplace_concat:
|
||||
if (!(tp->tp_as_sequence)) return NULL;
|
||||
return (void*)tp->tp_as_sequence->sq_inplace_concat;
|
||||
case Py_sq_inplace_repeat:
|
||||
if (!(tp->tp_as_sequence)) return NULL;
|
||||
return (void*)tp->tp_as_sequence->sq_inplace_repeat;
|
||||
case Py_sq_item:
|
||||
if (!(tp->tp_as_sequence)) return NULL;
|
||||
return (void*)tp->tp_as_sequence->sq_item;
|
||||
case Py_sq_length:
|
||||
if (!(tp->tp_as_sequence)) return NULL;
|
||||
return (void*)tp->tp_as_sequence->sq_length;
|
||||
case Py_sq_repeat:
|
||||
if (!(tp->tp_as_sequence)) return NULL;
|
||||
return (void*)tp->tp_as_sequence->sq_repeat;
|
||||
case Py_tp_alloc:
|
||||
return (void*)tp->tp_alloc;
|
||||
case Py_tp_base:
|
||||
return (void*)tp->tp_base;
|
||||
case Py_tp_bases:
|
||||
return (void*)tp->tp_bases;
|
||||
case Py_tp_call:
|
||||
return (void*)tp->tp_call;
|
||||
case Py_tp_clear:
|
||||
return (void*)tp->tp_clear;
|
||||
case Py_tp_dealloc:
|
||||
return (void*)tp->tp_dealloc;
|
||||
case Py_tp_del:
|
||||
return (void*)tp->tp_del;
|
||||
case Py_tp_descr_get:
|
||||
return (void*)tp->tp_descr_get;
|
||||
case Py_tp_descr_set:
|
||||
return (void*)tp->tp_descr_set;
|
||||
case Py_tp_doc:
|
||||
return (void*)tp->tp_doc;
|
||||
case Py_tp_getattr:
|
||||
return (void*)tp->tp_getattr;
|
||||
case Py_tp_getattro:
|
||||
return (void*)tp->tp_getattro;
|
||||
case Py_tp_hash:
|
||||
return (void*)tp->tp_hash;
|
||||
case Py_tp_init:
|
||||
return (void*)tp->tp_init;
|
||||
case Py_tp_is_gc:
|
||||
return (void*)tp->tp_is_gc;
|
||||
case Py_tp_iter:
|
||||
return (void*)tp->tp_iter;
|
||||
case Py_tp_iternext:
|
||||
return (void*)tp->tp_iternext;
|
||||
case Py_tp_methods:
|
||||
return (void*)tp->tp_methods;
|
||||
case Py_tp_new:
|
||||
return (void*)tp->tp_new;
|
||||
case Py_tp_repr:
|
||||
return (void*)tp->tp_repr;
|
||||
case Py_tp_richcompare:
|
||||
return (void*)tp->tp_richcompare;
|
||||
case Py_tp_setattr:
|
||||
return (void*)tp->tp_setattr;
|
||||
case Py_tp_setattro:
|
||||
return (void*)tp->tp_setattro;
|
||||
case Py_tp_str:
|
||||
return (void*)tp->tp_str;
|
||||
case Py_tp_traverse:
|
||||
return (void*)tp->tp_traverse;
|
||||
case Py_tp_members:
|
||||
return (void*)tp->tp_members;
|
||||
case Py_tp_getset:
|
||||
return (void*)tp->tp_getset;
|
||||
case Py_tp_free:
|
||||
return (void*)tp->tp_free;
|
||||
case Py_nb_matrix_multiply:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_matrix_multiply;
|
||||
case Py_nb_inplace_matrix_multiply:
|
||||
if (!(tp->tp_as_number)) return NULL;
|
||||
return (void*)tp->tp_as_number->nb_inplace_matrix_multiply;
|
||||
case Py_am_await:
|
||||
if (!(tp->tp_as_async)) return NULL;
|
||||
return (void*)tp->tp_as_async->am_await;
|
||||
case Py_am_aiter:
|
||||
if (!(tp->tp_as_async)) return NULL;
|
||||
return (void*)tp->tp_as_async->am_aiter;
|
||||
case Py_am_anext:
|
||||
if (!(tp->tp_as_async)) return NULL;
|
||||
return (void*)tp->tp_as_async->am_anext;
|
||||
case Py_tp_finalize:
|
||||
return (void*)tp->tp_finalize;
|
||||
case Py_am_send:
|
||||
if (!(tp->tp_as_async)) return NULL;
|
||||
return (void*)tp->tp_as_async->am_send;
|
||||
case Py_tp_vectorcall:
|
||||
return (void*)tp->tp_vectorcall;
|
||||
case Py_tp_token:
|
||||
if (!(tp->tp_flags & Py_TPFLAGS_HEAPTYPE)) return NULL;
|
||||
return (void*)((PyHeapTypeObject*)tp)->ht_token;
|
||||
case Py_bf_getbuffer:
|
||||
if (!(tp->tp_as_buffer)) return NULL;
|
||||
return (void*)tp->tp_as_buffer->bf_getbuffer;
|
||||
case Py_bf_releasebuffer:
|
||||
if (!(tp->tp_as_buffer)) return NULL;
|
||||
return (void*)tp->tp_as_buffer->bf_releasebuffer;
|
||||
case Py_mp_ass_subscript:
|
||||
if (!(tp->tp_as_mapping)) return NULL;
|
||||
return (void*)tp->tp_as_mapping->mp_ass_subscript;
|
||||
case Py_mp_length:
|
||||
if (!(tp->tp_as_mapping)) return NULL;
|
||||
return (void*)tp->tp_as_mapping->mp_length;
|
||||
}
|
||||
_PySlot_err_bad_slot("PyType_GetSlot", slot_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void
|
||||
_PySlot_heaptype_apply_field_slot(PyHeapTypeObject *ht, PySlot slot)
|
||||
{
|
||||
switch (slot.sl_id) {
|
||||
case Py_mp_subscript:
|
||||
ht->as_mapping.mp_subscript = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_absolute:
|
||||
ht->as_number.nb_absolute = (unaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_add:
|
||||
ht->as_number.nb_add = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_and:
|
||||
ht->as_number.nb_and = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_bool:
|
||||
ht->as_number.nb_bool = (inquiry)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_divmod:
|
||||
ht->as_number.nb_divmod = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_float:
|
||||
ht->as_number.nb_float = (unaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_floor_divide:
|
||||
ht->as_number.nb_floor_divide = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_index:
|
||||
ht->as_number.nb_index = (unaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_inplace_add:
|
||||
ht->as_number.nb_inplace_add = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_inplace_and:
|
||||
ht->as_number.nb_inplace_and = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_inplace_floor_divide:
|
||||
ht->as_number.nb_inplace_floor_divide = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_inplace_lshift:
|
||||
ht->as_number.nb_inplace_lshift = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_inplace_multiply:
|
||||
ht->as_number.nb_inplace_multiply = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_inplace_or:
|
||||
ht->as_number.nb_inplace_or = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_inplace_power:
|
||||
ht->as_number.nb_inplace_power = (ternaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_inplace_remainder:
|
||||
ht->as_number.nb_inplace_remainder = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_inplace_rshift:
|
||||
ht->as_number.nb_inplace_rshift = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_inplace_subtract:
|
||||
ht->as_number.nb_inplace_subtract = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_inplace_true_divide:
|
||||
ht->as_number.nb_inplace_true_divide = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_inplace_xor:
|
||||
ht->as_number.nb_inplace_xor = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_int:
|
||||
ht->as_number.nb_int = (unaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_invert:
|
||||
ht->as_number.nb_invert = (unaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_lshift:
|
||||
ht->as_number.nb_lshift = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_multiply:
|
||||
ht->as_number.nb_multiply = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_negative:
|
||||
ht->as_number.nb_negative = (unaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_or:
|
||||
ht->as_number.nb_or = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_positive:
|
||||
ht->as_number.nb_positive = (unaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_power:
|
||||
ht->as_number.nb_power = (ternaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_remainder:
|
||||
ht->as_number.nb_remainder = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_rshift:
|
||||
ht->as_number.nb_rshift = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_subtract:
|
||||
ht->as_number.nb_subtract = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_true_divide:
|
||||
ht->as_number.nb_true_divide = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_xor:
|
||||
ht->as_number.nb_xor = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_sq_ass_item:
|
||||
ht->as_sequence.sq_ass_item = (ssizeobjargproc)slot.sl_func;
|
||||
break;
|
||||
case Py_sq_concat:
|
||||
ht->as_sequence.sq_concat = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_sq_contains:
|
||||
ht->as_sequence.sq_contains = (objobjproc)slot.sl_func;
|
||||
break;
|
||||
case Py_sq_inplace_concat:
|
||||
ht->as_sequence.sq_inplace_concat = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_sq_inplace_repeat:
|
||||
ht->as_sequence.sq_inplace_repeat = (ssizeargfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_sq_item:
|
||||
ht->as_sequence.sq_item = (ssizeargfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_sq_length:
|
||||
ht->as_sequence.sq_length = (lenfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_sq_repeat:
|
||||
ht->as_sequence.sq_repeat = (ssizeargfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_alloc:
|
||||
ht->ht_type.tp_alloc = (allocfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_base:
|
||||
ht->ht_type.tp_base = slot.sl_ptr;
|
||||
break;
|
||||
case Py_tp_bases:
|
||||
ht->ht_type.tp_bases = slot.sl_ptr;
|
||||
break;
|
||||
case Py_tp_call:
|
||||
ht->ht_type.tp_call = (ternaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_clear:
|
||||
ht->ht_type.tp_clear = (inquiry)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_dealloc:
|
||||
ht->ht_type.tp_dealloc = (destructor)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_del:
|
||||
ht->ht_type.tp_del = (destructor)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_descr_get:
|
||||
ht->ht_type.tp_descr_get = (descrgetfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_descr_set:
|
||||
ht->ht_type.tp_descr_set = (descrsetfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_doc:
|
||||
ht->ht_type.tp_doc = slot.sl_ptr;
|
||||
break;
|
||||
case Py_tp_getattr:
|
||||
ht->ht_type.tp_getattr = (getattrfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_getattro:
|
||||
ht->ht_type.tp_getattro = (getattrofunc)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_hash:
|
||||
ht->ht_type.tp_hash = (hashfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_init:
|
||||
ht->ht_type.tp_init = (initproc)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_is_gc:
|
||||
ht->ht_type.tp_is_gc = (inquiry)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_iter:
|
||||
ht->ht_type.tp_iter = (getiterfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_iternext:
|
||||
ht->ht_type.tp_iternext = (iternextfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_methods:
|
||||
ht->ht_type.tp_methods = slot.sl_ptr;
|
||||
break;
|
||||
case Py_tp_new:
|
||||
ht->ht_type.tp_new = (newfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_repr:
|
||||
ht->ht_type.tp_repr = (reprfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_richcompare:
|
||||
ht->ht_type.tp_richcompare = (richcmpfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_setattr:
|
||||
ht->ht_type.tp_setattr = (setattrfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_setattro:
|
||||
ht->ht_type.tp_setattro = (setattrofunc)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_str:
|
||||
ht->ht_type.tp_str = (reprfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_traverse:
|
||||
ht->ht_type.tp_traverse = (traverseproc)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_members:
|
||||
ht->ht_type.tp_members = slot.sl_ptr;
|
||||
break;
|
||||
case Py_tp_getset:
|
||||
ht->ht_type.tp_getset = slot.sl_ptr;
|
||||
break;
|
||||
case Py_tp_free:
|
||||
ht->ht_type.tp_free = (freefunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_matrix_multiply:
|
||||
ht->as_number.nb_matrix_multiply = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_nb_inplace_matrix_multiply:
|
||||
ht->as_number.nb_inplace_matrix_multiply = (binaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_am_await:
|
||||
ht->as_async.am_await = (unaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_am_aiter:
|
||||
ht->as_async.am_aiter = (unaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_am_anext:
|
||||
ht->as_async.am_anext = (unaryfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_finalize:
|
||||
ht->ht_type.tp_finalize = (destructor)slot.sl_func;
|
||||
break;
|
||||
case Py_am_send:
|
||||
ht->as_async.am_send = (sendfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_tp_vectorcall:
|
||||
ht->ht_type.tp_vectorcall = (vectorcallfunc)slot.sl_func;
|
||||
break;
|
||||
case Py_bf_getbuffer:
|
||||
ht->as_buffer.bf_getbuffer = (getbufferproc)slot.sl_func;
|
||||
break;
|
||||
case Py_bf_releasebuffer:
|
||||
ht->as_buffer.bf_releasebuffer = (releasebufferproc)slot.sl_func;
|
||||
break;
|
||||
case Py_mp_ass_subscript:
|
||||
ht->as_mapping.mp_ass_subscript = (objobjargproc)slot.sl_func;
|
||||
break;
|
||||
case Py_mp_length:
|
||||
ht->as_mapping.mp_length = (lenfunc)slot.sl_func;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline _PySlot_DTYPE
|
||||
_PySlot_get_dtype(uint16_t slot_id)
|
||||
{
|
||||
switch (slot_id) {
|
||||
case Py_slot_end: return _PySlot_DTYPE_VOID;
|
||||
case Py_mp_subscript: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_absolute: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_add: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_and: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_bool: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_divmod: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_float: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_floor_divide: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_index: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_inplace_add: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_inplace_and: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_inplace_floor_divide: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_inplace_lshift: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_inplace_multiply: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_inplace_or: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_inplace_power: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_inplace_remainder: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_inplace_rshift: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_inplace_subtract: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_inplace_true_divide: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_inplace_xor: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_int: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_invert: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_lshift: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_multiply: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_negative: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_or: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_positive: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_power: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_remainder: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_rshift: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_subtract: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_true_divide: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_xor: return _PySlot_DTYPE_FUNC;
|
||||
case Py_sq_ass_item: return _PySlot_DTYPE_FUNC;
|
||||
case Py_sq_concat: return _PySlot_DTYPE_FUNC;
|
||||
case Py_sq_contains: return _PySlot_DTYPE_FUNC;
|
||||
case Py_sq_inplace_concat: return _PySlot_DTYPE_FUNC;
|
||||
case Py_sq_inplace_repeat: return _PySlot_DTYPE_FUNC;
|
||||
case Py_sq_item: return _PySlot_DTYPE_FUNC;
|
||||
case Py_sq_length: return _PySlot_DTYPE_FUNC;
|
||||
case Py_sq_repeat: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_alloc: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_base: return _PySlot_DTYPE_PTR;
|
||||
case Py_tp_bases: return _PySlot_DTYPE_PTR;
|
||||
case Py_tp_call: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_clear: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_dealloc: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_del: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_descr_get: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_descr_set: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_doc: return _PySlot_DTYPE_PTR;
|
||||
case Py_tp_getattr: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_getattro: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_hash: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_init: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_is_gc: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_iter: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_iternext: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_methods: return _PySlot_DTYPE_PTR;
|
||||
case Py_tp_new: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_repr: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_richcompare: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_setattr: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_setattro: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_str: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_traverse: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_members: return _PySlot_DTYPE_PTR;
|
||||
case Py_tp_getset: return _PySlot_DTYPE_PTR;
|
||||
case Py_tp_free: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_matrix_multiply: return _PySlot_DTYPE_FUNC;
|
||||
case Py_nb_inplace_matrix_multiply: return _PySlot_DTYPE_FUNC;
|
||||
case Py_am_await: return _PySlot_DTYPE_FUNC;
|
||||
case Py_am_aiter: return _PySlot_DTYPE_FUNC;
|
||||
case Py_am_anext: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_finalize: return _PySlot_DTYPE_FUNC;
|
||||
case Py_am_send: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_vectorcall: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_token: return _PySlot_DTYPE_PTR;
|
||||
case Py_mod_create: return _PySlot_DTYPE_FUNC;
|
||||
case Py_mod_exec: return _PySlot_DTYPE_FUNC;
|
||||
case Py_mod_multiple_interpreters: return _PySlot_DTYPE_UINT64;
|
||||
case Py_mod_gil: return _PySlot_DTYPE_UINT64;
|
||||
case Py_bf_getbuffer: return _PySlot_DTYPE_FUNC;
|
||||
case Py_bf_releasebuffer: return _PySlot_DTYPE_FUNC;
|
||||
case Py_mp_ass_subscript: return _PySlot_DTYPE_FUNC;
|
||||
case Py_mp_length: return _PySlot_DTYPE_FUNC;
|
||||
case Py_slot_subslots: return _PySlot_DTYPE_PTR;
|
||||
case Py_tp_slots: return _PySlot_DTYPE_PTR;
|
||||
case Py_mod_slots: return _PySlot_DTYPE_PTR;
|
||||
case Py_tp_name: return _PySlot_DTYPE_PTR;
|
||||
case Py_tp_basicsize: return _PySlot_DTYPE_SIZE;
|
||||
case Py_tp_extra_basicsize: return _PySlot_DTYPE_SIZE;
|
||||
case Py_tp_itemsize: return _PySlot_DTYPE_SIZE;
|
||||
case Py_tp_flags: return _PySlot_DTYPE_UINT64;
|
||||
case Py_mod_name: return _PySlot_DTYPE_PTR;
|
||||
case Py_mod_doc: return _PySlot_DTYPE_PTR;
|
||||
case Py_mod_state_size: return _PySlot_DTYPE_SIZE;
|
||||
case Py_mod_methods: return _PySlot_DTYPE_PTR;
|
||||
case Py_mod_state_traverse: return _PySlot_DTYPE_FUNC;
|
||||
case Py_mod_state_clear: return _PySlot_DTYPE_FUNC;
|
||||
case Py_mod_state_free: return _PySlot_DTYPE_FUNC;
|
||||
case Py_tp_metaclass: return _PySlot_DTYPE_PTR;
|
||||
case Py_tp_module: return _PySlot_DTYPE_PTR;
|
||||
case Py_mod_abi: return _PySlot_DTYPE_PTR;
|
||||
case Py_mod_token: return _PySlot_DTYPE_PTR;
|
||||
default: return _PySlot_DTYPE_VOID;
|
||||
}
|
||||
}
|
||||
|
||||
static inline _PySlot_PROBLEM_HANDLING
|
||||
_PySlot_get_duplicate_handling(uint16_t slot_id)
|
||||
{
|
||||
switch (slot_id) {
|
||||
case Py_mp_subscript:
|
||||
case Py_nb_absolute:
|
||||
case Py_nb_add:
|
||||
case Py_nb_and:
|
||||
case Py_nb_bool:
|
||||
case Py_nb_divmod:
|
||||
case Py_nb_float:
|
||||
case Py_nb_floor_divide:
|
||||
case Py_nb_index:
|
||||
case Py_nb_inplace_add:
|
||||
case Py_nb_inplace_and:
|
||||
case Py_nb_inplace_floor_divide:
|
||||
case Py_nb_inplace_lshift:
|
||||
case Py_nb_inplace_multiply:
|
||||
case Py_nb_inplace_or:
|
||||
case Py_nb_inplace_power:
|
||||
case Py_nb_inplace_remainder:
|
||||
case Py_nb_inplace_rshift:
|
||||
case Py_nb_inplace_subtract:
|
||||
case Py_nb_inplace_true_divide:
|
||||
case Py_nb_inplace_xor:
|
||||
case Py_nb_int:
|
||||
case Py_nb_invert:
|
||||
case Py_nb_lshift:
|
||||
case Py_nb_multiply:
|
||||
case Py_nb_negative:
|
||||
case Py_nb_or:
|
||||
case Py_nb_positive:
|
||||
case Py_nb_power:
|
||||
case Py_nb_remainder:
|
||||
case Py_nb_rshift:
|
||||
case Py_nb_subtract:
|
||||
case Py_nb_true_divide:
|
||||
case Py_nb_xor:
|
||||
case Py_sq_ass_item:
|
||||
case Py_sq_concat:
|
||||
case Py_sq_contains:
|
||||
case Py_sq_inplace_concat:
|
||||
case Py_sq_inplace_repeat:
|
||||
case Py_sq_item:
|
||||
case Py_sq_length:
|
||||
case Py_sq_repeat:
|
||||
case Py_tp_alloc:
|
||||
case Py_tp_base:
|
||||
case Py_tp_bases:
|
||||
case Py_tp_call:
|
||||
case Py_tp_clear:
|
||||
case Py_tp_dealloc:
|
||||
case Py_tp_del:
|
||||
case Py_tp_descr_get:
|
||||
case Py_tp_descr_set:
|
||||
case Py_tp_getattr:
|
||||
case Py_tp_getattro:
|
||||
case Py_tp_hash:
|
||||
case Py_tp_init:
|
||||
case Py_tp_is_gc:
|
||||
case Py_tp_iter:
|
||||
case Py_tp_iternext:
|
||||
case Py_tp_methods:
|
||||
case Py_tp_new:
|
||||
case Py_tp_repr:
|
||||
case Py_tp_richcompare:
|
||||
case Py_tp_setattr:
|
||||
case Py_tp_setattro:
|
||||
case Py_tp_str:
|
||||
case Py_tp_traverse:
|
||||
case Py_tp_getset:
|
||||
case Py_tp_free:
|
||||
case Py_nb_matrix_multiply:
|
||||
case Py_nb_inplace_matrix_multiply:
|
||||
case Py_am_await:
|
||||
case Py_am_aiter:
|
||||
case Py_am_anext:
|
||||
case Py_tp_finalize:
|
||||
case Py_am_send:
|
||||
case Py_tp_vectorcall:
|
||||
case Py_tp_token:
|
||||
case Py_bf_getbuffer:
|
||||
case Py_bf_releasebuffer:
|
||||
case Py_mp_ass_subscript:
|
||||
case Py_mp_length:
|
||||
return _PySlot_PROBLEM_DEPRECATED;
|
||||
case Py_mod_exec:
|
||||
case Py_mod_abi:
|
||||
return _PySlot_PROBLEM_ALLOW;
|
||||
default:
|
||||
return _PySlot_PROBLEM_REJECT;
|
||||
}
|
||||
}
|
||||
|
||||
static inline _PySlot_PROBLEM_HANDLING
|
||||
_PySlot_get_null_handling(uint16_t slot_id)
|
||||
{
|
||||
switch (slot_id) {
|
||||
case Py_slot_end:
|
||||
case Py_tp_doc:
|
||||
case Py_tp_token:
|
||||
case Py_mod_multiple_interpreters:
|
||||
case Py_mod_gil:
|
||||
case Py_slot_subslots:
|
||||
case Py_tp_slots:
|
||||
case Py_mod_slots:
|
||||
case Py_tp_basicsize:
|
||||
case Py_tp_extra_basicsize:
|
||||
case Py_tp_itemsize:
|
||||
case Py_tp_flags:
|
||||
case Py_mod_state_size:
|
||||
return _PySlot_PROBLEM_ALLOW;
|
||||
case Py_mp_subscript:
|
||||
case Py_nb_absolute:
|
||||
case Py_nb_add:
|
||||
case Py_nb_and:
|
||||
case Py_nb_bool:
|
||||
case Py_nb_divmod:
|
||||
case Py_nb_float:
|
||||
case Py_nb_floor_divide:
|
||||
case Py_nb_index:
|
||||
case Py_nb_inplace_add:
|
||||
case Py_nb_inplace_and:
|
||||
case Py_nb_inplace_floor_divide:
|
||||
case Py_nb_inplace_lshift:
|
||||
case Py_nb_inplace_multiply:
|
||||
case Py_nb_inplace_or:
|
||||
case Py_nb_inplace_power:
|
||||
case Py_nb_inplace_remainder:
|
||||
case Py_nb_inplace_rshift:
|
||||
case Py_nb_inplace_subtract:
|
||||
case Py_nb_inplace_true_divide:
|
||||
case Py_nb_inplace_xor:
|
||||
case Py_nb_int:
|
||||
case Py_nb_invert:
|
||||
case Py_nb_lshift:
|
||||
case Py_nb_multiply:
|
||||
case Py_nb_negative:
|
||||
case Py_nb_or:
|
||||
case Py_nb_positive:
|
||||
case Py_nb_power:
|
||||
case Py_nb_remainder:
|
||||
case Py_nb_rshift:
|
||||
case Py_nb_subtract:
|
||||
case Py_nb_true_divide:
|
||||
case Py_nb_xor:
|
||||
case Py_sq_ass_item:
|
||||
case Py_sq_concat:
|
||||
case Py_sq_contains:
|
||||
case Py_sq_inplace_concat:
|
||||
case Py_sq_inplace_repeat:
|
||||
case Py_sq_item:
|
||||
case Py_sq_length:
|
||||
case Py_sq_repeat:
|
||||
case Py_tp_alloc:
|
||||
case Py_tp_base:
|
||||
case Py_tp_bases:
|
||||
case Py_tp_call:
|
||||
case Py_tp_clear:
|
||||
case Py_tp_dealloc:
|
||||
case Py_tp_del:
|
||||
case Py_tp_descr_get:
|
||||
case Py_tp_descr_set:
|
||||
case Py_tp_getattr:
|
||||
case Py_tp_getattro:
|
||||
case Py_tp_hash:
|
||||
case Py_tp_init:
|
||||
case Py_tp_is_gc:
|
||||
case Py_tp_iter:
|
||||
case Py_tp_iternext:
|
||||
case Py_tp_methods:
|
||||
case Py_tp_new:
|
||||
case Py_tp_repr:
|
||||
case Py_tp_richcompare:
|
||||
case Py_tp_setattr:
|
||||
case Py_tp_setattro:
|
||||
case Py_tp_str:
|
||||
case Py_tp_traverse:
|
||||
case Py_tp_getset:
|
||||
case Py_tp_free:
|
||||
case Py_nb_matrix_multiply:
|
||||
case Py_nb_inplace_matrix_multiply:
|
||||
case Py_am_await:
|
||||
case Py_am_aiter:
|
||||
case Py_am_anext:
|
||||
case Py_tp_finalize:
|
||||
case Py_am_send:
|
||||
case Py_tp_vectorcall:
|
||||
case Py_mod_create:
|
||||
case Py_bf_getbuffer:
|
||||
case Py_bf_releasebuffer:
|
||||
case Py_mp_ass_subscript:
|
||||
case Py_mp_length:
|
||||
return _PySlot_PROBLEM_DEPRECATED;
|
||||
default:
|
||||
return _PySlot_PROBLEM_REJECT;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
_PySlot_get_must_be_static(uint16_t slot_id)
|
||||
{
|
||||
switch (slot_id) {
|
||||
case Py_tp_methods: return true;
|
||||
case Py_tp_members: return true;
|
||||
case Py_tp_getset: return true;
|
||||
case Py_mod_methods: return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* _PY_HAVE_INTERNAL_SLOTS_GENERATED_H */
|
||||
+1
-26
@@ -73,31 +73,6 @@ struct PyModuleDef_Slot {
|
||||
void *value;
|
||||
};
|
||||
|
||||
#define Py_mod_create 1
|
||||
#define Py_mod_exec 2
|
||||
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030c0000
|
||||
# define Py_mod_multiple_interpreters 3
|
||||
#endif
|
||||
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030d0000
|
||||
# define Py_mod_gil 4
|
||||
#endif
|
||||
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15)
|
||||
# define Py_mod_abi 5
|
||||
# define Py_mod_name 6
|
||||
# define Py_mod_doc 7
|
||||
# define Py_mod_state_size 8
|
||||
# define Py_mod_methods 9
|
||||
# define Py_mod_state_traverse 10
|
||||
# define Py_mod_state_clear 11
|
||||
# define Py_mod_state_free 12
|
||||
# define Py_mod_token 13
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef Py_LIMITED_API
|
||||
#define _Py_mod_LAST_SLOT 13
|
||||
#endif
|
||||
|
||||
#endif /* New in 3.5 */
|
||||
|
||||
/* for Py_mod_multiple_interpreters: */
|
||||
@@ -120,7 +95,7 @@ PyAPI_FUNC(int) PyUnstable_Module_SetGIL(PyObject *module, void *gil);
|
||||
#endif
|
||||
|
||||
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15)
|
||||
PyAPI_FUNC(PyObject *) PyModule_FromSlotsAndSpec(const PyModuleDef_Slot *slots,
|
||||
PyAPI_FUNC(PyObject *) PyModule_FromSlotsAndSpec(const PySlot *slots,
|
||||
PyObject *spec);
|
||||
PyAPI_FUNC(int) PyModule_Exec(PyObject *module);
|
||||
PyAPI_FUNC(int) PyModule_GetStateSize(PyObject *module, Py_ssize_t *result);
|
||||
|
||||
@@ -364,6 +364,9 @@ PyAPI_FUNC(Py_ssize_t) PyType_GetTypeDataSize(PyTypeObject *cls);
|
||||
PyAPI_FUNC(int) PyType_GetBaseByToken(PyTypeObject *, void *, PyTypeObject **);
|
||||
#define Py_TP_USE_SPEC NULL
|
||||
#endif
|
||||
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15)
|
||||
PyAPI_FUNC(PyObject *) PyType_FromSlots(struct PySlot *slots);
|
||||
#endif
|
||||
|
||||
/* Generic type check */
|
||||
PyAPI_FUNC(int) PyType_IsSubtype(PyTypeObject *, PyTypeObject *);
|
||||
|
||||
@@ -14,6 +14,7 @@ typedef struct PyModuleDef_Slot PyModuleDef_Slot;
|
||||
typedef struct PyMethodDef PyMethodDef;
|
||||
typedef struct PyGetSetDef PyGetSetDef;
|
||||
typedef struct PyMemberDef PyMemberDef;
|
||||
typedef struct PySlot PySlot;
|
||||
|
||||
typedef struct _object PyObject;
|
||||
typedef struct _longobject PyLongObject;
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifndef _Py_HAVE_SLOTS_H
|
||||
#define _Py_HAVE_SLOTS_H
|
||||
|
||||
typedef void (*_Py_funcptr_t)(void);
|
||||
|
||||
struct PySlot {
|
||||
uint16_t sl_id;
|
||||
uint16_t sl_flags;
|
||||
_Py_ANONYMOUS union {
|
||||
uint32_t sl_reserved; // must be 0
|
||||
};
|
||||
_Py_ANONYMOUS union {
|
||||
void *sl_ptr;
|
||||
_Py_funcptr_t sl_func;
|
||||
Py_ssize_t sl_size;
|
||||
int64_t sl_int64;
|
||||
uint64_t sl_uint64;
|
||||
};
|
||||
};
|
||||
|
||||
#define PySlot_OPTIONAL 0x0001
|
||||
#define PySlot_STATIC 0x0002
|
||||
#define PySlot_INTPTR 0x0004
|
||||
|
||||
#define Py_slot_invalid 0xffff
|
||||
|
||||
#define PySlot_DATA(NAME, VALUE) \
|
||||
{.sl_id=(NAME), .sl_flags=PySlot_INTPTR, .sl_ptr=(void*)(VALUE)}
|
||||
|
||||
#define PySlot_FUNC(NAME, VALUE) \
|
||||
{.sl_id=(NAME), .sl_func=(_Py_funcptr_t)(VALUE)}
|
||||
|
||||
#define PySlot_SIZE(NAME, VALUE) \
|
||||
{.sl_id=(NAME), .sl_size=(Py_ssize_t)(VALUE)}
|
||||
|
||||
#define PySlot_INT64(NAME, VALUE) \
|
||||
{.sl_id=(NAME), .sl_int64=(int64_t)(VALUE)}
|
||||
|
||||
#define PySlot_UINT64(NAME, VALUE) \
|
||||
{.sl_id=(NAME), .sl_uint64=(uint64_t)(VALUE)}
|
||||
|
||||
#define PySlot_STATIC_DATA(NAME, VALUE) \
|
||||
{.sl_id=(NAME), .sl_flags=PySlot_STATIC, .sl_ptr=(VALUE)}
|
||||
|
||||
#define PySlot_END {0}
|
||||
|
||||
|
||||
// Macros without designated initializers (for C++11 and below):
|
||||
|
||||
#define PySlot_PTR(NAME, VALUE) \
|
||||
{(NAME), PySlot_INTPTR, {0}, {(void*)(VALUE)}}
|
||||
|
||||
#define PySlot_PTR_STATIC(NAME, VALUE) \
|
||||
{(NAME), PySlot_INTPTR | PySlot_STATIC, {0}, {(void*)(VALUE)}}
|
||||
|
||||
#endif // _Py_HAVE_SLOTS_H
|
||||
+42
-17
@@ -1,8 +1,15 @@
|
||||
/* Do not renumber the file; these numbers are part of the stable ABI. */
|
||||
#define Py_bf_getbuffer 1
|
||||
#define Py_bf_releasebuffer 2
|
||||
#define Py_mp_ass_subscript 3
|
||||
#define Py_mp_length 4
|
||||
/* Generated by Tools/build/generate_slots.py */
|
||||
|
||||
#ifndef _PY_HAVE_SLOTS_GENERATED_H
|
||||
#define _PY_HAVE_SLOTS_GENERATED_H
|
||||
|
||||
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15)
|
||||
#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) NEW
|
||||
#else
|
||||
#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) OLD
|
||||
#endif
|
||||
|
||||
#define Py_slot_end 0
|
||||
#define Py_mp_subscript 5
|
||||
#define Py_nb_absolute 6
|
||||
#define Py_nb_add 7
|
||||
@@ -78,19 +85,37 @@
|
||||
#define Py_am_await 77
|
||||
#define Py_am_aiter 78
|
||||
#define Py_am_anext 79
|
||||
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03050000
|
||||
/* New in 3.5 */
|
||||
#define Py_tp_finalize 80
|
||||
#endif
|
||||
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000
|
||||
/* New in 3.10 */
|
||||
#define Py_am_send 81
|
||||
#endif
|
||||
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030E0000
|
||||
/* New in 3.14 */
|
||||
#define Py_tp_vectorcall 82
|
||||
#endif
|
||||
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030E0000
|
||||
/* New in 3.14 */
|
||||
#define Py_tp_token 83
|
||||
#endif
|
||||
#define Py_mod_create _Py_SLOT_COMPAT_VALUE(1, 84)
|
||||
#define Py_mod_exec _Py_SLOT_COMPAT_VALUE(2, 85)
|
||||
#define Py_mod_multiple_interpreters _Py_SLOT_COMPAT_VALUE(3, 86)
|
||||
#define Py_mod_gil _Py_SLOT_COMPAT_VALUE(4, 87)
|
||||
#define Py_bf_getbuffer _Py_SLOT_COMPAT_VALUE(1, 88)
|
||||
#define Py_bf_releasebuffer _Py_SLOT_COMPAT_VALUE(2, 89)
|
||||
#define Py_mp_ass_subscript _Py_SLOT_COMPAT_VALUE(3, 90)
|
||||
#define Py_mp_length _Py_SLOT_COMPAT_VALUE(4, 91)
|
||||
#define Py_slot_subslots 92
|
||||
#define Py_tp_slots 93
|
||||
#define Py_mod_slots 94
|
||||
#define Py_tp_name 95
|
||||
#define Py_tp_basicsize 96
|
||||
#define Py_tp_extra_basicsize 97
|
||||
#define Py_tp_itemsize 98
|
||||
#define Py_tp_flags 99
|
||||
#define Py_mod_name 100
|
||||
#define Py_mod_doc 101
|
||||
#define Py_mod_state_size 102
|
||||
#define Py_mod_methods 103
|
||||
#define Py_mod_state_traverse 104
|
||||
#define Py_mod_state_clear 105
|
||||
#define Py_mod_state_free 106
|
||||
#define Py_tp_metaclass 107
|
||||
#define Py_tp_module 108
|
||||
#define Py_mod_abi 109
|
||||
#define Py_mod_token 110
|
||||
|
||||
#define _Py_slot_COUNT 111
|
||||
#endif /* _PY_HAVE_SLOTS_GENERATED_H */
|
||||
@@ -923,8 +923,8 @@ class CAPITest(unittest.TestCase):
|
||||
|
||||
def test_tp_bases_slot_none(self):
|
||||
self.assertRaisesRegex(
|
||||
SystemError,
|
||||
"Py_tp_bases is not a tuple",
|
||||
TypeError,
|
||||
"metaclass conflict",
|
||||
_testcapi.create_heapctype_with_none_bases_slot
|
||||
)
|
||||
|
||||
@@ -1055,13 +1055,13 @@ class TestHeapTypeRelative(unittest.TestCase):
|
||||
def test_heaptype_relative_members_errors(self):
|
||||
with self.assertRaisesRegex(
|
||||
SystemError,
|
||||
r"With Py_RELATIVE_OFFSET, basicsize must be negative"):
|
||||
r"With Py_RELATIVE_OFFSET, basicsize must be extended"):
|
||||
_testlimitedcapi.make_heaptype_with_member(0, 1234, 0, True)
|
||||
with self.assertRaisesRegex(
|
||||
SystemError, r"Member offset out of range \(0\.\.-basicsize\)"):
|
||||
SystemError, r"Member offset out of range \(0\.\.extra_basicsize\)"):
|
||||
_testlimitedcapi.make_heaptype_with_member(0, -8, 1234, True)
|
||||
with self.assertRaisesRegex(
|
||||
SystemError, r"Member offset out of range \(0\.\.-basicsize\)"):
|
||||
SystemError, r"Member offset must not be negative"):
|
||||
_testlimitedcapi.make_heaptype_with_member(0, -8, -1, True)
|
||||
|
||||
Sub = _testlimitedcapi.make_heaptype_with_member(0, -8, 0, True)
|
||||
@@ -1078,7 +1078,7 @@ class TestHeapTypeRelative(unittest.TestCase):
|
||||
with self.subTest(member_name=member_name):
|
||||
with self.assertRaisesRegex(
|
||||
SystemError,
|
||||
r"With Py_RELATIVE_OFFSET, basicsize must be negative."):
|
||||
r"With Py_RELATIVE_OFFSET, basicsize must be extended"):
|
||||
_testlimitedcapi.make_heaptype_with_member(
|
||||
basicsize=sys.getsizeof(object()) + 100,
|
||||
add_relative_flag=True,
|
||||
@@ -1089,7 +1089,7 @@ class TestHeapTypeRelative(unittest.TestCase):
|
||||
)
|
||||
with self.assertRaisesRegex(
|
||||
SystemError,
|
||||
r"Member offset out of range \(0\.\.-basicsize\)"):
|
||||
r"Member offset must not be negative"):
|
||||
_testlimitedcapi.make_heaptype_with_member(
|
||||
basicsize=-8,
|
||||
add_relative_flag=True,
|
||||
@@ -1098,6 +1098,17 @@ class TestHeapTypeRelative(unittest.TestCase):
|
||||
member_type=_testlimitedcapi.Py_T_PYSSIZET,
|
||||
member_flags=_testlimitedcapi.Py_READONLY,
|
||||
)
|
||||
with self.assertRaisesRegex(
|
||||
SystemError,
|
||||
r"Member offset out of range \(0\.\.extra_basicsize\)"):
|
||||
_testlimitedcapi.make_heaptype_with_member(
|
||||
basicsize=-8,
|
||||
add_relative_flag=True,
|
||||
member_name=member_name,
|
||||
member_offset=1234,
|
||||
member_type=_testlimitedcapi.Py_T_PYSSIZET,
|
||||
member_flags=_testlimitedcapi.Py_READONLY,
|
||||
)
|
||||
with self.assertRaisesRegex(
|
||||
SystemError,
|
||||
r"type of %s must be Py_T_PYSSIZET" % member_name):
|
||||
|
||||
@@ -150,11 +150,13 @@ class TestModFromSlotsAndSpec(unittest.TestCase):
|
||||
with self.assertRaises(SystemError) as cm:
|
||||
_testcapi.module_from_slots_repeat_slot(spec)
|
||||
self.assertIn(name, str(cm.exception))
|
||||
self.assertIn("more than one", str(cm.exception))
|
||||
self.assertRegex(
|
||||
str(cm.exception),
|
||||
rf"^module( [_\w]+)? has multiple {name}( \(\d+\))? slots$")
|
||||
|
||||
def test_null_def_slot(self):
|
||||
"""Slots that replace PyModuleDef fields can't be NULL"""
|
||||
for name in (*DEF_SLOTS, 'Py_mod_exec'):
|
||||
for name in {*DEF_SLOTS, 'Py_mod_exec'} - {'Py_mod_state_size'}:
|
||||
with self.subTest(name):
|
||||
spec = FakeSpec()
|
||||
spec._test_slot_id = getattr(_testcapi, name)
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
from test.support import import_helper, subTests
|
||||
import contextlib
|
||||
import unittest
|
||||
import types
|
||||
import sys
|
||||
|
||||
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
|
||||
_testcapi = import_helper.import_module('_testcapi')
|
||||
|
||||
class FakeSpec:
|
||||
name = 'module'
|
||||
|
||||
|
||||
# See Modules/_testlimitedcapi/slots.c for the definitions.
|
||||
# This module is full of "magic constants" which simply need to match
|
||||
# between the C and Python part of the tests.
|
||||
|
||||
class TypeSlotsTests(unittest.TestCase):
|
||||
def test_basic_type_slots(self):
|
||||
cls = _testlimitedcapi.type_from_slots("basic")
|
||||
self.assertEqual(cls.__name__, "MyType")
|
||||
|
||||
# Py_TPFLAGS_IMMUTABLETYPE is *not* set
|
||||
cls.attr = 123
|
||||
|
||||
# Py_TPFLAGS_BASETYPE is *not* set
|
||||
with self.assertRaises(TypeError):
|
||||
class Sub(cls):
|
||||
pass
|
||||
|
||||
def test_mod_slot_in_type(self):
|
||||
with self.assertRaisesRegex(SystemError, "invalid.* 100 .*Py_mod_name"):
|
||||
_testlimitedcapi.type_from_slots("foreign_slot")
|
||||
|
||||
def test_size_slots(self):
|
||||
cls = _testlimitedcapi.type_from_slots("basicsize")
|
||||
self.assertGreaterEqual(cls.__basicsize__, 256)
|
||||
|
||||
cls = _testlimitedcapi.type_from_slots("extra_basicsize")
|
||||
self.assertGreaterEqual(cls.__basicsize__, object.__basicsize__ + 256)
|
||||
|
||||
cls = _testlimitedcapi.type_from_slots("itemsize")
|
||||
self.assertGreaterEqual(cls.__itemsize__, 16)
|
||||
|
||||
def test_flag_slots(self):
|
||||
cls = _testlimitedcapi.type_from_slots("flags")
|
||||
with self.assertRaises(TypeError):
|
||||
# Py_TPFLAGS_IMMUTABLETYPE is set
|
||||
cls.attr = 123
|
||||
class Sub(cls):
|
||||
# Py_TPFLAGS_BASETYPE is set
|
||||
pass
|
||||
|
||||
def test_func_slots(self):
|
||||
cls = _testlimitedcapi.type_from_slots("matmul_123")
|
||||
self.assertEqual(cls() @ None, 123)
|
||||
|
||||
def test_optional_end(self):
|
||||
with self.assertRaisesRegex(SystemError, "invalid flags.*Py_slot_end"):
|
||||
cls = _testlimitedcapi.type_from_slots("optional_end")
|
||||
|
||||
def test_invalid(self):
|
||||
with self.assertRaisesRegex(SystemError, "Py_slot_invalid"):
|
||||
cls = _testlimitedcapi.type_from_slots("invalid")
|
||||
with self.assertRaisesRegex(SystemError, f"slot ID {0xfbad}"):
|
||||
cls = _testlimitedcapi.type_from_slots("invalid_fbad")
|
||||
|
||||
cls = _testlimitedcapi.type_from_slots("optional_invalid")
|
||||
self.assertGreaterEqual(cls.__basicsize__, object.__basicsize__ + 256)
|
||||
|
||||
cls = _testlimitedcapi.type_from_slots("optional_invalid_fbad")
|
||||
self.assertGreaterEqual(cls.__basicsize__, object.__basicsize__ + 256)
|
||||
|
||||
@subTests("case_name", ["old_slot_numbers", "new_slot_numbers"])
|
||||
def test_compat_slot_numbers(self, case_name):
|
||||
cls = _testlimitedcapi.type_from_slots(case_name)
|
||||
obj = cls()
|
||||
|
||||
# Py_bf_getbuffer (1), Py_bf_releasebuffer (2)
|
||||
self.assertEqual(obj.buf_counter, 0)
|
||||
mem = memoryview(obj)
|
||||
self.assertEqual(bytes(mem), b"buf\0")
|
||||
self.assertEqual(obj.buf_counter, 1)
|
||||
mem.release()
|
||||
self.assertEqual(obj.buf_counter, 0)
|
||||
|
||||
# Py_mp_ass_subscript (3)
|
||||
with self.assertRaises(KeyError):
|
||||
obj["key"] = "value"
|
||||
|
||||
# Py_mp_length (4)
|
||||
self.assertEqual(len(obj), 456)
|
||||
|
||||
def test_nonstatic_tp_members(self):
|
||||
with self.assertRaisesRegex(SystemError, "Py_tp_members.*STATIC"):
|
||||
_testlimitedcapi.type_from_slots("nonstatic_tp_members")
|
||||
|
||||
def test_intptr_flags(self):
|
||||
cls = _testlimitedcapi.type_from_slots("intptr_flags_macro")
|
||||
with self.assertRaises(TypeError):
|
||||
# Py_TPFLAGS_IMMUTABLETYPE is set
|
||||
cls.attr = 123
|
||||
|
||||
cls = _testlimitedcapi.type_from_slots("intptr_flags_struct")
|
||||
with self.assertRaises(TypeError):
|
||||
# Py_TPFLAGS_IMMUTABLETYPE is set
|
||||
cls.attr = 123
|
||||
|
||||
cls = _testlimitedcapi.type_from_slots("intptr_static")
|
||||
cls.attribute = 123
|
||||
|
||||
def test_nested(self):
|
||||
cls = _testlimitedcapi.type_from_slots("nested")
|
||||
self.assertEqual(cls() + 1, 123)
|
||||
self.assertEqual(cls() - 1, 234)
|
||||
|
||||
cls = _testlimitedcapi.type_from_slots("nested_max")
|
||||
self.assertEqual(cls() + 1, 123)
|
||||
self.assertEqual(cls() - 1, 234)
|
||||
self.assertEqual(cls() * 1, 345)
|
||||
self.assertEqual(cls() / 1, 456)
|
||||
self.assertEqual(cls() % 1, 567)
|
||||
|
||||
with self.assertRaisesRegex(SystemError, "too many levels"):
|
||||
_testlimitedcapi.type_from_slots("nested_over_limit")
|
||||
|
||||
cls = _testlimitedcapi.type_from_slots("nested_old")
|
||||
self.assertEqual(cls() + 1, 123)
|
||||
self.assertEqual(cls() - 1, 234)
|
||||
|
||||
cls = _testlimitedcapi.type_from_slots("nested_old_max")
|
||||
self.assertEqual(cls() + 1, 123)
|
||||
self.assertEqual(cls() - 1, 234)
|
||||
self.assertEqual(cls() * 1, 345)
|
||||
self.assertEqual(cls() / 1, 456)
|
||||
self.assertEqual(cls() % 1, 567)
|
||||
|
||||
with self.assertRaisesRegex(SystemError, "too many levels"):
|
||||
_testlimitedcapi.type_from_slots("nested_old_over_limit")
|
||||
|
||||
cls = _testlimitedcapi.type_from_slots("nested_pingpong")
|
||||
self.assertEqual(cls() + 1, 123)
|
||||
self.assertEqual(cls() - 1, 234)
|
||||
self.assertEqual(cls() * 1, 345)
|
||||
self.assertEqual(cls() / 1, 456)
|
||||
self.assertEqual(cls() % 1, 567)
|
||||
|
||||
# Slot names aren't exposed to Python yet; see Include/slots_generated.h
|
||||
# for the definitions.
|
||||
|
||||
@subTests("slot_number", [
|
||||
*range(1, 83), # Original slots
|
||||
*range(88, 92), # New compat slot values
|
||||
*range(95, 99), # Slots for PyType_Spec fields
|
||||
*range(107, 109), # Slots for PyType_FromMetaclass args
|
||||
])
|
||||
def test_null_slot_handling(self, slot_number):
|
||||
if slot_number == 56:
|
||||
# Py_tp_doc
|
||||
return
|
||||
elif slot_number == 72 or slot_number >= 95:
|
||||
# Py_tp_members; all new slots
|
||||
ctx = self.assertRaisesRegex(
|
||||
SystemError, "NULL not allowed|must be positive")
|
||||
ctx_old = ctx
|
||||
else:
|
||||
ctx = self.assertWarnsRegex(DeprecationWarning, "NULL")
|
||||
ctx_old = contextlib.nullcontext()
|
||||
with ctx:
|
||||
_testlimitedcapi.type_from_null_slot(slot_number)
|
||||
if slot_number < 95:
|
||||
with ctx_old:
|
||||
_testlimitedcapi.type_from_null_spec_slot(slot_number)
|
||||
|
||||
def test_repeat_warning(self):
|
||||
with self.assertWarnsRegex(DeprecationWarning, "multiple"):
|
||||
cls = _testlimitedcapi.type_from_slots("repeat_add")
|
||||
self.assertEqual(cls() + 1, 456)
|
||||
|
||||
def test_repeat_error(self):
|
||||
with self.assertRaisesRegex(SystemError, "multiple"):
|
||||
cls = _testlimitedcapi.type_from_slots("repeat_module")
|
||||
|
||||
class ModuleSlotsTests(unittest.TestCase):
|
||||
def test_basic_module_slots(self):
|
||||
mod = _testlimitedcapi.module_from_slots("basic", FakeSpec())
|
||||
self.assertIsInstance(mod, types.ModuleType)
|
||||
|
||||
def test_type_slot_in_module(self):
|
||||
with self.assertRaisesRegex(SystemError, "invalid.* 95 .*Py_tp_name"):
|
||||
_testlimitedcapi.module_from_slots("foreign_slot", FakeSpec())
|
||||
|
||||
def test_size_slots(self):
|
||||
mod = _testlimitedcapi.module_from_slots("state_size", FakeSpec())
|
||||
self.assertEqual(mod.state_size, 42)
|
||||
|
||||
def test_flag_slots(self):
|
||||
mod = _testlimitedcapi.module_from_slots("multi_interp", FakeSpec())
|
||||
|
||||
def test_exec_slot(self):
|
||||
mod = _testlimitedcapi.module_from_slots("exec", FakeSpec())
|
||||
self.assertEqual(mod.exec_done, "yes")
|
||||
|
||||
def test_optional_end(self):
|
||||
with self.assertRaisesRegex(SystemError, "invalid flags.*Py_slot_end"):
|
||||
_testlimitedcapi.module_from_slots("optional_end", FakeSpec())
|
||||
|
||||
def test_invalid(self):
|
||||
with self.assertRaisesRegex(SystemError, "Py_slot_invalid"):
|
||||
_testlimitedcapi.module_from_slots("invalid", FakeSpec())
|
||||
with self.assertRaisesRegex(SystemError, f"slot ID {0xfbad}"):
|
||||
_testlimitedcapi.module_from_slots("invalid_fbad", FakeSpec())
|
||||
|
||||
mod = _testlimitedcapi.module_from_slots("optional_invalid", FakeSpec())
|
||||
self.assertEqual(mod.exec_done, "yes")
|
||||
|
||||
mod = _testlimitedcapi.module_from_slots("optional_invalid_fbad", FakeSpec())
|
||||
self.assertEqual(mod.exec_done, "yes")
|
||||
|
||||
@subTests("case_name", ["old_slot_numbers", "new_slot_numbers"])
|
||||
def test_compat_slot_numbers(self, case_name):
|
||||
mod = _testlimitedcapi.module_from_slots(case_name, FakeSpec())
|
||||
self.assertEqual(mod.exec_done, "yes")
|
||||
|
||||
@subTests("case_name", ["old_slot_number_create", "new_slot_number_create"])
|
||||
def test_compat_slot_number_create(self, case_name):
|
||||
spec = FakeSpec()
|
||||
mod = _testlimitedcapi.module_from_slots(case_name, spec)
|
||||
self.assertIs(mod, spec)
|
||||
|
||||
@subTests("slot_number", [4, 87])
|
||||
def test_compat_slot_number_gil(self, slot_number):
|
||||
spec = FakeSpec()
|
||||
gil_enabled = sys._is_gil_enabled()
|
||||
mod = _testlimitedcapi.module_from_gil_slot(slot_number, spec)
|
||||
self.assertEqual(gil_enabled, sys._is_gil_enabled())
|
||||
|
||||
def test_nonstatic_mod_methods(self):
|
||||
with self.assertRaisesRegex(SystemError, "Py_mod_methods.*STATIC"):
|
||||
_testlimitedcapi.module_from_slots("nonstatic_mod_methods",
|
||||
FakeSpec())
|
||||
|
||||
def test_intptr_methods(self):
|
||||
mod = _testlimitedcapi.module_from_slots("intptr_methods",
|
||||
FakeSpec())
|
||||
self.assertEqual(mod.type_from_slots.__name__, "type_from_slots")
|
||||
|
||||
def test_nested(self):
|
||||
mod = _testlimitedcapi.module_from_slots("nested", FakeSpec())
|
||||
self.assertEqual(mod.exec_done, "yes")
|
||||
self.assertEqual(mod.__doc__, "doc")
|
||||
|
||||
mod = _testlimitedcapi.module_from_slots("nested_max", FakeSpec())
|
||||
self.assertEqual(mod.exec_done, "yes")
|
||||
self.assertEqual(mod.state_size, 53)
|
||||
self.assertEqual(mod.__doc__, "doc")
|
||||
|
||||
with self.assertRaisesRegex(SystemError, "too many levels"):
|
||||
_testlimitedcapi.module_from_slots("nested_over_limit", FakeSpec())
|
||||
|
||||
mod = _testlimitedcapi.module_from_slots("nested_old", FakeSpec())
|
||||
self.assertEqual(mod.exec_done, "yes")
|
||||
self.assertEqual(mod.__doc__, "doc")
|
||||
|
||||
mod = _testlimitedcapi.module_from_slots("nested_old_max", FakeSpec())
|
||||
self.assertEqual(mod.exec_done, "yes")
|
||||
self.assertEqual(mod.state_size, 53)
|
||||
self.assertEqual(mod.__doc__, "doc")
|
||||
|
||||
with self.assertRaisesRegex(SystemError, "too many levels"):
|
||||
_testlimitedcapi.module_from_slots("nested_old_over_limit", FakeSpec())
|
||||
|
||||
mod = _testlimitedcapi.module_from_slots("nested_pingpong", FakeSpec())
|
||||
self.assertEqual(mod.exec_done, "yes")
|
||||
self.assertEqual(mod.state_size, 53)
|
||||
self.assertEqual(mod.__doc__, "doc")
|
||||
|
||||
def test_nested_nonstatic_from_def(self):
|
||||
with self.assertRaisesRegex(SystemError, "must be static"):
|
||||
_testcapi.module_from_def_nonstatic_nested(FakeSpec())
|
||||
|
||||
# Slot names aren't exposed to Python yet; see Include/slots_generated.h
|
||||
# for the definitions.
|
||||
|
||||
@subTests("slot_number", [
|
||||
*range(1, 5), # Old compat slot values
|
||||
*range(84, 88), # New compat slot values
|
||||
*range(100, 107), # Slots for PyModuleDef fields
|
||||
*range(109, 111), # Slots new in 3.15
|
||||
])
|
||||
def test_null_slot_handling(self, slot_number):
|
||||
if slot_number in {3, 86, 4, 87, 102}:
|
||||
# Py_mod_mult.interp., Py_mod_gil, Py_mod_state_size
|
||||
return
|
||||
elif slot_number in {2, 85} or slot_number > 85:
|
||||
# Py_mod_exec, new slots
|
||||
ctx = self.assertRaisesRegex(SystemError, "NULL not allowed")
|
||||
ctx_old = ctx
|
||||
else:
|
||||
ctx = self.assertWarnsRegex(DeprecationWarning, "NULL")
|
||||
ctx_old = contextlib.nullcontext()
|
||||
with ctx:
|
||||
_testlimitedcapi.module_from_null_slot(slot_number, FakeSpec())
|
||||
with ctx_old:
|
||||
_testcapi.module_from_null_def_slot(slot_number,
|
||||
FakeSpec())
|
||||
|
||||
def test_repeat_error(self):
|
||||
with self.assertRaisesRegex(SystemError, "multiple"):
|
||||
_testlimitedcapi.module_from_slots("repeat_create", FakeSpec())
|
||||
with self.assertRaisesRegex(SystemError, "multiple"):
|
||||
_testlimitedcapi.module_from_slots("repeat_exec", FakeSpec())
|
||||
with self.assertRaisesRegex(SystemError, "multiple"):
|
||||
_testlimitedcapi.module_from_slots("repeat_gil", FakeSpec())
|
||||
@@ -125,13 +125,13 @@ _Py_COMP_DIAG_PUSH
|
||||
PyDoc_STRVAR(_testcext_doc, "C test extension.");
|
||||
PyABIInfo_VAR(abi_info);
|
||||
|
||||
static PyModuleDef_Slot _testcext_slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_name, STR(MODULE_NAME)},
|
||||
{Py_mod_doc, (void*)(char*)_testcext_doc},
|
||||
{Py_mod_exec, (void*)_testcext_exec},
|
||||
{Py_mod_methods, _testcext_methods},
|
||||
{0, NULL}
|
||||
static PySlot _testcext_slots[] = {
|
||||
PySlot_STATIC_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_STATIC_DATA(Py_mod_name, STR(MODULE_NAME)),
|
||||
PySlot_STATIC_DATA(Py_mod_doc, (void*)(char*)_testcext_doc),
|
||||
PySlot_FUNC(Py_mod_exec, (void*)_testcext_exec),
|
||||
PySlot_STATIC_DATA(Py_mod_methods, _testcext_methods),
|
||||
PySlot_END,
|
||||
};
|
||||
|
||||
_Py_COMP_DIAG_POP
|
||||
|
||||
Generated
+1
@@ -743,6 +743,7 @@ SYMBOL_NAMES = (
|
||||
"PyType_Freeze",
|
||||
"PyType_FromMetaclass",
|
||||
"PyType_FromModuleAndSpec",
|
||||
"PyType_FromSlots",
|
||||
"PyType_FromSpec",
|
||||
"PyType_FromSpecWithBases",
|
||||
"PyType_GenericAlloc",
|
||||
|
||||
+16
-10
@@ -497,6 +497,8 @@ PYTHON_OBJS= \
|
||||
Python/qsbr.o \
|
||||
Python/bootstrap_hash.o \
|
||||
Python/specialize.o \
|
||||
Python/slots.o \
|
||||
Python/slots_generated.o \
|
||||
Python/stackrefs.o \
|
||||
Python/structmember.o \
|
||||
Python/symtable.o \
|
||||
@@ -1244,12 +1246,13 @@ PYTHON_HEADERS= \
|
||||
$(srcdir)/Include/refcount.h \
|
||||
$(srcdir)/Include/setobject.h \
|
||||
$(srcdir)/Include/sliceobject.h \
|
||||
$(srcdir)/Include/slots.h \
|
||||
$(srcdir)/Include/slots_generated.h \
|
||||
$(srcdir)/Include/structmember.h \
|
||||
$(srcdir)/Include/structseq.h \
|
||||
$(srcdir)/Include/sysmodule.h \
|
||||
$(srcdir)/Include/traceback.h \
|
||||
$(srcdir)/Include/tupleobject.h \
|
||||
$(srcdir)/Include/typeslots.h \
|
||||
$(srcdir)/Include/unicodeobject.h \
|
||||
$(srcdir)/Include/warnings.h \
|
||||
$(srcdir)/Include/weakrefobject.h \
|
||||
@@ -1432,6 +1435,8 @@ PYTHON_HEADERS= \
|
||||
$(srcdir)/Include/internal/pycore_setobject.h \
|
||||
$(srcdir)/Include/internal/pycore_signal.h \
|
||||
$(srcdir)/Include/internal/pycore_sliceobject.h \
|
||||
$(srcdir)/Include/internal/pycore_slots.h \
|
||||
$(srcdir)/Include/internal/pycore_slots_generated.h \
|
||||
$(srcdir)/Include/internal/pycore_stats.h \
|
||||
$(srcdir)/Include/internal/pycore_strhex.h \
|
||||
$(srcdir)/Include/internal/pycore_stackref.h \
|
||||
@@ -1941,7 +1946,7 @@ regen-unicodedata:
|
||||
|
||||
# "clinic" is regenerated implicitly via "regen-global-objects".
|
||||
.PHONY: regen-all
|
||||
regen-all: regen-cases regen-typeslots \
|
||||
regen-all: regen-cases regen-slots \
|
||||
regen-token regen-ast regen-keyword regen-sre regen-frozen \
|
||||
regen-pegen-metaparser regen-pegen regen-test-frozenmain \
|
||||
regen-test-levenshtein regen-global-objects
|
||||
@@ -2314,16 +2319,17 @@ Python/import.o: $(srcdir)/Include/pydtrace.h
|
||||
Python/pydtrace.o: $(srcdir)/Include/pydtrace.d $(DTRACE_DEPS)
|
||||
CC="$(CC)" CFLAGS="$(CFLAGS)" $(DTRACE) $(DFLAGS) -o $@ -G -s $(srcdir)/Include/pydtrace.d $(DTRACE_DEPS)
|
||||
|
||||
Objects/typeobject.o: Objects/typeslots.inc
|
||||
|
||||
.PHONY: regen-typeslots
|
||||
regen-typeslots:
|
||||
# Regenerate Objects/typeslots.inc from Include/typeslotsh
|
||||
# using Objects/typeslots.py
|
||||
$(PYTHON_FOR_REGEN) $(srcdir)/Objects/typeslots.py \
|
||||
< $(srcdir)/Include/typeslots.h \
|
||||
$(srcdir)/Objects/typeslots.inc.new
|
||||
$(UPDATE_FILE) $(srcdir)/Objects/typeslots.inc $(srcdir)/Objects/typeslots.inc.new
|
||||
echo 'NOTE: "regen-typeslots" was renamed to "regen-slots"'
|
||||
$(MAKE) regen-slots
|
||||
|
||||
.PHONY: regen-slots
|
||||
regen-slots: Python/slots.toml
|
||||
# Regenerate {Python,Include}/slots_generated.{c,h}
|
||||
# from Python/slots.toml using Tools/build/generate_slots.py
|
||||
$(PYTHON_FOR_REGEN) $(srcdir)/Tools/build/generate_slots.py \
|
||||
--generate-all
|
||||
|
||||
$(LIBRARY_OBJS) $(MODOBJS) Programs/python.o: $(PYTHON_HEADERS)
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Implement :pep:`820`: Unified slot system for the C API.
|
||||
@@ -296,10 +296,12 @@
|
||||
[const.Py_sq_inplace_repeat]
|
||||
added = '3.2'
|
||||
[const.Py_mp_length]
|
||||
# Note: value changed in 3.15 (old value is still accepted)
|
||||
added = '3.2'
|
||||
[const.Py_mp_subscript]
|
||||
added = '3.2'
|
||||
[const.Py_mp_ass_subscript]
|
||||
# Note: value changed in 3.15 (old value is still accepted)
|
||||
added = '3.2'
|
||||
|
||||
[typedef.Py_uintptr_t]
|
||||
@@ -1914,8 +1916,10 @@
|
||||
[data.PyModuleDef_Type]
|
||||
added = '3.5'
|
||||
[const.Py_mod_create]
|
||||
# Note: value changed in 3.15 (old value is still accepted)
|
||||
added = '3.5'
|
||||
[const.Py_mod_exec]
|
||||
# Note: value changed in 3.15 (old value is still accepted)
|
||||
added = '3.5'
|
||||
[struct.PyModuleDef_Slot]
|
||||
added = '3.5'
|
||||
@@ -2325,8 +2329,10 @@
|
||||
[function.PyMemoryView_FromBuffer]
|
||||
added = '3.11'
|
||||
[const.Py_bf_getbuffer]
|
||||
# Note: value changed in 3.15 (old value is still accepted)
|
||||
added = '3.11'
|
||||
[const.Py_bf_releasebuffer]
|
||||
# Note: value changed in 3.15 (old value is still accepted)
|
||||
added = '3.11'
|
||||
|
||||
# Constants for Py_buffer API added to this list in Python 3.11.1 (https://github.com/python/cpython/issues/98680)
|
||||
@@ -2463,6 +2469,7 @@
|
||||
[const.Py_TPFLAGS_ITEMS_AT_END]
|
||||
added = '3.12'
|
||||
[const.Py_mod_multiple_interpreters]
|
||||
# Note: value changed in 3.15 (old value is still accepted)
|
||||
added = '3.12'
|
||||
|
||||
[function.PyImport_AddModuleRef]
|
||||
@@ -2544,6 +2551,7 @@
|
||||
[function.PyEval_GetFrameLocals]
|
||||
added = '3.13'
|
||||
[const.Py_mod_gil]
|
||||
# Note: value changed in 3.15 (old value is still accepted)
|
||||
added = '3.13'
|
||||
|
||||
[function.Py_TYPE]
|
||||
@@ -2711,8 +2719,62 @@
|
||||
added = '3.15'
|
||||
[macro.Py_END_CRITICAL_SECTION2]
|
||||
added = '3.15'
|
||||
[struct.PySlot]
|
||||
added = '3.15'
|
||||
struct_abi_kind = 'full-abi'
|
||||
[function.PyType_FromSlots]
|
||||
added = '3.15'
|
||||
[const.PySlot_OPTIONAL]
|
||||
added = '3.15'
|
||||
[const.PySlot_STATIC]
|
||||
added = '3.15'
|
||||
[const.PySlot_INTPTR]
|
||||
added = '3.15'
|
||||
[macro.PySlot_DATA]
|
||||
added = '3.15'
|
||||
[macro.PySlot_FUNC]
|
||||
added = '3.15'
|
||||
[macro.PySlot_SIZE]
|
||||
added = '3.15'
|
||||
[macro.PySlot_INT64]
|
||||
added = '3.15'
|
||||
[macro.PySlot_UINT64]
|
||||
added = '3.15'
|
||||
[macro.PySlot_STATIC_DATA]
|
||||
added = '3.15'
|
||||
[macro.PySlot_END]
|
||||
added = '3.15'
|
||||
[macro.PySlot_PTR]
|
||||
added = '3.15'
|
||||
[macro.PySlot_PTR_STATIC]
|
||||
added = '3.15'
|
||||
[const.Py_slot_end]
|
||||
added = '3.15'
|
||||
[const.Py_slot_invalid]
|
||||
added = '3.15'
|
||||
[const.Py_slot_subslots]
|
||||
added = '3.15'
|
||||
[const.Py_tp_slots]
|
||||
added = '3.15'
|
||||
[const.Py_mod_slots]
|
||||
added = '3.15'
|
||||
[const.Py_tp_name]
|
||||
added = '3.15'
|
||||
[const.Py_tp_basicsize]
|
||||
added = '3.15'
|
||||
[const.Py_tp_extra_basicsize]
|
||||
added = '3.15'
|
||||
[const.Py_tp_itemsize]
|
||||
added = '3.15'
|
||||
[const.Py_tp_flags]
|
||||
added = '3.15'
|
||||
[const.Py_tp_metaclass]
|
||||
added = '3.15'
|
||||
[const.Py_tp_module]
|
||||
added = '3.15'
|
||||
|
||||
# PEP 757 import/export API.
|
||||
|
||||
[function.PyLong_GetNativeLayout]
|
||||
added = '3.15'
|
||||
[function.PyLong_Export]
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
|
||||
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c _testinternalcapi/complex.c _testinternalcapi/interpreter.c _testinternalcapi/tuple.c
|
||||
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/modsupport.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c _testcapi/type.c _testcapi/function.c _testcapi/module.c
|
||||
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/threadstate.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c
|
||||
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/slots.c _testlimitedcapi/sys.c _testlimitedcapi/threadstate.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c
|
||||
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
|
||||
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
|
||||
|
||||
|
||||
+131
-82
@@ -13,8 +13,8 @@ PyABIInfo_VAR(abi_info);
|
||||
static PyObject *
|
||||
module_from_slots_empty(PyObject *self, PyObject *spec)
|
||||
{
|
||||
PyModuleDef_Slot slots[] = {
|
||||
{0},
|
||||
PySlot slots[] = {
|
||||
PySlot_END,
|
||||
};
|
||||
return PyModule_FromSlotsAndSpec(slots, spec);
|
||||
}
|
||||
@@ -22,9 +22,9 @@ module_from_slots_empty(PyObject *self, PyObject *spec)
|
||||
static PyObject *
|
||||
module_from_slots_minimal(PyObject *self, PyObject *spec)
|
||||
{
|
||||
PyModuleDef_Slot slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{0},
|
||||
PySlot slots[] = {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_END,
|
||||
};
|
||||
return PyModule_FromSlotsAndSpec(slots, spec);
|
||||
}
|
||||
@@ -38,12 +38,12 @@ module_from_slots_null(PyObject *self, PyObject *spec)
|
||||
static PyObject *
|
||||
module_from_slots_name(PyObject *self, PyObject *spec)
|
||||
{
|
||||
PyModuleDef_Slot slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_name, "currently ignored..."},
|
||||
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
||||
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
||||
{0},
|
||||
PySlot slots[] = {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_DATA(Py_mod_name, "currently ignored..."),
|
||||
PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
|
||||
PySlot_END,
|
||||
};
|
||||
return PyModule_FromSlotsAndSpec(slots, spec);
|
||||
}
|
||||
@@ -51,12 +51,12 @@ module_from_slots_name(PyObject *self, PyObject *spec)
|
||||
static PyObject *
|
||||
module_from_slots_doc(PyObject *self, PyObject *spec)
|
||||
{
|
||||
PyModuleDef_Slot slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_doc, "the docstring"},
|
||||
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
||||
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
||||
{0},
|
||||
PySlot slots[] = {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_DATA(Py_mod_doc, "the docstring"),
|
||||
PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
|
||||
PySlot_END,
|
||||
};
|
||||
return PyModule_FromSlotsAndSpec(slots, spec);
|
||||
}
|
||||
@@ -64,12 +64,12 @@ module_from_slots_doc(PyObject *self, PyObject *spec)
|
||||
static PyObject *
|
||||
module_from_slots_size(PyObject *self, PyObject *spec)
|
||||
{
|
||||
PyModuleDef_Slot slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_state_size, (void*)123},
|
||||
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
||||
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
||||
{0},
|
||||
PySlot slots[] = {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_SIZE(Py_mod_state_size, (void*)123),
|
||||
PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
|
||||
PySlot_END,
|
||||
};
|
||||
PyObject *mod = PyModule_FromSlotsAndSpec(slots, spec);
|
||||
if (!mod) {
|
||||
@@ -92,12 +92,12 @@ static PyMethodDef a_methoddef_array[] = {
|
||||
static PyObject *
|
||||
module_from_slots_methods(PyObject *self, PyObject *spec)
|
||||
{
|
||||
PyModuleDef_Slot slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_methods, a_methoddef_array},
|
||||
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
||||
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
||||
{0},
|
||||
PySlot slots[] = {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_STATIC_DATA(Py_mod_methods, a_methoddef_array),
|
||||
PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
|
||||
PySlot_END,
|
||||
};
|
||||
return PyModule_FromSlotsAndSpec(slots, spec);
|
||||
}
|
||||
@@ -111,14 +111,14 @@ static void noop_free(void *self) { }
|
||||
static PyObject *
|
||||
module_from_slots_gc(PyObject *self, PyObject *spec)
|
||||
{
|
||||
PyModuleDef_Slot slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_state_traverse, noop_traverse},
|
||||
{Py_mod_state_clear, noop_clear},
|
||||
{Py_mod_state_free, noop_free},
|
||||
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
||||
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
||||
{0},
|
||||
PySlot slots[] = {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_FUNC(Py_mod_state_traverse, noop_traverse),
|
||||
PySlot_FUNC(Py_mod_state_clear, noop_clear),
|
||||
PySlot_FUNC(Py_mod_state_free, noop_free),
|
||||
PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
|
||||
PySlot_END,
|
||||
};
|
||||
PyObject *mod = PyModule_FromSlotsAndSpec(slots, spec);
|
||||
if (!mod) {
|
||||
@@ -144,12 +144,12 @@ static const char test_token;
|
||||
static PyObject *
|
||||
module_from_slots_token(PyObject *self, PyObject *spec)
|
||||
{
|
||||
PyModuleDef_Slot slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_token, (void*)&test_token},
|
||||
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
||||
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
||||
{0},
|
||||
PySlot slots[] = {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_DATA(Py_mod_token, (void*)&test_token),
|
||||
PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
|
||||
PySlot_END,
|
||||
};
|
||||
PyObject *mod = PyModule_FromSlotsAndSpec(slots, spec);
|
||||
if (!mod) {
|
||||
@@ -175,12 +175,12 @@ simple_exec(PyObject *module)
|
||||
static PyObject *
|
||||
module_from_slots_exec(PyObject *self, PyObject *spec)
|
||||
{
|
||||
PyModuleDef_Slot slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_exec, simple_exec},
|
||||
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
||||
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
||||
{0},
|
||||
PySlot slots[] = {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_FUNC(Py_mod_exec, simple_exec),
|
||||
PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
|
||||
PySlot_END,
|
||||
};
|
||||
PyObject *mod = PyModule_FromSlotsAndSpec(slots, spec);
|
||||
if (!mod) {
|
||||
@@ -209,12 +209,12 @@ create_attr_from_spec(PyObject *spec, PyModuleDef *def)
|
||||
static PyObject *
|
||||
module_from_slots_create(PyObject *self, PyObject *spec)
|
||||
{
|
||||
PyModuleDef_Slot slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_create, create_attr_from_spec},
|
||||
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
||||
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
||||
{0},
|
||||
const PySlot slots[] = {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_FUNC(Py_mod_create, create_attr_from_spec),
|
||||
PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
|
||||
PySlot_END,
|
||||
};
|
||||
return PyModule_FromSlotsAndSpec(slots, spec);
|
||||
}
|
||||
@@ -241,13 +241,13 @@ module_from_slots_repeat_slot(PyObject *self, PyObject *spec)
|
||||
if (slot_id < 0) {
|
||||
return NULL;
|
||||
}
|
||||
PyModuleDef_Slot slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{slot_id, "anything"},
|
||||
{slot_id, "anything else"},
|
||||
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
||||
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
||||
{0},
|
||||
const PySlot slots[] = {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_PTR_STATIC(slot_id, "anything"),
|
||||
PySlot_PTR_STATIC(slot_id, "anything_else"),
|
||||
PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
|
||||
PySlot_END,
|
||||
};
|
||||
return PyModule_FromSlotsAndSpec(slots, spec);
|
||||
}
|
||||
@@ -259,12 +259,12 @@ module_from_slots_null_slot(PyObject *self, PyObject *spec)
|
||||
if (slot_id < 0) {
|
||||
return NULL;
|
||||
}
|
||||
PyModuleDef_Slot slots[] = {
|
||||
{slot_id, NULL},
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
||||
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
||||
{0},
|
||||
const PySlot slots[] = {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_PTR_STATIC(slot_id, NULL),
|
||||
PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
|
||||
PySlot_END,
|
||||
};
|
||||
return PyModule_FromSlotsAndSpec(slots, spec);
|
||||
}
|
||||
@@ -347,12 +347,12 @@ module_from_bad_abiinfo(PyObject *self, PyObject *spec)
|
||||
1, 0,
|
||||
.abi_version=0x02080000,
|
||||
};
|
||||
PyModuleDef_Slot slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_abi, &bad_abi_info},
|
||||
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
||||
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
||||
{0},
|
||||
PySlot slots[] = {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_DATA(Py_mod_abi, &bad_abi_info),
|
||||
PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
|
||||
PySlot_END,
|
||||
};
|
||||
return PyModule_FromSlotsAndSpec(slots, spec);
|
||||
}
|
||||
@@ -365,14 +365,14 @@ module_from_multiple_abiinfo(PyObject *self, PyObject *spec)
|
||||
.flags=PyABIInfo_STABLE | PyABIInfo_FREETHREADING_AGNOSTIC,
|
||||
.abi_version=0x03040000,
|
||||
};
|
||||
PyModuleDef_Slot slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_abi, &extra_abi_info},
|
||||
{Py_mod_abi, &extra_abi_info},
|
||||
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
||||
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
||||
{0},
|
||||
PySlot slots[] = {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_DATA(Py_mod_abi, &extra_abi_info),
|
||||
PySlot_DATA(Py_mod_abi, &extra_abi_info),
|
||||
PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
|
||||
PySlot_END,
|
||||
};
|
||||
return PyModule_FromSlotsAndSpec(slots, spec);
|
||||
}
|
||||
@@ -466,6 +466,53 @@ pymodule_get_state_size(PyObject *self, PyObject *module)
|
||||
return PyLong_FromSsize_t(size);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
module_from_null_def_slot(PyObject* Py_UNUSED(module), PyObject *args)
|
||||
{
|
||||
long slot_number;
|
||||
PyObject *spec;
|
||||
if (!PyArg_ParseTuple(args, "lO", &slot_number, &spec)) {
|
||||
return NULL;
|
||||
}
|
||||
static PyModuleDef_Slot slots[] = {
|
||||
{0, NULL},
|
||||
{0},
|
||||
};
|
||||
static PyModuleDef def = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
.m_name = "mymod",
|
||||
.m_slots = slots,
|
||||
};
|
||||
// hack: def is supposed to be constant.
|
||||
// Don't do this at home; use PyModule_FromSlotsAndSpec throwaway
|
||||
// definitions!
|
||||
slots[0].slot = slot_number;
|
||||
return PyModule_FromDefAndSpec(&def, spec);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
module_from_def_nonstatic_nested(PyObject* Py_UNUSED(module), PyObject *spec)
|
||||
{
|
||||
static PyModuleDef_Slot subsubslots[] = {
|
||||
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
||||
{0},
|
||||
};
|
||||
static PySlot subslots[] = {
|
||||
PySlot_DATA(Py_mod_slots, subsubslots),
|
||||
PySlot_END,
|
||||
};
|
||||
static PyModuleDef_Slot slots[] = {
|
||||
{Py_slot_subslots, subslots},
|
||||
{0},
|
||||
};
|
||||
static PyModuleDef def = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
.m_name = "mymod",
|
||||
.m_slots = slots,
|
||||
};
|
||||
return PyModule_FromDefAndSpec(&def, spec);
|
||||
}
|
||||
|
||||
static PyMethodDef test_methods[] = {
|
||||
{"module_from_slots_empty", module_from_slots_empty, METH_O},
|
||||
{"module_from_slots_minimal", module_from_slots_minimal, METH_O},
|
||||
@@ -489,6 +536,8 @@ static PyMethodDef test_methods[] = {
|
||||
{"pymodule_get_def", pymodule_get_def, METH_O},
|
||||
{"pymodule_get_state_size", pymodule_get_state_size, METH_O},
|
||||
{"pymodule_exec", pymodule_exec, METH_O},
|
||||
{"module_from_null_def_slot", module_from_null_def_slot, METH_VARARGS},
|
||||
{"module_from_def_nonstatic_nested", module_from_def_nonstatic_nested, METH_O},
|
||||
{NULL},
|
||||
};
|
||||
|
||||
|
||||
@@ -110,9 +110,9 @@ test_get_statictype_slots(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *over_value = PyType_GetSlot(&PyLong_Type, Py_bf_releasebuffer + 1);
|
||||
void *over_value = PyType_GetSlot(&PyLong_Type, Py_mod_name + 1);
|
||||
if (over_value != NULL) {
|
||||
PyErr_SetString(PyExc_AssertionError, "mismatch: max+1 of long");
|
||||
PyErr_SetString(PyExc_AssertionError, "mismatch: mod_name of long");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,9 @@ PyInit__testlimitedcapi(void)
|
||||
if (_PyTestLimitedCAPI_Init_Set(mod) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (_PyTestLimitedCAPI_Init_Slots(mod) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (_PyTestLimitedCAPI_Init_Sys(mod) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ int _PyTestLimitedCAPI_Init_List(PyObject *module);
|
||||
int _PyTestLimitedCAPI_Init_Long(PyObject *module);
|
||||
int _PyTestLimitedCAPI_Init_PyOS(PyObject *module);
|
||||
int _PyTestLimitedCAPI_Init_Set(PyObject *module);
|
||||
int _PyTestLimitedCAPI_Init_Slots(PyObject *module);
|
||||
int _PyTestLimitedCAPI_Init_Sys(PyObject *module);
|
||||
int _PyTestLimitedCAPI_Init_ThreadState(PyObject *module);
|
||||
int _PyTestLimitedCAPI_Init_Tuple(PyObject *module);
|
||||
|
||||
@@ -0,0 +1,629 @@
|
||||
#define Py_LIMITED_API 0x030f0000
|
||||
|
||||
#include "parts.h"
|
||||
|
||||
PyABIInfo_VAR(abi_info);
|
||||
|
||||
/* Define a bunch of (mostly nonsensical) functions to put in slots, so
|
||||
* Lib/test/test_capi/test_slots.py can verify they've been assigned to
|
||||
* the right slots.
|
||||
|
||||
* This module is full of "magic constants" which simply need to match
|
||||
* between the C and Python part of the tests.
|
||||
*/
|
||||
|
||||
// getbufferproc: export buffer; increment a counter
|
||||
static int
|
||||
demo_getbuffer(PyObject *exporter, Py_buffer *view, int flags)
|
||||
{
|
||||
Py_INCREF(exporter);
|
||||
// PyObject_GetTypeData & Py_TYPE: safe on non-subclassable type
|
||||
int *data = PyObject_GetTypeData(exporter, Py_TYPE(exporter));
|
||||
if (!data) {
|
||||
return -1;
|
||||
}
|
||||
(*data)++;
|
||||
return PyBuffer_FillInfo(view, exporter, "buf", 4, 1, flags);
|
||||
}
|
||||
|
||||
// releasebufferproc: release buffer; decrement a counter
|
||||
static void
|
||||
demo_releasebuffer(PyObject *exporter, Py_buffer *view)
|
||||
{
|
||||
Py_DECREF(exporter);
|
||||
// PyObject_GetTypeData & Py_TYPE: safe on non-subclassable type
|
||||
int *data = PyObject_GetTypeData(exporter, Py_TYPE(exporter));
|
||||
if (!data) {
|
||||
PyErr_WriteUnraisable(exporter);
|
||||
return;
|
||||
}
|
||||
(*data)--;
|
||||
return;
|
||||
}
|
||||
|
||||
// objobjargproc: raise KeyError
|
||||
static int
|
||||
demo_ass_subscript(PyObject *o, PyObject *key, PyObject *v)
|
||||
{
|
||||
PyErr_Format(PyExc_KeyError, "I don't like that key");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// lenfunc: report 456
|
||||
static Py_ssize_t
|
||||
demo_length(PyObject *o)
|
||||
{
|
||||
return (Py_ssize_t)456;
|
||||
}
|
||||
|
||||
// binaryfunc; return constant value
|
||||
static PyObject *binop_123(PyObject* a, PyObject *b) { return PyLong_FromLong(123); }
|
||||
static PyObject *binop_234(PyObject* a, PyObject *b) { return PyLong_FromLong(234); }
|
||||
static PyObject *binop_345(PyObject* a, PyObject *b) { return PyLong_FromLong(345); }
|
||||
static PyObject *binop_456(PyObject* a, PyObject *b) { return PyLong_FromLong(456); }
|
||||
static PyObject *binop_567(PyObject* a, PyObject *b) { return PyLong_FromLong(567); }
|
||||
static PyObject *binop_678(PyObject* a, PyObject *b) { return PyLong_FromLong(678); }
|
||||
|
||||
static PyObject *
|
||||
type_from_slots(PyObject* module, PyObject *args)
|
||||
{
|
||||
char *case_name;
|
||||
if (!PyArg_ParseTuple(args, "s", &case_name)) {
|
||||
return NULL;
|
||||
}
|
||||
#define CASE(NAME) \
|
||||
if (strcmp(case_name, NAME) == 0) { \
|
||||
return PyType_FromSlots((PySlot[]) { \
|
||||
PySlot_DATA(Py_tp_name, "_testlimitedcapi.MyType"), \
|
||||
PySlot_DATA(Py_tp_module, module), \
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
#define ENDCASE() \
|
||||
PySlot_END \
|
||||
}); \
|
||||
} \
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CASE("basic")
|
||||
ENDCASE()
|
||||
CASE("foreign_slot")
|
||||
PySlot_DATA(Py_mod_name, "this is not a module"),
|
||||
ENDCASE()
|
||||
CASE("basicsize")
|
||||
PySlot_SIZE(Py_tp_basicsize, 256),
|
||||
ENDCASE()
|
||||
CASE("extra_basicsize")
|
||||
PySlot_SIZE(Py_tp_extra_basicsize, 256),
|
||||
ENDCASE()
|
||||
CASE("itemsize")
|
||||
PySlot_SIZE(Py_tp_itemsize, 16),
|
||||
ENDCASE()
|
||||
CASE("flags")
|
||||
PySlot_UINT64(Py_tp_flags,
|
||||
Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_BASETYPE),
|
||||
ENDCASE()
|
||||
CASE("matmul_123")
|
||||
PySlot_FUNC(Py_nb_matrix_multiply, binop_123),
|
||||
ENDCASE()
|
||||
CASE("optional_end")
|
||||
{.sl_flags=PySlot_OPTIONAL},
|
||||
ENDCASE()
|
||||
CASE("invalid")
|
||||
{.sl_id=Py_slot_invalid},
|
||||
ENDCASE()
|
||||
CASE("invalid_fbad")
|
||||
{.sl_id=0xfbad},
|
||||
ENDCASE()
|
||||
CASE("optional_invalid")
|
||||
{.sl_id=Py_slot_invalid, .sl_flags=PySlot_OPTIONAL},
|
||||
PySlot_SIZE(Py_tp_extra_basicsize, 256),
|
||||
ENDCASE()
|
||||
CASE("optional_invalid_fbad")
|
||||
{.sl_id=0xfbad, .sl_flags=PySlot_OPTIONAL},
|
||||
PySlot_SIZE(Py_tp_extra_basicsize, 256),
|
||||
ENDCASE()
|
||||
CASE("old_slot_numbers")
|
||||
PySlot_FUNC(1, demo_getbuffer),
|
||||
PySlot_FUNC(2, demo_releasebuffer),
|
||||
PySlot_FUNC(3, demo_ass_subscript),
|
||||
PySlot_FUNC(4, demo_length),
|
||||
PySlot_SIZE(Py_tp_extra_basicsize, sizeof(int)),
|
||||
PySlot_STATIC_DATA(Py_tp_members, ((PyMemberDef[]) {
|
||||
{"buf_counter", Py_T_INT, 0, Py_READONLY | Py_RELATIVE_OFFSET},
|
||||
{NULL},
|
||||
})),
|
||||
ENDCASE()
|
||||
CASE("new_slot_numbers")
|
||||
PySlot_FUNC(88, demo_getbuffer),
|
||||
PySlot_FUNC(89, demo_releasebuffer),
|
||||
PySlot_FUNC(90, demo_ass_subscript),
|
||||
PySlot_FUNC(91, demo_length),
|
||||
PySlot_SIZE(Py_tp_extra_basicsize, sizeof(int)),
|
||||
PySlot_STATIC_DATA(Py_tp_members, ((PyMemberDef[]) {
|
||||
{"buf_counter", Py_T_INT, 0, Py_READONLY | Py_RELATIVE_OFFSET},
|
||||
{NULL},
|
||||
})),
|
||||
ENDCASE()
|
||||
CASE("nonstatic_tp_members")
|
||||
PySlot_SIZE(Py_tp_extra_basicsize, sizeof(int)),
|
||||
PySlot_DATA(Py_tp_members, ((PyMemberDef[]) {
|
||||
{"buf_counter", Py_T_INT, 0, Py_READONLY | Py_RELATIVE_OFFSET},
|
||||
{NULL},
|
||||
})),
|
||||
ENDCASE()
|
||||
CASE("intptr_flags_macro")
|
||||
PySlot_PTR(Py_tp_flags, (void*)(intptr_t)Py_TPFLAGS_IMMUTABLETYPE),
|
||||
ENDCASE()
|
||||
CASE("intptr_flags_struct")
|
||||
{.sl_id=Py_tp_flags,
|
||||
.sl_flags=PySlot_INTPTR,
|
||||
.sl_ptr=(void*)(intptr_t)Py_TPFLAGS_IMMUTABLETYPE,
|
||||
},
|
||||
ENDCASE()
|
||||
CASE("intptr_static")
|
||||
PySlot_SIZE(Py_tp_extra_basicsize, sizeof(int)),
|
||||
PySlot_PTR_STATIC(Py_tp_members, ((PyMemberDef[]) {
|
||||
{"attribute", Py_T_INT, 0, Py_READONLY | Py_RELATIVE_OFFSET},
|
||||
{NULL},
|
||||
})),
|
||||
ENDCASE()
|
||||
CASE("nested")
|
||||
PySlot_FUNC(Py_nb_add, binop_123),
|
||||
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_FUNC(Py_nb_subtract, binop_234),
|
||||
PySlot_END,
|
||||
})),
|
||||
ENDCASE()
|
||||
CASE("nested_max")
|
||||
PySlot_FUNC(Py_nb_add, binop_123),
|
||||
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_FUNC(Py_nb_subtract, binop_234),
|
||||
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_FUNC(Py_nb_multiply, binop_345),
|
||||
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_FUNC(Py_nb_true_divide, binop_456),
|
||||
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_FUNC(Py_nb_remainder, binop_567),
|
||||
PySlot_END
|
||||
})),
|
||||
PySlot_END,
|
||||
})),
|
||||
PySlot_END,
|
||||
})),
|
||||
PySlot_END,
|
||||
})),
|
||||
ENDCASE()
|
||||
CASE("nested_over_limit")
|
||||
PySlot_FUNC(Py_nb_add, binop_123),
|
||||
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_FUNC(Py_nb_subtract, binop_234),
|
||||
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_FUNC(Py_nb_multiply, binop_345),
|
||||
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_FUNC(Py_nb_true_divide, binop_456),
|
||||
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_FUNC(Py_nb_remainder, binop_567),
|
||||
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_FUNC(Py_nb_xor, binop_678),
|
||||
PySlot_END
|
||||
})),
|
||||
PySlot_END
|
||||
})),
|
||||
PySlot_END,
|
||||
})),
|
||||
PySlot_END,
|
||||
})),
|
||||
PySlot_END,
|
||||
})),
|
||||
ENDCASE()
|
||||
CASE("nested_old")
|
||||
PySlot_FUNC(Py_nb_add, binop_123),
|
||||
PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) {
|
||||
{Py_nb_subtract, binop_234},
|
||||
{0},
|
||||
})),
|
||||
ENDCASE()
|
||||
CASE("nested_old_max")
|
||||
PySlot_FUNC(Py_nb_add, binop_123),
|
||||
PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) {
|
||||
{Py_nb_subtract, binop_234},
|
||||
{Py_tp_slots, ((PyType_Slot[]) {
|
||||
{Py_nb_multiply, binop_345},
|
||||
{Py_tp_slots, ((PyType_Slot[]) {
|
||||
{Py_nb_true_divide, binop_456},
|
||||
{Py_tp_slots, ((PyType_Slot[]) {
|
||||
{Py_nb_remainder, binop_567},
|
||||
{0},
|
||||
})},
|
||||
{0},
|
||||
})},
|
||||
{0},
|
||||
})},
|
||||
{0},
|
||||
})),
|
||||
ENDCASE()
|
||||
CASE("nested_old_over_limit")
|
||||
PySlot_FUNC(Py_nb_add, binop_123),
|
||||
PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) {
|
||||
{Py_nb_subtract, binop_234},
|
||||
{Py_tp_slots, ((PyType_Slot[]) {
|
||||
{Py_nb_multiply, binop_345},
|
||||
{Py_tp_slots, ((PyType_Slot[]) {
|
||||
{Py_nb_true_divide, binop_456},
|
||||
{Py_tp_slots, ((PyType_Slot[]) {
|
||||
{Py_nb_remainder, binop_567},
|
||||
{Py_tp_slots, ((PyType_Slot[]) {
|
||||
{Py_nb_xor, binop_678},
|
||||
{0},
|
||||
})},
|
||||
{0},
|
||||
})},
|
||||
{0},
|
||||
})},
|
||||
{0},
|
||||
})},
|
||||
{0},
|
||||
})),
|
||||
ENDCASE()
|
||||
CASE("nested_pingpong")
|
||||
PySlot_FUNC(Py_nb_add, binop_123),
|
||||
PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) {
|
||||
{Py_nb_subtract, binop_234},
|
||||
{Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_FUNC(Py_nb_multiply, binop_345),
|
||||
PySlot_DATA(Py_tp_slots, ((PyType_Slot[]) {
|
||||
{Py_nb_true_divide, binop_456},
|
||||
{Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_FUNC(Py_nb_remainder, binop_567),
|
||||
PySlot_END
|
||||
})},
|
||||
{0},
|
||||
})),
|
||||
PySlot_END,
|
||||
})},
|
||||
{0},
|
||||
})),
|
||||
ENDCASE()
|
||||
CASE("repeat_add")
|
||||
PySlot_FUNC(Py_nb_add, binop_123),
|
||||
PySlot_FUNC(Py_nb_add, binop_456),
|
||||
ENDCASE()
|
||||
CASE("repeat_module")
|
||||
PySlot_DATA(Py_tp_module, Py_True),
|
||||
PySlot_DATA(Py_tp_module, Py_False),
|
||||
ENDCASE()
|
||||
|
||||
#undef CASE
|
||||
#undef ENDCASE
|
||||
PyErr_Format(PyExc_SystemError, "bad case: %s", case_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
type_from_null_slot(PyObject* module, PyObject *args)
|
||||
{
|
||||
long slot_number;
|
||||
if (!PyArg_ParseTuple(args, "l", &slot_number)) {
|
||||
return NULL;
|
||||
}
|
||||
return PyType_FromSlots((PySlot[]) {
|
||||
PySlot_DATA(Py_tp_name, "_testlimitedcapi.MyType"),
|
||||
PySlot_DATA(Py_tp_module, module),
|
||||
PySlot_PTR_STATIC((uint16_t)slot_number, NULL),
|
||||
PySlot_END
|
||||
});
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
type_from_null_spec_slot(PyObject* Py_UNUSED(module), PyObject *args)
|
||||
{
|
||||
long slot_number;
|
||||
if (!PyArg_ParseTuple(args, "l", &slot_number)) {
|
||||
return NULL;
|
||||
}
|
||||
return PyType_FromSpec(&(PyType_Spec) {
|
||||
.name = "_testlimitedcapi.MyType",
|
||||
.slots = (PyType_Slot[]) {
|
||||
{slot_number, NULL},
|
||||
{0},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
demo_create(PyObject *spec, PyModuleDef *def)
|
||||
{
|
||||
assert(def == NULL);
|
||||
return Py_NewRef(spec);
|
||||
}
|
||||
|
||||
static int
|
||||
demo_exec(PyObject *mod)
|
||||
{
|
||||
return PyModule_AddStringConstant(mod, "exec_done", "yes");
|
||||
}
|
||||
|
||||
static PyMethodDef *TestMethods;
|
||||
|
||||
static PyObject *
|
||||
module_from_slots(PyObject* Py_UNUSED(module), PyObject *args)
|
||||
{
|
||||
PyObject *spec;
|
||||
char *case_name;
|
||||
if (!PyArg_ParseTuple(args, "sO", &case_name, &spec)) {
|
||||
return NULL;
|
||||
}
|
||||
PyObject *mod = NULL;
|
||||
#define CASE(NAME) \
|
||||
if (strcmp(case_name, NAME) == 0) { \
|
||||
mod = PyModule_FromSlotsAndSpec((PySlot[]) { \
|
||||
PySlot_DATA(Py_mod_abi, &abi_info), \
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), \
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
#define ENDCASE() \
|
||||
PySlot_END \
|
||||
}, spec); \
|
||||
} \
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CASE("basic")
|
||||
ENDCASE()
|
||||
CASE("foreign_slot")
|
||||
PySlot_DATA(Py_tp_name, "this is not a type"),
|
||||
ENDCASE()
|
||||
CASE("state_size")
|
||||
PySlot_SIZE(Py_mod_state_size, 42),
|
||||
ENDCASE()
|
||||
CASE("multi_interp")
|
||||
PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
|
||||
ENDCASE()
|
||||
CASE("exec")
|
||||
PySlot_FUNC(Py_mod_exec, demo_exec),
|
||||
ENDCASE()
|
||||
CASE("optional_end")
|
||||
{.sl_flags=PySlot_OPTIONAL},
|
||||
ENDCASE()
|
||||
CASE("invalid")
|
||||
{.sl_id=Py_slot_invalid},
|
||||
ENDCASE()
|
||||
CASE("invalid_fbad")
|
||||
{.sl_id=0xfbad},
|
||||
ENDCASE()
|
||||
CASE("optional_invalid")
|
||||
{.sl_id=Py_slot_invalid, .sl_flags=PySlot_OPTIONAL},
|
||||
PySlot_SIZE(Py_mod_exec, demo_exec),
|
||||
ENDCASE()
|
||||
CASE("optional_invalid_fbad")
|
||||
{.sl_id=0xfbad, .sl_flags=PySlot_OPTIONAL},
|
||||
PySlot_SIZE(Py_mod_exec, demo_exec),
|
||||
ENDCASE()
|
||||
CASE("old_slot_numbers")
|
||||
// 1: see old_slot_number_create case
|
||||
PySlot_FUNC(2, demo_exec),
|
||||
PySlot_DATA(3, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
|
||||
// 4: see module_from_gil_slot function
|
||||
ENDCASE()
|
||||
CASE("new_slot_numbers")
|
||||
// 84: see new_slot_number_create case
|
||||
PySlot_FUNC(85, demo_exec),
|
||||
PySlot_FUNC(86, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
|
||||
// 87: see module_from_gil_slot function
|
||||
ENDCASE()
|
||||
CASE("old_slot_number_create")
|
||||
PySlot_FUNC(1, demo_create),
|
||||
ENDCASE()
|
||||
CASE("new_slot_number_create")
|
||||
PySlot_FUNC(84, demo_create),
|
||||
ENDCASE()
|
||||
CASE("nonstatic_mod_methods")
|
||||
PySlot_DATA(Py_mod_methods, TestMethods),
|
||||
ENDCASE()
|
||||
CASE("intptr_methods")
|
||||
PySlot_PTR_STATIC(Py_mod_methods, TestMethods),
|
||||
ENDCASE()
|
||||
CASE("nested")
|
||||
PySlot_FUNC(Py_mod_exec, demo_exec),
|
||||
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_DATA(Py_mod_doc, "doc"),
|
||||
PySlot_END,
|
||||
})),
|
||||
ENDCASE()
|
||||
CASE("nested_max")
|
||||
PySlot_FUNC(Py_mod_exec, demo_exec),
|
||||
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_SIZE(Py_mod_state_size, 53),
|
||||
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_DATA(Py_mod_doc, "doc"),
|
||||
PySlot_END
|
||||
})),
|
||||
PySlot_END,
|
||||
})),
|
||||
PySlot_END,
|
||||
})),
|
||||
PySlot_END,
|
||||
})),
|
||||
ENDCASE()
|
||||
CASE("nested_over_limit")
|
||||
PySlot_FUNC(Py_mod_exec, demo_exec),
|
||||
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_SIZE(Py_mod_state_size, 53),
|
||||
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_DATA(Py_mod_doc, "doc"),
|
||||
PySlot_DATA(Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_END
|
||||
})),
|
||||
PySlot_END
|
||||
})),
|
||||
PySlot_END,
|
||||
})),
|
||||
PySlot_END,
|
||||
})),
|
||||
PySlot_END,
|
||||
})),
|
||||
ENDCASE()
|
||||
CASE("nested_old")
|
||||
PySlot_FUNC(Py_mod_exec, demo_exec),
|
||||
PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) {
|
||||
{Py_mod_doc, "doc"},
|
||||
{0},
|
||||
})),
|
||||
ENDCASE()
|
||||
CASE("nested_old_max")
|
||||
PySlot_FUNC(Py_mod_exec, demo_exec),
|
||||
PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) {
|
||||
{Py_mod_slots, ((PyModuleDef_Slot[]) {
|
||||
{Py_mod_state_size, (void*)(intptr_t)53},
|
||||
{Py_mod_slots, ((PyModuleDef_Slot[]) {
|
||||
{Py_mod_slots, ((PyModuleDef_Slot[]) {
|
||||
{Py_mod_doc, "doc"},
|
||||
{0},
|
||||
})},
|
||||
{0},
|
||||
})},
|
||||
{0},
|
||||
})},
|
||||
{0},
|
||||
})),
|
||||
ENDCASE()
|
||||
CASE("nested_old_over_limit")
|
||||
PySlot_FUNC(Py_mod_exec, demo_exec),
|
||||
PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) {
|
||||
{Py_mod_slots, ((PyModuleDef_Slot[]) {
|
||||
{Py_mod_state_size, (void*)(intptr_t)53},
|
||||
{Py_mod_slots, ((PyModuleDef_Slot[]) {
|
||||
{Py_mod_slots, ((PyModuleDef_Slot[]) {
|
||||
{Py_mod_slots, ((PyModuleDef_Slot[]) {
|
||||
{Py_mod_doc, "doc"},
|
||||
{0},
|
||||
})},
|
||||
{0},
|
||||
})},
|
||||
{0},
|
||||
})},
|
||||
{0},
|
||||
})},
|
||||
{0},
|
||||
})),
|
||||
ENDCASE()
|
||||
CASE("nested_pingpong")
|
||||
PySlot_FUNC(Py_mod_exec, demo_exec),
|
||||
PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) {
|
||||
{Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_DATA(Py_mod_slots, ((PyModuleDef_Slot[]) {
|
||||
{Py_mod_state_size, (void*)(intptr_t)53},
|
||||
{Py_slot_subslots, ((PySlot[]) {
|
||||
PySlot_DATA(Py_mod_doc, "doc"),
|
||||
PySlot_END
|
||||
})},
|
||||
{0},
|
||||
})),
|
||||
PySlot_END,
|
||||
})},
|
||||
{0},
|
||||
})),
|
||||
ENDCASE()
|
||||
CASE("repeat_create")
|
||||
PySlot_DATA(Py_mod_create, demo_create),
|
||||
PySlot_DATA(Py_mod_create, demo_create),
|
||||
PySlot_DATA(Py_mod_create, demo_create),
|
||||
ENDCASE()
|
||||
CASE("repeat_gil")
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
|
||||
ENDCASE()
|
||||
CASE("repeat_exec")
|
||||
PySlot_FUNC(Py_mod_exec, demo_exec),
|
||||
PySlot_FUNC(Py_mod_exec, demo_exec),
|
||||
ENDCASE()
|
||||
|
||||
#undef CASE
|
||||
#undef ENDCASE
|
||||
if (!mod) {
|
||||
if (!PyErr_Occurred()) {
|
||||
PyErr_Format(PyExc_SystemError, "bad case: %s", case_name);
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
if (PyModule_Check(mod)) {
|
||||
Py_ssize_t size;
|
||||
if (PyModule_GetStateSize(mod, &size) < 0) {
|
||||
Py_DECREF(mod);
|
||||
return NULL;
|
||||
}
|
||||
if (PyModule_AddIntConstant(mod, "state_size", (long)size) < 0) {
|
||||
Py_DECREF(mod);
|
||||
return NULL;
|
||||
}
|
||||
if (PyModule_Exec(mod) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return mod;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
module_from_gil_slot(PyObject* Py_UNUSED(module), PyObject *args)
|
||||
{
|
||||
long slot_number;
|
||||
PyObject *spec;
|
||||
if (!PyArg_ParseTuple(args, "lO", &slot_number, &spec)) {
|
||||
return NULL;
|
||||
}
|
||||
return PyModule_FromSlotsAndSpec((PySlot[]) {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_PTR_STATIC((uint16_t)slot_number, Py_MOD_GIL_NOT_USED),
|
||||
PySlot_END
|
||||
}, spec);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
module_from_null_slot(PyObject* Py_UNUSED(module), PyObject *args)
|
||||
{
|
||||
long slot_number;
|
||||
PyObject *spec;
|
||||
if (!PyArg_ParseTuple(args, "lO", &slot_number, &spec)) {
|
||||
return NULL;
|
||||
}
|
||||
uint16_t maybe_gil_slot = Py_mod_gil;
|
||||
if ((slot_number == 4) || (slot_number == 87)) {
|
||||
// Do not repeat the GIL slot
|
||||
maybe_gil_slot = Py_slot_invalid;
|
||||
}
|
||||
return PyModule_FromSlotsAndSpec((PySlot[]) {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_DATA(Py_mod_name, "mymod"),
|
||||
PySlot_PTR_STATIC((uint16_t)slot_number, NULL),
|
||||
{
|
||||
.sl_id=maybe_gil_slot,
|
||||
.sl_flags=PySlot_OPTIONAL,
|
||||
.sl_ptr=Py_MOD_GIL_NOT_USED,
|
||||
},
|
||||
PySlot_END
|
||||
}, spec);
|
||||
}
|
||||
|
||||
static PyMethodDef _TestMethods[] = {
|
||||
{"type_from_slots", type_from_slots, METH_VARARGS},
|
||||
{"module_from_gil_slot", module_from_gil_slot, METH_VARARGS},
|
||||
{"type_from_null_slot", type_from_null_slot, METH_VARARGS},
|
||||
{"type_from_null_spec_slot", type_from_null_spec_slot, METH_VARARGS},
|
||||
{"module_from_slots", module_from_slots, METH_VARARGS},
|
||||
{"module_from_null_slot", module_from_null_slot, METH_VARARGS},
|
||||
{NULL},
|
||||
};
|
||||
static PyMethodDef *TestMethods = _TestMethods;
|
||||
|
||||
int
|
||||
_PyTestLimitedCAPI_Init_Slots(PyObject *m)
|
||||
{
|
||||
if (PyModule_AddFunctions(m, TestMethods) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
+44
-44
@@ -603,7 +603,7 @@ PyInit__testmultiphase_null_slots(void)
|
||||
/**** Problematic modules ****/
|
||||
|
||||
static PyModuleDef_Slot slots_bad_large[] = {
|
||||
{_Py_mod_LAST_SLOT + 1, NULL},
|
||||
{Py_slot_invalid, NULL},
|
||||
{0, NULL},
|
||||
};
|
||||
|
||||
@@ -1051,12 +1051,12 @@ PyABIInfo_VAR(abi_info);
|
||||
PyMODEXPORT_FUNC
|
||||
PyModExport__test_from_modexport(void)
|
||||
{
|
||||
static PyModuleDef_Slot slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_name, "_test_from_modexport"},
|
||||
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
||||
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
||||
{0},
|
||||
static PySlot slots[] = {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_DATA(Py_mod_name, "_test_from_modexport"),
|
||||
PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
|
||||
PySlot_END,
|
||||
};
|
||||
return slots;
|
||||
}
|
||||
@@ -1064,12 +1064,12 @@ PyModExport__test_from_modexport(void)
|
||||
PyMODEXPORT_FUNC
|
||||
PyModExport__test_from_modexport_gil_used(void)
|
||||
{
|
||||
static PyModuleDef_Slot slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_name, "_test_from_modexport_gil_used"},
|
||||
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
||||
{Py_mod_gil, Py_MOD_GIL_USED},
|
||||
{0},
|
||||
static PySlot slots[] = {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_DATA(Py_mod_name, "_test_from_modexport_gil_used"),
|
||||
PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_USED),
|
||||
PySlot_END,
|
||||
};
|
||||
return slots;
|
||||
}
|
||||
@@ -1115,13 +1115,13 @@ modexport_create_string(PyObject *spec, PyModuleDef *def)
|
||||
PyMODEXPORT_FUNC
|
||||
PyModExport__test_from_modexport_create_nonmodule(void)
|
||||
{
|
||||
static PyModuleDef_Slot slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_name, "_test_from_modexport_create_nonmodule"},
|
||||
{Py_mod_create, modexport_create_string},
|
||||
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
||||
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
||||
{0},
|
||||
static PySlot slots[] = {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_DATA(Py_mod_name, "_test_from_modexport_create_nonmodule"),
|
||||
PySlot_FUNC(Py_mod_create, modexport_create_string),
|
||||
PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
|
||||
PySlot_END,
|
||||
};
|
||||
return slots;
|
||||
}
|
||||
@@ -1129,19 +1129,19 @@ PyModExport__test_from_modexport_create_nonmodule(void)
|
||||
PyMODEXPORT_FUNC
|
||||
PyModExport__test_from_modexport_create_nonmodule_gil_used(void)
|
||||
{
|
||||
static PyModuleDef_Slot slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_name, "_test_from_modexport_create_nonmodule"},
|
||||
{Py_mod_create, modexport_create_string},
|
||||
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
||||
{Py_mod_gil, Py_MOD_GIL_USED},
|
||||
{0},
|
||||
static PySlot slots[] = {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_DATA(Py_mod_name, "_test_from_modexport_create_nonmodule"),
|
||||
PySlot_FUNC(Py_mod_create, modexport_create_string),
|
||||
PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_USED),
|
||||
PySlot_END,
|
||||
};
|
||||
return slots;
|
||||
}
|
||||
|
||||
static PyModuleDef_Slot modexport_empty_slots[] = {
|
||||
{0},
|
||||
static PySlot modexport_empty_slots[] = {
|
||||
PySlot_END,
|
||||
};
|
||||
|
||||
PyMODEXPORT_FUNC
|
||||
@@ -1151,9 +1151,9 @@ PyModExport__test_from_modexport_empty_slots(void)
|
||||
}
|
||||
|
||||
|
||||
static PyModuleDef_Slot modexport_minimal_slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{0},
|
||||
static PySlot modexport_minimal_slots[] = {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_END,
|
||||
};
|
||||
|
||||
PyMODEXPORT_FUNC
|
||||
@@ -1234,18 +1234,18 @@ PyModExport__test_from_modexport_smoke(void)
|
||||
{"get_modexport_minimal_slots", modexport_get_minimal_slots, METH_NOARGS},
|
||||
{0},
|
||||
};
|
||||
static PyModuleDef_Slot slots[] = {
|
||||
{Py_mod_abi, &abi_info},
|
||||
{Py_mod_name, "_test_from_modexport_smoke"},
|
||||
{Py_mod_doc, "the expected docstring"},
|
||||
{Py_mod_exec, modexport_smoke_exec},
|
||||
{Py_mod_state_size, (void*)sizeof(int)},
|
||||
{Py_mod_methods, methods},
|
||||
{Py_mod_state_free, modexport_smoke_free},
|
||||
{Py_mod_token, (void*)&modexport_smoke_test_token},
|
||||
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
|
||||
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
||||
{0},
|
||||
static PySlot slots[] = {
|
||||
PySlot_DATA(Py_mod_abi, &abi_info),
|
||||
PySlot_DATA(Py_mod_name, "_test_from_modexport_smoke"),
|
||||
PySlot_DATA(Py_mod_doc, "the expected docstring"),
|
||||
PySlot_FUNC(Py_mod_exec, modexport_smoke_exec),
|
||||
PySlot_SIZE(Py_mod_state_size, (void*)sizeof(int)),
|
||||
PySlot_STATIC_DATA(Py_mod_methods, methods),
|
||||
PySlot_FUNC(Py_mod_state_free, modexport_smoke_free),
|
||||
PySlot_DATA(Py_mod_token, (void*)&modexport_smoke_test_token),
|
||||
PySlot_DATA(Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED),
|
||||
PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED),
|
||||
PySlot_END,
|
||||
};
|
||||
return slots;
|
||||
}
|
||||
|
||||
+98
-159
@@ -14,6 +14,7 @@
|
||||
#include "pycore_object.h" // _PyType_AllocNoTrack
|
||||
#include "pycore_pyerrors.h" // _PyErr_FormatFromCause()
|
||||
#include "pycore_pystate.h" // _PyInterpreterState_GET()
|
||||
#include "pycore_slots.h" // _PySlotIterator_Init
|
||||
#include "pycore_unicodeobject.h" // _PyUnicode_EqualToASCIIString()
|
||||
#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
|
||||
|
||||
@@ -396,22 +397,20 @@ _PyModule_CreateInitialized(PyModuleDef* module, int module_api_version)
|
||||
return (PyObject*)m;
|
||||
}
|
||||
|
||||
typedef PyObject *(*createfunc_t)(PyObject *, PyModuleDef*);
|
||||
|
||||
static PyObject *
|
||||
module_from_def_and_spec(
|
||||
PyModuleDef* def_like, /* not necessarily a valid Python object */
|
||||
module_from_slots_and_spec(
|
||||
const PySlot *slots,
|
||||
PyObject *spec,
|
||||
int module_api_version,
|
||||
PyModuleDef* original_def /* NULL if not defined by a def */)
|
||||
{
|
||||
PyModuleDef_Slot* cur_slot;
|
||||
PyObject *(*create)(PyObject *, PyModuleDef*) = NULL;
|
||||
createfunc_t create = NULL;
|
||||
PyObject *nameobj;
|
||||
PyObject *m = NULL;
|
||||
int has_multiple_interpreters_slot = 0;
|
||||
void *multiple_interpreters = (void *)0;
|
||||
int has_gil_slot = 0;
|
||||
uint64_t multiple_interpreters = (uint64_t)Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED;
|
||||
bool requires_gil = true;
|
||||
int has_execution_slots = 0;
|
||||
const char *name;
|
||||
int ret;
|
||||
void *token = NULL;
|
||||
@@ -431,142 +430,57 @@ module_from_def_and_spec(
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (def_like->m_size < 0) {
|
||||
PyErr_Format(
|
||||
PyExc_SystemError,
|
||||
"module %s: m_size may not be negative for multi-phase initialization",
|
||||
name);
|
||||
goto error;
|
||||
_PySlotIterator it;
|
||||
|
||||
PyModuleDef _dummy_def = {0};
|
||||
PyModuleDef *def_like;
|
||||
if (slots) {
|
||||
assert(!original_def);
|
||||
def_like = &_dummy_def;
|
||||
_PySlotIterator_Init(&it, slots, _PySlot_KIND_MOD);
|
||||
}
|
||||
else {
|
||||
assert(original_def);
|
||||
def_like = original_def;
|
||||
_PySlotIterator_InitLegacy(&it, def_like->m_slots, _PySlot_KIND_MOD);
|
||||
}
|
||||
it.name = name;
|
||||
|
||||
bool seen_m_name_slot = false;
|
||||
bool seen_m_doc_slot = false;
|
||||
bool seen_m_size_slot = false;
|
||||
bool seen_m_methods_slot = false;
|
||||
bool seen_m_traverse_slot = false;
|
||||
bool seen_m_clear_slot = false;
|
||||
bool seen_m_free_slot = false;
|
||||
bool seen_m_abi_slot = false;
|
||||
for (cur_slot = def_like->m_slots; cur_slot && cur_slot->slot; cur_slot++) {
|
||||
|
||||
// Macro to copy a non-NULL, non-repeatable slot.
|
||||
#define COPY_NONNULL_SLOT(SLOTNAME, TYPE, DEST) \
|
||||
do { \
|
||||
if (!(TYPE)(cur_slot->value)) { \
|
||||
PyErr_Format( \
|
||||
PyExc_SystemError, \
|
||||
"module %s: %s must not be NULL", \
|
||||
name, SLOTNAME); \
|
||||
goto error; \
|
||||
} \
|
||||
DEST = (TYPE)(cur_slot->value); \
|
||||
} while (0); \
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
// Macro to copy a non-NULL, non-repeatable slot to def_like.
|
||||
#define COPY_DEF_SLOT(SLOTNAME, TYPE, MEMBER) \
|
||||
do { \
|
||||
if (seen_ ## MEMBER ## _slot) { \
|
||||
PyErr_Format( \
|
||||
PyExc_SystemError, \
|
||||
"module %s has more than one %s slot", \
|
||||
name, SLOTNAME); \
|
||||
goto error; \
|
||||
} \
|
||||
seen_ ## MEMBER ## _slot = true; \
|
||||
if (original_def) { \
|
||||
TYPE orig_value = (TYPE)original_def->MEMBER; \
|
||||
TYPE new_value = (TYPE)cur_slot->value; \
|
||||
if (orig_value != new_value) { \
|
||||
PyErr_Format( \
|
||||
PyExc_SystemError, \
|
||||
"module %s: %s conflicts with " \
|
||||
"PyModuleDef." #MEMBER, \
|
||||
name, SLOTNAME); \
|
||||
goto error; \
|
||||
} \
|
||||
} \
|
||||
COPY_NONNULL_SLOT(SLOTNAME, TYPE, (def_like->MEMBER)) \
|
||||
} while (0); \
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
// Macro to copy a non-NULL, non-repeatable slot without a
|
||||
// corresponding PyModuleDef member.
|
||||
// DEST must be initially NULL (so we don't need a seen_* flag).
|
||||
#define COPY_NONDEF_SLOT(SLOTNAME, TYPE, DEST) \
|
||||
do { \
|
||||
if (DEST) { \
|
||||
PyErr_Format( \
|
||||
PyExc_SystemError, \
|
||||
"module %s has more than one %s slot", \
|
||||
name, SLOTNAME); \
|
||||
goto error; \
|
||||
} \
|
||||
COPY_NONNULL_SLOT(SLOTNAME, TYPE, DEST) \
|
||||
} while (0); \
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
// Define the whole common case
|
||||
#define DEF_SLOT_CASE(SLOT, TYPE, MEMBER) \
|
||||
case SLOT: \
|
||||
COPY_DEF_SLOT(#SLOT, TYPE, MEMBER); \
|
||||
break; \
|
||||
/////////////////////////////////////////////////////////////////
|
||||
switch (cur_slot->slot) {
|
||||
while (_PySlotIterator_Next(&it)) {
|
||||
switch (it.current.sl_id) {
|
||||
case Py_slot_invalid:
|
||||
goto error;
|
||||
case Py_mod_create:
|
||||
if (create) {
|
||||
PyErr_Format(
|
||||
PyExc_SystemError,
|
||||
"module %s has multiple create slots",
|
||||
name);
|
||||
goto error;
|
||||
}
|
||||
create = cur_slot->value;
|
||||
create = (createfunc_t)it.current.sl_func;
|
||||
break;
|
||||
case Py_mod_exec:
|
||||
has_execution_slots = 1;
|
||||
if (!original_def) {
|
||||
COPY_NONDEF_SLOT("Py_mod_exec", _Py_modexecfunc, m_exec);
|
||||
if (m_exec) {
|
||||
PyErr_Format(
|
||||
PyExc_SystemError,
|
||||
"module %s has multiple Py_mod_exec slots",
|
||||
name);
|
||||
goto error;
|
||||
}
|
||||
m_exec = (_Py_modexecfunc)it.current.sl_func;
|
||||
}
|
||||
break;
|
||||
case Py_mod_multiple_interpreters:
|
||||
if (has_multiple_interpreters_slot) {
|
||||
PyErr_Format(
|
||||
PyExc_SystemError,
|
||||
"module %s has more than one 'multiple interpreters' "
|
||||
"slots",
|
||||
name);
|
||||
goto error;
|
||||
}
|
||||
multiple_interpreters = cur_slot->value;
|
||||
has_multiple_interpreters_slot = 1;
|
||||
multiple_interpreters = it.current.sl_uint64;
|
||||
break;
|
||||
case Py_mod_gil:
|
||||
if (has_gil_slot) {
|
||||
PyErr_Format(
|
||||
PyExc_SystemError,
|
||||
"module %s has more than one 'gil' slot",
|
||||
name);
|
||||
goto error;
|
||||
{
|
||||
uint64_t val = it.current.sl_uint64;
|
||||
requires_gil = (val != (uint64_t)Py_MOD_GIL_NOT_USED);
|
||||
}
|
||||
requires_gil = (cur_slot->value != Py_MOD_GIL_NOT_USED);
|
||||
has_gil_slot = 1;
|
||||
break;
|
||||
case Py_mod_abi:
|
||||
if (PyABIInfo_Check((PyABIInfo *)cur_slot->value, name) < 0) {
|
||||
if (PyABIInfo_Check(it.current.sl_ptr, name) < 0) {
|
||||
goto error;
|
||||
}
|
||||
seen_m_abi_slot = true;
|
||||
break;
|
||||
DEF_SLOT_CASE(Py_mod_name, char*, m_name)
|
||||
DEF_SLOT_CASE(Py_mod_doc, char*, m_doc)
|
||||
DEF_SLOT_CASE(Py_mod_state_size, Py_ssize_t, m_size)
|
||||
DEF_SLOT_CASE(Py_mod_methods, PyMethodDef*, m_methods)
|
||||
DEF_SLOT_CASE(Py_mod_state_traverse, traverseproc, m_traverse)
|
||||
DEF_SLOT_CASE(Py_mod_state_clear, inquiry, m_clear)
|
||||
DEF_SLOT_CASE(Py_mod_state_free, freefunc, m_free)
|
||||
case Py_mod_token:
|
||||
if (original_def && original_def != cur_slot->value) {
|
||||
if (original_def && original_def != it.current.sl_ptr) {
|
||||
PyErr_Format(
|
||||
PyExc_SystemError,
|
||||
"module %s: arbitrary Py_mod_token not "
|
||||
@@ -574,22 +488,40 @@ module_from_def_and_spec(
|
||||
name);
|
||||
goto error;
|
||||
}
|
||||
COPY_NONDEF_SLOT("Py_mod_token", void*, token);
|
||||
token = it.current.sl_ptr;
|
||||
break;
|
||||
default:
|
||||
assert(cur_slot->slot < 0 || cur_slot->slot > _Py_mod_LAST_SLOT);
|
||||
PyErr_Format(
|
||||
PyExc_SystemError,
|
||||
"module %s uses unknown slot ID %i",
|
||||
name, cur_slot->slot);
|
||||
goto error;
|
||||
}
|
||||
// Common case: Copy a PEP 793 slot to def_like
|
||||
#define DEF_SLOT_CASE(SLOT, TYPE, SL_MEMBER, MEMBER) \
|
||||
case SLOT: \
|
||||
do { \
|
||||
if (original_def) { \
|
||||
TYPE orig_value = (TYPE)original_def->MEMBER; \
|
||||
TYPE new_value = (TYPE)it.current.SL_MEMBER; \
|
||||
if (orig_value != new_value) { \
|
||||
PyErr_Format( \
|
||||
PyExc_SystemError, \
|
||||
"module %s: %s conflicts with " \
|
||||
"PyModuleDef." #MEMBER, \
|
||||
name, _PySlot_GetName(it.current.sl_id)); \
|
||||
goto error; \
|
||||
} \
|
||||
} \
|
||||
(def_like->MEMBER) = (TYPE)it.current.SL_MEMBER; \
|
||||
} while (0); \
|
||||
break; \
|
||||
/////////////////////////////////////////////////////////////////
|
||||
DEF_SLOT_CASE(Py_mod_name, char*, sl_ptr, m_name)
|
||||
DEF_SLOT_CASE(Py_mod_doc, char*, sl_ptr, m_doc)
|
||||
DEF_SLOT_CASE(Py_mod_state_size, Py_ssize_t, sl_size, m_size)
|
||||
DEF_SLOT_CASE(Py_mod_methods, PyMethodDef*, sl_ptr, m_methods)
|
||||
DEF_SLOT_CASE(Py_mod_state_traverse,
|
||||
traverseproc, sl_func, m_traverse)
|
||||
DEF_SLOT_CASE(Py_mod_state_clear, inquiry, sl_func, m_clear)
|
||||
DEF_SLOT_CASE(Py_mod_state_free, freefunc, sl_func, m_free)
|
||||
#undef DEF_SLOT_CASE
|
||||
#undef COPY_DEF_SLOT
|
||||
#undef COPY_NONDEF_SLOT
|
||||
#undef COPY_NONNULL_SLOT
|
||||
}
|
||||
}
|
||||
if (!original_def && !seen_m_abi_slot) {
|
||||
if (!original_def && !_PySlotIterator_SawSlot(&it, Py_mod_abi)) {
|
||||
PyErr_Format(
|
||||
PyExc_SystemError,
|
||||
"module %s does not define Py_mod_abi,"
|
||||
@@ -614,19 +546,24 @@ module_from_def_and_spec(
|
||||
}
|
||||
#endif
|
||||
|
||||
if (def_like->m_size < 0) {
|
||||
PyErr_Format(
|
||||
PyExc_SystemError,
|
||||
"module %s: m_size may not be negative for multi-phase initialization",
|
||||
name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* By default, multi-phase init modules are expected
|
||||
to work under multiple interpreters. */
|
||||
if (!has_multiple_interpreters_slot) {
|
||||
multiple_interpreters = Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED;
|
||||
}
|
||||
if (multiple_interpreters == Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED) {
|
||||
if (multiple_interpreters == (int64_t)(intptr_t)Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED) {
|
||||
if (!_Py_IsMainInterpreter(interp)
|
||||
&& _PyImport_CheckSubinterpIncompatibleExtensionAllowed(name) < 0)
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else if (multiple_interpreters != Py_MOD_PER_INTERPRETER_GIL_SUPPORTED
|
||||
else if (multiple_interpreters != (int64_t)(intptr_t)Py_MOD_PER_INTERPRETER_GIL_SUPPORTED
|
||||
&& interp->ceval.own_gil
|
||||
&& !_Py_IsMainInterpreter(interp)
|
||||
&& _PyImport_CheckSubinterpIncompatibleExtensionAllowed(name) < 0)
|
||||
@@ -688,7 +625,7 @@ module_from_def_and_spec(
|
||||
name);
|
||||
goto error;
|
||||
}
|
||||
if (has_execution_slots) {
|
||||
if (_PySlotIterator_SawSlot(&it, Py_mod_exec)) {
|
||||
PyErr_Format(
|
||||
PyExc_SystemError,
|
||||
"module %s specifies execution slots, but did not create "
|
||||
@@ -733,11 +670,11 @@ PyObject *
|
||||
PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_version)
|
||||
{
|
||||
PyModuleDef_Init(def);
|
||||
return module_from_def_and_spec(def, spec, module_api_version, def);
|
||||
return module_from_slots_and_spec(NULL, spec, module_api_version, def);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
PyModule_FromSlotsAndSpec(const PyModuleDef_Slot *slots, PyObject *spec)
|
||||
PyModule_FromSlotsAndSpec(const PySlot *slots, PyObject *spec)
|
||||
{
|
||||
if (!slots) {
|
||||
PyErr_SetString(
|
||||
@@ -745,11 +682,9 @@ PyModule_FromSlotsAndSpec(const PyModuleDef_Slot *slots, PyObject *spec)
|
||||
"PyModule_FromSlotsAndSpec called with NULL slots");
|
||||
return NULL;
|
||||
}
|
||||
// Fill in enough of a PyModuleDef to pass to common machinery
|
||||
PyModuleDef def_like = {.m_slots = (PyModuleDef_Slot *)slots};
|
||||
|
||||
return module_from_def_and_spec(&def_like, spec, PYTHON_API_VERSION,
|
||||
NULL);
|
||||
return module_from_slots_and_spec(slots, spec, PYTHON_API_VERSION,
|
||||
NULL);
|
||||
}
|
||||
|
||||
#ifdef Py_GIL_DISABLED
|
||||
@@ -835,8 +770,6 @@ PyModule_Exec(PyObject *module)
|
||||
int
|
||||
PyModule_ExecDef(PyObject *module, PyModuleDef *def)
|
||||
{
|
||||
PyModuleDef_Slot *cur_slot;
|
||||
|
||||
if (alloc_state(module) < 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -847,13 +780,19 @@ PyModule_ExecDef(PyObject *module, PyModuleDef *def)
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (cur_slot = def->m_slots; cur_slot && cur_slot->slot; cur_slot++) {
|
||||
if (cur_slot->slot == Py_mod_exec) {
|
||||
int (*func)(PyObject *) = cur_slot->value;
|
||||
if (run_exec_func(module, func) < 0) {
|
||||
_PySlotIterator it;
|
||||
_PySlotIterator_InitLegacy(&it, def->m_slots, _PySlot_KIND_MOD);
|
||||
while (_PySlotIterator_Next(&it)) {
|
||||
_Py_modexecfunc func;
|
||||
switch (it.current.sl_id) {
|
||||
case Py_slot_invalid:
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
case Py_mod_exec:
|
||||
func = (_Py_modexecfunc)it.current.sl_func;
|
||||
if (run_exec_func(module, func) < 0) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
+238
-177
@@ -18,6 +18,7 @@
|
||||
#include "pycore_pyatomic_ft_wrappers.h"
|
||||
#include "pycore_pyerrors.h" // _PyErr_Occurred()
|
||||
#include "pycore_pystate.h" // _PyThreadState_GET()
|
||||
#include "pycore_slots.h" // _PySlotIterator_Init
|
||||
#include "pycore_symtable.h" // _Py_Mangle()
|
||||
#include "pycore_tuple.h" // _PyTuple_FromPair
|
||||
#include "pycore_typeobject.h" // struct type_cache
|
||||
@@ -198,11 +199,6 @@ type_lock_allow_release(void)
|
||||
|
||||
#define PyTypeObject_CAST(op) ((PyTypeObject *)(op))
|
||||
|
||||
typedef struct PySlot_Offset {
|
||||
short subslot_offset;
|
||||
short slot_offset;
|
||||
} PySlot_Offset;
|
||||
|
||||
static void
|
||||
slot_bf_releasebuffer(PyObject *self, Py_buffer *buffer);
|
||||
|
||||
@@ -5112,21 +5108,6 @@ type_vectorcall(PyObject *metatype, PyObject *const *args,
|
||||
return _PyObject_MakeTpCall(tstate, metatype, args, nargs, kwnames);
|
||||
}
|
||||
|
||||
/* An array of type slot offsets corresponding to Py_tp_* constants,
|
||||
* for use in e.g. PyType_Spec and PyType_GetSlot.
|
||||
* Each entry has two offsets: "slot_offset" and "subslot_offset".
|
||||
* If is subslot_offset is -1, slot_offset is an offset within the
|
||||
* PyTypeObject struct.
|
||||
* Otherwise slot_offset is an offset to a pointer to a sub-slots struct
|
||||
* (such as "tp_as_number"), and subslot_offset is the offset within
|
||||
* that struct.
|
||||
* The actual table is generated by a script.
|
||||
*/
|
||||
static const PySlot_Offset pyslot_offsets[] = {
|
||||
{0, 0},
|
||||
#include "typeslots.inc"
|
||||
};
|
||||
|
||||
/* Align up to the nearest multiple of alignof(max_align_t)
|
||||
* (like _Py_ALIGN_UP, but for a size rather than pointer)
|
||||
*/
|
||||
@@ -5136,43 +5117,6 @@ _align_up(Py_ssize_t size)
|
||||
return (size + ALIGNOF_MAX_ALIGN_T - 1) & ~(ALIGNOF_MAX_ALIGN_T - 1);
|
||||
}
|
||||
|
||||
/* Given a PyType_FromMetaclass `bases` argument (NULL, type, or tuple of
|
||||
* types), return a tuple of types.
|
||||
*/
|
||||
inline static PyObject *
|
||||
get_bases_tuple(PyObject *bases_in, PyType_Spec *spec)
|
||||
{
|
||||
if (!bases_in) {
|
||||
/* Default: look in the spec, fall back to (type,). */
|
||||
PyTypeObject *base = &PyBaseObject_Type; // borrowed ref
|
||||
PyObject *bases = NULL; // borrowed ref
|
||||
const PyType_Slot *slot;
|
||||
for (slot = spec->slots; slot->slot; slot++) {
|
||||
switch (slot->slot) {
|
||||
case Py_tp_base:
|
||||
base = slot->pfunc;
|
||||
break;
|
||||
case Py_tp_bases:
|
||||
bases = slot->pfunc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!bases) {
|
||||
return PyTuple_Pack(1, base);
|
||||
}
|
||||
if (PyTuple_Check(bases)) {
|
||||
return Py_NewRef(bases);
|
||||
}
|
||||
PyErr_SetString(PyExc_SystemError, "Py_tp_bases is not a tuple");
|
||||
return NULL;
|
||||
}
|
||||
if (PyTuple_Check(bases_in)) {
|
||||
return Py_NewRef(bases_in);
|
||||
}
|
||||
// Not a tuple, should be a single type
|
||||
return PyTuple_Pack(1, bases_in);
|
||||
}
|
||||
|
||||
static inline int
|
||||
check_basicsize_includes_size_and_offsets(PyTypeObject* type)
|
||||
{
|
||||
@@ -5274,10 +5218,11 @@ special_offset_from_member(
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
PyType_FromMetaclass(
|
||||
PyTypeObject *metaclass, PyObject *module,
|
||||
PyType_Spec *spec, PyObject *bases_in)
|
||||
|
||||
static PyObject *
|
||||
type_from_slots_or_spec(
|
||||
PySlot *slots, PyType_Spec *spec,
|
||||
PyTypeObject *metaclass, PyObject *module, PyObject *bases_in)
|
||||
{
|
||||
/* Invariant: A non-NULL value in one of these means this function holds
|
||||
* a strong reference or owns allocated memory.
|
||||
@@ -5292,47 +5237,130 @@ PyType_FromMetaclass(
|
||||
|
||||
int r;
|
||||
|
||||
/* Prepare slots that need special handling.
|
||||
* Keep in mind that a slot can be given multiple times:
|
||||
* if that would cause trouble (leaks, UB, ...), raise an exception.
|
||||
*/
|
||||
/* First pass of slots */
|
||||
|
||||
const PyType_Slot *slot;
|
||||
Py_ssize_t nmembers = 0;
|
||||
const PyMemberDef *weaklistoffset_member = NULL;
|
||||
const PyMemberDef *dictoffset_member = NULL;
|
||||
const PyMemberDef *vectorcalloffset_member = NULL;
|
||||
char *res_start;
|
||||
Py_ssize_t basicsize = 0;
|
||||
Py_ssize_t extra_basicsize = 0;
|
||||
Py_ssize_t itemsize = 0;
|
||||
int flags = 0;
|
||||
void *token = NULL;
|
||||
|
||||
for (slot = spec->slots; slot->slot; slot++) {
|
||||
if (slot->slot < 0
|
||||
|| (size_t)slot->slot >= Py_ARRAY_LENGTH(pyslot_offsets)) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "invalid slot offset");
|
||||
goto finally;
|
||||
bool have_relative_members = false;
|
||||
Py_ssize_t max_relative_offset = 0;
|
||||
|
||||
PyObject *bases_slot = NULL; /* borrowed from the slots */
|
||||
|
||||
_PySlotIterator it;
|
||||
|
||||
if (spec) {
|
||||
assert(!slots);
|
||||
if (spec->basicsize > 0) {
|
||||
basicsize = spec->basicsize;
|
||||
}
|
||||
switch (slot->slot) {
|
||||
case Py_tp_members:
|
||||
if (nmembers != 0) {
|
||||
if (spec->basicsize < 0) {
|
||||
extra_basicsize = -spec->basicsize;
|
||||
}
|
||||
itemsize = spec->itemsize;
|
||||
flags = spec->flags;
|
||||
_PySlotIterator_InitLegacy(&it, spec->slots, _PySlot_KIND_TYPE);
|
||||
it.name = spec->name;
|
||||
}
|
||||
else {
|
||||
assert(!spec);
|
||||
assert(!metaclass);
|
||||
assert(!module);
|
||||
assert(!bases_in);
|
||||
_PySlotIterator_Init(&it, slots, _PySlot_KIND_TYPE);
|
||||
}
|
||||
|
||||
#define NO_SPEC \
|
||||
if (spec) { \
|
||||
PyErr_Format( \
|
||||
PyExc_SystemError, \
|
||||
"%s must not be used with PyType_Spec", \
|
||||
_PySlot_GetName(it.current.sl_id)); \
|
||||
goto finally; \
|
||||
} \
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
while (_PySlotIterator_Next(&it)) {
|
||||
switch (it.current.sl_id) {
|
||||
case Py_slot_invalid:
|
||||
goto finally;
|
||||
case Py_tp_name:
|
||||
NO_SPEC;
|
||||
it.name = it.current.sl_ptr;
|
||||
break;
|
||||
case Py_tp_metaclass:
|
||||
NO_SPEC;
|
||||
metaclass = it.current.sl_ptr;
|
||||
break;
|
||||
case Py_tp_module:
|
||||
NO_SPEC;
|
||||
module = it.current.sl_ptr;
|
||||
break;
|
||||
case Py_tp_bases:
|
||||
bases_slot = it.current.sl_ptr;
|
||||
break;
|
||||
case Py_tp_base:
|
||||
if (!_PySlotIterator_SawSlot(&it, Py_tp_bases)) {
|
||||
bases_slot = it.current.sl_ptr;
|
||||
}
|
||||
break;
|
||||
case Py_tp_basicsize:
|
||||
NO_SPEC;
|
||||
basicsize = it.current.sl_size;
|
||||
if (basicsize <= 0) {
|
||||
PyErr_SetString(
|
||||
PyExc_SystemError,
|
||||
"Multiple Py_tp_members slots are not supported.");
|
||||
"Py_tp_basicsize must be positive");
|
||||
goto finally;
|
||||
}
|
||||
for (const PyMemberDef *memb = slot->pfunc; memb->name != NULL; memb++) {
|
||||
break;
|
||||
case Py_tp_extra_basicsize:
|
||||
NO_SPEC;
|
||||
extra_basicsize = it.current.sl_size;
|
||||
if (extra_basicsize <= 0) {
|
||||
PyErr_SetString(
|
||||
PyExc_SystemError,
|
||||
"Py_tp_extra_basicsize must be positive");
|
||||
goto finally;
|
||||
}
|
||||
break;
|
||||
case Py_tp_itemsize:
|
||||
NO_SPEC;
|
||||
itemsize = it.current.sl_size;
|
||||
if (itemsize <= 0) {
|
||||
PyErr_SetString(
|
||||
PyExc_SystemError,
|
||||
"Py_tp_itemsize must be positive");
|
||||
goto finally;
|
||||
}
|
||||
break;
|
||||
case Py_tp_flags:
|
||||
NO_SPEC;
|
||||
flags = (int)it.current.sl_uint64;
|
||||
break;
|
||||
case Py_tp_members:
|
||||
for (const PyMemberDef *memb = it.current.sl_ptr;
|
||||
memb->name != NULL;
|
||||
memb++)
|
||||
{
|
||||
nmembers++;
|
||||
if (memb->flags & Py_RELATIVE_OFFSET) {
|
||||
if (spec->basicsize > 0) {
|
||||
if (memb->offset < 0) {
|
||||
PyErr_SetString(
|
||||
PyExc_SystemError,
|
||||
"With Py_RELATIVE_OFFSET, basicsize must be negative.");
|
||||
goto finally;
|
||||
}
|
||||
if (memb->offset < 0 || memb->offset >= -spec->basicsize) {
|
||||
PyErr_SetString(
|
||||
PyExc_SystemError,
|
||||
"Member offset out of range (0..-basicsize)");
|
||||
"Member offset must not be negative");
|
||||
goto finally;
|
||||
}
|
||||
have_relative_members = true;
|
||||
max_relative_offset = Py_MAX(max_relative_offset,
|
||||
memb->offset);
|
||||
}
|
||||
if (strcmp(memb->name, "__weaklistoffset__") == 0) {
|
||||
weaklistoffset_member = memb;
|
||||
@@ -5345,43 +5373,86 @@ PyType_FromMetaclass(
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Py_tp_token:
|
||||
token = it.current.sl_ptr;
|
||||
if (token == Py_TP_USE_SPEC) {
|
||||
if (!spec) {
|
||||
PyErr_SetString(
|
||||
PyExc_SystemError,
|
||||
"Py_tp_token: Py_TP_USE_SPEC (NULL) can only be "
|
||||
"used with PyType_Spec");
|
||||
goto finally;
|
||||
}
|
||||
token = spec;
|
||||
}
|
||||
break;
|
||||
case Py_tp_doc:
|
||||
/* For the docstring slot, which usually points to a static string
|
||||
literal, we need to make a copy */
|
||||
if (tp_doc != NULL) {
|
||||
PyErr_SetString(
|
||||
PyExc_SystemError,
|
||||
"Multiple Py_tp_doc slots are not supported.");
|
||||
goto finally;
|
||||
}
|
||||
if (slot->pfunc == NULL) {
|
||||
if (it.current.sl_ptr == NULL) {
|
||||
PyMem_Free(tp_doc);
|
||||
tp_doc = NULL;
|
||||
}
|
||||
else {
|
||||
size_t len = strlen(slot->pfunc)+1;
|
||||
size_t len = strlen(it.current.sl_ptr)+1;
|
||||
tp_doc = PyMem_Malloc(len);
|
||||
if (tp_doc == NULL) {
|
||||
PyErr_NoMemory();
|
||||
goto finally;
|
||||
}
|
||||
memcpy(tp_doc, slot->pfunc, len);
|
||||
memcpy(tp_doc, it.current.sl_ptr, len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#undef NO_SPEC
|
||||
|
||||
/* Required slots & bad combinations */
|
||||
|
||||
if (it.name == NULL) {
|
||||
if (spec) {
|
||||
PyErr_SetString(PyExc_SystemError,
|
||||
"Type spec does not define the name field.");
|
||||
}
|
||||
else {
|
||||
PyErr_SetString(PyExc_SystemError,
|
||||
"Py_tp_name slot is required.");
|
||||
}
|
||||
goto finally;
|
||||
}
|
||||
|
||||
if (_PySlotIterator_SawSlot(&it, Py_tp_basicsize)
|
||||
&& _PySlotIterator_SawSlot(&it, Py_tp_extra_basicsize))
|
||||
{
|
||||
PyErr_Format(
|
||||
PyExc_SystemError,
|
||||
"type %s: Py_tp_basicsize and Py_tp_extra_basicsize are "
|
||||
"mutually exclusive",
|
||||
it.name);
|
||||
goto finally;
|
||||
}
|
||||
|
||||
if (have_relative_members) {
|
||||
if (!extra_basicsize) {
|
||||
PyErr_SetString(
|
||||
PyExc_SystemError,
|
||||
"With Py_RELATIVE_OFFSET, basicsize must be extended");
|
||||
goto finally;
|
||||
}
|
||||
if (max_relative_offset >= extra_basicsize) {
|
||||
PyErr_SetString(
|
||||
PyExc_SystemError,
|
||||
"Member offset out of range (0..extra_basicsize)");
|
||||
goto finally;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prepare the type name and qualname */
|
||||
|
||||
if (spec->name == NULL) {
|
||||
PyErr_SetString(PyExc_SystemError,
|
||||
"Type spec does not define the name field.");
|
||||
goto finally;
|
||||
}
|
||||
|
||||
const char *s = strrchr(spec->name, '.');
|
||||
assert(it.name);
|
||||
const char *s = strrchr(it.name, '.');
|
||||
if (s == NULL) {
|
||||
s = spec->name;
|
||||
s = it.name;
|
||||
}
|
||||
else {
|
||||
s++;
|
||||
@@ -5392,7 +5463,7 @@ PyType_FromMetaclass(
|
||||
goto finally;
|
||||
}
|
||||
|
||||
/* Copy spec->name to a buffer we own.
|
||||
/* Copy the name to a buffer we own.
|
||||
*
|
||||
* Unfortunately, we can't use tp_name directly (with some
|
||||
* flag saying that it should be deallocated with the type),
|
||||
@@ -5401,28 +5472,42 @@ PyType_FromMetaclass(
|
||||
* So, we use a separate buffer, _ht_tpname, that's always
|
||||
* deallocated with the type (if it's non-NULL).
|
||||
*/
|
||||
Py_ssize_t name_buf_len = strlen(spec->name) + 1;
|
||||
Py_ssize_t name_buf_len = strlen(it.name) + 1;
|
||||
_ht_tpname = PyMem_Malloc(name_buf_len);
|
||||
if (_ht_tpname == NULL) {
|
||||
goto finally;
|
||||
}
|
||||
memcpy(_ht_tpname, spec->name, name_buf_len);
|
||||
memcpy(_ht_tpname, it.name, name_buf_len);
|
||||
|
||||
/* Get a tuple of bases.
|
||||
* bases is a strong reference (unlike bases_in).
|
||||
* (This is convoluted for backwards compatibility -- preserving priority
|
||||
* of the various ways to specify bases)
|
||||
*/
|
||||
bases = get_bases_tuple(bases_in, spec);
|
||||
if (!bases_in) {
|
||||
bases_in = bases_slot;
|
||||
}
|
||||
if (bases_in) {
|
||||
if (PyTuple_Check(bases_in)) {
|
||||
bases = Py_NewRef(bases_in);
|
||||
}
|
||||
else {
|
||||
bases = PyTuple_Pack(1, bases_in);
|
||||
}
|
||||
}
|
||||
else {
|
||||
bases = PyTuple_Pack(1, &PyBaseObject_Type);
|
||||
}
|
||||
if (!bases) {
|
||||
goto finally;
|
||||
}
|
||||
|
||||
/* If this is an immutable type, check if all bases are also immutable,
|
||||
* and (for now) fire a deprecation warning if not.
|
||||
/* If this is an immutable type, check if all bases are also immutable.
|
||||
* (This isn't necessary for static types: those can't have heap bases,
|
||||
* and only heap types can be mutable.)
|
||||
*/
|
||||
if (spec->flags & Py_TPFLAGS_IMMUTABLETYPE) {
|
||||
if (check_immutable_bases(spec->name, bases, 0) < 0) {
|
||||
if (flags & Py_TPFLAGS_IMMUTABLETYPE) {
|
||||
if (check_immutable_bases(it.name, bases, 0) < 0) {
|
||||
goto finally;
|
||||
}
|
||||
}
|
||||
@@ -5460,20 +5545,16 @@ PyType_FromMetaclass(
|
||||
|
||||
/* Calculate sizes */
|
||||
|
||||
Py_ssize_t basicsize = spec->basicsize;
|
||||
Py_ssize_t type_data_offset = spec->basicsize;
|
||||
if (basicsize == 0) {
|
||||
/* Inherit */
|
||||
basicsize = base->tp_basicsize;
|
||||
}
|
||||
else if (basicsize < 0) {
|
||||
Py_ssize_t type_data_offset = basicsize;
|
||||
if (extra_basicsize) {
|
||||
/* Extend */
|
||||
assert(basicsize == 0);
|
||||
type_data_offset = _align_up(base->tp_basicsize);
|
||||
basicsize = type_data_offset + _align_up(-spec->basicsize);
|
||||
basicsize = type_data_offset + _align_up(extra_basicsize);
|
||||
|
||||
/* Inheriting variable-sized types is limited */
|
||||
if (base->tp_itemsize
|
||||
&& !((base->tp_flags | spec->flags) & Py_TPFLAGS_ITEMS_AT_END))
|
||||
&& !((base->tp_flags | flags) & Py_TPFLAGS_ITEMS_AT_END))
|
||||
{
|
||||
PyErr_SetString(
|
||||
PyExc_SystemError,
|
||||
@@ -5481,8 +5562,10 @@ PyType_FromMetaclass(
|
||||
goto finally;
|
||||
}
|
||||
}
|
||||
|
||||
Py_ssize_t itemsize = spec->itemsize;
|
||||
if (basicsize == 0) {
|
||||
/* Inherit */
|
||||
basicsize = base->tp_basicsize;
|
||||
}
|
||||
|
||||
/* Compute special offsets */
|
||||
|
||||
@@ -5514,11 +5597,10 @@ PyType_FromMetaclass(
|
||||
if (res == NULL) {
|
||||
goto finally;
|
||||
}
|
||||
res_start = (char*)res;
|
||||
|
||||
type = &res->ht_type;
|
||||
/* The flags must be initialized early, before the GC traverses us */
|
||||
type_set_flags(type, spec->flags | Py_TPFLAGS_HEAPTYPE);
|
||||
type_set_flags(type, flags | Py_TPFLAGS_HEAPTYPE);
|
||||
|
||||
res->ht_module = Py_XNewRef(module);
|
||||
|
||||
@@ -5547,15 +5629,20 @@ PyType_FromMetaclass(
|
||||
res->_ht_tpname = _ht_tpname;
|
||||
_ht_tpname = NULL; // Give ownership to the type
|
||||
|
||||
res->ht_token = token;
|
||||
|
||||
/* Copy the sizes */
|
||||
|
||||
type->tp_basicsize = basicsize;
|
||||
type->tp_itemsize = itemsize;
|
||||
|
||||
/* Copy all the ordinary slots */
|
||||
/* Second pass of slots: copy most of them into the type */
|
||||
|
||||
for (slot = spec->slots; slot->slot; slot++) {
|
||||
switch (slot->slot) {
|
||||
_PySlotIterator_Rewind(&it, spec ? (void*)spec->slots : (void*)slots);
|
||||
while (_PySlotIterator_Next(&it)) {
|
||||
switch (it.current.sl_id) {
|
||||
case Py_slot_invalid:
|
||||
goto finally;
|
||||
case Py_tp_base:
|
||||
case Py_tp_bases:
|
||||
case Py_tp_doc:
|
||||
@@ -5565,7 +5652,7 @@ PyType_FromMetaclass(
|
||||
{
|
||||
/* Move the slots to the heap type itself */
|
||||
size_t len = Py_TYPE(type)->tp_itemsize * nmembers;
|
||||
memcpy(_PyHeapType_GET_MEMBERS(res), slot->pfunc, len);
|
||||
memcpy(_PyHeapType_GET_MEMBERS(res), it.current.sl_ptr, len);
|
||||
type->tp_members = _PyHeapType_GET_MEMBERS(res);
|
||||
PyMemberDef *memb;
|
||||
Py_ssize_t i;
|
||||
@@ -5579,26 +5666,8 @@ PyType_FromMetaclass(
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Py_tp_token:
|
||||
{
|
||||
res->ht_token = slot->pfunc == Py_TP_USE_SPEC ? spec : slot->pfunc;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
/* Copy other slots directly */
|
||||
PySlot_Offset slotoffsets = pyslot_offsets[slot->slot];
|
||||
short slot_offset = slotoffsets.slot_offset;
|
||||
if (slotoffsets.subslot_offset == -1) {
|
||||
/* Set a slot in the main PyTypeObject */
|
||||
*(void**)((char*)res_start + slot_offset) = slot->pfunc;
|
||||
}
|
||||
else {
|
||||
void *procs = *(void**)((char*)res_start + slot_offset);
|
||||
short subslot_offset = slotoffsets.subslot_offset;
|
||||
*(void**)((char*)procs + subslot_offset) = slot->pfunc;
|
||||
}
|
||||
}
|
||||
_PySlot_heaptype_apply_field_slot(res, it.current);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -5664,10 +5733,10 @@ PyType_FromMetaclass(
|
||||
goto finally;
|
||||
}
|
||||
if (r == 0) {
|
||||
s = strrchr(spec->name, '.');
|
||||
s = strrchr(it.name, '.');
|
||||
if (s != NULL) {
|
||||
PyObject *modname = PyUnicode_FromStringAndSize(
|
||||
spec->name, (Py_ssize_t)(s - spec->name));
|
||||
it.name, (Py_ssize_t)(s - it.name));
|
||||
if (modname == NULL) {
|
||||
goto finally;
|
||||
}
|
||||
@@ -5680,7 +5749,7 @@ PyType_FromMetaclass(
|
||||
else {
|
||||
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
|
||||
"builtin type %.200s has no __module__ attribute",
|
||||
spec->name))
|
||||
it.name))
|
||||
goto finally;
|
||||
}
|
||||
}
|
||||
@@ -5702,22 +5771,36 @@ PyType_FromMetaclass(
|
||||
return (PyObject*)res;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
PyType_FromSlots(PySlot *slots)
|
||||
{
|
||||
return type_from_slots_or_spec(slots, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
PyType_FromMetaclass(
|
||||
PyTypeObject *metaclass, PyObject *module,
|
||||
PyType_Spec *spec, PyObject *bases)
|
||||
{
|
||||
return type_from_slots_or_spec(NULL, spec, metaclass, module, bases);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases)
|
||||
{
|
||||
return PyType_FromMetaclass(NULL, module, spec, bases);
|
||||
return type_from_slots_or_spec(NULL, spec, NULL, module, bases);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)
|
||||
{
|
||||
return PyType_FromMetaclass(NULL, NULL, spec, bases);
|
||||
return type_from_slots_or_spec(NULL, spec, NULL, NULL, bases);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
PyType_FromSpec(PyType_Spec *spec)
|
||||
{
|
||||
return PyType_FromMetaclass(NULL, NULL, spec, NULL);
|
||||
return type_from_slots_or_spec(NULL, spec, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
@@ -5739,32 +5822,10 @@ PyType_GetModuleName(PyTypeObject *type)
|
||||
}
|
||||
|
||||
void *
|
||||
PyType_GetSlot(PyTypeObject *type, int slot)
|
||||
PyType_GetSlot(PyTypeObject *type, int slot_in)
|
||||
{
|
||||
void *parent_slot;
|
||||
int slots_len = Py_ARRAY_LENGTH(pyslot_offsets);
|
||||
|
||||
if (slot <= 0 || slot >= slots_len) {
|
||||
PyErr_BadInternalCall();
|
||||
return NULL;
|
||||
}
|
||||
int slot_offset = pyslot_offsets[slot].slot_offset;
|
||||
|
||||
if (slot_offset >= (int)sizeof(PyTypeObject)) {
|
||||
if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
parent_slot = *(void**)((char*)type + slot_offset);
|
||||
if (parent_slot == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
/* Return slot directly if we have no sub slot. */
|
||||
if (pyslot_offsets[slot].subslot_offset == -1) {
|
||||
return parent_slot;
|
||||
}
|
||||
return *(void**)((char*)parent_slot + pyslot_offsets[slot].subslot_offset);
|
||||
uint16_t slot = _PySlot_resolve_type_slot(slot_in);
|
||||
return _PySlot_type_getslot(type, slot);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
/* Generated by typeslots.py */
|
||||
{offsetof(PyBufferProcs, bf_getbuffer), offsetof(PyTypeObject, tp_as_buffer)},
|
||||
{offsetof(PyBufferProcs, bf_releasebuffer), offsetof(PyTypeObject, tp_as_buffer)},
|
||||
{offsetof(PyMappingMethods, mp_ass_subscript), offsetof(PyTypeObject, tp_as_mapping)},
|
||||
{offsetof(PyMappingMethods, mp_length), offsetof(PyTypeObject, tp_as_mapping)},
|
||||
{offsetof(PyMappingMethods, mp_subscript), offsetof(PyTypeObject, tp_as_mapping)},
|
||||
{offsetof(PyNumberMethods, nb_absolute), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_add), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_and), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_bool), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_divmod), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_float), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_floor_divide), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_index), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_inplace_add), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_inplace_and), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_inplace_floor_divide), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_inplace_lshift), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_inplace_multiply), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_inplace_or), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_inplace_power), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_inplace_remainder), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_inplace_rshift), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_inplace_subtract), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_inplace_true_divide), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_inplace_xor), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_int), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_invert), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_lshift), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_multiply), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_negative), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_or), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_positive), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_power), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_remainder), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_rshift), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_subtract), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_true_divide), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_xor), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PySequenceMethods, sq_ass_item), offsetof(PyTypeObject, tp_as_sequence)},
|
||||
{offsetof(PySequenceMethods, sq_concat), offsetof(PyTypeObject, tp_as_sequence)},
|
||||
{offsetof(PySequenceMethods, sq_contains), offsetof(PyTypeObject, tp_as_sequence)},
|
||||
{offsetof(PySequenceMethods, sq_inplace_concat), offsetof(PyTypeObject, tp_as_sequence)},
|
||||
{offsetof(PySequenceMethods, sq_inplace_repeat), offsetof(PyTypeObject, tp_as_sequence)},
|
||||
{offsetof(PySequenceMethods, sq_item), offsetof(PyTypeObject, tp_as_sequence)},
|
||||
{offsetof(PySequenceMethods, sq_length), offsetof(PyTypeObject, tp_as_sequence)},
|
||||
{offsetof(PySequenceMethods, sq_repeat), offsetof(PyTypeObject, tp_as_sequence)},
|
||||
{-1, offsetof(PyTypeObject, tp_alloc)},
|
||||
{-1, offsetof(PyTypeObject, tp_base)},
|
||||
{-1, offsetof(PyTypeObject, tp_bases)},
|
||||
{-1, offsetof(PyTypeObject, tp_call)},
|
||||
{-1, offsetof(PyTypeObject, tp_clear)},
|
||||
{-1, offsetof(PyTypeObject, tp_dealloc)},
|
||||
{-1, offsetof(PyTypeObject, tp_del)},
|
||||
{-1, offsetof(PyTypeObject, tp_descr_get)},
|
||||
{-1, offsetof(PyTypeObject, tp_descr_set)},
|
||||
{-1, offsetof(PyTypeObject, tp_doc)},
|
||||
{-1, offsetof(PyTypeObject, tp_getattr)},
|
||||
{-1, offsetof(PyTypeObject, tp_getattro)},
|
||||
{-1, offsetof(PyTypeObject, tp_hash)},
|
||||
{-1, offsetof(PyTypeObject, tp_init)},
|
||||
{-1, offsetof(PyTypeObject, tp_is_gc)},
|
||||
{-1, offsetof(PyTypeObject, tp_iter)},
|
||||
{-1, offsetof(PyTypeObject, tp_iternext)},
|
||||
{-1, offsetof(PyTypeObject, tp_methods)},
|
||||
{-1, offsetof(PyTypeObject, tp_new)},
|
||||
{-1, offsetof(PyTypeObject, tp_repr)},
|
||||
{-1, offsetof(PyTypeObject, tp_richcompare)},
|
||||
{-1, offsetof(PyTypeObject, tp_setattr)},
|
||||
{-1, offsetof(PyTypeObject, tp_setattro)},
|
||||
{-1, offsetof(PyTypeObject, tp_str)},
|
||||
{-1, offsetof(PyTypeObject, tp_traverse)},
|
||||
{-1, offsetof(PyTypeObject, tp_members)},
|
||||
{-1, offsetof(PyTypeObject, tp_getset)},
|
||||
{-1, offsetof(PyTypeObject, tp_free)},
|
||||
{offsetof(PyNumberMethods, nb_matrix_multiply), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyNumberMethods, nb_inplace_matrix_multiply), offsetof(PyTypeObject, tp_as_number)},
|
||||
{offsetof(PyAsyncMethods, am_await), offsetof(PyTypeObject, tp_as_async)},
|
||||
{offsetof(PyAsyncMethods, am_aiter), offsetof(PyTypeObject, tp_as_async)},
|
||||
{offsetof(PyAsyncMethods, am_anext), offsetof(PyTypeObject, tp_as_async)},
|
||||
{-1, offsetof(PyTypeObject, tp_finalize)},
|
||||
{offsetof(PyAsyncMethods, am_send), offsetof(PyTypeObject, tp_as_async)},
|
||||
{-1, offsetof(PyTypeObject, tp_vectorcall)},
|
||||
{-1, offsetof(PyHeapTypeObject, ht_token)},
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# Usage: typeslots.py < Include/typeslots.h typeslots.inc
|
||||
|
||||
import sys, re
|
||||
|
||||
|
||||
def generate_typeslots(out=sys.stdout):
|
||||
out.write("/* Generated by typeslots.py */\n")
|
||||
res = {}
|
||||
for line in sys.stdin:
|
||||
m = re.match("#define Py_([a-z_]+) ([0-9]+)", line)
|
||||
if not m:
|
||||
continue
|
||||
|
||||
member = m.group(1)
|
||||
if member == "tp_token":
|
||||
# The heap type structure (ht_*) is an implementation detail;
|
||||
# the public slot for it has a familiar `tp_` prefix
|
||||
member = '{-1, offsetof(PyHeapTypeObject, ht_token)}'
|
||||
elif member.startswith("tp_"):
|
||||
member = f'{{-1, offsetof(PyTypeObject, {member})}}'
|
||||
elif member.startswith("am_"):
|
||||
member = (f'{{offsetof(PyAsyncMethods, {member}),'+
|
||||
' offsetof(PyTypeObject, tp_as_async)}')
|
||||
elif member.startswith("nb_"):
|
||||
member = (f'{{offsetof(PyNumberMethods, {member}),'+
|
||||
' offsetof(PyTypeObject, tp_as_number)}')
|
||||
elif member.startswith("mp_"):
|
||||
member = (f'{{offsetof(PyMappingMethods, {member}),'+
|
||||
' offsetof(PyTypeObject, tp_as_mapping)}')
|
||||
elif member.startswith("sq_"):
|
||||
member = (f'{{offsetof(PySequenceMethods, {member}),'+
|
||||
' offsetof(PyTypeObject, tp_as_sequence)}')
|
||||
elif member.startswith("bf_"):
|
||||
member = (f'{{offsetof(PyBufferProcs, {member}),'+
|
||||
' offsetof(PyTypeObject, tp_as_buffer)}')
|
||||
res[int(m.group(2))] = member
|
||||
|
||||
M = max(res.keys())+1
|
||||
for i in range(1,M):
|
||||
if i in res:
|
||||
out.write("%s,\n" % res[i])
|
||||
else:
|
||||
out.write("{0, 0},\n")
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) == 2:
|
||||
with open(sys.argv[1], "w") as f:
|
||||
generate_typeslots(f)
|
||||
else:
|
||||
generate_typeslots()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Generated
+1
@@ -681,6 +681,7 @@ EXPORT_FUNC(PyType_ClearCache)
|
||||
EXPORT_FUNC(PyType_Freeze)
|
||||
EXPORT_FUNC(PyType_FromMetaclass)
|
||||
EXPORT_FUNC(PyType_FromModuleAndSpec)
|
||||
EXPORT_FUNC(PyType_FromSlots)
|
||||
EXPORT_FUNC(PyType_FromSpec)
|
||||
EXPORT_FUNC(PyType_FromSpecWithBases)
|
||||
EXPORT_FUNC(PyType_GenericAlloc)
|
||||
|
||||
@@ -269,6 +269,8 @@
|
||||
<ClCompile Include="..\Python\pytime.c" />
|
||||
<ClCompile Include="..\Python\qsbr.c" />
|
||||
<ClCompile Include="..\Python\remote_debugging.c" />
|
||||
<ClCompile Include="..\Python\slots.c" />
|
||||
<ClCompile Include="..\Python\slots_generated.c" />
|
||||
<ClCompile Include="..\Python\specialize.c" />
|
||||
<ClCompile Include="..\Python\structmember.c" />
|
||||
<ClCompile Include="..\Python\suggestions.c" />
|
||||
|
||||
@@ -415,6 +415,12 @@
|
||||
<ClCompile Include="..\Python\remote_debugging.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Python\slots.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Python\slots_generated.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Python\specialize.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
<ClCompile Include="..\Modules\_testlimitedcapi\object.c" />
|
||||
<ClCompile Include="..\Modules\_testlimitedcapi\pyos.c" />
|
||||
<ClCompile Include="..\Modules\_testlimitedcapi\set.c" />
|
||||
<ClCompile Include="..\Modules\_testlimitedcapi\slots.c" />
|
||||
<ClCompile Include="..\Modules\_testlimitedcapi\sys.c" />
|
||||
<ClCompile Include="..\Modules\_testlimitedcapi\threadstate.c" />
|
||||
<ClCompile Include="..\Modules\_testlimitedcapi\tuple.c" />
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
<ClCompile Include="..\Modules\_testlimitedcapi\object.c" />
|
||||
<ClCompile Include="..\Modules\_testlimitedcapi\pyos.c" />
|
||||
<ClCompile Include="..\Modules\_testlimitedcapi\set.c" />
|
||||
<ClCompile Include="..\Modules\_testlimitedcapi\slots.c" />
|
||||
<ClCompile Include="..\Modules\_testlimitedcapi\sys.c" />
|
||||
<ClCompile Include="..\Modules\_testlimitedcapi\testcapi_long.h" />
|
||||
<ClCompile Include="..\Modules\_testlimitedcapi\threadstate.c" />
|
||||
|
||||
@@ -316,6 +316,8 @@
|
||||
<ClInclude Include="..\Include\internal\pycore_setobject.h" />
|
||||
<ClInclude Include="..\Include\internal\pycore_signal.h" />
|
||||
<ClInclude Include="..\Include\internal\pycore_sliceobject.h" />
|
||||
<ClInclude Include="..\Include\internal\pycore_slots.h" />
|
||||
<ClInclude Include="..\Include\internal\pycore_slots_generated.h" />
|
||||
<ClInclude Include="..\Include\internal\pycore_stackref.h" />
|
||||
<ClInclude Include="..\Include\internal\pycore_stats.h" />
|
||||
<ClInclude Include="..\Include\internal\pycore_strhex.h" />
|
||||
@@ -387,6 +389,8 @@
|
||||
<ClInclude Include="..\Include\refcount.h" />
|
||||
<ClInclude Include="..\Include\setobject.h" />
|
||||
<ClInclude Include="..\Include\sliceobject.h" />
|
||||
<ClInclude Include="..\Include\slots.h" />
|
||||
<ClInclude Include="..\Include\slots_generated.h" />
|
||||
<ClInclude Include="..\Include\structmember.h" />
|
||||
<ClInclude Include="..\Include\structseq.h" />
|
||||
<ClInclude Include="..\Include\sysmodule.h" />
|
||||
@@ -685,6 +689,8 @@
|
||||
<ClCompile Include="..\Python\pythonrun.c" />
|
||||
<ClCompile Include="..\Python\specialize.c" />
|
||||
<ClCompile Include="..\Python\suggestions.c" />
|
||||
<ClCompile Include="..\Python\slots.c" />
|
||||
<ClCompile Include="..\Python\slots_generated.c" />
|
||||
<ClCompile Include="..\Python\structmember.c" />
|
||||
<ClCompile Include="..\Python\symtable.c" />
|
||||
<ClCompile Include="..\Python\sysmodule.c">
|
||||
|
||||
@@ -228,6 +228,12 @@
|
||||
<ClInclude Include="..\Include\sliceobject.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\slots.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\slots_generated.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\stats.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
@@ -846,6 +852,12 @@
|
||||
<ClInclude Include="..\Include\internal\pycore_sliceobject.h">
|
||||
<Filter>Include\internal</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\internal\pycore_slots.h">
|
||||
<Filter>Include\internal</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\internal\pycore_slots_generated.h">
|
||||
<Filter>Include\internal</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Include\internal\pycore_strhex.h">
|
||||
<Filter>Include\internal</Filter>
|
||||
</ClInclude>
|
||||
@@ -1574,6 +1586,12 @@
|
||||
<ClCompile Include="..\Python\specialize.c">
|
||||
<Filter>Python</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Objects\slots.c">
|
||||
<Filter>Objects</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Objects\slots_generated.c">
|
||||
<Filter>Objects</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Python\structmember.c">
|
||||
<Filter>Python</Filter>
|
||||
</ClCompile>
|
||||
|
||||
+1
-1
@@ -2059,7 +2059,7 @@ import_run_modexport(PyThreadState *tstate, PyModExportFunction ex0,
|
||||
/* This is like import_run_extension, but avoids interpreter switching
|
||||
* and code for for single-phase modules.
|
||||
*/
|
||||
PyModuleDef_Slot *slots = ex0();
|
||||
PySlot *slots = ex0();
|
||||
if (!slots) {
|
||||
if (!PyErr_Occurred()) {
|
||||
PyErr_Format(
|
||||
|
||||
+404
@@ -0,0 +1,404 @@
|
||||
/* Common handling of type/module slots
|
||||
*/
|
||||
|
||||
#include "Python.h"
|
||||
|
||||
#include "pycore_slots.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Iterating through a recursive structure doesn't look great in a debugger.
|
||||
// Flip the #if to 1 to get a trace on stderr.
|
||||
// (The messages can also serve as code comments.)
|
||||
#if 0
|
||||
#define MSG(...) { \
|
||||
fprintf(stderr, "slotiter: " __VA_ARGS__); fprintf(stderr, "\n");}
|
||||
#else
|
||||
#define MSG(...)
|
||||
#endif
|
||||
|
||||
static char*
|
||||
kind_name(_PySlot_KIND kind)
|
||||
{
|
||||
switch (kind) {
|
||||
case _PySlot_KIND_TYPE: return "type";
|
||||
case _PySlot_KIND_MOD: return "module";
|
||||
case _PySlot_KIND_COMPAT: return "compat";
|
||||
case _PySlot_KIND_SLOT: return "generic slot";
|
||||
}
|
||||
Py_UNREACHABLE();
|
||||
}
|
||||
|
||||
static void
|
||||
init_with_kind(_PySlotIterator *it, const void *slots,
|
||||
_PySlot_KIND result_kind,
|
||||
_PySlot_KIND slot_struct_kind)
|
||||
{
|
||||
MSG("");
|
||||
MSG("init (%s slot iterator)", kind_name(result_kind));
|
||||
it->state = it->states;
|
||||
it->state->any_slot = slots;
|
||||
it->state->slot_struct_kind = slot_struct_kind;
|
||||
it->kind = result_kind;
|
||||
it->name = NULL;
|
||||
it->recursion_level = 0;
|
||||
it->is_at_end = false;
|
||||
it->is_first_run = true;
|
||||
it->current.sl_id = 0;
|
||||
memset(it->seen, 0, sizeof(it->seen));
|
||||
}
|
||||
|
||||
void
|
||||
_PySlotIterator_Init(_PySlotIterator *it, const PySlot *slots,
|
||||
_PySlot_KIND result_kind)
|
||||
{
|
||||
init_with_kind(it, slots, result_kind, _PySlot_KIND_SLOT);
|
||||
}
|
||||
|
||||
void
|
||||
_PySlotIterator_InitLegacy(_PySlotIterator *it, const void *slots,
|
||||
_PySlot_KIND kind)
|
||||
{
|
||||
init_with_kind(it, slots, kind, kind);
|
||||
}
|
||||
|
||||
void
|
||||
_PySlotIterator_Rewind(_PySlotIterator *it, const void *slots)
|
||||
{
|
||||
MSG("");
|
||||
MSG("rewind (%s slot iterator)", kind_name(it->kind));
|
||||
assert (it->is_at_end);
|
||||
assert (it->recursion_level == 0);
|
||||
assert (it->state == it->states);
|
||||
it->is_at_end = false;
|
||||
it->state->any_slot = slots;
|
||||
it->is_first_run = false;
|
||||
}
|
||||
|
||||
static Py_ssize_t
|
||||
seen_index(uint16_t id)
|
||||
{
|
||||
return id / _PySlot_SEEN_ENTRY_BITS;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
seen_mask(uint16_t id)
|
||||
{
|
||||
return ((unsigned int)1) << (id % _PySlot_SEEN_ENTRY_BITS);
|
||||
}
|
||||
|
||||
bool
|
||||
_PySlotIterator_SawSlot(_PySlotIterator *it, int id)
|
||||
{
|
||||
assert (id > 0);
|
||||
assert (id < _Py_slot_COUNT);
|
||||
return it->seen[seen_index(id)] & seen_mask(id);
|
||||
}
|
||||
|
||||
// Advance `it` to the next entry. Currently cannot fail.
|
||||
static void
|
||||
advance(_PySlotIterator *it)
|
||||
{
|
||||
MSG("advance (at level %d)", (int)it->recursion_level);
|
||||
switch (it->state->slot_struct_kind) {
|
||||
case _PySlot_KIND_SLOT: it->state->slot++; break;
|
||||
case _PySlot_KIND_TYPE: it->state->tp_slot++; break;
|
||||
case _PySlot_KIND_MOD: it->state->mod_slot++; break;
|
||||
default:
|
||||
Py_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
static int handle_first_run(_PySlotIterator *it);
|
||||
|
||||
bool
|
||||
_PySlotIterator_Next(_PySlotIterator *it)
|
||||
{
|
||||
MSG("next");
|
||||
assert(it);
|
||||
assert(!it->is_at_end);
|
||||
assert(!PyErr_Occurred());
|
||||
|
||||
it->current.sl_id = -1;
|
||||
|
||||
while (true) {
|
||||
if (it->state->slot == NULL) {
|
||||
if (it->recursion_level == 0) {
|
||||
MSG("end (initial nesting level done)");
|
||||
it->is_at_end = true;
|
||||
return 0;
|
||||
}
|
||||
MSG("pop nesting level %d", (int)it->recursion_level);
|
||||
it->recursion_level--;
|
||||
it->state = &it->states[it->recursion_level];
|
||||
advance(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (it->state->slot_struct_kind) {
|
||||
case _PySlot_KIND_SLOT: {
|
||||
MSG("copying PySlot structure");
|
||||
it->current = *it->state->slot;
|
||||
} break;
|
||||
case _PySlot_KIND_TYPE: {
|
||||
MSG("converting PyType_Slot structure");
|
||||
memset(&it->current, 0, sizeof(it->current));
|
||||
it->current.sl_id = (uint16_t)it->state->tp_slot->slot;
|
||||
it->current.sl_flags = PySlot_INTPTR;
|
||||
it->current.sl_ptr = (void*)it->state->tp_slot->pfunc;
|
||||
} break;
|
||||
case _PySlot_KIND_MOD: {
|
||||
MSG("converting PyModuleDef_Slot structure");
|
||||
memset(&it->current, 0, sizeof(it->current));
|
||||
it->current.sl_id = (uint16_t)it->state->mod_slot->slot;
|
||||
it->current.sl_flags = PySlot_INTPTR;
|
||||
it->current.sl_ptr = (void*)it->state->mod_slot->value;
|
||||
} break;
|
||||
default: {
|
||||
Py_UNREACHABLE();
|
||||
} break;
|
||||
}
|
||||
|
||||
/* shorter local names */
|
||||
PySlot *const result = &it->current;
|
||||
uint16_t flags = result->sl_flags;
|
||||
|
||||
MSG("slot %d, flags 0x%x, from %p",
|
||||
(int)result->sl_id, (unsigned)flags, it->state->slot);
|
||||
|
||||
uint16_t orig_id = result->sl_id;
|
||||
switch (it->kind) {
|
||||
case _PySlot_KIND_TYPE:
|
||||
result->sl_id = _PySlot_resolve_type_slot(result->sl_id);
|
||||
break;
|
||||
case _PySlot_KIND_MOD:
|
||||
result->sl_id = _PySlot_resolve_mod_slot(result->sl_id);
|
||||
break;
|
||||
default:
|
||||
Py_UNREACHABLE();
|
||||
}
|
||||
MSG("resolved to slot %d (%s)",
|
||||
(int)result->sl_id, _PySlot_GetName(result->sl_id));
|
||||
|
||||
if (result->sl_id == Py_slot_invalid) {
|
||||
MSG("error (unknown/invalid slot)");
|
||||
if (flags & PySlot_OPTIONAL) {
|
||||
advance(it);
|
||||
continue;
|
||||
}
|
||||
_PySlot_err_bad_slot(kind_name(it->kind), orig_id);
|
||||
goto error;
|
||||
}
|
||||
if (result->sl_id == Py_slot_end) {
|
||||
MSG("sentinel slot, flags %x", (unsigned)flags);
|
||||
if (flags & PySlot_OPTIONAL) {
|
||||
MSG("error (bad flags on sentinel)");
|
||||
PyErr_Format(PyExc_SystemError,
|
||||
"invalid flags for Py_slot_end: 0x%x",
|
||||
(unsigned int)flags);
|
||||
goto error;
|
||||
}
|
||||
it->state->slot = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result->sl_id == Py_slot_subslots
|
||||
|| result->sl_id == Py_tp_slots
|
||||
|| result->sl_id == Py_mod_slots
|
||||
) {
|
||||
if (result->sl_ptr == NULL) {
|
||||
MSG("NULL subslots; skipping");
|
||||
advance(it);
|
||||
continue;
|
||||
}
|
||||
if ((it->states[0].slot_struct_kind == _PySlot_KIND_MOD)
|
||||
&& (it->state->slot_struct_kind == _PySlot_KIND_SLOT)
|
||||
&& !(result->sl_flags & PySlot_STATIC))
|
||||
{
|
||||
PyErr_Format(PyExc_SystemError,
|
||||
"slots included from PyModuleDef must be static");
|
||||
goto error;
|
||||
}
|
||||
it->recursion_level++;
|
||||
MSG("recursing into level %d", it->recursion_level);
|
||||
if (it->recursion_level >= _PySlot_MAX_NESTING) {
|
||||
MSG("error (too much nesting)");
|
||||
PyErr_Format(PyExc_SystemError,
|
||||
"%s (slot %d): too many levels of nested slots",
|
||||
_PySlot_GetName(result->sl_id), orig_id);
|
||||
goto error;
|
||||
}
|
||||
it->state = &it->states[it->recursion_level];
|
||||
memset(it->state, 0, sizeof(_PySlotIterator_state));
|
||||
it->state->slot = result->sl_ptr;
|
||||
switch (result->sl_id) {
|
||||
case Py_slot_subslots:
|
||||
it->state->slot_struct_kind = _PySlot_KIND_SLOT; break;
|
||||
case Py_tp_slots:
|
||||
it->state->slot_struct_kind = _PySlot_KIND_TYPE; break;
|
||||
case Py_mod_slots:
|
||||
it->state->slot_struct_kind = _PySlot_KIND_MOD; break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (flags & PySlot_INTPTR) {
|
||||
MSG("casting from intptr");
|
||||
/* this should compile to nothing on common architectures */
|
||||
switch (_PySlot_get_dtype(result->sl_id)) {
|
||||
case _PySlot_DTYPE_SIZE: {
|
||||
result->sl_size = (Py_ssize_t)(intptr_t)result->sl_ptr;
|
||||
} break;
|
||||
case _PySlot_DTYPE_INT64: {
|
||||
result->sl_int64 = (int64_t)(intptr_t)result->sl_ptr;
|
||||
} break;
|
||||
case _PySlot_DTYPE_UINT64: {
|
||||
result->sl_uint64 = (uint64_t)(intptr_t)result->sl_ptr;
|
||||
} break;
|
||||
case _PySlot_DTYPE_PTR:
|
||||
case _PySlot_DTYPE_FUNC:
|
||||
case _PySlot_DTYPE_VOID:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
advance(it);
|
||||
switch (_PySlot_get_dtype(result->sl_id)) {
|
||||
case _PySlot_DTYPE_VOID:
|
||||
case _PySlot_DTYPE_PTR:
|
||||
MSG("result: %d (%s): %p",
|
||||
(int)result->sl_id, _PySlot_GetName(result->sl_id),
|
||||
(void*)result->sl_ptr);
|
||||
break;
|
||||
case _PySlot_DTYPE_FUNC:
|
||||
MSG("result: %d (%s): %p",
|
||||
(int)result->sl_id, _PySlot_GetName(result->sl_id),
|
||||
(void*)result->sl_func);
|
||||
break;
|
||||
case _PySlot_DTYPE_SIZE:
|
||||
MSG("result: %d (%s): %zd",
|
||||
(int)result->sl_id, _PySlot_GetName(result->sl_id),
|
||||
(Py_ssize_t)result->sl_size);
|
||||
break;
|
||||
case _PySlot_DTYPE_INT64:
|
||||
MSG("result: %d (%s): %ld",
|
||||
(int)result->sl_id, _PySlot_GetName(result->sl_id),
|
||||
(long)result->sl_int64);
|
||||
break;
|
||||
case _PySlot_DTYPE_UINT64:
|
||||
MSG("result: %d (%s): %lu (0x%lx)",
|
||||
(int)result->sl_id, _PySlot_GetName(result->sl_id),
|
||||
(unsigned long)result->sl_int64,
|
||||
(unsigned long)result->sl_int64);
|
||||
break;
|
||||
}
|
||||
assert (result->sl_id > 0);
|
||||
assert (result->sl_id <= _Py_slot_COUNT);
|
||||
if (it->is_first_run && (handle_first_run(it) < 0)) {
|
||||
goto error;
|
||||
}
|
||||
return result->sl_id != Py_slot_end;
|
||||
}
|
||||
Py_UNREACHABLE();
|
||||
|
||||
error:
|
||||
it->current.sl_id = Py_slot_invalid;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Validate current slot, and do bookkeeping */
|
||||
static int
|
||||
handle_first_run(_PySlotIterator *it)
|
||||
{
|
||||
int id = it->current.sl_id;
|
||||
|
||||
if (_PySlot_get_must_be_static(id)) {
|
||||
if (!(it->current.sl_flags & PySlot_STATIC)
|
||||
&& (it->state->slot_struct_kind == _PySlot_KIND_SLOT))
|
||||
{
|
||||
PyErr_Format(
|
||||
PyExc_SystemError,
|
||||
"%s requires PySlot_STATIC",
|
||||
_PySlot_GetName(id));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
_PySlot_PROBLEM_HANDLING null_handling = _PySlot_get_null_handling(id);
|
||||
if (null_handling != _PySlot_PROBLEM_ALLOW) {
|
||||
bool is_null = false;
|
||||
switch (_PySlot_get_dtype(id)) {
|
||||
case _PySlot_DTYPE_PTR: {
|
||||
is_null = it->current.sl_ptr == NULL;
|
||||
} break;
|
||||
case _PySlot_DTYPE_FUNC: {
|
||||
is_null = it->current.sl_func == NULL;
|
||||
} break;
|
||||
default: {
|
||||
//Py_UNREACHABLE();
|
||||
} break;
|
||||
}
|
||||
if (is_null) {
|
||||
MSG("slot is NULL but shouldn't");
|
||||
if (null_handling == _PySlot_PROBLEM_REJECT) {
|
||||
MSG("error (NULL rejected)");
|
||||
PyErr_Format(PyExc_SystemError,
|
||||
"NULL not allowed for slot %s",
|
||||
_PySlot_GetName(id));
|
||||
return -1;
|
||||
}
|
||||
if (it->states[0].slot_struct_kind == _PySlot_KIND_SLOT) {
|
||||
MSG("deprecated NULL");
|
||||
if (PyErr_WarnFormat(
|
||||
PyExc_DeprecationWarning,
|
||||
1,
|
||||
"NULL value in slot %s is deprecated",
|
||||
_PySlot_GetName(id)) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
MSG("unwanted NULL in legacy struct");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_PySlot_PROBLEM_HANDLING duplicate_handling = _PySlot_get_duplicate_handling(id);
|
||||
if (duplicate_handling != _PySlot_PROBLEM_ALLOW) {
|
||||
if (_PySlotIterator_SawSlot(it, id)) {
|
||||
MSG("slot was seen before but shouldn't be duplicated");
|
||||
if (duplicate_handling == _PySlot_PROBLEM_REJECT) {
|
||||
MSG("error (duplicate rejected)");
|
||||
PyErr_Format(
|
||||
PyExc_SystemError,
|
||||
"%s%s%s has multiple %s (%d) slots",
|
||||
kind_name(it->kind),
|
||||
it->name ? " " : "",
|
||||
it->name ? it->name : "",
|
||||
_PySlot_GetName(id),
|
||||
(int)it->current.sl_id);
|
||||
return -1;
|
||||
}
|
||||
if (it->states[0].slot_struct_kind == _PySlot_KIND_SLOT) {
|
||||
MSG("deprecated duplicate");
|
||||
if (PyErr_WarnFormat(
|
||||
PyExc_DeprecationWarning,
|
||||
0,
|
||||
"%s%s%s has multiple %s (%d) slots. This is deprecated.",
|
||||
kind_name(it->kind),
|
||||
it->name ? " " : "",
|
||||
it->name ? it->name : "",
|
||||
_PySlot_GetName(id),
|
||||
(int)it->current.sl_id) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
MSG("unwanted duplicate in legacy struct");
|
||||
}
|
||||
}
|
||||
}
|
||||
it->seen[seen_index(id)] |= seen_mask(id);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,836 @@
|
||||
# This file lists all PySlot values
|
||||
# This should only be used as input to Tools/build/generate_slots.py,
|
||||
# its format can change at any time (e.g. we can switch to slots.csv)
|
||||
|
||||
# Entries:
|
||||
# name: name of the slot
|
||||
# kind:
|
||||
# - 'type', 'mod': slots to create a particular kind of object
|
||||
# - 'slot': special slots applicable to any kind of object
|
||||
# - 'compat': old IDs that need to be resolved
|
||||
# dtype: data type (tag for the union of sl_ptr, sl_size, etc.)
|
||||
# equivalents: for 'compat' slots; the slots to resolve to
|
||||
# is_type_field: slot that corresponds to a field in the type object (or in
|
||||
# an array like PyNumberMethods).
|
||||
# functype: C function type, where needed
|
||||
# duplicates, nulls: How to handle common "problems" -- duplicate slots with
|
||||
# the same ID, and NULL pointers, respectively
|
||||
# - 'allow': not a problem for this slot
|
||||
# - 'deprecated': issue a deprecation warning. Don't use for new slots.
|
||||
# (typically, the problem was disallowed in docs, but allowed in practice)
|
||||
# - 'reject': raise error
|
||||
# The default for duplicate slots is 'reject'
|
||||
# The default for NULLs is 'reject' for pointer slots; 'allow' for
|
||||
# non-pointer ones
|
||||
# must_be_static: true if slot needs the PySlot_STATIC flag (in PySlot struct)
|
||||
|
||||
|
||||
[0]
|
||||
name = 'Py_slot_end'
|
||||
kind = 'slot'
|
||||
dtype = 'void'
|
||||
|
||||
[1]
|
||||
kind = 'compat'
|
||||
equivalents = {type='Py_bf_getbuffer', mod='Py_mod_create'}
|
||||
|
||||
[2]
|
||||
kind = 'compat'
|
||||
equivalents = {type='Py_bf_releasebuffer', mod='Py_mod_exec'}
|
||||
|
||||
[3]
|
||||
kind = 'compat'
|
||||
equivalents = {type='Py_mp_ass_subscript', mod='Py_mod_multiple_interpreters'}
|
||||
|
||||
[4]
|
||||
kind = 'compat'
|
||||
equivalents = {type='Py_mp_length', mod='Py_mod_gil'}
|
||||
|
||||
[5]
|
||||
name = 'Py_mp_subscript'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[6]
|
||||
name = 'Py_nb_absolute'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'unaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[7]
|
||||
name = 'Py_nb_add'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[8]
|
||||
name = 'Py_nb_and'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[9]
|
||||
name = 'Py_nb_bool'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'inquiry'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[10]
|
||||
name = 'Py_nb_divmod'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[11]
|
||||
name = 'Py_nb_float'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'unaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[12]
|
||||
name = 'Py_nb_floor_divide'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[13]
|
||||
name = 'Py_nb_index'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'unaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[14]
|
||||
name = 'Py_nb_inplace_add'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[15]
|
||||
name = 'Py_nb_inplace_and'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[16]
|
||||
name = 'Py_nb_inplace_floor_divide'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[17]
|
||||
name = 'Py_nb_inplace_lshift'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[18]
|
||||
name = 'Py_nb_inplace_multiply'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[19]
|
||||
name = 'Py_nb_inplace_or'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[20]
|
||||
name = 'Py_nb_inplace_power'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'ternaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[21]
|
||||
name = 'Py_nb_inplace_remainder'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[22]
|
||||
name = 'Py_nb_inplace_rshift'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[23]
|
||||
name = 'Py_nb_inplace_subtract'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[24]
|
||||
name = 'Py_nb_inplace_true_divide'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[25]
|
||||
name = 'Py_nb_inplace_xor'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[26]
|
||||
name = 'Py_nb_int'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'unaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[27]
|
||||
name = 'Py_nb_invert'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'unaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[28]
|
||||
name = 'Py_nb_lshift'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[29]
|
||||
name = 'Py_nb_multiply'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[30]
|
||||
name = 'Py_nb_negative'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'unaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[31]
|
||||
name = 'Py_nb_or'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[32]
|
||||
name = 'Py_nb_positive'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'unaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[33]
|
||||
name = 'Py_nb_power'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'ternaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[34]
|
||||
name = 'Py_nb_remainder'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[35]
|
||||
name = 'Py_nb_rshift'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[36]
|
||||
name = 'Py_nb_subtract'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[37]
|
||||
name = 'Py_nb_true_divide'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[38]
|
||||
name = 'Py_nb_xor'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[39]
|
||||
name = 'Py_sq_ass_item'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'ssizeobjargproc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[40]
|
||||
name = 'Py_sq_concat'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[41]
|
||||
name = 'Py_sq_contains'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'objobjproc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[42]
|
||||
name = 'Py_sq_inplace_concat'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[43]
|
||||
name = 'Py_sq_inplace_repeat'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'ssizeargfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[44]
|
||||
name = 'Py_sq_item'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'ssizeargfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[45]
|
||||
name = 'Py_sq_length'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'lenfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[46]
|
||||
name = 'Py_sq_repeat'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'ssizeargfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[47]
|
||||
name = 'Py_tp_alloc'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'allocfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[48]
|
||||
name = 'Py_tp_base'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
dtype = 'ptr'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[49]
|
||||
name = 'Py_tp_bases'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
dtype = 'ptr'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[50]
|
||||
name = 'Py_tp_call'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'ternaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[51]
|
||||
name = 'Py_tp_clear'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'inquiry'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[52]
|
||||
name = 'Py_tp_dealloc'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'destructor'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[53]
|
||||
name = 'Py_tp_del'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'destructor'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[54]
|
||||
name = 'Py_tp_descr_get'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'descrgetfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[55]
|
||||
name = 'Py_tp_descr_set'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'descrsetfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[56]
|
||||
name = 'Py_tp_doc'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
dtype = 'ptr'
|
||||
nulls = 'allow'
|
||||
|
||||
[57]
|
||||
name = 'Py_tp_getattr'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'getattrfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[58]
|
||||
name = 'Py_tp_getattro'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'getattrofunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[59]
|
||||
name = 'Py_tp_hash'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'hashfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[60]
|
||||
name = 'Py_tp_init'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'initproc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[61]
|
||||
name = 'Py_tp_is_gc'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'inquiry'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[62]
|
||||
name = 'Py_tp_iter'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'getiterfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[63]
|
||||
name = 'Py_tp_iternext'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'iternextfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[64]
|
||||
name = 'Py_tp_methods'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
dtype = 'ptr'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
must_be_static = true
|
||||
|
||||
[65]
|
||||
name = 'Py_tp_new'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'newfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[66]
|
||||
name = 'Py_tp_repr'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'reprfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[67]
|
||||
name = 'Py_tp_richcompare'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'richcmpfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[68]
|
||||
name = 'Py_tp_setattr'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'setattrfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[69]
|
||||
name = 'Py_tp_setattro'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'setattrofunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[70]
|
||||
name = 'Py_tp_str'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'reprfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[71]
|
||||
name = 'Py_tp_traverse'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'traverseproc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[72]
|
||||
name = 'Py_tp_members'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
dtype = 'ptr'
|
||||
nulls = 'reject'
|
||||
must_be_static = true
|
||||
|
||||
[73]
|
||||
name = 'Py_tp_getset'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
dtype = 'ptr'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
must_be_static = true
|
||||
|
||||
[74]
|
||||
name = 'Py_tp_free'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'freefunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[75]
|
||||
name = 'Py_nb_matrix_multiply'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[76]
|
||||
name = 'Py_nb_inplace_matrix_multiply'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'binaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[77]
|
||||
name = 'Py_am_await'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'unaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[78]
|
||||
name = 'Py_am_aiter'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'unaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[79]
|
||||
name = 'Py_am_anext'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'unaryfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[80]
|
||||
name = 'Py_tp_finalize'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'destructor'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[81]
|
||||
name = 'Py_am_send'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'sendfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[82]
|
||||
name = 'Py_tp_vectorcall'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'vectorcallfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[83]
|
||||
name = 'Py_tp_token'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
dtype = 'ptr'
|
||||
field = 'ht_token'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'allow'
|
||||
|
||||
[84]
|
||||
name = 'Py_mod_create'
|
||||
kind = 'mod'
|
||||
dtype = 'func'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[85]
|
||||
name = 'Py_mod_exec'
|
||||
kind = 'mod'
|
||||
dtype = 'func'
|
||||
duplicates = 'allow' # only alowed in PyModuleDef.m_slots
|
||||
nulls = 'reject'
|
||||
|
||||
[86]
|
||||
name = 'Py_mod_multiple_interpreters'
|
||||
kind = 'mod'
|
||||
dtype = 'uint64'
|
||||
|
||||
[87]
|
||||
name = 'Py_mod_gil'
|
||||
kind = 'mod'
|
||||
dtype = 'uint64'
|
||||
|
||||
[88]
|
||||
name = 'Py_bf_getbuffer'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'getbufferproc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[89]
|
||||
name = 'Py_bf_releasebuffer'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'releasebufferproc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[90]
|
||||
name = 'Py_mp_ass_subscript'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'objobjargproc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[91]
|
||||
name = 'Py_mp_length'
|
||||
kind = 'type'
|
||||
is_type_field = true
|
||||
functype = 'lenfunc'
|
||||
duplicates = 'deprecated'
|
||||
nulls = 'deprecated'
|
||||
|
||||
[92]
|
||||
name = 'Py_slot_subslots'
|
||||
kind = 'slot'
|
||||
dtype = 'ptr'
|
||||
nulls = 'allow'
|
||||
|
||||
[93]
|
||||
name = 'Py_tp_slots'
|
||||
kind = 'type'
|
||||
dtype = 'ptr'
|
||||
nulls = 'allow'
|
||||
|
||||
[94]
|
||||
name = 'Py_mod_slots'
|
||||
kind = 'mod'
|
||||
dtype = 'ptr'
|
||||
nulls = 'allow'
|
||||
|
||||
[95]
|
||||
name = 'Py_tp_name'
|
||||
kind = 'type'
|
||||
dtype = 'ptr'
|
||||
|
||||
[96]
|
||||
name = 'Py_tp_basicsize'
|
||||
kind = 'type'
|
||||
dtype = 'size'
|
||||
|
||||
[97]
|
||||
name = 'Py_tp_extra_basicsize'
|
||||
kind = 'type'
|
||||
dtype = 'size'
|
||||
|
||||
[98]
|
||||
name = 'Py_tp_itemsize'
|
||||
kind = 'type'
|
||||
dtype = 'size'
|
||||
|
||||
[99]
|
||||
name = 'Py_tp_flags'
|
||||
kind = 'type'
|
||||
dtype = 'uint64'
|
||||
|
||||
[100]
|
||||
name = 'Py_mod_name'
|
||||
kind = 'mod'
|
||||
dtype = 'ptr'
|
||||
|
||||
[101]
|
||||
name = 'Py_mod_doc'
|
||||
kind = 'mod'
|
||||
dtype = 'ptr'
|
||||
|
||||
[102]
|
||||
name = 'Py_mod_state_size'
|
||||
kind = 'mod'
|
||||
dtype = 'size'
|
||||
|
||||
[103]
|
||||
name = 'Py_mod_methods'
|
||||
kind = 'mod'
|
||||
dtype = 'ptr'
|
||||
must_be_static = true
|
||||
|
||||
[104]
|
||||
name = 'Py_mod_state_traverse'
|
||||
kind = 'mod'
|
||||
dtype = 'func'
|
||||
|
||||
[105]
|
||||
name = 'Py_mod_state_clear'
|
||||
kind = 'mod'
|
||||
dtype = 'func'
|
||||
|
||||
[106]
|
||||
name = 'Py_mod_state_free'
|
||||
kind = 'mod'
|
||||
dtype = 'func'
|
||||
|
||||
[107]
|
||||
name = 'Py_tp_metaclass'
|
||||
kind = 'type'
|
||||
dtype = 'ptr'
|
||||
|
||||
[108]
|
||||
name = 'Py_tp_module'
|
||||
kind = 'type'
|
||||
dtype = 'ptr'
|
||||
|
||||
[109]
|
||||
name = 'Py_mod_abi'
|
||||
kind = 'mod'
|
||||
dtype = 'ptr'
|
||||
duplicates = 'allow'
|
||||
|
||||
[110]
|
||||
name = 'Py_mod_token'
|
||||
kind = 'mod'
|
||||
dtype = 'ptr'
|
||||
Generated
+119
@@ -0,0 +1,119 @@
|
||||
/* Generated by Tools/build/generate_slots.py */
|
||||
|
||||
#include "Python.h"
|
||||
#include "pycore_slots.h" // _PySlot_names
|
||||
|
||||
const char *const _PySlot_names[] = {
|
||||
"Py_slot_end",
|
||||
"Py_bf_getbuffer/Py_mod_create",
|
||||
"Py_bf_releasebuffer/Py_mod_exec",
|
||||
"Py_mp_ass_subscript/Py_mod_multiple_interpreters",
|
||||
"Py_mp_length/Py_mod_gil",
|
||||
"Py_mp_subscript",
|
||||
"Py_nb_absolute",
|
||||
"Py_nb_add",
|
||||
"Py_nb_and",
|
||||
"Py_nb_bool",
|
||||
"Py_nb_divmod",
|
||||
"Py_nb_float",
|
||||
"Py_nb_floor_divide",
|
||||
"Py_nb_index",
|
||||
"Py_nb_inplace_add",
|
||||
"Py_nb_inplace_and",
|
||||
"Py_nb_inplace_floor_divide",
|
||||
"Py_nb_inplace_lshift",
|
||||
"Py_nb_inplace_multiply",
|
||||
"Py_nb_inplace_or",
|
||||
"Py_nb_inplace_power",
|
||||
"Py_nb_inplace_remainder",
|
||||
"Py_nb_inplace_rshift",
|
||||
"Py_nb_inplace_subtract",
|
||||
"Py_nb_inplace_true_divide",
|
||||
"Py_nb_inplace_xor",
|
||||
"Py_nb_int",
|
||||
"Py_nb_invert",
|
||||
"Py_nb_lshift",
|
||||
"Py_nb_multiply",
|
||||
"Py_nb_negative",
|
||||
"Py_nb_or",
|
||||
"Py_nb_positive",
|
||||
"Py_nb_power",
|
||||
"Py_nb_remainder",
|
||||
"Py_nb_rshift",
|
||||
"Py_nb_subtract",
|
||||
"Py_nb_true_divide",
|
||||
"Py_nb_xor",
|
||||
"Py_sq_ass_item",
|
||||
"Py_sq_concat",
|
||||
"Py_sq_contains",
|
||||
"Py_sq_inplace_concat",
|
||||
"Py_sq_inplace_repeat",
|
||||
"Py_sq_item",
|
||||
"Py_sq_length",
|
||||
"Py_sq_repeat",
|
||||
"Py_tp_alloc",
|
||||
"Py_tp_base",
|
||||
"Py_tp_bases",
|
||||
"Py_tp_call",
|
||||
"Py_tp_clear",
|
||||
"Py_tp_dealloc",
|
||||
"Py_tp_del",
|
||||
"Py_tp_descr_get",
|
||||
"Py_tp_descr_set",
|
||||
"Py_tp_doc",
|
||||
"Py_tp_getattr",
|
||||
"Py_tp_getattro",
|
||||
"Py_tp_hash",
|
||||
"Py_tp_init",
|
||||
"Py_tp_is_gc",
|
||||
"Py_tp_iter",
|
||||
"Py_tp_iternext",
|
||||
"Py_tp_methods",
|
||||
"Py_tp_new",
|
||||
"Py_tp_repr",
|
||||
"Py_tp_richcompare",
|
||||
"Py_tp_setattr",
|
||||
"Py_tp_setattro",
|
||||
"Py_tp_str",
|
||||
"Py_tp_traverse",
|
||||
"Py_tp_members",
|
||||
"Py_tp_getset",
|
||||
"Py_tp_free",
|
||||
"Py_nb_matrix_multiply",
|
||||
"Py_nb_inplace_matrix_multiply",
|
||||
"Py_am_await",
|
||||
"Py_am_aiter",
|
||||
"Py_am_anext",
|
||||
"Py_tp_finalize",
|
||||
"Py_am_send",
|
||||
"Py_tp_vectorcall",
|
||||
"Py_tp_token",
|
||||
"Py_mod_create",
|
||||
"Py_mod_exec",
|
||||
"Py_mod_multiple_interpreters",
|
||||
"Py_mod_gil",
|
||||
"Py_bf_getbuffer",
|
||||
"Py_bf_releasebuffer",
|
||||
"Py_mp_ass_subscript",
|
||||
"Py_mp_length",
|
||||
"Py_slot_subslots",
|
||||
"Py_tp_slots",
|
||||
"Py_mod_slots",
|
||||
"Py_tp_name",
|
||||
"Py_tp_basicsize",
|
||||
"Py_tp_extra_basicsize",
|
||||
"Py_tp_itemsize",
|
||||
"Py_tp_flags",
|
||||
"Py_mod_name",
|
||||
"Py_mod_doc",
|
||||
"Py_mod_state_size",
|
||||
"Py_mod_methods",
|
||||
"Py_mod_state_traverse",
|
||||
"Py_mod_state_clear",
|
||||
"Py_mod_state_free",
|
||||
"Py_tp_metaclass",
|
||||
"Py_tp_module",
|
||||
"Py_mod_abi",
|
||||
"Py_mod_token",
|
||||
NULL
|
||||
};
|
||||
@@ -38,3 +38,7 @@ ignore = [
|
||||
"generate_{re_casefix,sre_constants,token}.py" = [
|
||||
"UP031", # Use format specifiers instead of percent format
|
||||
]
|
||||
"generate_slots.py" = [
|
||||
"I001", # Import block is un-sorted
|
||||
"ISC003", # Explicitly concatenated string
|
||||
]
|
||||
|
||||
Executable
+399
@@ -0,0 +1,399 @@
|
||||
#!/usr/bin/python
|
||||
"""Generate type/module slot files
|
||||
"""
|
||||
|
||||
# See the input file (Python/slots.toml) for a description of its format.
|
||||
|
||||
import io
|
||||
import sys
|
||||
import json
|
||||
import tomllib
|
||||
import argparse
|
||||
import functools
|
||||
import contextlib
|
||||
import collections
|
||||
from pathlib import Path
|
||||
|
||||
GENERATED_BY = 'Generated by Tools/build/generate_slots.py'
|
||||
|
||||
REPO_ROOT = Path(__file__).parent.parent.parent
|
||||
DEFAULT_INPUT_PATH = REPO_ROOT / 'Python/slots.toml'
|
||||
INCLUDE_PATH = REPO_ROOT / 'Include'
|
||||
DEFAULT_PUBLIC_HEADER_PATH = INCLUDE_PATH / 'slots_generated.h'
|
||||
DEFAULT_PRIVATE_HEADER_PATH = INCLUDE_PATH / 'internal/pycore_slots_generated.h'
|
||||
DEFAULT_C_PATH = REPO_ROOT / 'Python/slots_generated.c'
|
||||
|
||||
TABLES = {
|
||||
'tp': 'ht_type',
|
||||
'am': 'as_async',
|
||||
'nb': 'as_number',
|
||||
'mp': 'as_mapping',
|
||||
'sq': 'as_sequence',
|
||||
'bf': 'as_buffer',
|
||||
}
|
||||
|
||||
|
||||
class SlotInfo:
|
||||
def __init__(self, id, data):
|
||||
self.id = id
|
||||
self.kind = data['kind']
|
||||
self._data = data
|
||||
try:
|
||||
self.name = data['name']
|
||||
except KeyError:
|
||||
self.name = '/'.join(data["equivalents"].values())
|
||||
else:
|
||||
assert self.name.isidentifier
|
||||
|
||||
@functools.cached_property
|
||||
def equivalents(self):
|
||||
return self._data['equivalents']
|
||||
|
||||
@functools.cached_property
|
||||
def dtype(self):
|
||||
try:
|
||||
return self._data['dtype']
|
||||
except KeyError:
|
||||
if self.is_type_field:
|
||||
return 'func'
|
||||
raise
|
||||
|
||||
@functools.cached_property
|
||||
def functype(self):
|
||||
return self._data['functype']
|
||||
|
||||
@functools.cached_property
|
||||
def is_type_field(self):
|
||||
return self._data.get('is_type_field')
|
||||
|
||||
@functools.cached_property
|
||||
def type_field(self):
|
||||
assert self.is_type_field
|
||||
return self._data.get('field', self.name.removeprefix('Py_'))
|
||||
|
||||
@functools.cached_property
|
||||
def type_table_ident(self):
|
||||
assert self.is_type_field
|
||||
return self._data.get('table', self.type_field[:2])
|
||||
|
||||
@functools.cached_property
|
||||
def duplicate_handling(self):
|
||||
return self._data.get('duplicates', 'reject')
|
||||
|
||||
@functools.cached_property
|
||||
def null_handling(self):
|
||||
try:
|
||||
return self._data['nulls']
|
||||
except KeyError:
|
||||
if self.kind == 'compat':
|
||||
return 'allow'
|
||||
if self.dtype in {'ptr', 'func'}:
|
||||
return 'reject'
|
||||
return 'allow'
|
||||
|
||||
@functools.cached_property
|
||||
def must_be_static(self):
|
||||
return self._data.get('must_be_static', False)
|
||||
|
||||
|
||||
def parse_slots(file):
|
||||
toml_contents = tomllib.load(file)
|
||||
result = [None] * len(toml_contents)
|
||||
for key, data in toml_contents.items():
|
||||
slot_id = int(key)
|
||||
try:
|
||||
if result[slot_id]:
|
||||
raise ValueError(f'slot ID {slot_id} repeated')
|
||||
result[slot_id] = SlotInfo(slot_id, data)
|
||||
except Exception as e:
|
||||
e.add_note(f'handling slot {slot_id}')
|
||||
raise
|
||||
return result
|
||||
|
||||
|
||||
class CWriter:
|
||||
"""Simple helper for generating C code"""
|
||||
|
||||
def __init__(self, file):
|
||||
self.file = file
|
||||
self.indent = ''
|
||||
self(f'/* {GENERATED_BY} */')
|
||||
self()
|
||||
|
||||
def out(self, *args, **kwargs):
|
||||
"""print args to the file, with current indent at the start"""
|
||||
print(self.indent, end='', file=self.file)
|
||||
print(*args, file=self.file, **kwargs)
|
||||
|
||||
__call__ = out
|
||||
|
||||
@contextlib.contextmanager
|
||||
def block(self, header=None, end=''):
|
||||
"""Context for a {}-enclosed block of C"""
|
||||
if header is None:
|
||||
self.out('{')
|
||||
else:
|
||||
self.out(header, '{')
|
||||
old_indent = self.indent
|
||||
self.indent += ' '
|
||||
yield
|
||||
self.indent = old_indent
|
||||
self.out('}' + end)
|
||||
|
||||
|
||||
def write_public_header(f, slots):
|
||||
out = CWriter(f)
|
||||
out(f'#ifndef _PY_HAVE_SLOTS_GENERATED_H')
|
||||
out(f'#define _PY_HAVE_SLOTS_GENERATED_H')
|
||||
out()
|
||||
out(f'#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15)')
|
||||
out(f'#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) NEW')
|
||||
out(f'#else')
|
||||
out(f'#define _Py_SLOT_COMPAT_VALUE(OLD, NEW) OLD')
|
||||
out(f'#endif')
|
||||
out()
|
||||
compat_ids = {}
|
||||
for slot in slots:
|
||||
if slot.kind == 'compat':
|
||||
for new_name in slot.equivalents.values():
|
||||
compat_ids[new_name] = slot.id
|
||||
for slot in slots:
|
||||
if slot.kind == 'compat':
|
||||
continue
|
||||
slot_id = slot.id
|
||||
if compat := compat_ids.get(slot.name):
|
||||
slot_id = f'_Py_SLOT_COMPAT_VALUE({compat}, {slot_id})'
|
||||
out(f'#define {slot.name} {slot_id}')
|
||||
out()
|
||||
out(f'#define _Py_slot_COUNT {len(slots)}')
|
||||
out(f'#endif /* _PY_HAVE_SLOTS_GENERATED_H */')
|
||||
|
||||
|
||||
def write_private_header(f, slots):
|
||||
out = CWriter(f)
|
||||
|
||||
def add_case(slot):
|
||||
out(out(f' case {slot.id}:'))
|
||||
|
||||
slots_by_name = {slot.name: slot for slot in slots}
|
||||
|
||||
out(f'#ifndef _PY_HAVE_INTERNAL_SLOTS_GENERATED_H')
|
||||
out(f'#define _PY_HAVE_INTERNAL_SLOTS_GENERATED_H')
|
||||
for kind in 'type', 'mod':
|
||||
out()
|
||||
out(f'static inline uint16_t')
|
||||
out(f'_PySlot_resolve_{kind}_slot(uint16_t slot_id)')
|
||||
with out.block():
|
||||
with out.block('switch (slot_id)'):
|
||||
good_slots = []
|
||||
for slot in slots:
|
||||
if slot.kind == 'compat':
|
||||
new_slot = slots_by_name[slot.equivalents[kind]]
|
||||
out(f'case {slot.id}:')
|
||||
out(f' return {new_slot.name};')
|
||||
elif slot.kind in {kind, 'slot'}:
|
||||
good_slots.append(f'case {slot.name}:')
|
||||
for case in good_slots:
|
||||
out(case)
|
||||
out(f' return slot_id;')
|
||||
out(f'default:')
|
||||
out(f' return Py_slot_invalid;')
|
||||
out()
|
||||
out(f'static inline void*')
|
||||
out(f'_PySlot_type_getslot(PyTypeObject *tp, uint16_t slot_id)')
|
||||
with out.block():
|
||||
with out.block('switch (slot_id)'):
|
||||
for slot in slots:
|
||||
if slot.is_type_field:
|
||||
field = slot.type_field
|
||||
table_ident = slot.type_table_ident
|
||||
if table_ident == 'tp':
|
||||
out(f'case {slot.name}:')
|
||||
out(f' return (void*)tp->{field};')
|
||||
else:
|
||||
if table_ident == 'ht':
|
||||
cond = 'tp->tp_flags & Py_TPFLAGS_HEAPTYPE'
|
||||
val = f'((PyHeapTypeObject*)tp)->{field}'
|
||||
else:
|
||||
table = TABLES[table_ident]
|
||||
cond = f'tp->tp_{table}'
|
||||
val = f'tp->tp_{table}->{field}'
|
||||
out(f'case {slot.name}:')
|
||||
out(f' if (!({cond})) return NULL;')
|
||||
out(f' return (void*){val};')
|
||||
out(f'_PySlot_err_bad_slot("PyType_GetSlot", slot_id);')
|
||||
out(f'return NULL;')
|
||||
out()
|
||||
out(f'static inline void')
|
||||
out(f'_PySlot_heaptype_apply_field_slot(PyHeapTypeObject *ht,',
|
||||
f'PySlot slot)')
|
||||
with out.block():
|
||||
with out.block('switch (slot.sl_id)'):
|
||||
for slot in slots:
|
||||
if slot.is_type_field:
|
||||
field = slot.type_field
|
||||
table_ident = slot.type_table_ident
|
||||
if table_ident == 'ht':
|
||||
continue
|
||||
table = TABLES[table_ident]
|
||||
if slot.dtype == 'func':
|
||||
functype = f'({slot.functype})'
|
||||
else:
|
||||
functype = ''
|
||||
out(f'case {slot.name}:')
|
||||
out(f' ht->{table}.{field} = {functype}slot.sl_{slot.dtype};')
|
||||
out(f' break;')
|
||||
out()
|
||||
out(f'static inline _PySlot_DTYPE')
|
||||
out(f'_PySlot_get_dtype(uint16_t slot_id)')
|
||||
with out.block():
|
||||
with out.block('switch (slot_id)'):
|
||||
for slot in slots:
|
||||
if slot.kind == 'compat':
|
||||
continue
|
||||
dtype = slot.dtype
|
||||
name = slot.name
|
||||
out(f'case {name}: return _PySlot_DTYPE_{dtype.upper()};')
|
||||
out(f'default: return _PySlot_DTYPE_VOID;')
|
||||
out()
|
||||
out(f'static inline _PySlot_PROBLEM_HANDLING')
|
||||
out(f'_PySlot_get_duplicate_handling(uint16_t slot_id)')
|
||||
with out.block():
|
||||
with out.block('switch (slot_id)'):
|
||||
results = collections.defaultdict(list)
|
||||
for slot in slots:
|
||||
if slot.kind == 'compat':
|
||||
continue
|
||||
handling = slot.duplicate_handling
|
||||
results[handling.upper()].append(f'case {slot.name}:')
|
||||
results.pop('REJECT')
|
||||
for handling, cases in results.items():
|
||||
for case in cases:
|
||||
out(case)
|
||||
out(f' return _PySlot_PROBLEM_{handling};')
|
||||
out(f'default:')
|
||||
out(f' return _PySlot_PROBLEM_REJECT;')
|
||||
out()
|
||||
out(f'static inline _PySlot_PROBLEM_HANDLING')
|
||||
out(f'_PySlot_get_null_handling(uint16_t slot_id)')
|
||||
with out.block():
|
||||
with out.block('switch (slot_id)'):
|
||||
results = collections.defaultdict(list)
|
||||
for slot in slots:
|
||||
if slot.kind == 'compat':
|
||||
continue
|
||||
handling = slot.null_handling
|
||||
if handling is None:
|
||||
if slot.kind != 'compat' and slot.dtype in {'ptr', 'func'}:
|
||||
handling = 'reject'
|
||||
else:
|
||||
handling = 'allow'
|
||||
results[handling.upper()].append(f'case {slot.name}:')
|
||||
results.pop('REJECT')
|
||||
for handling, cases in results.items():
|
||||
for case in cases:
|
||||
out(case)
|
||||
out(f' return _PySlot_PROBLEM_{handling};')
|
||||
out(f'default:')
|
||||
out(f' return _PySlot_PROBLEM_REJECT;')
|
||||
out()
|
||||
out(f'static inline bool')
|
||||
out(f'_PySlot_get_must_be_static(uint16_t slot_id)')
|
||||
with out.block():
|
||||
with out.block('switch (slot_id)'):
|
||||
cases = []
|
||||
for slot in slots:
|
||||
if slot.must_be_static:
|
||||
out(f'case {slot.name}: return true;')
|
||||
out(f'return false;')
|
||||
out()
|
||||
out(f'#endif /* _PY_HAVE_INTERNAL_SLOTS_GENERATED_H */')
|
||||
|
||||
|
||||
def write_c(f, slots):
|
||||
out = CWriter(f)
|
||||
out('#include "Python.h"')
|
||||
out('#include "pycore_slots.h" // _PySlot_names')
|
||||
out()
|
||||
with out.block(f'const char *const _PySlot_names[] =', end=';'):
|
||||
for slot in slots:
|
||||
out(f'"{slot.name}",')
|
||||
out('NULL')
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def replace_file(filename):
|
||||
file_path = Path(filename)
|
||||
with io.StringIO() as sio:
|
||||
yield sio
|
||||
try:
|
||||
old_text = file_path.read_text()
|
||||
except FileNotFoundError:
|
||||
old_text = None
|
||||
new_text = sio.getvalue()
|
||||
if old_text == new_text:
|
||||
print(f'{filename}: not modified', file=sys.stderr)
|
||||
else:
|
||||
print(f'{filename}: writing new content', file=sys.stderr)
|
||||
file_path.write_text(new_text)
|
||||
|
||||
|
||||
def main(argv):
|
||||
if len(argv) == 1:
|
||||
# No sens calling this with no arguments.
|
||||
argv.append('--help')
|
||||
|
||||
parser = argparse.ArgumentParser(prog=argv[0], description=__doc__)
|
||||
parser.add_argument(
|
||||
'-i', '--input', default=DEFAULT_INPUT_PATH,
|
||||
help=f'the input file (default: {DEFAULT_INPUT_PATH})')
|
||||
parser.add_argument(
|
||||
'--generate-all', action=argparse.BooleanOptionalAction,
|
||||
help='write all output files to their default locations')
|
||||
parser.add_argument(
|
||||
'-j', '--jsonl', action=argparse.BooleanOptionalAction,
|
||||
help='write info to stdout in "JSON Lines" format (one JSON per line)')
|
||||
outfile_group = parser.add_argument_group(
|
||||
'output files',
|
||||
description='By default, no files are generated. Use --generate-all '
|
||||
+ 'or the options below to generate them.')
|
||||
outfile_group.add_argument(
|
||||
'-H', '--public-header',
|
||||
help='file into which to write the public header')
|
||||
outfile_group.add_argument(
|
||||
'-I', '--private-header',
|
||||
help='file into which to write the private header')
|
||||
outfile_group.add_argument(
|
||||
'-C', '--cfile',
|
||||
help='file into which to write internal C code')
|
||||
args = parser.parse_args(argv[1:])
|
||||
|
||||
if args.generate_all:
|
||||
if args.public_header is None:
|
||||
args.public_header = DEFAULT_PUBLIC_HEADER_PATH
|
||||
if args.private_header is None:
|
||||
args.private_header = DEFAULT_PRIVATE_HEADER_PATH
|
||||
if args.cfile is None:
|
||||
args.cfile = DEFAULT_C_PATH
|
||||
|
||||
with open(args.input, 'rb') as f:
|
||||
slots = parse_slots(f)
|
||||
|
||||
if args.jsonl:
|
||||
for slot in slots:
|
||||
print(json.dumps(slot.to_dict()))
|
||||
|
||||
if args.public_header:
|
||||
with replace_file(args.public_header) as f:
|
||||
write_public_header(f, slots)
|
||||
|
||||
if args.private_header:
|
||||
with replace_file(args.private_header) as f:
|
||||
write_private_header(f, slots)
|
||||
|
||||
if args.cfile:
|
||||
with replace_file(args.cfile) as f:
|
||||
write_c(f, slots)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv)
|
||||
@@ -462,6 +462,7 @@ Modules/_testcapi/exceptions.c - PyRecursingInfinitelyError_Type -
|
||||
Modules/_testcapi/heaptype.c - _testcapimodule -
|
||||
Modules/_testcapi/mem.c - FmData -
|
||||
Modules/_testcapi/mem.c - FmHook -
|
||||
Modules/_testcapi/module.c module_from_def_nonstatic_nested subslots -
|
||||
Modules/_testcapi/object.c - MyObject_dealloc_called -
|
||||
Modules/_testcapi/object.c - MyType -
|
||||
Modules/_testcapi/structmember.c - test_structmembersType_OldAPI -
|
||||
@@ -576,6 +577,7 @@ Modules/_testinternalcapi.c - Test_EvalFrame_Resumes -
|
||||
Modules/_testinternalcapi.c - Test_EvalFrame_Loads -
|
||||
Modules/_testinternalcapi/interpreter.c - Test_EvalFrame_Resumes -
|
||||
Modules/_testinternalcapi/interpreter.c - Test_EvalFrame_Loads -
|
||||
Modules/_testlimitedcapi/slots.c - TestMethods -
|
||||
Modules/_testmultiphase.c - Example_Type_slots -
|
||||
Modules/_testmultiphase.c - Example_Type_spec -
|
||||
Modules/_testmultiphase.c - Example_methods -
|
||||
@@ -618,6 +620,13 @@ Modules/_testmultiphase.c - slots_exec_unreported_exception -
|
||||
Modules/_testmultiphase.c - slots_nonmodule_with_exec_slots -
|
||||
Modules/_testmultiphase.c - testexport_methods -
|
||||
Modules/_testmultiphase.c - uninitialized_def -
|
||||
Modules/_testmultiphase.c PyModExport__test_from_modexport slots -
|
||||
Modules/_testmultiphase.c PyModExport__test_from_modexport_gil_used slots -
|
||||
Modules/_testmultiphase.c PyModExport__test_from_modexport_create_nonmodule slots -
|
||||
Modules/_testmultiphase.c PyModExport__test_from_modexport_create_nonmodule_gil_used slots -
|
||||
Modules/_testmultiphase.c PyModExport__test_from_modexport_smoke slots -
|
||||
Modules/_testmultiphase.c - modexport_empty_slots -
|
||||
Modules/_testmultiphase.c - modexport_minimal_slots -
|
||||
Modules/_testsinglephase.c - global_state -
|
||||
Modules/_testsinglephase.c - static_module_circular -
|
||||
Modules/_xxtestfuzz/_xxtestfuzz.c - _fuzzmodule -
|
||||
|
||||
|
@@ -3599,7 +3599,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
|
||||
if test "$srcdir" != . -a "$srcdir" != "$(pwd)"; then
|
||||
# If we're building out-of-tree, we need to make sure the following
|
||||
# resources get picked up before their $srcdir counterparts.
|
||||
# Objects/ -> typeslots.inc
|
||||
# Objects/ -> slots_generated.c
|
||||
# Include/ -> Python.h
|
||||
# (A side effect of this is that these resources will automatically be
|
||||
# regenerated when building out-of-tree, regardless of whether or not
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ AC_SUBST([BASECPPFLAGS])
|
||||
if test "$srcdir" != . -a "$srcdir" != "$(pwd)"; then
|
||||
# If we're building out-of-tree, we need to make sure the following
|
||||
# resources get picked up before their $srcdir counterparts.
|
||||
# Objects/ -> typeslots.inc
|
||||
# Objects/ -> slots_generated.c
|
||||
# Include/ -> Python.h
|
||||
# (A side effect of this is that these resources will automatically be
|
||||
# regenerated when building out-of-tree, regardless of whether or not
|
||||
|
||||
Reference in New Issue
Block a user