I created a metaclass that defines the __prepare__ method, which is supposed to consume a specific keyword in the class definition, like this:
class M(type):
@classmethod
def __prepare__(metaclass, name, bases, **kwds):
print('in M.__prepare__:')
print(f' {metaclass=}\n {name=}\n'
f' {bases=}\n {kwds=}\n {id(kwds)=}')
if 'for_prepare' not in kwds:
return super().__prepare__(name, bases, **kwds)
arg = kwds.pop('for_prepare')
print(f' arg popped for prepare: {arg}')
print(f' end of prepare: {kwds=} {id(kwds)=}')
return super().__prepare__(name, bases, **kwds)
def __new__(metaclass, name, bases, ns, **kwds):
print('in M.__new__:')
print(f' {metaclass=}\n {name=}\n'
f' {bases=}\n {ns=}\n {kwds=}\n {id(kwds)=}')
return super().__new__(metaclass, name, bases, ns, **kwds)
class A(metaclass=M, for_prepare='xyz'):
pass
When I run it, the for_prepare keyword argument in the definition of class A reappears in __new__ (and later in __init_subclass__, where it causes an error):
$ python3 ./weird_prepare.py
in M.__prepare__:
metaclass=<class '__main__.M'>
name='A'
bases=()
kwds={'for_prepare': 'xyz'}
id(kwds)=140128409916224
arg popped for prepare: xyz
end of prepare: kwds={} id(kwds)=140128409916224
in M.__new__:
metaclass=<class '__main__.M'>
name='A'
bases=()
ns={'__module__': '__main__', '__qualname__': 'A'}
kwds={'for_prepare': 'xyz'}
id(kwds)=140128409916224
Traceback (most recent call last):
File "./weird_prepare.py", line 21, in <module>
class A(metaclass=M, for_prepare='xyz'):
File "./weird_prepare.py", line 18, in __new__
return super().__new__(metaclass, name, bases, ns, **kwds)
TypeError: __init_subclass__() takes no keyword arguments
As you can see the for_prepare item is removed from the dict, and the dict that is passed to __new__ is the same object that was passed to __prepare__ and the same object that the for_prepare item was popped from, but in __new__ it reappeared! Why does a keyword that was deleted from the dict get added back in?