14

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?

Dimitris Fasarakis Hilliard
  • 119,766
  • 27
  • 228
  • 224
Dima Tisnek
  • 9,367
  • 4
  • 48
  • 106
  • Some ideas: this patch has the right key words (I dunno if this patch made it or not): http://bugs.python.org/file44533/classcell.patch – Dima Tisnek Dec 27 '16 at 10:21

1 Answers1

13

The warning is raised if you use super 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__.

Dimitris Fasarakis Hilliard
  • 119,766
  • 27
  • 228
  • 224
  • It took me a while to find out it is created if any method inside the class uses the `super()` form. This comes likely because before it was impossible to call any classmethod that would make use of `super()` inside the metaclass methods itself. More likely, the new `__init_subclass__` mechansm in Python 3.6 needs it. – jsbueno Dec 27 '16 at 15:22
  • @jsbueno yup, `__init_subclass__` with a custom metaclass, though. If you use `type` you're fine. – Dimitris Fasarakis Hilliard Dec 27 '16 at 22:11
  • @JimFasarakis-Hilliard could you provide a reference or a simple example to illustrate custom namespace? – Dima Tisnek Dec 29 '16 at 13:23