2016-03-11 3 views
0

EDITED2:Почему UnicodeDecodeError скрыт до добавления time.sleep (1)?

В EDITED ниже код, f_out.write(bytearray(out or "")) следует заменить (оба раза) с:
f_out.write(bytearray((out or ""), 'utf8')) # ПЕРЕД удалением universal_newlines=True
ИЛИ
f_out.write(out or "") # после удаления universal_newlines=True


msw, tdelaney и j- f-sebastian - Большое вам спасибо за вашу помощь!

EDITED - В результате, вот сокращенная версия моего сценария, который сейчас ПОСЛЕДОВАТЕЛЬНО ТРИГГЕРЫ 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 "")) 

EDITED - А теперь (с выше сценарий, минус universal_newlines=True), то 1.2MB, 18978 линия STDERR, которые выказывают генерироваться правильно захвачена:

Error: PDF file is damaged - attempting to reconstruct xref table... Error: Kid object (page 1) is not an indirect reference (integer)

....................................................................
(Evince.exe:6800): GLib-GObject-CRITICAL **: g_object_unref: assertion `G_IS_OBJECT (object)' failed

Для некоторых fuzzing я нахожусь Делая это, subprocess.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 (который был occcuring).]

Однако, поскольку исключение происходит только тогда, когда есть какой-то «Другой код» после моего proc.kill(), было бы кажется, что что-то еще, по-видимому, не совсем корректно с моим кодом. Итак, я временно оставил universal_newlines=True в моем коде и опустил мой «Другой код», чтобы лучше определить, что это такое.

Я попытался изменить buf_size и попытался flush() ИНГ как stdout и stderr, but none of that seems to make any difference.

Я видел в Python docs, что:

Popen objects are supported as context managers via the with statement: on exit, standard file descriptors are closed, and the process is waited for.

поэтому я попытался заменить мой Popen() вызов с:

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

и который сгенерировал UnicodeDecodeError, даже с нет «Другой код».Итак, это один из способов «исправить» мой код, но (из-за некоторых дополнительных вещей, которые мне нужно сделать), я бы идеально хотел использовать сторонний модуль PyPIpsutil. И, к сожалению, в настоящее время не поддерживает менеджеров контекста. Поэтому, если возможно, я хотел бы закодировать это без 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, что я должен делать?

Заранее спасибо.

+2

Когда вы нажимаете таймаут, потоки фона все еще читают 'stdout' и' stderr'. Вы не увидите ошибку до тех пор, пока эти потоки не прекратятся. В соответствии с документами _ «Детский процесс не убивается, если истекает время ожидания, поэтому для правильной очистки корректное приложение должно убить дочерний процесс и завершить связь:« _ так просто добавьте 'proc.communicate() 'после убийства. Я думаю, что ваш родитель вышел, прежде чем данные были закачаны в stderr. – tdelaney

+1

Есть ли причина не использовать 'stdin = subprocess.DEVNULL, stdout = subprocess.DEVNULL, stderr = subprocess.STDOUT' здесь? – jfs

+0

** tdelaney: ** Спасибо. \t Я пропустил это дополнение к 3.3 документам (где был добавлен тайм-аут). Извини за это. \t Но теперь я еще более озадачен. \t Теперь я добавил эту добавку в свой сценарий, и если я запустил измененный скрипт [без time.sleep()] только один раз, он не заметит разницы. \t Но если я запустил скрипт 2-го (или 3-го или ...) времени (без перезагрузки), то он получит UnicodeDecodeError со второго раза. – Russell

ответ

2

Выход может быть буферизирован, и поэтому текст может быть декодирован даже после того, как дочерний процесс уже мертв. Если нет time.sleep(1), то родительский может выйти до того, как декодирование столкнулось с ошибкой (потоки демонов ввода-вывода чтения, начатые с .communicate(), будут убиты, а затем выйдет родительский процесс).

+0

Я воспользовался комбинированной помощью всех комментариев и ответов (в том числе 1 из msw, который с тех пор был удален) И увеличил 'timeout' на моем' communication (timeout = sec) 'до 5 секунд. В результате мой скрипт (до моего окончательного удаления 'universal_newlines = True') удалось последовательно генерировать UnicodeDecodeError. Без' universal_newlines = True' мой код теперь корректно захватывает «stderr» Evince. Спасибо. – Russell

 Смежные вопросы

  • Нет связанных вопросов^_^