Từng bước gỡ lỗi với IPython


170

Từ những gì tôi đã đọc, có hai cách để gỡ lỗi mã trong Python:

  • Với một trình gỡ lỗi truyền thống như pdbhoặc ipdb. Điều này hỗ trợ các lệnh nhưc cho continue, ncho step-over, scho step-intovv), nhưng bạn không có quyền truy cập trực tiếp đến một vỏ IPython mà có thể cực kỳ hữu ích cho việc kiểm tra đối tượng.

  • Sử dụng IPython bằng cách nhúng vỏ IPython trong mã của bạn. Bạn có thể làm from ipython import embed, và sau đó sử dụng embed()trong mã của bạn. Khi chương trình / tập lệnh của bạn gặp phải một embed()câu lệnh, bạn sẽ rơi vào trình bao IPython. Điều này cho phép kiểm tra đầy đủ các đối tượng và kiểm tra mã Python bằng cách sử dụng tất cả các tính năng của IPython. Tuy nhiên, khi sử dụng, embed()bạn không thể từng bước thông qua mã nữa bằng các phím tắt tiện dụng.

Có cách nào để kết hợp tốt nhất của cả hai thế giới không? I E

  1. Có thể từng bước thông qua mã của bạn bằng các phím tắt pdb / ipdb tiện dụng.
  2. Tại bất kỳ bước nào như vậy (ví dụ: trên một tuyên bố nhất định), có quyền truy cập vào vỏ IPython chính thức .

Gỡ lỗi IPython như trong MATLAB:

Có thể tìm thấy một ví dụ về loại "gỡ lỗi nâng cao" này trong MATLAB, nơi người dùng luôn có quyền truy cập đầy đủ vào công cụ / vỏ MATLAB và cô ấy vẫn có thể từng bước thông qua mã của mình, xác định các điểm dừng có điều kiện, v.v. Những gì tôi đã thảo luận với những người dùng khác, đây là tính năng gỡ lỗi mà mọi người nhớ nhất khi chuyển từ MATLAB sang IPython.

Gỡ lỗi IPython trong Emacs và các trình soạn thảo khác:

Tôi không muốn làm cho câu hỏi quá cụ thể, nhưng tôi làm việc chủ yếu trong Emacs, vì vậy tôi tự hỏi liệu có cách nào để đưa chức năng này vào đó không. Lý tưởng nhất là Emacs (hoặc trình soạn thảo) sẽ cho phép lập trình viên đặt các điểm dừng ở bất kỳ đâu trên mã và liên lạc với trình thông dịch hoặc trình gỡ lỗi để nó dừng ở vị trí bạn chọn và đưa đến trình thông dịch IPython đầy đủ trên vị trí đó.


pdb có !lệnh thực thi bất kỳ lệnh python nào tại breakpoint
Dmitry Galchinsky

5
Tôi cũng đang tìm kiếm một trình gỡ lỗi python tương tự như Matlab! Ví dụ, tôi làm rất nhiều nguyên mẫu trong vỏ trăn. Tất cả các biến được lưu với shell. Bây giờ tôi gặp một vấn đề. Tôi hy vọng sẽ gỡ lỗi một đoạn mã nhỏ, với các biến được tính toán với vỏ. Tuy nhiên, một trình gỡ lỗi mới không thể truy cập các biến cũ. Nó không thuận tiện cho việc tạo mẫu.
dùng1914692

1
Đối với người dùng Emacs, RealGUD có giao diện cực kỳ tốt.
Clément

1
Cảm ơn @ Clément Tôi đã theo dõi repo trong tháng qua và tôi rất hào hứng với dự án :) Tôi chưa thử nó, nhưng một khi tôi làm (hoặc nếu bạn làm) hãy thoải mái viết câu trả lời ở đây mà có lẽ chỉ ra cách thực hiện những gì được yêu cầu. Đối với những người khác để tham khảo, URL là github.com/rocky/emacs-dbgr
Amelio Vazquez-Reina

@ Clément Ngoài ra, nếu bạn có bất kỳ kinh nghiệm nào với RealGUD & ipdb, tôi đã thử sử dụng nó như được giải thích ở đây github.com/rocky/emacs-dbgr/issues/96 mà không gặp may.
Amelio Vazquez-Reina

Câu trả lời:


60

Bạn có thể sử dụng %pdbphép thuật của IPython . Chỉ cần gọi %pdbIPython và khi xảy ra lỗi, bạn sẽ tự động rơi vào ipdb. Mặc dù bạn không có bước đi ngay lập tức, nhưng ipdbsau đó bạn sẽ tham gia .

Điều này làm cho việc gỡ lỗi các chức năng riêng lẻ trở nên dễ dàng, vì bạn chỉ có thể tải một tệp %loadvà sau đó chạy một chức năng. Bạn có thể buộc một lỗi với một assertvị trí phù hợp.

%pdblà một phép thuật dòng. Gọi nó như %pdb on, %pdb 1, %pdb offhoặc %pdb 0. Nếu được gọi mà không có đối số, nó hoạt động như một chuyển đổi.


6
^ Đây là câu trả lời thực sự
Cesar

Có điều gì đó tương tự khi pdb có chức năng như IPython mà không có trong IPython ban đầu không?
Lucas

4
Bạn cũng có thể bắt đầu chương trình với ipython --pdb file.py -- argsvà được thả xuống ipdb khi có ngoại lệ. Có thể đáng để thêm vào câu trả lời.
sebastian

110

Còn ipdb.set_trace () thì sao? Trong mã của bạn:

import ipdb; ipdb.set_trace()

cập nhật : bây giờ trong Python 3.7, chúng ta có thể viết breakpoint(). Nó hoạt động như nhau, nhưng nó cũng tuân theo PYTHONBREAKPOINTbiến môi trường. Tính năng này đến từ PEP này .

Điều này cho phép kiểm tra toàn bộ mã của bạn và bạn có quyền truy cập vào các lệnh như c(tiếp tục), n(thực hiện dòng tiếp theo),s (bước vào phương thức tại điểm), v.v.

Xem repo ipdbdanh sách các lệnh . IPython hiện được gọi là (chỉnh sửa: một phần của) Jupyter .


ps: lưu ý rằng một lệnh ipdb được ưu tiên hơn mã python. Vì vậy, để viết list(foo)bạn cầnprint list(foo) .

Ngoài ra, nếu bạn thích dấu nhắc ipython (chế độ emacs và vim, lịch sử, hoàn thành, của nó) thì thật dễ dàng để có được điều tương tự cho dự án của bạn vì nó dựa trên bộ công cụ nhắc nhở python .


10
Đây là cách để làm điều đó.
08

Câu trả lời của tôi bây giờ bao gồm cách sử dụng ipdbtrong Emacs bằng cách sử dụng RealGUDisend-modethực hiện chính xác những gì OP yêu cầu.
Amelio Vazquez-Reina

1
Jupyter không phải là sự thay thế cho IPython, mà là IPython Notebook. Máy tính xách tay Jupyter sử dụng kernel trong nền. Đối với notebook Python, kernel thường là kernel IPython. Các dự án IPython tiếp tục thêm.
FA

1
breakpoint()thật tuyệt vời Trong PyCharm, nó thậm chí thả bạn vào trình gỡ lỗi PyCharm. Đây cũng là một cách nhanh chóng để vào trình gỡ lỗi PyCharm từ các chức năng được dán vào bảng điều khiển.
Richard Möhn

40

(Cập nhật ngày 28 tháng 5 năm 2016) Sử dụng RealGUD trong Emacs

Đối với bất kỳ ai trong Emacs, chuỗi này cho biết cách hoàn thành mọi thứ được mô tả trong OP (và hơn thế nữa) bằng cách sử dụng

  1. một trình gỡ lỗi quan trọng mới trong Emacs được gọi là RealGUD có thể hoạt động với bất kỳ trình gỡ lỗi nào (bao gồmipdb ).
  2. Gói Emacs isend-mode.

Sự kết hợp của hai gói này cực kỳ mạnh mẽ và cho phép một người tái tạo chính xác hành vi được mô tả trong OP và thậm chí còn làm nhiều hơn thế.

Thông tin thêm về bài viết wiki của RealGUD cho ipdb.


Câu trả lời gốc:

Sau khi đã thử nhiều phương pháp khác nhau để gỡ lỗi Python, bao gồm mọi thứ được đề cập trong luồng này, một trong những cách gỡ lỗi ưa thích của tôi với Python bằng IPython là với các shell nhúng.

Xác định trình bao IPython nhúng tùy chỉnh:

Thêm phần sau vào tập lệnh vào tập lệnh của bạn PYTHONPATHđể phương thức ipsh()trở nên khả dụng.

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)

Sau đó, bất cứ khi nào tôi muốn gỡ lỗi một cái gì đó trong mã của mình, tôi đặt ipsh()ngay tại vị trí mà tôi cần kiểm tra đối tượng, v.v. Ví dụ: giả sử tôi muốn gỡ lỗimy_function bên dưới

Sử dụng nó:

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

và sau đó tôi gọi my_function(2)theo một trong những cách sau:

  1. Hoặc bằng cách chạy chương trình Python gọi hàm này từ shell Unix
  2. Hoặc bằng cách gọi nó trực tiếp từ IPython

Bất kể tôi gọi nó như thế nào, thông dịch viên dừng lại ở dòng nói ipsh(). Khi bạn đã hoàn tất, bạn có thể làm Ctrl-Dvà Python sẽ tiếp tục thực hiện (với bất kỳ cập nhật biến nào mà bạn đã thực hiện). Lưu ý rằng, nếu bạn chạy mã từ IPython thông thường, vỏ IPython (trường hợp 2 ở trên), vỏ IPython mới sẽ được lồng bên trong cái mà bạn đã gọi nó, điều này hoàn toàn tốt, nhưng bạn nên biết. Dù sao, một khi trình thông dịch dừng ở vị trí của ipsh, tôi có thể kiểm tra giá trị của a(đó là 2), xem các chức năng và đối tượng được xác định, v.v.

Vấn đề:

Giải pháp ở trên có thể được sử dụng để Python dừng bất cứ nơi nào bạn muốn trong mã của mình và sau đó thả bạn vào một trình thông dịch IPython chính thức. Thật không may, nó không cho phép bạn thêm hoặc xóa các điểm dừng một khi bạn gọi tập lệnh, điều này rất khó chịu. Theo tôi, đây là duy nhất điều nhất ngăn IPython trở thành một công cụ sửa lỗi tuyệt vời cho Python.

Điều tốt nhất bạn có thể làm bây giờ:

Cách giải quyết là đặt ipsh()một ưu tiên tại các vị trí khác nhau nơi bạn muốn trình thông dịch Python khởi chạy trình bao IPython (tức là a breakpoint). Sau đó, bạn có thể "nhảy" giữa các "điểm dừng" được mã hóa cứng được xác định trước khác nhau Ctrl-D, để thoát khỏi vỏ IPython được nhúng hiện tại và dừng lại bất cứ khi nào trình thông dịch nhấn vào cuộc gọi tiếp theoipsh() .

Nếu bạn đi theo tuyến đường này, một cách để thoát khỏi "chế độ gỡ lỗi" và bỏ qua tất cả các điểm dừng tiếp theo, là sử dụng ipshell.dummy_mode = Truenó sẽ khiến Python bỏ qua mọi cảnh báo tiếp theo của ipshellđối tượng mà chúng ta đã tạo ở trên.


2
Vui lòng cập nhật điều này bất cứ khi nào bạn tìm thấy một giải pháp thậm chí tốt hơn. Nhưng điều này có vẻ tuyệt vời. Tôi sẽ dùng nó.
Phani

1
Ở đâu / làm thế nào để bạn xác định / nhập cfg, banner_msg, exit_msg và kiểm tra?
Gordon Bean

1
Điều này có vẻ hoạt động tốt đối với tôi: github.com/kdodia/snippets/blob/master/ipsh.py
karan.dodia 18/12/14

1
Tôi cần phải thêm import inspecttrước khi nó hoạt động. Các tùy chỉnh dòng lệnh dường như bị phá vỡ đối với tôi mặc dù.
Pascal

7
Tại sao không chỉ sử dụng import ipdb; ipdb.set_trace()? Có thể tôi bỏ lỡ điều gì đó, nhưng tôi không thấy lợi thế của phương pháp phức tạp hơn nhiều của bạn.
luator

19

Bạn có thể bắt đầu phiên IPython từ pudb và quay lại phiên gỡ lỗi như bạn muốn.

BTW, ipdb đang sử dụng IPython đằng sau hậu trường và bạn thực sự có thể sử dụng chức năng IPython như hoàn thành TAB và các lệnh ma thuật (bắt đầu bằng %). Nếu bạn ổn với ipdb, bạn có thể khởi động nó từ IPython bằng các lệnh như %run%debug. Phiên ipdb thực sự tốt hơn phiên IPython đơn giản theo nghĩa bạn có thể đi lên và xuống trong theo dõi ngăn xếp, v.v. Điều gì còn thiếu trong ipdb cho "kiểm tra đối tượng"?

Ngoài ra, python.el đi kèm với Emacs> = 24.3 có hỗ trợ ipdb đẹp.


1
Cảm ơn tkf. Tôi là fan hâm mộ lớn của gói Emacs-Jedi của bạn. Khi bạn nói rằng Emacs 24.3 có hỗ trợ tốt cho ipdb, bạn có phiền khi xây dựng không? Tôi thường khởi động IPython trong một M-x ansi-termbộ đệm riêng và sau đó tôi sử dụng chế độ isend để liên kết bộ đệm nguồn của mình với bộ đệm IPython để tôi có thể gửi mã đến trình thông dịch IPython bằng phím tắt tự động gửi %pastema thuật đến bộ đệm IPython. Điều này cho phép tôi nhanh chóng kiểm tra các vùng trong IPython. Tôi luôn chạy các chương trình của mình từ vỏ IPython này runvà sử dụng embed()để dừng.
Amelio Vazquez-Reina

3
Ví dụ, khi bạn bước qua mã, mã nguồn được mở trong bộ đệm khác với mũi tên chỉ điểm thực hiện hiện tại. Bạn cũng có lệnh vùng gửi, giống như bạn làm với chế độ isend.
tkf

Cảm ơn @tkf Làm cách nào tôi có thể bắt đầu một ipdbphiên gỡ lỗi bằng cách sử dụng python.elvà có những thứ như send-regionvv vào trình bao tương ứng? Nói chung, tôi có thể tìm thêm thông tin về điều này ở đâu?
Amelio Vazquez-Reina

Tôi sử dụng %runhoặc %debug. Tôi đoán import ipdb; ipdb.set_trace()làm việc quá. Tôi không nghĩ bạn có thể gửi nhiều dòng tới ipdb. Đó là hạn chế của ipdb. Nhưng có lẽ %pastelừa hoạt động. Bạn có thể muốn gửi một yêu cầu tính năng cho nhà phát triển IPython.
tkf

13

Có vẻ như cách tiếp cận trong câu trả lời của @ gabious bị phản đối .

Cách tiếp cận mới dường như là:

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

def buggy_method():
    debug()

Điều này có cho phép bạn sử dụng các ipdbcâu lệnh trong ipython shell trong khi gỡ lỗi không?
alpha_989

6

Tiền tố một "!" biểu tượng cho các lệnh bạn nhập vào pdb dường như có tác dụng tương tự như làm một cái gì đó trong vỏ IPython. Điều này hoạt động để truy cập trợ giúp cho một chức năng nhất định, hoặc thậm chí tên biến. Có lẽ điều này sẽ giúp bạn ở một mức độ nào đó. Ví dụ,

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

Nhưng! Help (numpy.transpose) sẽ cung cấp cho bạn trang trợ giúp dự kiến ​​trên numpy.transpose. Tương tự cho các tên biến, giả sử bạn có một biến l, gõ "l" trong pdb liệt kê mã, nhưng! L in giá trị của l.


2
Đây là một gotcha pdb khó chịu, và cũng đáng để biết.
Wilfred Hughes

4

Bạn đã thử mẹo này ?

Hoặc tốt hơn nữa, sử dụng ipython và gọi:

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

sau đó bạn chỉ có thể sử dụng

debug_here()

bất cứ khi nào bạn muốn thiết lập một điểm dừng


4

Bạn có thể bắt đầu IPython từ bên trong ipdb !

Gây ra ipdbtrình gỡ lỗi 1 :

import idpb; ipdb.set_trace()

Nhập IPython từ bên trong trong ipdb>bảng điều khiển 2 :

from IPython import embed; embed()

Quay trở lại ipdb>bàn điều khiển từ bên trong IPython:

exit

Nếu bạn đủ may mắn để sử dụng Emacs, mọi thứ có thể trở nên thuận tiện hơn nữa!

Điều này đòi hỏi phải sử dụng M-x shell. Sử dụng yasnippetbm , xác định đoạn mã sau. Điều này sẽ thay thế văn bản ipdbtrong trình soạn thảo với set-tracedòng. Sau khi chèn đoạn mã, dòng sẽ được tô sáng để dễ dàng nhận thấy và điều hướng. Sử dụng M-x bm-nextđể điều hướng.

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

1 Tất cả trên một dòng để xóa dễ dàng. Vì importschỉ xảy ra một lần, hình thức này đảm bảoipdb sẽ được nhập khi bạn cần mà không cần thêm chi phí.

2 Bạn có thể tự lưu một số thao tác gõ bằng cách nhập IPython trong .pdbrctệp của mình :

try:
    from IPython import embed
except:
    pass

Điều này cho phép bạn chỉ cần gọi embed()từ bên trong ipdb(tất nhiên, chỉ khi IPython được cài đặt).


3

Một tùy chọn là sử dụng IDE như Spyder , cho phép bạn tương tác với mã của mình trong khi gỡ lỗi (trên thực tế sử dụng bảng điều khiển IPython). Trên thực tế, Spyder rất giống MATLAB, mà tôi cho là có chủ ý. Điều đó bao gồm các thanh tra biến, chỉnh sửa biến, truy cập tích hợp vào tài liệu, v.v.


3

câu trả lời đúng, dễ, mát, chính xác cho câu hỏi là sử dụng% run macro với cờ -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

Điều này nên được cao hơn lên. Nếu tôi cần chèn một dòng vào mã của mình (trong một mô-đun), tôi cần khởi động lại IPython để tải lại mô-đun. Trong trường hợp này, tôi chỉ có thể %save /tmp/foo.py x-yMã tôi muốn chạy và gỡ lỗi và sau đó %run -d /tmp/foo.pyđặt điểm dừng bất cứ nơi nào tôi muốn. Câu trả lời chính xác!
Romeo Valentin

2

Nếu bạn nhập exit () trong bảng điều khiển embed (), mã sẽ tiếp tục và đi đến dòng embed () tiếp theo.


2

các Pyzo IDE có khả năng tương tự như OP yêu cầu. Bạn không phải bắt đầu trong chế độ gỡ lỗi. Tương tự như MATLAB, các lệnh được thực thi trong trình bao. Khi bạn thiết lập một điểm dừng trong một số dòng mã nguồn, IDE sẽ dừng thực thi ở đó và bạn cũng có thể gỡ lỗi và phát hành các lệnh IPython thông thường.

Tuy nhiên, dường như bước vào không (chưa?) Hoạt động tốt (nghĩa là dừng ở một dòng và sau đó bước vào một chức năng khác) trừ khi bạn thiết lập một điểm dừng khác.

Tuy nhiên, đến từ MATLAB, đây có vẻ là giải pháp tốt nhất tôi tìm thấy.


2

Từ python 3.2, bạn có interactlệnh, cho phép bạn truy cập vào không gian lệnh python / ipython đầy đủ.


1

Chạy từ bên trong IPython-shell và breakpoint được đặt thông qua pdb.set_trace () sẽ hoạt động.

Đã kiểm tra với python-mode.el, Mx ipython RET, v.v.


1

Phát triển mã mới

Gỡ lỗi bên trong IPython

  1. Sử dụng thực thi tế bào Jupyter / IPython để tăng tốc độ lặp lại thử nghiệm
  2. Sử dụng gỡ lỗi %% cho từng bước

Ví dụ về ô:

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

Gỡ lỗi mã hiện có

IPython bên trong gỡ lỗi

  1. Gỡ lỗi một bài kiểm tra đơn vị bị hỏng: pytest ... --pdbcls=IPython.terminal.debugger:TerminalPdb --pdb
  2. Gỡ rối bên ngoài trường hợp thử nghiệm: breakpoint(), python -m ipdbvv
  3. IPython.embed () cho chức năng IPython đầy đủ khi cần trong trình gỡ lỗi

Suy nghĩ về Python

Tôi đồng ý với OP rằng nhiều thứ MATLAB làm tốt mà Python vẫn không có và thực sự nên vì mọi thứ trong ngôn ngữ đều ưu tiên tốc độ phát triển hơn tốc độ sản xuất. Có thể một ngày nào đó tôi sẽ đóng góp nhiều hơn những sửa lỗi nhỏ cho CPython.

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

Xem thêm Có thể chạy các lệnh trong IPython với gỡ lỗi không?

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.