This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author josh.r
Recipients bup, josh.r, ncoghlan, xiang.zhang
Date 2017-03-30.16:05:06
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1490889906.75.0.313319829897.issue29944@psf.upfronthosting.co.za>
In-reply-to
Content
It looks like this is a general problem caused by the fact that a function that is:

1. Defined in a class
2. References the name "super" (even if it's a local variable name, even if it's never called)

isn't "just" a plain function. Even though Python 3 officially removed the concept of unbound methods, there is still some unbound method-like behavior going on, which makes that sort of function intrinsically tied to where it executes.

Specifically, a function of that sort is effectively a closure, where the closure scope defines __class__ (insert.__code__.co_freevars will be non-empty with the string '__class__' in it); the value of that closure variable cell is set when the class finishes being defined (that is, the closure cell is empty when the def block finishes, but when the class block itself completes, the cell is populated with a reference to the class).

No argument super (specifically, super_init) relies on this assistance, as it looks up the __class__ cell in the caller's frame when not provided a class explicitly. In your repro, the problem is that __class__ is set to the original MyList (it was set at the moment the class MyList(list): block finished, before the decorator was invoked), but at call time, the self instance is of the new type you created (which has the same name, but the name isn't looked up, it has a cached reference to the class).

The same problem applies if you do:

class MyList(list):
    def insert(self, index, object):
        super().insert(index, object)

class MyOtherList(list):
    insert = MyList.insert

because MyList.insert is permanently tied to MyList, it doesn't get "reclosured" as a result of MyOtherList using it. Similarly:

class MyList(list):
    pass

def insert(self, index, object):
    super().insert(index, object)

MyList.insert = insert

fails when insert is called with error: "RuntimeError: super(): __class__ cell not found", because functions def-ed outside a class block entirely don't have __class__ as "virtual closure" scope.

It's arguable whether this should be changed: If you're using super() without arguments, the implicit behavior is that it works with "whatever class I'm currently defining", so trying to reuse it with some other class is trying to extend that implicit behavior to "whatever class I was eventually assigned to". Making it work would mean that already "class bound" methods must be rebound when assigned to a new class, and non-bound methods must be bound (in both cases, you'd need to handle this when implicitly assigned in the class body, or explicitly assigned later as an attribute on the constructed class).

Doing so would break at least one pattern someone might be using already, where they have multiple inheritance, and while most methods should prioritize the first class in the MRO, they want a specific method to bypass one or more classes in the MRO without calling it. For example:

class Stuff:  # Top of diamond, just so super().stuff() valid in all children
    def stuff(self):
        print("Stuff")

class Spam:
    def stuff(self):
        print("Spam")
        super().stuff()

class Eggs:
    def stuff(self):
        print("Eggs")
        super().stuff()

class SpamAndEggsWithoutSpam(Spam, Eggs):
    stuff = Eggs.stuff  # I'd like my stuff without Spam

Under the current design, Eggs.stuff is already bound to Eggs, so this works; you see:

Eggs
Stuff

printed, but not Spam. If rebinding were made a thing, you'd end up with:

Eggs
Spam
Eggs
Stuff

with the first Eggs being from the rebound method.
History
Date User Action Args
2017-03-30 16:05:06josh.rsetrecipients: + josh.r, ncoghlan, xiang.zhang, bup
2017-03-30 16:05:06josh.rsetmessageid: <1490889906.75.0.313319829897.issue29944@psf.upfronthosting.co.za>
2017-03-30 16:05:06josh.rlinkissue29944 messages
2017-03-30 16:05:06josh.rcreate