ipython: Fix for infinite recursion in autoreload

I have a class which memoizes contains a link to itself as an attribute. This is used as a to ensure that a class specific memoized computation is unique and not inherited from bases.

It turns out that this causes an infinite recursion between update_generic and update_class in autoreload.

This is fixed with a quick check to prevent this case. added after the “prevent recursion” comment.

Furthermore in this code, the for loop also loops on “new” object attributes, which will fix #8771

def update_class(old, new):
    """Replace stuff in the __dict__ of a class, and upgrade
    method code objects"""
    #for key in list(old.__dict__.keys()):
    for key in set(list(old.__dict__.keys()) + list(new.__dict__.keys())):
        old_obj = getattr(old, key)

        try:
            new_obj = getattr(new, key)
        except AttributeError:
            # obsolete attribute: remove it
            try:
                delattr(old, key)
            except (AttributeError, TypeError):
                pass
            continue

        #prevent recursion 
        if old_obj not in [old, new] and new_obj not in [old, new]:
            if update_generic(old_obj, new_obj): continue

        try:
            setattr(old, key, getattr(new, key))
        except (AttributeError, TypeError):
            pass # skip non-writable attributes

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 20 (11 by maintainers)

Commits related to this issue

Most upvoted comments

I was doing something a bit more strange to trigger it. It is a bit of a metaclass emulating behavior that needs some notion whether or not the logic has been run. In my case I was memoizing some class-specific values. This should be able to trigger it.

class A(object):
    _clsetup = None

    @classmethod
    def clsetup_do(cls):
        pass #do some kind of unique class-specific setup

    def clsetup_check(self):
        if self._clsetup is not self.__class__:
             self.clsetup_do()
             self.__class__._clsetup = self.__class__

    def __init__(self):
         self.clsetup_check()