首页 > 解决方案 > 使用断开连接的标准输入启动 Python 子进程

问题描述

我正在用 Python 执行一个程序,该程序似乎明确地检查是否stdin正在使用,如果确实如此,它会导致一些不良行为,从而输出一些我不想要的垃圾二进制文件。由于这个原因,我特别希望程序“看到”任何连接的标准输入。但是,默认情况下,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断开了标准输入。但是,如果 I 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标志如何,我的子进程都不会检测到连接的标准输入?


编辑:通过查看我的目标程序(samtools)的源代码,它似乎isatty()用于检查它是否应该产生输出。所以在这种情况下,看起来我需要愚弄isatty()

标签: pythonsubprocess

解决方案


事实证明,这个特定问题涉及欺骗函数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总是成功,万岁!


推荐阅读