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:
Petr Viktorin
2026-05-05 09:18:04 +02:00
committed by GitHub
parent 5dd2161716
commit 508b49845d
56 changed files with 5433 additions and 935 deletions
+2 -1
View File
@@ -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
+8 -8
View File
@@ -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
+1
View File
@@ -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
View File
@@ -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
+240
View File
@@ -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
View File
@@ -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`.
+10
View File
@@ -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
+26
View File
@@ -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,,
+13 -8
View File
@@ -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.
+6 -6
View 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
+38
View File
@@ -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
View File
@@ -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
View File
@@ -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 */
+1 -1
View File
@@ -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,
+138
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+3
View File
@@ -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 *);
+1
View File
@@ -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;
+56
View File
@@ -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
View File
@@ -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 */
+18 -7
View File
@@ -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):
+4 -2
View File
@@ -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)
+314
View File
@@ -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())
+7 -7
View File
@@ -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
+1
View File
@@ -743,6 +743,7 @@ SYMBOL_NAMES = (
"PyType_Freeze",
"PyType_FromMetaclass",
"PyType_FromModuleAndSpec",
"PyType_FromSlots",
"PyType_FromSpec",
"PyType_FromSpecWithBases",
"PyType_GenericAlloc",
+16 -10
View File
@@ -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.
+62
View File
@@ -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]
+1 -1
View File
@@ -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
View File
@@ -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},
};
+2 -2
View File
@@ -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;
}
+3
View File
@@ -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;
}
+1
View File
@@ -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);
+629
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 *
-84
View File
@@ -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)},
-55
View File
@@ -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()
+1
View File
@@ -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)
+2
View File
@@ -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" />
+6
View File
@@ -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>
+1
View File
@@ -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" />
+1
View File
@@ -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" />
+6
View File
@@ -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">
+18
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+836
View File
@@ -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'
+119
View File
@@ -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
};
+4
View File
@@ -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
]
+399
View File
@@ -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)
+9
View File
@@ -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 -
1 filename funcname name reason
462 Modules/_testcapimodule.c - _testcapimodule _HashInheritanceTester_Type -
463 Modules/_testcapimodule.c - awaitType _testcapimodule -
464 Modules/_testcapimodule.c - awaitType_as_async awaitType -
465 Modules/_testcapimodule.c - awaitType_as_async -
466 Modules/_testcapimodule.c - capsule_context -
467 Modules/_testcapimodule.c - capsule_destructor_call_count -
468 Modules/_testcapimodule.c - capsule_error -
577 Modules/_xxtestfuzz/fuzzer.c - struct_error bytesio_type -
578 Modules/_xxtestfuzz/fuzzer.c - struct_unpack_method compiled_patterns -
579 Modules/_xxtestfuzz/fuzzer.c - xmlparser_type csv_error -
580 Modules/_xxtestfuzz/fuzzer.c - csv_module -
581 Modules/_xxtestfuzz/fuzzer.c - pycompile_scratch json_loads_method -
582 Modules/_xxtestfuzz/fuzzer.c - start_vals regex_patterns -
583 Modules/_xxtestfuzz/fuzzer.c - optimize_vals re_compile_method -
620 Modules/_io/_iomodule.h - _PyIO_str_getstate _PyIO_Module -
621 Modules/_io/_iomodule.h - _PyIO_str_isatty _PyIO_str_close -
622 Modules/_io/_iomodule.h - _PyIO_str_newlines _PyIO_str_closed -
623 Modules/_io/_iomodule.h - _PyIO_str_decode -
624 Modules/_io/_iomodule.h - _PyIO_str_encode -
625 Modules/_io/_iomodule.h - _PyIO_str_fileno -
626 Modules/_io/_iomodule.h - _PyIO_str_flush -
627 Modules/_io/_iomodule.h - _PyIO_str_getstate -
628 Modules/_io/_iomodule.h - _PyIO_str_isatty -
629 Modules/_io/_iomodule.h - _PyIO_str_newlines -
630 Modules/_io/_iomodule.h - _PyIO_str_nl -
631 Modules/_io/_iomodule.h - _PyIO_str_peek -
632 Modules/_io/_iomodule.h - _PyIO_str_read -
Generated Vendored
+1 -1
View File
@@ -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
View File
@@ -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