Working with pdb in Python

operator:(Pdb) !x, y = 1, 2(Pdb) !x, y = y, x + y*Note the lack of the whitespace after the exclamation mark.

NavigationNext, we need to be able to navigate the source code.

For that, we will use list.

Without arguments, list displays 11 lines around the current position in the file or continues going through the previous listing.

Seems straightforward, except for that last part.

It turns out that default list behavior is somewhat counterintuitive — list displays code at the pointer which is shifted down every time the command is called.

When executed for the first time, the pointer is taken to be the current position.

Therefore, if we keep repeating the command, we keep going down through the code.

To list the same chunk of code, use l .

(there is another fix for this which we will discuss later).

You can also specify lines to print, for example list 9 11.

Since Python 3.

2 we also have ll (or longlist), which displays the whole source code for the current function or a lexical block.

When debugging module-level code, this will print the entire file!Next up, we have n(ext) — execute until the next line is reached or the function returns.

This is self-explanatory.

However, one big gotcha is that next refers to the next logical line.

This means that if we are at the end of the loop body, we will jump to its beginning (given that loop condition holds).

Stepping with nextDemonstration of the side-stepping with “next” command, which continues until the next logical one (hence looping).

asciinema.

orgStepping through the loop with n(ext) gets tiresome pretty quickly.

Luckily, we have unt(il) — without argument, it continues the execution until the line with a greater number than the current one is reached.

If a line number is specified, continue until that line.

This means that when at the end of the loop body, until will not loop around, but instead break out of the loop.

Stepping in and out of the loop with “until”Demonstration of how “until” command can be used to skip until the specified line, for example, when exit ting from the…asciinema.

orgIf you want to break out of the function, use return.

It allows following the execution and breaking at the last (logical) statement in the function.

If there are multiple return statements, return will stop at the one determined by the control flow.

If you want to check the value of the last returned function, use retval.

Skipping the function body with “return” asciinema.

orgTo continue execution of the program (i.

e.

, to give up the control and effectively end the debugging), use continue.

Pro-tip: If you started your session via set_trace, and the setup line is placed in a loop (or any block of code being called repeatedly), you might easily find yourself caught in a seemingly endless spiral of pdb's prompts.

In such case continue won’t help you (since it can only refer to the current session, not the future ones).

To break out of it, use this hack: pdb.

set_trace = NoneJumping aroundReversing time and stepping back through program state is called reverse debugging.

It’s a very advanced technique and pdb does not support it at the time (although there are some other experimental implementations for Python).

Nonetheless, in order to jump back and forth between/through/around executable lines, without resetting the program state, we can use jump.

To jump forth, simply type jump specifying the next line that should be executed.

Please note that all the lines between the current and the target are not going to be executed.

To jump back, use the same format.

This time we will move up to line 100.

Please note that every line between the current one and the target (which has smaller line number this time around) still remains executed.

Moving up and down with “jump”Demonstrate how to move up and down executable lines with “jump” command.

Please note that as a jump around the file…asciinema.

orgTo summarize:list displays code;next continues execution until the next logical step;until continues execution until the next physical line;continue continues execution indefinitely;return jumps to the end of the current function;jump allows moving up and down along the executable lines without changing the program state.

BreakpointsBefore we can really start getting things done, we need one last component — dynamic breakpoints.

Until now, we’ve used set_trace to specify where debugger should stop the execution.

With break we can create breakpoints dynamically, during an active session.

Let’s look at the signature one more time:That’s a mouthful, so let’s unpack it.

Simplest usage (what you will do most of the time) is just ordering pdb to break at a line.

To set a breakpoint, type break <lineno>.

Additionally, we can specify a filename in which interesting line is located by prepending it to the invocation:(Pdb) break some_file:123If you don’t feel like counting lines, you can use function name:(Pdb) break some_functionIn such a case, the debugger will stop at the first line of the function body.

To clear a breakpoint, use clear lineno (clear without any arguments will delete all breakpoints).

To list all existing breakpoints, simply type break (without any arguments):(Pdb) breakNum Type Disp Enb Where1 breakpoint keep yes /script.

py:32 breakpoint keep yes /script.

py:5Conditional breakpointsIf you’re interested only in certain conditions when setting up a breakpoint (i.

e.

, conditions under which the debugger should stop), you can specify them as the last argument, condition:(Pdb) b 15, x == 7The above command says that the debugger should stop whenever line 15 is reached and x is equal to 7.

In general, all valid expressions are allowed, and all expressions are to be evaluated in the scope in which the breakpoint was defined.

Setting up breakpointsDemonstration of how to set up, list and clear breakpoints.

asciinema.

orgAdvanced topicsPost-mortem debuggingPost-mortem debugging is a technique which allows us to debug a crashed program, at the time it died, using its traceback object.

Naturally, because it is no longer running, we cannot step through it and are restricted to inspecting the last state of its execution context (which is represented by the traceback).

A traceback is a stack trace (a report on the state of the call stack) from the point of an exception handler (code catching the error) down the call chain to the origin of the error (where the exception was thrown).

The usual way of entering post-mortem is via pdb.

pm() or pdb.

post_mortem().

Those methods are available when sys.

last_traceback is not null:>>> import scriptTraceback (most recent call last): File "script.

py", line 1, in <module> 0/0ZeroDivisionError: division by zero>>> pdb.

pm()> <.

>/script.

py(1)<module>()-> 0/0(Pdb)The catch is, this will only work when Python interpreter is handling the error.

(so that last_traceback is properly set up).

The following code:try: 0/0except: import pdb; pdb.

pm()will throw an error:Traceback (most recent call last): File "exc.

py", line 2, in <module> 0/0ZeroDivisionError: division by zeroDuring handling of the above exception, another exception occurred:Traceback (most recent call last): File "exc.

py", line 4, in <module> import pdb; pdb.

pm() File "/usr/lib/python3.

7/pdb.

py", line 1625, in pm post_mortem(sys.

last_traceback)AttributeError: module 'sys' has no attribute 'last_traceback'Of course, it doesn’t make much sense to enter pm in the except clause anyway.

Additionally, you can call post_mortem with explicit traceback:pdb.

post_mortem(exception_obj.

__traceback__)Traversing the stackSo far we’ve only seen navigation around the code and stepping inside functions.

However, pdb allows traversing the call stack (chain of function calls in the running program).

This is very handy when you want to know what function called what, and when.

It is achieved with three commands: where, up and down.

where — print the stack trace, with the most recent frame at the bottom.

up — move to the previous frame in the stack trace.

down — move to next frame in the stack trace (if applicable).

Moving up and down the stack trace with “up” and “down”Demonstrate navigating up and down the stack trace.

asciinema.

orgRemote debuggingIf you are trying to debug a program running on a distant machine (or one that you have no direct access to), it is often useful to use remote debugging.

pdb does not support it out-of-the-box, but luckily we have remote-pdb package.

Setting up a remote session is very simple:>>> import remote_pdb as rpdb >>> rpdb.

set_trace('0.

0.

0.

0', 46485) CRITICAL:root:RemotePdb session open at 0.

0.

0.

0:46485, waiting for connection .

RemotePdb session open at 0.

0.

0.

0:46485, waiting for connection .

As you can see, this opens up a connection on the displayed port (46485).

We can connect to it using Telnet:telnet 0.

0.

0.

0 46485Trying 0.

0.

0.

0.

Connected to 0.

0.

0.

0.

Escape character is '^]'.

–Return–> <stdin>(1)<module>()->None(Pdb)or, from another host:telnet 10.

3.

39.

124 46485Configurationpdb can be configured via .

pdbrc file:If a file .

pdbrc exists in the user’s home directory or in the current directory, it is read in and executed as if it had been typed at the debugger prompt.

This is particularly useful for aliases.

If both files exist, the one in the home directory is read first and aliases defined there can be overridden by the local file.

For details, consult the documentation.

ExtensionsAs with most open-source software, pdb has community extensions.

Their description is beyond the scope of this article, but take a look at ipdb and pdb++.

One of the best additions to look out for is sticky mode.

SummaryIn conclusion, we’ve looked at all most-used pdb features.

We learned:different ways of starting a pdb session;how to get help;how to read command signatures;how to inspect variables;how to use pdb as an interpreter;how to navigate through the code;how to inspect the stack;how to use breakpoints;what is post-mortem debugging;how pdb can be utilized to debug remotely;how pdb can be configured.

This should give you a good start in incorporating pdb in your usual Python workflow.

Thank you for reading!.You can find me on GitHub or here.

.

. More details

Leave a Reply