适用于3.7 PEP 553之前版本的python条件调试断点单行代码,其行为类似于Perl的$ DB :: single = 1

719*_*016 15 python debugging perl breakpoints conditional-execution

在PEP 553 breakpoint()实用程序之前的python版本中,推荐的方法是添加(理想情况下是单行)代码以具有可以在某种条件下忽略的断点(例如,全局调试标志或args.debug标志)的推荐方法。

在Perl中,我习惯于使用$DB::single=1;1;单行,我知道我可以放心地留在代码中,perl code.pl除非明确调用,否则不会影响的正常运行perl -d code.pl。例如:

my $a = 1;
$DB::single=1;1; # breakpoint line
my $b = 2;
print "$a $b\n";
Run Code Online (Sandbox Code Playgroud)

如果我将此代码运行为:perl code.pl,它将运行完成。如果我使用以下代码运行该代码:perl -d code.plpdb它将在断点行处停止(而不是在带有my $b = 2;语句的下一行之前),因为它在1;语句后包含一条$DB::single=1;语句;

同样,如果我写:

my $debug = 1;
my $a = 1;
$DB::single=$debug;1; # first breakpoint line
my $b = 2;
$DB::single=$debug;1; # second breakpoint line
print "$a $b\n";
# [...] Lots more code sprinkled with more of these
$DB::single=$debug;1; # n'th breakpoint line
Run Code Online (Sandbox Code Playgroud)

然后perl -d code.pl,我可以执行,它将在第一个断点行中停止,然后在pdb会话中,一旦我很高兴它不需要在其他任何地方停止,则执行:$debug = 0,然后pdb继续c,这将使它不会在第二个或其他位置停止代码中类似的断点行。

我如何才能在python(PEP 553之前的2.x和3.x)中的单行语句中实现相同的效果?

我知道PEP 553,除了必须明确设置PYTHONBREAKPOINT=0 python3.7 code.py或注释breakpoint()行的麻烦之外,它还是这里问题的解决方案。

我想到了类似的选择:

import pdb; pdb.set_trace()
dummy=None;
Run Code Online (Sandbox Code Playgroud)

下面的语句pdb.set_trace()是为了使我能够与Perl 1;之后的同一行中的功能相同$DB::single=1;,这是使调试器停止我放置断点的位置,而不是下一条语句。这样,如果在它们之间有大量的注释代码或文档,调试器将不会跳转到远离断点的下一条语句。

或条件如下:

if args.debug or debug:
    import pdb; pdb.set_trace()
    _debug=False; #args.debug=False
Run Code Online (Sandbox Code Playgroud)

这样,如果我完成了脚本调试,便可以设置args.debug=Falsedebug=False不必触摸代码中的所有这些断点。

Arn*_*rne 7

设置条件断点

与perl相同,可以与python一起运行-d以设置调试标志:

$ python --help
[...]
-d     : debug output from parser; also PYTHONDEBUG=x
[...]
Run Code Online (Sandbox Code Playgroud)

您可以通过sys.flags以下方式在运行时检查其状态:

$ python -d
Python 2.7.15+ (default, Nov 27 2018, 23:36:35) 
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.flags
sys.flags(debug=1, py3k_warning=0, division_warning=0, ...)
#         ^ there it is, right at the front
Run Code Online (Sandbox Code Playgroud)

它允许以下一线式启用调试:

import pdb, sys; pdb.set_trace() if sys.flags[0] else None
Run Code Online (Sandbox Code Playgroud)

从调试器禁用条件断点

关于这部分

[...]一旦我很高兴它不需要在其他任何地方停止,然后执行[something],这将使它不会停止在代码的第二个或其他类似的断点行。

它变得有点棘手不过,因为Python没有允许突变flags结构,甚至是创建它的一个实例:

>>> import sys
>>> sys.flags.debug = 0                 # no mutating ...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: readonly attribute
>>> type(sys.flags)()                   # ... and no instanciating
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: cannot create 'sys.flags' instances
Run Code Online (Sandbox Code Playgroud)

但据我测试,除非您使用其他标志运行python,否则以下工作可停用后续跟踪,而不会改变程序的其他行为:

import sys; sys.flags = [0]*len(sys.flags)  # even fits onto a single line
Run Code Online (Sandbox Code Playgroud)

如果您要使用一个稍微更健壮的猴子补丁,以防前一个补丁导致奇怪的错误,则需要具有以下内容:

def untrace():
  """Call this function in the pdb session to skip debug-only set_trace() calls"""
  import re
  import sys
  from collections import namedtuple  # has the same interface as struct sequence
  sys.flags = namedtuple(
    'sys_flags', 
    [m.group() for m in re.finditer(r'\w{2,}', repr(sys.flags)[10:-1])]
  )(0, *sys.flags[1:])
Run Code Online (Sandbox Code Playgroud)

虽然此语句可以放在一行上,但可能有点太多。您可以将该函数粘贴到.py计划使用它的文件中,或者utils.py在调试期间从中导入该函数,然后ac(ontinue)应该再次运行程序的其余部分:

(Pdb) import utils; utils.untrace()
(Pdb) c
Run Code Online (Sandbox Code Playgroud)


Håk*_*and 4

.pdbrc这是使用当前目录中的文件的简单方法:

t.py

def my_trace():
    global debug
    if debug:
        import pdb; pdb.set_trace()

debug = True
a= 1
my_trace()
b = 2
c = 3
my_trace()
d = 4
Run Code Online (Sandbox Code Playgroud)

.pdbrc

r
Run Code Online (Sandbox Code Playgroud)

会话示例

$ python t.py
--Return--
> [...]/t.py(12)<module>()
-> b = 2
(Pdb) p a
1
(Pdb) p b
*** NameError: name 'b' is not defined
(Pdb) !debug=False
(Pdb) c
$ 
Run Code Online (Sandbox Code Playgroud)