Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IPython autoreload gives error for repeated calls to Python2 super()

I am prototyping stuff in an IPython notebook that is loading Python2 code from modules on my computer. I activated reloading magic commands to make it easier to go back-and-forth between my text editor and the notebook when I make a code change and re-run a cell to see its affect:

%reload_ext autoreload
%autoreload 2

I am working in Python 2.7.10 because I working with some legacy code that doesn't compile for 3. Part of my work is extending some classes in this legacy code and overloading some of their methods. But, I also need to call some of the original base methods to do important work. For example:

class LegacyBase:

    def important_method(self):
        #old stuff i'll need

class NewClass(LegacyBase):

    #overload base method
    def important_method(self):
       #do some new stuff

       while 1:
           #call old method to do stuff
           super(NewClass, self).important_method() #use old Python2 super calls :(

           #some break conditions

When I call important_method() with some NewClass instance the first time in my notebook (meaning, after a kernel reset) it runs fine. The loop is such that the super call is happening more than once! No errors

But, if I go and modify some code to my new method in my text editor and go back to the IPython cell and call it again I get the following error at the line in my overloaded important_method() where the super call is made.

TypeError: super(type, obj): obj must be an instance or subtype of type

Note: I tried just naming my new method a different name because I thought it was something to do with the overloading method calling itself again but that didn't help. Also, I want them to be the same name because this is an API method and I want users of the legacy code to be able to call the same methods they know from before.

Any idea how to use reloading in IPython notebooks with these Python2 super calls?

Thanks!

like image 983
kilgoretrout Avatar asked Oct 20 '22 01:10

kilgoretrout


2 Answers

Your existing instances are still pointing to the old class from before the reload.

You need to either re-create the instances, or update their __class__ attribute:

instance_of_newclass.__class__ = NewClass
like image 112
Martijn Pieters Avatar answered Oct 24 '22 20:10

Martijn Pieters


Reproducing the problem

I use a module reload_test.py modeled after your example:

class LegacyBase:

    def important_method(self):
        print 'doing'

class NewClass(LegacyBase):

    def important_method(self):
        for x in range(3):
            super(NewClass, self).important_method()

In my IPython Notebook:

In [1]: %reload_ext autoreload
        %autoreload 2

In [2]: import reload_test

In [3]: nc = reload_test.NewClass()

Calling a method throws this exception:

In [4]: nc.important_method()

    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-5-09eb0eac244b> in <module>()
    ----> 1 nc.important_method()

    /Users/mike/tmp/reload_test.py in important_method(self)
          9 
         10        for x in range(3):
    ---> 11            super(NewClass, self).important_method()

    TypeError: must be type, not classobj

The class LegacyBase needs to inherit from object:

class LegacyBase(object):

    def important_method(self):
        print 'doing'

class NewClass(LegacyBase):

    def important_method(self):
        for x in range(3):
            super(NewClass, self).important_method()

Now, I can reproduce your problem. Works the first time:

In [5]: nc = reload_test.NewClass()

In [6]: nc.important_method()
    doing
    doing
    doing

After modifying the file in an editor:

class LegacyBase(object):

    def important_method(self):
        print 'doing'

class NewClass(LegacyBase):

    def important_method(self):
        for x in range(3):
            super(NewClass, self).important_method()

Calling the methods throws an exception:

In [7]: nc.important_method()

     ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-8-09eb0eac244b> in <module>()
    ----> 1 nc.important_method()

    /Users/mike/tmp/reload_test.py in important_method(self)
          9 
         10        for x in range(3):
    ---> 11            super(NewClass, self).important_method()

    TypeError: super(type, obj): obj must be an instance or subtype of type

Solution

Just make a new instance:

In [8]: nc = reload_test.NewClass()

In [9]: nc.important_method()
    doing 2
    doing 2
    doing 2

In practice this means just re-executing the cell that holds nc = reload_test.NewClass().

like image 37
Mike Müller Avatar answered Oct 24 '22 19:10

Mike Müller