PEP 590: minor edits (#973)

This commit is contained in:
Jeroen Demeyer 2019-04-07 13:34:18 +02:00 committed by Mark Shannon
parent ad9c99dd8d
commit 92f4dbdbf1
1 changed files with 21 additions and 23 deletions

View File

@ -59,10 +59,10 @@ Changes to the ``PyTypeObject``
-------------------------------
The a new slot called ``tp_vectorcall_offset`` is added. It has the type ``uint32_t``.
A new flag is added, ``Py_TPFLAGS_HAVE_VECTOR_CALL``, which is set for any new PyTypeObjects that include the
A new flag is added, ``Py_TPFLAGS_HAVE_VECTORCALL``, which is set for any new PyTypeObjects that include the
``tp_vectorcall_offset`` member.
If ``Py_TPFLAGS_HAS_VECTORCALL`` is set then ``tp_vectorcall_offset`` is the offset
If ``Py_TPFLAGS_HAVE_VECTORCALL`` is set then ``tp_vectorcall_offset`` is the offset
into the object of the ``vectorcall`` function-pointer.
The unused slot ``printfunc tp_print`` is replaced with ``vectorcall tp_vectorcall``, so that classes
@ -71,21 +71,21 @@ can support the vectorcall calling convention.
Additional flags
----------------
One additional flag is specified: ``PY_METHOD_DESCRIPTOR``.
One additional flag is specified: ``Py_TPFLAGS_METHOD_DESCRIPTOR``.
``PY_METHOD_DESCRIPTOR`` should be set if the the callable uses the descriptor protocol to create a method or method-like object.
``Py_TPFLAGS_METHOD_DESCRIPTOR`` should be set if the the callable uses the descriptor protocol to create a method or method-like object.
This is used by the interpreter to avoid creating temporary objects when calling methods.
If this flag is set for a class ``F``, then instances of that class are expected to behave the same as a Python function when used as a class attribute.
Specifically, this mean that the value of ``c.m`` where ``C.m`` is an instanceof the class ``F`` (and ``c`` is an instance of ``C``)
must be an object that acts like a bound-method binding ``C.m`` and ``c``.
Specifically, this means that the value of ``c.m`` where ``C.m`` is an instance of the class ``F`` (and ``c`` is an instance of ``C``)
must be an object that acts like a bound method binding ``C.m`` and ``c``.
This flag is necessary if custom callables are to be able to behave like Python functions *and* be called as efficiently as Python or built-in functions.
The call
--------
The call takes the form ``((vectorcall)(((char *)o)+offset))(o, n, args, kwnames)`` where
``offset`` is ``TYPE(o)->tp_vectorcall``
``offset`` is ``Py_TYPE(o)->tp_vectorcall_offset``.
The caller is responsible for creating the ``kwnames`` tuple and ensuring that there are no duplicates in it.
``n`` is the number of postional arguments plus ``PY_VECTORCALL_ARGUMENTS_OFFSET`` if the argument vector pointer points to argument 1 in the
allocated vector.
@ -94,7 +94,7 @@ allocated vector.
Example of how ``PY_VECTORCALL_ARGUMENTS_OFFSET`` is used by a callee to avoid allocation [3]_
Whenever they can do so cheaply (without allocation) callers are encouraged to offset the arguments.
Doing so will allow callables such as bound-methods to make their onward calls cheaply.
Doing so will allow callables such as bound methods to make their onward calls cheaply.
The interpreter already allocates space on the stack for the callable, so it can offset its arguments for no additional cost.
Continued prohibition of callable classes as base classes
@ -107,7 +107,7 @@ If callables could be sub-classed then any call to a ``function`` or a ``method_
New C API and changes to CPython
================================
``PyObject *PyObject_VectorCallWithCallable(PyObject *func, PyObject **args, Py_ssize_t nargs, PyTupleObject *kwnames)``
``PyObject *PyObject_VectorCallWithCallable(PyObject *obj, PyObject **args, Py_ssize_t nargs, PyTupleObject *kwnames)``
Calls ``obj`` with the given arguments.
Note that ``nargs`` is the number of positional arguments; no offsetting is allowed.
@ -118,16 +118,16 @@ Note that ``nargs`` is the number of positional arguments, including the callabl
Both functions raise an exception if ``obj`` is not callable.
Two utility functions are provided to call the new calling convention from the old one, or vice-versa.
These functions are ``PyObject *``PyCall_MakeVectorCall(PyObject *obj, PyObject *tuple, PyObject **dict);`` and
``PyObject *PyCall_MakeTpCall(PyObject *obj, PyObject **args, Py_ssize_t nargs, PyTupleObject *kwnames);``, respectively.
These functions are ``PyObject *PyCall_MakeVectorCall(PyObject *obj, PyObject *tuple, PyObject **dict)`` and
``PyObject *PyCall_MakeTpCall(PyObject *obj, PyObject **args, Py_ssize_t nargs, PyTupleObject *kwnames)``, respectively.
Both functions raise an exception if ``obj`` does not support the relevant protocol.
``METH_FASTCALL`` and ``METH_VECTORCALL`` flags
-----------------------------------------------
A new ``METH_VECTORCALL`` flag is added for specifying ``MethodDef`` structs. It is equivalent to the currently undocumented ``METH_FASTCALL | METH_KEYWORD`` flag.
The new flag specifies that the function has the type ``PyObject *(*call) (PyObject * self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwname)``
A new ``METH_VECTORCALL`` flag is added for specifying ``PyMethodDef`` structs. It is equivalent to the currently undocumented ``METH_FASTCALL | METH_KEYWORD`` flags.
The new flag specifies that the function has the type ``PyObject *(*call) (PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwname)``
Internal CPython changes
========================
@ -155,12 +155,12 @@ Third-party built-in classes using the new extended call interface
------------------------------------------------------------------
To enable call performance on a par with Python functions and built-in functions, third-party callables should include a ``vectorcall`` function pointer
and set ``tp_vectorcall`` to the correct value.
Any class the sets ``tp_vectorcall`` to non-zero should also implement the ``tp_call`` function and make sure its behaviour is consistent with the ``vectorcall`` function.
and set ``tp_vectorcall_offset`` to the correct value.
Any class that sets ``tp_vectorcall_offset`` to non-zero should also implement the ``tp_call`` function and make sure its behaviour is consistent with the ``vectorcall`` function.
Setting ``tp_call`` to ``PyCall_MakeVectorCall`` will suffice.
The `MethodDef` protocol and Argument Clinic
============================================
The ``PyMethodDef`` protocol and Argument Clinic
================================================
Argument Clinic [4]_ automatically generates wrapper functions around lower-level callables, providing safe unboxing of primitive types and
other safety checks.
@ -190,20 +190,18 @@ This PEP is broader in scope than PEP 576 and uses variable rather than fixed of
The underlying calling convention is similar. Because PEP 576 only allows a fixed offset for the function pointer,
it would not allow the improvements to any objects with constraints on their layout.
PEP 580 proposes a major change to the `MethodDef` protocol used to define builtin functions.
PEP 580 proposes a major change to the ``PyMethodDef`` protocol used to define builtin functions.
This PEP provides a more general and simpler mechanism in the form of a new calling convention.
This PEP also extends the `MethodDef` protocol, but merely to formalise existing conventions.
PEP 580 is specifically targetted at function-like objects, and doesn't support other callables like classes, partial functions,
or proxies.
This PEP also extends the ``PyMethodDef`` protocol, but merely to formalise existing conventions.
Other rejected approaches
-------------------------
A longer, 6 argument, form combining both the vector and optional tuple and dictionary arguments was considered.
However, it was found that the code to convert between it and the old `tp_call` form was overly cumbersome and inefficient.
However, it was found that the code to convert between it and the old ``tp_call`` form was overly cumbersome and inefficient.
Also, since only 4 arguments are passed in registers on x64 Windows, the two extra arguments would have non-neglible costs.
Removing any special cases and making all calls use the `tp_call` form was also considered.
Removing any special cases and making all calls use the ``tp_call`` form was also considered.
However, unless a much more efficient way was found to create and destroy tuples, and to a lesser extent dictionaries,
then it would be too slow.