如何异步捕获长时间运行的Python子进程

时间:2017-12-06 10:08:59

标签: python asynchronous subprocess stdout

对于测试我想在Linux上启动一个长期运行的Python 3脚本,捕获它的一些输出,检查它是否符合预期并再次杀死它。

我只是想让普通框架现在正常运作。

当我运行以下代码时,我希望输出包含line = "0" - python脚本只打印出一个递增的整数序列,每秒一个。相反,完全跳过for循环。

import subprocess
from tempfile import NamedTemporaryFile
import time
import unittest

import asynchronousfilereader

class TestProcessSpawning(unittest.TestCase):
    def test_spawning_counter(self):
        counter_code = \
"""
import time
i = 0
while True:
    print(i)
    i = i + 1
    time.sleep(1)
"""

        with NamedTemporaryFile(mode="w", suffix='.py', delete=False)\
                as temp_file:
            temp_file.write(counter_code)
            file_name = temp_file.name

            # proc = subprocess.Popen(['ping', 'localhost'],
            #                         stdout=subprocess.PIPE, close_fds=True)
            proc = subprocess.Popen(['python3', file_name],
                                    stdout=subprocess.PIPE, close_fds=True)

            time.sleep(3)

            assert proc.returncode is None  # None => still running

            reader = asynchronousfilereader.AsynchronousFileReader(proc.stdout)

            time.sleep(3)  # give it a chance?

            for line in reader.readlines():
                print('line = "{}"'.format(line))
                break  # just grab first one

            proc.kill()

但是,如果我将['python3', file_name]更改为['ping', 'localhost']从ping获取一行输出(我在Linux上运行,因此ping继续生成输出,直到你停止它。)

知道为什么这似乎适用于其他类型的子进程而不适用于python?

注意:

  • 此代码使用pip
  • 提供的asynchronousfilereader
  • 如果我只是在shell中运行python3 <temp_file_name>,脚本会按预期运行,打印0,1,2,......
  • 几秒钟后进程返回代码None表明进程已运行(即不只是崩溃)
  • 我按照建议here试图通过unbuffer运行它:输出没有显示
  • 我尝试将bufsize=1传递给Popen:输出未显示
  • 如果我包含-v标志,我会得到很多输出,包括这个,这表明python试图以交互方式运行,即使我提供了一个文件名:
  

Python 3.5.2(默认,2017年11月23日,16:37:01)

     

[GCC 5.4.0 20160609] on linux

     

输入“帮助”,“版权”,“信用”或“许可”以获取更多信息。

1 个答案:

答案 0 :(得分:1)

您需要刷新您编写的文件

with NamedTemporaryFile(mode="w", suffix='.py', delete=False) as temp_file:
    temp_file.write(counter_code)
    temp_file.flush()

你的程序太短,以至于无法将其从缓冲区中取出,否则你正在运行一个空文件。

我还要确保程序在每次写入时都会刷新;对于未缓冲的标准输出运行python3 -u或在每次打印时使用flush=True。这样,您就不必在父程序中添加这么多睡眠。

通过这些更改,单元测试打印

line = "b'0\n'"

以及有关子进程仍在运行且stdout缓冲区在完成之前仍处于打开状态的几个警告。