python print end 堵塞问题以及如何非堵塞读取subprocess的所有输出做到实时读取

python print end

如下代码:

    for i in range(5):
        time.sleep(1)
        print(i, end='')

本来想要的效果是每秒输出,但是发现这样写会等所有循环完毕后才会打印,发现需要使用flush参数来立即输出,正确代码如下:

    for i in range(5):
        time.sleep(1)
        print(i, end='', flush=True)

实时读取subprocess的输出

linux

fcntl库似乎在windows中使用有问题,待找其他库代替,下面代码是基于linux系统

如何实时读取subprocess的输出是一个困扰我很久的问题,最近终于得到了解决,之前是使用subprocess的readline(),但是如果那一行仍未运行完时还是会堵塞,无法做到真正的实时读取,多方调查发现配合fcntl库可以做到,方法如下:

import fcntl
import subprocess
import os
import time

p = subprocess.Popen('ping www.baidu.com', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

# 避免阻塞
def non_block_read(output):
    fd = output.fileno()
    fl = fcntl.fcntl(fd, fcntl.F_GETFL)
    fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
    try:
        return output.read()
    except:
        return ""

# 当程序未运行结束时输出
while p.poll() is None:
    out = non_block_read(p.stdout)
    if out != None:
        outstr = out.decode('utf8')
        print(outstr, end='', flush=True)

windows 可能有用,需验证,在airtest源码中发现

# _*_ coding:UTF-8 _*_
import time
from threading import Thread, Event
from six.moves import queue
from .logger import get_logger
LOGGING = get_logger(__name__)


class NonBlockingStreamReader:

    def __init__(self, stream, raise_EOF=False, print_output=True, print_new_line=True, name=None, auto_kill=False):
        '''
        stream: the stream to read from.
                Usually a process' stdout or stderr.
        raise_EOF: if True, raise an UnexpectedEndOfStream
                when stream is EOF before kill
        print_output: if True, print when readline
        '''
        self._s = stream
        self._q = queue.Queue()
        self._lastline = None
        self.name = name or id(self)

        def _populateQueue(stream, queue, kill_event):
            '''
            Collect lines from 'stream' and put them in 'queue'.
            '''
            while not kill_event.is_set():
                line = stream.readline()
                if line is not None:
                    queue.put(line)
                    if print_output:
                        # print only new line
                        if print_new_line and line == self._lastline:
                            continue
                        self._lastline = line
                        LOGGING.debug("[%s]%s" % (self.name, repr(line.strip())))
                    if auto_kill and line == b"":
                        self.kill()
                elif kill_event.is_set():
                    break
                elif raise_EOF:
                    raise UnexpectedEndOfStream
                else:
                    break

        self._kill_event = Event()
        self._t = Thread(target=_populateQueue, args=(self._s, self._q, self._kill_event), name="nbsp_%s"%self.name)
        self._t.daemon = True
        self._t.start()  # start collecting lines from the stream

    def readline(self, timeout=None):
        try:
            return self._q.get(block=timeout is not None, timeout=timeout)
        except queue.Empty:
            return None

    def read(self, timeout=0):
        time.sleep(timeout)
        lines = []
        while True:
            line = self.readline()
            if line is None:
                break
            lines.append(line)
        return b"".join(lines)

    def kill(self):
        self._kill_event.set()


class UnexpectedEndOfStream(Exception):
    pass

例子

nbsp = NonBlockingStreamReader(logcat_proc.stdout, print_output=False)
while True:
    line = nbsp.readline(read_timeout)
    if line is None:
        break
    else:
        yield line

subprocess 以 ctrl c 方式终止

直接p.kill()方式可能与我们使用命令行时使用ctrl c终止方式不同,比如pytest-html,如果kill()子进程,则报告不会保留,但是以ctrl c方式会保存已测试的结果

import signal

p = subprocess...

p.send_signal(signal.SIGINT)

 

posted @ 2021-05-14 11:26  风,又奈何  阅读(979)  评论(0编辑  收藏  举报