[Python-Dev] PEP 580/590 discussion

Petr Viktorin encukou at gmail.com
Thu May 9 14:30:23 EDT 2019

PEP 590 is on its way to be accepted, with some details still to be
discussed. I've rejected PEP 580 so we can focus on one place.

Here are things we discussed on GitHub but now seem to agree on:

* The vectorcall function's kwname argument can be NULL.
* Let's use `vectorcallfunc`, not `vectorcall`, and stop the bikeshedding.
* `tp_vectorcall_offset` can be `Py_ssize_t` (The discussions around
signedness and C standards and consistency are interesting, but
ultimately irrelevant here.)
* `PyCall_MakeTpCall` can be removed.
* `PyVectorcall_Function` (for getting the `vectorcallfunc` of an
object) can be an internal helper. External code should go through
`PyCall_Vectorcall` (whatever we name it).
* `PY_VECTORCALL_ARGUMENTS_OFFSET` is OK, bikeshedding over variants
like `PY_VECTORCALL_PREPEND` won't bring much benefit.

Anyone against, make your point now :)

The following have discussion PRs open:

* `PyCall_MakeVectorCall` name: https://github.com/python/peps/pull/1037
* Passing a dict to `PyObject_Vectorcall`:
* Type of the kwnames argument (PyObject/PyTupleObject):

The remaining points are:

### Making things private

For Python 3.8, the public API should be private, so the API can get
some contact with the real world. I'd especially like to be able to
learn from
Cython's experience using it.
That would mean:

* _PyObject_Vectorcall
* _PyCall_MakeVectorCall
* _PyVectorcall_NARGS

### Can the kwnames tuple be empty?

Disallowing empty tuples means it's easier for the *callee* to detect
the case of no keyword arguments. Instead of:

    if (kwnames != NULL && PyTuple_GET_SIZE(kwnames))

you have:

    if (kwnames != NULL)

On the other hand, the *caller* would now be responsible for handling
the no-kwarg case specially.

Jeroen points out:
> The side of the caller (which should ensure not to send an empty tuple)
> is CPython and there the issue of people implementing the protocol wrongly
> doesn't arise.
> External C code is not expected to manually use tp_vectorcall_offset to make
> vectorcalls: it is expected to use an API like PyCall_Vectorcall() and that
> API will ensure to replace an empty tuple with NULL.
> I see it as an application of https://en.wikipedia.org/wiki/Robustness_principle
> (Be conservative in what you send, be liberal in what you accept):
> PyCall_Vectorcall should accept an empty tuple but it should not send an
> empty tuple to the vectorcall function.

But, if you apply the robustness principle to vectorcallfunc, it
should accept empty tuples.

### `METH_VECTORCALL` function type

Jeroen suggested changing this from:

    `PyObject *(*call) (PyObject *self, PyObject *const *args,
Py_ssize_t nargs, PyObject *kwname)`

to `vectorcallfunc`, i.e.:

    `PyObject *(*call) (PyObject *callable, Py_ssize_t n, PyObject
*const *args, PyObject *kwnames)`

Mark argues that this is a major change and prevents the interpreter
from sanity checking the return value of PyMethodDef defined
(Since the functions are defined by extension code, they need to be
sanity-checked, and this will be done by PyCFunction's vectorcall
adapter. Tools like Cython can bypass the check if needed.)

The underlying C function should not need to know how to extract
"self" from the function object, or how to handle the argument
Those should be implementation details.

I see the value in having METH_VECTORCALL equivalent to the existing
(Even though PEP 573 will need to add to the calling convention.)

More information about the Python-Dev mailing list