使用断开连接的标准输入启动 Python 子进程

Starting a Python subprocess with a disconnected stdin

我正在 Python 中执行一个程序,它似乎明确检查是否正在使用 stdin,如果使用了,它会导致一些不良行为,从而输出一些垃圾二进制文件我不想。由于这个原因,我特别 希望程序 "see" 任何连接的标准输入。但是,默认情况下,subprocess.run() 连接到父进程的标准输入,因此如果父进程(例如 pytest)有标准输入文件,测试将失败。

例如说我的测试是这样的(samtools 可以使用 conda install -c bioconda samtools 安装):

import subprocess
def test_execute():
    print(subprocess.run(
        ['samtools', 'sort'],
        stdout=subprocess.PIPE,
        encoding='utf-8'
    ))

如果我 运行 pytest test.py -s,代码可以工作,因为 -s 断开标准输入。但是,如果我 pytest test.py,该命令会输出一些导致测试失败的垃圾二进制文件:

>       (result, consumed) = self._buffer_decode(data, self.errors, final)
E       UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte

/media/michael/Storage2/miniconda/lib/python3.7/codecs.py:322: UnicodeDecodeError

是否有某种方法可以强制标准输入断开连接,这样无论我的 pytest 标志如何,我的子进程都不会检测到已连接的标准输入?


编辑:通过查看 source code of my target program (samtools),它似乎正在使用 isatty() 来检查它是否应该产生输出。所以在这种情况下,看起来我需要愚弄 isatty().

事实证明,这个特定问题涉及欺骗函数 isatty(),因为这是我的子进程用来确定它是否应该输出二进制文件的东西。因此,要解决这个问题,我只需要将 stdin 连接到 tty:

import subprocess 
import pty # <--------------------------- Added

def test_execute():
    master, slave = pty.openpty() # <---- Added
    print(subprocess.run(
        ['samtools', 'sort'],
        stdout=subprocess.PIPE,
        stdin=slave, # <----------------- Added
        encoding='utf-8'
    ))

现在pytest test.py总是成功,万岁!