Как сделать так, чтобы ваше исключение передавало информацию о вызвавшем его коде (например, строку кода)?

Исключение можно дополнить информацией о вызвавшем его коде, используя модуль inspect. Например:

  import inspect

  class MyException(Exception):
    def __init__(self, message):
      frame = inspect.currentframe().f_back
      self.filename = frame.f_code.co_filename
      self.lineno = frame.f_lineno
      self.message = message
      super().__init__(f"{message} (file: {self.filename}, line: {self.lineno})")

  try:
    # some code that might raise an exception
    raise MyException("Something went wrong")
  except MyException as e:
    print(e)
  
В данном примере, при создании исключения MyException, извлекается информация о файле и номере строки, где исключение было выброшено, и добавляется к сообщению об ошибке.

Для того, чтобы исключение передавало информацию о вызвавшем его коде (например, строку кода), можно использовать модуль traceback.

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

Вот пример кода, демонстрирующий, как это сделать:


import traceback
import sys

class MyCustomError(Exception):
    def __init__(self, message, filename=None, lineno=None, code=None):
        super().__init__(message)
        self.filename = filename
        self.lineno = lineno
        self.code = code

def my_function(a, b):
    try:
        result = a / b
    except ZeroDivisionError:
        tb = traceback.extract_stack()[-2] # Get info about the caller (my_function)
        raise MyCustomError("Деление на ноль!", tb.filename, tb.lineno, tb.line) from None # Suppress chained exception
    return result

try:
    my_function(10, 0)
except MyCustomError as e:
    print(f"Ошибка: {e}")
    print(f"Файл: {e.filename}")
    print(f"Строка: {e.lineno}")
    print(f"Код: {e.code}")
except Exception as e:
    print(f"Другая ошибка: {e}")
  

Разъяснение:

  • Создается пользовательский класс исключения MyCustomError, который принимает в конструкторе сообщение об ошибке, имя файла, номер строки и строку кода.
  • В функции my_function, в блоке except, используется traceback.extract_stack() для получения стека вызовов.
  • traceback.extract_stack()[-2] возвращает фрейм стека вызовов, предшествующий текущему (т.е. вызвавшей функции). Мы берем -2, чтобы получить информацию о строке кода в `my_function`, вызвавшей исключение. `-1` вернуло бы информацию о строке, где `raise` было вызвано.
  • Объект фрейма содержит атрибуты filename (имя файла), lineno (номер строки) и line (строка кода).
  • Эти данные передаются в конструктор MyCustomError. Важно использовать `from None` после `raise MyCustomError(...)` для подавления chaining исключений. В противном случае, traceback будет содержать исходное исключение `ZeroDivisionError`, что усложнит отладку.
  • В основной части программы происходит обработка MyCustomError и вывод информации об ошибке.

Альтернативные подходы и улучшения:

  • sys._getframe(): Этот менее предпочтительный способ доступа к фреймам стека может быть быстрее, но не является частью публичного API и может измениться. Рекомендуется использовать traceback.
  • Библиотеки для логирования: Для более сложной обработки исключений и логирования, рассмотрите использование библиотек, таких как logging, которые могут автоматически включать информацию о стеке вызовов в логи.
  • Декораторы: Можно создать декоратор, который автоматически перехватывает исключения и добавляет информацию о вызывающем коде.
  • Объекты исключений, специфичные для домена: Создавайте классы исключений, которые отражают конкретные проблемы в вашем домене. Это улучшает читаемость и упрощает обработку ошибок.

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

0