[Python-checkins] bpo-39245: Make Vectorcall C API public (GH-17893)

Petr Viktorin webhook-mailer at python.org
Thu Feb 6 09:48:34 EST 2020


https://github.com/python/cpython/commit/3f563cea567fbfed9db539ecbbacfee2d86f7735
commit: 3f563cea567fbfed9db539ecbbacfee2d86f7735
branch: master
author: Petr Viktorin <encukou at gmail.com>
committer: GitHub <noreply at github.com>
date: 2020-02-06T15:48:27+01:00
summary:

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

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

This partially reverts commit 2ff58a24e8a1c7e290d025d69ebaea0bbead3b8c
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

files:
A Misc/NEWS.d/next/C API/2020-01-07-13-46-40.bpo-39245.G7wog6.rst
M Doc/c-api/call.rst
M Doc/c-api/typeobj.rst
M Include/cpython/abstract.h
M Include/object.h

diff --git a/Doc/c-api/call.rst b/Doc/c-api/call.rst
index 0833531b1d5ee..06db12666d787 100644
--- a/Doc/c-api/call.rst
+++ b/Doc/c-api/call.rst
@@ -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 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 not part of the `limited API <stable>`_.
 
-   .. versionadded:: 3.8
+   .. versionadded:: 3.9
 
-.. 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.8
+   .. versionadded:: 3.9
 
-.. 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
 
 
diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst
index a8a779ef6165a..ff0e70e6e52e2 100644
--- a/Doc/c-api/typeobj.rst
+++ b/Doc/c-api/typeobj.rst
@@ -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
diff --git a/Include/cpython/abstract.h b/Include/cpython/abstract.h
index 2c4eae70b9690..4bd7b1a61a532 100644
--- a/Include/cpython/abstract.h
+++ b/Include/cpython/abstract.h
@@ -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 *
diff --git a/Include/object.h b/Include/object.h
index e7e9c1b8ed752..38794a0e98cea 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -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 */
diff --git a/Misc/NEWS.d/next/C API/2020-01-07-13-46-40.bpo-39245.G7wog6.rst b/Misc/NEWS.d/next/C API/2020-01-07-13-46-40.bpo-39245.G7wog6.rst
new file mode 100644
index 0000000000000..e5836b5255d3d
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2020-01-07-13-46-40.bpo-39245.G7wog6.rst	
@@ -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``.



More information about the Python-checkins mailing list