Python 只向子进程的stdin发送一个字符

Python 只向子进程的stdin发送一个字符,python,subprocess,Python,Subprocess,我正在尝试对一个模块进行单元测试,该模块可以从stdin获得单个按键。获取密钥的代码工作得很好,但是写的是一个字符字节吗?子流程的stdin给了我一些问题 我使用的基本上是根据文档和其他SO答案进行修改后推荐的内容: for ch in range(0, 128): p = sp.Popen( [py, "-u", TEST_HELP, "getch"], stdin=sp.PIPE, stdout=sp.PIPE, std

我正在尝试对一个模块进行单元测试,该模块可以从stdin获得单个按键。获取密钥的代码工作得很好,但是写的是一个字符字节吗?子流程的stdin给了我一些问题

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

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只接收一个ASCII字符的stdin,然后退出。ch有时是NUL、EOF和其他控制字符这一事实不是问题;这正是我想要的

问题是,在我按下CTRL-C键之前,它似乎什么也不做,然后在键盘中断时退出。堆栈跟踪的最后一行在selectors.py:fd\u event\u list=self.\u poll.polltimeout中,它告诉我它正在等待超时,但我没有提供timeout=int-kwarg

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

以下是帮助测试的相关部分:

write_和_flush只运行stdout.write;冲洗

和_ic._Getch是:

在打破这一点的子流程调用中,我做错了什么

将呼叫更改为:

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和变体,不会/不会改变任何内容。

您的代码表明,您希望bufsize=1将缓冲区大小设置为正好1,但它会打开行缓冲模式。通信阻塞,直到写入下线。

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

p如果要发送单个字节,则通信代码是正确的。问题是您的helptest.py

Ctrl+C行为表示helptest.py尝试直接从控制台读取,而不是使用stdin。似乎您在Windows上使用的是msvcrt.getch,也就是说,它可能从控制台而不是stdin读取

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

如果您的意图是读取密钥,则可以使用。最好在一个地方解决可能出现的微妙问题


不相关:如果你使用缓冲区并不重要。交流,也就是说,你可以删除-u,bufsize。除非子进程中断,否则输入=s的行为应类似于输入=s+换行符,即EOF是隐式EOL。

未解决此问题,且bufsize与-unbuffered选项的组合不会更改anything@cat当前位置没有。您很可能也更改了其他内容。我不知道你把Flush=True放在哪里,也不知道它如何解释观察到的行为。我用write_和_Flush的调用替换了print的调用,后者修复了它。写入和刷新==打印刷新=True@cat:1-问题中的代码已经使用write_和_flush 2-flush应该是小写的3-它不能解释Ctrl+C的行为。我看到的唯一一个打印是在父项的末尾,除非其他内容被破坏,否则它不会挂起。
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)
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"))