Python перехватывает предыдущее исключение в широком обработчике - PullRequest
0 голосов
/ 04 мая 2018

Некоторые из моих представлений API имеют что-то вроде этого:

try:
    do_stuff()
except KeyError as exc:
    logger.log(exc)
    raise APIException("No good")

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

try:
    do_stuff()
except KeyError as exc:
    raise APIException(exc)

exception_handler.py

def exception_handler(...):

    logger.log(exc)  # I want to log the KeyError...

    return Response({"message": "try again sam"}, status_code=400)

Моя проблема в том, что exc в обработчике - это не keyerror, а apiexception, могу ли я как-нибудь получить KeyError из sys.exc_info или stacktrace?

Ответы [ 2 ]

0 голосов
/ 04 мая 2018

Поскольку вы используете Python 3, вы можете просто поднять APIException:

try:
    do_stuff()
except KeyError as exc:
    raise APIException() from exc

Тогда в вашем обработчике исключений, если exc является APIException, вы можете получить доступ к исходному исключению с помощью exc.__context__.

def exception_handler(exc):
    logger.log(exc.__context__)
    return Response({"message": "try again sam"}, status_code=400)

from exc на самом деле не нужен для доступа к __context__, но он дает понять, что KeyError был преобразован в APIException, а не APIException, возникающий при обработке KeyError. См. документацию по исключениям для получения дополнительной информации.

0 голосов
/ 04 мая 2018

Хорошей практикой является поднятие собственного исключения (здесь оно APIException) и добавление исходного исключения.

Вы можете взглянуть на six.raise_from (если вы хотите решение, совместимое с Python 2/3):

Вызовите исключение из контекста. На Python 3 это эквивалентно raise exc_value from exc_value_from. В Python 2, который не поддерживает цепочку исключений, это эквивалентно повышению exc_value.

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

class APIException(Exception):
    def __init__(self, msg, exc_from=None):
        self.exc_from = exc_from
        if exc_from:
            msg += ': raise from {0}'.format(str(exc_from))
        super(APIException, self).__init__(self, msg)

# demo

def do_stuff():
    raise KeyError('bad key')


def main():
    try:
        do_stuff()
    except KeyError as exc:
        raise APIException('error in main', exc)


try:
    main()
except APIException as exc:
    print(str(exc))

Конечно, вместо того, чтобы печатать / регистрировать ваше сообщение об ошибке APIException, вы можете записать свое исходное сообщение:

try:
    main()
except APIException as exc:
    print(str(exc.exc_from))

Редактировать: использовать иерархию классов для исключений

Но, если do_stuff() является частью вашего API, лучше выполнять обработку исключений внутри этой функции и генерировать собственное исключение, которое может наследовать APIException.

class APIException(Exception):
    pass


class BadStuffError(APIException):
    pass


def do_stuff():
    try:
        # ...
        raise KeyError('bad key')
    except KeyError as exc:
        raise BadStuffError('bad stuff: ' + exc.args[0])



def main():
    do_stuff()


try:
    main()
except APIException as exc:
    print(str(exc))

Это лучшее решение, ИМО.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...