From 84871930a5283e231753a4a19e2dbc83dd7bbb3c Mon Sep 17 00:00:00 2001 From: jdemeyer Date: Mon, 30 Jul 2018 17:02:03 +0200 Subject: [PATCH] PEP 580: minor update (#749) --- pep-0580.rst | 100 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 39 deletions(-) diff --git a/pep-0580.rst b/pep-0580.rst index 125243618..3ec91984d 100644 --- a/pep-0580.rst +++ b/pep-0580.rst @@ -23,7 +23,7 @@ this new protocol is used for the existing classes ``builtin_function_or_method`` and ``method_descriptor``. However, in the future, more classes may implement it. -**NOTE**: This PEP deals only with CPython implementation details, +**NOTE**: This PEP deals only with the Python/C API, it does not affect the Python language or standard library. @@ -177,28 +177,34 @@ Its precise signature depends on flags. Below are the possible values for ``cc_flags & CCALL_SIGNATURE`` together with the arguments that the C function takes. The return value is always ``PyObject *``. -The following are completely analogous to the existing ``PyMethodDef`` +The following are analogous to the existing ``PyMethodDef`` signature flags: -- ``CCALL_VARARGS``: ``cc_func(PyObject *self, PyObject *args)`` +- ``CCALL_VARARGS``: + ``cc_func(PyObject *self, PyObject *args)`` -- ``CCALL_VARARGS | CCALL_KEYWORDS``: ``cc_func(PyObject *self, PyObject *args, PyObject *kwds)`` +- ``CCALL_VARARGS | CCALL_KEYWORDS``: + ``cc_func(PyObject *self, PyObject *args, PyObject *kwds)`` -- ``CCALL_FASTCALL``: ``cc_func(PyObject *self, PyObject *const *args, Py_ssize_t nargs)`` +- ``CCALL_FASTCALL``: + ``cc_func(PyObject *self, PyObject *const *args, Py_ssize_t nargs)`` -- ``CCALL_FASTCALL | CCALL_KEYWORDS``: ``cc_func(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)`` +- ``CCALL_FASTCALL | CCALL_KEYWORDS``: + ``cc_func(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)`` -- ``CCALL_NULLARG``: ``cc_func(PyObject *self, PyObject *null)`` - (the function takes no arguments but a ``NULL`` is passed to the C function) +- ``CCALL_NOARGS``: + ``cc_func(PyObject *self)`` -- ``CCALL_O``: ``cc_func(PyObject *self, PyObject *arg)`` +- ``CCALL_O``: + ``cc_func(PyObject *self, PyObject *arg)`` The flag ``CCALL_FUNCARG`` may be combined with any of these. -If so, the C function takes an additional argument as first argument -which is the function object (the ``self`` in ``__call__``). +If so, the C function takes an additional argument as second argument +after ``self``. +This argument is used to pass the function object (the ``self`` in ``__call__``). For example, we have the following signature: -- ``CCALL_FUNCARG | CCALL_VARARGS``: ``cc_func(PyObject *func, PyObject *self, PyObject *args)`` +- ``CCALL_VARARGS | CCALL_FUNCARG``: ``cc_func(PyObject *self, PyObject *func, PyObject *args)`` **NOTE**: in the case of bound methods, it is currently unspecified whether the "function object" in the paragraph above refers @@ -209,6 +215,10 @@ Despite this ambiguity, the implementation of bound methods guarantees that ``PyCCall_CCALLDEF(func)`` points to the ``PyCCallDef`` of the original function. +**NOTE**: ``METH_NOARGS`` takes a second unused argument. +This is compatible with ``CCALL_NOARGS | CCALL_FUNCARG``, +which also takes two arguments. + **NOTE**: unlike the existing ``METH_...`` flags, the ``CCALL_...`` constants do not necessarily represent single bits. So checking ``(cc_flags & CCALL_VARARGS) == 0`` is not a valid way @@ -230,18 +240,18 @@ If not, a ``TypeError`` is raised. Self slicing ------------ -If ``cr_self`` is not NULL or if the flag ``CCALL_SLICE_SELF`` +If ``cr_self`` is not NULL or if the flag ``CCALL_SELFARG`` is not set in ``cc_flags``, then the argument passed as ``self`` is simply ``cr_self``. -If ``cr_self`` is NULL and the flag ``CCALL_SLICE_SELF`` is set, +If ``cr_self`` is NULL and the flag ``CCALL_SELFARG`` is set, then the first positional argument is removed from ``args`` and instead passed as first argument to the C function. Effectively, the first positional argument is treated as ``__self__``. If there are no positional arguments, ``TypeError`` is raised. This process is called self slicing and a function is said to have self -slicing if ``cr_self`` is NULL and ``CCALL_SLICE_SELF`` is set. +slicing if ``cr_self`` is NULL and ``CCALL_SELFARG`` is set. Note that a ``METH_NULLARG`` function with self slicing effectively has one argument, namely ``self``. @@ -294,13 +304,14 @@ Furthermore, this must be idempotent in the sense that getting the ``__name__`` attribute twice in a row must return exactly the same Python object. This implies that it cannot be a temporary object, it must be stored somewhere. -This is required because ``PyEval_GetFuncName`` and ``PyEval_GetFuncDesc`` -use borrowed references to the ``__name__`` attribute. +This is required because ``PyEval_GetFuncName`` +uses a borrowed reference to the ``__name__`` attribute. Generic API functions --------------------- -This section lists the new public API functions dealing with the C call protocol. +This section lists the new public API functions or macros +dealing with the C call protocol. - ``int PyCCall_Check(PyObject *op)``: return true if ``op`` implements the C call protocol. @@ -309,12 +320,12 @@ All the functions and macros below apply to any instance supporting the C call protocol. In other words, ``PyCCall_Check(func)`` must be true. -- ``PyObject * PyCCall_Call(PyObject *func, PyObject *args, PyObject *kwds)``: +- ``PyObject *PyCCall_Call(PyObject *func, PyObject *args, PyObject *kwds)``: call ``func`` with positional arguments ``args`` and keyword arguments ``kwds`` (``kwds`` may be NULL). This function is meant to be put in the ``tp_call`` slot. -- ``PyObject * PyCCall_FASTCALL(PyObject *func, PyObject *const *args, Py_ssize_t nargs, PyObject *kwds)``: +- ``PyObject *PyCCall_FASTCALL(PyObject *func, PyObject *const *args, Py_ssize_t nargs, PyObject *kwds)``: call ``func`` with ``nargs`` positional arguments given by ``args[0]``, …, ``args[nargs-1]``. The parameter ``kwds`` can be NULL (no keyword arguments), a dict with ``name:value`` items or a tuple with keyword names. @@ -323,33 +334,29 @@ In other words, ``PyCCall_Check(func)`` must be true. Macros to access the ``PyCCallRoot`` and ``PyCCallDef`` structures: -- ``PyCCallRoot * PyCCall_CCALLROOT(PyObject *func)``: +- ``PyCCallRoot *PyCCall_CCALLROOT(PyObject *func)``: pointer to the ``PyCCallRoot`` structure inside ``func``. -- ``PyCCallDef * PyCCall_CCALLDEF(PyObject *func)``: +- ``PyCCallDef *PyCCall_CCALLDEF(PyObject *func)``: shorthand for ``PyCCall_CCALLROOT(func)->cr_ccall``. -- ``PyCCallDef * PyCCall_FLAGS(PyObject *func)``: +- ``PyCCallDef *PyCCall_FLAGS(PyObject *func)``: shorthand for ``PyCCall_CCALLROOT(func)->cr_ccall->cc_flags``. -- ``PyObject * PyCCall_SELF(PyOject *func)``: +- ``PyObject *PyCCall_SELF(PyOject *func)``: shorthand for ``PyCCall_CCALLROOT(func)->cr_self``. Generic getters, meant to be put into the ``tp_getset`` array: -- ``PyObject * PyCCall_GenericGetParent(PyObject *func, void *closure)``: +- ``PyObject *PyCCall_GenericGetParent(PyObject *func, void *closure)``: return ``cc_parent``. Raise ``AttributeError`` if ``cc_parent`` is NULL. -- ``PyObject * PyCCall_GenericGetQualname(PyObject *func, void *closure)``: +- ``PyObject *PyCCall_GenericGetQualname(PyObject *func, void *closure)``: return a string suitable for using as ``__qualname__``. This uses the ``__qualname__`` of ``cc_parent`` if possible. It also uses the ``__name__`` attribute. -- ``PyObject * PyCCall_GenericGetSelf(PyObject *func, void *closure)``: - return ``cr_self``. - Raise ``AttributeError`` if ``cr_self`` is NULL. - Profiling --------- @@ -374,7 +381,7 @@ the implementation becomes very similar, but they remain separate classes The ``PyCCallDef`` structure is simply stored as part of the object structure. Both classes use ``PyCFunctionObject`` as object structure. -This is the new layout:: +This is the new layout for both classes:: typedef struct { PyObject_HEAD @@ -404,7 +411,13 @@ The following function is added (also to the stable ABI [#pep384]_): - ``PyObject * PyCFunction_ClsNew(PyTypeObject *cls, PyMethodDef *ml, PyObject *self, PyObject *module, PyObject *parent)``: create a new object with object structure ``PyCFunctionObject`` and class ``cls``. - This is called in turn by ``PyCFunction_NewEx`` and ``PyDescr_NewMethod``. + Note that the entries of the ``PyMethodDef`` structure are used to construct + the new object, but it is no longer stored in the object. + The flags for the C call protocol are automatically determined in terms + of ``ml->ml_flags``, ``self`` and ``parent``. + +The existing functions ``PyCFunction_New``, ``PyCFunction_NewEx`` and +``PyDescr_NewMethod`` are implemented in terms of ``PyCFunction_ClsNew``. The undocumented functions ``PyCFunction_GetFlags`` and ``PyCFunction_GET_FLAGS`` @@ -431,6 +444,15 @@ This PEP should not impact the performance of existing code It is meant to allow efficient new code to be written, not to make existing code faster. +Here are a few pointers to the ``python-dev`` mailing list where +performance improvements are discussed: + +- https://mail.python.org/pipermail/python-dev/2018-July/154571.html + +- https://mail.python.org/pipermail/python-dev/2018-July/154740.html + +- https://mail.python.org/pipermail/python-dev/2018-July/154775.html + Stable ABI ========== @@ -453,8 +475,8 @@ we are free to do that without restrictions. Backwards compatibility ======================= -There should be no difference at all for the Python interface, -and neither for the documented C API +There is no difference at all for the Python interface, +nor for the documented C API (in the sense that all functions remain supported with the same functionality). The removed function ``PyCFunction_GetFlags``, @@ -537,10 +559,10 @@ In the reference implementation, only the first of these uses the new code. The other examples show that these kind of checks appear in multiple places, so it makes sense to add generic support for them. -Why CCALL_SLICE_SELF? ---------------------- +Why CCALL_SELFARG? +------------------ -The flag ``CCALL_SLICE_SELF`` and the concept of self slicing +The flag ``CCALL_SELFARG`` and the concept of self slicing are needed to support methods: the C function should not care whether it is called as unbound method or as bound method. @@ -553,9 +575,9 @@ translate to the C call ``list_append([], 42)``. Thanks to the proposed C call protocol, we can support this in such a way that both the unbound and the bound method share a ``PyCCallDef`` -structure (with the ``CCALL_SLICE_SELF`` flag set). +structure (with the ``CCALL_SELFARG`` flag set). -Concluding, ``CCALL_SLICE_SELF`` has two advantages: +Concluding, ``CCALL_SELFARG`` has two advantages: there is no extra layer of indirection for calling and constructing bound methods does not require setting up a ``PyCCallDef`` structure.