运行 不使用 pytest no-capture 时子进程失败
Running a subprocess fails when not using pytest no-capture
设置
我已经把问题简化到核心了:
- 已安装 pytest
pip install pytest==5.4.3
- 有一个shell脚本
# has-stdin.sh
# Detect stdin
if [[ ! -t 0 ]]; then
echo "yes"
else
echo "no"
fi
- 进行 Python 测试
# test.py
import subprocess
import unittest
class TestCase(unittest.TestCase):
def test(self):
process = subprocess.run(
["sh", "./has-stdin.sh"],
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
check=True,
shell=False
)
assert process.stdout.decode("utf-8") == "no\n"
测试
✅ 脚本在 bash shell
中运行
$ sh ./has-stdin.sh
no
$ echo '' | sh ./has-stdin.sh
yes
$ sh ./has-stdin.sh <<< ''
yes
✅ 使用 -s
成功运行(例如 --capture=no
)
$ pytest test.py -s
platform linux -- Python 3.7.7, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
rootdir: /Users/maikel/docker/library/postgresql
collected 1 item
test.py .
=========================== 1 passed in 0.02s ===========================
❌没有-s
运行不成功
$ pytest test.py
========================== test session starts ==========================
platform linux -- Python 3.7.7, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
rootdir: /Users/maikel/docker/library/postgresql
collected 1 item
test.py F [100%]
================================ FAILURES ===============================
_____________________________ TestCase.test _____________________________
self = <test.TestCase testMethod=test>
def test(self):
process = subprocess.run(
["sh", "./has-stdin.sh"],
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
check=True,
shell=False
)
> assert process.stdout.decode("utf-8") == "no\n"
E AssertionError: assert 'yes\n' == 'no\n'
E - no
E + yes
test.py:23: AssertionError
======================== short test summary info ========================
FAILED test.py::TestCase::test - AssertionError: assert 'yes\n' == 'no\n'
=========================== 1 failed in 0.10s ===========================
使用 -s
有何不同?如何在没有 -s
的情况下成功 运行 这个 pytest?
我试过 ,但是当使用该方法时,两个 pytest
命令都失败了。
这是一个非常有趣的问题。
我非常感兴趣,所以我在本地做了 运行 代码并检查了发生了什么:
在第一种情况下 (运行ning without -s
) shell 的 /proc/self/fd/0
是 /dev/null
但 运行ning with -s
shell 的 /proc/self/fd/0
在我的例子中是 /dev/pts/7
。
$ pgrep -f has-st
12727
$ ls -la /proc/12727/fd/0
lrwx------ 1 quoyn quoyn 64 Aug 4 00:13 /proc/12727/fd/0 -> /dev/pts/7
和
$ pgrep -f has-st
12793
$ ls -la /proc/12793/fd/0
lr-x------ 1 quoyn quoyn 64 Aug 4 00:17 /proc/12793/fd/0 -> /dev/null
这个 si 因为当你 运行 使用“-s”时 pytest 不捕获 终端并简单地通过它,这就是为什么它是 /dev/pts/7
.如果在 pytest 退出后我在终端中 运行 ls -l /proc/self/fd/0
我确实得到:
lrwx------ 1 quoyn quoyn 64 Aug 4 00:29 /proc/self/fd/0 -> /dev/pts/7
你也可以尝试 不使用 pytest,只有 python:
import subprocess
s = subprocess.run(["ls", "-l", "/proc/self/fd/0"], stderr=subprocess.PIPE, stdout=subprocess.PIPE, input=None, check=True, shell=False)
print(s.stdout)
并尝试以 python code_above.py
和 echo '' | python code_above.py
的形式执行
编辑:
并回答第二个问题
How to run this pytest successfully without -s?
自己使用伪终端。 运行 pty 中的子进程。\
编辑 2:
小试reproduce/demonstrate这道题,运行如下代码为pytest test.py
和pytest -s test.py
:
import sys
def test():
assert sys.stdin.isatty()
$ pip install ptyprocess==0.6.0
# test.py
import unittest
from ptyprocess import PtyProcess
class TestCase(unittest.TestCase):
def test(self):
process = PtyProcess.spawn(
["sh", "./has-stdin.sh"]
)
process.wait()
output = process.read().decode("utf-8")
if process.exitstatus:
raise Exception(output.strip("\r\n"))
assert output.strip("\r\n") == 'no'
✅ 使用 -s
:
成功运行
$ pytest test.py -s
platform linux -- Python 3.7.7, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
rootdir: /Users/maikel/docker/library/postgresql
collected 1 item
test.py .
=========================== 1 passed in 0.60s ===========================
✅ 在没有 -s
的情况下成功运行:
$ pytest test.py
platform linux -- Python 3.7.7, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
rootdir: /Users/maikel/docker/library/postgresql
collected 1 item
test.py .
=========================== 1 passed in 0.60s ===========================
注意事项
- 它的运行速度比
subprocess
慢很多(本例中为 0.60 秒对 0.02 秒)
stdout
和stderr
没有区别
这张图解释了这种情况:
来源:https://github.com/pexpect/ptyprocess/blob/3931cd4/docs/index.rst
设置
我已经把问题简化到核心了:
- 已安装 pytest
pip install pytest==5.4.3
- 有一个shell脚本
# has-stdin.sh
# Detect stdin
if [[ ! -t 0 ]]; then
echo "yes"
else
echo "no"
fi
- 进行 Python 测试
# test.py
import subprocess
import unittest
class TestCase(unittest.TestCase):
def test(self):
process = subprocess.run(
["sh", "./has-stdin.sh"],
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
check=True,
shell=False
)
assert process.stdout.decode("utf-8") == "no\n"
测试
✅ 脚本在 bash shell
中运行$ sh ./has-stdin.sh
no
$ echo '' | sh ./has-stdin.sh
yes
$ sh ./has-stdin.sh <<< ''
yes
✅ 使用 -s
成功运行(例如 --capture=no
)
$ pytest test.py -s
platform linux -- Python 3.7.7, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
rootdir: /Users/maikel/docker/library/postgresql
collected 1 item
test.py .
=========================== 1 passed in 0.02s ===========================
❌没有-s
$ pytest test.py
========================== test session starts ==========================
platform linux -- Python 3.7.7, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
rootdir: /Users/maikel/docker/library/postgresql
collected 1 item
test.py F [100%]
================================ FAILURES ===============================
_____________________________ TestCase.test _____________________________
self = <test.TestCase testMethod=test>
def test(self):
process = subprocess.run(
["sh", "./has-stdin.sh"],
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
check=True,
shell=False
)
> assert process.stdout.decode("utf-8") == "no\n"
E AssertionError: assert 'yes\n' == 'no\n'
E - no
E + yes
test.py:23: AssertionError
======================== short test summary info ========================
FAILED test.py::TestCase::test - AssertionError: assert 'yes\n' == 'no\n'
=========================== 1 failed in 0.10s ===========================
使用 -s
有何不同?如何在没有 -s
的情况下成功 运行 这个 pytest?
我试过 pytest
命令都失败了。
这是一个非常有趣的问题。
我非常感兴趣,所以我在本地做了 运行 代码并检查了发生了什么:
在第一种情况下 (运行ning without -s
) shell 的 /proc/self/fd/0
是 /dev/null
但 运行ning with -s
shell 的 /proc/self/fd/0
在我的例子中是 /dev/pts/7
。
$ pgrep -f has-st
12727
$ ls -la /proc/12727/fd/0
lrwx------ 1 quoyn quoyn 64 Aug 4 00:13 /proc/12727/fd/0 -> /dev/pts/7
和
$ pgrep -f has-st
12793
$ ls -la /proc/12793/fd/0
lr-x------ 1 quoyn quoyn 64 Aug 4 00:17 /proc/12793/fd/0 -> /dev/null
这个 si 因为当你 运行 使用“-s”时 pytest 不捕获 终端并简单地通过它,这就是为什么它是 /dev/pts/7
.如果在 pytest 退出后我在终端中 运行 ls -l /proc/self/fd/0
我确实得到:
lrwx------ 1 quoyn quoyn 64 Aug 4 00:29 /proc/self/fd/0 -> /dev/pts/7
你也可以尝试 不使用 pytest,只有 python:
import subprocess
s = subprocess.run(["ls", "-l", "/proc/self/fd/0"], stderr=subprocess.PIPE, stdout=subprocess.PIPE, input=None, check=True, shell=False)
print(s.stdout)
并尝试以 python code_above.py
和 echo '' | python code_above.py
编辑:
并回答第二个问题
How to run this pytest successfully without -s?
自己使用伪终端。 运行 pty 中的子进程。\
编辑 2:
小试reproduce/demonstrate这道题,运行如下代码为pytest test.py
和pytest -s test.py
:
import sys
def test():
assert sys.stdin.isatty()
$ pip install ptyprocess==0.6.0
# test.py
import unittest
from ptyprocess import PtyProcess
class TestCase(unittest.TestCase):
def test(self):
process = PtyProcess.spawn(
["sh", "./has-stdin.sh"]
)
process.wait()
output = process.read().decode("utf-8")
if process.exitstatus:
raise Exception(output.strip("\r\n"))
assert output.strip("\r\n") == 'no'
✅ 使用 -s
:
$ pytest test.py -s
platform linux -- Python 3.7.7, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
rootdir: /Users/maikel/docker/library/postgresql
collected 1 item
test.py .
=========================== 1 passed in 0.60s ===========================
✅ 在没有 -s
的情况下成功运行:
$ pytest test.py
platform linux -- Python 3.7.7, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
rootdir: /Users/maikel/docker/library/postgresql
collected 1 item
test.py .
=========================== 1 passed in 0.60s ===========================
注意事项
- 它的运行速度比
subprocess
慢很多(本例中为 0.60 秒对 0.02 秒) stdout
和stderr
没有区别
这张图解释了这种情况:
来源:https://github.com/pexpect/ptyprocess/blob/3931cd4/docs/index.rst