-
Notifications
You must be signed in to change notification settings - Fork 715
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
Passing loguru function to multiprocessing #102
Comments
Wow, thanks a lot for such a good minimal reproducible example! I will investigate this and give an update on what is causing this weird bug. Maybe is it related to my usage of |
I don't know what is happening, but it seems defining a function in the main scope that wraps the call to logger.info works in this case. def log(message):
logger.info(message) with multiprocessing.Pool(1) as pool:
for my_id in range(5):
pool.apply_async(do_something, (my_id, log))
pool.close()
pool.join() Using a lambda or defining it in a local scope doesn't work |
Thanks for this interesting workaround, @AnesBenmerzoug. I suppose the |
I managed to reduce the test case to just this: import pickle
from loguru import logger
pickle.loads(pickle.dumps(logger.info)) or this (to mimic more closely what is happening inside apply_async): from multiprocessing import SimpleQueue
from loguru import logger
q = SimpleQueue()
q.get(q.put(logger.info)) It seems apply_async puts the given arguments pickled by a ForkingPickler in a multiprocessing.SimpleQueue object. |
import pickle
class Test:
def log(self, *args, **kwargs):
print("oups wrong method...")
@staticmethod
def _make_function1():
def log(_self, x):
print("local function 1: " + str(x))
return log
@staticmethod
def _make_function2():
def log_function(_self, x):
print("local function 2: " + str(x))
return log_function
info1 = _make_function1.__func__()
info2 = _make_function2.__func__()
if __name__ == "__main__":
test = Test()
# Method info1 works
test.info1("test1 before")
func = pickle.loads(pickle.dumps(test.info1))
func("test1 after")
# Method info2 fails
test.info2("test2 before")
func = pickle.loads(pickle.dumps(test.info2))
func("test2 after") It seems that by defining a normal method that has the same name as the local function defined in _make_log_function even if it does nothing allows us to pickle the other methods. Admittedly this would feel more like a hack than an actual solution. EDIT: local function 1: test1 before
oups wrong method...
local function 2: test2 before
Traceback (most recent call last):
File "test.py", line 33, in <module>
func = pickle.loads(pickle.dumps(test.info2))
AttributeError: 'Test' object has no attribute 'log_function' |
Again, thanks a lot for helping me understanding the issue, @AnesBenmerzoug! 👍 Python seems to have problem pickling closure functions inside others functions. Probably related question on SO: Python: pickling nested functions. The problem with adding a no-op function with the same name is that it makes I guess I will need to refactor the |
@Delgan you're welcome. |
Ok, I fixed this by refactoring the The fix will be available in the next |
When passing a
logger.info
into amultiprocessing.Pool
, like as following:the following exception occurs:
A fully reproducible test case is here
This is using Python 3.7.3 via pyenv in Ubuntu 18.04
The text was updated successfully, but these errors were encountered: