2025, Dec 18 00:01

Почему multiprocessing не работает в макросах LibreOffice и чем его заменить

Почему multiprocessing в Python‑макросах LibreOffice падает с ошибкой pickling, что мешает запуску на Windows, и как заменить процессы потоками в макросах.

Запуск multiprocessing Python внутри макроса LibreOffice кажется заманчивым, пока интерпретатор не напомнит, где на самом деле живёт этот код. В этой среде код макроса подгружается в модуль, который LibreOffice использует внутри своего фреймворка, и это меняет правила сериализации объектов и запуска процессов. В итоге при попытке стартовать новый процесс из макроса возникает на первый взгляд странный сбой pickling.

Как воспроизвести проблему

Следующий минимальный макрос записывает файл и затем пытается выполнить ту же работу в отдельном процессе. Структура простая, но она провоцирует ошибку сериализации, связанную с моделью исполнения макросов.

import multiprocessing as mproc
import time
def write_stub():
    with open("./test.txt", "w") as out_f:
        out_f.write("toto")
def entry_macro():
    proc = mproc.Process(write_stub)
    proc.start()
    time.sleep(1)
    proc.stop()

Сбой проявляется сообщением вроде:

"Can't pickle <function task at 0x...>: import of module 'ooo_script_framework' failed"

Функция, которую вы собирались запустить в дочернем процессе, воспринимается как часть модуля, хостимого LibreOffice, а не как обычная функция верхнего уровня. После этого при инициализации процесса её просто не удаётся импортировать обратно.

Что происходит под капотом

LibreOffice исполняет Python‑макросы, загружая их в модуль, используемый его скриптовым фреймворком. На практике ваша функция — как бы она ни называлась — становится членом этого хост‑модуля. Когда multiprocessing пытается породить новый процесс, он сериализует целевую функцию и ожидает, что дочерний процесс её импортирует. Теперь путь импорта указывает на модуль LibreOffice, и в новом контексте интерпретатора этот импорт проваливается, что и приводит к видимой вам ошибке pickling.

Есть ещё один нюанс в Windows. Модуль multiprocessing должен запускаться за стандартной защитой if __name__ == "__main__":, но макросы так не исполняются, поэтому привычная для multiprocessing логика точки входа отсутствует.

Практическое решение в макросе: используйте потоки

В макросах threading.Thread работает надёжно и обходится без сложностей с сериализацией и путями импорта модулей, потому что потоки разделяют один интерпретатор и общее состояние модулей. Замените multiprocessing на threading — это прагматичный способ выполнять фоновые задачи в окружении макросов LibreOffice.

import threading as th
import time
def write_stub():
    with open("./test.txt", "w") as out_f:
        out_f.write("toto")
def entry_macro():
    worker = th.Thread(target=write_stub)
    worker.start()
    time.sleep(1)

Такой подход согласуется с тем, как исполняются макросы, и полностью устраняет проблему импорта в дочернем процессе. В качестве примера посмотрите образец, демонстрирующий работу потоков в этом контексте: https://stackoverflow.com/a/35853062/5100564.

Почему это важно

Важно понимать, что среда выполнения макросов меняет то, как Python видит структуру ваших модулей. Попытки «спрятать» целевую функцию от хост‑модуля не помогут: интерпретатор всё равно считает её частью скриптового модуля LibreOffice, а multiprocessing попытается сериализовать и импортировать её именно оттуда. Осознание того, что на Windows макросы не запускаются под if __name__ == "__main__":, дополнительно объясняет, почему multiprocessing плохо вписывается в этот сценарий.

Есть и более общий аспект производительности. В данном случае переход на потоки оправдан, потому что задача связана с вводом‑выводом. В целом же многопоточность не является универсальной заменой многопроцессности.

Выводы

Если вам нужна фоновая работа внутри макроса LibreOffice, отдайте предпочтение потокам, а не процессам. Они органично вписываются в среду макросов, избавляют от ошибок сериализации, связанных со скриптовым модулем, и не зависят от защитной конструкции __main__ в Windows. При этом учитывайте характер нагрузки: для I/O‑связанных задач потоки обычно подходят; за их пределами потоки не всегда станут полноценной заменой процессам.