Как отключить "sys.excepthook отсутствует" ошибка?

NB: Я не пытался воспроизвести проблему, описанную ниже под Windows, или с версиями Python, отличными от 2.7.3.

Самый надежный способ вызвать эту проблему – передать результат следующего тестового сценария через : (под bash ):

 try: for n in range(20): print n except: pass 

То есть:

 % python testscript.py | : close failed in file object destructor: sys.excepthook is missing lost sys.stderr 

Мой вопрос:

Как я могу изменить тестовый скрипт выше, чтобы избежать сообщения об ошибке при запуске скрипта (как показано в Unix / bash )?

(Как показывает тестовый скрипт, ошибка не может быть захвачена с помощью try-except .)

Приведенный выше пример, по общему признанию, очень искусственный, но я сталкиваюсь с той же проблемой, иногда, когда вывод моего скрипта передается через какое-то стороннее программное обеспечение.

Сообщение об ошибке, безусловно, безвредно, но оно смущает конечных пользователей, поэтому я хотел бы заставить его замолчать.

EDIT: Следующий сценарий, который отличается от исходного выше, только тем, что он переопределяет sys.excepthook, ведет себя точно так же, как приведенный выше.

 import sys STDERR = sys.stderr def excepthook(*args): print >> STDERR, 'caught' print >> STDERR, args sys.excepthook = excepthook try: for n in range(20): print n except: pass 

  • python круговой импорт еще раз (что-то не так с этим дизайном)
  • Не удается запустить мастер в учебнике Heroku с помощью Python
  • Что такое синтаксис для вставки одного списка в другой список в python?
  • Python warnings.warn () против logging.warning ()
  • Как эффективно использовать SQLDB SScursor?
  • Повторная почасовая настройка TimeSeries с определенным стартовым часом
  • Графический интерфейс Python для Mac OS X
  • Увеличивает скорость продукта itertools
  • 4 Solutions collect form web for “Как отключить "sys.excepthook отсутствует" ошибка?”

    Как я могу изменить тестовый скрипт выше, чтобы избежать сообщения об ошибке при запуске скрипта (как показано в Unix / bash )?

    Вам нужно будет запретить сценарию писать что-либо на стандартный вывод. Это означает удаление любых операторов print и любого использования sys.stdout.write , а также любого кода, который их вызывает.

    Причина, по которой это происходит, заключается в том, что вы передаете ненулевой объем вывода из вашего сценария Python на то, что никогда не читается со стандартного ввода. Это не уникально для команды:; вы можете получить тот же результат по трубопроводу любой команде, которая не читает стандартный ввод, например

     python testscript.py | cd . 

    Или для более простого примера рассмотрим сценарий printer.py содержащий не более

     print 'abcde' 

    затем

     python printer.py | python printer.py 

    приведет к той же ошибке.

    Когда вы передаете вывод одной программы в другую, вывод, созданный программой записи, подкрепляется в буфере и ожидает, что программа чтения запросит данные из буфера. До тех пор, пока буфер не пуст, любая попытка закрыть файл с файлом записи должна завершиться с ошибкой. Это основная причина сообщений, которые вы видите.

    Конкретный код, вызывающий ошибку, – это реализация языка C на языке Python, что объясняет, почему вы не можете поймать его с помощью блока try / except : он запускается после завершения обработки вашего скрипта. В принципе, в то время как Python закрывается, он пытается закрыть stdout , но это терпит неудачу, потому что есть еще буферизованный вывод, ожидающий чтения. Таким образом, Python пытается сообщить об этой ошибке, как это было бы обычно, но sys.excepthook уже был удален как часть процедуры завершения, поэтому это не удается. Затем Python пытается распечатать сообщение в sys.stderr , но это уже было освобождено, так что это не удается. Причина, по которой вы видите сообщения на экране, заключается в том, что код Python содержит параметр fprintf для непредвиденных ситуаций, чтобы напрямую вывести какой-либо вывод в указатель файла, даже если выходной объект Python не существует.

    Технические подробности

    Для тех, кто интересуется деталями этой процедуры, давайте посмотрим на последовательность выключения интерпретатора Python, которая реализована в функции pythonrun.c .

    1. После вызова выходов и закрытия потоков код завершения вызывает PyImport_Cleanup для завершения и освобождения всех импортированных модулей. Следующей задачей, выполняемой этой функцией, является удаление модуля sys , который в основном состоит из вызова _PyModule_Clear для удаления всех записей в словаре модуля, включая, в частности, стандартные потоковые объекты (объекты Python), такие как stdout и stderr .
    2. Когда значение удаляется из словаря или заменяется новым значением, его счетчик ссылок уменьшается с Py_DECREF макроса Py_DECREF . Объекты, число отсчетов которых достигает нуля, становятся доступными для освобождения. Поскольку модуль sys содержит последние оставшиеся ссылки на стандартные объекты потока, когда эти ссылки не _PyModule_Clear , они затем готовы к освобождению. 1
    3. Освобождение файлового объекта Python выполняется с file_dealloc функции fileobject.c в fileobject.c . Сначала он вызывает метод close объекта Python с использованием функции close_the_file с точно close_the_file именем:

       ret = close_the_file(f); 

      Для стандартного файлового объекта close_the_file(f) делегирует функцию C fclose , которая устанавливает условие ошибки, если все еще есть данные, которые должны быть записаны в указатель файла. file_dealloc проверяет это условие ошибки и печатает первое сообщение, которое вы видите:

       if (!ret) { PySys_WriteStderr("close failed in file object destructor:\n"); PyErr_Print(); } else { Py_DECREF(ret); } 
    4. После печати этого сообщения Python затем пытается отобразить исключение с помощью PyErr_Print . Тот, кто делегирует PyErr_PrintEx , и как часть его функций, PyErr_PrintEx пытается получить доступ к принтеру исключений Python из sys.excepthook .

       hook = PySys_GetObject("excepthook"); 

      Это было бы хорошо, если бы это было сделано в ходе обычной программы Python, но в этой ситуации sys.excepthook уже очищен. 2 Python проверяет это условие ошибки и печатает второе сообщение в качестве уведомления.

       if (hook && hook != Py_None) { ... } else { PySys_WriteStderr("sys.excepthook is missing\n"); PyErr_Display(exception, v, tb); } 
    5. После того, как мы уведомили нас об отсутствующем excepthook , Python затем возвращается к печати информации об исключении с помощью PyErr_Display , который является методом по умолчанию для отображения трассировки стека. Самое первое, что делает эта функция, это попытаться получить доступ к sys.stderr .

       PyObject *f = PySys_GetObject("stderr"); 

      В этом случае это не работает, потому что sys.stderr уже очищен и недоступен. 3 Таким образом, код вызывает fprintf напрямую, чтобы отправить третье сообщение в стандартный поток ошибок C.

       if (f == NULL || f == Py_None) fprintf(stderr, "lost sys.stderr\n"); 

    Интересно, что поведение немного отличается в Python 3.4+, потому что процедура финализации теперь явно очищает стандартные потоки вывода и ошибок до того, как встроенные модули будут очищены. Таким образом, если у вас есть данные, ожидающие записи, вы получите сообщение об ошибке, которое явно сигнализирует об этом условии, а не о «случайном» сбое в обычной процедуре завершения. Кроме того, если вы запустите

     python printer.py | python printer.py 

    используя Python 3.4 (после того, как вы, конечно, поместив скобки в оператор print ), вы вообще не получите никакой ошибки. Я полагаю, что второй вызов Python по какой-то причине может потреблять стандартный ввод, но это целая отдельная проблема.


    На самом деле, это ложь. Механизм импорта Python кэширует копию каждого словаря импортируемого модуля , который не _PyImport_Fini до _PyImport_Fini , а затем в реализации Py_Finalize , и тогда исчезают последние ссылки на стандартные объекты потока. Когда счетчик ссылок достигнет нуля, Py_DECREF освободит объекты. Но все, что имеет значение для основного ответа, заключается в том, что ссылки удаляются из словаря sys модуля и затем освобождаются позже.

    2 Опять же, это связано с тем, что словарь модуля sys полностью очищен, прежде чем что-то действительно освободится, благодаря механизму кэширования атрибутов. Вы можете запустить Python с параметром -vv чтобы увидеть, что все атрибуты модуля не установлены, прежде чем вы получите сообщение об ошибке закрытия указателя файла.

    3 Эта конкретная часть поведения является единственной частью, которая не имеет смысла, если вы не знаете об механизме кэширования атрибутов, упомянутом в предыдущих сносках.

    Сегодня я столкнулся с такой проблемой и пошел искать ответ. Я думаю, что простой способ заключается в том, чтобы убедиться, что вы сначала запустили stdio, поэтому блоки python вместо сбоев при завершении работы скрипта. Например:

     --- a/testscript.py +++ b/testscript.py @@ -9,5 +9,6 @@ sys.excepthook = excepthook try: for n in range(20): print n + sys.stdout.flush() except: pass 

    Тогда с этим скриптом ничего не происходит, поскольку исключение (IOError: [Errno 32] Broken pipe) подавляется try … except.

     $ python testscript.py | : $ 

    В вашей программе выбрасывается исключение, которое невозможно захватить с помощью блока try / except. Чтобы поймать его, переопределите функцию sys.excepthook :

     import sys sys.excepthook = lambda *args: None 

    Из документации :

    sys.excepthook (тип, значение, трассировка)

    Когда исключение создается и не отображается, интерпретатор вызывает sys.excepthook с тремя аргументами, классом исключений, экземпляром исключения и объектом traceback. В интерактивном сеансе это происходит непосредственно перед возвратом элемента управления в приглашение; в программе Python это происходит непосредственно перед выходом программы. Обработка таких исключений верхнего уровня может быть настроена путем назначения другой функции с тремя аргументами для sys.excepthook.

    Пример:

     import sys import logging def log_uncaught_exceptions(exception_type, exception, tb): logging.critical(''.join(traceback.format_tb(tb))) logging.critical('{0}: {1}'.format(exception_type, exception)) sys.excepthook = log_uncaught_exceptions 

    Я понимаю, что это старый вопрос, но я нашел его в поиске Google ошибки. В моем случае это была ошибка кодирования. Одним из моих последних заявлений было:

     print "Good Bye" 

    Решение было просто исправлением синтаксиса:

     print ("Good Bye") 

    [Raspberry Pi Zero, Python 2.7.9]

    Python - лучший язык программирования в мире.