Пошаговая отладка с помощью 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 130

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, и сделать еще больше.

подробнее о статья 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. либо запустив a Программа Python, которая вызывает эту функцию из оболочки Unix
  2. или вызывая его непосредственно из IPython

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

проблема:

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

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

обходной путь должен разместить ipsh() априори в разных местах, где вы хотите, чтобы интерпретатор Python запускал оболочку IPython (т. е. a 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.транспонировать) даст вам ожидаемую страницу справки по numpy.транспонировать. Аналогично для имен переменных, скажем, у вас есть переменная l, набрав "l" в pdb перечисляет код, но !l выводит значение l.

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

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

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

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

debug_here()

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

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

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

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

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

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

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

проверено с помощью python-mode.Эль, м-х с оболочкой IPython, рэт и т. д.

из 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