Python Как напечатать полный стек, в том числе маги c методы (методы дундер)? - PullRequest
0 голосов
/ 12 апреля 2020

Я пытаюсь отладить встроенный класс Python. Моя отладка привела меня в сферу магических c методов (также называемых методами "дундер").

Я пытаюсь выяснить, какие методы с вызовом вызываются, если таковые имеются. Обычно я делал бы что-то вроде этого:

import sys
import traceback

# This would be located where the I'm currently debugging
traceback.print_stack(file=sys.stdout)

Однако, traceback.print_stack не дает мне уровня детализации печати, какие области с более трудными методами использовались поблизости.

Есть ли способ, которым я могу распечатать, очень многословно, что на самом деле происходит внутри блока кода?


Пример кода

#!/usr/bin/env python3.6

import sys
import traceback
from enum import Enum


class TestEnum(Enum):
    """Test enum."""

    A = "A"


def main():
    for enum_member in TestEnum:
        traceback.print_stack(file=sys.stdout)
        print(f"enum member = {enum_member}.")


if __name__ == "__main__":
    main()

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

В настоящее время он выводит путь к вызову traceback.print_stack:

/path/to/venv/bin/python /path/to/file.py
  File "/path/to/file.py", line 56, in <module>
    main()
  File "/path/to/file.py", line 51, in main
    traceback.print_stack(file=sys.stdout)
enum member = TestEnum.A.

PS Мне не интересно переходить на уровень байтового кода, заданный dis.dis.

Ответы [ 2 ]

1 голос
/ 12 апреля 2020

Я думаю, с помощью трассировки стека вы смотрите не в том месте. Когда вы вызываете print_stack из места, которое выполняется только при поступлении из более сложного метода, этот метод очень хорошо включается в вывод.

Я попробовал этот код для проверки:

import sys
import traceback
from enum import Enum


class TestEnum(Enum):
    """Test enum."""

    A = "A"


class MyIter:

    def __init__(self):
        self.i = 0

    def __next__(self):
        self.i += 1
        if self.i <= 1:
            traceback.print_stack(file=sys.stdout)
            return TestEnum.A
        raise StopIteration

    def __iter__(self):
        return self


def main():
    for enum_member in MyIter():
        print(f"enum member = {enum_member}.")


if __name__ == "__main__":
    main()

Последняя строка трассировки стека печатается как

File "/home/lydia/playground/demo.py", line 21, in __next__
traceback.print_stack(file=sys.stdout)

В исходном коде вы получаете трассировка стека в то время, когда все более сложные методы уже возвращены. Таким образом, они были удалены из стека.

Так что я думаю, вы хотите вместо этого взглянуть на граф вызовов. Я знаю, что IntelliJ / PyCharm может делать это красиво, по крайней мере, в платных изданиях.

Есть и другие инструменты, которые вы можете попробовать. Как pycallgraph выглядит для вас?

Обновление:

Python делает на самом деле довольно легко вывести простой список всех функций звонки.

В общем, все, что вам нужно сделать, это

import sys
sys.setprofile(tracefunc)

Напишите tracefunc в зависимости от ваших потребностей. Найдите рабочий пример по такому вопросу: Как напечатать функции, как они называются

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

Официальная документация sys.setprofile: https://docs.python.org/3/library/sys.html#sys .setprofile

И случайное руководство по трассировке в Python: https://pymotw.com/2/sys/tracing.html

Учтите, однако, что по моему опыту вы можете лучше понять вопросы "кто кого зовет?" или "откуда вообще это значение?" используя обычный старый отладчик.

0 голосов
/ 13 апреля 2020

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

Печать всего стека вызовов

Как указывает @LydiaVanDyke отладчик IDE - действительно отличный способ. Я использую PyCharm и обнаружил, что это мое любимое решение, потому что можно:

  • Отслеживать вызовы функций + точные номера строк в коде
  • Читать код вокруг вызовов, лучше понимать ввод текста
  • Пропускать звонки, которые никто не хочет исследовать

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

И еще один - встроенный модуль отладчика Python, pdb. Это (вызванное через pdb.set_trace()) действительно изменило для меня игру.

Визуализация вывода Profiler

gprof2dot - еще один полезный инструмент визуализации профилировщика.

Поиск исходного кода

Одной из моих других проблем было на самом деле не видеть реальный исходный код из-за файлов-заглушек моей IDE (PyCharm).

Как получить исходный код Python функций подробностей два методы фактической печати исходного кода


Со всеми этими инструментами вы чувствуете себя вполне уверенно!

...