[solved - ItemBase issue] Custom ItemList : RecursionError: maximum recursion depth exceeded while calling a Python object

Hi,

I’ve tried using the custom ImageTupleList which is introduced in this tutorial.

I am seeing RecursionError: maximum recursion depth exceeded.

Here is the code:

class ImageTuple(ItemBase):
    def __init__(self, img1, img2):
        self.img1,self.img2 = img1,img2
        self.obj,self.data = (img1,img2),[-1+2*img1.data,-1+2*img2.data]

    def apply_tfms(self, tfms, **kwargs):
        self.img1 = self.img1.apply_tfms(tfms, **kwargs)
        self.img2 = self.img2.apply_tfms(tfms, **kwargs)
        self.data = [-1+2*self.img1.data,-1+2*self.img2.data]
        return self
    
    def to_one(self): 
        return Image(0.5+torch.cat(self.data,2)/2)

class ImageTupleList(ImageItemList):
#     _label_cls=TargetTupleList
    def __init__(self, items, itemsB=None, **kwargs):
        self.itemsB = itemsB
        super().__init__(items, **kwargs)
    
    def new(self, items, **kwargs):
        return super().new(items, itemsB=self.itemsB, **kwargs)
    
    def get(self, i):
        img1 = super().get(i)
        fn = self.itemsB[random.randint(0, len(self.itemsB)-1)]
        return ImageTuple(img1, open_image(fn))
    
    def reconstruct(self, t:Tensor): 
        return ImageTuple(Image(t[0]/2+0.5),Image(t[1]/2+0.5))
    
    @classmethod
    def from_folders(cls, path, folderA, folderB, **kwargs):
        itemsB = ImageItemList.from_folder(path/folderB).items
        res = super().from_folder(path/folderA, itemsB=itemsB, **kwargs)
        res.path = path
        return res

# list of fnames
itemsA = ImageItemList.from_df(train_df_dummy, cols=['ImageA'], path=data_path, folder='cropped_training').items
itemsB = ImageItemList.from_df(train_df_dummy, cols=['ImageB'], path=data_path, folder='cropped_training').items

ImageTupleList(itemsA, itemsB)


---------------------------------------------------------------------------
RecursionError                            Traceback (most recent call last)
~/anaconda3/lib/python3.7/site-packages/IPython/core/formatters.py in __call__(self, obj)
    700                 type_pprinters=self.type_printers,
    701                 deferred_pprinters=self.deferred_printers)
--> 702             printer.pretty(obj)
    703             printer.flush()
    704             return stream.getvalue()

~/anaconda3/lib/python3.7/site-packages/IPython/lib/pretty.py in pretty(self, obj)
    381                 if cls in self.type_pprinters:
    382                     # printer registered in self.type_pprinters
--> 383                     return self.type_pprinters[cls](obj, self, cycle)
    384                 else:
    385                     # deferred printer

~/anaconda3/lib/python3.7/site-packages/IPython/lib/pretty.py in _repr_pprint(obj, p, cycle)
    693     """A pprint that just redirects to the normal repr function."""
    694     # Find newlines and replace them with p.break_()
--> 695     output = repr(obj)
    696     for idx,output_line in enumerate(output.splitlines()):
    697         if idx:

~/anaconda3/lib/python3.7/site-packages/fastai/data_block.py in __repr__(self)
     60     def __repr__(self)->str:
     61         items = [self[i] for i in range(min(5,len(self.items)))]
---> 62         return f'{self.__class__.__name__} ({len(self.items)} items)\n{items}...\nPath: {self.path}'
     63 
     64     def process(self, processor=None):

~/anaconda3/lib/python3.7/site-packages/fastai/core.py in __repr__(self)
    148     "Base item type in the fastai library."
    149     def __init__(self, data:Any): self.data=self.obj=data
--> 150     def __repr__(self): return f'{self.__class__.__name__} {self}'
    151     def show(self, ax:plt.Axes, **kwargs):
    152         "Subclass this method if you want to customize the way this `ItemBase` is shown on `ax`."

... last 1 frames repeated, from the frame below ...

~/anaconda3/lib/python3.7/site-packages/fastai/core.py in __repr__(self)
    148     "Base item type in the fastai library."
    149     def __init__(self, data:Any): self.data=self.obj=data
--> 150     def __repr__(self): return f'{self.__class__.__name__} {self}'
    151     def show(self, ax:plt.Axes, **kwargs):
    152         "Subclass this method if you want to customize the way this `ItemBase` is shown on `ax`."

RecursionError: maximum recursion depth exceeded

What might be the reason to this?

Thanks

1 Like

Problem occurs when implementing a custom ItemBase by inheriting it and it’s due to function __repr__:

__repr__ is defined as:

def __repr__(self): return f'{self.__class__.__name__} {self}'

I assume that {self} again calls __repr__ and it causes the infinite recursion.

As a solution I overwrite __repr__:

def __repr__(self):
     return f'{self.__class__.__name__}{(self.img1.shape, self.img2.shape)}'
     

Which will result in :

ImageTuple(torch.Size([3, 337, 699]), torch.Size([3, 337, 699]))

So maybe this warning should be made to the user, when inheriting from ItemBase, they should re-implement __repr__.

Similarly initializing ItemBase class will result in same error:

ItemBase([1])

I didn’t create a PR for the docs since there might be better solution or something that I am missing.

3 Likes

This seems weird and definitely not wanted, will have a look.

1 Like

Just a reminder, I tried this tutorial and this still seems to be the case?

That would be because I forgot to have a look…

2 Likes

Okay, removed __repr__ altogether to use the default of python when there isn’t one overwritten.

1 Like

I’m still getting the same error on version 1.0.52 when running the custom ItemList as defined here
I’m just testing it out before changing it for my own purposes, I have a train and valid folder each with a single image.

Code:

path_to_folders = Path(’…/temp/’)
path_to_train = Path(‘train/’)
path_to_valid = Path(‘valid/’)

sample = (ImageTupleList.from_folders(path_to_folders, path_to_train, path_to_valid))
sample = (sample.split_none()
.label_from_folder()
.databunch(bs=1))

Error Snippet:

/opt/anaconda3/lib/python3.6/site-packages/fastai/core.py in repr(self)
154 “Base item type in the fastai library.”
155 def init(self, data:Any): self.data=self.obj=data
–> 156 def repr(self)->str: return f’{self.class.name} {str(self)}’
157 def show(self, ax:plt.Axes, **kwargs):
158 “Subclass this method if you want to customize the way this ItemBase is shown on ax.”

RecursionError: maximum recursion depth exceeded while calling a Python object

Adding the following to ImageTuple fixed it

def __repr__(self):
    return f'{self.__class__.__name__}{(self.img1.shape, self.img2.shape)}'

I just ran into the same error with version 1.0.57. I was defining a custom image base class (corresponding to ImageTuple in the docs example) and a custom imageList class (corresponding to the ImageTupleLIst). As others said, the key was writing __repr__, but I just wanted to add that the error is caused by not having __repr__ defined in the base class. The custom ImageList class didn’t cause errors without it.

It solved my problem in this case, thanks @kcturgutlu .

This is some out off-topic but yesterday, when I was working on my project that it “print anything just swapping from your phone” and my coding, is almost finished but I don’t know what happened but I test it yesterday and my printer got stuck and my printer is in an error state now and I don’t understand the reason. Does anyone have any idea how can I solve this?