Updates to PEP 560 (#460)
* Change title and use __mro_entry__ * Add more discussion and types.resolve_bases * Improve example
This commit is contained in:
parent
d85cbb9264
commit
4fd3245ea1
65
pep-0560.rst
65
pep-0560.rst
|
@ -1,5 +1,5 @@
|
||||||
PEP: 560
|
PEP: 560
|
||||||
Title: Core support for generic types
|
Title: Core support for typing module and generic types
|
||||||
Author: Ivan Levkivskyi <levkivskyi@gmail.com>
|
Author: Ivan Levkivskyi <levkivskyi@gmail.com>
|
||||||
Status: Draft
|
Status: Draft
|
||||||
Type: Standards Track
|
Type: Standards Track
|
||||||
|
@ -18,7 +18,7 @@ the ``typing`` module are extensively used by the community, e.g. PEP 526
|
||||||
and PEP 557 extend the usage of type hints, and the backport of ``typing``
|
and PEP 557 extend the usage of type hints, and the backport of ``typing``
|
||||||
on PyPI has 1M downloads/month. Therefore, this restriction can be removed.
|
on PyPI has 1M downloads/month. Therefore, this restriction can be removed.
|
||||||
It is proposed to add two special methods ``__class_getitem__`` and
|
It is proposed to add two special methods ``__class_getitem__`` and
|
||||||
``__subclass_base__`` to the core CPython for better support of
|
``__mro_entry__`` to the core CPython for better support of
|
||||||
generic types.
|
generic types.
|
||||||
|
|
||||||
|
|
||||||
|
@ -134,31 +134,72 @@ For example::
|
||||||
Note that this method is used as a fallback, so if a metaclass defines
|
Note that this method is used as a fallback, so if a metaclass defines
|
||||||
``__getitem__``, then that will have the priority.
|
``__getitem__``, then that will have the priority.
|
||||||
|
|
||||||
``__subclass_base__``
|
|
||||||
---------------------
|
``__mro_entry__``
|
||||||
|
-----------------
|
||||||
|
|
||||||
If an object that is not a class object appears in the bases of a class
|
If an object that is not a class object appears in the bases of a class
|
||||||
definition, then ``__subclass_base__`` is searched on it. If found,
|
definition, then ``__mro_entry__`` is searched on it. If found,
|
||||||
it is called with the original tuple of bases as an argument. If the result
|
it is called with the original tuple of bases as an argument. If the result
|
||||||
of the call is not ``None``, then it is substituted instead of this object.
|
of the call is not ``None``, then it is substituted instead of this object.
|
||||||
Otherwise (if the result is ``None``), the base is just removed. This is
|
Otherwise (if the result is ``None``), the base is just removed. This is
|
||||||
necessary to avoid inconsistent MRO errors, that are currently prevented by
|
necessary to avoid inconsistent MRO errors, that are currently prevented by
|
||||||
manipulations in ``GenericMeta.__new__``. After creating the class,
|
manipulations in ``GenericMeta.__new__``. After creating the class,
|
||||||
the original bases are saved in ``__orig_bases__`` (currently this is also
|
the original bases are saved in ``__orig_bases__`` (currently this is also
|
||||||
done by the metaclass).
|
done by the metaclass). For example::
|
||||||
|
|
||||||
NOTE: These two method names are reserved for exclusive use by
|
class GenericAlias:
|
||||||
the ``typing`` module and the generic types machinery, and any other use is
|
def __init__(self, origin, item):
|
||||||
strongly discouraged. The reference implementation (with tests) can be found
|
self.origin = origin
|
||||||
in [4]_, and the proposal was originally posted and discussed on
|
self.item = item
|
||||||
the ``typing`` tracker, see [5]_.
|
def __mro_entry__(self, bases):
|
||||||
|
return self.origin
|
||||||
|
|
||||||
|
class NewList:
|
||||||
|
def __class_getitem__(cls, item):
|
||||||
|
return GenericAlias(cls, item)
|
||||||
|
|
||||||
|
class Tokens(NewList[int]):
|
||||||
|
...
|
||||||
|
|
||||||
|
assert Tokens.__bases__ == (NewList,)
|
||||||
|
assert Tokens.__orig_bases__ == (NewList[int],)
|
||||||
|
assert Tokens.__mro__ == (Tokens, NewList, object)
|
||||||
|
|
||||||
|
NOTE: These two method names are reserved for use by the ``typing`` module
|
||||||
|
and the generic types machinery, and any other use is discouraged.
|
||||||
|
The reference implementation (with tests) can be found in [4]_, and
|
||||||
|
the proposal was originally posted and discussed on the ``typing`` tracker,
|
||||||
|
see [5]_.
|
||||||
|
|
||||||
|
|
||||||
|
Dynamic class creation and ``types.resolve_bases``
|
||||||
|
--------------------------------------------------
|
||||||
|
|
||||||
|
``type.__new__`` will not perform any MRO entry resolution. So that a direct
|
||||||
|
call ``type('Tokens', (List[int],), {})`` will fail. This is done for
|
||||||
|
performance reasons and to minimize the number of implicit transformations.
|
||||||
|
Instead, a helper function ``resolve_bases`` will be added to
|
||||||
|
the ``types`` module to allow an explicit ``__mro_entry__`` resolution in
|
||||||
|
the context of dynamic class creation. Correspondingly, ``types.new_class``
|
||||||
|
will be updated to reflect the new class creation steps while maintaining
|
||||||
|
the backwards compatibility::
|
||||||
|
|
||||||
|
def new_class(name, bases=(), kwds=None, exec_body=None):
|
||||||
|
resolved_bases = resolve_bases(bases) # This step is added
|
||||||
|
meta, ns, kwds = prepare_class(name, resolved_bases, kwds)
|
||||||
|
if exec_body is not None:
|
||||||
|
exec_body(ns)
|
||||||
|
cls = meta(name, resolved_bases, ns, **kwds)
|
||||||
|
cls.__orig_bases__ = bases # This step is added
|
||||||
|
return cls
|
||||||
|
|
||||||
|
|
||||||
Backwards compatibility and impact on users who don't use ``typing``
|
Backwards compatibility and impact on users who don't use ``typing``
|
||||||
====================================================================
|
====================================================================
|
||||||
|
|
||||||
This proposal may break code that currently uses the names
|
This proposal may break code that currently uses the names
|
||||||
``__class_getitem__`` and ``__subclass_base__``. (But the language
|
``__class_getitem__`` and ``__mro_entry__``. (But the language
|
||||||
reference explicitly reserves *all* undocumented dunder names, and
|
reference explicitly reserves *all* undocumented dunder names, and
|
||||||
allows "breakage without warning"; see [6]_.)
|
allows "breakage without warning"; see [6]_.)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue