I wrote a metaclass to handle the creation of two classes at class definition (with one accessible as an attribute of the other).
MRE with logic removed:
class MyMeta(type):
def __new__(mcs, clsname, bases, namespace, **kwds):
result = super().__new__(mcs, clsname, bases, namespace)
# Omitted logic to prefix namespace
print(namespace)
# namespace.pop('__classcell__', None)
disambiguated_cls = super().__new__(mcs, clsname + 'Prefixed', bases, namespace)
result.disambiguated_cls = disambiguated_cls
return result
class A(metaclass=MyMeta):
@classmethod
def f(cls, x):
return x + 1
class B(A):
@classmethod
def f(cls, x):
# return A.f(x)
return super().f(x)
Anytime super is called (e.g. in class B definition) a __classcell__ object is added to the namespace. If that __classcell__ object is duplicated (e.g. in the creation of disambiguated_cls) the following exception is raised:
TypeError: __class__ set to <class '__main__.B'> defining 'B' as <class '__main__.B'>
Looks like there are two solutions (corresponding to the two commented lines in the example):
- Don't call super (just directly call function on the superclass:
A.f(x)) - Pop the
__classcell__out of the namespace
The first means manual resolution of superclasses and I'm not sure what downstream effects the latter can have.
For reference the docs on __classcell__ in metaclasses state:
In CPython 3.6 and later, the
__class__cell is passed to the metaclass as a__classcell__ entry in the class namespace. If present, this must be propagated up to thetype.__new__call in order for the class to be initialised correctly. Failing to do so will result in a RuntimeError in Python 3.8.
Is there another option? What is the best way to do this?