uqfoundation / pathos

parallel graph management and execution in heterogeneous computing
http://pathos.rtfd.io
Other
1.38k stars 89 forks source link

Getting "TypeError: can't pickle _cffi_backend.FFI objects" on "results.get()" #251

Closed SohanTirpude closed 2 years ago

SohanTirpude commented 2 years ago

Hello all,

When I tried to run below code I am getting "TypeError: can't pickle _cffi_backend.FFI objects" on "results.get()".

from pathos.pools import ProcessPool

with ProcessPool(nodes=os.cpu_count()) as pool:
    results = pool.amap(self._some_function, some_parameters)
    while not results.ready():
        sleep(5)
print("result_list: ", results.get())

And this is the error log

print("result_list: ", results.get())
  File "/home/some_user/python_envs/env/lib64/python3.6/site-packages/multiprocess/pool.py", line 644, in get
    raise self._value
  File "/home/some_user/python_envs/env/lib64/python3.6/site-packages/multiprocess/pool.py", line 424, in _handle_tasks
    put(task)
  File "/home/some_user/python_envs/env/lib64/python3.6/site-packages/multiprocess/connection.py", line 209, in send
    self._send_bytes(_ForkingPickler.dumps(obj))
  File "/home/some_user/python_envs/env/lib64/python3.6/site-packages/multiprocess/reduction.py", line 54, in dumps
    cls(buf, protocol, *args, **kwds).dump(obj)
  File "/home/some_user/python_envs/env/lib64/python3.6/site-packages/dill/_dill.py", line 498, in dump
    StockPickler.dump(self, obj)
  File "/usr/lib64/python3.6/pickle.py", line 409, in dump
    self.save(obj)
  File "/usr/lib64/python3.6/pickle.py", line 476, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib64/python3.6/pickle.py", line 751, in save_tuple
    save(element)
  File "/usr/lib64/python3.6/pickle.py", line 476, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib64/python3.6/pickle.py", line 736, in save_tuple
    save(element)
  File "/usr/lib64/python3.6/pickle.py", line 476, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib64/python3.6/pickle.py", line 736, in save_tuple
    save(element)
  File "/usr/lib64/python3.6/pickle.py", line 476, in save
    f(self, obj) # Call unbound method with explicit self
  File "/home/some_user/python_envs/env/lib64/python3.6/site-packages/dill/_dill.py", line 1496, in save_function
    obj.__dict__, fkwdefaults), obj=obj)
  File "/usr/lib64/python3.6/pickle.py", line 610, in save_reduce
    save(args)
  File "/usr/lib64/python3.6/pickle.py", line 476, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib64/python3.6/pickle.py", line 751, in save_tuple
    save(element)
  File "/usr/lib64/python3.6/pickle.py", line 476, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib64/python3.6/pickle.py", line 736, in save_tuple
    save(element)
  File "/usr/lib64/python3.6/pickle.py", line 476, in save
    f(self, obj) # Call unbound method with explicit self
  File "/home/some_user/python_envs/env/lib64/python3.6/site-packages/dill/_dill.py", line 1227, in save_cell
    pickler.save_reduce(_create_cell, (f,), obj=obj)
  File "/usr/lib64/python3.6/pickle.py", line 610, in save_reduce
    save(args)
  File "/usr/lib64/python3.6/pickle.py", line 476, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib64/python3.6/pickle.py", line 736, in save_tuple
    save(element)
  File "/usr/lib64/python3.6/pickle.py", line 476, in save
    f(self, obj) # Call unbound method with explicit self
  File "/home/some_user/python_envs/env/lib64/python3.6/site-packages/dill/_dill.py", line 1176, in save_instancemethod0
    pickler.save_reduce(MethodType, (obj.__func__, obj.__self__), obj=obj)
  File "/usr/lib64/python3.6/pickle.py", line 610, in save_reduce
    save(args)
  File "/usr/lib64/python3.6/pickle.py", line 476, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib64/python3.6/pickle.py", line 736, in save_tuple
    save(element)
  File "/usr/lib64/python3.6/pickle.py", line 521, in save
    self.save_reduce(obj=obj, *rv)
  File "/usr/lib64/python3.6/pickle.py", line 634, in save_reduce
    save(state)
  File "/usr/lib64/python3.6/pickle.py", line 476, in save
    f(self, obj) # Call unbound method with explicit self
  File "/home/some_user/python_envs/env/lib64/python3.6/site-packages/dill/_dill.py", line 990, in save_module_dict
    StockPickler.save_dict(pickler, obj)
  File "/usr/lib64/python3.6/pickle.py", line 821, in save_dict
    self._batch_setitems(obj.items())
  File "/usr/lib64/python3.6/pickle.py", line 847, in _batch_setitems
    save(v)
  File "/usr/lib64/python3.6/pickle.py", line 521, in save
    self.save_reduce(obj=obj, *rv)
  File "/usr/lib64/python3.6/pickle.py", line 634, in save_reduce
    save(state)
  File "/usr/lib64/python3.6/pickle.py", line 476, in save
    f(self, obj) # Call unbound method with explicit self
  File "/home/some_user/python_envs/env/lib64/python3.6/site-packages/dill/_dill.py", line 990, in save_module_dict
    StockPickler.save_dict(pickler, obj)
  File "/usr/lib64/python3.6/pickle.py", line 821, in save_dict
    self._batch_setitems(obj.items())
  File "/usr/lib64/python3.6/pickle.py", line 847, in _batch_setitems
    save(v)
  File "/usr/lib64/python3.6/pickle.py", line 521, in save
    self.save_reduce(obj=obj, *rv)
  File "/usr/lib64/python3.6/pickle.py", line 634, in save_reduce
    save(state)
  File "/usr/lib64/python3.6/pickle.py", line 476, in save
    f(self, obj) # Call unbound method with explicit self
  File "/home/some_user/python_envs/env/lib64/python3.6/site-packages/dill/_dill.py", line 990, in save_module_dict
    StockPickler.save_dict(pickler, obj)
  File "/usr/lib64/python3.6/pickle.py", line 821, in save_dict
    self._batch_setitems(obj.items())
  File "/usr/lib64/python3.6/pickle.py", line 847, in _batch_setitems
    save(v)
  File "/usr/lib64/python3.6/pickle.py", line 521, in save
    self.save_reduce(obj=obj, *rv)
  File "/usr/lib64/python3.6/pickle.py", line 634, in save_reduce
    save(state)
  File "/usr/lib64/python3.6/pickle.py", line 476, in save
    f(self, obj) # Call unbound method with explicit self
  File "/home/some_user/python_envs/env/lib64/python3.6/site-packages/dill/_dill.py", line 990, in save_module_dict
    StockPickler.save_dict(pickler, obj)
  File "/usr/lib64/python3.6/pickle.py", line 821, in save_dict
    self._batch_setitems(obj.items())
  File "/usr/lib64/python3.6/pickle.py", line 852, in _batch_setitems
    save(v)
  File "/usr/lib64/python3.6/pickle.py", line 521, in save
    self.save_reduce(obj=obj, *rv)
  File "/usr/lib64/python3.6/pickle.py", line 634, in save_reduce
    save(state)
  File "/usr/lib64/python3.6/pickle.py", line 476, in save
    f(self, obj) # Call unbound method with explicit self
  File "/home/some_user/python_envs/env/lib64/python3.6/site-packages/dill/_dill.py", line 990, in save_module_dict
    StockPickler.save_dict(pickler, obj)
  File "/usr/lib64/python3.6/pickle.py", line 821, in save_dict
    self._batch_setitems(obj.items())
  File "/usr/lib64/python3.6/pickle.py", line 847, in _batch_setitems
    save(v)
  File "/usr/lib64/python3.6/pickle.py", line 521, in save
    self.save_reduce(obj=obj, *rv)
  File "/usr/lib64/python3.6/pickle.py", line 634, in save_reduce
    save(state)
  File "/usr/lib64/python3.6/pickle.py", line 476, in save
    f(self, obj) # Call unbound method with explicit self
  File "/home/some_user/python_envs/env/lib64/python3.6/site-packages/dill/_dill.py", line 990, in save_module_dict
    StockPickler.save_dict(pickler, obj)
  File "/usr/lib64/python3.6/pickle.py", line 821, in save_dict
    self._batch_setitems(obj.items())
  File "/usr/lib64/python3.6/pickle.py", line 847, in _batch_setitems
    save(v)
  File "/usr/lib64/python3.6/pickle.py", line 521, in save
    self.save_reduce(obj=obj, *rv)
  File "/usr/lib64/python3.6/pickle.py", line 634, in save_reduce
    save(state)
  File "/usr/lib64/python3.6/pickle.py", line 476, in save
    f(self, obj) # Call unbound method with explicit self
  File "/home/some_user/python_envs/env/lib64/python3.6/site-packages/dill/_dill.py", line 990, in save_module_dict
    StockPickler.save_dict(pickler, obj)
  File "/usr/lib64/python3.6/pickle.py", line 821, in save_dict
    self._batch_setitems(obj.items())
  File "/usr/lib64/python3.6/pickle.py", line 847, in _batch_setitems
    save(v)
  File "/usr/lib64/python3.6/pickle.py", line 521, in save
    self.save_reduce(obj=obj, *rv)
  File "/usr/lib64/python3.6/pickle.py", line 634, in save_reduce
    save(state)
  File "/usr/lib64/python3.6/pickle.py", line 476, in save
    f(self, obj) # Call unbound method with explicit self
  File "/home/some_user/python_envs/env/lib64/python3.6/site-packages/dill/_dill.py", line 990, in save_module_dict
    StockPickler.save_dict(pickler, obj)
  File "/usr/lib64/python3.6/pickle.py", line 821, in save_dict
    self._batch_setitems(obj.items())
  File "/usr/lib64/python3.6/pickle.py", line 852, in _batch_setitems
    save(v)
  File "/usr/lib64/python3.6/pickle.py", line 521, in save
    self.save_reduce(obj=obj, *rv)
  File "/usr/lib64/python3.6/pickle.py", line 634, in save_reduce
    save(state)
  File "/usr/lib64/python3.6/pickle.py", line 476, in save
    f(self, obj) # Call unbound method with explicit self
  File "/home/some_user/python_envs/env/lib64/python3.6/site-packages/dill/_dill.py", line 990, in save_module_dict
    StockPickler.save_dict(pickler, obj)
  File "/usr/lib64/python3.6/pickle.py", line 821, in save_dict
    self._batch_setitems(obj.items())
  File "/usr/lib64/python3.6/pickle.py", line 847, in _batch_setitems
    save(v)
  File "/usr/lib64/python3.6/pickle.py", line 521, in save
    self.save_reduce(obj=obj, *rv)
  File "/usr/lib64/python3.6/pickle.py", line 634, in save_reduce
    save(state)
  File "/usr/lib64/python3.6/pickle.py", line 476, in save
    f(self, obj) # Call unbound method with explicit self
  File "/home/some_user/python_envs/env/lib64/python3.6/site-packages/dill/_dill.py", line 990, in save_module_dict
    StockPickler.save_dict(pickler, obj)
  File "/usr/lib64/python3.6/pickle.py", line 821, in save_dict
    self._batch_setitems(obj.items())
  File "/usr/lib64/python3.6/pickle.py", line 847, in _batch_setitems
    save(v)
  File "/usr/lib64/python3.6/pickle.py", line 496, in save
    rv = reduce(self.proto)
TypeError: can't pickle _cffi_backend.FFI objects

I cannot share the code due to organization guidelines. Without code if no one can help me then let me know, I'll close this issue.

Thank you.

mmckerns commented 2 years ago

I would guess, from the traceback, that you are trying to use a class method in the map and not a function. That's ok, but generally causes the class instance to also be serialized. The traceback shows that right away, dill hands off the object to pickle, and then pickle fails to serialize it. A small bit of googling shows that _cffi_backend is actually _cffi_backend.so and thus not python code. So, the class will fail to serialize, unless a __reduce__ method (or similar) is defined in the class. You can try avoiding the serialization of the class by using other serialization variants (e.g. dill.settings['recurse'] = True or dill.settings['byref'] = True), but I assume these will also fail. You could write your own custom class that wraps the class method and provides a __reduce__ method, or potentially a custom function. Hard to say much more without more information. You can, potentially, post the versions of pathos, multiprocess, dill, CFFI, and etc you are using, and also the pickle traceback (e.g. use dill.detect.trace(True)) before running your code.

SohanTirpude commented 2 years ago

Hey @mmckerns , thanks for the analysis. So far I have checked throughout my codebase but I could not find the CFFI module anywhere. Hence now I am confused about this error. But after sometime I found out that the Paramiko library uses FFI and this might be the issue in my case. But I am not sure.

And I tried with dill.settings but it didn't work as you expected. Anyway, this is versions of pathos, multiprocess, dill and cffi.

>>> import pathos
>>> pathos.__version__
'0.2.8'
>>> import multiprocess
>>> multiprocess.__version__
'0.70.12.2'
>>> import dill
>>> dill.__version__
'0.3.4'
>>> import cffi
>>> cffi.__version__
'1.15.0'
mmckerns commented 2 years ago

So, it looks like your best route might be to build a custom class with a reduce method. Here's a super simple one, as an example:

Python 3.7.14 (default, Sep 10 2022, 11:17:06) 
[Clang 10.0.1 (clang-1001.0.46.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import _cffi_backend as _cffi
>>> class FFI(_cffi.FFI):
...   def __reduce__(self):
...     return (self.__class__, ()) # tuple of args to pass to __init__
... 
>>> f = FFI()
>>> import dill
>>> g = dill.loads(dill.dumps(f))

Something to note is that if you are able to discover the appropriate tuple of args that preserves the state of the FFI object, then you essentially have all the information you need to build a custom serialization function which you can then use with copy_reg to register the serialization function.

SohanTirpude commented 2 years ago

So that means, I'll have to make changes somewhere in library files where instead of using _cffi_backend object directly, I'll have to extend the _cffi_backend class and override the __reduce__ method of it and will have to make sure that this extended class will be used in the pickle.py file. Kindly correct me if I am wrong.

I checked in the files, but could not find the __reduce__ or __reduce__ex_ method of _cff_backend class.

mmckerns commented 2 years ago

You will either have to extend (or derive a class from) the _cffi_backend class to have a __reduce__ method... or you'll need to register a reduction method using copy_reg. Both are equivalent. I'm going to close this, but feel free to open it if you feel this needs more discussion.