Skip to content

Commit f03c91d

Browse files
committed
Avoid subprocess hang when OS pipe fills up
1 parent 8f9c180 commit f03c91d

File tree

1 file changed

+25
-7
lines changed

1 file changed

+25
-7
lines changed

src/watts/fileutils.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# SPDX-License-Identifier: MIT
33

44
from contextlib import contextmanager
5+
import errno
6+
import fcntl
57
import os
68
import platform
79
import select
@@ -91,18 +93,34 @@ def run(args):
9193
Based on https://stackoverflow.com/a/12272262 and
9294
https://stackoverflow.com/a/7730201
9395
"""
94-
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
95-
universal_newlines=True)
96+
97+
# Helper function to add the O_NONBLOCK flag to a file descriptor
98+
def make_async(fd):
99+
fcntl.fcntl(fd, fcntl.F_SETFL, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK)
100+
101+
# Helper function to read some data from a file descriptor, ignoring EAGAIN errors
102+
def read_async(fd):
103+
try:
104+
return fd.read()
105+
except IOError as e:
106+
if e.errno != errno.EAGAIN:
107+
raise e
108+
else:
109+
return ''
110+
111+
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
112+
make_async(p.stdout)
113+
make_async(p.stderr)
96114

97115
while True:
98-
select.select([p.stdout, p.stderr], [], [])
116+
select.select([p.stdout, p.stderr], [], [], 0)
99117

100-
stdout_data = p.stdout.read()
101-
stderr_data = p.stderr.read()
118+
stdout_data = read_async(p.stdout)
119+
stderr_data = read_async(p.stderr)
102120
if stdout_data:
103-
sys.stdout.write(stdout_data)
121+
sys.stdout.write(stdout_data.decode())
104122
if stderr_data:
105-
sys.stderr.write(stderr_data)
123+
sys.stderr.write(stderr_data.decode())
106124

107125
if p.poll() is not None:
108126
break

0 commit comments

Comments
 (0)