diff options
author | Petr Viktorin <encukou@gmail.com> | 2020-02-06 14:48:27 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-06 14:48:27 (GMT) |
commit | 3f563cea567fbfed9db539ecbbacfee2d86f7735 (patch) | |
tree | d7817605e6d152770f680bcffb03fe04bef50c7c | |
parent | d2f96672642cc51b92f61b951ca1b11d615c12d1 (diff) | |
download | cpython-3f563cea567fbfed9db539ecbbacfee2d86f7735.zip cpython-3f563cea567fbfed9db539ecbbacfee2d86f7735.tar.gz cpython-3f563cea567fbfed9db539ecbbacfee2d86f7735.tar.bz2 |
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
-rw-r--r-- | Doc/c-api/call.rst | 89 | ||||
-rw-r--r-- | Doc/c-api/typeobj.rst | 20 | ||||
-rw-r--r-- | Include/cpython/abstract.h | 37 | ||||
-rw-r--r-- | Include/object.h | 4 | ||||
-rw-r--r-- | Misc/NEWS.d/next/C API/2020-01-07-13-46-40.bpo-39245.G7wog6.rst | 5 |
5 files changed, 89 insertions, 66 deletions
diff --git a/Doc/c-api/call.rst b/Doc/c-api/call.rst index 0833531..06db126 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 a8a779e..ff0e70e 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 2c4eae7..4bd7b1a 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 e7e9c1b..38794a0 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 0000000..e5836b5 --- /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``. |