bpo-39245: Make Vectorcall C API public (GH-17893)

* Add backcompat defines and move non-limited API declaration to cpython/

This partially reverts commit 2ff58a24e8
which added PyObject_CallNoArgs to the 3.9+ stable ABI. This should not
be done; there are enough other call APIs in the stable ABI to choose from.

* Adjust documentation

Mark all newly public functions as added in 3.9.
Add a note about the 3.8 provisional names.
Add notes on public API.

* Put PyObject_CallNoArgs back in the limited API

* Rename PyObject_FastCallDict to PyObject_VectorcallDict
This commit is contained in:
Petr Viktorin 2020-02-06 15:48:27 +01:00 committed by GitHub
parent d2f9667264
commit 3f563cea56
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 89 additions and 66 deletions

View file

@ -35,17 +35,11 @@ To call an object, use :c:func:`PyObject_Call` or other
The Vectorcall Protocol
-----------------------
.. versionadded:: 3.8
.. versionadded:: 3.9
The vectorcall protocol was introduced in :pep:`590` as an additional protocol
for making calls more efficient.
.. warning::
The vectorcall API is provisional and expected to become public in
Python 3.9, with a different names and, possibly, changed semantics.
If you use the it, plan for updating your code for Python 3.9.
As rule of thumb, CPython will prefer the vectorcall for internal calls
if the callable supports it. However, this is not a hard rule.
Additionally, some third-party extensions use *tp_call* directly
@ -69,7 +63,7 @@ the arguments to an args tuple and kwargs dict anyway, then there is no point
in implementing vectorcall.
Classes can implement the vectorcall protocol by enabling the
:const:`_Py_TPFLAGS_HAVE_VECTORCALL` flag and setting
:const:`Py_TPFLAGS_HAVE_VECTORCALL` flag and setting
:c:member:`~PyTypeObject.tp_vectorcall_offset` to the offset inside the
object structure where a *vectorcallfunc* appears.
This is a pointer to a function with the following signature:
@ -97,7 +91,7 @@ This is a pointer to a function with the following signature:
argument 1 (not 0) in the allocated vector.
The callee must restore the value of ``args[-1]`` before returning.
For :c:func:`_PyObject_VectorcallMethod`, this flag means instead that
For :c:func:`PyObject_VectorcallMethod`, this flag means instead that
``args[0]`` may be changed.
Whenever they can do so cheaply (without additional allocation), callers
@ -107,7 +101,20 @@ This is a pointer to a function with the following signature:
To call an object that implements vectorcall, use a :ref:`call API <capi-call>`
function as with any other callable.
:c:func:`_PyObject_Vectorcall` will usually be most efficient.
:c:func:`PyObject_Vectorcall` will usually be most efficient.
.. note::
In CPython 3.8, the vectorcall API and related functions were available
provisionally under names with a leading underscore:
``_PyObject_Vectorcall``, ``_Py_TPFLAGS_HAVE_VECTORCALL``,
``_PyObject_VectorcallMethod``, ``_PyVectorcall_Function``,
``_PyObject_CallOneArg``, ``_PyObject_CallMethodNoArgs``,
``_PyObject_CallMethodOneArg``.
Additionally, ``PyObject_VectorcallDict`` was available as
``_PyObject_FastCallDict``.
The old names are still defined as aliases of the new, non-underscored names.
Recursion Control
@ -137,9 +144,11 @@ Vectorcall Support API
However, the function ``PyVectorcall_NARGS`` should be used to allow
for future extensions.
This function is not part of the `limited API <stable>`_.
.. versionadded:: 3.8
.. c:function:: vectorcallfunc _PyVectorcall_Function(PyObject *op)
.. c:function:: vectorcallfunc PyVectorcall_Function(PyObject *op)
If *op* does not support the vectorcall protocol (either because the type
does not or because the specific instance does not), return *NULL*.
@ -147,7 +156,9 @@ Vectorcall Support API
This function never raises an exception.
This is mostly useful to check whether or not *op* supports vectorcall,
which can be done by checking ``_PyVectorcall_Function(op) != NULL``.
which can be done by checking ``PyVectorcall_Function(op) != NULL``.
This function is not part of the `limited API <stable>`_.
.. versionadded:: 3.8
@ -158,9 +169,11 @@ Vectorcall Support API
This is a specialized function, intended to be put in the
:c:member:`~PyTypeObject.tp_call` slot or be used in an implementation of ``tp_call``.
It does not check the :const:`_Py_TPFLAGS_HAVE_VECTORCALL` flag
It does not check the :const:`Py_TPFLAGS_HAVE_VECTORCALL` flag
and it does not fall back to ``tp_call``.
This function is not part of the `limited API <stable>`_.
.. versionadded:: 3.8
@ -185,7 +198,7 @@ please see individual documentation for details.
+------------------------------------------+------------------+--------------------+---------------+
| :c:func:`PyObject_CallNoArgs` | ``PyObject *`` | --- | --- |
+------------------------------------------+------------------+--------------------+---------------+
| :c:func:`_PyObject_CallOneArg` | ``PyObject *`` | 1 object | --- |
| :c:func:`PyObject_CallOneArg` | ``PyObject *`` | 1 object | --- |
+------------------------------------------+------------------+--------------------+---------------+
| :c:func:`PyObject_CallObject` | ``PyObject *`` | tuple/``NULL`` | --- |
+------------------------------------------+------------------+--------------------+---------------+
@ -197,15 +210,15 @@ please see individual documentation for details.
+------------------------------------------+------------------+--------------------+---------------+
| :c:func:`PyObject_CallMethodObjArgs` | obj + name | variadic | --- |
+------------------------------------------+------------------+--------------------+---------------+
| :c:func:`_PyObject_CallMethodNoArgs` | obj + name | --- | --- |
| :c:func:`PyObject_CallMethodNoArgs` | obj + name | --- | --- |
+------------------------------------------+------------------+--------------------+---------------+
| :c:func:`_PyObject_CallMethodOneArg` | obj + name | 1 object | --- |
| :c:func:`PyObject_CallMethodOneArg` | obj + name | 1 object | --- |
+------------------------------------------+------------------+--------------------+---------------+
| :c:func:`_PyObject_Vectorcall` | ``PyObject *`` | vectorcall | vectorcall |
| :c:func:`PyObject_Vectorcall` | ``PyObject *`` | vectorcall | vectorcall |
+------------------------------------------+------------------+--------------------+---------------+
| :c:func:`_PyObject_FastCallDict` | ``PyObject *`` | vectorcall | dict/``NULL`` |
| :c:func:`PyObject_VectorcallDict` | ``PyObject *`` | vectorcall | dict/``NULL`` |
+------------------------------------------+------------------+--------------------+---------------+
| :c:func:`_PyObject_VectorcallMethod` | arg + name | vectorcall | vectorcall |
| :c:func:`PyObject_VectorcallMethod` | arg + name | vectorcall | vectorcall |
+------------------------------------------+------------------+--------------------+---------------+
@ -235,7 +248,7 @@ please see individual documentation for details.
.. versionadded:: 3.9
.. c:function:: PyObject* _PyObject_CallOneArg(PyObject *callable, PyObject *arg)
.. c:function:: PyObject* PyObject_CallOneArg(PyObject *callable, PyObject *arg)
Call a callable Python object *callable* with exactly 1 positional argument
*arg* and no keyword arguments.
@ -243,6 +256,8 @@ please see individual documentation for details.
Return the result of the call on success, or raise an exception and return
*NULL* on failure.
This function is not part of the `limited API <stable>`_.
.. versionadded:: 3.9
@ -320,7 +335,7 @@ please see individual documentation for details.
*NULL* on failure.
.. c:function:: PyObject* _PyObject_CallMethodNoArgs(PyObject *obj, PyObject *name)
.. c:function:: PyObject* PyObject_CallMethodNoArgs(PyObject *obj, PyObject *name)
Call a method of the Python object *obj* without arguments,
where the name of the method is given as a Python string object in *name*.
@ -328,10 +343,12 @@ please see individual documentation for details.
Return the result of the call on success, or raise an exception and return
*NULL* on failure.
This function is not part of the `limited API <stable>`_.
.. versionadded:: 3.9
.. c:function:: PyObject* _PyObject_CallMethodOneArg(PyObject *obj, PyObject *name, PyObject *arg)
.. c:function:: PyObject* PyObject_CallMethodOneArg(PyObject *obj, PyObject *name, PyObject *arg)
Call a method of the Python object *obj* with a single positional argument
*arg*, where the name of the method is given as a Python string object in
@ -340,10 +357,12 @@ please see individual documentation for details.
Return the result of the call on success, or raise an exception and return
*NULL* on failure.
This function is not part of the `limited API <stable>`_.
.. versionadded:: 3.9
.. c:function:: PyObject* _PyObject_Vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)
.. c:function:: PyObject* PyObject_Vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)
Call a callable Python object *callable*.
The arguments are the same as for :c:type:`vectorcallfunc`.
@ -353,15 +372,11 @@ please see individual documentation for details.
Return the result of the call on success, or raise an exception and return
*NULL* on failure.
.. note::
This function is not part of the `limited API <stable>`_.
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.9
.. versionadded:: 3.8
.. c:function:: PyObject* _PyObject_FastCallDict(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwdict)
.. c:function:: PyObject* PyObject_VectorcallDict(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwdict)
Call *callable* with positional arguments passed exactly as in the vectorcall_ protocol,
but with keyword arguments passed as a dictionary *kwdict*.
@ -373,15 +388,11 @@ please see individual documentation for details.
already has a dictionary ready to use for the keyword arguments,
but not a tuple for the positional arguments.
.. note::
This function is not part of the `limited API <stable>`_.
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.9
.. versionadded:: 3.8
.. c:function:: PyObject* _PyObject_VectorcallMethod(PyObject *name, PyObject *const *args, size_t nargsf, PyObject *kwnames)
.. c:function:: PyObject* PyObject_VectorcallMethod(PyObject *name, PyObject *const *args, size_t nargsf, PyObject *kwnames)
Call a method using the vectorcall calling convention. The name of the method
is given as a Python string *name*. The object whose method is called is
@ -390,7 +401,7 @@ please see individual documentation for details.
*nargsf* is the number of positional arguments including *args[0]*,
plus :const:`PY_VECTORCALL_ARGUMENTS_OFFSET` if the value of ``args[0]`` may
temporarily be changed. Keyword arguments can be passed just like in
:c:func:`_PyObject_Vectorcall`.
:c:func:`PyObject_Vectorcall`.
If the object has the :const:`Py_TPFLAGS_METHOD_DESCRIPTOR` feature,
this will call the unbound method object with the full
@ -399,6 +410,8 @@ please see individual documentation for details.
Return the result of the call on success, or raise an exception and return
*NULL* on failure.
This function is not part of the `limited API <stable>`_.
.. versionadded:: 3.9

View file

@ -684,15 +684,15 @@ and :c:type:`PyType_Type` effectively act as defaults.)
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`
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.
The *vectorcallfunc* pointer may be ``NULL``, in which case the instance behaves
as if :const:`_Py_TPFLAGS_HAVE_VECTORCALL` was not set: calling the instance
as if :const:`Py_TPFLAGS_HAVE_VECTORCALL` was not set: calling the instance
falls back to :c:member:`~PyTypeObject.tp_call`.
Any class that sets ``_Py_TPFLAGS_HAVE_VECTORCALL`` must also set
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 :c:func:`PyVectorcall_Call`.
@ -719,7 +719,7 @@ and :c:type:`PyType_Type` effectively act as defaults.)
**Inheritance:**
This field is always inherited.
However, the :const:`_Py_TPFLAGS_HAVE_VECTORCALL` flag is not
However, the :const:`Py_TPFLAGS_HAVE_VECTORCALL` flag is not
always inherited. If it's not, then the subclass won't use
:ref:`vectorcall <vectorcall>`, except when
:c:func:`PyVectorcall_Call` is explicitly called.
@ -1153,7 +1153,7 @@ and :c:type:`PyType_Type` effectively act as defaults.)
type structure.
.. data:: _Py_TPFLAGS_HAVE_VECTORCALL
.. data:: Py_TPFLAGS_HAVE_VECTORCALL
This bit is set when the class implements
the :ref:`vectorcall protocol <vectorcall>`.
@ -1163,15 +1163,9 @@ and :c:type:`PyType_Type` effectively act as defaults.)
This bit is inherited for *static* subtypes if
:c:member:`~PyTypeObject.tp_call` is also inherited.
`Heap types`_ do not inherit ``_Py_TPFLAGS_HAVE_VECTORCALL``.
`Heap types`_ do not inherit ``Py_TPFLAGS_HAVE_VECTORCALL``.
.. 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
.. versionadded:: 3.9
.. c:member:: const char* PyTypeObject.tp_doc

View file

@ -29,7 +29,7 @@ PyAPI_FUNC(PyObject *) _PyStack_AsDict(
/* Suggested size (number of positional arguments) for arrays of PyObject*
allocated on a C stack to avoid allocating memory on the heap memory. Such
array is used to pass positional arguments to call functions of the
_PyObject_Vectorcall() family.
PyObject_Vectorcall() family.
The size is chosen to not abuse the C stack and so limit the risk of stack
overflow. The size is also chosen to allow using the small stack for most
@ -45,8 +45,8 @@ PyAPI_FUNC(PyObject *) _Py_CheckFunctionResult(
/* === Vectorcall protocol (PEP 590) ============================= */
/* Call callable using tp_call. Arguments are like _PyObject_Vectorcall()
or _PyObject_FastCallDict() (both forms are supported),
/* Call callable using tp_call. Arguments are like PyObject_Vectorcall()
or PyObject_FastCallDict() (both forms are supported),
except that nargs is plainly the number of arguments without flags. */
PyAPI_FUNC(PyObject *) _PyObject_MakeTpCall(
PyThreadState *tstate,
@ -63,7 +63,7 @@ PyVectorcall_NARGS(size_t n)
}
static inline vectorcallfunc
_PyVectorcall_Function(PyObject *callable)
PyVectorcall_Function(PyObject *callable)
{
assert(callable != NULL);
PyTypeObject *tp = Py_TYPE(callable);
@ -103,7 +103,7 @@ _PyObject_VectorcallTstate(PyThreadState *tstate, PyObject *callable,
assert(kwnames == NULL || PyTuple_Check(kwnames));
assert(args != NULL || PyVectorcall_NARGS(nargsf) == 0);
vectorcallfunc func = _PyVectorcall_Function(callable);
vectorcallfunc func = PyVectorcall_Function(callable);
if (func == NULL) {
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
return _PyObject_MakeTpCall(tstate, callable, args, nargs, kwnames);
@ -113,7 +113,7 @@ _PyObject_VectorcallTstate(PyThreadState *tstate, PyObject *callable,
}
static inline PyObject *
_PyObject_Vectorcall(PyObject *callable, PyObject *const *args,
PyObject_Vectorcall(PyObject *callable, PyObject *const *args,
size_t nargsf, PyObject *kwnames)
{
PyThreadState *tstate = PyThreadState_GET();
@ -121,9 +121,18 @@ _PyObject_Vectorcall(PyObject *callable, PyObject *const *args,
args, nargsf, kwnames);
}
/* Same as _PyObject_Vectorcall except that keyword arguments are passed as
// Backwards compatibility aliases for API that was provisional in Python 3.8
#define _PyObject_Vectorcall PyObject_Vectorcall
#define _PyObject_VectorcallMethod PyObject_VectorcallMethod
#define _PyObject_FastCallDict PyObject_VectorcallDict
#define _PyVectorcall_Function PyVectorcall_Function
#define _PyObject_CallOneArg PyObject_CallOneArg
#define _PyObject_CallMethodNoArgs PyObject_CallMethodNoArgs
#define _PyObject_CallMethodOneArg PyObject_CallMethodOneArg
/* Same as PyObject_Vectorcall except that keyword arguments are passed as
dict, which may be NULL if there are no keyword arguments. */
PyAPI_FUNC(PyObject *) _PyObject_FastCallDict(
PyAPI_FUNC(PyObject *) PyObject_VectorcallDict(
PyObject *callable,
PyObject *const *args,
size_t nargsf,
@ -133,7 +142,7 @@ PyAPI_FUNC(PyObject *) _PyObject_FastCallDict(
"tuple" and keyword arguments "dict". "dict" may also be NULL */
PyAPI_FUNC(PyObject *) PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict);
/* Same as _PyObject_Vectorcall except without keyword arguments */
/* Same as PyObject_Vectorcall except without keyword arguments */
static inline PyObject *
_PyObject_FastCall(PyObject *func, PyObject *const *args, Py_ssize_t nargs)
{
@ -151,7 +160,7 @@ _PyObject_CallNoArg(PyObject *func) {
}
static inline PyObject *
_PyObject_CallOneArg(PyObject *func, PyObject *arg)
PyObject_CallOneArg(PyObject *func, PyObject *arg)
{
assert(arg != NULL);
PyObject *_args[2];
@ -162,19 +171,19 @@ _PyObject_CallOneArg(PyObject *func, PyObject *arg)
return _PyObject_VectorcallTstate(tstate, func, args, nargsf, NULL);
}
PyAPI_FUNC(PyObject *) _PyObject_VectorcallMethod(
PyAPI_FUNC(PyObject *) PyObject_VectorcallMethod(
PyObject *name, PyObject *const *args,
size_t nargsf, PyObject *kwnames);
static inline PyObject *
_PyObject_CallMethodNoArgs(PyObject *self, PyObject *name)
PyObject_CallMethodNoArgs(PyObject *self, PyObject *name)
{
return _PyObject_VectorcallMethod(name, &self,
1 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
}
static inline PyObject *
_PyObject_CallMethodOneArg(PyObject *self, PyObject *name, PyObject *arg)
PyObject_CallMethodOneArg(PyObject *self, PyObject *name, PyObject *arg)
{
assert(arg != NULL);
PyObject *args[2] = {self, arg};
@ -207,7 +216,7 @@ _PyObject_VectorcallMethodId(
if (!oname) {
return NULL;
}
return _PyObject_VectorcallMethod(oname, args, nargsf, kwnames);
return PyObject_VectorcallMethod(oname, args, nargsf, kwnames);
}
static inline PyObject *

View file

@ -279,7 +279,9 @@ given type object has a specified feature.
/* Set if the type implements the vectorcall protocol (PEP 590) */
#ifndef Py_LIMITED_API
#define _Py_TPFLAGS_HAVE_VECTORCALL (1UL << 11)
#define Py_TPFLAGS_HAVE_VECTORCALL (1UL << 11)
// Backwards compatibility alias for API that was provisional in Python 3.8
#define _Py_TPFLAGS_HAVE_VECTORCALL Py_TPFLAGS_HAVE_VECTORCALL
#endif
/* Set if the type is 'ready' -- fully initialized */

View file

@ -0,0 +1,5 @@
The Vectorcall API (PEP 590) was made public, adding the functions
``PyObject_Vectorcall``, ``PyObject_VectorcallMethod``,
``PyVectorcall_Function``, ``PyObject_CallOneArg``,
``PyObject_CallMethodNoArgs``, ``PyObject_CallMethodOneArg``,
``PyObject_FastCallDict``, and the flag ``Py_TPFLAGS_HAVE_VECTORCALL``.