Поймать исключения Python и распечатать отдельное сообщение
В настоящее время я пытаюсь написать код для перехвата исключений, и в зависимости от того, какое исключение выдается, будет импортирован другой модуль, чем когда не было выброшено исключение.
try:
import sge_execution_engine as execution_engine
except ImportError:
print "Use local execution engine because SGE support is missing!"
print sys.exc_info() # Print out the exception message
import local_execution_engine as execution_engine
except RuntimeError:
print "Using local execution engine because SGE support is missing!"
print sys.exc_info()
import local_execution_engine as execution_engine
Первое исключение, ImportError
то ловится, то ловит исключение выбрасываемое когда питон drmaa
модуль не может быть найден во время выполнения import sge_execution_engine
(внутри sge_execution_engine
, есть import drmaa
заявление). Второе исключение, RuntimeError
, пойман когда drmaa
найдена библиотека питона (аналогично во время выполнения import drmaa
заявление внутри sge_execution_engine
), но drmaa
Библиотека C не установлена в ОС. Мы надеемся, что эти два except
операторов достаточно, чтобы перехватить все возможные исключения, которые могут быть сгенерированы, когда пользователь пытается запустить этот модуль на машине, у которой просто нет python drmaa
библиотека, drmaa
Библиотека C, или не установлен Sun Grid Engine. без какого-либо из этих действий модуль переходит к import local_execution_engine
и поэтому код может затем выполняться на компьютере пользователя локально. Прямо сейчас код работает, как и ожидалось, в том смысле, что он идет на импорт local, когда находит исключения с помощью sge, но мы все еще стремимся улучшить обработку исключений, чтобы сделать его более устойчивым.
На мой взгляд, я думаю, что иметь фактическое сообщение об исключении, которое было сгенерировано, выводится на стандартный вывод, поскольку это позволит пользователю узнать, почему он не смог импортировать sge_execution_engine, особенно если он не ожидал, что оно не сможет быть импортировано.
Однако вместо использования print sys.exc_info()
чтобы фактически отобразить сообщение об исключении на экране, я понял, что, возможно, лучшим способом было бы использовать except EXCEPTION as some_variable_name
отформатировать, а затем распечатать print some_variable_name
а также вызвать некоторые атрибуты, связанные с Исключением, которое выбрасывается и назначается some_variable_name
,
Я видел, как это делается в уроке по Python, за исключениями, где был этот кусок кода:
import sys
try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except IOError as e:
print "I/O error({0}): {1}".format(e.errno, e.strerror)
except ValueError:
print "Could not convert data to an integer."
except:
print "Unexpected error:", sys.exc_info()[0]
raise
Кажется, что except IOError as e
chunk обрабатывает сообщение об исключительной ситуации детально, вызывая errno
а также strerror
атрибуты IOError
объект. Тем не менее, когда я смотрю на IOError
документация, я не вижу этих конкретных атрибутов, перечисленных как часть документации для исключения. Фактически, это также относится ко всем остальным исключениям в документации Python, поэтому, похоже, нет способа выяснить, какие атрибуты будут связаны с конкретным исключением. Если мы ничего не знаем об этом, то как мы сможем выяснить, какие атрибуты вызывать на some_variable_name
возражать, когда мы используем import EXCEPTION as some_variable_name
синтаксис для обработки наших исключений?
Я был бы признателен за любые предложения по этому вопросу, и даже если ваш ответ не дает прямого ответа на мой вопрос, но если у вас есть еще одно совершенно другое предложение о том, как мне лучше справиться с моим исключением, пожалуйста, не стесняйтесь сделать сообщение!
Большое спасибо!
2 ответа
Используя голый except
редко бывает хорошей идеей, но это один из тех редких случаев - в первую очередь потому, что у вас есть резервная копия, которую вы хотите использовать, если по какой-либо причине вы не можете импортировать систему sge
:
try:
import sge_execution_engine as execution_engine
except:
print "Use local execution engine because SGE support is missing!"
print sys.exc_info() # Print out the exception message
import local_execution_engine as execution_engine
Обратите внимание, что теперь вам нужен только один except
пункт.
Лучший способ передать эту информацию пользователю - это, вероятно, пойти дальше и распечатать ее, а затем также использовать модуль ведения журнала для создания постоянной записи:
import logging
logger = logging.getLogger()
logger.setLevel(logging.WARNING)
а затем в вашем except
пункт добавить:
logger.exception('unable to import sge')
и ваше сообщение вместе с фактическим исключением будет сохранено в файле журнала.
Сначала вы правы, что лучше перехватить исключение в переменной, чем игнорировать его, а затем вытащить его обратно с помощью sys.exc_info()
, Есть несколько веских причин для использования exc_info
(низкоуровневый код, код, который должен работать как с пре-2.6, так и с 3.х и т. д.), но в целом, когда вы получаете, можете сделать это по-своему, вам следует.
И это работает даже для вашего последнего голого, кроме. В 2.7 равнина except:
означает то же самое, что и except Exception:
так что вы можете написать except Exception as e:
, а затем используйте e
значение.
Также обратите внимание, что если вы хотите сделать одно и то же для нескольких типов исключений, вы можете написать except (RuntimeError, ImportError) as e:
,
Что касается "сделать его более надежным", это не абсолютная вещь. Например, если есть какое-то неожиданное исключение, это не RuntimeError
ни ImportError
, вы хотите войти в систему и попробовать резервный код, или совсем и сбросить трассировку? Если это, например, SyntaxError
вызванный тем, что кто-то проверил неправильное редактирование вашей программы, вы можете не захотеть воспринимать это как ошибку во время выполнения... или, может быть, это так; это действительно зависит от вашей практики разработки и целевой базы пользователей.
В то же время:
Кажется, что
except IOError as e
chunk обрабатывает сообщение об исключительной ситуации детально, вызываяerrno
а такжеstrerror
атрибутыIOError
объект. Тем не менее, когда я смотрю наIOError
документация, я не вижу этих конкретных атрибутов, перечисленных как часть документации для исключения.
Вам нужно посмотреть иерархию. Заметить, что IOError
это подкласс EnvironmentError
, который документирует errno
а также strerror
атрибутов. (Что на самом деле означают эти атрибуты, OSError
и его подклассы, но факт их существования задокументирован.)
Если вы думаете, что все это немного беспорядок... ну, это так. Это все вычищено в Python 3.x, где IOError
а также EnvironmentError
объединены в OSError
, который четко документирует свои атрибуты, и где вам обычно не нужно включать errno
во-первых, потому что общий errno
значения генерируют определенный подкласс, как FileNotFoundError
, и так далее. Но пока вы используете 2.7, вы не получаете преимуществ от последних 6 лет улучшений языка.
Например, глядя на иерархию или
ValueError=>StandardError=>Exception
(от низшего к высшему в иерархии), я не могу найти никаких атрибутов об этом.
если ты dir
ValueError
вы увидите, что он имеет только два атрибута (помимо обычных специальных вещей, таких как __repr__
а также __class_
): args
а также message
,
message
не документирован, потому что в 2.5 он устарел, и существует только в 2.7, чтобы позволить некоторому коду до 2.5 продолжать работать.* Но args
задокументировано; вам просто нужно подняться на один уровень дальше, чтобы BaseException
:
args
Кортеж аргументов, переданных конструктору исключений. Некоторые встроенные исключения (например,
IOError
) ожидать определенного количества аргументов и присваивать специальное значение элементам этого кортежа, в то время как другие обычно вызываются только с одной строкой, дающей сообщение об ошибке.
Итак, причина, по которой вы не можете найти другие атрибуты в ValueError
в том, что нет других атрибутов для поиска. И то же самое касается тех других классов. Горстка типов, которые имеют специальные атрибуты (OSError
, SyntaxError
, может быть, несколько специфичных для модуля типов в другом месте в stdlib) документируют их явно.**
Если мы используем
except some_exception as e
синтаксис, делаетprint e
достаточно, чтобы распечатать исключение без вызова его атрибутов
Достаточно напечатать некоторую полезную форму исключения. Опять же из BaseException
документы:
Если
str()
или жеunicode()
вызывается для экземпляра этого класса, возвращается представление аргумента (ов) для экземпляра или пустая строка, когда не было аргументов.
В некоторых случаях это не то, что вы хотите. В частности, обратите внимание, что оно не включает тип исключения. Вы можете получить это с repr
, который дает вам что-то похожее на вызов конструктора (например, ValueError("invalid literal for int() with base 10: 'f'")
).
Если вы хотите получить тот же результат, что и при трассировке, вы должны поставить type
и str
или же unicode
вместе, например, '{}: {}'.format(type(e), e)
,
Если вы хотите получить актуальную информацию из исключения, как эта база 10
или эта строка 'f'
- ну, ты не можешь,*** потому что эта информация уже выброшена. Вам нужно будет написать свой собственный код, чтобы отслеживать его, например:
try:
i = int(s, 16)
except ValueError as e:
print '{} is not a base-16 number'.format(s)
* Это как если бы BaseException.__init__(self, *args)
были определены, чтобы сделать self.args = tuple(args); self.message = str(args[0]) if args else ''
,
** Я полагаю, что в 2.5 было несколько типов исключений, которые имели недокументированные атрибуты как детали реализации CPython, но к 2.7 все они либо пропали, либо документированы.
*** Ну, вы могли бы проанализировать строку исключения, но, разумеется, это деталь реализации, а не то, что гарантированно будет стабильным и переносимым. Он может отличаться в Jython или в испаноязычной системе или может не заключать строки в кавычки так, как вы ожидаете, и т. Д.