我有一些看起来像这样的Python 3.5代码:
try:
my_process = Popen(someargs, stdin=None, stdout=PIPE, stderr=PIPE)
stdout, stderr = my_process.communicate(timeout=10)
my_process.wait()
except TimeoutExpired:
my_process.kill()
stdout, stderr = my_process.communicate()
我试图遵循the python subprocess documentation here中描述的原则,即在TimeoutError的情况下,我应该手动终止进程,然后完成通信。
原则上听起来很好,但是周期性地(可能每50次一次,非常近似),我得到这样的错误:
Traceback (most recent call last):
File "/Users/xyz/myprogram/myprogram", line 125, in <module>
stdout, stderr = my_process.communicate()
File "/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/subprocess.py", line 1068, in communicate
stdout, stderr = self._communicate(input, endtime, timeout)
File "/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/subprocess.py", line 1689, in _communicate
selector.register(self.stdout, selectors.EVENT_READ)
File "/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/selectors.py", line 342, in register
key = super().register(fileobj, events, data)
File "/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/selectors.py", line 228, in register
key = SelectorKey(fileobj, self._fileobj_lookup(fileobj), events, data)
File "/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/selectors.py", line 215, in _fileobj_lookup
return _fileobj_to_fd(fileobj)
File "/usr/local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/selectors.py", line 39, in _fileobj_to_fd
"{!r}".format(fileobj)) from None
ValueError: Invalid file object: <_io.BufferedReader name=5>
第125行是我案例中的第二行communicate()
。
这似乎失败了,因为流程对象底层的某些流已关闭或终止 - 有时可能会发生在kill()
和communicate()
之间的情况。但如果是这样,我应该采用一种优雅的方式处理这个问题吗? Python文档似乎不包括这种情况。
答案 0 :(得分:1)
使用空字节对象作为stdout
和stderr
的返回值可能是一种解决方案。 my_process.communicate()
为您读取管道。所以最好不要在这里使用文件对象:
try:
my_process = Popen(someargs, stdin=None, stdout=PIPE, stderr=PIPE)
stdout, stderr = my_process.communicate(timeout=10)
my_process.wait()
except TimeoutExpired:
my_process.kill()
try:
stdout, stderr = my_process.communicate()
except ValueError:
stdout = b''
stderr = b''
为stdout
和stderr
创建空文件对象可能是一个解决方案:
import io
try:
my_process = Popen(someargs, stdin=None, stdout=PIPE, stderr=PIPE)
stdout, stderr = my_process.communicate(timeout=10)
my_process.wait()
except TimeoutExpired:
my_process.kill()
try:
stdout, stderr = my_process.communicate()
except ValueError:
stdout = io.BytesIO()
stderr = io.BytesIO()
答案 1 :(得分:1)
要解决my_process.communicate()
在异常处理程序中引发ValueError
,您可以直接从流中读取(更简单的代码路径 - 在一般情况下不要使用它):
from subprocess import Popen, PIPE, TimeoutExpired
with Popen(cmd, stdout=PIPE, stderr=PIPE) as process:
try:
stdout, stderr = process.communicate(timeout=10)
except TimeoutExpired:
process.kill()
stdout = process.stdout.read() # the process is dead, no deadlock
stderr = process.stderr.read()
在Python 3.5上,您可以使用subprocess.run()
:
import subprocess
from subprocess import PIPE, TimeoutExpired
try:
result = subprocess.run(cmd, timeout=10, stdout=PIPE, stderr=PIPE)
except TimeoutExpired as e:
result = e
stdout, stderr = result.stdout, result.stderr
虽然它以与代码相同的方式处理TimeoutExpired
,因此无论如何都可以获得ValueError
。如果您使用此代码获得ValueError
;在http://bugs.python.org