подпроцесс python, ожидающий внука в Windows с установленным stdout - PullRequest
0 голосов
/ 14 марта 2019

У меня есть скрипт, который является частью автоматизированного набора тестов.Он работает очень медленно в Windows, но не в Linux, и я выяснил, почему.Процесс, который мы тестируем («откровенный»), создает дочерний процесс (то есть внука).Код Python не вернется, пока этот процесс внука также не закончится (в Windows - не делает этого в Linux).Процесс внука убьет себя через 5 секунд, если нет родителя (он зависает в случае, если с ним разговаривает другой процесс)

Я обнаружил, что могу прекратить зависание функции связи таким образом, если яне захватывать стандартный выводНо мне нужен стандартный вывод.Я где-то читал, что функция связи ожидает закрытия всех каналов.Я знаю, что дескриптор stdout дублирован для внука, но я не могу изменить код, который я тестирую.

Я искал решение.Я попробовал некоторые флаги создания (все еще в коде), но это не помогло.

Это сокращенный тест -

import os
import sys
import threading
import subprocess


def read_from_pipe(process):
    last_stdout = process.communicate()[0]
    print (last_stdout)

CREATE_NEW_PROCESS_GROUP = 0x00000200
DETACHED_PROCESS = 0x00000008

# start process
command = 'frank my arguments'

cwd = "C:\\dev\\ui_test\\frank_test\\workspace\\report183"

p = subprocess.Popen(command,
                     stdout=subprocess.PIPE,
                     cwd=cwd)

# run thread to read from output
t = threading.Thread(target=read_from_pipe, args=[p])
t.start()
t.join(30)
print('finished')

Есть идеи?

Спасибо.

Питер.

1 Ответ

0 голосов
/ 15 марта 2019

После советов от @eryksun и большого количества Google, у меня есть довольно сложный код!В какой-то момент я подумал о мошенничестве и использовании os.system и перенаправлении во временный файл, но потом понял, что наш тестовый код допускает тайм-аут команды.os.system просто заблокирует навсегда, если дочерний процесс не умрет.

import os
import sys
import threading
import subprocess
import time
if os.name == 'nt':
    import msvcrt
    import ctypes

# See /9549700/podprotsess-python-ozhidayschii-vnuka-v-windows-s-ustanovlennym-stdout for details on Windows code
# Based on https://github.com/it2school/Projects/blob/master/2017/Python/party4kids-2/CarGame/src/pygame/tests/test_utils/async_sub.py

from ctypes.wintypes import DWORD
if sys.version_info >= (3,):
    null_byte = '\x00'.encode('ascii')
else:
    null_byte = '\x00'

def ReadFile(handle, desired_bytes, ol = None):
    c_read = DWORD()
    buffer = ctypes.create_string_buffer(desired_bytes+1)
    success = ctypes.windll.kernel32.ReadFile(handle, buffer, desired_bytes, ctypes.byref(c_read), ol)
    buffer[c_read.value] = null_byte
    return ctypes.windll.kernel32.GetLastError(), buffer.value


def PeekNamedPipe(handle):
    c_avail = DWORD()
    c_message = DWORD()
    success = ctypes.windll.kernel32.PeekNamedPipe(handle, None, 0, None, ctypes.byref(c_avail), ctypes.byref(c_message))
    return "", c_avail.value, c_message.value


def read_available(handle):
    buffer, bytesToRead, result = PeekNamedPipe(handle)
    if bytesToRead:
        hr, data = ReadFile(handle, bytesToRead, None)
        return data
    return b''

def read_from_pipe(process):
    if os.name == 'posix':
        last_stdout = process.communicate()[0]
    else:
        handle = msvcrt.get_osfhandle(process.stdout.fileno())
        last_stdout = b''
        while process.poll() is None:
            last_stdout += read_available(handle)
            time.sleep(0.1)
        last_stdout += read_available(handle)
    print (last_stdout)

# start process
command = 'frank my arguments'

cwd = "C:\\dev\\ui_test\\frank_test\\workspace\\report183"

p = subprocess.Popen(command,
                     stdout=subprocess.PIPE,
                     cwd=cwd)

# run thread to read from output
t = threading.Thread(target=read_from_pipe, args=[p])
t.start()
t.join(30)
print('finished')
...