Поймать исключения 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 или в испаноязычной системе или может не заключать строки в кавычки так, как вы ожидаете, и т. Д.

Другие вопросы по тегам