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

170

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

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

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

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

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

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

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

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

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

Амелио Васкес-Рейна
источник
В pdb есть !команда, которая выполняет любую команду python в точке останова
Дмитрий Галчинский
5
Я ищу отладчик Python, похожий на Matlab! Например, я делаю много прототипов в оболочке Python. Все переменные сохраняются в оболочке. Теперь я сталкиваюсь с проблемой. Я надеюсь отладить один небольшой фрагмент кода с этими вычисляемыми переменными в оболочке. Однако новый отладчик не может получить доступ к старым переменным. Это не удобно для прототипирования.
user1914692
1
Для пользователей Emacs RealGUD имеет невероятно хороший интерфейс.
Clément
1
Спасибо @ Clément Я следил за репо в течение последнего месяца, и я очень взволнован проектом :) Я еще не пробовал, но как только я (или если вы делаете), не стесняйтесь написать здесь ответ, который может быть показывает, как выполнить то, что требуется. Для справки - URL: github.com/rocky/emacs-dbgr
Амелио Васкес-Рейна
@ Clément Кроме того, если у вас есть опыт работы с RealGUD & ipdb, я попытался использовать его, как описано здесь github.com/rocky/emacs-dbgr/issues/96 без удачи.
Амелио Васкес-Рейна

Ответы:

61

Вы можете использовать %pdbмагию IPython . Просто позвоните %pdbв IPython, и когда возникнет ошибка, вы автоматически попадете на ipdb. Пока у вас нет ступеньки сразу, вы попадаете ipdbпотом.

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

%pdbэто линия магии. Назовите это , как %pdb on, %pdb 1, %pdb offили %pdb 0. Если вызывается без аргумента, он работает как переключатель.

Себастьян
источник
6
^ Это реальный ответ
Цезарь
Есть ли что-то похожее, когда pdb имеет IPython-подобную функциональность, не будучи изначально в IPython?
Лукас
4
Вы также можете запустить программу с помощью ipython --pdb file.py -- argsи исключить в ipdb после исключения. Возможно, стоит добавить к ответу.
Себастиан
110

Как насчет 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 .

Ehvince
источник
10
Это способ сделать это.
j08lue
Мой ответ теперь включает в себя, как использовать ipdbв Emacs, используя RealGUDи isend-modeделать именно то, что просит OP.
Амелио Васкес-Рейна
1
Jupyter - это не замена IPython, а ноутбук IPython. Ноутбуки Jupyter используют ядро ​​в фоновом режиме. Для ноутбуков Python ядро ​​обычно является ядром IPython. Проект IPython продолжается и дальше.
FA
1
breakpoint()это чудесно. В PyCharm он даже бросает вас в отладчик PyCharm. Это также быстрый способ войти в отладчик PyCharm из функций, которые были вставлены в консоль.
Ричард Мон
40

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

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

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

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

Больше информации о вики-статье 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 будет вложена в ту, из которой вы ее вызывали, что прекрасно, но об этом следует знать. В любом случае, когда переводчик останавливается на месте ipsh, я могу проверить значение a(которое будет2 ), посмотреть, какие функции и объекты определены, и т. Д.

Эта проблема:

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

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

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

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

Амелио Васкес-Рейна
источник
2
Пожалуйста, обновляйте это всякий раз, когда вы найдете еще лучшее решение. Но это выглядит великолепно. Я буду использовать это.
Phani
1
Где / как вы определяете / импортируете cfg, banner_msg, exit_msg и проверяете?
Гордон Бин
1
Мне кажется, это хорошо работает: github.com/kdodia/snippets/blob/master/ipsh.py
karan.dodia
1
Мне нужно было добавить, import inspectпрежде чем это сработало. Настройка командной строки, кажется, сломана для меня, хотя.
Паскаль
7
Почему бы просто не использовать import ipdb; ipdb.set_trace()? Может быть, я что-то упускаю, но я не вижу преимущества вашего гораздо более сложного метода.
Луатор
19

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

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

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

ТКФ
источник
1
Спасибо ткф. Я большой поклонник вашего пакета Emacs-Jedi. Когда вы сказали, что Emacs 24.3 имеет хорошую поддержку ipdb, вы не могли бы остановиться? Обычно я запускаю IPython в отдельном M-x ansi-termбуфере, а затем использую isend-mode, чтобы привязать мои исходные буферы к буферу IPython, чтобы я мог отправить код интерпретатору IPython с помощью сочетания клавиш, который автоматически отправляет %pasteмагию в буфер IPython. Это позволяет мне быстро тестировать регионы в IPython. Я всегда запускаю свои программы из этой оболочки IPython runи использую embed()для остановки.
Амелио Васкес-Рейна
3
Например, когда вы шагаете по коду, исходный код открывается в другом буфере со стрелкой, указывающей текущую точку выполнения. У вас также есть команда send-region, как и в isend-mode.
tkf
Спасибо @tkf. Как я могу начать ipdbсеанс отладки, используя python.elтакие вещи и send-regionт.п. в соответствующей оболочке? В общем, где я могу найти больше информации об этом?
Амелио Васкес-Рейна
Я использую %runили %debug. Я думаю, import ipdb; ipdb.set_trace()работает тоже. Я не думаю, что вы можете отправить несколько строк в ipdb. Это ограничение ipdb. Но, вероятно, %pasteтрюк работает. Вы можете отправить запрос на добавление в IPython dev.
TKF
13

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

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

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

def buggy_method():
    debug()
Эллиот Ларсон
источник
ipdbПозволяет ли это использовать операторы внутри оболочки ipython при отладке?
alpha_989
6

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

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

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

user1953384
источник
2
Это неприятная ошибка в pdb, о которой стоит знать.
Уилфред Хьюз
4

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

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

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

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

debug_here()

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

gaborous
источник
4

Вы можете начать IPython изнутри ipdb !

Индуцировать ipdbотладчик 1 :

import idpb; ipdb.set_trace()

Введите IPython изнутри в ipdb>консоли 2 :

from IPython import embed; embed()

Вернитесь к ipdb>консоли изнутри IPython:

exit

Если вам повезло использовать Emacs, все станет еще удобнее!

Это требует использования M-x shell. Используя yasnippetи bm , определите следующий фрагмент. Это заменит текст ipdbв редакторе на set-traceстроку. После вставки фрагмента строка будет выделена, чтобы ее было легко заметить и с которой можно было ориентироваться. Используйте M-x bm-nextдля навигации.

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

1 Все в одной строке для легкого удаления. Так importsкак это происходит только один раз, эта форма гарантирует, ipdbчто будет импортирована, когда вам это нужно, без лишних затрат.

2 Вы можете сэкономить при печати, импортировав IPython в свой .pdbrcфайл :

try:
    from IPython import embed
except:
    pass

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

Лорем Ипсум
источник
3

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

Бенджамин Б.
источник
3

правильный, легкий, классный, точный ответ на вопрос - использовать макрос% 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
пинг
источник
Это должно быть выше. Если мне нужно вставить строку в мой код (внутри модуля), мне нужно перезапустить IPython, чтобы перезагрузить модуль. С этим я могу просто %save /tmp/foo.py x-yкод, который я хочу запустить и отладить, а затем %run -d /tmp/foo.pyустановить точку останова, где мне нравится. Отличный ответ!
Ромео Валентин
2

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

Турин Турамбар
источник
2

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

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

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

user6715080
источник
2

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

alpha_989
источник
1

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

Проверяется с помощью python-mode.el, Mx ipython RET и т. Д.

Андреас Рёлер
источник
1

Разработка нового кода

Отладка внутри IPython

  1. Используйте выполнение ячеек Jupyter / IPython для ускорения экспериментов.
  2. Используйте %% debug для выполнения шага

Пример ячейки:

%%debug
...: for n in range(4):
...:    n>2

Отладка существующего кода

IPython внутри отладки

  1. Отладка сломанного юнит-теста: pytest ... --pdbcls=IPython.terminal.debugger:TerminalPdb --pdb
  2. Отладка вне теста: breakpoint(), python -m ipdbи т.д.
  3. IPython.embed () для полной функциональности IPython, где это необходимо, находясь в отладчике

Мысли о Python

Я согласен с ОП в том, что многие вещи, которые MATLAB хорошо выполняет в Python, по-прежнему отсутствуют и действительно должны, поскольку практически все в языке поддерживает скорость разработки по сравнению со скоростью производства. Возможно, когда-нибудь я внесу не только тривиальные исправления ошибок в CPython.

https://github.com/ipython/ipython/commit/f042f3fea7560afcb518a1940daa46a72fbcfa68

Смотрите также Возможно ли запускать команды в IPython с отладкой?

SwimBikeRun
источник