当前位置: 首页 > 工具软件 > PySnooper > 使用案例 >

Python-调试工具:pysnooper模块

梁渊
2023-12-01

0 前言

>>返回Python系列文章目录<<

1 pysnooper模块(第三方库)

Pysnooper 是一款大受欢迎的Debug模块,pysnooper模块比print更方便,以装饰器的形式存在,安装方式为:

pip install pysnooper

使用方式为:

import pysnooper

@pysnooper.snoop(output=, watch=, prefix=, custom_repr=, depth=)
def example():

1.1 调试结果输出

@pysnooper.snoop()修饰的函数运行过程中所有函数内部局部变量的变化过程都会被记录下来

@pysnooper.snoop()不加任何参数时,默认输出到标准输出

import pysnooper

@pysnooper.snoop()
def example():
    a = 5
    b = 'x'
    a = a + 1
    b = b + 'y'

example()

结果输出:

Source path:... D:\6_SoftwareTest\LOGTEST\logs\pysnooperTest.py
10:43:00.189184 call         4 def example():
10:43:00.189184 line         5     a = 5
New var:....... a = 5
10:43:00.189184 line         6     b = 'x'
New var:....... b = 'x'
10:43:00.189184 line         7     a = a + 1
Modified var:.. a = 6
10:43:00.189184 line         8     b = b + 'y'
Modified var:.. b = 'xy'
10:43:00.189184 return       8     b = b + 'y'
Return value:.. None
Elapsed time: 00:00:00.000000

@pysnooper.snoop()添加output参数时,输出到log文件

import pysnooper

@pysnooper.snoop(output='out.log')
def example():
    a = 5
    b = 'x'
    a = a + 1
    b = b + 'y'

example()

1.2 设置调试日志的前缀

当使用@pysnooper.snoop()跟踪多个函数时,调试日志会显得杂乱无章,不方便查看。在这种情况下,可以使用prefix参数,方便为不同的函数设置不同的标志

import pysnooper

@pysnooper.snoop(prefix='example_1:')
def example_1():
    a = 5
    a = a + 1

@pysnooper.snoop(prefix='example_2:')
def example_2():
    b = 'x'
    b = b + 'y'

example_1()
example_2()

结果输出:

example_1:Source path:... D:\6_SoftwareTest\LOGTEST\logs\pysnooperTest.py
example_1:10:50:06.741409 call         4 def example_1():
example_1:10:50:06.741409 line         5     a = 5
example_1:New var:....... a = 5
example_1:10:50:06.741409 line         6     a = a + 1
example_1:Modified var:.. a = 6
example_1:10:50:06.741409 return       6     a = a + 1
example_1:Return value:.. None
example_1:Elapsed time: 00:00:00.000000
example_2:Source path:... D:\6_SoftwareTest\LOGTEST\logs\pysnooperTest.py
example_2:10:50:06.741409 call         9 def example_2():
example_2:10:50:06.741409 line        10     b = 'x'
example_2:New var:....... b = 'x'
example_2:10:50:06.741409 line        11     b = b + 'y'
example_2:Modified var:.. b = 'xy'
example_2:10:50:06.741409 return      11     b = b + 'y'
example_2:Return value:.. None
example_2:Elapsed time: 00:00:00.000000

1.3 自定义对象的格式输出

python当中有不少数据类型是不支持print输出的,在这种情况下,可以使用custom_repr参数,自定义数据类型转化为str的方式

以正则表达式re模块为例,正常情况如下:

import pysnooper
import re

@pysnooper.snoop()
def example():
    pattern = re.compile(r'[a-zA-Z]\w*')
    result = re.match(pattern, 'fun%_LIST')

example()

输出结果为:

Source path:... D:\6_SoftwareTest\LOGTEST\logs\pysnooperTest.py
11:08:27.942424 call         5 def example():
11:08:27.942424 line         6     pattern = re.compile(r'[a-zA-Z]\w*')
New var:....... pattern = re.compile('[a-zA-Z]\\w*')
11:08:27.942424 line         7     result = re.match(pattern, 'fun%_LIST')
New var:....... result = <re.Match object; span=(0, 3), match='fun'>
11:08:27.942424 return       7     result = re.match(pattern, 'fun%_LIST')
Return value:.. None
Elapsed time: 00:00:00.000000

可与看到:

函数没有return时,返回Return value:.. None

pattern保存的不是数据类型,而是pattern = re.compile(r'[a-zA-Z]\w*')这个公式,每次调用pattern参数,都会重新处理一次公式

result的数据类型是re.Match,需要group()方法才能转化为字符串形式

使用custom_repr参数后:

import pysnooper
import re

def print_match_object(obj):
    return obj.group()

@pysnooper.snoop(custom_repr=(re.Match, print_match_object))
def example():
    pattern = re.compile(r'[a-zA-Z]\w*')
    result = re.match(pattern, 'fun%_LIST')

example()

输出结果为:

Source path:... D:\6_SoftwareTest\LOGTEST\logs\pysnooperTest.py
11:13:54.449604 call         8 def example():
11:13:54.449604 line         9     pattern = re.compile(r'[a-zA-Z]\w*')
New var:....... pattern = re.compile('[a-zA-Z]\\w*')
11:13:54.449604 line        10     result = re.match(pattern, 'fun%_LIST')
New var:....... result = fun
11:13:54.449604 return      10     result = re.match(pattern, 'fun%_LIST')
Return value:.. None
Elapsed time: 00:00:00.000000

custom_repr输入一个元组(re.Match, print_match_object),第一个元素是变量数据类型re.Match,第二个元素是处理将re.Match处理为可输出形式的函数print_match_object

定义函数print_match_object(obj),其中obj为要处理的re.Match数据类型,并return处理后的可输出数据

1.4 跟踪非局部变量

PySnooper 是以函数为单位进行调试的,它默认只会跟踪函数体内的局部变量,若想跟踪全局变量,可以给 pysnooper.snoop() 加上watch 参数

  1. 不加watch参数时
import pysnooper

class Calculation():
    number = 20
    @pysnooper.snoop()
    def add(self, input):
        self.number = input + 10

cal = Calculation()
cal.add(10)

结果只能显示self是Calculation类

Source path:... D:/6_SoftwareTest/snoop/main.py
Starting var:.. self = <__main__.Calculation object at 0x000001CCCD0E6F10>
Starting var:.. input = 10
14:06:49.884343 call         8     def add(self, input):
14:06:49.884343 line         9         self.number = input + 10
14:06:49.884343 return       9         self.number = input + 10
Return value:.. None
Elapsed time: 00:00:00.000000
  1. watch参数时
import pysnooper

class Calculation():
    number = 20
    @pysnooper.snoop(watch='self.number')
    def add(self, input):
        self.number = input + 10

cal = Calculation()
cal.add(10)

结果可以显示self.number的变化:

Source path:... D:/6_SoftwareTest/snoop/main.py
Starting var:.. self = <__main__.Calculation object at 0x0000018ADC732A60>
Starting var:.. input = 10
Starting var:.. self.number = 20
14:11:36.094900 call         8     def add(self, input):
14:11:36.094900 line         9         self.number = input + 10
14:11:36.094900 return       9         self.number = input + 10
Return value:.. None
Elapsed time: 00:00:00.000000

watch参数可以使用listtuple输入多个变量

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

1.5 设置跟踪函数的深度

当你使用 PySnooper 调试某个函数时,若该函数中还调用了其他函数,PySnooper 是不会傻傻的跟踪进去的。

如果你想继续跟踪该函数中调用的其他函数,可以通过指定 depth参数来设置跟踪深度(不指定的话默认为 1)

@pysnooper.snoop(depth=2)
def demo_func():

>>返回Python系列文章目录<<

 类似资料: