Пошаговая отладка с помощью IPython

из того, что я читал, есть два способа отладки кода в Python:

  • С традиционным отладчиком, таким как pdb или ipdb. Это поддерживает такие команды как c на continue, n на step-over, s на step-into etc.), но у вас нет прямого доступа к оболочке IPython, которая может быть чрезвычайно полезна для проверки объектов.

  • используя оболочкой IPython by вложение с оболочкой IPython оболочка в вашем коде. Вы можете сделать from ipython import embed, а затем использовать embed() в коде. Когда ваша программа / скрипт попадает в embed() заявление, вы попали в оболочку IPython. Это позволяет полностью проверять объекты и тестировать код Python, используя все лакомства IPython. Однако, при использовании embed() вы можете шаг за шагом через код больше с удобными сочетаниями клавиш.

есть ли способ объединить лучшее из обоих миров? Т. е.

  1. уметь шаг за шагом через ваш код с удобными сочетаниями клавиш pdb/ipdb.
  2. на любом таком шаге (например, на данном заявлении), иметь доступ к полноценному IPython shell.

IPython отладки а в MATLAB:

пример такого типа "расширенной отладки" можно найти в MATLAB, где пользователь всегда имеет полный доступ к MATLAB engine / shell, и она все еще может шаг за шагом через ее код, определите условные точки останова и т. д. Из того, что я обсуждал с другими пользователями, это функция отладки, которую люди пропускают больше всего при переходе от MATLAB к IPython.

отладка IPython в Emacs и других редакторах:

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

13 ответов


как насчет ipdb.set_trace() ? В вашем коде:

import ipdb; ipdb.set_trace()

обновление: сейчас в Python 3.7, мы можем написать breakpoint(). Он работает так же, но он также подчиняется PYTHONBREAKPOINT переменные среды. Эта функция происходит от этот Пеп.

это позволяет полностью проверить ваш код, и у вас есть доступ к таким командам, как c (далее), n (выполнить следующую строку), s (шаг в метод в точку) и так на.

посмотреть ipdb repo и список команд. оболочкой IPython теперь называется (edit: часть)Jupyter.


ps: обратите внимание, что команда ipdb имеет приоритет над кодом python. Так для того, чтобы написать list(foo) вам понадобится print list(foo).

кроме того, если вам нравится приглашение ipython (его режимы emacs и vim, история, завершения,...), легко получить то же самое для вашего проекта, поскольку он основан на python prompt toolkit.


(обновление от 28 мая 2016 г.) Использование RealGUD в Emacs

для всех в Emacs,этой теме показывает, как выполнить все описанное в OP (и многое другое) с помощью

  1. новый важный отладчик в Emacs называется RealGUD который может работать с любым отладчиком (в том числе ipdb).
  2. пакет Emacs isend-mode.

сочетание из эти 2 пакета весьма мощно и позволяет точно воссоздать поведение, описанное в OP, и сделать еще больше.

подробнее о статья wiki RealGUD для ipdb.


оригинальный ответ:

попробовав множество различных методов отладки Python, включая все, что упоминается в этом потоке, один из моих предпочтительных способов отладки Python с IPython-это встроенные оболочки.

определение пользовательского встроенного IPython Шелл:

добавьте в свой PYTHONPATH, так что метод ipsh() становится доступным.

import inspect

# First import the embed function
from IPython.terminal.embed import InteractiveShellEmbed
from IPython.config.loader import Config

# Configure the prompt so that I know I am in a nested (embedded) shell
cfg = Config()
prompt_config = cfg.PromptManager
prompt_config.in_template = 'N.In <\#>: '
prompt_config.in2_template = '   .\D.: '
prompt_config.out_template = 'N.Out<\#>: '

# Messages displayed when I drop into and exit the shell.
banner_msg = ("\n**Nested Interpreter:\n"
"Hit Ctrl-D to exit interpreter and continue program.\n"
"Note that if you use %kill_embedded, you can fully deactivate\n"
"This embedded instance so it will never turn on again")   
exit_msg = '**Leaving Nested interpreter'

# Wrap it in a function that gives me more context:
def ipsh():
    ipshell = InteractiveShellEmbed(config=cfg, banner1=banner_msg, exit_msg=exit_msg)

    frame = inspect.currentframe().f_back
    msg   = 'Stopped at {0.f_code.co_filename} at line {0.f_lineno}'.format(frame)

    # Go back one level! 
    # This is needed because the call to ipshell is inside the function ipsh()
    ipshell(msg,stack_depth=2)

затем, когда я хочу отладить что-то в своем коде, я помещаю ipsh() прямо в том месте, где мне нужно сделать осмотр объекта и т. д. Например, скажем, я хочу отладить my_function ниже

используя это:

def my_function(b):
  a = b
  ipsh() # <- This will embed a full-fledged IPython interpreter
  a = 4

и тогда я взываю my_function(2) одним из следующих способов:

  1. выполняемое Программа Python, которая вызывает эту функцию из оболочки Unix
  2. или вызывая его непосредственно из IPython

независимо от того, как я его вызываю, интерпретатор останавливается на строке, которая говорит ipsh(). Как только вы закончите, вы можете сделать Ctrl-D и Python возобновит выполнение (с любыми обновлениями переменных, которые вы сделали). Обратите внимание, что если вы запустите код из обычного IPython оболочки IPython (случай 2 выше), новая оболочка IPython будет вложенные внутри тот, из которого вы его вызвали, что совершенно нормально, но это хорошо осознавать. Eitherway, как только переводчик останавливается на месте ipsh, Я могу проверить значение a (что составляет 2), посмотрите, какие функции и объекты определены и т. д.

проблема:

решение выше может быть использовано для остановки Python в любом месте вашего кода, а затем поместите вас в полноценный интерпретатор IPython. К сожалению, это не позволяет вам добавить или удалите точки останова после вызова скрипта, что очень расстраивает. На мой взгляд, это только вещь, которая мешает IPython стать отличным инструментом отладки для Python.

лучшее, что вы можете сделать сейчас:

обходной путь должен разместить ipsh() априори в разных местах, где вы хотите, чтобы интерпретатор Python запускал оболочку IPython (т. е. breakpoint). Затем вы можете "прыгать" между различными заранее определенными, жестко "точки останова" с Ctrl-D, который выйдет из текущей встроенной оболочки IPython и снова остановится, когда интерпретатор нажмет следующий вызов ipsh().

если вы идете по этому маршруту, Один из способов выйти из "режима отладки" и игнорировать все последующие точки останова-использовать ipshell.dummy_mode = True что заставит Python игнорировать любые последующие экземпляры ipshell объект, который мы создали выше.


вы можете начать сеанс IPython с pudb и вернуться к сеансу отладки, как вам нравится.

кстати, ipdb использует IPython за кулисами, и вы можете фактически использовать функции IPython, такие как завершение вкладки и магические команды (один начинается с %). Если вы в порядке с ipdb, вы можете запустить его с IPython, используя такие команды, как %run и %debug. сеанс ipdb на самом деле лучше, чем простой IPython one в том смысле, что вы можете идти вверх и вниз в трассировка стека и т. д. Что отсутствует в ipdb для "проверки объекта"?

также, python.el в комплекте с Emacs >= 24.3 имеет хорошую поддержку ipdb.


никто не упомянул оболочкой IPython это %pdb флаг еще. Просто позвони %pdb в IPython и при возникновении ошибки вы автоматически сбрасываетесь до ipdb. Пока вы не имеете шагать немедленно, вы в ipdb потом.

это упрощает отладку отдельных функций, так как вы можете просто загрузить файл с помощью %load а затем запустите функцию. Вы можете вызвать ошибку с помощью assert в правильном положении.


похоже на подход в ответе @gaborous осуждается.

новый подход, кажется:

from IPython.core import debugger
debug = debugger.Pdb().set_trace

def buggy_method():
    debug()

префикс "!"символ команд, которые вы вводите в pdb, похоже, имеет тот же эффект, что и что-то в оболочке IPython. Это работает для доступа к справке для определенной функции или даже имен переменных. Возможно, это поможет вам в некоторой степени. Например,

ipdb> help(numpy.transpose)
*** No help on (numpy.transpose)

но !помогите (numpy.transpose) даст вам ожидаемую страницу справки на numpy.транспонировать. Аналогично для имен переменных, скажем, у вас есть переменная l, набрав " l " в pdb перечисляет код, но !л печатает значение l.


ты этот совет?

или еще лучше, используйте ipython и вызовите:

from IPython.Debugger import Tracer; debug_here = Tracer()

затем вы можете просто использовать

debug_here()

всякий раз, когда вы хотите установить точку останова


один из вариантов-использовать IDE, например Spyder что должно позволить вам взаимодействовать с вашим кодом во время отладки (фактически используя консоль IPython). На самом деле, Spyder очень похож на MATLAB, что, я полагаю, было преднамеренным. Это включает инспекторы переменных, редактирование переменных, встроенный доступ к документации и т. д.


Если вы вводите exit() в embed () console, код продолжается и переходит к следующей строке embed ().


на Pyzo IDE имеет те же возможности,что и OP. Вам не нужно начинать в режиме отладки. Подобно MATLAB, команды выполняются в оболочке. Когда вы настраиваете точку останова в какой-либо строке исходного кода, IDE останавливает выполнение там, и вы можете отлаживать и выдавать регулярные команды IPython.

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

тем не менее, исходя из MATLAB, это кажется лучшим решением, которое я нашел.


запуск изнутри IPython-оболочки Emacs и точки останова, установленной через pdb.set_trace() должен работать.

проверено с помощью python-mode.el, M-x ipython RET etc.


из Python 3.2, у вас есть interact команда, которая дает вам доступ к полному командному пространству python/ipython.


правильный, простой,прохладный, точный ответ на вопрос-использовать % run macro с флагом-D.

In [4]: run -d myscript.py
NOTE: Enter 'c' at the ipdb>  prompt to continue execution.        
> /cygdrive/c/Users/mycodefolder/myscript.py(4)<module>()
      2                                                            
      3                        
----> 4 a=1                                            
      5 b=2