From 47c9bc089be9279d278397afe29036db09700b33 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 20 May 2019 11:59:17 +0200 Subject: [PATCH 01/14] bpo-36974: document PEP 590 --- Doc/c-api/object.rst | 52 +++++++++++++++++++++++++++++++++++++++ Doc/c-api/structures.rst | 8 ++++++ Doc/c-api/typeobj.rst | 31 ++++++++++++----------- Doc/includes/typestruct.h | 2 +- Doc/whatsnew/3.8.rst | 16 ++++++++++++ 5 files changed, 94 insertions(+), 15 deletions(-) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index ffc35241e7a4d7..42f29b52542098 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -335,6 +335,58 @@ Object Protocol *NULL* on failure. +.. c:function:: PyObject* _PyObject_Vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames) + + Call a callable Python object *callable*. + + *args* is a C array with the positional arguments. + + *nargsf* is the number of positional arguments plus optionally the flag + :const:`PY_VECTORCALL_ARGUMENTS_OFFSET` which means that the callee is + allowed to modify ``args[-1]``. + + *kwnames* can be either NULL (no keyword arguments) or a tuple of keyword + names. In the latter case, the values of the keyword arguments are stored + in *args* after the positional arguments + (note that the number of keyword arguments does not change *nargsf*). + + *kwnames* must only objects of type ``str`` (not a subclass), + and all keys must be unique. + + Return the result of the call on success, or *NULL* on failure. + + This uses the vectorcall protocol if the callable supports it; + otherwise, the arguments are converted to use + :c:member:`~PyTypeObject.tp_call`. + +.. note:: + + This function is provisional and expected to become public in Python 3.9, + with a different name. If so, the plan is to keep the old name + ``_PyObject_Vectorcall`` at least in Python 3.9 for backwards compatibility. + It is also possible however that the API will be changed and that + this functions will be dropped or replaced by a different function. + +.. c:function:: PyObject* _PyObject_FastCallDict(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwdict) + + Same as :c:func:`_PyObject_Vectorcall` except that the keyword arguments + are passed as a dictionary in *kwdict*. This may be *NULL* if there + are no keyword arguments. + + For callables supporting vectorcall, the arguments are internally + converted to the vectorcall convention. Therefore, this function + adds some overhead compared to :c:func:`_PyObject_Vectorcall`. + It should only be used if the caller already has a dictionary ready to use. + +.. note:: + + This function is provisional and expected to become public in Python 3.9, + with a different name. If so, the plan is to keep the old name + ``_PyObject_FastCallDict`` at least in Python 3.9 for backwards compatibility. + It is also possible however that the API will be changed and that + this functions will be dropped or replaced by a different function. + + .. c:function:: Py_hash_t PyObject_Hash(PyObject *o) .. index:: builtin: hash diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index 5e0cfd0264f931..71e398066ef8ea 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -335,3 +335,11 @@ definition with the same method name. In case the attribute should be deleted the second parameter is *NULL*. Should return ``0`` on success or ``-1`` with a set exception on failure. + + +Vectorcall: implementing fast callables +--------------------------------------- + + + +.. c:type:: PyObject *(*_vectorcallfunc)(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames) diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index e2f8f54be79ae8..24fec349004f13 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -49,7 +49,7 @@ Quick Reference +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ | :c:member:`~PyTypeObject.tp_dealloc` | :c:type:`destructor` | | X | X | | X | +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | (:c:member:`~PyTypeObject.tp_print`) | | + | :c:member:`~PyTypeObject.tp_vectorcall_offset`| Py_ssize_t | | | | | ? | +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ | (:c:member:`~PyTypeObject.tp_getattr`) | :c:type:`getattrfunc` | __getattribute__, | | | | G | | | | __getattr__ | | | | | @@ -364,12 +364,6 @@ slot typedefs +-----------------------------+-----------------------------+----------------------+ | :c:type:`reprfunc` | :c:type:`PyObject` * | :c:type:`PyObject` * | +-----------------------------+-----------------------------+----------------------+ -| :c:type:`printfunc` | .. line-block:: | int | -| | | | -| | :c:type:`PyObject` * | | -| | FILE * | | -| | int | | -+-----------------------------+-----------------------------+----------------------+ | :c:type:`getattrfunc` | .. line-block:: | :c:type:`PyObject` * | | | | | | | :c:type:`PyObject` * | | @@ -675,9 +669,22 @@ and :c:type:`PyType_Type` effectively act as defaults.) This field is inherited by subtypes. -.. c:member:: printfunc PyTypeObject.tp_print +.. c:member:: Py_ssize_t PyTypeObject.tp_vectorcall_offset + + This field is only used if the flag :const:`Py_TPFLAGS_HAVE_VECTORCALL` + is set. If so, this must be a positive integer containing the offset in the + instance struct of the ``vectorcallfunc`` pointer used for the vectorcall + protocol. - Reserved slot, formerly used for print formatting in Python 2.x. + This pointer may be zero, in which case the instance behaves as if + :const:`Py_TPFLAGS_HAVE_VECTORCALL` was not set. + + **Inheritance:** + + This field is inherited for extension types + together with the flag :const:`Py_TPFLAGS_HAVE_VECTORCALL`, + but only if :c:member:`~PyTypeObject.tp_call` is also inherited. + Heap types never inherit this. .. c:member:: getattrfunc PyTypeObject.tp_getattr @@ -2302,10 +2309,6 @@ Slot Type typedefs See :c:member:`~PyTypeObject.tp_repr`. -.. c:type:: int (*printfunc)(PyObject *, FILE *, int) - - This is hidden if :const:`PY_LIMITED_API` is set. - .. c:type:: PyObject *(*getattrfunc)(PyObject *self, char *attr) Return the value of the named attribute for the object. @@ -2409,7 +2412,7 @@ with a more verbose initializer:: sizeof(MyObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)myobj_dealloc, /* tp_dealloc */ - 0, /* tp_print */ + 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ diff --git a/Doc/includes/typestruct.h b/Doc/includes/typestruct.h index 9f47899a198e01..9ada03cfc4a4cb 100644 --- a/Doc/includes/typestruct.h +++ b/Doc/includes/typestruct.h @@ -6,7 +6,7 @@ typedef struct _typeobject { /* Methods to implement standard operations */ destructor tp_dealloc; - printfunc tp_print; + Py_ssize_t tp_vectorcall_offset; getattrfunc tp_getattr; setattrfunc tp_setattr; PyAsyncMethods *tp_as_async; /* formerly known as tp_compare (Python 2) diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 98f0c3474f26ed..c383185b87a691 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -238,6 +238,22 @@ See :pep:`587` for a full description. (Contributed by Victor Stinner in :issue:`36763`.) +Vectorcall: a fast calling protocol for CPython +----------------------------------------------- + +The "vectorcall" protocol is added to the Python/C API. +It is meant to formalize existing optimizations which were already done +for various classes. +Any extension type implementing callables can use this protocol. + +This is currently provisional, +the aim is to make it fully public in Python 3.9. + +See :pep:`590` for a full description. + +(Contributed by Jeroen Demeyer and Mark Shannon in :issue:`36974`.) + + Other Language Changes ====================== From 3e1c2d707cdbf2c9a02ed8c8a9af37dd177318dd Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 31 May 2019 11:09:46 +0200 Subject: [PATCH 02/14] Fix table in c-api/typeobj --- Doc/c-api/typeobj.rst | 218 +++++++++++++++++++++--------------------- 1 file changed, 109 insertions(+), 109 deletions(-) diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 24fec349004f13..c9daa9cccf8ae0 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -36,115 +36,115 @@ Quick Reference .. table:: :widths: 18,18,18,1,1,1,1 - +---------------------------------------------+-----------------------------------+-------------------+---------------+ - | PyTypeObject Slot [#slots]_ | :ref:`Type ` | special | Info [#cols]_ | - | | | methods/attrs +---+---+---+---+ - | | | | O | T | D | I | - +=============================================+===================================+===================+===+===+===+===+ - | :c:member:`~PyTypeObject.tp_name` | const char * | __name__ | X | X | | | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_basicsize` | Py_ssize_t | | X | X | | X | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_itemsize` | Py_ssize_t | | | X | | X | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_dealloc` | :c:type:`destructor` | | X | X | | X | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_vectorcall_offset`| Py_ssize_t | | | | | ? | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | (:c:member:`~PyTypeObject.tp_getattr`) | :c:type:`getattrfunc` | __getattribute__, | | | | G | - | | | __getattr__ | | | | | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | (:c:member:`~PyTypeObject.tp_setattr`) | :c:type:`setattrfunc` | __setattr__, | | | | G | - | | | __delattr__ | | | | | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_as_async` | :c:type:`PyAsyncMethods` * | :ref:`sub-slots` | | | | % | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_repr` | :c:type:`reprfunc` | __repr__ | X | X | | X | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_as_number` | :c:type:`PyNumberMethods` * | :ref:`sub-slots` | | | | % | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_as_sequence` | :c:type:`PySequenceMethods` * | :ref:`sub-slots` | | | | % | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_as_mapping` | :c:type:`PyMappingMethods` * | :ref:`sub-slots` | | | | % | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_hash` | :c:type:`hashfunc` | __hash__ | X | | | G | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_call` | :c:type:`ternaryfunc` | __call__ | | X | | X | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_str` | :c:type:`reprfunc` | __str__ | X | | | X | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_getattro` | :c:type:`getattrofunc` | __getattribute__, | X | X | | G | - | | | __getattr__ | | | | | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_setattro` | :c:type:`setattrofunc` | __setattr__, | X | X | | G | - | | | __delattr__ | | | | | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_as_buffer` | :c:type:`PyBufferProcs` * | | | | | % | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_flags` | unsigned long | | X | X | | ? | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_doc` | const char * | __doc__ | X | X | | | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_traverse` | :c:type:`traverseproc` | | | X | | G | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_clear` | :c:type:`inquiry` | | | X | | G | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_richcompare` | :c:type:`richcmpfunc` | __lt__, | X | | | G | - | | | __le__, | | | | | - | | | __eq__, | | | | | - | | | __ne__, | | | | | - | | | __gt__, | | | | | - | | | __ge__ | | | | | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_weaklistoffset` | Py_ssize_t | | | X | | ? | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_iter` | :c:type:`getiterfunc` | __iter__ | | | | X | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_iternext` | :c:type:`iternextfunc` | __next__ | | | | X | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_methods` | :c:type:`PyMethodDef` [] | | X | X | | | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_members` | :c:type:`PyMemberDef` [] | | | X | | | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_getset` | :c:type:`PyGetSetDef` [] | | X | X | | | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_base` | :c:type:`PyTypeObject` * | __base__ | | | X | | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_dict` | :c:type:`PyObject` * | __dict__ | | | ? | | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_descr_get` | :c:type:`descrgetfunc` | __get__ | | | | X | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_descr_set` | :c:type:`descrsetfunc` | __set__, | | | | X | - | | | __delete__ | | | | | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_dictoffset` | Py_ssize_t | | | X | | ? | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_init` | :c:type:`initproc` | __init__ | X | X | | X | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_alloc` | :c:type:`allocfunc` | | X | | ? | ? | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_new` | :c:type:`newfunc` | __new__ | X | X | ? | ? | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_free` | :c:type:`freefunc` | | X | X | ? | ? | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_is_gc` | :c:type:`inquiry` | | | X | | X | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | <:c:member:`~PyTypeObject.tp_bases`> | :c:type:`PyObject` * | __bases__ | | | ~ | | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | <:c:member:`~PyTypeObject.tp_mro`> | :c:type:`PyObject` * | __mro__ | | | ~ | | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | [:c:member:`~PyTypeObject.tp_cache`] | :c:type:`PyObject` * | | | | | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | [:c:member:`~PyTypeObject.tp_subclasses`] | :c:type:`PyObject` * | __subclasses__ | | | | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | [:c:member:`~PyTypeObject.tp_weaklist`] | :c:type:`PyObject` * | | | | | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | (:c:member:`~PyTypeObject.tp_del`) | :c:type:`destructor` | | | | | | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | [:c:member:`~PyTypeObject.tp_version_tag`] | unsigned int | | | | | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ - | :c:member:`~PyTypeObject.tp_finalize` | :c:type:`destructor` | __del__ | | | | X | - +---------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + +------------------------------------------------+-----------------------------------+-------------------+---------------+ + | PyTypeObject Slot [#slots]_ | :ref:`Type ` | special | Info [#cols]_ | + | | | methods/attrs +---+---+---+---+ + | | | | O | T | D | I | + +================================================+===================================+===================+===+===+===+===+ + | :c:member:`~PyTypeObject.tp_name` | const char * | __name__ | X | X | | | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_basicsize` | Py_ssize_t | | X | X | | X | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_itemsize` | Py_ssize_t | | | X | | X | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_dealloc` | :c:type:`destructor` | | X | X | | X | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_vectorcall_offset` | Py_ssize_t | | | | | ? | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | (:c:member:`~PyTypeObject.tp_getattr`) | :c:type:`getattrfunc` | __getattribute__, | | | | G | + | | | __getattr__ | | | | | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | (:c:member:`~PyTypeObject.tp_setattr`) | :c:type:`setattrfunc` | __setattr__, | | | | G | + | | | __delattr__ | | | | | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_as_async` | :c:type:`PyAsyncMethods` * | :ref:`sub-slots` | | | | % | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_repr` | :c:type:`reprfunc` | __repr__ | X | X | | X | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_as_number` | :c:type:`PyNumberMethods` * | :ref:`sub-slots` | | | | % | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_as_sequence` | :c:type:`PySequenceMethods` * | :ref:`sub-slots` | | | | % | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_as_mapping` | :c:type:`PyMappingMethods` * | :ref:`sub-slots` | | | | % | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_hash` | :c:type:`hashfunc` | __hash__ | X | | | G | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_call` | :c:type:`ternaryfunc` | __call__ | | X | | X | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_str` | :c:type:`reprfunc` | __str__ | X | | | X | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_getattro` | :c:type:`getattrofunc` | __getattribute__, | X | X | | G | + | | | __getattr__ | | | | | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_setattro` | :c:type:`setattrofunc` | __setattr__, | X | X | | G | + | | | __delattr__ | | | | | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_as_buffer` | :c:type:`PyBufferProcs` * | | | | | % | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_flags` | unsigned long | | X | X | | ? | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_doc` | const char * | __doc__ | X | X | | | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_traverse` | :c:type:`traverseproc` | | | X | | G | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_clear` | :c:type:`inquiry` | | | X | | G | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_richcompare` | :c:type:`richcmpfunc` | __lt__, | X | | | G | + | | | __le__, | | | | | + | | | __eq__, | | | | | + | | | __ne__, | | | | | + | | | __gt__, | | | | | + | | | __ge__ | | | | | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_weaklistoffset` | Py_ssize_t | | | X | | ? | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_iter` | :c:type:`getiterfunc` | __iter__ | | | | X | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_iternext` | :c:type:`iternextfunc` | __next__ | | | | X | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_methods` | :c:type:`PyMethodDef` [] | | X | X | | | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_members` | :c:type:`PyMemberDef` [] | | | X | | | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_getset` | :c:type:`PyGetSetDef` [] | | X | X | | | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_base` | :c:type:`PyTypeObject` * | __base__ | | | X | | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_dict` | :c:type:`PyObject` * | __dict__ | | | ? | | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_descr_get` | :c:type:`descrgetfunc` | __get__ | | | | X | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_descr_set` | :c:type:`descrsetfunc` | __set__, | | | | X | + | | | __delete__ | | | | | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_dictoffset` | Py_ssize_t | | | X | | ? | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_init` | :c:type:`initproc` | __init__ | X | X | | X | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_alloc` | :c:type:`allocfunc` | | X | | ? | ? | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_new` | :c:type:`newfunc` | __new__ | X | X | ? | ? | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_free` | :c:type:`freefunc` | | X | X | ? | ? | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_is_gc` | :c:type:`inquiry` | | | X | | X | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | <:c:member:`~PyTypeObject.tp_bases`> | :c:type:`PyObject` * | __bases__ | | | ~ | | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | <:c:member:`~PyTypeObject.tp_mro`> | :c:type:`PyObject` * | __mro__ | | | ~ | | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | [:c:member:`~PyTypeObject.tp_cache`] | :c:type:`PyObject` * | | | | | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | [:c:member:`~PyTypeObject.tp_subclasses`] | :c:type:`PyObject` * | __subclasses__ | | | | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | [:c:member:`~PyTypeObject.tp_weaklist`] | :c:type:`PyObject` * | | | | | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | (:c:member:`~PyTypeObject.tp_del`) | :c:type:`destructor` | | | | | | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | [:c:member:`~PyTypeObject.tp_version_tag`] | unsigned int | | | | | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ + | :c:member:`~PyTypeObject.tp_finalize` | :c:type:`destructor` | __del__ | | | | X | + +------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+ If :const:`COUNT_ALLOCS` is defined then the following (internal-only) fields exist as well: From 37c09f8d3bf0bdd621e947507379d7d820ea9318 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 31 May 2019 12:18:51 +0200 Subject: [PATCH 03/14] Adjust "provisional" warnings Don't document our plans for the functions, but rather the expectations for their users. --- Doc/c-api/object.rst | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 42f29b52542098..74a5ffc4d58812 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -362,10 +362,8 @@ Object Protocol .. note:: This function is provisional and expected to become public in Python 3.9, - with a different name. If so, the plan is to keep the old name - ``_PyObject_Vectorcall`` at least in Python 3.9 for backwards compatibility. - It is also possible however that the API will be changed and that - this functions will be dropped or replaced by a different function. + with a different name and, possibly, changed semantics. + If you use the function, plan for updating your code for Python 3.9. .. c:function:: PyObject* _PyObject_FastCallDict(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwdict) @@ -381,10 +379,8 @@ Object Protocol .. note:: This function is provisional and expected to become public in Python 3.9, - with a different name. If so, the plan is to keep the old name - ``_PyObject_FastCallDict`` at least in Python 3.9 for backwards compatibility. - It is also possible however that the API will be changed and that - this functions will be dropped or replaced by a different function. + with a different name and, possibly, changed semantics. + If you use the function, plan for updating your code for Python 3.9. .. c:function:: Py_hash_t PyObject_Hash(PyObject *o) From 60e043181ba50483ca51502e064b53e1615f14b7 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 31 May 2019 12:32:52 +0200 Subject: [PATCH 04/14] Rewording: _PyObject_Vectorcall, _PyObject_FastCallDict, PyVectorcall_NARGS --- Doc/c-api/object.rst | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 74a5ffc4d58812..c3a4718b93d12d 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -337,20 +337,22 @@ Object Protocol .. c:function:: PyObject* _PyObject_Vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames) - Call a callable Python object *callable*. + Call a callable Python object *callable*, using + :c:data:`vectorcall ` if possible. *args* is a C array with the positional arguments. *nargsf* is the number of positional arguments plus optionally the flag - :const:`PY_VECTORCALL_ARGUMENTS_OFFSET` which means that the callee is - allowed to modify ``args[-1]``. + :const:`PY_VECTORCALL_ARGUMENTS_OFFSET` (see below). + To get actual number of arguments, use + :c:func:`PyVectorcall_NARGS(nargsf) `. *kwnames* can be either NULL (no keyword arguments) or a tuple of keyword names. In the latter case, the values of the keyword arguments are stored - in *args* after the positional arguments - (note that the number of keyword arguments does not change *nargsf*). + in *args* after the positional arguments. + The number of keyword arguments does not influence *nargsf*. - *kwnames* must only objects of type ``str`` (not a subclass), + *kwnames* must contain only objects of type ``str`` (not a subclass), and all keys must be unique. Return the result of the call on success, or *NULL* on failure. @@ -365,15 +367,34 @@ Object Protocol with a different name and, possibly, changed semantics. If you use the function, plan for updating your code for Python 3.9. +.. c:data:``PY_VECTORCALL_ARGUMENTS_OFFSET`` + + If set in a vectorcall *nargsf* argument, the callee is allowed to + temporarily change ``args[-1]``. In other words, *args* points to + argument 1 (not 0) in the allocated vector. + The callee must restore the value of ``args[-1]`` before returning. + + Whenever they can do so cheaply (without additional allocation), callers + are encouraged to use :const:`PY_VECTORCALL_ARGUMENTS_OFFSET`. + Doing so will allow callables such as bound methods to make their onward + calls (which include a prepended *self* argument) cheaply. + +.. c:function:: Py_ssize_t PyVectorcall_NARGS(size_t nargsf) + + Given a vectorcall *nargsf* argument, return the actual number of + arguments. + Currently equivalent to ``nargsf & ~PY_VECTORCALL_ARGUMENTS_OFFSET``. + .. c:function:: PyObject* _PyObject_FastCallDict(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwdict) Same as :c:func:`_PyObject_Vectorcall` except that the keyword arguments are passed as a dictionary in *kwdict*. This may be *NULL* if there are no keyword arguments. - For callables supporting vectorcall, the arguments are internally - converted to the vectorcall convention. Therefore, this function - adds some overhead compared to :c:func:`_PyObject_Vectorcall`. + For callables supporting :c:data:`vectorcall `, + the arguments are internally converted to the vectorcall convention. + Therefore, this function adds some overhead compared to + :c:func:`_PyObject_Vectorcall`. It should only be used if the caller already has a dictionary ready to use. .. note:: From 01805856bf1280d23770556c9f9694e5963db550 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 31 May 2019 12:44:57 +0200 Subject: [PATCH 05/14] Move docs of vectorcallfunc to c-api/typeobj.rst --- Doc/c-api/structures.rst | 8 -------- Doc/c-api/typeobj.rst | 6 +++++- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Doc/c-api/structures.rst b/Doc/c-api/structures.rst index 71e398066ef8ea..5e0cfd0264f931 100644 --- a/Doc/c-api/structures.rst +++ b/Doc/c-api/structures.rst @@ -335,11 +335,3 @@ definition with the same method name. In case the attribute should be deleted the second parameter is *NULL*. Should return ``0`` on success or ``-1`` with a set exception on failure. - - -Vectorcall: implementing fast callables ---------------------------------------- - - - -.. c:type:: PyObject *(*_vectorcallfunc)(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames) diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index c9daa9cccf8ae0..6ab6b11bad9475 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -673,7 +673,7 @@ and :c:type:`PyType_Type` effectively act as defaults.) This field is only used if the flag :const:`Py_TPFLAGS_HAVE_VECTORCALL` is set. If so, this must be a positive integer containing the offset in the - instance struct of the ``vectorcallfunc`` pointer used for the vectorcall + instance struct of the :c:type:`vectorcallfunc` pointer used for the vectorcall protocol. This pointer may be zero, in which case the instance behaves as if @@ -2293,6 +2293,10 @@ Slot Type typedefs .. c:type:: void (*destructor)(PyObject *) +.. c:type:: PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames) + + See :c:member:`~PyTypeObject.tp_vectorcall_offset`. + .. c:type:: void (*freefunc)(void *) See :c:member:`~PyTypeObject.tp_free`. From e46f1d7df9e25ae2176f564da118cf2383e8f233 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 31 May 2019 12:45:58 +0200 Subject: [PATCH 06/14] Add leading underscore for _Py_TPFLAGS_HAVE_VECTORCALL --- Doc/c-api/typeobj.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 6ab6b11bad9475..867f4b016af48b 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -671,18 +671,18 @@ and :c:type:`PyType_Type` effectively act as defaults.) .. c:member:: Py_ssize_t PyTypeObject.tp_vectorcall_offset - This field is only used if the flag :const:`Py_TPFLAGS_HAVE_VECTORCALL` + This field is only used if the flag :const:`_Py_TPFLAGS_HAVE_VECTORCALL` is set. If so, this must be a positive integer containing the offset in the instance struct of the :c:type:`vectorcallfunc` pointer used for the vectorcall protocol. This pointer may be zero, in which case the instance behaves as if - :const:`Py_TPFLAGS_HAVE_VECTORCALL` was not set. + :const:`_Py_TPFLAGS_HAVE_VECTORCALL` was not set. **Inheritance:** This field is inherited for extension types - together with the flag :const:`Py_TPFLAGS_HAVE_VECTORCALL`, + together with the flag :const:`_Py_TPFLAGS_HAVE_VECTORCALL`, but only if :c:member:`~PyTypeObject.tp_call` is also inherited. Heap types never inherit this. From ae26300b1801eff30d9fe8b2bc73af02942b5cee Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 31 May 2019 12:47:58 +0200 Subject: [PATCH 07/14] Whatsnew: Clarify wording --- Doc/whatsnew/3.8.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index c383185b87a691..9bf26cab74233d 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -244,7 +244,7 @@ Vectorcall: a fast calling protocol for CPython The "vectorcall" protocol is added to the Python/C API. It is meant to formalize existing optimizations which were already done for various classes. -Any extension type implementing callables can use this protocol. +Any extension type implementing a callable can use this protocol. This is currently provisional, the aim is to make it fully public in Python 3.9. From 24d79fe9d094e4f98351194e66e87cc959807d1a Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 31 May 2019 13:30:50 +0200 Subject: [PATCH 08/14] Indent "provisional" notices and add versionadded notes --- Doc/c-api/object.rst | 22 ++++++++++++++-------- Doc/c-api/typeobj.rst | 13 +++++++++++++ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index c3a4718b93d12d..def6dd59041dfb 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -361,11 +361,13 @@ Object Protocol otherwise, the arguments are converted to use :c:member:`~PyTypeObject.tp_call`. -.. note:: + .. note:: + + This function is provisional and expected to become public in Python 3.9, + with a different name and, possibly, changed semantics. + If you use the function, plan for updating your code for Python 3.9. - This function is provisional and expected to become public in Python 3.9, - with a different name and, possibly, changed semantics. - If you use the function, plan for updating your code for Python 3.9. + .. versionadded:: 3.8 .. c:data:``PY_VECTORCALL_ARGUMENTS_OFFSET`` @@ -379,6 +381,8 @@ Object Protocol Doing so will allow callables such as bound methods to make their onward calls (which include a prepended *self* argument) cheaply. + .. versionadded:: 3.8 + .. c:function:: Py_ssize_t PyVectorcall_NARGS(size_t nargsf) Given a vectorcall *nargsf* argument, return the actual number of @@ -397,11 +401,13 @@ Object Protocol :c:func:`_PyObject_Vectorcall`. It should only be used if the caller already has a dictionary ready to use. -.. note:: + .. note:: + + This function is provisional and expected to become public in Python 3.9, + with a different name and, possibly, changed semantics. + If you use the function, plan for updating your code for Python 3.9. - This function is provisional and expected to become public in Python 3.9, - with a different name and, possibly, changed semantics. - If you use the function, plan for updating your code for Python 3.9. + .. versionadded:: 3.8 .. c:function:: Py_hash_t PyObject_Hash(PyObject *o) diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 867f4b016af48b..0972ee18344d44 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -686,6 +686,17 @@ and :c:type:`PyType_Type` effectively act as defaults.) but only if :c:member:`~PyTypeObject.tp_call` is also inherited. Heap types never inherit this. + .. note:: + + The semantics of slot are provisional and expected to be finalized + in Python 3.9. + If you use vectorcall, plan for updating your code for Python 3.9. + + .. versionchanged:: 3.8 + + This slot was used for print formatting in Python 2.x. + Up to Python 3.7, it was named ``tp_print``. + .. c:member:: getattrfunc PyTypeObject.tp_getattr @@ -2297,6 +2308,8 @@ Slot Type typedefs See :c:member:`~PyTypeObject.tp_vectorcall_offset`. + .. versionadded: 3.8 + .. c:type:: void (*freefunc)(void *) See :c:member:`~PyTypeObject.tp_free`. From 3e5f5b0261d600956216bfbd1f336c9da9c86043 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 31 May 2019 14:04:58 +0200 Subject: [PATCH 09/14] Reword tp_vectorcall_offset; document PyVectorcall_Call --- Doc/c-api/typeobj.rst | 50 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 0972ee18344d44..32631d74cb3dc8 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -671,20 +671,28 @@ and :c:type:`PyType_Type` effectively act as defaults.) .. c:member:: Py_ssize_t PyTypeObject.tp_vectorcall_offset + An optional offset to a per-instance function that implements calling + the object using the *vectorcall* protocol, a more efficient alternative + of the simpler :c:member:`~PyTypeObject.tp_call`. + This field is only used if the flag :const:`_Py_TPFLAGS_HAVE_VECTORCALL` is set. If so, this must be a positive integer containing the offset in the - instance struct of the :c:type:`vectorcallfunc` pointer used for the vectorcall - protocol. + instance of a :c:type:`vectorcallfunc` pointer. + Arguments to ``vectorcallfunc`` are the same as for :c:func:`_PyObject_Vectorcall`. - This pointer may be zero, in which case the instance behaves as if - :const:`_Py_TPFLAGS_HAVE_VECTORCALL` was not set. + The *vectorcallfunc* pointer may be zero, in which case the instance behaves + as if :const:`_Py_TPFLAGS_HAVE_VECTORCALL` was not set: calling the instance + falls back to :c:member:`~PyTypeObject.tp_call`. - **Inheritance:** + Any class that sets ``_Py_TPFLAGS_HAVE_VECTORCALL`` must also set + :c:member:`~PyTypeObject.tp_call` and make sure its behaviour is consistent + with the *vectorcallfunc* function. This can be done by setting *tp_call* to + ``PyVectorcall_Call``: - This field is inherited for extension types - together with the flag :const:`_Py_TPFLAGS_HAVE_VECTORCALL`, - but only if :c:member:`~PyTypeObject.tp_call` is also inherited. - Heap types never inherit this. + .. c:function:: PyObject *PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict) + + Call *callable*'s *vectorcallfunc* with positional and keyword + arguments given in a tuple and dict, respectively. .. note:: @@ -695,7 +703,14 @@ and :c:type:`PyType_Type` effectively act as defaults.) .. versionchanged:: 3.8 This slot was used for print formatting in Python 2.x. - Up to Python 3.7, it was named ``tp_print``. + In Python 3.0 to 3.7, it was reserved and named ``tp_print``. + + **Inheritance:** + + This slot is inherited for static extension types + together with the flag :const:`_Py_TPFLAGS_HAVE_VECTORCALL`, + but only if :c:member:`~PyTypeObject.tp_call` is also inherited. + `Heap types`_ never inherit this. .. c:member:: getattrfunc PyTypeObject.tp_getattr @@ -1122,6 +1137,19 @@ and :c:type:`PyType_Type` effectively act as defaults.) :c:member:`~PyTypeObject.tp_finalize` slot is always present in the type structure. + .. data:: _Py_TPFLAGS_HAVE_VECTORCALL + + This bit is set when the class implements the vectorcall protocol. + See :c:member:`~PyTypeObject.tp_vectorcall_offset` for details. + + .. note:: + + This flag is provisional and expected to become public in Python 3.9, + with a different name and, possibly, changed semantics. + If you use vectorcall, plan for updating your code for Python 3.9. + + .. versionadded:: 3.8 + .. c:member:: const char* PyTypeObject.tp_doc @@ -2308,6 +2336,8 @@ Slot Type typedefs See :c:member:`~PyTypeObject.tp_vectorcall_offset`. + Arguments to ``vectorcallfunc`` are the same as for :c:func:`_PyObject_Vectorcall`. + .. versionadded: 3.8 .. c:type:: void (*freefunc)(void *) From 438b1d691a1bc7dea8c8d32c2e8bd4d45900941d Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 31 May 2019 14:12:38 +0200 Subject: [PATCH 10/14] Use standard worning for joint inheritance and "slot" signature --- Doc/c-api/typeobj.rst | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 32631d74cb3dc8..ca261977a544c2 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -678,7 +678,9 @@ and :c:type:`PyType_Type` effectively act as defaults.) This field is only used if the flag :const:`_Py_TPFLAGS_HAVE_VECTORCALL` is set. If so, this must be a positive integer containing the offset in the instance of a :c:type:`vectorcallfunc` pointer. - Arguments to ``vectorcallfunc`` are the same as for :c:func:`_PyObject_Vectorcall`. + The signature is the same as for :c:func:`_PyObject_Vectorcall`:: + + PyObject *vectorcallfunc(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames) The *vectorcallfunc* pointer may be zero, in which case the instance behaves as if :const:`_Py_TPFLAGS_HAVE_VECTORCALL` was not set: calling the instance @@ -707,10 +709,14 @@ and :c:type:`PyType_Type` effectively act as defaults.) **Inheritance:** - This slot is inherited for static extension types - together with the flag :const:`_Py_TPFLAGS_HAVE_VECTORCALL`, - but only if :c:member:`~PyTypeObject.tp_call` is also inherited. - `Heap types`_ never inherit this. + This field is inherited by *static* subtypes together with + :c:member:`~PyTypeObject.tp_call`: a subtype inherits both + :c:member:`~PyTypeObject.tp_vectorcall_offset` and + :c:member:`~PyTypeObject.tp_call` from its base type when the subtype’s + :c:member:`~PyTypeObject.tp_call` and :c:member:`~PyTypeObject.tp_vectorcall_offset` + are both NULL. + + `Heap types`_ never inherit :c:member:`~PyTypeObject.tp_vectorcall_offset`. .. c:member:: getattrfunc PyTypeObject.tp_getattr From d12fe1d9b5f3904311df637bf264bce4b7f54478 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 3 Jun 2019 00:12:18 +0200 Subject: [PATCH 11/14] Clarify how vectorcall works with subclassing This is not described in detail in PEP 590; see https://github.com/python/cpython/pull/13699 for the practical issue. --- Doc/c-api/typeobj.rst | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index ca261977a544c2..5a3d327a18357c 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -688,18 +688,31 @@ and :c:type:`PyType_Type` effectively act as defaults.) Any class that sets ``_Py_TPFLAGS_HAVE_VECTORCALL`` must also set :c:member:`~PyTypeObject.tp_call` and make sure its behaviour is consistent - with the *vectorcallfunc* function. This can be done by setting *tp_call* to - ``PyVectorcall_Call``: + with the *vectorcallfunc* function. + This can be done by setting *tp_call* to ``PyVectorcall_Call``: .. c:function:: PyObject *PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict) Call *callable*'s *vectorcallfunc* with positional and keyword arguments given in a tuple and dict, respectively. + This function is intended to be used in the ``tp_call`` slot. + It does not fall back to ``tp_call`` and it currently does not check the + ``_Py_TPFLAGS_HAVE_VECTORCALL`` flag. + To call an object, use one of the :c:func:`PyObject_Call ` + functions instead. + .. note:: - The semantics of slot are provisional and expected to be finalized - in Python 3.9. + It is not recommended for :ref:`heap types ` to implement + the vectorcall protocol. + When a user sets ``__call__`` in Python code, only ``tp_call`` is updated, + possibly making it inconsistent with the vectorcall function. + + .. note:: + + The semantics of the ``tp_vectorcall_offset`` slot are provisional and + expected to be finalized in Python 3.9. If you use vectorcall, plan for updating your code for Python 3.9. .. versionchanged:: 3.8 @@ -709,14 +722,13 @@ and :c:type:`PyType_Type` effectively act as defaults.) **Inheritance:** - This field is inherited by *static* subtypes together with - :c:member:`~PyTypeObject.tp_call`: a subtype inherits both - :c:member:`~PyTypeObject.tp_vectorcall_offset` and - :c:member:`~PyTypeObject.tp_call` from its base type when the subtype’s - :c:member:`~PyTypeObject.tp_call` and :c:member:`~PyTypeObject.tp_vectorcall_offset` - are both NULL. + This field is inherited by subtypes together with + :c:member:`~PyTypeObject.tp_call`: a subtype inherits + :c:member:`~PyTypeObject.tp_vectorcall_offset` from its base type when + the subtype’s :c:member:`~PyTypeObject.tp_call` is NULL. - `Heap types`_ never inherit :c:member:`~PyTypeObject.tp_vectorcall_offset`. + Note that `heap types`_ (including subclasses defined in Python) do not + inherit the :const:`_Py_TPFLAGS_HAVE_VECTORCALL` flag. .. c:member:: getattrfunc PyTypeObject.tp_getattr @@ -1148,6 +1160,15 @@ and :c:type:`PyType_Type` effectively act as defaults.) This bit is set when the class implements the vectorcall protocol. See :c:member:`~PyTypeObject.tp_vectorcall_offset` for details. + **Inheritance:** + + This bit is set on *static* subtypes if ``tp_flags`` is not overridden: + a subtype inherits ``_Py_TPFLAGS_HAVE_VECTORCALL`` from its base type + when the subtype’s :c:member:`~PyTypeObject.tp_call` is NULL + and the subtype's ``Py_TPFLAGS_HEAPTYPE`` is not set. + + `Heap types`_ do not inherit ``_Py_TPFLAGS_HAVE_VECTORCALL``. + .. note:: This flag is provisional and expected to become public in Python 3.9, From e78552001a7455a3548beaef0d8751965cdd0bba Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 3 Jun 2019 00:23:22 +0200 Subject: [PATCH 12/14] Fix ReST syntax --- Doc/c-api/typeobj.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 5a3d327a18357c..83fcc5abed7079 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -2365,7 +2365,7 @@ Slot Type typedefs Arguments to ``vectorcallfunc`` are the same as for :c:func:`_PyObject_Vectorcall`. - .. versionadded: 3.8 + .. versionadded:: 3.8 .. c:type:: void (*freefunc)(void *) From 77e690a6f7269a769343bb15fa55802ea3552fd5 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 3 Jun 2019 00:42:40 +0200 Subject: [PATCH 13/14] Another ReST fix --- Doc/c-api/object.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index def6dd59041dfb..c40244a033c41e 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -369,7 +369,7 @@ Object Protocol .. versionadded:: 3.8 -.. c:data:``PY_VECTORCALL_ARGUMENTS_OFFSET`` +.. c:var:: PY_VECTORCALL_ARGUMENTS_OFFSET If set in a vectorcall *nargsf* argument, the callee is allowed to temporarily change ``args[-1]``. In other words, *args* points to From 986e5d23081e7731b8668e4ad1a61f1d894bfac8 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 3 Jun 2019 01:28:38 +0200 Subject: [PATCH 14/14] One more versionadded directive --- Doc/c-api/object.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index c40244a033c41e..ce0d05942f4e79 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -389,6 +389,8 @@ Object Protocol arguments. Currently equivalent to ``nargsf & ~PY_VECTORCALL_ARGUMENTS_OFFSET``. + .. versionadded:: 3.8 + .. c:function:: PyObject* _PyObject_FastCallDict(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwdict) Same as :c:func:`_PyObject_Vectorcall` except that the keyword arguments