Per the 3.6.0 docs:
CPython implementation detail: 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 the type.__new__
call in order for the class to be initialized correctly. Failing to do
so will result in a DeprecationWarning
in Python 3.6, and a
RuntimeWarning
in the future.
Can someone provide an example of doing this correctly?
An example where it's actually needed?
The warning is raised if you use the zero argument super super().__method__(args)
that relies on __class__
being available or reference __class__
inside the class body.
What the text essentially says is that, this is needed if you define a custom meta-class and tamper with the namespace you get before passing it up to type.__new__
. You'll need to be careful and always make sure you pass __classcell__
to type.__new__
in your metaclass.__new__
.
That is, if you create a new fancy namespace to pass up, always check if __classcell__
is defined in the original namespace created and add it:
class MyMeta(type):
def __new__(cls, name, bases, namespace):
my_fancy_new_namespace = {....}
if '__classcell__' in namespace:
my_fancy_new_namespace['__classcell__'] = namespace['__classcell__']
return super().__new__(cls, name, bases, my_fancy_new_namespace)
The file you linked in the comment is actually the first of many attempted patches, issue23722_classcell_reference_validation_v2.diff
is the final patch that made it in, from Issue 23722.
An example of doing this correctly can be seen in a pull request made to Django that uses this to fix an issue that was introduced in Python 3.6:
new_attrs = {'__module__': module}
classcell = attrs.pop('__classcell__', None)
if classcell is not None:
new_attrs['__classcell__'] = classcell
new_class = super_new(cls, name, bases, new_attrs)
The __classcell__
is simply added to the new namespace before being passed to type.__new__
.