Debug Python code interactively
Tags: python, tutorial, testing, coding
Published: 2023-06-04 12:44:57

The debugging process using print statements can get very tedious especially when you don’t really know where the issue might be. Also running the same script multiple times with lots of print statements after each slight change in the code can be time consuming.

The disadvantages with *print* debugging: 

- Lots of print statements that only output info about script state for the current run
- Lack of knowledge about the state of the whole script
- Inability to alter the script for a quick test in the same run

 Wouldn't it be nice to make the debugging experience more *interactive?*
___
## TLDR

Debugging python scripts using <u>python interactive console</u>.

Start you python script in interactive mode: `python -i my_script.py`

Use python built in `code` module to drop inside interactive console from you python code:
```python
code.interact(
    banner='Start interactive mode',
    local=locals(),
    exitmsg='Exiting interactive mode')
```
___
## Interactive mode
You can run your python scripts in <u>interactive</u> mode using the `-i` parameter in the command line.

```python
# example_sript.py

class Person:
    def __init__(self, age):
        self.age = age

age_list = [19, 4, 55, 14]
person_list = [Person(age=age) for age in age_list]

def display_age_rating():
    for person in person_list:
        if person.age < 18:
            print(f"The content is rated 'M' for mature and can not be shown to a {person.age} year old.")
        else:
            print(f"Being {person.age} year you fall under the mature age group.")

```
Now run it from inside of your console:
```bash
python -i example_sript.py
>>> 
```
From here you get all the advantages of using python interactive console.

- Checkout the values of your local variables:

```bash
>>> age_list
[19, 4, 55, 14]
```
- Invoke functions:

```bash
>>> display_age_rating()
Being 19 year you fall under the mature age group.
The content is rated 'M' for mature and can not be shown to a 4 year old.
Being 55 year you fall under the mature age group.
The content is rated 'M' for mature and can not be shown to a 14 year old.
```
- Change your program state for quick testing:

```bash
>>> age_list = [100]*4
>>> age_list
[100, 100, 100, 100]
>>> person_list = [Person(age=age) for age in age_list]
>>> display_age_rating()
Being 100 year you fall under the mature age group.
Being 100 year you fall under the mature age group.
Being 100 year you fall under the mature age group.
Being 100 year you fall under the mature age group.
```
Use `Ctrl+D` or type in `exit()` and press enter to quit the interactive python console. 

This method makes debugging experience much more flexible compared to print debugging. But what if you need to debug not a single script but a whole application with multiple files?
___
## Dropping inside interactive mode from inside the code
The built in `code` python module will allow us to drop inside interactive mode from anywhere in our code during the script execution. To do that we need to invoke `interact()` function in our script. 

```python
# Import code module
import code

class Person:
    def __init__(self, age):
        self.age = age

age_list = [19, 4, 55, 14]
person_list = [Person(age=age) for age in age_list]

def display_age_rating():
    for age in age_list:
        if age < 18:
            print(f"The content is rated 'M' for mature and can not be shown to a {age} year old.")
        else:
            print(f"Being {age} year you fall under the mature age group.")

code.interact(
    banner='Start interactive mode before invoking display_age_rating',
    local=locals(),
    exitmsg='Exiting interactive mode')

display_age_rating()
```
`interact()` creates an `InteractiveConsole` instance. This pauses the execution of the script and starts the <u>read-eval-print loop</u>. It takes in four parameters:
- `banner` accepts a string which will be displayed at the start of the interactive mode instance
- `local` sets a default namespace for the interpreter loop
- `readfunc` can be used as the [InteractiveConsole.raw_input()](https://docs.python.org/3/library/code.html#code.InteractiveConsole.raw_input) method if provided
- `exitmsg` accepts a string which will be displayed after exiting interactive mode instance

> 💡 For the `local` parameter we can manually provide a dictionary of local or global variables that we can access inside interactive mode instance
> `local={’age_list’: age_list}`

Now we can start our small program and use the interactive console the same way as if we provided the `-i` parameter mentioned previously.

```bash
python3 interactive_mode.py                                                                                            
Start interactive mode before invoking display_age_rating
>>> age_list
[19, 4, 55, 14]
>>> display_age_rating()
Being 19 year you fall under the mature age group.
The content is rated 'M' for mature and can not be shown to a 4 year old.
Being 55 year you fall under the mature age group.
The content is rated 'M' for mature and can not be shown to a 14 year old.
>>>
```
By pressing `Ctrl+D` we exit current interactive session and <u>let our program finish executing</u>

```bash
>>> 
Exiting interactive mode
Being 19 year you fall under the mature age group.
The content is rated 'M' for mature and can not be shown to a 4 year old.
Being 55 year you fall under the mature age group.
The content is rated 'M' for mature and can not be shown to a 14 year old.
```

We are able to enter interactive console from different parts of our code by placing the `interact()` function where needed.

```python
import code

age_list = [19, 4, 55, 14]

def display_age_rating():
    for age in age_list:
        code.interact(
            banner='age_list loop iteration',
            local=locals(),
            exitmsg='Exiting interactive mode')
            
        if age < 18:
            print(f"The content is rated 'M' for mature and can not be shown to a {age} year old.")
        else:
            print(f"Being {age} year you fall under the mature age group.")

code.interact(
    banner='Start interactive mode before invoking display_age_rating',
    local=locals(),
    exitmsg='Exiting interactive mode')
display_age_rating()

```
That way we are able to <u>jump to the next point</u> of our scripts execution by pressing `Ctrl+D`.

```bash
python3 interactive_mode.py                                                                                       
Start interactive mode before invoking display_age_rating
>>> age_list
[19, 4, 55, 14]
>>> 
Exiting interactive mode
age_list loop iteration
>>> person.age
19
>>> person.age = 100
>>> person.age
100
>>> 
Exiting interactive mode
Being 100 year you fall under the mature age group.
age_list loop iteration
>>> person.age
4
>>> 
Exiting interactive mode
The content is rated 'M' for mature and can not be shown to a 4 year old.
age_list loop iteration
>>>...
```
To quit the interactive mode use `exit()`.
___
## Conclusion
<u>Python interactive console</u> safes time debugging our code by letting us check the code state and change it on the fly. The code module provides a more flexible way to interact with our code and better understand it’s behavior. For more info check our the official documentation of the code module.

Hopefully this post was helpful. I’d like to now what tools and approaches do you use for debugging your python code. Feel free to comment down below.