See :ref:`PyMemberDef documentation <pymemberdef-offsets>`
for details.
- The following fields cannot be set at all when creating a heap type:
-
- * :c:member:`~PyTypeObject.tp_vectorcall`
- (use :c:member:`~PyTypeObject.tp_new` and/or
- :c:member:`~PyTypeObject.tp_init`)
+ The following internal fields cannot be set at all when creating a heap
+ type:
* Internal fields:
:c:member:`~PyTypeObject.tp_dict`,
: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 set
+ using ``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
.. c:member:: vectorcallfunc PyTypeObject.tp_vectorcall
- Vectorcall function to use for calls of this type object.
- In other words, it is used to implement
- :ref:`vectorcall <vectorcall>` for ``type.__call__``.
- If ``tp_vectorcall`` is ``NULL``, the default call implementation
- using :meth:`~object.__new__` and :meth:`~object.__init__` is used.
+ A :ref:`vectorcall function <vectorcall>` to use for calls of this type
+ object (rather than instances).
+ In other words, ``tp_vectorcall`` can be used to optimize ``type.__call__``,
+ which typically returns a new instance of *type*.
+
+ As with any vectorcall function, if ``tp_vectorcall`` is ``NULL``,
+ the *tp_call* protocol (``Py_TYPE(type)->tp_call``) is used instead.
+
+ .. note::
+
+ The :ref:`vectorcall protocol <vectorcall>` requires that the vectorcall
+ function has the same behavior as the corresponding ``tp_call``.
+ This means that ``type->tp_vectorcall`` must match the behavior of
+ ``Py_TYPE(type)->tp_call``.
+
+ Specifically, if *type* uses the default metaclass,
+ ``type->tp_vectorcall`` must behave the same as
+ :c:expr:`PyType_Type->tp_call`, which:
+
+ - calls ``type->tp_new``,
+
+ - if the result is a subclass of *type*, calls ``type->tp_init``
+ on the result of ``tp_new``, and
+
+ - returns the result of ``tp_new``.
+
+ Typically, ``tp_vectorcall`` is overridden to optimize this process
+ for specific :c:member:`~PyTypeObject.tp_new` and
+ :c:member:`~PyTypeObject.tp_init`.
+ When doing this for user-subclassable types, note that both can be
+ overridden (using :py:func:`~object.__new__` and
+ :py:func:`~object.__init__`, respectively).
+
+
**Inheritance:**
/* 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
with self.assertRaisesRegex(TypeError, msg):
sub = _testcapi.make_type_with_base(Base)
+ def test_heaptype_with_tp_vectorcall(self):
+ tp = _testcapi.HeapCTypeVectorcall
+ v0 = tp.__new__(tp)
+ v0.__init__()
+ v1 = tp()
+ self.assertEqual(v0.value, 2)
+ self.assertEqual(v1.value, 1)
+
def test_multiple_inheritance_ctypes_with_weakref_or_dict(self):
for weakref_cls in (_testcapi.HeapCTypeWithWeakref,
_testlimitedcapi.HeapCTypeWithRelativeWeakref):
--- /dev/null
+Added a slot ``Py_tp_vectorcall`` to set
+:c:member:`~PyTypeObject.tp_vectorcall` via the :c:func:`PyType_FromSpec`
+function family. Limited API extensions can use this feature to provide more
+efficient vector call-based implementation of ``__new__`` and ``__init__``.
added = '3.14'
[function.PyLong_AsUInt64]
added = '3.14'
+[const.Py_tp_vectorcall]
+ added = '3.14'
\ No newline at end of file
HeapCTypeSetattr_slots
};
+/*
+ * The code below is for a test that uses PyType_FromSpec API to create a heap
+ * type that simultaneously exposes
+ *
+ * - A regular __new__ / __init__ constructor pair
+ * - A vector call handler in the type object
+ *
+ * A general requirement of vector call implementations is that they should
+ * behave identically (except being potentially faster). The example below
+ * deviates from this rule by initializing the instance with a different value.
+ * This is only done here only so that we can see which path was taken and is
+ * strongly discouraged in other cases.
+ */
+
+typedef struct {
+ PyObject_HEAD
+ long value;
+} HeapCTypeVectorcallObject;
+
+static PyObject *heapctype_vectorcall_vectorcall(PyObject *self,
+ PyObject *const *args_in,
+ size_t nargsf,
+ PyObject *kwargs_in)
+{
+ if (kwargs_in || PyVectorcall_NARGS(nargsf)) {
+ return PyErr_Format(PyExc_IndexError, "HeapCTypeVectorcall() takes no arguments!");
+ }
+
+ HeapCTypeVectorcallObject *r =
+ PyObject_New(HeapCTypeVectorcallObject, (PyTypeObject *) self);
+
+ if (!r) {
+ return NULL;
+ }
+
+ r->value = 1;
+
+ return (PyObject *) r;
+}
+
+static PyObject *
+heapctype_vectorcall_new(PyTypeObject* type, PyObject* args, PyObject *kwargs)
+{
+ if (PyTuple_GET_SIZE(args) || kwargs) {
+ return PyErr_Format(PyExc_IndexError, "HeapCTypeVectorcall() takes no arguments!");
+ }
+
+ return (PyObject *) PyObject_New(HeapCTypeVectorcallObject, type);
+}
+
+static int
+heapctype_vectorcall_init(PyObject *self, PyObject *args, PyObject *kwargs) {
+ if (PyTuple_GET_SIZE(args) || kwargs) {
+ PyErr_Format(PyExc_IndexError, "HeapCTypeVectorcall() takes no arguments!");
+ return -1;
+ }
+
+ HeapCTypeVectorcallObject *o = (HeapCTypeVectorcallObject *) self;
+ o->value = 2;
+ return 0;
+}
+
+static struct PyMemberDef heapctype_vectorcall_members[] = {
+ {"value", Py_T_LONG, offsetof(HeapCTypeVectorcallObject, value), 0, NULL},
+ {NULL}
+};
+
+static PyType_Slot HeapCTypeVectorcall_slots[] = {
+ {Py_tp_new, heapctype_vectorcall_new},
+ {Py_tp_init, heapctype_vectorcall_init},
+ {Py_tp_vectorcall, heapctype_vectorcall_vectorcall},
+ {Py_tp_members, heapctype_vectorcall_members},
+ {0, 0},
+};
+
+static PyType_Spec HeapCTypeVectorcall_spec = {
+ "_testcapi.HeapCTypeVectorcall",
+ sizeof(HeapCTypeVectorcallObject),
+ 0,
+ Py_TPFLAGS_DEFAULT,
+ HeapCTypeVectorcall_slots
+};
+
PyDoc_STRVAR(HeapCCollection_doc,
"Tuple-like heap type that uses PyObject_GetItemData for items.");
PyObject *HeapCTypeSetattr = PyType_FromSpec(&HeapCTypeSetattr_spec);
ADD("HeapCTypeSetattr", HeapCTypeSetattr);
+ PyObject *HeapCTypeVectorcall = PyType_FromSpec(&HeapCTypeVectorcall_spec);
+ ADD("HeapCTypeVectorcall", HeapCTypeVectorcall);
+
PyObject *subclass_with_finalizer_bases = PyTuple_Pack(1, HeapCTypeSubclass);
if (subclass_with_finalizer_bases == NULL) {
return -1;
{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)},