在 python 中将输出捕获为 tty
Capture output as a tty in python
我有一个需要 tty(如 stdin 和 stderr)的可执行文件,并希望能够对其进行测试。
我想输入 stdin,并捕获 stdout 和 stderr 的输出,这是一个示例脚本:
# test.py
import sys
print("stdin: {}".format(sys.stdin.isatty()))
print("stdout: {}".format(sys.stdout.isatty()))
print("stderr: {}".format(sys.stderr.isatty()))
sys.stdout.flush()
line = sys.stdin.readline()
sys.stderr.write("read from stdin: {}".format(line))
sys.stderr.flush()
我可以 运行 这个没有 tty,但是 .isatty
和每个 return False:
import subprocess
p = subprocess.Popen(["python", "test.py"], stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
p.stdin.write(b"abc\n")
print(p.communicate())
# (b'stdin: False\nstdout: False\nstderr: False\n', b'read from stdin: abc\n')
我想捕获 stdout 和 stderr 并将所有三个 return True - 作为 tty。
我可以使用 pty
来制作一个 tty stdin:
import subprocess
m, s = pty.openpty()
p = subprocess.Popen(["python", "test.py"], stdin=s, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdin = os.fdopen(m, 'wb', 0)
os.close(s)
stdin.write(b"abc\n")
(stdout, stderr) = p.communicate()
stdin.close()
print((stdout, stderr))
# (b'stdin: True\nstdout: False\nstderr: False\n', b'read from stdin: abc\n')
我已经尝试了一堆排列来使 stdout 和 stderr tty 无济于事。
我在这里想要的输出是:
(b'stdin: True\nstdout: True\nstderr: True\n', b'read from stdin: abc\n')
下面的代码基于 jfs 的回答 here and , plus your idea of using 3 pseudo-terminals to distinguish stdout, stderr and stdin (though note there is a cryptic warning 这样做可能会出错(例如 OSX 上的标准错误可能被截断?)。
另请注意,文档说 pty
是 for Linux-only,但对于其他平台来说是 "supposed to work":
import errno
import os
import pty
import select
import subprocess
def tty_capture(cmd, bytes_input):
"""Capture the output of cmd with bytes_input to stdin,
with stdin, stdout and stderr as TTYs.
Based on Andy Hayden's gist:
https://gist.github.com/hayd/4f46a68fc697ba8888a7b517a414583e
"""
mo, so = pty.openpty() # provide tty to enable line-buffering
me, se = pty.openpty()
mi, si = pty.openpty()
p = subprocess.Popen(
cmd,
bufsize=1, stdin=si, stdout=so, stderr=se,
close_fds=True)
for fd in [so, se, si]:
os.close(fd)
os.write(mi, bytes_input)
timeout = 0.04 # seconds
readable = [mo, me]
result = {mo: b'', me: b''}
try:
while readable:
ready, _, _ = select.select(readable, [], [], timeout)
for fd in ready:
try:
data = os.read(fd, 512)
except OSError as e:
if e.errno != errno.EIO:
raise
# EIO means EOF on some systems
readable.remove(fd)
else:
if not data: # EOF
readable.remove(fd)
result[fd] += data
finally:
for fd in [mo, me, mi]:
os.close(fd)
if p.poll() is None:
p.kill()
p.wait()
return result[mo], result[me]
out, err = tty_capture(["python", "test.py"], b"abc\n")
print((out, err))
产量
(b'stdin: True\r\nstdout: True\r\nstderr: True\r\n', b'read from stdin: abc\r\n')
我有一个需要 tty(如 stdin 和 stderr)的可执行文件,并希望能够对其进行测试。 我想输入 stdin,并捕获 stdout 和 stderr 的输出,这是一个示例脚本:
# test.py
import sys
print("stdin: {}".format(sys.stdin.isatty()))
print("stdout: {}".format(sys.stdout.isatty()))
print("stderr: {}".format(sys.stderr.isatty()))
sys.stdout.flush()
line = sys.stdin.readline()
sys.stderr.write("read from stdin: {}".format(line))
sys.stderr.flush()
我可以 运行 这个没有 tty,但是 .isatty
和每个 return False:
import subprocess
p = subprocess.Popen(["python", "test.py"], stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
p.stdin.write(b"abc\n")
print(p.communicate())
# (b'stdin: False\nstdout: False\nstderr: False\n', b'read from stdin: abc\n')
我想捕获 stdout 和 stderr 并将所有三个 return True - 作为 tty。
我可以使用 pty
来制作一个 tty stdin:
import subprocess
m, s = pty.openpty()
p = subprocess.Popen(["python", "test.py"], stdin=s, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdin = os.fdopen(m, 'wb', 0)
os.close(s)
stdin.write(b"abc\n")
(stdout, stderr) = p.communicate()
stdin.close()
print((stdout, stderr))
# (b'stdin: True\nstdout: False\nstderr: False\n', b'read from stdin: abc\n')
我已经尝试了一堆排列来使 stdout 和 stderr tty 无济于事。
我在这里想要的输出是:
(b'stdin: True\nstdout: True\nstderr: True\n', b'read from stdin: abc\n')
下面的代码基于 jfs 的回答 here and
另请注意,文档说 pty
是 for Linux-only,但对于其他平台来说是 "supposed to work":
import errno
import os
import pty
import select
import subprocess
def tty_capture(cmd, bytes_input):
"""Capture the output of cmd with bytes_input to stdin,
with stdin, stdout and stderr as TTYs.
Based on Andy Hayden's gist:
https://gist.github.com/hayd/4f46a68fc697ba8888a7b517a414583e
"""
mo, so = pty.openpty() # provide tty to enable line-buffering
me, se = pty.openpty()
mi, si = pty.openpty()
p = subprocess.Popen(
cmd,
bufsize=1, stdin=si, stdout=so, stderr=se,
close_fds=True)
for fd in [so, se, si]:
os.close(fd)
os.write(mi, bytes_input)
timeout = 0.04 # seconds
readable = [mo, me]
result = {mo: b'', me: b''}
try:
while readable:
ready, _, _ = select.select(readable, [], [], timeout)
for fd in ready:
try:
data = os.read(fd, 512)
except OSError as e:
if e.errno != errno.EIO:
raise
# EIO means EOF on some systems
readable.remove(fd)
else:
if not data: # EOF
readable.remove(fd)
result[fd] += data
finally:
for fd in [mo, me, mi]:
os.close(fd)
if p.poll() is None:
p.kill()
p.wait()
return result[mo], result[me]
out, err = tty_capture(["python", "test.py"], b"abc\n")
print((out, err))
产量
(b'stdin: True\r\nstdout: True\r\nstderr: True\r\n', b'read from stdin: abc\r\n')