Невозможно раскрыть выражение Pyparsing с помощью метода setParseAction(). Требуется для многопроцессорной обработки

Моя первоначальная проблема в том, что я пытаюсь сделать следующее:

def submit_decoder_process(decoder, input_line):
    decoder.process_line(input_line)
    return decoder

self.pool = Pool(processes=num_of_processes)
self.pool.apply_async(submit_decoder_process, [decoder, input_line]).get()

декодер немного задействован для описания здесь, но важно то, что декодер является объектом, который инициализируется выражением PyParsing, которое вызывает setParseAction(). Это не позволяет определить, что использует многопроцессорность, и это, в свою очередь, не позволяет выполнить вышеуказанный код.

Теперь вот проблема рассола /PyParsing, которую я выделил и упростил. Следующий код дает сообщение об ошибке из-за сбоя рассола.

import pickle
from pyparsing import *

def my_pa_func():
    pass

pickle.dumps(Word(nums).setParseAction(my_pa_func))

Сообщение об ошибке:

pickle.PicklingError: Can't pickle <function wrapper at 0x00000000026534A8>: it not found as pyparsing.wrapper

Теперь, если вы удалите вызов .setParseAction(my_pa_func), он будет работать без проблем:

pickle.dumps(Word(nums))

Как я могу обойти это? Многопроцессор использует рассол, поэтому я не могу этого избежать. Патсовый пакет, который предположительно использует укроп, недостаточно зрелый, по крайней мере, у меня возникают проблемы с его установкой на моем Windows-64bit. Я действительно почесываю голову здесь.

Ответ 1

ОК, вот решение, вдохновленное rockportrocker: Ошибка трассировки Python для мультипроцессора

Идея состоит в том, чтобы укротить объект, который не может быть маринован при передаче его назад и вперед между процессами, а затем "не допустить" его после того, как он был передан:

from multiprocessing import Pool
import dill

def submit_decoder_process(decoder_dill, input_line):
    decoder = dill.loads(decoder_dill)  # undill after it was passed to a pool process
    decoder.process_line(input_line)
    return dill.dumps(decoder)  # dill before passing back to parent process

self.pool = Pool(processes=num_of_processes)

# Dill before sending to a pool process
decoder_processed = dill.loads(self.pool.apply_async(submit_decoder_process, [dill.dumps(decoder), input_line]).get())

Ответ 2

https://docs.python.org/2/library/pickle.html#what-can-be-pickled-and-unpickled

Многопроцессорный .Pool использует протокол Pickle для сериализации имен функций и модулей (в вашем примере setParseAction и pyparse), которые доставляются через Pipe к дочернему процессу.

Детский процесс, после его получения, импортирует модуль и пытается вызвать функцию. Проблема в том, что то, что вы проходите, не является функцией, а методом. Чтобы решить эту проблему, протокол Pickle должен быть достаточно умен, чтобы создать объект "Word" с параметром "user", а затем вызвать метод setParseAction. Поскольку обработка этих случаев слишком сложна, протокол Pickle предотвращает сериализацию функций верхнего уровня.

Чтобы решить вашу проблему, вы инструктируете модуль Pickle о том, как сериализовать метод setParseAction (https://docs.python.org/2/library/pickle.html#pickle-protocol), или вы реорганизуете свой код в способ, которым передается пул Pool.apply_async является сериализуемым.

Что делать, если вы передаете объект Word дочернему процессу, и вы позволите ему вызвать Word(). setParseAction()?

Ответ 3

Я бы предложил pathos.multiprocessing, как вы упомянули. Конечно, я автор pathos, поэтому я думаю, что это не сюрприз. Похоже, что может быть ошибка distutils, с которой вы работаете, как указано здесь: https://github.com/uqfoundation/pathos/issues/49.

Ваше решение, использующее dill, является хорошим обходным решением. Вы также можете отказаться от установки всего пакета pathos и просто установить pathos fork пакета multiprocessing (который использует dill вместо pickle). Вы можете найти его здесь: http://dev.danse.us/packages или здесь: https://github.com/uqfoundation/pathos/tree/master/external,