For the record - the 3rd process that is currently un-customizable when
creating a class, i.e. things that happen in an opaque way inside
`type.__new__`,
is the ABC class machinery. I could not recall it when
writing the previous e-mail.

Still - I think this might be very little disruptive, and yet provide
metaclasses back with full customization power, including
being able to address the problem brought up by Etham.


   js
 -><-

On Fri, 25 Dec 2020 at 01:40, Joao S. O. Bueno <jsbu...@python.org.br>
wrote:

> Actually, there are a few steps that `type.__new__`  perform that are not
> customizable in metaclasses.
>
> I had sometimes thought about mailing this here, or Python ideas, but
> could not
> come up with a "real world" use case where the customization of those
> would be meaningful.
>
> Let'me see if I recall all cases - two of them are the calls to
> `__init_subclass__` and the descriptors `__set_name__` as you put it,
> I think there is a third behavior that can't be separated from
> `type.__new__` - but
> I can't remember it now
>
>
> Anyway, the "thing to do" that always occurred to me about it is to add
> "soft" method slots
> to `type` itself - so that `type.__new__` would call those  on the
> corresponding initialization phases.
>
> Since these are to be run only when classes are created, their impact
> should be negligible.
>
> In other words, have `type` implement methods like
> `__run_init_subclass__`, `__run_descriptor_setname__`,
> (and one for the other task I can't remember now). So, all metaclass code
> written up to today remains valid,
> and these behaviors become properly customizable.
>
> Adding keyword parameters to `type.__new__`, IMHO, besides a little bit
> fishy as we are talking of
> arguments to change the behavior of the method, would themselves compete
> and have to be
> filtered out, or otherwise special-cased in the `__init_subclass__` method
> itself.
> I mean - let's suppose we add `__suppress_init_subclass__` as an named
> parameter to
> `type.__new__` - what would happen with this argument in
> `__init_subclass__` ? Would it show
> up in the kwargs? Otherwise it would be the _only_  kwarg popped out and
> not passed
> to __init_subclass__, being an inconvenient exception.
>
> Having an overridable, separate, method in type to run __init_subclass__
> and __set_name__
> bypass these downsides.
>
> In time, Happy holidays everyone!
>
>    js
>  -><-
>
> On Fri, 25 Dec 2020 at 00:38, Ethan Furman <et...@stoneleaf.us> wrote:
>
>> PEP 487 introduced __init_subclass__ and __set_name__, and both of those
>> were wins for the common cases of metaclass usage.
>>
>> Unfortunately, the implementation of PEP 487 with regards to
>> __init_subclass__ has made the writing of correct
>> metaclasses significantly harder, if not impossible.
>>
>> The cause is that when a metaclass calls type.__new__ to actually create
>> the class, type.__new__ calls the
>> __init_subclass__ methods of the new class' parents, passing it the newly
>> created, but incomplete, class.  In code:
>>
>> ```
>> class Meta(type):
>>      #
>>      def __new__(mcls, name, bases, namespace, **kwds):
>>          # create new class, which will call __init_subclass__ and
>> __set_name__
>>          new_class = type.__new__(mcls, name, bases, namespace, **kwds)
>>          # finish setting up class
>>          new_class.some_attr = 9
>> ```
>>
>> As you can deduce, when the parent __init_subclass__ is called with the
>> new class, `some_attr` has not been added yet --
>> the new class is incomplete.
>>
>> For Enum, this means that __init_subclass__ doesn't have access to the
>> new Enum's members (they haven't beet added yet).
>>
>> For ABC, this means that __init_subclass__ doesn't have access to
>> __abstract_methods__ (it hasn't been created yet).
>>
>> Because Enum is pure Python code I was able to work around it:
>> - remove new __init_subclass__ (if it exists)
>> - insert dummy class with a no-op __init_subclass__
>> - call type.__new__
>> - save any actual __init_subclass__
>> - add back any new __init_subclass__
>> - rewrite the new class' __bases__, removing the no-op dummy class
>> - finish creating the class
>> - call the parent __init_subclass__ with the now complete Enum class
>>
>> I have not been able to work around the problem for ABC.
>>
>> Two possible solutions I can think of:
>>
>> - pass a keyword argument to type.__new__ that suppresses the call to
>> __init_subclass__; and
>> - provide a way to invoke new class' parent's __init_subclass__ before
>> returning it
>>
>> or
>>
>> - instead of type.__new__ doing that work, have type.__init__ do it.
>>
>> Thoughts?
>>
>> --
>> ~Ethan~
>> _______________________________________________
>> Python-Dev mailing list -- python-dev@python.org
>> To unsubscribe send an email to python-dev-le...@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/ZMRRNSFSLJZDGGZ66CFCYQBINU62CDNX/
>> Code of Conduct: http://python.org/psf/codeofconduct/
>>
>
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@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/NUOMIKVCJMS6S5UUSPDIGD2HD3L56N2F/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to