你如何在pdb中观察变量

时间:2011-10-05 23:36:29

标签: python pdb

我正在调试一个python脚本,我想看一个变量进行更改(很像你可以在gdb中观看内存地址)。有没有办法做到这一点?

6 个答案:

答案 0 :(得分:23)

使用pdb执行此操作非常简单。每次使用~/.pdbrc时,都可以将这些命令放入pdb进行自动加载。

!global __currentframe, __stack; from inspect import currentframe as __currentframe, stack as __stack
!global __copy; from copy import copy as __copy
!global __Pdb; from pdb import Pdb as __Pdb
!global __pdb; __pdb = [__framerec[0].f_locals.get("pdb") or __framerec[0].f_locals.get("self") for __framerec in __stack() if (__framerec[0].f_locals.get("pdb") or __framerec[0].f_locals.get("self")).__class__ == __Pdb][-1]

alias _setup_watchpoint !global __key, __dict, __val; __key = '%1'; __dict = __currentframe().f_locals if __currentframe().f_locals.has_key(__key) else __currentframe().f_globals; __val = __copy(%1)

alias _nextwatch_internal next;; !if __dict[__key] == __val: __pdb.cmdqueue.append("_nextwatch_internal %1")
alias _stepwatch_internal step;; !if __dict[__key] == __val: __pdb.cmdqueue.append("_stepwatch_internal %1")

alias nextwatch __pdb.cmdqueue.extend(["_setup_watchpoint %1", "_nextwatch_internal"])
alias stepwatch __pdb.cmdqueue.extend(["_setup_watchpoint %1", "_stepwatch_internal"])

这会添加两个命令nextwatchstepwatch,每个命令都使用变量名 varname 作为参数。如果可能,他们将为 varname 创建当前帧的局部变量的浅表副本,并分别继续执行nextstep,直到该名称指向更改为止。

这适用于CPython 2.7.2,但依赖于某些pdb内部,因此它可能会在其他地方发生。

答案 1 :(得分:21)

要在遇到断点时观察变量,可以使用commands命令。例如。在遇到断点#1(canonical example from pdb doc)时打印some_variable

(Pdb) commands 1
(com) print some_variable
(com) end
(Pdb)

Python 3的更新

(Pdb) commands 1
(com) print(some_variable)
(com) end
(Pdb)

此外,您可以使用condition命令确保仅在变量获取特定值时才会触发断点。

例如:

(Pdb) condition 1 some_variable==some_value

答案 2 :(得分:15)

Python 3

您可以使用pdb的 display 功能

点击断点后,只需输入

即可

IPDB> 显示 表达

示例:

ipdb> display instance
display instance: <AppUser: dmitry4>
ipdb> display instance.id
display instance.id: 9
ipdb> display instance.university
display instance.university: <University: @domain.com>

ipdb> display

Currently displaying:
instance.university: <University: @domain.com>
instance.id: 9
instance: <AppUser: dmitry4>
ipdb> 

正如您所看到的,每次键入显示时 - 它都会打印所有手表(表达式)。您可以使用内置函数undisplay删除某些监视。

您还可以使用 pp表达式来打印表达式(非常有用)

答案 3 :(得分:12)

可能的解决方案是使用pdb++

pip install pdbpp

然后使用装饰器@pdb.break_on_setattr“标记”您要观看的对象:

from pdb import break_on_setattr
@break_on_setattr('bar')
class Foo(object):
    pass

f = Foo()
f.bar = 42    # the program breaks here

此处pdb将在任何Foo对象上的属性bar的任何更改中中断。

<强>注意事项
只有对基础__setattr__ - 方法的调用才会触发断点。这意味着f.bar = 'XYZ'setattr(f, 'XYZ')将起作用,但操纵bar - 对象不会触发断点:

f.bar = []
f.bar.append(7) # will NOT trigger breakpoint

f.bar = 2
f.bar += 5      # will trigger breakpoint

注意:@break_on_setattr不是标准pdb - 模块的一部分。 pdb - 包覆盖pdbpp pdb.set_trace()

您还可以在(Pdb++) import pdb (Pdb++) pdb.break_on_setattr('tree_id')(self.__class__) (Pdb++) continue

之后包装现有对象(通过其类)
var signatureHashHexExpected = "559bd871bfd21ab76ad44513ed5d65774f9954d3232ab68dab1806163f806447";
var signature = "123456:some-string:2016-04-12T12:44:16Z";
var key = "AgQGCAoMDhASFAIEBggKDA4QEhQCBAYICgwOEBIUAgQ=";

var shaKeyBytes = Convert.FromBase64String(key);
using (var shaAlgorithm = new System.Security.Cryptography.HMACSHA256(shaKeyBytes))
{
    var signatureBytes = System.Text.Encoding.UTF8.GetBytes(signature);
    var signatureHashBytes = shaAlgorithm.ComputeHash(signatureBytes);
    var signatureHashHex = string.Concat(Array.ConvertAll(signatureHashBytes, b => b.ToString("X2"))).ToLower();

    System.Diagnostics.Debug.Assert(signatureHashHex == signatureHashHexExpected);
}

答案 4 :(得分:2)

这是我的ipdb和python 3.7采用的Michael Hoffman's解决方案版本(与检查类类型和dict的has_key有关的更改):

!global __currentframe, __stack; from inspect import currentframe as __currentframe, stack as __stack
!global __copy; from copy import copy as __copy
!global __Pdb; from IPython.terminal.debugger import TerminalPdb as __Pdb
!global __pdb_list; __pdb_list = [__fr[0].f_locals.get("pdb") or __fr[0].f_locals.get("self") for __fr in __stack() if ((type(__fr[0].f_locals.get("pdb")) is __Pdb) or (type(__fr[0].f_locals.get("self")) is __Pdb))]
!global __pdb; __pdb = __pdb_list[0]
alias _setup_watchpoint !global __key, __dict, __val; __key = '%1'; __dict = __currentframe().f_locals if (__key in __currentframe().f_locals) else __currentframe().f_globals; __val = __copy(%1)
alias _nextwatch_internal next;; !if __dict[__key] == __val: __pdb.cmdqueue.append("_nextwatch_internal %1")
alias _stepwatch_internal step;; !if __dict[__key] == __val: __pdb.cmdqueue.append("_stepwatch_internal %1")
alias nextwatch __pdb.cmdqueue.extend(["_setup_watchpoint %1", "_nextwatch_internal"])
alias stepwatch __pdb.cmdqueue.extend(["_setup_watchpoint %1", "_stepwatch_internal"])

要在python 3.7和pdb中使用它,请更改__Pdb的定义,如下所示:

!global __Pdb; from pdb import Pdb as __Pdb

答案 5 :(得分:0)

两个现有的答案都有一些本质的麻烦。

直接在pdb中执行此操作,或使用一些技巧来实现观察点,仅在您处于同一范围内时才可以使用。当您脱离框架时,它将无法正常工作。

def change(var):
    var.append(1)

a = []
change(a)

它无法捕获函数中发生的事情,仅在change(a)之后知道变量a已更改。当功能不重要时,此方法有效,但实际上,功能可能很深,以至于有价值的东西都在里面。否则可能会更糟,a可能会返回并传递,并且永远不会更改当前范围。然后,您将永远不会捉住它。

只有您可以创建自己的类,__setattr__方法才有效。它不能处理我上面提到的简单情况。在许多情况下,使用新型对象是不可接受的。例如,如果您使用内置的dictlist

我建议使用一个名为watchpoints的库。

使用它比pdb容易得多(如果您还不熟悉它的话),并且比该线程中的任何一种解决方案都涵盖更多的情况。

您唯一需要做的就是watch您要观看的变量。

from watchpoints import watch

a = []
watch(a)
a.append(1)  # will print the changes

输出应为

> <module> (my_script.py:5):
>     a = 1
a:
0
->
1

它适用于内置类型(包括不可变类型),自定义类,甚至属性和索引。例如,您可以执行watch(my_obj.my_attr),它就可以正常工作。

您提到了pdb,因此除了简单地知道变量已更改之外,您还可能需要实现其他目标。 watchpoints工作时,pdb支持breakpoints()上拉。

您需要配置watchpoints

watch.config(pdb=True)

然后再添加watch项。更改变量后将弹出pdb。您可以q(uit) pdb,下次更改变量时,它将显示它。

您可以参考github页以更多地使用该库。