ipython: autoreload magic doesn't reload objects

update: the problem appears to be with any object, unrelated to pickle. pickle was just exposing it in a forceful way. Skip to the latest diagnostic https://github.com/ipython/ipython/issues/11588#issuecomment-464949800

when autoreload reloads a python module, it appears to not be fully reloading the already instantiated objects, and pickle fails to pickle them with:

Can't pickle <class 'mytestclass.Test'>: it's not the same object as mytestclass.Test

Here is a notebook to reproduce, must be run in 3 parts (or 3 cells):

# cell1
%reload_ext autoreload
%autoreload 2

import pickle

file = "mytestclass.py"

text = """
class Test():
    def __init__(self):
        self.ok = 1
"""
with open("mytestclass.py","w") as f: f.write(text)

from mytestclass import *
test = Test()

# works
p = pickle.dumps(test)
# cell2
# touch forces autoreloader to reload the library file
!echo touch $file
!touch $file
# cell3
# now it fails
p = pickle.dumps(test)

The error is:

In [6]: p = pickle.dumps(test)
---------------------------------------------------------------------------
PicklingError                             Traceback (most recent call last)
<ipython-input-6-740c326b650e> in <module>()
----> 1 p = pickle.dumps(test)

PicklingError: Can't pickle <class 'mytestclass.Test'>: it's not the same object as mytestclass.Test

Please note that it should be run in 3 parts - if it’s run in one go the reload effect won’t be activated. Can be run in jupyter notebook or directly in ipython shell.

I traced why pickle fails and expanded on it in this issue https://github.com/fastai/fastai/issues/1493 - tldr; it compares memory addresses of the object’s class and the class in sys.modules - if their memory addresses don’t match it refuses to pickle.

I think there are other issues with the incomplete reload, (i.e. object methods disappearing after reload), but I’m yet to make reproducible cases for those. I believe pickle is just exposing a deeper problem.

I tried the git master ipython and went all the way back to ipython-6.0.0 - the problem is the same.

And yes, I read the caveats section, that does say that reload isn’t 100% perfect.

Thank you.

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 5
  • Comments: 22 (9 by maintainers)

Most upvoted comments

I have looked into this a little. From what I see, only classes and functions within a reloaded module are updated, but instances of these classes are not touched.

Updating these would be relatively straight forward setting instance.__class__ = new_class with new_class being the updated class reference.

Finding these instances is more complicated. The only way I can think of right now is passing globals() to the update function and iterate through all objects in the workspace (and recursively through all nested objects like lists, dicts etc.), checking whether isinstance(obj, old_class) and then doing the above update.

Although I believe this would be the most logical behavior for %autoreload, I am not sure of the performance costs.

Any other ideas?

So happy you noticed that change, @jdanbrown!

Indeed, both test cases that were failing: https://github.com/ipython/ipython/issues/11588#issuecomment-464949800 https://github.com/ipython/ipython/issues/11588#issue-405929887 no longer fail with ipython-7.9.0.

I’m not sure whether more tests are needed. I will start using the %autoreload feature again and see if I notice any more glitches.

Thanks, I think it would make sens to try to fix this behavior and have autoreload not break pickle. I’m unsure how easy it would be.

Hmm, was this maybe fixed in 7.9.0 by https://github.com/ipython/ipython/pull/11876? I run into this error all the time in my notebook workflows, and upgrading from ipython 7.7.0 to 7.11.1 appears to have fixed one instance where the error was occurring.

Does anyone have a robust set of repros that we can test?

FWIW, I tested @stas00’s code and it works in IPython 7.2.

Hum, so now I’m unsure. I rarely use it. But if it was doing it then:

  1. there should be a test for it
  2. it’s likely easier than I though to fix it 😃

Yes, I do not believe autoreload update the current class of existing objects. I’m sure it is possible in Python, but I don’t think we even try to do it. We could look up the MRO and modify it, but that might mean messing around with Ctypes.