On 28 September 2017 at 08:27, Nick Coghlan <ncoghlan@gmail.com> wrote:
On 27 September 2017 at 19:28, Ivan Levkivskyi <levkivskyi@gmail.com> wrote:
> If an object that is not a class object appears in the bases of a class
> definition, the ``__subclass_base__`` 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).

How would you feel about calling it "__mro_entry__", as a mnemonic for
"the substitute entry to use instead of this object when calculating a
subclass MRO"?


I don't have any preferences for the name, __mro_entry__ sounds equally OK to me. 

I think the other thing that needs to be clarified is whether or not
the actual metaclass can expect to receive an already-resolved
sequence of MRO entries as its list of bases, or if it will need to
repeat the base resolution process executed while figuring out the
metaclass.


There are three points for discussion here:

1) It is necessary to make the bases resolution soon, before the metaclass
is calculated. This is why I do this at the beginning of __build_class__ in the
reference implementation.

2) Do we need to update type.__new__ to be able to accept non-classes as bases?
I think no. One might be a bit surprised that

    class C(Iterable[int]):
        pass

works, but

    type('C', (Iterable[int],), {})

fails with a metaclass conflict, but I think it is natural that static typing and dynamic
class creation should not be used together. I propose to update ``type.__new__`` to just give
a better error message explaining this.

3) Do we need to update types.new_class and types.prepare_class?
Here I am not sure. These functions are rather utility functions and are designed to
mimic in Python what __build_class__ does in C. I think we might add types._update_bases
that does the same as its C counterpart. Then we can update types.new_class and types.prepare_class
like you proposed, this will preserve their current API while types.new_class will match behaviour of __build_class__

If you and others agree with this, then I will update the PEP text and the reference implementation.

Thanks for comments!

--
Ivan