Покрокова налагодження за допомогою IPython


170

З того, що я прочитав, є два способи налагодження коду в Python:

  • З традиційним налагоджувачем, таким як pdbабо ipdb. Він підтримує команди, такі як cfor continue, nfor step-over, sfor step-intoetc.), але у вас немає прямого доступу до оболонки 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
Я шукаю налагоджувача пітона, схожого на Matlab! Наприклад, я багато прототипую в оболонці python. Всі змінні зберігаються разом із оболонкою. Зараз я зустрічаю проблему. Я сподіваюся налагодити один невеликий шматок коду, з тими обчисленими змінними разом із оболонкою. Однак новий відладчик не може отримати доступ до старих змінних. Це не зручно для прототипування.
користувач1914692

1
Для користувачів Emacs RealGUD має неймовірно хороший інтерфейс.
Clément

1
Дякую @ Clément, я слідкував за репо за останній місяць, і я дуже в захваті від проекту :) Я ще не пробував цього, але як тільки я (або якщо ви це зробите) не соромтесь написати відповідь тут, що, можливо показує, як виконати те, що потрібно. Для довідок для інших, URL-адреса github.com/rocky/emacs-dbgr
Амеліо Васкес-Рейна

@ Clément Крім того, якщо у вас є досвід роботи з RealGUD & ipdb, я намагався використовувати його, як пояснено тут, github.com/rocky/emacs-dbgr/isissue/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змінній середовища. Ця функція походить від цього PEP .

Це дозволяє провести повний огляд вашого коду, і у вас є доступ до таких команд, як c(продовжувати), n(виконувати наступний рядок), s(крок до методу в точці) тощо.

Дивіться репортаж ipdb та список команд . IPython зараз називається (редагувати: частина) Юпітера .


ps: зауважте, що команда ipdb має перевагу над кодом python. Тож для того, щоб написати, list(foo)вам потрібно print list(foo).

Крім того, якщо вам подобається підказка ipython (її режими emacs та vim, історія, доповнення,…), то легко отримати те саме для вашого проекту, оскільки він заснований на інструментарії підказки python .


10
Це спосіб це зробити.
j08lue

Зараз моя відповідь включає, як використовувати ipdbв Emacs використання RealGUDта isend-modeробити саме те, що вимагає ОП.
Амеліо Васкес-Рейна

1
Jupyter - це не заміна для IPython, а для IPython Notebook. У ноутбуках Юпітера використовується ядро ​​у фоновому режимі. Для ноутбука Python ядро ​​зазвичай ядро ​​IPython. Проект IPython продовжується далі.
FA

1
breakpoint()чудово. У PyCharm це навіть потрапляє в налагоджувач PyCharm. Це також швидкий спосіб ввести налагоджувач PyCharm з функцій, які були вставлені в консоль.
Річард

40

(Оновлення 28 травня 2016 р.) Використання RealGUD в Emacs

Для всіх, хто в Emacs, цей потік показує, як виконати все, що описано в ОП (і більше), використовуючи

  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()апріорі розмістити в різних місцях, де ви хочете, щоб інтерпретатор Python запустив оболонку IPython (тобто a breakpoint). Потім ви можете "перестрибувати" між різними заздалегідь визначеними жорстко кодованими "точками розриву" за допомогою Ctrl-D, які вийшли б із поточної вбудованої оболонки IPython і знову зупиняються, коли інтерпретатор потрапляє на наступний дзвінок ipsh().

Якщо ви йдете цим маршрутом, одним із способів виходу з «режиму налагодження» та ігнорування всіх наступних точок прориву є використання, ipshell.dummy_mode = Trueяке змусить Python ігнорувати будь-які наступні моменти ipshellоб’єкта, який ми створили вище.


2
Будь ласка, оновлюйте це щоразу, коли знайдете ще краще рішення. Але це виглядає чудово. Я цим скористаюся.
Фані

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 і повернутися до сеансу налагодження, як вам завгодно.

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

Крім того, python.el у комплекті з Emacs> = 24.3 має хорошу підтримку ipdb.


1
Дякую tkf. Я великий фанат вашого пакету Emacs-Jedi. Коли ви сказали, що Emacs 24.3 має гарну підтримку ipdb, чи не заперечуєте ви докладно? Зазвичай я запускаю IPython в окремому M-x ansi-termбуфері, а потім використовую isend-режим щоб прив’язати свої вихідні буфери до буфера IPython, щоб я міг надіслати код інтерпретатору IPython за допомогою комбінації клавіш, яка автоматично надсилає %pasteмагію до буфера IPython. Це дозволяє мені швидко перевірити регіони в IPython. Я завжди запускаю свої програми з цієї оболонки IPython runі використовую embed()для зупинки.
Амеліо Васкес-Рейна

3
Наприклад, при перегляді коду вихідний код відкривається в іншому буфері зі стрілкою, що вказує на поточну точку виконання. Ви також маєте команду send-region, як і в режимі isend.
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.


2
Це неприємний pcb gotcha, і про нього варто знати.
Вільфред Х'юз

4

Ви спробували цю пораду ?

Або ще краще, використовуйте ipython та телефонуйте:

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

то можна просто використовувати

debug_here()

всякий раз, коли ви хочете встановити точку перерви


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, це здається найкращим рішенням, що я знайшов.


2

З python 3.2 ви маєте interactкоманду, яка надає вам доступ до повного простору команд python / ipython.


1

Запуск зсередини Emacs 'IPython-оболонки та точки зупинки через pdb.set_trace () повинен працювати.

Перевірено за допомогою python-mode.el, Mx ipython RET тощо.


1

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

Налагодження всередині IPython

  1. Використовуйте виконання клітинок Jupyter / IPython для прискорення ітерацій експерименту
  2. Використовуйте налагодження %% для проходження крок

Приклад комірки:

%%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 там, де це потрібно під час налагодження

Думки про Пітона

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

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

Див. Також Чи можна запускати команди в IPython з налагодженням?

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.