Crashprobe: перехват ошибок Python в синхронном коде и потоках

от автора

Привет, Хабр! Меня зовут Андрей, как и многие, я уставал от красных сообщений об ошибках в консоли, где не видно значений переменных. Приходилось ставить print(), гуглить – терял кучу времени. Поэтому я написал crashprobe – библиотеку, которая делает отладку простой и наглядной.

Проблема

Я думаю, всем, у кого программа падала из-за ошибки, надоело вот это:

Рис. 1. Обычное сообщение об ошибке в консоли

Рис. 1. Обычное сообщение об ошибке в консоли

Такое сообщение неинформативно, и вот почему:

  • В нём нет списка локальных переменных и их значений во время падения

  • В нём нет контекста кода

  • В нём нет ссылок на документацию (нужно самостоятельно гуглить)

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

Решение: crashprobe

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

Вот как та же самая ошибка выглядит с crashprobe:

Рис. 2. HTML-отчёт, сгенерированный crashprobe

Рис. 2. HTML-отчёт, сгенерированный crashprobe

Что умеет crashprobe (версия 0.4.0)

  • Перехватывает любые необработанные исключения в синхронном коде и в любых потоках (через threading.excepthook).

  • Генерирует HTML‑отчёт (тёмная тема, эмодзи, кнопка копирования) или TXT‑файл (для консоли, удобно на серверах).

  • Показывает локальные переменные (с типами) для каждого фрейма стека.

  • Показывает контекст кода – несколько строк вокруг проблемной, с подсветкой строки ошибки.

  • Добавляет системную информацию (версия Python, ОС, hostname, имя и ID потока).

  • Умеет скрывать пароли/токены через параметр hide_secrets=["password", ...].

  • Не имеет внешних зависимостей – только стандартная библиотека Python.

Как начать пользоваться

Установка – одна команда:

pip install crashprobe

И две строчки в вашем коде:

import crashprobecrashprobe.start()

После этого любая необработанная ошибка автоматически сгенерирует отчёт и откроет его в браузере (или напечатает в консоль, если выбран TXT-формат).

Пример с ошибкой:

def divide(a, b):    return a / bprint(divide(1, "Hello"))   # TypeError: unsupported operand type(s) for /: 'int' and 'str'

Вместо скучного сообщения в консоли вы увидите красивую HTML-страницу с подробностями.

Настройка: как изменить поведение crashprobe

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

Параметр

Тип

По умолчанию

Краткое описание

auto_open

bool

True

Автоматически открывать отчёт (в браузере для HTML, печатать в консоль для TXT)

file_format

str

"HTML"

Формат отчёта: "HTML" (красочная страница) или "TXT" (простой текст)

hide_secrets

list

[]

Список ключевых слов. Если имя переменной содержит любое из них, её значение заменится на <hidden>

Подробнее о каждом параметре

  • auto_open

    • Если True, HTML-отчёт открывается в браузере, а TXT-отчёт печатается прямо в консоль.

    • Если False, файл только сохраняется в папку имя_файла_crash_dir. Удобно для серверов без графического интерфейса.

  • file_format

    • "HTML" – генерируется красивая веб-страница с тёмной темой, эмодзи, кнопкой копирования.

    • "TXT" – создаётся текстовый файл, который легко просматривать в любом редакторе или в терминале.

  • hide_secrets

    • Передаётся список строк, например ["password", "token", "api_key"].

    • При сборе локальных переменных библиотека проверяет имя каждой переменной: если оно содержит одну из этих подстрок (регистр не важен), значение заменяется на <hidden> (тип переменной сохраняется).

    • Это помогает случайно не «засветить» пароли, ключи API и другие чувствительные данные в отчётах.

Примеры использования

python

import crashprobe# 1. Поведение по умолчанию (HTML, автопоказ)crashprobe.start()# 2. Только сохранить TXT, не показывать в консолиcrashprobe.start(auto_open=False, file_format="TXT")# 3. Скрыть переменные, в имени которых есть "password" или "token"crashprobe.start(hide_secrets=["password", "token"])

Полное описание всех аргументов и их допустимых значений можно найти в документации на PyPI.

Почему это удобно?

  • Экономит время – не нужно вручную копировать трейсбек, гуглить, добавлять print().

  • Помогает новичкам – сразу видно, какие значения были в переменных.

  • Полезно для профи – полная информация о потоке, системные данные.

  • Можно поделиться – сохранить HTML-файл и отправить коллеге.

Как устроена библиотека (технические детали)

Crashprobe не использует никаких сторонних библиотек – только стандартные модули Python. В основе лежат несколько простых, но мощных идей.

1. Перехват исключений

  • В синхронном коде – библиотека подменяет sys.excepthook. Это стандартный механизм Python, который вызывается при любом необработанном исключении. Crashprobe устанавливает туда свою функцию, которая и начинает обработку.

  • В потоках – обычный sys.excepthook не срабатывает. Поэтому дополнительно переопределяется threading.excepthook. Он передаёт в обработчик три аргумента (exc_typeexc_valueexc_traceback), упакованные в один объект, поэтому я написал небольшую функцию-обёртку, которая распаковывает их и вызывает основную логику. Так одна и та же функция _error_catching работает и для главного потока, и для дополнительных.

2. Сбор локальных переменных

Когда происходит ошибка, я получаю последний фрейм стека (tb.tb_frame), а затем перебираю все его локальные переменные через frame.f_locals. Чтобы отсеять мусор, я исключаю:

  • встроенные имена, начинающиеся и заканчивающиеся на  (например, __name__,и т.д.);

  • callable-объекты (функции, классы);

  • модули (<class 'module'>).

Для каждого ключа я сохраняю имя, значение (через repr()) и тип (type(val).__name__).

3. Контекст кода

Чтобы показать строки вокруг ошибки, используется linecache.getline(filename, line_number). Я беру 2 строки до и 2 после ошибочной. Для HTML-отчёта строка с ошибкой оборачивается в <div class="errorStr"> и получает красный фон.

4. Генерация HTML и TXT

HTML – это шаблон со встроенным CSS (тёмная тема). Все данные вставляются через f-строки. Кроме того, добавлен небольшой JavaScript, который копирует содержимое страницы в буфер обмена (кнопка «Click to copy»).

TXT – такой же набор данных, но без HTML-тегов, с разделителями из подчёркиваний. Если auto_open=True, TXT-отчёт не открывается в редакторе, а печатается прямо в консоль – это удобно для серверов.

5. Скрытие секретов

Параметр hide_secrets принимает список ключевых слов (например, ["password", "token"]). При обходе локальных переменных проверяется, содержит ли имя переменной любое из этих слов. Если да, то значение заменяется на <hidden> (тип при этом сохраняется). Это простое, но эффективное решение, чтобы случайно не «засветить» пароли в отчётах.

6. Нет зависимостей

Все перечисленные механизмы – systhreadinglinecachehtmlpathlibwebbrowserplatform – входят в стандартную библиотеку Python. Это значит, что для установки crashprobe не нужно тянуть десятки сторонних пакетов. Библиотека работает сразу после pip install.

Сравнение с аналогами

Чтобы понять чем crashprobe отличается от своих аналогов, посмотрим на таблицу:

Инструмент

Перехват ошибок в потоках

Зависимости

Создание HTML отчёта

Скрытие значений конфиденциальных переменных

crashprobe

нет

rich (traceback)

есть

Django Debug Toolbar

есть

better-exceptions

есть

Как видно, crashprobe – единственный инструмент, который одновременно:

  • Ловит ошибки в потоках

  • Не имеет внешних зависимостей

  • Создаёт автономный HTML отчёт

  • Скрывает значения конфиденциальных переменных в отчётах

Статистика и достижения

Crashprobe уже показывает хорошие результаты, хотя я почти не занимался продвижением.

Пик популярности

Сразу после выхода версии 0.4.0 библиотека входила в топ‑25% всех Python‑пакетов на PyPI (обгоняла 75% библиотек). В тот момент её место было примерно 180‑тысячное среди более чем 900 000 пакетов.

Текущая ситуация

Со временем активность немного снизилась (это нормально: пик новизны прошёл, пользователи перешли к повседневному использованию). Сейчас crashprobe находится в верхней части топ‑30% (примерно 282‑тысячное место), что всё равно означает, что она обгоняет около 70% всех библиотек на PyPI.

Для сравнения: большинство пакетов на PyPI получают 0–5 установок в месяц. У crashprobe в среднем около 20 установок в день, и это не считая возможных всплесков после публикации статей.

Что это значит

  • Библиотека нужна разработчикам – её регулярно скачивают.

  • Даже без активной рекламы проект остаётся в верхней трети рейтинга.

  • Как только я начну рассказывать о crashprobe на Хабр и других площадках, позиции могут снова вырасти.

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

Честные ограничения

  • Пока нет поддержки асинхронного кода

  • Пока нет интеграций с популярными фреймворками (Django, Flask, FastAPI)

Планы на будущее

  1. Поддержка асинхронного кода (asyncio)

  2. Интеграции с популярными фреймворками (Django, Flask, FastAPI)

  3. Улучшение HTML и TXT отчётов

Ссылки

Заключение

Спасибо, что дочитали до конца! Я создал crashprobe, потому что сам уставал от неудобной отладки, надеюсь моя библиотека сэкономит вам много времени.

Пожалуйста, попробуйте crashprobe в своём проекте, поделитесь мнением и расскажите знакомым о библиотеке. Установка – одна команда, использование – две строки кода. Вы ничего не теряете, а выиграть можете много времени.

Давайте вместе сделаем отладку Python проще, удобнее и красивее!

ссылка на оригинал статьи https://habr.com/ru/articles/1044368/