forked from python/peps
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Change title and use __mro_entry__ * Add more discussion and types.resolve_bases * Improve example
- Loading branch information
1 parent
d85cbb9
commit 4fd3245
Showing
1 changed file
with
53 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
PEP: 560 | ||
Title: Core support for generic types | ||
Title: Core support for typing module and generic types | ||
Author: Ivan Levkivskyi <[email protected]> | ||
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:: | ||
|
||
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:: | ||
|
||
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]_. | ||
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]_.) | ||
|
||
|