只发送一个字符到子进程'标准输入

时间:2016-02-21 22:35:14

标签: python subprocess

我试图unittest一个从stdin获取单独按键的模块。获取密钥的代码完美无缺,但是将一个字符(字节?)写入子进程'斯坦丁给了我一些问题。

根据文档和其他SO答案,我基本上使用了推荐here的内容进行了修改:

for ch in range(0, 128):
    p = sp.Popen(
        [py, "-u", TEST_HELP, "getch"],
        stdin=sp.PIPE,
        stdout=sp.PIPE,
        stderr=sp.PIPE,
        bufsize=1
    )
    out, err = p.communicate(input=bytes(chr(ch), "ascii"))
    print(out, ",", err)

我想要的是p只收到stdin的一个ASCII字符然后退出。 ch有时NULEOF和其他控制字符不是问题;它正是我想要的。

问题是,在按 CTRL - C 之前,这似乎没有任何作用,然后它会以键盘中断退出。堆栈跟踪的最后一行是in selectors.py: fd_event_list = self._poll.poll(timeout),它告诉我它正在等待超时(?),但我没有提供timeout=int kwarg。

我使用的命令解析为python3 -u helptest.py getch,它看起来像这样,当我自己从命令行运行它时它可以正常工作。

这是helptest的相关部分:

def getch():
    write_and_flush(ord(_ic._Getch()))

write_and_flush只运行stdout.write; stdout.flush

_ic._Getch()是:

def _Getch():
    if sys.stdin.isatty():
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
            return ch
    else:
        return sys.stdin.read(1)

我在subprocess电话中做错了什么?

将通话更改为:

p = sp.Popen(
    [py, TEST_HELP, "getch"],
    stdin=sp.PIPE,
    stdout=sp.PIPE,
    stderr=sp.PIPE,
)
out, err = p.communicate(input=bytes(chr(ch) + "\n", "ascii"))

省略bufsize,删除--unbuffered选项,然后添加" EOL" (以及其中的变化)不会改变任何东西。

3 个答案:

答案 0 :(得分:-1)

最后一次打印调用应该有Flush=True

答案 1 :(得分:-1)

如果要发送单个字节(ignoring the data corruption that Windows may introduce for the redirected data),

p.communicate()代码是正确的。问题是您的helptest.py

Ctrl + C 行为表示helptest.py尝试直接从控制台读取而不是使用stdin。您似乎使用_Getch() from here在Windows上使用msvcrt.getch(),即它可以从控制台而不是stdin读取。

此外,sys.stdin.read(1)可能会读取多个字节 - sys.stdin默认情况下处于文本模式。要从stdin读取字节,您可以使用b = os.read(0, 1)或以二进制模式重新打开sys.stdin,例如,在Python 3上调用sys.stdin.detach()

如果您的目的是阅读密钥,那么您可以使用use readchar package。最好在一个地方修复可能的(微妙的)问题。

不相关:如果您使用.communicate(),缓冲区无关紧要,即您可以删除-ubufsize。除非子进程被破坏,input=s的行为应与input=s + newline类似(即,EOF是隐式EOL)。

答案 2 :(得分:-2)

您的代码建议您希望bufsize=1将缓冲区大小设置为1,而是启用行缓冲模式。通信阻塞直到写入EOL。