Network Automation using YANG Models across XE, XR, & NX

Bonus Content - Debugging in PDB

This module briefly covers how to use Pythons interactive command line debugger.

What is PDB?

Per the kind folks at docs.python.org

"The module pdb defines an interactive source code debugger for Python programs. It supports setting (conditional) breakpoints and single stepping at the source line level, inspection of stack frames, source code listing, and evaluation of arbitrary Python code in the context of any stack frame. It also supports post-mortem debugging and can be called under program control. The debugger is extensible – it is actually defined as the class Pdb. This is currently undocumented but easily understood by reading the source. The extension interface uses the modules bdb and cmd."

PDB is an absolutely critical tool used in day-to-day Python debugging. When working in production environments, we aren't always afforded a nice IDE with interactive breakpoint support. As such, having an understanding of how to use PDB in a practical manner will significantly improve your ability to quickly debug script issues.

The Basic Idea

When working on a command line and executing Python scripts, as we shall do throughout this lab, we don't have the luxery of interactive breakpoint debugging and instead must rely on PDB. The basic idea is that if you want to "set a breakpoint" in your script, simply add "import pdb; pdb.set_trace()" above the location from which you'd like to start debugging. The pdb.set_trace() function effectively halts the execution of Python and hands control over to the user on stdin, or the terminal, such that the user and keystroke-by-keystroke perform debugging steps, inspecting objects and functions in Python, and exercise common debugging techniques that may otherwise have been traditionally been done in an interactive IDE debugging session.

Common Commands

Here is a list of common commands that are generally useful in day-to-day PDB usage.

  1. c - Continues program execution until the next breakpoint or until the script terminates.
  2. n - Executes the next line of Python.
  3. b - Sets a break point at the specified line number.
  4. l - Print snippet of script near current breakpoint.
  5. ll - Print full script with arrow pointing to current breakpoint.
  6. s - Steps into the function body of the current function being called.
  7. q - Quits execution of the script.
  8. dir() - Lists all the attributes of a Python object.
  9. pp() - Attempts to pretty-print an objects data.

Example Script to Exercise PDB

Paste the following script into a new file called working_with_pdb.py and execute the script with python3.6 working_with_pdb.py.

    
    # Imports
    import os

    # Break at start of script.
    import pdb; pdb.set_trace()

    # List of known directories on filesytem.
    known_directories = ['/tmp', '/user']

    def get_files(directory):
        """
        For a given filesystem path, gets list of files directly inside the directory.
        """
        try:
            # Return the list of files in the directory.
            return os.listdir(directory)
        except Exception as e:
            # Creak if exception was hit!
            import pdb; pdb.set_trace()

            # Print warning but return empty list to caller.
            print(f"Warning: Could not get files in {directory}, Error: {e}")
            return []

    # Iterate each directory and print the file count.
    for directory in known_directories:
        # Get list of files in directory.
        files = get_files(directory)
        # Print the count.
        print(f"{directory}: Contains {len(files)} files")
    

Take about 5 minutes to play around with the commands in PDB to ensure you understand how to break, continue, step, next, and print information about runtime objects in Python. When you run working_with_pdb.py, you will immediately see the following:

    
    [root@cfa2c7b31323 workspace]# python3.6 lab/src/working_with_pdb.py
    > /workspace/lab/src/working_with_pdb.py(8)()
    -> known_directories = ['/tmp', '/user']
    (Pdb)
    

This is PDB telling you that it is awaiting your instruction on how to proceed further. Typing, in this order n (hit enter), l (hit enter), and pp(known_directories) then hitting enter a last time should yield the following.

    
        (Pdb) n
        > /workspace/lab/src/working_with_pdb.py(10)()
        -> def get_files(directory):
        (Pdb) l
        5  	import pdb; pdb.set_trace()
        6
        7  	# List of known directories on filesytem.
        8  	known_directories = ['/tmp', '/user']
        9
        10  ->	def get_files(directory):
        11  	    """
        12  	    For a given filesystem path, gets list of files directly inside the directory.
        13  	    """
        14  	    try:
        15  	        # Return the list of files in the directory.
        (Pdb) pp(known_directories)
        ['/tmp', '/user']
    

Pressing c and then enter to continue should result the program continuing until another breakpoint is hit, or until the script terminantes.

    
    (Pdb) c
    /tmp: Contains 39 files
    > /workspace/lab/src/working_with_pdb.py(22)get_files()
    -> print(f"Warning: Could not get files in {directory}, Error: {e}")
    (Pdb) l
    17  	    except Exception as e:
    18  	        # Creak if exception was hit!
    19  	        import pdb; pdb.set_trace()
    20
    21  	        # Print warning but return empty list to caller.
    22  ->	        print(f"Warning: Could not get files in {directory}, Error: {e}")
    23  	        return []
    24
    25  	# Iterate each directory and print the file count.
    26  	for directory in known_directories:
    27  	    # Get list of files in directory.
    (Pdb) c
    Warning: Could not get files in /user, Error: [Errno 2] No such file or directory: '/user'
    /user: Contains 0 files
    [root@cfa2c7b31323 workspace]#
    

Observe that the directory /user was not found (because it's called /usr), so naturally the breakpoint we placed in the except block was hit and PDB paused us there. Pressing l to see where you are and c to continue should cause the script to terminate. Freel free to check out The Python Debugger and Debugging with PDB to learn more.