IPython has another way to start a debugger. You don't need to modify the source code of any file as we did before. If you run the %run -d filename.py magic command, IPython will execute the filename.py file and put a breakpoint on the first line there. It's just as if you would put the import ipdb; ipdb.
Debug code in Jupyter notebooksSet the breakpoints in the selected cell and press Alt + Shift + Enter for Windows or ⌥⇧↩ for macOS. Alternatively, you can right-click the cell and select Debug Cell from the context menu. The Jupyter Notebook Debugger tool window opens.
What about ipdb.set_trace() ? In your code :
import ipdb; ipdb.set_trace()
update: now in Python 3.7, we can write breakpoint()
. It works the same, but it also obeys to the PYTHONBREAKPOINT
environment variable. This feature comes from this PEP.
This allows for full inspection of your code, and you have access to commands such as c
(continue), n
(execute next line), s
(step into the method at point) and so on.
See the ipdb repo and a list of commands. IPython is now called (edit: part of) Jupyter.
ps: note that an ipdb command takes precedence over python code. So in order to write list(foo)
you'd need print(list(foo))
, or !list(foo)
.
Also, if you like the ipython prompt (its emacs and vim modes, history, completions,…) it's easy to get the same for your project since it's based on the python prompt toolkit.
You can use IPython's %pdb
magic. Just call %pdb
in IPython and when an error occurs, you're automatically dropped to ipdb
. While you don't have the stepping immediately, you're in ipdb
afterwards.
This makes debugging individual functions easy, as you can just load a file with %load
and then run a function. You could force an error with an assert
at the right position.
%pdb
is a line magic. Call it as %pdb on
, %pdb 1
, %pdb off
or %pdb 0
. If called without argument it works as a toggle.
For anyone in Emacs, this thread shows how to accomplish everything described in the OP (and more) using
ipdb
).isend-mode
.The combination of these two packages is extremely powerful and allows one to recreate exactly the behavior described in the OP and do even more.
More info on the wiki article of RealGUD for ipdb.
After having tried many different methods for debugging Python, including everything mentioned in this thread, one of my preferred ways of debugging Python with IPython is with embedded shells.
Add the following on a script to your PYTHONPATH
, so that the method ipsh()
becomes available.
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)
Then, whenever I want to debug something in my code, I place ipsh()
right at the location where I need to do object inspection, etc. For example, say I want to debug my_function
below
def my_function(b):
a = b
ipsh() # <- This will embed a full-fledged IPython interpreter
a = 4
and then I invoke my_function(2)
in one of the following ways:
Regardless of how I invoke it, the interpreter stops at the line that says ipsh()
. Once you are done, you can do Ctrl-D
and Python will resume execution (with any variable updates that you made). Note that, if you run the code from a regular IPython the IPython shell (case 2 above), the new IPython shell will be nested inside the one from which you invoked it, which is perfectly fine, but it's good to be aware of. Eitherway, once the interpreter stops on the location of ipsh
, I can inspect the value of a
(which be 2
), see what functions and objects are defined, etc.
The solution above can be used to have Python stop anywhere you want in your code, and then drop you into a fully-fledged IPython interpreter. Unfortunately it does not let you add or remove breakpoints once you invoke the script, which is highly frustrating. In my opinion, this is the only thing that is preventing IPython from becoming a great debugging tool for Python.
A workaround is to place ipsh()
a priori at the different locations where you want the Python interpreter to launch an IPython shell (i.e. a breakpoint
). You can then "jump" between different pre-defined, hard-coded "breakpoints" with Ctrl-D
, which would exit the current embedded IPython shell and stop again whenever the interpreter hits the next call to ipsh()
.
If you go this route, one way to exit "debugging mode" and ignore all subsequent breakpoints, is to use ipshell.dummy_mode = True
which will make Python ignore any subsequent instantiations of the ipshell
object that we created above.
You can start IPython session from pudb and go back to the debugging session as you like.
BTW, ipdb is using IPython behind the scenes and you can actually use IPython functionality such as TAB completion and magic commands (the one starts with %
). If you are OK with ipdb you can start it from IPython using commands such as %run
and %debug
. ipdb session is actually better than plain IPython one in the sense you can go up and down in the stack trace etc. What is missing in ipdb for "object inspection"?
Also, python.el bundled with Emacs >= 24.3 has nice ipdb support.
Looks like the approach in @gaborous's answer is deprecated.
The new approach seems to be:
from IPython.core import debugger
debug = debugger.Pdb().set_trace
def buggy_method():
debug()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With