强大的python调试工具

snoop的Python项目详细描述


窥探

构建状态支持python版本2.7和3.4+,包括pypy

snoop是一组强大的python调试工具。它的主要目的是a更具特色和更精致的版本。它还包括一个更精确的版本的冰淇淋和其他一些漂亮的东西。

您正试图找出为什么您的python代码没有做您认为应该做的事情。您很想使用一个完整的带有断点和监视的调试器,但现在您不必费心设置它。

您想知道哪些行正在运行,哪些没有运行,以及本地变量的值是什么。

大多数人会在战略位置使用打印行,其中一些显示变量值。

snoop允许您执行相同的操作,但您只需在感兴趣的函数中添加一个装饰符行,而不是精心设计右侧的print行。您将获得函数的逐行播放日志,包括运行的行数、运行的时间以及更改局部变量的确切时间。

安装非常简单,只要pip install snoop

基本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()

输出如下内容:

foo output

常见参数

  • 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中。
  • 在有魔法存在的情况下,可以在引擎盖下转换源代码,例如pytestbirdseye(因此还有@spy装饰器)。
  • 当苏RCE文件在第一次调用ppsnoop之前已被修改

在引擎盖下,p p使用库

pp的灵感来自于冰淇淋,并提供了相同的基本打印api,但是冰淇淋的算法比执行的算法更不准确,并且ppsnoop无缝集成。它还提供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的依赖性,需要分别安装birdseyepip install birdseye

@spy的最大缺点是它显著降低了性能,因此对于具有许多循环迭代的函数应避免使用它。否则,您基本上可以一直使用它而不是@snoop。然后,如果日志没有您需要的信息,您可以打开birdseye ui查看更多详细信息,而无需编辑或重新运行代码。当你感到懒惰和不确定哪种工具是最好的时候,这是个不错的选择。

spy将其参数传递给snoop,因此例如@spy(depth=2,watch='x.y')有效。

阅读此处文档中有关birdseye的更多信息。

('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

然后snoopp pspy将在每个文件中都可用,而无需导入它们。

您可以通过传递关键字参数来选择不同的名称,例如:

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 3.7+中,如果设置环境变量python breakpoint=snoop.snoop,则可以使用新的breakpoint函数代替snoop

禁用

如果希望将snoop和其他函数留在代码库中,但禁用它们的效果,请传递enabled=false。例如,如果您使用django,将snoop.install(enabled=debug)放在settings.py中,以便在生产中自动禁用它。禁用后,性能影响降至最低,任何地方都没有输出。

您还可以在任何时候通过再次调用snoop.install(enabled=true)来动态重新启用函数,例如在特殊视图或信号处理程序中。

输出配置

install有几个关键字参数用于控制snoopp p的输出:

  • out:确定输出目标。默认情况下,这是stderr。您也可以通过:

    • 要写入该位置的文件的字符串或路径对象。默认情况下,这将始终附加到文件中。传递overwrite=true以最初清除文件。
    • 任何具有write方法的对象,例如sys.stdout或文件对象。
    • 使用单个字符串参数(例如logger.info
  • color:确定输出是否包含转义字符,以便在控制台中显示彩色文本。如果在输出中看到奇怪的字符,则控制台不支持颜色,因此passcolor=false

    • 代码是使用pygments突出显示的语法,此参数作为样式传递。您可以通过传递命名样式的字符串(请参见此库或样式类)来选择不同的颜色方案。默认样式为Monokai。
    • 默认情况下,此参数设置为out.isatty(),对于stdout和stderr通常为true,但如果重定向或管道化,则为false。如果要强制着色,请传递true或样式。
    • 要在PyCharm运行窗口中查看颜色,请编辑运行配置并勾选"在输出控制台中模拟终端"。
  • 前缀:传递一个字符串以用该字符串开始所有窥探行,以便您可以轻松地为它们添加grep。

  • :指定每个输出行开头的列。可以传递一个字符串,该字符串的内置列的名称由空格或逗号分隔。这些是可用的列:

    • 时间:当前时间。默认情况下,这是唯一一列。
    • 线程:当前线程的名称。
    • 线程标识:如果线程名称不唯一,则当前线程的标识符
    • 文件:当前函数的文件名(不是完整路径)。
    • 完整文件:文件的完整路径(调用函数时也会显示)。
    • 函数:当前函数的名称。
    • 函数名:当前函数的限定名。
  • watch_extras替换watch_extras:在高级用法下阅读这些内容

    如果你想要一个自定义列,请打开一个问题告诉我你对什么感兴趣!同时,您可以传递一个列表,其中的元素可以是字符串,也可以是可调用的。callables应该有一个参数,它将是一个事件对象。它有属性frame、eventarg,如sys.settrace()中指定,以及其他可能更改的属性。

api与pysnooper的区别

如果您熟悉pysnooper并想使用snoop,您应该注意以下几点,您必须采取不同的做法:

    ass前缀覆盖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跟踪该单元格:

%%snoop example

高级用法

观看附加节目

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

您编写的函数应该接受两个参数sourcevalue-通常这两个参数是变量名及其实际值。它们应该返回一对,表示返回信息的"源"(仅用于显示,不必是有效的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.snoopconfig.p pconfig.spy将使用该配置,而不是全局配置。

参数与install()中与输出配置和启用相关的参数相同。

贡献

反馈和讨论

我很想听听用户的意见!显然,如果您有问题,请打开一个问题,但也可以查看问题?Q=IS%3ASSUE+IS%3AOpen+label%3AConversation" rel="nofollow">带有‘discussion’标签的问题。还有很多工作要做,我真的很想听取人们的意见,这样我才能把事情做好。

你也可以给我发电子邮件,告诉我你喜欢或讨厌什么。知道它正在被使用是有帮助的。

开发

始终欢迎拉取请求

请编写测试并使用tox运行它们

tox自动安装所有依赖项。您只需要安装tox本身:

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

如果要对所有目标python版本运行测试,请使用pyenv安装它们。否则,你可以跑 只有您已安装在计算机上的版本:

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

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java比较数据源中的行以打印特定记录   Java HashMap键值存储和检索   java使用Xugler实现MXF到FLV   将自定义字体设置为aspose Word java不起作用   java为什么示例1不合法而示例2合法?   无法解析java eclipse类型   java NativeScript:将本机安卓项目添加到NativeScript   java DAO函数,通过字符串concat将变量填充到SQL查询中,而不是使用var args和命名查询   java Ctrl+箭头键、多字变量名和eclipse pydev   java BufferReader显然没有从socket接收数据。简单聊天服务   java Spring启动版本从2.0.1升级到2.1.6,使URL“受保护”   java在iTextPDF中将粗体设置为自定义字体   java程序持续运行,但从未实际执行任何操作   spring boot如何使JWTfilter身份验证服务MS对java sts中不同n个微服务的n个数字请求通用   java Vaadin日期验证,2个或更多验证程序   svn贾瓦尔。无法使用我的java应用程序提交   excel Java Apache POI HSSF CellRangeAddressList   java意外标记左大括号({)位于位置4   java需要创建一个大型应用程序。使用ApachePOI的xls电子表格,堆内存使用有限   [Java][Eclipse]NoClassDefFoundError/ClassNotFoundException>>>ObjectMapper