Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

skimage.measure._regionprops.RegionProperties is not serializable. RecursionError is thrown from __getattr__ #6465

Open
anirudh44 opened this issue Aug 11, 2022 · 2 comments

Comments

@anirudh44
Copy link

anirudh44 commented Aug 11, 2022

Description

In latest version of RegionProperties class, getattr is implemented. This function goes into recursive call when it is serialized and deserialized. while deserializing, setstate is called before init. Since setstate is not available, getattr is called, and since init has not yet been called, _extra_properties attribute is not defined which ends up calling getattr multiple times until recursion limit is reached.

Way to reproduce

import numpy as np
import os
from skimage import measure
import pickle

def create_fake_label_image():
    all_ones = np.full((5,5), 1, dtype=int)
    all_twos = np.full((5,5), 2, dtype=int)
    all_zero = np.full((5,5), 0, dtype=int)

    return np.concatenate((all_ones, all_zero, all_twos))

def regionProp_serialization_demo():

    #generate region props from fake data
    label_img = create_fake_label_image()
    regions = measure.regionprops(label_img)

    #pickle and unpickle
    pickled = pickle.dumps(regions[0])
    un_pickled = pickle.loads(pickled) #RecursionError here



if __name__ == "__main__":
    
    regionProp_serialization_demo()

Output

File "array_reader.py", line 32, in <module>
    regionProp_serialization_demo()
  File "array_reader.py", line 26, in regionProp_serialization_demo
    un_pickled = pickle.loads(pickled) #RecursionError here
  File "/usr/local/lib/python3.8/site-packages/skimage/measure/_regionprops.py", line 323, in __getattr__
    if attr in self._extra_properties:
  File "/usr/local/lib/python3.8/site-packages/skimage/measure/_regionprops.py", line 323, in __getattr__
    if attr in self._extra_properties:
  File "/usr/local/lib/python3.8/site-packages/skimage/measure/_regionprops.py", line 323, in __getattr__
    if attr in self._extra_properties:
  [Previous line repeated 994 more times]
RecursionError: maximum recursion depth exceeded

Workaround solution

This is the workaround I am thinking to use for now. I am new to python, can someone please review and let me know if this is the best way or if there is any other better way. A cleaner solution in the package would be the best.

Create a derived class that does not allow recursive call in getattr and use the derived class in client code

class SerializableRegionProperties(measure._regionprops.RegionProperties):

    def __init__(self, slice = None, label = None, label_image = None, intensity_image = None, cache_active = None,extra_properties=None):
        super().__init__(slice, label, label_image, intensity_image, cache_active, extra_properties = extra_properties)
        self._initialized = True
    
    def __getattr__(self, attr):
        if(attr == "_initialized" or not self._initialized):
            self.__init__()
        
        super().__getattr__(attr)

Version information

3.8.10 (default, Jul  3 2022, 03:19:42) 
[GCC 9.3.0]
Linux-4.12.14-150.47-default-x86_64-with-glibc2.29
scikit-image version: 0.19.3
numpy version: 1.19.5
@lagru
Copy link
Member

lagru commented Sep 21, 2022

Sorry for the late response @anirudh44 and thank you for reporting this. You write-up and supplied code was very helpful! I can reproduce this.

While your suggested solution might do the trick I think a simpler solution is to use object.__getattribute__(self, "_extra_properties) instead of self._extra_properties. That way we can keep the fix to the actual function and don't need to track the state of an additional attribute. To be safe, object.__getattribute__ should probably be used for each access of instance attributes in that function.

Would you like to submit a PR for this? 😁

@github-actions
Copy link

Hey, there hasn't been any activity on this issue for more than 180 days. For now, we have marked it as "dormant" until there is some new activity. You are welcome to reach out to people by mentioning them here or on our forum if you need more feedback! If you think that this issue is no longer relevant, you may close it by yourself; otherwise, we may do it at some point (either way, it will be done manually). In any case, thank you for your contributions so far!

@github-actions github-actions bot added the 😴 Dormant no recent activity label Mar 21, 2023
@lagru lagru added the 🐛 Bug label Sep 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants