8. Debugging#

Code always eventually breaks. Let’s look at some broken code:

from typing import Tuple
import sys
def broken() -> None:
    1 / 0
def my_broken_function() -> Tuple[int, int]:
    x = 6
    y = 4
    x += 2
    y *= 2
    x -= y
    y /= x
    return x, y
my_broken_function()
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Cell In[5], line 1
----> 1 my_broken_function()

Cell In[4], line 7, in my_broken_function()
      5 y *= 2
      6 x -= y
----> 7 y /= x
      8 return x, y

ZeroDivisionError: division by zero

8.1. IPython debugger#

Try writing %debug into the cell below! (you can even skip the %)

# %debug

The mini-language here is pdb, and is similar to gdb and many other debuggers. You can step forward, up, etc. You can set breakpoints, or in Python 3.7+, you can just write breakpoint() anywhere, and the “current” debugger will pick up there!

8.2. Rich tracebacks#

Another trick comes from the Rich library. You can install a nicer traceback handler. Never do this in a library, but only in applications and user code.

import rich.traceback

rich.traceback.install(show_locals=True)
<bound method InteractiveShell.excepthook of <ipykernel.zmqshell.ZMQInteractiveShell object at 0x7f02b4b21160>>

This needs to be in a file (normally it will be) for the traceback to show up nicely:

%%writefile tmp_rich.py
def my_broken_function():
    x = 6
    y = 4
    x += 2
    y *= 2
    x -= y
    y /= x
    return x, y
Writing tmp_rich.py
import tmp_rich

tmp_rich.my_broken_function()
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
 in <module>:3                                                                                    
                                                                                                  
   1 import tmp_rich                                                                              
   2                                                                                              
 3 tmp_rich.my_broken_function()                                                                
   4                                                                                              
                                                                                                  
 ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ 
         exit = <IPython.core.autocall.ZMQExitAutocall object at 0x7f02b4b23770>                
  get_ipython = <bound method InteractiveShell.get_ipython of                                   
                <ipykernel.zmqshell.ZMQInteractiveShell object at 0x7f02b4b21160>>              
           In = [                                                                               
                '',                                                                         
                '# WebAssembly version using Pyodide!\n# The following code is specific to  
                the Pyo'+124,                                                                   
                'from typing import Tuple\nimport sys',                                     
                'def broken() -> None:\n    1 / 0',                                         
                'def my_broken_function() -> Tuple[int, int]:\n    x = 6\n    y = 4\n    x  
                += 2\n    '+44,                                                                 
                'my_broken_function()',                                                     
                '# %debug',                                                                 
                'import rich.traceback\n\nrich.traceback.install(show_locals=True)',        
                "get_ipython().run_cell_magic('writefile', 'tmp_rich.py', 'def              
                my_broken_function"+94,                                                         
                'import tmp_rich\n\ntmp_rich.my_broken_function()'                          
                ]                                                                               
          Out = {                                                                               
                7: <bound method InteractiveShell.excepthook of                             
                <ipykernel.zmqshell.ZMQInteractiveShell object at 0x7f02b4b21160>>              
                }                                                                               
         quit = <IPython.core.autocall.ZMQExitAutocall object at 0x7f02b4b23770>                
         rich = <module 'rich' from                                                             
                '/home/runner/work/level-up-your-python/level-up-your-python/.pixi/envs/defau…  
          sys = <module 'sys' (built-in)>                                                       
     tmp_rich = <module 'tmp_rich' from                                                         
                '/home/runner/work/level-up-your-python/level-up-your-python/notebooks/tmp_ri…  
        Tuple = typing.Tuple                                                                    
 ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ 
                                                                                                  
 /home/runner/work/level-up-your-python/level-up-your-python/notebooks/tmp_rich.py:7 in           
 my_broken_function                                                                               
                                                                                                  
   4 x += 2                                                                                   
   5 y *= 2                                                                                   
   6 x -= y                                                                                   
 7 y /= x                                                                                   
   8 return x, y                                                                              
   9                                                                                              
                                                                                                  
 ╭─ locals ─╮                                                                                     
  x = 0                                                                                         
  y = 8                                                                                         
 ╰──────────╯                                                                                     
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
ZeroDivisionError: division by zero

8.3. Debugging in Jupyter Lab#

This used to require the Xeus Python kernel instead of IPython, but IPyKernel 6+ now supports the visual debugger protocol directly.

Turn on the debugger with the switch on the top right. Click on the line numbers to set a breakpoint. Then run.

def my_broken_function():
    # breakpoint()
    x = 6
    y = 4
    x += 2
    y *= 2
    x -= y
    y /= x
    return x, y
my_broken_function()
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
 in <module>:1                                                                                    
                                                                                                  
 1 my_broken_function()                                                                         
   2                                                                                              
                                                                                                  
 ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ 
         exit = <IPython.core.autocall.ZMQExitAutocall object at 0x7f02b4b23770>                
  get_ipython = <bound method InteractiveShell.get_ipython of                                   
                <ipykernel.zmqshell.ZMQInteractiveShell object at 0x7f02b4b21160>>              
           In = [                                                                               
                '',                                                                         
                '# WebAssembly version using Pyodide!\n# The following code is specific to  
                the Pyo'+124,                                                                   
                'from typing import Tuple\nimport sys',                                     
                'def broken() -> None:\n    1 / 0',                                         
                'def my_broken_function() -> Tuple[int, int]:\n    x = 6\n    y = 4\n    x  
                += 2\n    '+44,                                                                 
                'my_broken_function()',                                                     
                '# %debug',                                                                 
                'import rich.traceback\n\nrich.traceback.install(show_locals=True)',        
                "get_ipython().run_cell_magic('writefile', 'tmp_rich.py', 'def              
                my_broken_function"+94,                                                         
                'import tmp_rich\n\ntmp_rich.my_broken_function()',                         
                ... +2                                                                      
                ]                                                                               
          Out = {                                                                               
                7: <bound method InteractiveShell.excepthook of                             
                <ipykernel.zmqshell.ZMQInteractiveShell object at 0x7f02b4b21160>>              
                }                                                                               
         quit = <IPython.core.autocall.ZMQExitAutocall object at 0x7f02b4b23770>                
         rich = <module 'rich' from                                                             
                '/home/runner/work/level-up-your-python/level-up-your-python/.pixi/envs/defau…  
          sys = <module 'sys' (built-in)>                                                       
     tmp_rich = <module 'tmp_rich' from                                                         
                '/home/runner/work/level-up-your-python/level-up-your-python/notebooks/tmp_ri…  
        Tuple = typing.Tuple                                                                    
 ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ 
                                                                                                  
 in my_broken_function:8                                                                          
                                                                                                  
    5 x += 2                                                                                  
    6 y *= 2                                                                                  
    7 x -= y                                                                                  
  8 y /= x                                                                                  
    9 return x, y                                                                             
   10                                                                                             
                                                                                                  
 ╭─ locals ─╮                                                                                     
  x = 0                                                                                         
  y = 8                                                                                         
 ╰──────────╯                                                                                     
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
ZeroDivisionError: division by zero