PEP 580: minor update (#749)

This commit is contained in:
jdemeyer 2018-07-30 17:02:03 +02:00 committed by Chris Angelico
parent 3bdd70578a
commit 84871930a5
1 changed files with 61 additions and 39 deletions

View File

@ -23,7 +23,7 @@ this new protocol is used for the existing classes
``builtin_function_or_method`` and ``method_descriptor``. ``builtin_function_or_method`` and ``method_descriptor``.
However, in the future, more classes may implement it. 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. 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`` Below are the possible values for ``cc_flags & CCALL_SIGNATURE``
together with the arguments that the C function takes. together with the arguments that the C function takes.
The return value is always ``PyObject *``. 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: 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)`` - ``CCALL_NOARGS``:
(the function takes no arguments but a ``NULL`` is passed to the C function) ``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. The flag ``CCALL_FUNCARG`` may be combined with any of these.
If so, the C function takes an additional argument as first argument If so, the C function takes an additional argument as second argument
which is the function object (the ``self`` in ``__call__``). after ``self``.
This argument is used to pass the function object (the ``self`` in ``__call__``).
For example, we have the following signature: 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 **NOTE**: in the case of bound methods, it is currently unspecified
whether the "function object" in the paragraph above refers 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)`` guarantees that ``PyCCall_CCALLDEF(func)``
points to the ``PyCCallDef`` of the original function. 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, **NOTE**: unlike the existing ``METH_...`` flags,
the ``CCALL_...`` constants do not necessarily represent single bits. the ``CCALL_...`` constants do not necessarily represent single bits.
So checking ``(cc_flags & CCALL_VARARGS) == 0`` is not a valid way So checking ``(cc_flags & CCALL_VARARGS) == 0`` is not a valid way
@ -230,18 +240,18 @@ If not, a ``TypeError`` is raised.
Self slicing 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 not set in ``cc_flags``, then the argument passed as ``self``
is simply ``cr_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 then the first positional argument is removed from
``args`` and instead passed as first argument to the C function. ``args`` and instead passed as first argument to the C function.
Effectively, the first positional argument is treated as ``__self__``. Effectively, the first positional argument is treated as ``__self__``.
If there are no positional arguments, ``TypeError`` is raised. If there are no positional arguments, ``TypeError`` is raised.
This process is called self slicing and a function is said to have self 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 Note that a ``METH_NULLARG`` function with self slicing effectively has
one argument, namely ``self``. 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 that getting the ``__name__`` attribute twice in a row must return
exactly the same Python object. exactly the same Python object.
This implies that it cannot be a temporary object, it must be stored somewhere. This implies that it cannot be a temporary object, it must be stored somewhere.
This is required because ``PyEval_GetFuncName`` and ``PyEval_GetFuncDesc`` This is required because ``PyEval_GetFuncName``
use borrowed references to the ``__name__`` attribute. uses a borrowed reference to the ``__name__`` attribute.
Generic API functions 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)``: - ``int PyCCall_Check(PyObject *op)``:
return true if ``op`` implements the C call protocol. 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. apply to any instance supporting the C call protocol.
In other words, ``PyCCall_Check(func)`` must be true. 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`` call ``func`` with positional arguments ``args``
and keyword arguments ``kwds`` (``kwds`` may be NULL). and keyword arguments ``kwds`` (``kwds`` may be NULL).
This function is meant to be put in the ``tp_call`` slot. 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]``. call ``func`` with ``nargs`` positional arguments given by ``args[0]``, …, ``args[nargs-1]``.
The parameter ``kwds`` can be NULL (no keyword arguments), The parameter ``kwds`` can be NULL (no keyword arguments),
a dict with ``name:value`` items or a tuple with keyword names. 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: 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``. pointer to the ``PyCCallRoot`` structure inside ``func``.
- ``PyCCallDef * PyCCall_CCALLDEF(PyObject *func)``: - ``PyCCallDef *PyCCall_CCALLDEF(PyObject *func)``:
shorthand for ``PyCCall_CCALLROOT(func)->cr_ccall``. 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``. 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``. shorthand for ``PyCCall_CCALLROOT(func)->cr_self``.
Generic getters, meant to be put into the ``tp_getset`` array: 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``. return ``cc_parent``.
Raise ``AttributeError`` if ``cc_parent`` is NULL. 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__``. return a string suitable for using as ``__qualname__``.
This uses the ``__qualname__`` of ``cc_parent`` if possible. This uses the ``__qualname__`` of ``cc_parent`` if possible.
It also uses the ``__name__`` attribute. It also uses the ``__name__`` attribute.
- ``PyObject * PyCCall_GenericGetSelf(PyObject *func, void *closure)``:
return ``cr_self``.
Raise ``AttributeError`` if ``cr_self`` is NULL.
Profiling Profiling
--------- ---------
@ -374,7 +381,7 @@ the implementation becomes very similar, but they remain separate classes
The ``PyCCallDef`` structure is simply stored The ``PyCCallDef`` structure is simply stored
as part of the object structure. as part of the object structure.
Both classes use ``PyCFunctionObject`` as object structure. Both classes use ``PyCFunctionObject`` as object structure.
This is the new layout:: This is the new layout for both classes::
typedef struct { typedef struct {
PyObject_HEAD 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)``: - ``PyObject * PyCFunction_ClsNew(PyTypeObject *cls, PyMethodDef *ml, PyObject *self, PyObject *module, PyObject *parent)``:
create a new object with object structure ``PyCFunctionObject`` and class ``cls``. 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`` The undocumented functions ``PyCFunction_GetFlags``
and ``PyCFunction_GET_FLAGS`` 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, It is meant to allow efficient new code to be written,
not to make existing code faster. 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 Stable ABI
========== ==========
@ -453,8 +475,8 @@ we are free to do that without restrictions.
Backwards compatibility Backwards compatibility
======================= =======================
There should be no difference at all for the Python interface, There is no difference at all for the Python interface,
and neither for the documented C API nor for the documented C API
(in the sense that all functions remain supported with the same functionality). (in the sense that all functions remain supported with the same functionality).
The removed function ``PyCFunction_GetFlags``, 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 The other examples show that these kind of checks appear
in multiple places, so it makes sense to add generic support for them. 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: are needed to support methods:
the C function should not care the C function should not care
whether it is called as unbound method or as bound method. 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 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`` 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 there is no extra layer of indirection for calling
and constructing bound methods does not require setting up a ``PyCCallDef`` structure. and constructing bound methods does not require setting up a ``PyCCallDef`` structure.