一个可怜的Python调试器。
PySnooper的Python项目详细描述
pysnooper-不再使用print进行调试
pysnooper是一个穷人的调试器。
您正试图找出为什么您的python代码没有做您认为应该做的事情。您很想使用一个完整的带有断点和监视点的调试器,但现在您不必费心设置一个断点和监视点。
您想知道哪些行正在运行,哪些没有运行,以及本地变量的值是什么。
大多数人会在战略位置使用print
行,其中一些显示变量的值。
pysnooper允许您执行相同的操作,但您只需在感兴趣的函数中添加一个装饰符行,而不是仔细地绘制正确的print
行。您将获得函数的逐行播放日志,包括运行的行数、运行的时间以及更改局部变量的确切时间。
是什么使pysnooper在所有其他代码智能工具中脱颖而出?你可以在你那肮脏的、杂乱无章的企业代码库中使用它,而不必做任何设置。只需打开decorator,如下所示,并通过将其路径指定为第一个参数将输出重定向到专用日志文件。
示例
我们正在编写一个函数,通过返回一个位列表,将数字转换为二进制。让我们通过添加@pysnooper.snoop()
装饰器来窥探它:
importpysnooper@pysnooper.snoop()defnumber_to_bits(number):ifnumber:bits=[]whilenumber:number,remainder=divmod(number,2)bits.insert(0,remainder)returnbitselse:return[0]number_to_bits(6)
stderr的输出是:
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]
或者如果不想跟踪整个函数,可以将相关部分包装在一个with
块中:
importpysnooperimportrandomdeffoo():lst=[]foriinrange(10):lst.append(random.randrange(1,1000))withpysnooper.snoop():lower=min(lst)upper=max(lst)mid=(lower+upper)/2print(lower,mid,upper)foo()
输出如下内容:
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)
功能
如果您不容易访问stderr,可以将输出重定向到文件:
@pysnooper.snoop('/my/log/file.log')
您也可以传递流或可调用的,它们将被使用。
查看一些非局部变量表达式的值:
@pysnooper.snoop(watch=('foo.bar','self.x["whatever"]'))
展开值以查看其所有属性或列表/词典项:
@pysnooper.snoop(watch_explode=('foo','self'))
这将输出如下行:
Modified var:.. foo[2] = 'whatever'
New var:....... self.baz = 8
(有关更多控制,请参见Advanced Usage)
显示函数调用的函数的窥探行:
@pysnooper.snoop(depth=2)
用前缀开始所有窥探线,以便轻松地为它们添加grep:
@pysnooper.snoop(prefix='ZZZ ')
在多线程应用程序上,确定输出中窥探的线程:
@pysnooper.snoop(thread_info=True)
pysnooper支持装饰生成器。
您还可以自定义对象的repr:
deflarge(l):returnisinstance(l,list)andlen(l)>5defprint_list_size(l):return'list(size={})'.format(len(l))defprint_ndarray(a):return'ndarray(shape={}, dtype={})'.format(a.shape,a.dtype)@pysnooper.snoop(custom_repr=((large,print_list_size),(numpy.ndarray,print_ndarray)))defsum_to_x(x):l=list(range(x))a=numpy.zeros((10,10))returnsum(l)sum_to_x(10000)
您将获得列表的l = list(size=10000)
,以及ndarray的a = ndarray(shape=(10, 10), dtype=float64)
。
custom_repr
按顺序匹配,如果有一个条件匹配,则不会检查其他条件。
安装
您可以通过以下方式安装pysnooper
- 管道:
$ pip install pysnooper
- 带康达锻造通道的康达:
$ conda install -c conda-forge pysnooper
高级用法
watch_explode
将根据其类自动猜测如何展开传递给它的表达式。您可以使用下列类中的一个进行更具体的说明:
importpysnooper@pysnooper.snoop(watch=(pysnooper.Attrs('x'),# attributespysnooper.Keys('y'),# mapping (e.g. dict) itemspysnooper.Indices('z'),# sequence (e.g. list/tuple) items))
使用exclude
参数排除特定键/属性/索引,例如Attrs('x', exclude=('_foo', '_bar'))
。
在Indices
之后添加一个切片,以仅查看该切片中的值,例如Indices('z')[-3:]
。
$exportPYSNOOPER_DISABLED=1# This makes PySnooper not do any snooping
许可证
版权所有(c)2019 Ram Rachum和合作者,根据麻省理工学院许可证发布。
我提供Development services in Python and Django和give Python workshops教人 python和相关主题。
媒体报道
Hacker News thread 以及/r/Python Reddit thread(2019年4月22日)