classification
Title: Multiprocessing module cannot call instance methods across processes
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 2.7
process
Status: closed Resolution: wont fix
Dependencies: Superseder:
Assigned To: Nosy List: alexandre.vassalotti, asksol, dimitrios, jnoller, serhiy.storchaka
Priority: normal Keywords:

Created on 2010-12-16 14:08 by dimitrios, last changed 2015-09-22 18:38 by serhiy.storchaka. This issue is now closed.

Files
File name Uploaded Description Edit
issue10717.py alexandre.vassalotti, 2013-12-01 23:19
Messages (4)
msg124133 - (view) Author: Dimitrios Pritsos (dimitrios) Date: 2010-12-16 14:08
My name is Dimitrios and I am newbie in python. I am working on a Project (part of my PhD) that is called Synergeticprocessing module. Initially is imitating the multiprocessing built in module but the processes are distributed on a LAN and not Locally. The main issue I have is with Pickle module. And I think I found some kind of BUG in the built in multiprocessing module.

(Synergeticprocessing module is located at GitHub: https://github.com/dpritsos/synergeticprocessing)

Starting with the "BUG". In case someone uses the multiprocessing.Pool of processes he/she has to face the problem of types.MehtodType Impossible pickling. That is you cannot dispatch an class instance method to the to the Process Pool. However, digging in to the Source Code of the module there are few lines that resolve this issue however are not activated or they are faultily activated so they do not work. This is the 'BUG'

@ ....../multiprocessing/forking.py


.
.
.

#
# Try making some callable types picklable
#

from pickle import Pickler
class ForkingPickler(Pickler):
    dispatch = Pickler.dispatch.copy()

    @classmethod
    def register(cls, type, reduce):
        def dispatcher(self, obj):
            rv = reduce(obj)
            self.save_reduce(obj=obj, *rv)
        cls.dispatch[type] = dispatcher

def _reduce_method(m):
    if m.im_self is None:
        return getattr, (m.im_class, m.im_func.func_name)
    else:
        return getattr, (m.im_self, m.im_func.func_name)
ForkingPickler.register(type(ForkingPickler.save), _reduce_method)

def _reduce_method_descriptor(m):
    return getattr, (m.__objclass__, m.__name__)
ForkingPickler.register(type(list.append), _reduce_method_descriptor)
ForkingPickler.register(type(int.__add__), _reduce_method_descriptor)

#def _reduce_builtin_function_or_method(m):
#    return getattr, (m.__self__, m.__name__)
#ForkingPickler.register(type(list().append), _reduce_builtin_function_or_method)
#ForkingPickler.register(type(int().__add__), _reduce_builtin_function_or_method)
.
.
.

The RED lines are not doing the job, for some reason they are not managing to register the GREEN function as a global reduce/pickling function even if you call the registration function into you __main__ script.

The solution I found is just to do this

import copy_reg
import types

def _reduce_method(m):
    if m.im_self is None:
        return getattr, (m.im_class, m.im_func.func_name)
    else:
        return getattr, (m.im_self, m.im_func.func_name)

copy_reg.pickle(types.MethodType, _reduce_method)
.
.
.

Doing that everything works FINE. But ONLY for local methods i.e. the ones that their class is defined on the __main__ script or other import-ed.

In case you want to send something remotely (in an other machine) or to an other __main__ script running separately then you get a message like this:

'module' object has no attribute '<my_class>'

The only way to resolve this is firstly to import a script that has <my_class> defined there and everything works fine.

SO the problems it seems to be that the m.im_class  (look code above) has some attribute __module__ defined as __module__ = '__main__' or something like that. And this is the reason why remote script cannot execute the function. I mean that the _reduce_method() above DOES is pickling the whole CLASS object so there is no reason not to be executed at the remote script. Besides it does as mentioned above in you just import this the user defined class form an other script.


I have already spent about 12 weeks working on building my synergeticPool and resolve the issue of Pickling and only 2 days needed for the code of the Pool and the rest of the time was spent for the Pickling issues, and study all the Class related mechanics of python. That was the reason I ve started digging the multipocessessing module and found this say 'BUG', and finally sent this email.

Best Regards,


Dimitrios
msg204974 - (view) Author: Alexandre Vassalotti (alexandre.vassalotti) * (Python committer) Date: 2013-12-01 22:42
Without a reproducible test case, I am afraid there is nothing we can do here.
msg204977 - (view) Author: Alexandre Vassalotti (alexandre.vassalotti) * (Python committer) Date: 2013-12-01 23:19
Look like I isolated the problem. It seems multiprocessing is using cPickle which cannot be extended with ForkingPickler, unlike the Python version of the pickle module.

15:09:29 [ ~/pythondev/python2.7 ]$ ./python.exe issue10717.py
Traceback (most recent call last):
  File "issue10717.py", line 8, in <module>
    p.apply(Test().hello, ("jimbo",))
  File "/Users/avassalotti/PythonDev/python2.7/Lib/multiprocessing/pool.py", line 244, in apply
    return self.apply_async(func, args, kwds).get()
  File "/Users/avassalotti/PythonDev/python2.7/Lib/multiprocessing/pool.py", line 558, in get
    raise self._value
cPickle.PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup __builtin__.instancemethod failed
msg235494 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2015-02-06 20:09
I think this issue should be closed as "won't fix". In Python 3 both Pickler implementations have dispatch_table, but in Python 2 there is no way to use private dispatch table. The solution is to migrate to Python 3.
History
Date User Action Args
2015-09-22 18:38:10serhiy.storchakasetstatus: pending -> closed
resolution: wont fix
stage: needs patch -> resolved
2015-02-06 20:09:25serhiy.storchakasetstatus: open -> pending
nosy: + serhiy.storchaka
messages: + msg235494

2013-12-01 23:59:52alexandre.vassalottisettitle: Multiprocessing module Pickling unPickling issues -> Multiprocessing module cannot call instance methods across processes
2013-12-01 23:19:21alexandre.vassalottisetstatus: closed -> open
files: + issue10717.py
messages: + msg204977

assignee: alexandre.vassalotti ->
resolution: works for me -> (no value)
stage: test needed -> needs patch
2013-12-01 22:42:02alexandre.vassalottisetstatus: open -> closed

assignee: alexandre.vassalotti
components: + Library (Lib), - None
versions: - Python 3.1, Python 3.2
nosy: + alexandre.vassalotti

messages: + msg204974
resolution: works for me
stage: test needed
2010-12-16 14:12:24pitrousetnosy: + asksol, jnoller

versions: + Python 3.1, Python 2.7, Python 3.2, - Python 2.6
2010-12-16 14:08:57dimitrioscreate