PEP 580: minor update (#749)
This commit is contained in:
parent
3bdd70578a
commit
84871930a5
100
pep-0580.rst
100
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.
|
||||
|
||||
|
|
Loading…
Reference in New Issue