Skip to content

Debugging

Debuggers

pdb

python -m pdb script.py
import sys


def main():
    name : str = sys.argv[1]
    print(f"Hello, {name}!")


if __name__ == "__main__":
    main()

Use debugging to intercept the value of the variable before the final string is displayed.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import math
import sys


def main():
    number = get_number()
    factorial = get_factorial(number)
    print(f"{number}! = {factorial}")


def get_factorial(number: int):
    factorial = math.prod(list(range(number + 1)))
    return factorial


def get_number():
    number = 0

    if len(sys.argv) == 2:
        try:
            number = int(sys.argv[1])
            return number
        except IndexError:
            pass
        except ValueError:
            pass

    while True:
        try:
            number = int(input("Enter a number: "))
            break
        except ValueError:
            print("Argument must be an integer")

    return number


if __name__ == "__main__":
    main()

When running pdb with no breakpoints, top-level statements begin to be executed. The debugger proceeds from top to bottom, following the interpreter, from import statements to function definitions, until it finally reaches the entrypoint. If the debugger is directed to step over the main function, the program will execute normally.

The entirety of the source code can be printed to the interactive pdb shell by using ll or longlist .

A similar command is l[ist] which lists 11 lines around the current line. Repeated execution progressively print more of the source.

The c[ontinue] command results in the execution being resumed without interruption until the next breakpoint. If no breakpoints are defined, the program is executed as it would be normally.

Invoking pdb without any breakpoints and proceeding with n[ext] results in top-level code being executed.

Stepping into function calls with s[tep] is similar to adding a breakpoint at the function definition with b[reak] , at least for a single function call.

In these examples, the factorial is being incorrectly calculated, such that it will always produce a 0. This can be inspected in the debugger by using the p command, which evaluates the following arguments as an expression.

Alternatively, any Python statement can be preceded with the ! command, with which you can change variable values. Notably, any whitespace after ! will cause an IndentationError

p factorial
!factorial = 120

The interact command allows you to enter a Python REPL, which allows statements to be interactively entered. However these do not have an effect on the program state once the shell is terminated.

(Pdb) p factorial
0
(Pdb) interact
>>> factorial = 120
>>> ^D
now exiting InteractiveConsole...
(Pdb) p factorial
0

gdb

Invoke on executable "program", compiled with debugging symbols.

gdb program
Display the first 10 lines of source code from main.rs
list main.rs:0
Display the source code of the stack_only function
list stack_only

Run code after placing a breakpoint on functions "stack_only" and "stack_and_heap"

b stack_only
b stack_and_heap
r
Display stack frames
bt 2
Inspect local variables and arguments
info locals
info args
Step forward
n
Display data at given memory location as a digit
x /d 0x55555559bba0
Enter TUI mode Ctrl-XA. This mode does not work well with print statements.

GDB also contains a Python runtime, so you can run commands inline using the python command.

📘 Glossary

step into

If the current line contains a function call, move execution context into it to continue stepwise execution of code statements within that function.

If not, identical to step over

step over
Execute the current line and pause on the next.