PySnooper python debugging prerequisites

Posted by amites on Thu, 13 Jan 2022 10:40:40 +0100

Github:

https://github.com/cool-RR/PySnooper

install

$ pip install pysnooper

Conda with Conda forge channel:

$ conda install -c conda-forge pysnooper

Arch Linux:

$ yay -S python-pysnooper

Fedora Linux:

$ dnf install python3-pysnooper

readme file

PySnooper is a simple debugger. If you've used Bash, it's like set - x python, but it's more advanced.

Your story: you're trying to figure out why your Python code doesn't do what you think it should do. You'll like to use a mature debugger with breakpoints and monitoring, but you don't have to bother setting up one now.

You want to know which rows are running, which are not running, and what the value of the local variable is.

Most people use print lines in strategic locations, some of which display the values of variables.

PySnooper can let you do the same thing. Except that you don't make the correct print line carefully, you just need to add a decorator line to the function you are interested in. You will get the playback log of your function, including which lines run, and the time and exact time of changing local variables.

What makes PySnooper stand out from all other code intelligence tools? You can use it in your bad, huge enterprise code base without any setup. Just open the decorator, as shown below, and redirect the output to a dedicated log file by specifying its path as the first parameter.

example

We are writing a function that converts numbers to binary by returning a list of bits. Let's add @ pysnooper Snoop () decorator to spy on it:

import pysnooper

@pysnooper.snoop()
def number_to_bits(number):
    if number:
        bits = []
        while number:
            number, remainder = divmod(number, 2)
            bits.insert(0, remainder)
        return bits
    else:
        return [0]

number_to_bits(6)

The output of stderr is:

Source path:... /my_code/foo.py
Starting var:.. number = 6
15:29:11.327032 call         4 def number_to_bits(number):
15:29:11.327032 line         5     if number:
15:29:11.327032 line         6         bits = []
New var:....... bits = []
15:29:11.327032 line         7         while number:
15:29:11.327032 line         8             number, remainder = divmod(number, 2)
New var:....... remainder = 0
Modified var:.. number = 3
15:29:11.327032 line         9             bits.insert(0, remainder)
Modified var:.. bits = [0]
15:29:11.327032 line         7         while number:
15:29:11.327032 line         8             number, remainder = divmod(number, 2)
Modified var:.. number = 1
Modified var:.. remainder = 1
15:29:11.327032 line         9             bits.insert(0, remainder)
Modified var:.. bits = [1, 0]
15:29:11.327032 line         7         while number:
15:29:11.327032 line         8             number, remainder = divmod(number, 2)
Modified var:.. number = 0
15:29:11.327032 line         9             bits.insert(0, remainder)
Modified var:.. bits = [1, 1, 0]
15:29:11.327032 line         7         while number:
15:29:11.327032 line        10         return bits
15:29:11.327032 return      10         return bits
Return value:.. [1, 1, 0]
Elapsed time: 00:00:00.000001

Or, if you don't want to track the entire function, wrap the relevant parts in a with block:

import pysnooper
import random

def foo():
    lst = []
    for i in range(10):
        lst.append(random.randrange(1, 1000))

    with pysnooper.snoop():
        lower = min(lst)
        upper = max(lst)
        mid = (lower + upper) / 2
        print(lower, mid, upper)

foo()

The output is similar:

New var:....... i = 9
New var:....... lst = [681, 267, 74, 832, 284, 678, ...]
09:37:35.881721 line        10         lower = min(lst)
New var:....... lower = 74
09:37:35.882137 line        11         upper = max(lst)
New var:....... upper = 832
09:37:35.882304 line        12         mid = (lower + upper) / 2
74 453.0 832
New var:....... mid = 453.0
09:37:35.882486 line        13         print(lower, mid, upper)
Elapsed time: 00:00:00.000344

features

If you cannot easily access stderr, you can redirect the output to a file:

@pysnooper.snoop('/my/log/file.log')

You can also pass streams or callable objects instead, which will be used.

View the values of expressions that are not local variables:

@pysnooper.snoop(watch=('foo.bar', 'self.x["whatever"]'))

Display the listening line of the function called by the function:

@pysnooper.snoop(depth=2)

Advanced usage:

link: https://github.com/cool-RR/PySnooper/blob/master/ADVANCED_USAGE.md

Topics: Python Back-end