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

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

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

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

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

  • Уметь шаг за шагом через ваш код с удобными сочетаниями клавиш pdb/ipdb.
  • На любом таком этапе (например, в заданном операторе) есть доступ к полнофункциональной оболочке IPython.

Отладка IPython, как в MATLAB:

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

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

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

Ответ 1

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

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

Ответ 2

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

import ipdb; ipdb.set_trace()

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

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

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


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

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

Ответ 3

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

Для всех в Emacs этот поток показывает, как выполнить все, что описано в OP (и более), используя

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

Комбинация этих двух пакетов чрезвычайно эффективна и позволяет точно воссоздать поведение, описанное в OP, и сделать еще больше.

Дополнительная информация о статье wiki RealGUD для ipdb.


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

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

Определение настраиваемой встроенной оболочки IPython:

Добавьте в < PYTHONPATH следующее на script, чтобы метод 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) одним из следующих способов:

  • Либо запустить программу Python, которая вызывает эту функцию из оболочки Unix
  • Или путем вызова его непосредственно из IPython

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

Проблема:

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

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

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

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

Ответ 4

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

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

Кроме того, python.el в комплекте с Emacs >= 24.3 имеет приятную поддержку ipdb.

Ответ 5

Похож на подход в @gaborous ответе устарел.

Новый подход выглядит следующим образом:

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

def buggy_method():
    debug()

Ответ 6

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

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

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

Ответ 7

Вы попробовали этот совет?

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

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

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

debug_here()

когда вы хотите установить точку останова

Ответ 8

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

Ответ 9

правильный, легкий, классный, точный ответ на вопрос - использовать макрос% run с флагом -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

Ответ 10

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

Ответ 11

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

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

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

Ответ 12

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

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

Ответ 13

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

Ответ 14

IPython.embed() явно не говорилось, что вы можете вызывать IPython.embed() из ipdb.

ipdb отлично подходит для пошагового выполнения кода и для его самонаблюдения, но он не справляется с другими вещами, например, с несколькими строками. IPython хорошо обрабатывает многострочные операторы.

Чтобы вызвать отладчик, вызовите в своем коде следующее:

import idpb; ipdb.set_trace()

Это учитывает самоанализ и степпинг.

Если вам нужна функциональность IPython, позвоните в ipdb>:

from IPython import embed; embed()

Что касается Emacs, здесь мое уникальное решение, которое, я надеюсь, вдохновляет вас.

Я использую Emacs с Mx shell. У меня есть yassnippet определенный для ipdb который вызывает отладчик. Пакет bm выделяет строку, чтобы я не забыл удалить ее из исходного кода:

# -*- mode: snippet -*-
# name: ipdb
# key: ipdb
# expand-env: ((yas-after-exit-snippet-hook #'bm-toggle))
# --
import ipdb; ipdb.set_trace()

Я импортирую IPython в мой файл .pdbrc:

try:
    from IPython import embed
except:
    pass

Это позволяет мне вызывать embed() из любого экземпляра ipdb (когда установлен IPython).