为什么在添加time.sleep(1)之前隐藏了UnicodeDecodeError?

时间:2016-03-11 02:30:03

标签: windows python-3.x subprocess popen stderr

EDITED2:

在下面的已修改代码中,f_out.write(bytearray(out or ""))应更换(两次): f_out.write(bytearray((out or ""), 'utf8'))#删除universal_newlines=True之前 OR
f_out.write(out or "")#AFTER删除universal_newlines=True

msw,tdelaney和j-f-sebastian - 非常感谢你的帮助!

已编辑 - 因此,这是我的脚本的编辑版本,它现在一直触发UnicodeDecodeError:

#!python3  # Run this script with Python 3.x (in Windows, assuming pylauncher is installed).
import subprocess
import sys

sys.stderr = sys.stdout = open('std.outerr', 'w')
# Redirected stdout/stderr so that they can be seen even when script is not run from command line.
child = subprocess.Popen([r"Evince\bin\Evince.exe", "fuzzed.pdf"], bufsize=0,
                         stdin=subprocess.DEVNULL, stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT, universal_newlines=True)
# `universal_newlines=True` TEMPORARILY left in to show that UnicodeDecodeError is triggered.
# `universal_newlines=True` WILL be removed from FINAL script.
try:
    (out, _) = child.communicate(timeout=5)
# 1 second wasn't long enough for UnicodeDecodeError to consistently be triggered.
# Since subprocess's stderr was redirected to its stdout, 2nd element of tuple will be `None`.
except subprocess.TimeoutExpired:
    child.kill()
    (out, _) = child.communicate()  # Try a 2nd time, without timeout.
    with open('subprocess.out', 'wb') as f_out:
        f_out.write(bytearray(out or ""))  # Treat `None` as an empty string).
else:
    print("\nERROR: A crash occurred before the timeout expired!\n")
    with open('subprocess.out', 'wb') as f_out:
        f_out.write(bytearray(out or ""))

已编辑 - 现在(使用上面的脚本,减去universal_newlines=True),正确捕获了Evince生成的1.2MB,18,978行stderr:

  

错误:PDF文件已损坏 - 尝试重建外部参照表...   错误:Kid对象(第1页)不是间接引用(整数)

     

............................................... .....................
  (Evince.exe:6800):GLib-GObject-CRITICAL **:g_object_unref:   断言“G_IS_OBJECT(对象)”失败

对于我正在做的一些fuzzingsubprocess.Popen()调用以下内容:

import subprocess
proc = subprocess.Popen([r"Evince\bin\Evince.exe", "fuzzed.pdf"],
                         stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                         universal_newlines=True)
try:
    proc.communicate(timeout=1)  # Works the same with timeout=60 seconds.
except subprocess.TimeoutExpired:  # This exception is new to Python 3.3.
    proc.kill()
    # Other code here.
else:
    print("\nERROR: A crash occurred before the timeout expired!\n")

给了我一个UnicodeDecodeError

Exception in thread Thread-1: Traceback (most recent call last):  
File "p:\python35-64\lib\threading.py", line 914, in _bootstrap_inner self.run()   
File "p:\python35-64\lib\threading.py", line 862, in run self._target(*self._args, **self._kwargs)   
File "p:\python35-64\lib\subprocess.py", line 1279, in _readerthread buffer.append(fh.read())
File "p:\python35-64\lib\encodings\cp1252.py", line 23, in decode return codecs.charmap_decode(input,self.errors,decoding_table)[0]
UnicodeDecodeError: 'charmap' codec can't decode byte 0x81 in position 291289:  character maps to [undefined]

即使我将“其他代码”简化为time.sleep(1)这样简单的事情,也会发生这种情况。然而,当我删除“其他代码”时,没有发生异常。

我现在意识到发生异常是因为我在universal_newlines=True电话上不必要地指定了Popen()。 [那是与写入stderr的字节兼容,其值大于127(这是一种发作)。]

但是,因为只有在proc.kill()之后有一些“其他代码”时才会发生异常,所以看起来其他东西对我的代码来说可能不太合适。所以,我暂时在我的代码中留下了universal_newlines=True,并省略了我的“其他代码”,以便更好地确定它是什么。

我尝试更改buf_size并尝试flush()同时stdoutstderr, but none of that seems to make any difference.

我在Python docs中看到:

  

通过with语句支持Popen对象作为上下文管理器:在退出时,   标准文件描述符已关闭,等待该过程。

所以我尝试用{/ p>替换我的Popen()来电

with subprocess.Popen(..., universal_newlines=True) as proc:

并生成了UnicodeDecodeError,即使没有“其他代码”也存在。所以,这是“修复”我的代码的一种方法,但是(由于我还需要做一些额外的事情),我最好使用第三方PyPI psutil模块。而且,令人遗憾的是,目前不支持上下文管理器。所以,如果可能的话,我想在没有with ... as的情况下对此进行编码。

我可以在代码中更改其他内容(universal_newlines的值除外)以“修复”它吗?

基于文档所说的“'Popen'对象作为上下文管理器的支持”,我尝试添加:

if proc.stdout:
    proc.stdout.close()
if proc.stderr:
    proc.stderr.close()
if proc.stdin:
    proc.stdin.close()

和/或proc.wait()就在proc.kill()之前,但是proc.kill从未到达。

with ... as我应该做什么?

提前致谢。

1 个答案:

答案 0 :(得分:2)

输出可以被缓冲,因此即使在子进程已经死亡之后也可以解码文本。如果没有time.sleep(1),那么父进程可能会在解码遇到错误之前退出(.communicate()启动的I / O阅读器守护程序线程被终止,然后父进程退出)。