Получить строку аргумента exec C python или получить доступ к стеку оценки

В моем отладчике Python у меня есть способ переназначить строку в имя файла, чтобы при переходе через функцию exec'd внутри отладчика вы могли вывести список пигментированных строк или просмотреть их в редакторе, таком как Emacs, через realgud.

Поэтому я хотел бы иметь возможность извлечь строку в операторе exec, когда CPython останавливается внутри, оценивая это.

У меня уже есть механизм, который может оглянуться назад в кадре вызова, чтобы увидеть, был ли вызывающий абонентEXEC_STMT и я могу оглянуться назад одну инструкцию, чтобы увидеть, если предыдущая инструкция была сказать DUP_TOP, Так что я был бы свободен, если бы мог просто найти способ прочитать запись в стеке во время вызова, и это даст оценку строки. Вероятно, есть способ заглянуть в C, чтобы получить это, но мои знания о внутренностях CPython отсутствуют, и я бы предпочел не делать этого. Если есть пакет, может быть, я мог бы включить это по желанию.

CPython уже предоставляет доступ к аргументам функции и локальным переменным, но, разумеется, поскольку это встроенная функция, она не записывается как параметр функции.

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

Я понимаю, что CPython2 и CPython3 здесь могут немного отличаться, но для начала можно было бы сделать что-то другое.

3 ответа

Решение

Я думаю, что теперь я нашел способ.

Внутри отладчика я поднимаюсь вверх по стеку вызовов на один уровень, чтобы добраться до exec заявление. Затем я могу использовать uncompyle6 для получения абстрактного синтаксического дерева исходного кода. (В uncompyle6 может потребоваться изменение, чтобы сделать это проще.)

Дерево в точке вызова будет иметь что-то вроде exec_stmt -> expr ..., Это выражение будет иметь текст выражения, который не обязательно является значением выражения. Выражение может быть постоянным строковым значением, но может быть чем-то сложным, например "foo" + var1,

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

Это все еще имеет проблему переоценки выражения может иметь побочные эффекты. Но это плохая практика программирования, верно?;-)

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

Совершенно другой подход заключается в том, чтобы рано останавливаться и переключаться на суб-интерпретатор, такой как byterun (или какой-либо соответствующим образом модифицированный модуль Python C), который будет иметь доступ к стеку.

В Thonny IDE с открытым исходным кодом есть [sub] степенная оценка выражений. Посмотрите ответ автора на вопрос SO " Оценка трассировки Python" шаг за шагом.

Не совсем ответ, но в некоторых случаях это может быть решением этой проблемы.

Вы можете предоставить свой собственный execфункция, которая расширяет linecacheвключить это. Затем многие трассировки будут включать код. Нравиться:

      def custom_exec(code_str, _globals=None, _locals=None):
  compile_string_fn = f"<custom code str {hash(code_str)}>"
  c = compile(code_str, compile_string_fn, "exec")
  set_linecache(compile_string_fn, code_str)
  exec(c, _globals, _locals)

С:

      def set_linecache(filename, source):
  import linecache
  linecache.cache[filename] = None, None, [line+'\n' for line in source.splitlines()], filename
Другие вопросы по тегам