Skip to content

Commit 4fd3245

Browse files
ilevkivskyigvanrossum
authored andcommitted
Updates to PEP 560 (#460)
* Change title and use __mro_entry__ * Add more discussion and types.resolve_bases * Improve example
1 parent d85cbb9 commit 4fd3245

File tree

1 file changed

+53
-12
lines changed

1 file changed

+53
-12
lines changed

pep-0560.rst

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PEP: 560
2-
Title: Core support for generic types
2+
Title: Core support for typing module and generic types
33
Author: Ivan Levkivskyi <[email protected]>
44
Status: Draft
55
Type: Standards Track
@@ -18,7 +18,7 @@ the ``typing`` module are extensively used by the community, e.g. PEP 526
1818
and PEP 557 extend the usage of type hints, and the backport of ``typing``
1919
on PyPI has 1M downloads/month. Therefore, this restriction can be removed.
2020
It is proposed to add two special methods ``__class_getitem__`` and
21-
``__subclass_base__`` to the core CPython for better support of
21+
``__mro_entry__`` to the core CPython for better support of
2222
generic types.
2323

2424

@@ -134,31 +134,72 @@ For example::
134134
Note that this method is used as a fallback, so if a metaclass defines
135135
``__getitem__``, then that will have the priority.
136136

137-
``__subclass_base__``
138-
---------------------
137+
138+
``__mro_entry__``
139+
-----------------
139140

140141
If an object that is not a class object appears in the bases of a class
141-
definition, then ``__subclass_base__`` is searched on it. If found,
142+
definition, then ``__mro_entry__`` is searched on it. If found,
142143
it is called with the original tuple of bases as an argument. If the result
143144
of the call is not ``None``, then it is substituted instead of this object.
144145
Otherwise (if the result is ``None``), the base is just removed. This is
145146
necessary to avoid inconsistent MRO errors, that are currently prevented by
146147
manipulations in ``GenericMeta.__new__``. After creating the class,
147148
the original bases are saved in ``__orig_bases__`` (currently this is also
148-
done by the metaclass).
149+
done by the metaclass). For example::
150+
151+
class GenericAlias:
152+
def __init__(self, origin, item):
153+
self.origin = origin
154+
self.item = item
155+
def __mro_entry__(self, bases):
156+
return self.origin
157+
158+
class NewList:
159+
def __class_getitem__(cls, item):
160+
return GenericAlias(cls, item)
161+
162+
class Tokens(NewList[int]):
163+
...
164+
165+
assert Tokens.__bases__ == (NewList,)
166+
assert Tokens.__orig_bases__ == (NewList[int],)
167+
assert Tokens.__mro__ == (Tokens, NewList, object)
168+
169+
NOTE: These two method names are reserved for use by the ``typing`` module
170+
and the generic types machinery, and any other use is discouraged.
171+
The reference implementation (with tests) can be found in [4]_, and
172+
the proposal was originally posted and discussed on the ``typing`` tracker,
173+
see [5]_.
174+
175+
176+
Dynamic class creation and ``types.resolve_bases``
177+
--------------------------------------------------
178+
179+
``type.__new__`` will not perform any MRO entry resolution. So that a direct
180+
call ``type('Tokens', (List[int],), {})`` will fail. This is done for
181+
performance reasons and to minimize the number of implicit transformations.
182+
Instead, a helper function ``resolve_bases`` will be added to
183+
the ``types`` module to allow an explicit ``__mro_entry__`` resolution in
184+
the context of dynamic class creation. Correspondingly, ``types.new_class``
185+
will be updated to reflect the new class creation steps while maintaining
186+
the backwards compatibility::
149187

150-
NOTE: These two method names are reserved for exclusive use by
151-
the ``typing`` module and the generic types machinery, and any other use is
152-
strongly discouraged. The reference implementation (with tests) can be found
153-
in [4]_, and the proposal was originally posted and discussed on
154-
the ``typing`` tracker, see [5]_.
188+
def new_class(name, bases=(), kwds=None, exec_body=None):
189+
resolved_bases = resolve_bases(bases) # This step is added
190+
meta, ns, kwds = prepare_class(name, resolved_bases, kwds)
191+
if exec_body is not None:
192+
exec_body(ns)
193+
cls = meta(name, resolved_bases, ns, **kwds)
194+
cls.__orig_bases__ = bases # This step is added
195+
return cls
155196

156197

157198
Backwards compatibility and impact on users who don't use ``typing``
158199
====================================================================
159200

160201
This proposal may break code that currently uses the names
161-
``__class_getitem__`` and ``__subclass_base__``. (But the language
202+
``__class_getitem__`` and ``__mro_entry__``. (But the language
162203
reference explicitly reserves *all* undocumented dunder names, and
163204
allows "breakage without warning"; see [6]_.)
164205

0 commit comments

Comments
 (0)