Передача данных из дочернего процесса в родительский процесс, канал не работает в Windows

Цель состоит в том, чтобы передать некоторые данные (несколько байтов) из дочернего процесса в родительский процесс в Python (stdout и stderr использовать нельзя). Ниже приведена его упрощенная версия (фактический код считывает все данные из канала перед блокировкой в ​​подпроцессе).

import os
import subprocess
import sys

CHILD_SCRIPT = r'''
import os
os.write({wfd}, b'Hello, World!')
'''

rfd, wfd = os.pipe()
if hasattr(os, 'set_inheritable'):
    os.set_inheritable(wfd, True)
subprocess.check_call([sys.executable, '-c', CHILD_SCRIPT.format(wfd=wfd)])
print(os.read(rfd, 1024))

В Unix он работает на Python 2, но не на Python 3, где происходит сбой:

Traceback (most recent call last):
  File "<string>", line 3, in <module>
OSError: [Errno 9] Bad file descriptor
Traceback (most recent call last):
  File "test_inherit_fd.py", line 13, in <module>
    subprocess.check_call([sys.executable, '-c', CHILD_SCRIPT.format(wfd=wfd)])
  File "/usr/lib/python3.8/subprocess.py", line 364, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['/bin/python3', '-c', "\nimport os\nos.write(5, b'Hello, World!')\n"]' returned non-zero exit status 1.

По-видимому, сбой не связан с тем, наследуется ли FD. В Python 3.2 (где FD по-прежнему наследовались по умолчанию) он не работает так же. Что еще вызывает разницу? (EDIT: причина в том, что, начиная с Python 3.2, FD выше 2 закрыты по умолчанию. Проблема может быть решена путем передачи close_fds=False).

В Windows он не работает на Python 2 и Python 3.

Какой надежный и чистый способ передачи данных (несколько байтов) от дочернего к родительскому, который работает на Python 2 и Python 3 на всех платформах?


person Manuel Jacob    schedule 08.07.2020    source источник


Ответы (2)


Из документации subprocess:

Если close_fds имеет значение true, все файловые дескрипторы, кроме 0, 1 и 2, будут закрыты перед выполнением дочернего процесса. В противном случае, когда close_fds имеет значение false, дескрипторы файлов подчиняются своему флагу наследования, как описано в Наследование файловых дескрипторов.

pass_fds – это необязательная последовательность файловых дескрипторов, которая должна оставаться открытой между родительским и дочерним элементами.

Чтобы это работало, добавьте close_fds=False или pass_fds=[wfd] в качестве аргумента к subprocess.check_call. Быстрый тест показывает, что если вы используете pass_fds, вам больше не понадобится вызов set_inheritable, но это неверно, если вы используете close_fds.

person Joseph Sible-Reinstate Monica    schedule 10.07.2020

Я думаю, что вы ищете multiprocessing.Queue.

Python 3: https://docs.python.org/3/library/multiprocessing.html#exchange-objects-between-processes

Python 2: https://docs.python.org/2/library/multiprocessing.html#exchange-objects-between-processes

Вы также можете использовать канал (из той же библиотеки), но он намного медленнее, чем очередь: Многопроцессорная обработка Python 3.4 Очередь быстрее, чем Pipe, неожиданно

РЕДАКТИРОВАТЬ

Как это решает вопрос?

Итак, вы пытаетесь выполнить код в дочернем процессе, и вам нужен способ передать данные родительскому процессу. Если вы посмотрите документацию, вы увидите такой фрагмент кода:

from multiprocessing import Process, Queue

def f(q):
    q.put([42, None, 'hello'])

if __name__ == '__main__':
    q = Queue()
    p = Process(target=f, args=(q,))
    p.start()
    print(q.get())    # prints "[42, None, 'hello']"
    p.join()

В данном случае f — это код, который будет выполняться дочерним процессом. Вы можете делать там все, что хотите! Сделайте функцию на 1000 строк. Создайте класс, создайте его экземпляр и сходите с ума. Измените имя с f на что-нибудь осмысленное. Поместите эту функцию в другой файл и импортируйте ее, а затем запустите ее выполнение.

person Mike Sandford    schedule 08.07.2020