Лутц М. - Изучаем Python (1077325), страница 162
Текст из файла (страница 162)
Запуск тестов в рамках единого процесса Некоторые из приемов, которые мы только что рассмотрели, можно было бы объединить в тестовом приложении, которое позволяет тестировать другой программный код в рамках одного и того же процесса: зарогт зуз 1ор = орел('Тез11ор', 'а') Тгов гез1ар( зарог( вогетевгв, гринах(Тазг, Тез1йаве Ое( Тезыпнег(): и)П)е вогеТезгз(); ггу: гспйех1Тевг() ЕхСарт: рпп1 » 1ор, 'РАТ[Е0', Тез1йаве(), вуз.ехс 1п(о()[:2] е(ве: рппг » 1ор, 'РАявер', гевтйаае() гевыпнег() здесь функция тевшпнег выполняет в цикле серию тестов (модуль Тевгарт — некая абстракция в этом примере). Поскольку в обычной ситуации необработанное исключение приводило бы к завершению самого тестового приложения, можно обернуть вызовы очередного теста инструкцией 1 ту, чтобы обеспечить продолжение процесса тестирования после неудачного завершения любого из тестов.
Здесь, как обычно, пустое предложение ехсер( перехватывает любые необработанные исключения, возникшие в ходе выполнения теста, и регистрирует в файле информацию об исключении, полученную с помощью функции вув. ехс (п(о. Такой подход типичен для систем, которые тестируют функции, модули и классы, запуская их в рамках того же самого процесса, что и само 759 Идиомы исключений тестовое приложение.
Однако на практике тестирование может оказаться процедурой гораздо более сложной, чем показано здесь. Например, чтобы протестировать внешнюю программу, может потребоваться проверять коды состояния или вывод, создаваемый такими средствами запуска программ, как ов, вувтез и ов, рореп, описания которых вы найдете в стандартном руководстве по библиотеке (такие инструменты вообще не возбуждают исключений в случае появления ошибок во внешней программе — фактически тест выполняется параллельно с программой, выполняющей тестирование). В конце этой главы мы познакомимся с некоторыми законченными платформами, предназначенными для проведения тестов, такими как ВосЬезЬ и Ру()п1$, которые обеспечивают возможность сравнения ожидаемого вывода с фактическими результатами.
Подробнее о функции буб.ехс ! про Функция вув.ехс 1пГо, результаты которой использовались в последних двух разделах, является предпочтительным способом доступа к последнему возбужденному исключению. Если в момент ее вызова никакое исключение не обрабатывается, функция возвращает кортеж с тремя объектами йопе. В противном случае возвращаются (тнп, значение, грвссировочная ннфорнвинв), где: ° Тип — это тип обрабатываемого исключения (объект класса для исключений на основе классов). ° Значение — это параметр исключения (ассоциированное значение или второй аргумент инструкции га1эе, который всегда является экземпляром класса, если типом исключения является объект класса). ° Трассированная информация — это объект, который представляет стек вызовов в точке, где возникло исключение (в документации к модулю ггасеоаск описываются инструменты, которые могут использоваться вместе с этим объектом для создания сообщений об ошибках вручную).
Для извлечения типа и значения самого последнего исключения по- прежнему могут использоваться более старые инструментальные средства, такие как вув. ехс Гуре и вув. ехс ха)ое, но они могут использоваться только применительно к единственному исключению, глобальному для всего процесса. В то время как более предпочтительная функция вув, ехс )пго запоминает информацию об исключениях в каждом потоке выполнения. Конечно, это имеет значение только при использовании нескольких потоков выполнения в программах на языке Руьпоп (тема, которая далеко выходит за рамки этой книги). За дополнительной информацией обращайтесь к справочному руководству по библиотеке языка РуЫзоп и к другим специализированным книгам.
Глава 29. Использование исключений 760 Советы по применению исключений Вообще говоря, исключения в языке Ру(Ьоп очень просты в обращении. Настоящее искусство их использования заключается в принятии решения, насколько универсальными должны быть предложения ексер1 и какой объем программного кода должен быть обернут инструкциями 1гу. Рассмотрим сначала вторую проблему. Что должно быть обернуто В принципе, можно было бы обернуть каждую инструкцию в сценарии в свою собственную инструкцию 1гу, но это будет выглядеть достаточно глупо (тогда инструкции 1гу тоже следовало бы обернуть в инструкции 1гу!).
Это настоящая проблема проектирования, которая никак не связана с конкретным языком программирования и становится более очевидной на практике. Однако, ниже приводится несколько правил, выработанных на практике: ° В инструкции 1гу следует заворачивать операции, которые обычно терпят неудачу. Например, операции, взаимодействующие с системой (открытие файлов, взаимодействия с сокетами и т. д.), являются первыми кандидатами для заключения их в инструкции ггу.
° При этом из первого правила есть исключение — в простых сценариях бывает желательно, чтобы подобные неудачи приводили к завершению работы программы. Это особенно верно, когда неудачи ожидаемы. Неудачи в языке Ру(акоп приводят к выводу полезных сообщений (только не в случае краха программы), и они часто представляют собой лучший результат, на который только можно надеяться. ° Завершающие операции должны заключаться в инструкции 1гу/ 1.
пг11у, чтобы гарантировать их выполнение. Эта форма инструкции позволяет выполнять программный код независимо от того, возникло исключение или нет. ° Иногда более удобно завернуть вызов крупной функции в единственную инструкцию 1гу, чем засорять эту функцию несколькими инструкциями 1гу. При таком подходе все исключения, возникшие в функции, будут перехвачены инструкцией ггу, окружающей вызов, за счет чего можно уменьшить объем программного кода внутри самой функции. Влияние на количество обработчиков исключений нередко оказывает тип программы.
Например, серверные программы должны работать постоянно, и поэтому в них инструкции 1гу наверняка будут необходимы, чтобы перехватывать исключения и выполнять восстановительные операции после них. В программах тестирования, таких как мы видели в этой главе, также необходимо выполнять обработку исключений. Однако в более простых сценариях часто можно вообще игнорировать исключения, потому что неудача на любом этапе выполнения требует прекращения работы сценария.
761 Советы по применению исключений Не перехватывайте слишком много: избегайте пустых предложений ехсерт К вопросу о степени универсальности обработчика. Язык РуЖоп позволяет явно указывать, какие исключения должны перехватываться, и иногда бывает необходимо проявлять осторожность, чтобы не перехватывать слишком много. Например, вы уже знаете, что пустое предложение ехсерт перехватывает все исключения, которые только могут возникнуть в блоке т гу. Сделать это несложно и иногда даже желательно, но это может привести к тому, что будет перехвачена ошибка, обработка которой предусмотрена в инструкции т гу на более высоком уровне вложенной структуры.
В предлагаемом примере обработчик исключения перехватывает и деактивирует все исключения, которые достигнут его, независимо от того, ожидает ли какие-либо исключения обработчик уровнем выше: бег тппс() тгу: Я Здесь аазбуждается исключение 1лбехбггсг ехсещ я нс асе исключения попадают сюда' тгу тспс() ехсерт !пбехЕггог; я исключение должно сбрабатиаатнся здесн Что еще хуже, такой программный код может перехватывать исключения, которые вообще не имеют никакого отношения к программе.
Даже такие ситуации, как ошибки работы с памятью, настоящие ошибки в программном коде, остановки итераций и выход из программы, возбуждают исключения. Обычно такие исключения не должны перехватываться. Например, сценарии обычно завершают работу, когда поток управления достигает конца главного файла. Однако в языке Рус)топ имеется специальная функция вув. ехбт(втатввсобе), с помощью которой можно завершить работу программы.
Чтобы завершить программу, эта функция в действительности возбуждает исключение БувтеюЕх)т, благодаря чему имеется возможность предусмотреть возможность выполнения завершающих операций в инструкции тгу!т1па11у, а в специализированных программах — перехватить это событие.' По этой причине Похожая функция ов, ехтт также завершает работу программы, но делает это непосредственно — она пропускает этап выполнения завершающих действий н не может быть перехвачена с помощью инструкций тгутехсерт нли т г утттпа!1у. Обычно эта функция используется в дочерних процессах, описание которых выходит далеко за рамки этой книги.
За дополнительной информацией обращайтесь к справочному руководству по библиотеке языка Рук)топ и к другим специализированным книгам. 762 Глава 29. Использование исключений инструкция Гту с пустым предложением ехсерт может непреднамерен- но перехватить такое важное исключение, как показано в следующем файле (ехССег ру): зшрогг вув Оег Ьуе(): зув.ехы(40) № Серьвзнаи ошибка: завершить работу программи немедленно' тгу; Ьуе() ехсер(: рг(п( '9от И ' № Ой! Ии проигнорировали команду на завершение ргтпт сопмппСп9...
% ау(Поп ех11ег.ру 9о( ы сопс)питп9,. Вы просто не сможете предугадать все исключения, которые могут произойти во время выполнения операции. Вероятно, хуже всего то, что пустое предложение ехсер( может перехватить настоящие ошибки в программном коде, которым желательно было бы позволить пройти дальше. Фактически пустые предложения ехсерс могут отключать механизм интерпретатора, предназначенный для вывода сообщений об ошибках, скрывая возможные ошибки в программном коде.
Например, рассмотрим такой фрагмент: шус)сстопагу = (...) Сгу: х = шубгтсыопагу('враз') № Ой: опе~атка ехсер( х = Моле ...продолшение работа с х... № Я мм предполагаем, что получили Кеубггог Здесь программист предполагает, что в данной ситуации возможен единственный тип ошибки — это ошибка отсутствующего ключа. Но поскольку в имени словаря ауб((с(!олегу была допущена опечатка (должно быть зубзсС(опагу), интерпретатор возбуждает исключение 'мааеЕггог, встретив ссылку на неопределенное имя, которое благополучно будет перехвачено и проигнорировано обработчиком. Обработчик неправильно запишет в переменную значение по умолчанию, замаскировав ошибку в программе.
Если этот программный код будет находиться достаточно далеко от места, где используется выбранное значение, его отладка превратится в весьма захватывающую задачу! Возьмите за правило специализировать свои обработчики, насколько это возможно — пустые предложения ехсерг удобны в использовании, но они потенциально опасны. Так, в последнем примере было бы лучше использовать предложение ехсерС КеуЕггог:, чтобы более явно обозначить свои намерения и избежать возможности перехвата посторон- 763 Советы по применению исключений них событий. В более простых сценариях подобные проблемы могут иметь не такое существенное значение, чтобы перевесить удобство использования, но в общем универсальные обработчики обычно доставляют массу неприятностей. Не перехватывайте слишком мало: используйте категории С другой стороны, было бы нежелательно делать обработчики слишком узкоспециализированными.