强大的python调试工具
snoop的Python项目详细描述
窥探
snoop是一组强大的python调试工具。它的主要目的是a更具特色和更精致的版本。它还包括一个更精确的版本的冰淇淋和其他一些漂亮的东西。
您正试图找出为什么您的python代码没有做您认为应该做的事情。您很想使用一个完整的带有断点和监视的调试器,但现在您不必费心设置它。
您想知道哪些行正在运行,哪些没有运行,以及本地变量的值是什么。
大多数人会在战略位置使用打印行,其中一些显示变量值。
snoop允许您执行相同的操作,但您只需在感兴趣的函数中添加一个装饰符行,而不是精心设计右侧的print
行。您将获得函数的逐行播放日志,包括运行的行数、运行的时间以及更改局部变量的确切时间。
安装非常简单,只要pip install snoop
- 基本窥探用法
- pp-awesome print debugging
- 用于跟踪子表达式的"pp deep" rel="nofollow">pp.deep用于跟踪子表达式
- @间谍
- 安装()
- 禁用
- 输出配置
- API与Pysnooper的区别
- ipython/jupyter integration
- 高级用法
- 贡献
- 反馈和讨论
- 开发
基本snoop用法
我们正在编写一个函数,通过返回一个位列表,将数字转换为二进制。让我们通过添加@snoop
装饰来窥探它:
importsnoop@snoopdefnumber_to_bits(number):ifnumber:bits=[]whilenumber:number,remainder=divmod(number,2)bits.insert(0,remainder)returnbitselse:return[0]number_to_bits(6)
注意这是多么简单:只需导入snoop
和@snoop
。如果您不喜欢神奇的导入,snoop import snoop中的snoop.snoop
和也可以工作。或者如果您根本不想在项目中导入,只需调用一次。
stderr的输出如下:
让我们尝试一个更复杂的例子。我们正在编写一个记忆犹新的decorator:它将函数参数和返回值存储在缓存中以避免重新计算:
importsnoopdefcache(func):d={}defwrapper(*args):try:returnd[args]exceptKeyError:result=d[args]=func(*args)returnresultreturnwrapper@snoop(depth=2)@cachedefadd(x,y):returnx+yadd(1,2)add(1,2)
在这里,我们指定depth=2
意味着我们还应该向下一步进入内部函数调用。然后我们调用函数两次以查看缓存的运行情况。输出如下:
乍一看,我们可以看到,在第一次调用中,缓存查找失败,出现keyerror
错误,因此调用了原始的add
函数,而在第二次调用中,立即返回先前缓存的结果。
如果您不想跟踪整个函数,可以用块:
importsnoopimportrandomdeffoo():lst=[]foriinrange(10):lst.append(random.randrange(1,1000))withsnoop:lower=min(lst)upper=max(lst)mid=(lower+upper)/2returnlower,mid,upperfoo()
输出如下内容:
常见参数
depth
:如上所示,窥探由跟踪的函数/块进行的更深层次的调用。默认值是1,表示没有内部调用,因此传递更大的值。观看
:通过将任意表达式的值指定为字符串来显示它们,例如:
@snoop(watch=('foo.bar','self.x["whatever"]'))
watch_explode
:展开变量或表达式以查看其所有属性或列表/词典项:
@snoop(watch_explode=['foo','self'])
这将输出如下行:
........ foo[2] = 'whatever'
........ self.baz = 8
有关此参数的更高级用法,请参见控制控制手表爆炸rel="nofollow">控制手表爆炸。
请参见"watch_u extras" rel="nofollow">watch_u extras
以自动显示有关任何值(局部变量、关注的表达式或分解项)的附加信息。
pp
-出色的打印调试
虽然snoop
是为了避免编写print
调用,但有时这仍然是您所需要的。pp
旨在成为这方面的最佳版本。它可以单独使用,也可以与snoop结合使用。
p p(x)
将输出x=<;x>;
的漂亮打印值,即它将显示其参数的源代码,以便您知道正在打印的内容,并使用pprint.p format
格式化该值,以便您可以轻松看到复杂数据结构的布局。
pp
将直接返回其参数,因此您可以轻松地将其插入到代码中,而无需重新排列。如果给定多个参数,它将以元组的形式返回它们,因此您可以将foo(x,y)
替换为foo(*p p(x,y))
以保持代码的行为不变。
下面是一个示例:
fromsnoopimportppx=1y=2pp(pp(x+1)+max(*pp(y+2,y+3)))
输出:
12:34:56.78 LOG:
12:34:56.78 .... x + 1 = 2
12:34:56.78 LOG:
12:34:56.78 .... y + 2 = 4
12:34:56.78 .... y + 3 = 5
12:34:56.78 LOG:
12:34:56.78 .... pp(x + 1) + max(*pp(y + 2, y + 3)) = 7
如果您已经有了import snoop
您还可以使用snoop.pp
。但理想情况下,您可以使用install()
来完全避免导入。
有些情况下p p
找不到其参数的源代码,在这种情况下,它将显示一个占位符:
- 当无法找到源文件时,通常是因为它不存在,例如,如果您在python shell中。源是从
linecache
- 在python 3.4和pypy中。
- 在有魔法存在的情况下,可以在引擎盖下转换源代码,例如
pytest
或birdseye
(因此还有@spy
装饰器)。 - 当苏RCE文件在第一次调用
pp
或snoop
之前已被修改
在引擎盖下, " 如果您有 输出: (文本值和内置值被忽略,因为它们微不足道) 如果引发异常,它将显示哪个子表达式负责,如下所示: 如果你喜欢这样,你可能会喜欢间谍。
利用 大致相当于: 要减少 (' 要使定期调试项目更加方便,请在早期运行此代码: 然后 您可以通过传递关键字参数来选择不同的名称,例如: 将允许您用 如果您不喜欢此功能,并且希望只正常导入,但您希望对其他配置使用 另外,在python 3.7+中,如果设置环境变量 如果希望将 您还可以在任何时候通过再次调用snoop.install(enabled=true)来动态重新启用函数,例如在特殊视图或信号处理程序中。 如果你想要一个自定义列,请打开一个问题告诉我你对什么感兴趣!同时,您可以传递一个列表,其中的元素可以是字符串,也可以是可调用的。callables应该有一个参数,它将是一个 如果您熟悉 如果您不确定是否值得使用p p
使用库pp
的灵感来自于冰淇淋,并提供了相同的基本打印api,但是冰淇淋的算法比执行的算法更不准确,并且pp
与snoop
无缝集成。它还提供p p.deep
,这是唯一的。pp
"代表"漂亮的印刷品",完全没有其他含义。打字也非常简单快捷。pp.deep
用于跟踪子表达式
pp(<;complex expression>;)
并且希望查看该表达式内部发生的情况,而不仅仅是最终值,请将其替换为pp.deep(lambda:<;complex expression>;)
。这将以正确的顺序记录每个中间子表达式,而不产生任何额外的副作用,并返回最终值。重复前面的示例:pp.deep(lambda:x+1+max(y+2,y+3))
importsnoop@snoopdefnumber_to_bits(number):ifnumber:bits=[]whilenumber:number,remainder=divmod(number,2)bits.insert(0,remainder)returnbitselse:return[0]number_to_bits(6)
0
importsnoop@snoopdefnumber_to_bits(number):ifnumber:bits=[]whilenumber:number,remainder=divmod(number,2)bits.insert(0,remainder)returnbitselse:return[0]number_to_bits(6)
1
@spy
@spy
装饰器,您可以将@snoop
与功能强大的调试器结合起来。代码:importsnoop@snoopdefnumber_to_bits(number):ifnumber:bits=[]whilenumber:number,remainder=divmod(number,2)bits.insert(0,remainder)returnbitselse:return[0]number_to_bits(6)
2
importsnoop@snoopdefnumber_to_bits(number):ifnumber:bits=[]whilenumber:number,remainder=divmod(number,2)bits.insert(0,remainder)returnbitselse:return[0]number_to_bits(6)
3
snoop
的依赖性,需要分别安装birdseye
:pip install birdseye
@spy
的最大缺点是它显著降低了性能,因此对于具有许多循环迭代的函数应避免使用它。否则,您基本上可以一直使用它而不是@snoop
。然后,如果日志没有您需要的信息,您可以打开birdseye ui查看更多详细信息,而无需编辑或重新运行代码。当你感到懒惰和不确定哪种工具是最好的时候,这是个不错的选择。spy
将其参数传递给snoop
,因此例如@spy(depth=2,watch='x.y')
有效。spy
'之所以这样命名,是因为它是装饰名称'snoop
'和'eye
'的组合)install()
importsnoop@snoopdefnumber_to_bits(number):ifnumber:bits=[]whilenumber:number,remainder=divmod(number,2)bits.insert(0,remainder)returnbitselse:return[0]number_to_bits(6)
4
snoop
,p p
和spy
将在每个文件中都可用,而无需导入它们。importsnoop@snoopdefnumber_to_bits(number):ifnumber:bits=[]whilenumber:number,remainder=divmod(number,2)bits.insert(0,remainder)returnbitselse:return[0]number_to_bits(6)
5
@ss
install()
,请传递builtins=false
python breakpoint=snoop.snoop
,则可以使用新的breakpoint
函数代替snoop
禁用
snoop
和其他函数留在代码库中,但禁用它们的效果,请传递enabled=false
。例如,如果您使用django,将snoop.install(enabled=debug)
放在settings.py
中,以便在生产中自动禁用它。禁用后,性能影响降至最低,任何地方都没有输出。输出配置
install
有几个关键字参数用于控制snoop
和p p
的输出:out
:确定输出目标。默认情况下,这是stderr。您也可以通过:路径
对象。默认情况下,这将始终附加到文件中。传递overwrite=true
以最初清除文件。write
方法的对象,例如sys.stdout
或文件对象。logger.info
color
:确定输出是否包含转义字符,以便在控制台中显示彩色文本。如果在输出中看到奇怪的字符,则控制台不支持颜色,因此passcolor=false
out.isatty()
,对于stdout和stderr通常为true,但如果重定向或管道化,则为false。如果要强制着色,请传递true
或样式。前缀
:传递一个字符串以用该字符串开始所有窥探行,以便您可以轻松地为它们添加grep。列
:指定每个输出行开头的列。可以传递一个字符串,该字符串的内置列的名称由空格或逗号分隔。这些是可用的列:时间
:当前时间。默认情况下,这是唯一一列。线程
:当前线程的名称。线程标识
:如果线程名称不唯一,则当前线程的标识符。文件
:当前函数的文件名(不是完整路径)。完整文件
:文件的完整路径(调用函数时也会显示)。函数
:当前函数的名称。函数名
:当前函数的限定名。watch_extras
和替换watch_extras
:在高级用法下阅读这些内容
事件
对象。它有属性frame、event
和arg
,如sys.settrace()
中指定,以及其他可能更改的属性。api与
pysnooper的区别
pysnooper
并想使用snoop
,您应该注意以下几点,您必须采取不同的做法:前缀
和覆盖
到install()
,而不是snoop()
pysnooper.snoop
的第一个参数名为output
,应该用关键字out
传递给install
。
snoop(thread\u info=true)
,而是编写install(columns='time thread thread\u ident')
install(enabled=false)
,而不是环境变量pysnooper\u disabled
。
snoop而不是
pysnooper
,请阅读此处的比较
ipython/jupyter集成
snoop附带了一个ipython扩展,您可以在shell或笔记本中使用它。
首先,您需要使用笔记本单元格中的%load\/extn snoop
或将'snoop'
添加到列表中,来加载扩展名在您的ipython配置文件中,例如~/.ipython/profile\u default/ipython\u config.py
然后使用笔记本单元格顶部的单元格魔术%%snoop
跟踪该单元格:
高级用法
观看附加节目
install
有另一个参数名为watch_extras
。您可以将函数列表传递给它,以自动显示有关任何值的额外信息:局部变量、关注的表达式和分解项。例如,假设您想查看每个变量的类型。您可以定义这样的函数:
importsnoop@snoopdefnumber_to_bits(number):ifnumber:bits=[]whilenumber:number,remainder=divmod(number,2)bits.insert(0,remainder)returnbitselse:return[0]number_to_bits(6)6
然后编写安装(watch_extras=[type_watch])
。结果输出如下:
importsnoop@snoopdefnumber_to_bits(number):ifnumber:bits=[]whilenumber:number,remainder=divmod(number,2)bits.insert(0,remainder)returnbitselse:return[0]number_to_bits(6)7
您编写的函数应该接受两个参数source
和value
-通常这两个参数是变量名及其实际值。它们应该返回一对,表示返回信息的"源"(仅用于显示,不必是有效的python)和实际信息。如果不想为此特定值显示任何内容,请返回none
。任何引发的异常都会被捕获并被屏蔽。
默认情况下已经启用了两个这样的函数:一个显示值的len()
或.shape
属性(由numpy、pandas、tensorflow等使用),另一个显示.dtype
属性。
watch_extras
被添加到这两个默认函数中,因此您不必再次指定它们。如果不想包含它们,请使用replace_watch_extras
来指定确切的列表。原始功能可在此处找到:
importsnoop@snoopdefnumber_to_bits(number):ifnumber:bits=[]whilenumber:number,remainder=divmod(number,2)bits.insert(0,remainder)returnbitselse:return[0]number_to_bits(6)8
控制观看爆炸
watch_explode
将自动猜测如何根据类扩展传递给它的表达式。您可以使用下列类中的一个进行更具体的说明:
importsnoop@snoopdefnumber_to_bits(number):ifnumber:bits=[]whilenumber:number,remainder=divmod(number,2)bits.insert(0,remainder)returnbitselse:return[0]number_to_bits(6)9
使用exclude
参数排除特定键/属性/索引,例如attrs('x',exclude=(''u foo',''u bar')
在索引
之后添加一个切片,以仅查看该切片中的值,例如索引('z')[-3:
自定义变量的显示
(另请参见watch_extras
)
值使用廉价repr
库,以提高性能并避免控制台溢出。它为大多数常用类(包括来自第三方库的类)定义了一个特殊的repr函数。如果某个类丢失,请在那里打开一个问题。你也可以为班级注册你自己的代表。下面是一个示例:
importsnoopdefcache(func):d={}defwrapper(*args):try:returnd[args]exceptKeyError:result=d[args]=func(*args)returnresultreturnwrapper@snoop(depth=2)@cachedefadd(x,y):returnx+yadd(1,2)add(1,2)0
阅读更多信息,请在这里注册您自己的repr函数rel="nofollow"
您还可以增加各个类的详细程度(参见文档),例如:
importsnoopdefcache(func):d={}defwrapper(*args):try:returnd[args]exceptKeyError:result=d[args]=func(*args)returnresultreturnwrapper@snoop(depth=2)@cachedefadd(x,y):returnx+yadd(1,2)add(1,2)1
多个独立配置
如果需要比全局install
函数更多的控制,例如,如果要在一个进程中写入多个不同的文件,可以创建config
对象,例如:config=snoop.config(out=filename)
。然后config.snoop
,config.p p
和config.spy
将使用该配置,而不是全局配置。
参数与 我很想听听用户的意见!显然,如果您有问题,请打开一个问题,但也可以查看问题?Q=IS%3ASSUE+IS%3AOpen+label%3AConversation" rel="nofollow">带有‘discussion’标签的问题。还有很多工作要做,我真的很想听取人们的意见,这样我才能把事情做好。 你也可以给我发电子邮件,告诉我你喜欢或讨厌什么。知道它正在被使用是有帮助的。 请编写测试并使用tox运行它们 tox自动安装所有依赖项。您只需要安装tox本身: 如果要对所有目标python版本运行测试,请使用pyenv安装它们。否则,你可以跑
只有您已安装在计算机上的版本: 或者只需在开发人员模式下安装项目,其中包含测试依赖项: 并运行测试:install()
中与输出配置和启用相关的参数相同。
贡献
反馈和讨论
开发
importsnoopdefcache(func):d={}defwrapper(*args):try:returnd[args]exceptKeyError:result=d[args]=func(*args)returnresultreturnwrapper@snoop(depth=2)@cachedefadd(x,y):returnx+yadd(1,2)add(1,2)
2
importsnoopdefcache(func):d={}defwrapper(*args):try:returnd[args]exceptKeyError:result=d[args]=func(*args)returnresultreturnwrapper@snoop(depth=2)@cachedefadd(x,y):returnx+yadd(1,2)add(1,2)
3
importsnoopdefcache(func):d={}defwrapper(*args):try:returnd[args]exceptKeyError:result=d[args]=func(*args)returnresultreturnwrapper@snoop(depth=2)@cachedefadd(x,y):returnx+yadd(1,2)add(1,2)
4
importsnoopdefcache(func):d={}defwrapper(*args):try:returnd[args]exceptKeyError:result=d[args]=func(*args)returnresultreturnwrapper@snoop(depth=2)@cachedefadd(x,y):returnx+yadd(1,2)add(1,2)
5
推荐PyPI第三方库