LINUX.ORG.RU

[python] Правильная архитектура исключений

 


0

0

Имеется некий модуль, который содержит обёртки вокруг xmlrpc сервера.

Возник вопрос, как правильно реализовать обработку ошибок. Сначала начал делать абстрактный класс Error и к нему кучу потомков на каждый чих.

Сейчас подумал, а нужно ли оно? Есть же прекрасные готовые классы вроде socket.error, xmlrpc.Error и проч. Зачем повторять их функциональность в моём модуле?

Итак, какой же правильный способ обработки ошибок в модуле?

★★★★

s/xmlrpc.Error/xmlrpclib.Fault/

Davidov ★★★★
() автор топика

>> Зачем повторять их функциональность в моём модуле?

Собственно ты сам и ответил на свой вопрос.

А вообще все зависит от того, какой уровень абстракции предполагает обертка. Бросаемые исключения должны соответсвовать уровню абстракции. Скажем, если пользователь не будет оперировать терминами а-ля сокет, то socket.error ему видеть незчем. В этом случае лучше более низкоуровневое исключение заварачивать в более высокоуровневое. Типа жабавского setCause/getCause. Тогда и не придется плодить "кучу потомков на каждый чих".

cathode
()

На каждом уровне абстракции - свои исключения, исключения из более высокого уровня оборачивают исключения более низкого уровня (aka exception chaining).

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

>На каждом уровне абстракции - свои исключения, исключения из более высокого уровня оборачивают исключения более низкого уровня (aka exception chaining).

Тем не менее, xmlrpclib выбрасывает socket.error

Davidov ★★★★
() автор топика
Ответ на: комментарий от tailgunner

>исключения из более высокого уровня оборачивают исключения более низкого уровня

И в чем профит? Только в том, что не появляется ощущение того, что ты просрал абстракцию?

temp
()
Ответ на: комментарий от temp

>> исключения из более высокого уровня оборачивают исключения более низкого уровня

> И в чем профит?

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

tailgunner ★★★★★
()
Ответ на: комментарий от temp

>> И в чем профит? Только в том, что не появляется ощущение того, что ты просрал абстракцию?

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

cathode
()
Ответ на: комментарий от cathode

Однако ошибка произошла во внутренностях и до нее придется все равно пробираться. Только в вашем цепочка перекинутых исключений еще и скрывает где она конкретно произошла.

Это как пробираться через пяток goto.

anonymous
()
Ответ на: комментарий от tailgunner

>Даже в случае ошибки с декодированием XML?

Нет. В этом случае он выбрасывает исключение парсера XML )

Я и говорю, никакого оборачивания не происходит.

Davidov ★★★★
() автор топика
Ответ на: комментарий от anonymous

> цепочка перекинутых исключений еще и скрывает где она конкретно произошла.

Добраться до информации о конкретном исключении можно, если есть желание. А какие альтернативы?

tailgunner ★★★★★
()
Ответ на: комментарий от Davidov

> никакого оборачивания не происходит.

Видно, xmlrpc не считает себя достаточно толстым слоем :)

А что такое тогда xmlrpc.Error?

tailgunner ★★★★★
()

>Сейчас подумал, а нужно ли оно? Есть же прекрасные готовые классы вроде socket.error, xmlrpc.Error и проч.

Ну так используй socket/xmlrpc error-ы когда ошибка там, и свои, когда ошибка в твоей части проги.

anonymous
()
Ответ на: комментарий от anonymous

>> Однако ошибка произошла во внутренностях и до нее придется все равно пробираться. Только в вашем цепочка перекинутых исключений еще и скрывает где она конкретно произошла. 

Ну это совсем не сложно. Я не знаю как это делается в Питное, а в жаве в блоках catch делается что-то вроде этого:

try
{
    ...
}
catch (Exception e)
{
    e2 = new OtherException();
    e2.setCause(e);
    throw e2;
}

В коде на самом верхнем уровне разбор полетов последовательно вызывая getCause() и getSource()

catch (Throwable e)
{
    Throwable e1 = e.getCause()
    Throwable e2 = e1.getCause();
    ....
}

Так можно спустится до первоначального источника. При этом на каждом уровне можно посмотреть откуда вывалился эксепшн (из какого обекта) и с какими параметрами. Кроме этого на каждом уровне можно получить состояние объекта, бросившего эксепшн (через рефлексию). Таким образом складывается полная картинка приоисходящего и на каждом уровне получаем вполне адекватные уровню сообщения из эксепшнов и их тип.

cathode
()
Ответ на: комментарий от cathode

> Я не знаю как это делается в Питное

ключевая фраза

> последовательно вызывая getCause() и getSource()

raise SecondaryException() from primary_exception появилось только в Python 3

anonymous
()

>Итак, какой же правильный способ обработки ошибок в модуле?
Выкинуть петон и взять Common Lisp, посмотреть на его restarts.

anonymous
()
Ответ на: комментарий от tailgunner

>А что такое тогда xmlrpc.Error?

Если ты про моё первое сообщение, то на самом деле называется xmlrpclib.Fault,

Davidov ★★★★
() автор топика
Ответ на: комментарий от Davidov

...которое содержит ответ сервера об ошибке. То есть это значит пришёл ответ «ошибка № 3, неправильный пароль», например.

Davidov ★★★★
() автор топика
Ответ на: комментарий от anonymous

>> ключевая фраза

Тут скорее речь не о том как это реализовано в Питоне, а как это делается вобще. И таки я подозреваю, что если в питоне можно сделать throw то и запихнуть в бросаемое исключение объект первоначального исключения тоже можно и без питон3, вручную отредактировав словарь атрибутов например или таки реализовав соответсвующий метод\атрибут в классе исключения.

cathode
()
Ответ на: комментарий от cathode

>Тут скорее речь не о том как это реализовано в Питоне

Ну много раз уже говорили, что ООП в питоне другое, чем в жаве. И обработка ошибок, видимо, тоже.

В общем, насколько я понял, изучая исходники, идея состоит как раз в том, чтобы каждый модуль имел только свои специфичные исключения. То есть в моём случае, это только неправильные данные, возвращённые сервером.

В этом есть и плюсы (мало кода, прозрачный трейс, не нужно заново реализовывать готовое). И минусы (для адекватной обработки исключений нужно хорошо представлять архитектуру системы). Ну так уж сложилось, что в питоне документация хуже, чем в той же Java. Зато не enterprise :)

Davidov ★★★★
() автор топика
Ответ на: комментарий от Davidov

> ООП в питоне другое, чем в жаве.

Не вижу существенных отличий

> И обработка ошибок, видимо, тоже.

Тем не менее chained exceptions в Py3k роддерживаются уже на уровне языка.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

>Не вижу существенных отличий

Ну мне объясняли, что те же декораторы, например, иногда лучше делать функциями, а не методами класса. Потом, я так понимаю, a.__class__ = B в Java просто нельзя.

>Тем не менее chained exceptions в Py3k роддерживаются уже на уровне языка.

Ну посмотрим, исправят ли они стандартную библиотеку.

Davidov ★★★★
() автор топика
Ответ на: комментарий от tailgunner

С ужасом понял, что все мои топики про питон и меркуриал с вашим участием ;)

Скоро, наверное, мои темы будут удалять по шаблону *python*, по причине "личная переписка". ;)

Davidov ★★★★
() автор топика

API должен иметь стройчный и законченый вид. Если он так выглядит с твоими эксепшенами то делай с ними. Если с чужими -делай с чужими.

А вообще это из разряда вечных вопросов типа "как назвать переменную".

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

>А вообще это из разряда вечных вопросов типа "как назвать переменную".

Частично на него отвечает PEP8 :)

Davidov ★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.