Написание кода для обработки ошибок
Обычное действие при сигнализации об ошибке — завершение выполняемой команды и немедленный возврат в цикл команд редактора Emacs. Вы можете организовать перехват ошибок, возникающих в части вашей программы, установив обработчик ошибок с помощью специальной формы condition-case. Простой пример выглядит так:
(condition-case nil
(delete-file filename)
(error nil))
Это удаляет файл с именем filename, перехватывая любую ошибку и возвращая nil в случае возникновения ошибки. (Вы можете использовать макрос ignore-errors для простого случая, как этот; см. ниже.)
Конструкцию condition-case часто используют для перехвата предсказуемых ошибок, таких как неудача при открытии файла в вызове insert-file-contents. Она также используется для перехвата полностью непредсказуемых ошибок, например, когда программа оценивает выражение, введенное пользователем.
Второй аргумент condition-case называется защищенной формой. (В примере выше защищенная форма — это вызов delete-file.) Обработчики ошибок вступают в силу при начале выполнения этой формы и деактивируются при её возврате. Они остаются в силе всё время между этими моментами. В частности, они действуют во время выполнения функций, вызываемых этой формой, в их подпрограммах и так далее. Это хорошо, поскольку, строго говоря, ошибки могут быть сигнализированы только примитивами Lisp (включая signal и error) вызываемыми защищенной формой, а не самой защищенной формой.
Аргументы после защищенной формы — это обработчики. Каждый обработчик перечисляет один или несколько имен условий (которые являются символами), чтобы указать, какие ошибки он будет обрабатывать. Символ ошибки, указанный при сигнализации об ошибке, также определяет список имён условий. Обработчик применяется к ошибке, если у них есть общие имена условий. В примере выше есть один обработчик, и он указывает одно имя условия, error, которое охватывает все ошибки.
Поиск подходящего обработчика проверяет все установленные обработчики, начиная с самого последнего. Таким образом, если две вложенные condition-case формы предлагают обработать одну и ту же ошибку, обработчик изнутри обработает её.
Если ошибка обрабатывается какой-либо condition-case формой, это обычно предотвращает запуск отладчика, даже если debug-on-error говорит, что эта ошибка должна вызвать отладчик.
Если вы хотите иметь возможность отлаживать ошибки, которые перехватываются condition-case, установите переменную debug-on-signal в значение, отличное от nil. Вы также можете указать, что определённый обработчик должен сначала запустить отладчик, написав debug среди условий, как показано ниже:
(condition-case nil
(delete-file filename)
((debug error) nil))
Воздействие debug здесь только в том, чтобы предотвратить condition-case от подавления вызова отладчика. Любая данная ошибка вызовет отладчик только в том случае, если debug-on-error и другие обычные механизмы фильтрации укажут на это. См. Отладка ошибок.
- Макрос: condition-case-unless-debug перем защищенная-форма обработчики…
Макрос
condition-case-unless-debugпредоставляет другой способ обработки отладки таких форм. Он ведет себя точно так же, какcondition-case, за исключением случая, когда переменнаяdebug-on-errorимеет значение, отличное отnil, в этом случае он вообще не обрабатывает никакие ошибки.
Как только Emacs определит, что определённый обработчик обрабатывает ошибку, он возвращает управление этому обработчику. Для этого Emacs отвязывает все привязки переменных, сделанные при выходе из конструкций привязки, и выполняет очистку всех unwind-protect форм, которые выходят. Как только управление доходит до обработчика, тело обработчика выполняется в обычном режиме.
После выполнения тела обработчика выполнение возвращается из condition-case формы. Поскольку защищенная форма полностью завершается до выполнения обработчика, обработчик не может возобновить выполнение в точке ошибки, ни может он исследовать привязки переменных, которые были сделаны внутри защищенной формы. Всё, что он может сделать, это очиститься и продолжить.
Сигнализация и обработка ошибок имеют некоторое сходство с throw и catch (см. Перехват и бросок), но это совершенно отдельные средства. Ошибку нельзя перехватить с помощью catch, а throw не может быть обработано обработчиком ошибок (хотя использование throw в случае отсутствия подходящего catch сигнализирует об ошибке, которую можно обработать).
- Специальная форма: condition-case перем защищенная-форма обработчики…
-
Эта специальная форма устанавливает обработчики ошибок handlers вокруг выполнения protected-form. Если protected-form выполняется без ошибок, значение, которое она возвращает, становится значением
condition-caseформы (в отсутствие обработчика успеха; см. ниже). В этом случаеcondition-caseне имеет эффекта. Формаcondition-caseиграет роль, когда во время выполнения protected-form происходит ошибка.Каждый из handlers является списком формы
(conditions body…). Здесь conditions — это имя условия ошибки, которое будет обработано, или список имён условий (который может включатьdebugдля запуска отладчика перед обработчиком). Имя условияtсоответствует любому условию. body — это одно или несколько выражений Lisp, которые будут выполняться, когда этот обработчик обрабатывает ошибку. Вот примеры обработчиков:(error nil) (arith-error (message "Division by zero")) ((arith-error file-error) (message "Either division by zero or failure to open a file"))
Каждая ошибка имеет символ ошибки, описывающий тип ошибки, и который также описывает список имён условий (см. Символы ошибок). Emacs ищет все активные
condition-caseформы для обработчика, который определяет одно или несколько из этих имён условий; самый внутренний совпадающийcondition-caseобрабатывает ошибку. Внутри этойcondition-case, первый соответствующий обработчик обрабатывает ошибку.После выполнения тела обработчика,
condition-caseвозвращается нормально, используя значение последней формы в теле обработчика в качестве общего значения.Аргумент var — это переменная.
condition-caseне связывает эту переменную при выполнении protected-form, только при обработке ошибки. В этот момент он локально связывает var с описанием ошибки, которое является списком, дающим подробности об ошибке. Описание ошибки имеет вид(error-symbol . data). Обработчик может обратиться к этому списку, чтобы решить, что делать. Например, если ошибка связана с неудачей при открытии файла, имя файла — это второй элемент data — третий элемент описания ошибки.Если var является
nil, это означает, что никакая переменная не связана. Тогда символ ошибки и связанные данные недоступны для обработчика.В качестве специального случая, один из handlers может быть списком формы
(:success body…), где body выполняется с var (если неnil) связанным со значением возврата protected-form, когда это выражение завершается без ошибок.Иногда необходимо повторно сгенерировать сигнал, перехваченный
condition-case, чтобы обработчик верхнего уровня его перехватил. Вот как это сделать:(signal (car err) (cdr err))
где
err— это переменная описания ошибки, первый аргументcondition-case, чье условие ошибки вы хотите перебросить. См. Определение сигнала.
- Функция: error-message-string описание-ошибки
Эта функция возвращает строку сообщения об ошибке для данного описания ошибки. Это полезно, если вы хотите обработать ошибку, напечатав стандартное сообщение об ошибке для этой ошибки. См. Определение сигнала.
Вот пример использования condition-case для обработки ошибки, которая возникает при делении на ноль. Обработчик отображает сообщение об ошибке (но без звука), затем возвращает очень большое число.
(defun safe-divide (dividend divisor)
(condition-case err
;; Protected form.
(/ dividend divisor)
;; The handler. (arith-error ; Condition. ;; Display the usual message for this error. (message "%s" (error-message-string err)) 1000000))) ⇒ safe-divide
(safe-divide 5 0)
-| Arithmetic error: (arith-error)
⇒ 1000000
Обработчик задает имя условия arith-error, чтобы он обрабатывал только ошибки деления на ноль. Другие типы ошибок не будут обрабатываться (этим condition-case). Таким образом:
(safe-divide nil 3)
error→ Wrong type argument: number-or-marker-p, nil
Вот condition-case , который перехватывает все виды ошибок, включая те, которые происходят от error:
(setq baz 34)
⇒ 34
(condition-case err
(if (eq baz 35)
t
;; This is a call to the function error.
(error "Rats! The variable %s was %s, not 35" 'baz baz))
;; This is the handler; it is not a form.
(error (princ (format "The error was: %s" err))
2))
-| The error was: (error "Rats! The variable baz was 34, not 35")
⇒ 2
- Макрос: ignore-errors тело…
-
Этот конструкт выполняет body, игнорируя любые ошибки, которые возникают во время его выполнения. Если выполнение проходит без ошибок,
ignore-errorsвозвращает значение последней формы в body; в противном случае, он возвращаетnil.Вот пример в начале этого раздела, переписанный с использованием
ignore-errors:(ignore-errors (delete-file filename))
- Макрос: ignore-error условие тело…
-
Этот макрос похож на
ignore-errors, но будет игнорировать только определённое условие ошибки.(ignore-error end-of-file (read ""))condition также может быть списком условий ошибок.
- Макрос: with-demoted-errors формат тело…
Этот макрос похож на более мягкую версию
ignore-errors. Вместо подавления ошибок полностью, он преобразует их в сообщения. Он использует строку format для форматирования сообщения. format должен содержать одну последовательность ‘%’; например,"Error: %S". Используйтеwith-demoted-errorsвокруг кода, который не должен сигнализировать об ошибках, но должен быть надёжным, если это произойдёт. Обратите внимание, что этот макрос используетcondition-case-unless-debugвместоcondition-case.
Copyright © 1990-1996, 1998-2022 Free Software Foundation, Inc.
Licensed under the GNU GPL license.
https://www.gnu.org/software/emacs/manual/html_node/elisp/Handling-Errors.html