Stop using print output to debug Python code

Introduction: PySnooper, a python debugging artifact, recently appeared on github, claiming to be able to eliminate print during debugging. So what are the advantages of this tool and how to use it. This article introduces the advantages, disadvantages and usage of this tool.

Foreword:

During the development process using python, debugging is always unavoidable. The traditional debugging process is roughly divided into two types:

a) Breakpoint + single-step debugging.

Breakpoint + single-step debugging is probably the most commonly used method. For larger projects, the process is roughly as follows: first add print statements to key code locations, and then narrow the scope by analyzing the print value. This process may require Repeat several times and use the print method to generally narrow the scope to a relatively complete functional module; then put breakpoints on key parts of the module where bugs may occur, and use single-step debugging after entering the breakpoint to check each Check whether the value of the variable is correct, and finally locate the specific line of code based on the incorrect variable value, and finally make modifications.

b) pdb debugging.

pdb is a package that comes with Python. It provides an interactive source code debugging function for Python programs. Its main features include setting breakpoints, single-step debugging, entering function debugging, viewing the current code, viewing stack fragments, and dynamically changing variables. Worth waiting. The debugging process of pdb is basically the same as 1). You can search online for its specific usage.

The disadvantages of traditional debugging methods include:

a) A print statement needs to be added to the code, which changes the original code; 

b) During breakpoint debugging and single-step debugging, you need to maintain continuous concentration. Once you skip a key point, you have to start from the beginning.

Recently, a debugging tool appeared on github, which can solve the shortcomings of the traditional debugging process. Let’s take a look at the use and magic of this tool.

1. What is PySnooper?

This tool uses a decorator to print the running process of the function to a file in the form of a log. It records which lines of code were run, the running time and the values ​​of each variable when the current code was run. Problems can be located based on changes in variables. After trying this tool myself, its advantages can be summarized in the following points:

1. There is no need to use print to print the value of the variable in order to view the value of the variable, thus modifying the original code.

2. The running process of the interface is saved in the form of a log for easy viewing at any time.

3. You can set the number of layers of functions called by the function as needed, so that you can focus on the code segments that need to be focused on.

4. Logs of multiple functions can be identified by setting a log prefix to facilitate filtering during viewing.

This tool has so many advantages, so how to use it? Let’s introduce the use of this tool with the demo below.

2. Introduction to usage

1. Tool installation

pip install pysnooper

2. Official demo introduction

Official demo code:

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)

 Console output:

The output of the console is as shown in the figure above. As you can see from the figure, starting from entering the function, the execution of each line of code and the recording of new local variables or changes in existing local variables will be recorded until the end of the function. After using this tool in the form of a decorator, the intermediate results of function execution will be printed out, which facilitates subsequent bug location and analysis.

3. Parameter introduction

Use this tool in the form of a decorator, which contains four parameters, including output, variables, depth, and prefix, as shown below.

1. Output parameters. This parameter specifies the storage location of the intermediate results generated during the function running. If the value is empty, the intermediate results will be output to the console.

2. Variable parameters. This parameter is of vector type, because by default, the decorator only tracks local variables. To track non-local variables, you can specify it through this field. The default value is an empty vector.

3. Depth parameter. This parameter indicates the depth of function calls that need to be traced. In many cases, we will call other functions in the function, and this parameter can be used to specify the depth of tracing the calling function. The default value is 1.

4. prefix parameter. This parameter is used to specify the intermediate result prefix of the function interface. When multiple functions use this decorator, the intermediate results of these function calls will be saved to a file. At this time, the intermediate results of different function calls can be filtered by prefixes. The default value is the empty string.

3. Tool application

To use this tool, you only need to understand the meaning of the parameters of the decorator (snoop). The following is a combination of several demos to introduce the use of parameters and their impact on the results.

1. Use of output parameters

If you use the default parameters, the intermediate results will be output to the console. If you fill in this parameter, the intermediate results will be written to the directory specified by the parameters. If you run the following code, the intermediate results will be saved in the decorator snoop to set the log. In the saved path, note that the directory will not be automatically created here, so the directory needs to be created in advance. For example, the log directory needs to be created after filling in the path in the test code.

Test code:

import pysnooper
def add(num1, num2):    return num1 + num2
@pysnooper.snoop("./log/debug.log", prefix="--*--")def multiplication(num1, num2):    sum_value = 0    for i in range(0, num1):        sum_value = add(sum_value, num2)    return sum_value
value = multiplication(3, 4)

After running this code, the contents of ./log/debug.log are as follows:

It can be seen from the intermediate results of running the code that the execution process of each line of code and changes in local variables are recorded in the file. During debugging, by analyzing the file, you can track the execution process of each step and the changes in local variables, so that you can quickly locate the problem; because the intermediate results of the operation are saved in the file, it is convenient to analyze the intermediate results of the operation at any time. , also easy to share.

2. Use of variables parameters

With the default parameters, you can only use this tool to view the changing process of local variables. When you need to view variables other than local variables, you can set them through the variables parameters. For example, in the code below, in the Foo type, you need to view the variables of the class instance. self.num1, self.num2, self.sum_value, you can set this variable as a parameter and pass it into the snoop decorator.

Test code:


import pysnooper

class Foo(object):
    def __init__(self):
        self.num1 = 0
        self.num2 = 0
        self.sum_value = 0

    def add(self, num1, num2):
        return num1 + num2
    @pysnooper.snoop(output="./log/debug.log", variables=("self.num1", "self.num2", "self.sum_value"))
    def multiplication(self, num1, num2):
        self.num1 = num1
        self.num2 = num2
        sum_value = 0
        for i in range(0, num1):
            sum_value = self.add(sum_value, num2)
        self.sum_value = sum_value
        return sum_value

foo = Foo()
foo.multiplication(3, 4)

In order to reflect the role of this parameter, here we use the default parameters and the above parameters (parameters set in the code) to run the code. The results obtained are as follows:

Results using default parameters

Using the results of parameters in code

It can be seen from the two intermediate results that if the variable is not a local variable, even if the variable is used in the function, if the intermediate result of the variable is not set to be printed, the intermediate result of the variable will not be printed to the file.

3. Use of depth parameter

This parameter is used to specify the result of recording the number of function call levels. The default value is 1. If you want to view the intermediate results of multi-level function calls, you can set this parameter to >=2.

Test code:

import pysnooper

def add(num1, num2):
    return num1 + num2

@pysnooper.snoop("./log/debug.log", depth=2)
def multiplication(num1, num2):
    sum_value = 0
    for i in range(0, num1):
        sum_value = add(sum_value, num2)
    return sum_value

value = multiplication(3, 4)

For comparison, set the depth values ​​to 1 and 2 respectively. The results are as follows:

The result of depth=2

As can be seen from the above results, if you want to view deeper function calls, you can view it by setting the depth value. This makes it convenient for users to selectively view the calling status of functions.

4. Use of prefix parameters

This parameter is mainly used to set the prefix of the intermediate result, so that the intermediate results of different function calls can be distinguished. The default parameter is "".

Test code:

import pysnooper

def add(num1, num2):
    return num1 + num2

@pysnooper.snoop("./log/debug.log", prefix="--*--")
def multiplication(num1, num2):
    sum_value = 0
    for i in range(0, num1):
        sum_value = add(sum_value, num2)
    return sum_value

value = multiplication(3, 4)

The intermediate results after running the code are as follows:

As you can see from the results, each line of the intermediate result contains the prefix set by prefix, which makes it easy to distinguish the intermediate results of different function calls.

In order to focus attention on specific parameters, the above introduction is introduced in the form of setting a single parameter (output + other single parameters). In actual use, multiple parameters can be set at the same time. Use the PySnooper tool to record the intermediate results of function execution. Compared with the traditional debugging methods such as breakpoints + single-step debugging, pdb, etc., the PySnooper tool has huge advantages.

4. Disadvantages

Although using debug is very convenient when using PySnooper, there are still some problems (based on the code pulled on April 26), such as:

1. It cannot support recursive calls very well.

2. The intermediate results of calling each function can only be saved in one file. If you need to distinguish the results of different files, you need to use prefix for prefix identification.

3. For cross-file function calls, recording the file name where the calling function is located is not supported.

Of course, PySnooper is a project that has recently become popular on github. It is normal that it is not perfect enough. I believe these shortcomings will be improved in the future, and I look forward to a better PySnooper.

5. Summary

This article introduces the PpySooper tool. It first introduces what the tool is and its advantages over traditional debugging methods. Then it introduces the parameters of the tool and a demo that explains the function of the parameters. Finally, the shortcomings of the tool are introduced.

Guess you like

Origin blog.csdn.net/licongzhuo/article/details/132901577