Update PEP 575 (#608)

This commit is contained in:
jdemeyer 2018-04-05 17:30:16 +02:00 committed by Chris Angelico
parent e79b63a128
commit f58b37ce8e
1 changed files with 57 additions and 50 deletions

View File

@ -84,15 +84,17 @@ It behaves like the existing ``builtin_function_or_method``
with some differences: with some differences:
#. It acts as a descriptor implementing ``__get__`` to turn a function into a method #. It acts as a descriptor implementing ``__get__`` to turn a function into a method
if there was no ``__self__`` attribute. if ``m_self`` is ``NULL``.
If the ``__self__`` attribute was already set, then this is a no-op: If ``m_self`` is not ``NULL``,
the existing function is returned instead. then this is a no-op: the existing function is returned instead.
#. A new read-only slot ``__objclass__``, represented in the C structure as ``m_objclass``. #. A new read-only slot ``__objclass__``, represented in the C structure as ``m_objclass``.
If this attribute exists, it must be an extension type. If this attribute exists, it must be a class.
If so, the function must be called with ``self`` being an instance of that class. If so, the function must be called with ``self`` being an instance of that class.
This is meant to support unbound methods of extension types, replacing ``method_descriptor``. This is mainly meant to support unbound methods of extension types,
The pointer ``m_objclass`` is not considered a reference. replacing ``method_descriptor``.
Also ``__qualname__`` will use ``__objclass__`` as namespace
(instead of ``__self__``).
#. Argument Clinic [#clinic]_ is not supported. #. Argument Clinic [#clinic]_ is not supported.
@ -107,10 +109,17 @@ with some differences:
equal to the function object instead of ``__self__``. equal to the function object instead of ``__self__``.
#. A new flag ``METH_ARG0_NO_SLICE`` for ``ml_flags``. #. A new flag ``METH_ARG0_NO_SLICE`` for ``ml_flags``.
If this flag is *not* set, ``__objclass__`` is set and ``__self__`` is not set, If this flag is *not* set and ``__self__`` is not set,
then the first positional argument is treated as ``__self__``. then the first positional argument is treated as ``__self__``.
For more details, see `Self slicing`_. For more details, see `Self slicing`_.
#. A new flag ``METH_BINDING`` for ``ml_flags`` which only applies to
functions of a module (not methods of a class).
If this flag is set, then ``m_self`` will be set to ``NULL`` instead
of the module.
This allows the function to behave more like a Python function
as it enables ``__get__``.
#. A new flag ``METH_PYTHON`` for ``ml_flags``. #. A new flag ``METH_PYTHON`` for ``ml_flags``.
This flag indicates that this function should be treated as Python function. This flag indicates that this function should be treated as Python function.
Ideally, use of this flag should be avoided because it goes Ideally, use of this flag should be avoided because it goes
@ -257,16 +266,16 @@ It adds one new attribute on top of ``basefunction``:
which was used only for Python functions bound as method. which was used only for Python functions bound as method.
There is a complication because we want to allow There is a complication because we want to allow
constructing a method from a arbitrary callable which constructing a method from an arbitrary callable.
may not be an instance of ``basefunction``. This may be an already-bound method or simply not an instance of ``basefunction``.
Therefore, in practice there are two kinds of methods: Therefore, in practice there are two kinds of methods:
for arbitrary callables, we use a single fixed ``PyCFunctionDef`` for arbitrary callables, we use a single fixed ``PyCFunctionDef``
structure with ``ml_name`` equal to ``"?"`` structure with the ``METH_ARG0_FUNCTION`` flag set.
and with the ``METH_ARG0_FUNCTION`` flag set.
The C function then calls ``__func__`` with the correct arguments. The C function then calls ``__func__`` with the correct arguments.
For methods which bind instances of ``basefunction`` For methods which bind instances of ``basefunction``
(more precisely, which have the ``Py_TPFLAGS_BASEFUNCTION`` flag set), (more precisely, which have the ``Py_TPFLAGS_BASEFUNCTION`` flag set)
that have ``m_self == NULL``,
we instead use the ``PyCFunctionDef`` from the original function. we instead use the ``PyCFunctionDef`` from the original function.
In this case, the ``__func__`` attribute is only used to implement various attributes In this case, the ``__func__`` attribute is only used to implement various attributes
but not for calling the method. but not for calling the method.
@ -292,8 +301,8 @@ We specify the implementation of ``__call__`` for instances of ``basefunction``.
__objclass__ __objclass__
------------ ------------
First of all, if the function has an ``__objclass__`` attribute but no First of all, if the function has an ``__objclass__`` attribute but
``__self__`` attribute (this is the case for unbound methods of extension types), ``m_self`` is ``NULL`` (this is the case for non-static unbound methods of extension types),
then the function must be called with at least one positional argument then the function must be called with at least one positional argument
and the first (typically called ``self``) must be an instance of ``__objclass__``. and the first (typically called ``self``) must be an instance of ``__objclass__``.
If not, a ``TypeError`` is raised. If not, a ``TypeError`` is raised.
@ -321,9 +330,9 @@ We explain the others shortly.
Self slicing Self slicing
------------ ------------
If the function has a ``__objclass__`` attribute, no ``__self__`` If the function has no ``__self__``
attribute and neither ``METH_ARG0_FUNCTION`` nor ``METH_ARG0_NO_SLICE`` is set, attribute and none of the flags ``METH_ARG0_FUNCTION``, ``METH_ARG0_NO_SLICE`` nor ``METH_STATIC`` is set,
then the first positional argument (which must exist because of ``__objclass__``) then the first positional argument (if any)
is removed from ``*args`` and instead passed as first argument to the C function. is removed from ``*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__``.
This process is called "self slicing". This process is called "self slicing".
@ -395,7 +404,7 @@ Built-in functions of a module
------------------------------ ------------------------------
For the case of functions of a module, For the case of functions of a module,
``__self__`` will be set to the module unless the flag ``METH_STATIC`` is set. ``__self__`` will be set to the module unless the flag ``METH_BINDING`` is set.
An important consequence is that such functions by default An important consequence is that such functions by default
do not become methods when used as attribute do not become methods when used as attribute
@ -405,9 +414,7 @@ in an initial post on python-ideas [#proposal]_ the concensus was to keep this
misfeature of built-in functions. misfeature of built-in functions.
However, to allow this anyway for specific or newly implemented However, to allow this anyway for specific or newly implemented
built-in functions, the ``METH_STATIC`` flag prevents setting ``__self__``. built-in functions, the ``METH_BINDING`` flag prevents setting ``__self__``.
Previously, ``METH_STATIC`` was an error, so this is fullt backwards compatible.
Specifying ``METH_CLASS`` is still an error.
Further changes Further changes
@ -439,12 +446,19 @@ We add and change some Python/C API functions:
- ``int PyBaseFunction_Check(PyObject *op)``: return true if ``op`` - ``int PyBaseFunction_Check(PyObject *op)``: return true if ``op``
is an instance of a type with the ``Py_TPFLAGS_BASEFUNCTION`` set. is an instance of a type with the ``Py_TPFLAGS_BASEFUNCTION`` set.
- ``int PyCFunction_Check(PyObject *op)``: return true if ``PyBaseFunction_Check(op)`` - ``PyObject* PyBaseFunction_New(PyTypeObject *cls, PyCFunctionDef *ml, PyObject *self, PyObject *module, PyTypeObject *objclass)``:
is True and the function ``op`` does not have the flag ``METH_PYTHON`` set. create a new instance of ``cls`` (which must be a subclass of ``basefunction``)
from the given data.
- ``int PyBuiltinFunction_Check(PyObject *op)``: return true if ``op`` - ``int PyCFunction_Check(PyObject *op)``: return true if ``op``
is an instance of ``builtin_function``. is an instance of ``builtin_function``.
- ``int PyCFunction_NewEx(PyMethodDef* ml, PyObject *self, PyObject* module)``:
create a new instance of ``builtin_function``.
As special case, if ``self`` is ``NULL``,
then set ``self = Py_None`` instead.
This is done for backwards compatibility.
- ``int PyFunction_Check(PyObject *op)``: return true if ``op`` - ``int PyFunction_Check(PyObject *op)``: return true if ``op``
is an instance of ``generic_function``. is an instance of ``generic_function``.
@ -460,10 +474,6 @@ We add and change some Python/C API functions:
For backwards compatibility, For backwards compatibility,
the old functions are kept as aliases of the new functions. the old functions are kept as aliases of the new functions.
**TODO**: more functions may be added when implementing this PEP.
In particular, there should probably be functions for creating instances of ``basefunction``
or ``generic_function``.
Changes to the types module Changes to the types module
--------------------------- ---------------------------
@ -659,36 +669,33 @@ of ``PyBaseFunctionObject.m_ml.ml_meth`` with flags
``METH_VARARGS | METH_KEYWORDS | METH_ARG0_FUNCTION``. ``METH_VARARGS | METH_KEYWORDS | METH_ARG0_FUNCTION``.
Therefore, it should be easy to change existing ``tp_call`` slots Therefore, it should be easy to change existing ``tp_call`` slots
to use ``METH_ARG0_FUNCTION``. to use ``METH_ARG0_FUNCTION``.
There is just one extra complication: ``__self__`` must be handled manually. There is one extra complication though: ``__self__`` must be handled manually.
That is not hard though: it just means adapting that logic from ``method``.
Self slicing: METH_ARG0_NO_SLICE Self slicing: METH_ARG0_NO_SLICE
-------------------------------- --------------------------------
We define "self slicing" to mean slicing off the ``self`` argument of a method **TODO**: ``METH_ARG0_NO_SLICE`` will probably be dropped from
from the ``*args`` tuple when an unbound method is called. the PEP since both ``METH_ARG0_FUNCTION`` and ``METH_STATIC``
This ``self`` argument is then passed as first argument to the C function. already disable self slicing.
The specification of ``METH_ARG0_NO_SLICE`` may seem strange at first. .. We define "self slicing" to mean slicing off the ``self`` argument of a method
The negation is confusing, but it is done for backwards compatibility: from the ``*args`` tuple when an unbound method is called.
existing methods require self slicing but do not specify a flag for it. This ``self`` argument is then passed as first argument to the C function.
The requirement for ``__objclass__`` in order to use self slicing .. The specification of ``METH_ARG0_NO_SLICE`` may seem strange at first.
makes sense because it guarantees that there is a ``self`` argument in the first place. The negation is confusing, but it is done for backwards compatibility:
existing methods require self slicing but do not specify a flag for it.
Since ``METH_ARG0_FUNCTION`` is clearly incompatible with self slicing .. Since ``METH_ARG0_FUNCTION`` is clearly incompatible with self slicing
(both use the first argument of the C function), (both use the first argument of the C function),
this PEP dictates that ``METH_ARG0_FUNCTION`` disables self slicing. this PEP dictates that ``METH_ARG0_FUNCTION`` disables self slicing.
So one may wonder if there is actually a use case for ``METH_ARG0_NO_SLICE`` So one may wonder if there is actually a use case for ``METH_ARG0_NO_SLICE``
without ``METH_ARG0_FUNCTION``. without ``METH_ARG0_FUNCTION``.
If not, then one could simply unify those two flags in one flag If not, then one could simply unify those two flags in one flag
``METH_ARG0_FUNCTION``. ``METH_ARG0_FUNCTION``.
However, a priori, the flag ``METH_ARG0_NO_SLICE`` is meaningful, .. However, a priori, the flag ``METH_ARG0_NO_SLICE`` is meaningful,
so we keep the two flags ``METH_ARG0_FUNCTION`` and ``METH_ARG0_NO_SLICE`` separate. so we keep the two flags ``METH_ARG0_FUNCTION`` and ``METH_ARG0_NO_SLICE`` separate.
**TODO**: this should be reconsidered after initial implementation
and testing of this PEP.
User flags: METH_CUSTOM and METH_USRx User flags: METH_CUSTOM and METH_USRx
------------------------------------- -------------------------------------