2025, Oct 20 04:16

Ограничиваем вывод стека в pdb при рекурсии с помощью traceback

Как в Python сузить вывод where в pdb при глубокой рекурсии: используйте модуль traceback, лимиты print_stack и срезы format_stack, с учётом фреймов breakpoint().

Отладка глубокой рекурсии с помощью pdb в Python может превратить простой вопрос в стену шума. Команда where выводит всю цепочку вызовов — отлично для полноты картины, но не для фокуса. Когда проблема находится где-то рядом с текущим фреймом, обычно хочется видеть узкое окно вокруг него, а не сотни уровней сверху и снизу. В pdb нет встроенного способа ограничить вывод where N соседними уровнями, но того же эффекта можно добиться с помощью модуля traceback прямо внутри отладчика.

Минимальный пример, воспроизводящий проблему

Ниже приведён фрагмент, который создаёт короткую цепочку вызовов, заканчивающуюся рекурсивной функцией. Когда глубина рекурсии достигает нуля, управление переходит в отладчик. Оттуда where выведет полный стек.

import pdb

def f1():
    f2()


def f2():
    f3()


def f3():
    f4()


def f4():
    go_deep(5)


def go_deep(k):
    if k == 0:
        breakpoint()
    else:
        go_deep(k - 1)

f1()

В приглашении (Pdb) команда where покажет весь стек, включая внутренние фреймы pdb при использовании breakpoint().

Что на самом деле происходит

В pdb сейчас нет функции, которая показала бы только ограниченный срез стека вокруг текущего фрейма. Поэтому в ситуациях с максимальной глубиной рекурсии вывод разрастается до неприличия. Практичный обходной путь — использовать средства модуля traceback из стандартной библиотеки прямо из приглашения (Pdb). Внутри pdb можно импортировать traceback и через него печатать или форматировать подмножество фреймов. Можно задать положительный лимит, чтобы показать заданное число уровней относительно текущей позиции, или отрицательный — чтобы отсчитывать от вершины или от низа стека. Когда задействован breakpoint(), список «засоряется» внутренними фреймами pdb; на практике это означает, что стоит увеличить ожидаемое число примерно на десяток для компенсации.

Решение: используйте traceback изнутри pdb

Сначала импортируйте модуль в сеансе отладчика.

(Pdb) import traceback

Чтобы вывести стек с ограничением, используйте traceback.print_stack. Положительный лимит печатает указанное число фреймов, а отрицательный — отсчёт от вершины или от низа стека. Если фрейм не указан, используется текущий.

(Pdb) traceback.print_stack()
(Pdb) traceback.print_stack(None, 15)
(Pdb) traceback.print_stack(None, -15)

Если скрипт был запущен через python -m pdb script.py, можно явно указать текущий фрейм.

(Pdb) traceback.print_stack(self.curframe, -10)

Для более тонкого контроля и чтобы избежать печати внутренних фреймов отладчика, отформатируйте стек в список и возьмите ровно тот срез, который нужен. Затем выведите только его — так вывод останется чистым.

(Pdb) {print(chunk, end='') for chunk in traceback.format_stack()[-30:-10]}

Это выводит окно уровней без лишних внутренних вызовов, которые добавляет breakpoint(). Срез позволяет сосредоточиться на «N соседних уровнях» рядом с текущей позицией.

Почему это важно

Когда вы разбираетесь с проблемой максимальной глубины рекурсии, нужный сигнал тонет среди сотен фреймов. Сужение обзора до целевого окна сокращает цикл обратной связи и избавляет от прокрутки нерелевантного шума. Это естественно работает прямо в pdb, поэтому не нужно покидать интерактивный контекст, где состояние уже под рукой.

Итоги

Если where перегружает, переключайтесь на модуль traceback прямо из приглашения (Pdb). Используйте traceback.print_stack с положительными или отрицательными лимитами для быстрых «снимков», или traceback.format_stack со срезами, когда нужен точный контроль над тем, какие фреймы выводить. Помните, что breakpoint() добавляет внутренние фреймы; скорректируйте лимиты соответственно. Если вы запустили отладку через python -m pdb script.py, передайте self.curframe в traceback.print_stack, чтобы привязать вывод к текущему фрейму. Такой подход удерживает обзор стека на важных уровнях и делает отладку рекурсии более управляемой.

Статья основана на вопросе на StackOverflow от quazgar и ответе от bruno.