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-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/ZMRRNSFSLJZDGGZ66CFCYQBINU62CDNX/
Code of Conduct: http://python.org/psf/codeofconduct/
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-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/ZMRRNSFSLJZDGGZ66CFCYQBINU62CDNX/
Code of Conduct: http://python.org/psf/codeofconduct/