Popen with PIPE blocks called process, when it's output large enough, how to unblock it?

huangapple go评论71阅读模式
英文:

Popen with PIPE blocks called process, when it's output large enough, how to unblock it?

问题

I need to start subprocess, which can just exit, printing something to console, or never exits (became a web server e.g.).

So in my runner I'd like to check is process still running or already exits, and do it in a completely unblocking manner, so if it exits, I'd like to capture the output.

So it looks like this

import subprocess
import sys
from subprocess import Popen
from time import sleep

with Popen(
        [
            sys.executable,
            r"...\stub1.py"
        ],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        encoding="utf-8",
) as proc:
    while True:
        ret = proc.poll()
        print(ret)
        if ret is not None: break
        sleep(1)

where stub1.py is just

print("foo"*100)

So it works fine while the output of subprocess is relatively small.

None
0

But with larger output, the called process is blocked (on writing to a filled pipe as I understand) and never exits, so poll() always returns None.

So how to unblock the called process, flushing the pipe in an unblocking manner?

I see I can pass real file descriptors for stdout/stderr, but is it possible with PIPEs?

英文:

I need to start subprocess, which can just exit, printing something to console, or never exits (became a web server e.g.).

So in my runner I'd like to check is process still running or already exits, and do it in completely unblocking manner, so if it exits, I'd like to capture the output.

So it looks like this


import subprocess
import sys
from subprocess import Popen
from time import sleep

with Popen(
        [
            sys.executable,
            r"...\stub1.py"
        ],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        encoding="utf-8",
) as proc:
    while True:
        ret = proc.poll()
        print(ret)
        if ret is not None: break
        sleep(1)

where stub1.py is just

print("foo"*100)

So it works fine while output of subprocess is relatively small.

None
0

But with larger output called process is blocked (on writing to filled pipe as I understand) and never exits, so poll() always returns None.

So how to unblock called process, flushing pipe in unblocking manner?

I see I can pass real file descriptors for stdout/stderr, but is it possible with PIPEs?

答案1

得分: 1

Subprocess pipes apparently have a size limit of 65536, which you can change by adding a pipesize argument to your Popen. A reasonable maximum value (for Linux, not sure about other OS) is 1024KB (1024*1024=1048576 bytes). You can apply this modification to your code like this:

import sys
from subprocess import Popen
from time import sleep

with Popen(
        [
            sys.executable,
            r"...\stub1.py"
        ],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        encoding="utf-8",
        pipesize=1024*1024
) as proc:
    while True:
        ret = proc.poll()
        print(ret)
        if ret is not None: break
        sleep(1)

If 1024 KB is still not enough, you can use os.set_blocking() on the Popen's stdout like this (Note: This works only on Linux):

import os
import sys
from subprocess import Popen
from time import sleep

with Popen(
        [
            sys.executable,
            r"...\stub1.py"
        ],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        encoding="utf-8",
        pipesize=1024*1024
) as proc:
     os.set_blocking(proc.stdout.fileno(), os.O_NONBLOCK)
     while True:
         output = proc.stdout.read()
         if output:
            break
         # do stuff

For Windows, you might want to explore alternatives like threading if the program outputs more than 1024KB.

Read more: https://bugs.python.org/issue41586
https://docs.python.org/3/library/os.html#os.set_blocking

英文:

Apparently subprocess pipes have a size limit of 65536, which you can change simply by adding a pipesize argument to your Popen. A reasonable maximum value (for Linux, not sure about other OS) is 1024KB (1024*1024=1048576 bytes). You can apply this modification to your code like this:

import sys
from subprocess import Popen
from time import sleep

with Popen(
        [
            sys.executable,
            r"...\stub1.py"
        ],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        encoding="utf-8",
        pipesize=1024*1024
) as proc:
    while True:
        ret = proc.poll()
        print(ret)
        if ret is not None: break
        sleep(1)

I tested this with a sample program and it works just fine.

If 1024 KB still is not enough, you could just use os.set_blocking() on the Popen's stdout like this:

import os
import sys
from subprocess import Popen
from time import sleep

with Popen(
        [
            sys.executable,
            r"...\stub1.py"
        ],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        encoding="utf-8",
        pipesize=1024*1024
) as proc:
     os.set_blocking(proc.stdout.fileno(),os.O_NONBLOCK)
     while True:
         output = proc.stdout.read()
         if output:
            break
         # do stuff

This, however, works only on Linux, so if you are using Windows, you might want to check something like threading if the program outputs more than 1024KB
Read more: https://bugs.python.org/issue41586 <br>
https://docs.python.org/3/library/os.html#os.set_blocking

huangapple
  • 本文由 发表于 2023年6月8日 02:10:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/76426007.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定