I need to think about this more.

Technically the class *is* created at the point `__init_subclass__` is called. What the metaclass does after that point is embellishment.

Most metaclasses will have sufficient control for their needs because they can manipulate the contents of the namespace that's passed to type_new() -- I understand that doesn't work for Enum because the instances have a reference to the class in them.

I suspect the classes that you claim are "buggy" are fine in practice, even if you can construct a counterexample.

All in all I just worry about the backward compatibility here: one could easily imagine a metaclass whose `__new__` relies on the effect of the `__init_subclass__` called by type_new(), and moving the call would break that. So it's probably 6 bugs fixed, half a dozen new bugs created. In such a case, the status quo should win.

--Guido

On Tue, Dec 29, 2020 at 10:44 AM Ethan Furman <ethan@stoneleaf.us> wrote:
On 12/29/20 8:59 AM, Guido van Rossum wrote:
> On Mon, Dec 28, 2020 at 10:24 PM Ethan Furman wrote:

>> The `__init_subclass__` and `__set_name__` protocols are intended to be run before a new type is finished, but creating
>> a new type has three major steps:
>>
>> - `__prepare__` to get the namespace
>> - `__new__` to get the memory and data structures
>> - `__init__` for any final polishing
>>
>> We can easily move the calls from `type_new()` to `type_init`.
>
> No, we can't. There is a window where the subclass is initialized after `typing_new()` returned before `__init__`
> starts, and you propose to move the subclass after that window. There may be code that depends on the class being
> initialized at that point, and you will break that code.

True, there will be a few custom metaclasses that need to move some code from their `__new__` to `__init__` instead, and
a few that need to add an `__init__` to consume any keyword arguments that don't need to get passed to
`__init_subclass__`.  That seems like a small price to pay to be able to write custom metaclasses that are able to fully
participate in the `__set_name__` and `__init_subclass__` protocols.

Just in the stdlib we have two custom metaclasses that would start working correctly with this change (for the
`__init_subclass__` case).

> Honestly I disapprove of the shenanigans you're committing in enum.py, and if those are in in the 3.10 (master) branch I
> recommend that you take them out. It just looks too fragile and obscure.

Trust me, I don't like them either.  But I like even less that a custom `__init_subclass__` for an Enum would fail if it
tried to do anything with the members.  That's one of the reasons why I would like to see this fix put in (the
shenanigans would be unnecessary then).

 From the point of view of a metaclass author, the current behavior feels buggy.  In theory, `__init_subclass__` and
`__set_name__` are supposed to be called after a class is created, and yet they are being called somewhere in the middle
of my metaclass' `__new__`.

Looked at another way, `__init_subclass__` should be receiving a `cls` that is ready for use, i.e. done and fully
constructed and complete. But if that class is being created by a custom metaclass, then the thing that is being given
to `__init_subclass__` could easily be only partial.

--
~Ethan~
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/7WPNGL267FDGN6WWCHZQUAXWVBTUJOMN/
Code of Conduct: http://python.org/psf/codeofconduct/


--
--Guido van Rossum (python.org/~guido)
Pronouns: he/him (why is my pronoun here?)