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`: https://github.com/python/peps/pull/1038 * Type of the kwnames argument (PyObject/PyTupleObject): https://github.com/python/peps/pull/1039
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 * _METH_VECTORCALL * _Py_TPFLAGS_HAVE_VECTORCALL * _Py_TPFLAGS_METHOD_DESCRIPTOR
### 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))
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 functions. (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 offsetting. Those should be implementation details.
I see the value in having METH_VECTORCALL equivalent to the existing METH_FASTCALL|METH_KEYWORDS. (Even though PEP 573 will need to add to the calling convention.)