diff --git a/pep-0560.rst b/pep-0560.rst index a27f0a6a5..a17e3a1b6 100644 --- a/pep-0560.rst +++ b/pep-0560.rst @@ -1,5 +1,5 @@ PEP: 560 -Title: Core support for generic types +Title: Core support for typing module and generic types Author: Ivan Levkivskyi Status: Draft 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`` on PyPI has 1M downloads/month. Therefore, this restriction can be removed. 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. @@ -134,31 +134,72 @@ For example:: Note that this method is used as a fallback, so if a metaclass defines ``__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 -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 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 necessary to avoid inconsistent MRO errors, that are currently prevented by manipulations in ``GenericMeta.__new__``. After creating the class, 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 -the ``typing`` module and the generic types machinery, and any other use is -strongly 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]_. + class GenericAlias: + def __init__(self, origin, item): + self.origin = origin + self.item = item + 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`` ==================================================================== 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 allows "breakage without warning"; see [6]_.)