Python程序的跟踪表

时间:2009-10-29 16:41:30

标签: python

有没有办法获取Python程序的跟踪表?或者一个程序运行另一个程序并获取其跟踪表?我是一名老师,试图完美地验证我们在测试中使用的跟踪问题的答案。

因此,例如,假设我有一个名为problem1.py的Python程序,其中包含以下内容:

problem1.py

 a = 1
 b = 2

 a = a + b

执行假定的程序traceTable.py应该如下:

 $ python traceTable.py problem1.py
 L || a | b
 1 || 1 |
 2 || 1 | 2
 4 || 3 | 2

(或使用不同语法的相同信息)

我查看了trace模块,我看不到它支持这种方式。


更新

女士们,先生们:使用Ned Batchelder的优秀建议,我给你traceTable.py

好吧......差不多。正如您在Ned Batchelder的示例中所看到的,frame.f_lineno并不总是直观地表现(例如,第3行和第4行都被计为第4行),但行号足够接近以获得相当好的参考。此外,所有计算都是正确的。

我用一个包含if语句的长程序测试了它,它给出了正确的表格(没有行号)。

您还会注意到,由于在他提到的较大程序中考虑了“更有趣的数据生态系统”,我的程序比Ned Batchelder的概念验证要长得多。在使用execfile和管理它所需的所有变量以及降低噪声(ala ignored_variables)以及生成正确的字符串输出的范围内,需要更多代码:

traceTable.py

 '''
 Usage: python traceTable.py program

     -program  Python program to be traced
 '''

 import sys

 if len(sys.argv) < 2:
      print __doc__
      exit()
 else:
      file_name = sys.argv[1]

 past_locals = {}
 variable_list = []
 table_content = ""

 ignored_variables = set([
      'file_name',
      'trace',
      'sys',
      'past_locals',
      'variable_list',
      'table_content',
      'getattr',
      'name',
      'self',
      'object',
      'consumed',
      'data',
      'ignored_variables'])

 def trace(frame, event, arg_unused):
      global past_locals, variable_list, table_content, ignored_variables
      relevant_locals = {}
      all_locals = frame.f_locals.copy()
      for k,v in all_locals.items():
           if not k.startswith("__") and k not in ignored_variables:
                relevant_locals[k] = v
      if len(relevant_locals) > 0 and past_locals != relevant_locals:
           for i in relevant_locals:
                if i not in past_locals:
                     variable_list.append(i)
           table_content += str(frame.f_lineno) + " || "
           for variable in variable_list:
                table_content += str(relevant_locals[variable]) + " | "
           table_content = table_content[:-2]
           table_content += '\n'
           past_locals = relevant_locals
      return trace

 sys.settrace(trace)

 execfile(file_name)

 table_header = "L || "
 for variable in variable_list:
      table_header += variable + ' | '
 table_header = table_header[:-2]
 print table_header
 print table_content

调用时,会产生输出

 $ python traceTable.py problem1.py
 L || a | b
 2 || 1
 4 || 1 | 2
 4 || 3 | 2

3 个答案:

答案 0 :(得分:10)

这不是当前Python跟踪工具支持的用例,但应该可以构建。我不知道你如何决定输出哪些列。在您的示例中,a和b是唯一的局部变量,但较大的程序会有更有趣的数据生态系统。

更新:这是一个简单的概念证明:

 1     import sys
 2
 3     def trace(frame, event, arg_unused):
 4         print event, frame.f_lineno, frame.f_locals
 5         return trace
 6
 7     sys.settrace(trace)
 8
 9     def foo():
10         a = 1
11         b = 2
12
13         a = a + b
14
15     foo()

运行时,输出为:

call 9 {}
line 10 {}
line 11 {'a': 1}
line 13 {'a': 1, 'b': 2}
return 13 {'a': 3, 'b': 2}

答案 1 :(得分:0)

您可以使用Python debugger,虽然我不知道如何让它自行完成,但它很可能,但您可以解析输出。

这是一个非常粗略的例子:

<强> adding.py

a = 1
b = 2

a = a + b

运行它......

PS >python -m pdb adding.py
> adding.py(1)<module>()
-> a = 1
(Pdb) alias stepprint step;;print a;;print b
(Pdb) stepprint
> adding.py(2)<module>()
-> b = 2
1
*** NameError: name 'b' is not defined
(Pdb) stepprint
> adding.py(4)<module>()
-> a = a + b
1
2
(Pdb) stepprint
--Return--
> adding.py(4)<module>()->None
-> a = a + b
3
2
(Pdb) stepprint
--Return--
> <string>(1)<module>()->None
3
2
(Pdb) stepprint
The program finished and will be restarted
> adding.py(1)<module>()
-> a = 1
*** NameError: name 'a' is not defined
*** NameError: name 'b' is not defined
(Pdb) q

PS >

结束(q)“程序完成”位。

答案 2 :(得分:0)

根据ned-batchelder提出的建议,作为一名教师,我已经制作了一个LaTeX课程,帮助创建longtable输出input(),显示一个程序的跟踪选择性变量,绕过\bash进行自动化过程(特别是在强大的害羞LaTeX包中由import sys class Tracer(): def __init__(self, varList=[], startLine=1, jeuEssai=[]): """ Arguments : \tvarList\ttraced variable list (used as column header) \tstartLine\toffset numbering line from the beginning of the program \tjeuEssai\tinput values to be sent to the automated input bypass """ self.traced_variables = varList self.traced_line_start = startLine self.input_values = jeuEssai self.input_cursor = int(0) self.traced_variables_new_values = dict( (k, '') for k in self.traced_variables) print("\\begin{longtable}{c*{%i}{>{\\ttfamily}c}}" % len(self.traced_variables), file=sys.stderr, flush=True) print("\t\\hline\\no ligne",end='', file=sys.stderr) for header in self.traced_variables: print(" &", header,end='', file=sys.stderr) print(" \\\\ \\hline", file=sys.stderr) sys.settrace(self.tracer_programme_latex) def tracer_programme_latex(self, frame, event, args): if frame.f_code.co_name not in ['input','print','close']: if event == "line": output = str() for var in self.traced_variables: current_val = str(frame.f_locals.get(var, "-")) if str(self.traced_variables_new_values.get(var, "-")) != current_val: self.traced_variables_new_values[var] = current_val current_val = "\hit{}" + current_val output += " & " output += current_val output += " \\\\" print("\t%s%s" % (str(frame.f_lineno - self.traced_line_start), output), file=sys.stderr, flush=True) return self.tracer_programme_latex def close(self): """Close the 'longtable' LaTeX environnement.""" print("\\end{longtable}", file=sys.stderr, flush=True) def input(self, prompt=None): """ bypass de la fonction 'input()' pour injecter les valeurs d'essais. Le jeu d'essai est fourni de manière cyclique. Cela peut causer des boucles infinies si vous ne fournissez pas une valeur permettant de réaliser l'arrêt des entrées (dans le cas bien-sûr où 'input()' est appelé dans une boucle). """ self.input_cursor = (1 + self.input_cursor) % len(self.input_values) return self.input_values[self.input_cursor - 1] def print(self, *args): pass 宏调用时)。

tracer.py:

def factor():
    question = "Give a number: "
    number = float(input(question))
    product = 1
    while number != 0 :
        product *= number
        print("Product:", product)
        number = float(input(question))

if __name__ == "__main__":
    import sys
    TRACING = len(sys.argv) == 2 and sys.argv[1] == 'trace'
    if TRACING:
        from tracer import Tracer
        t = Tracer(varList=['question','number','product'], startLine=2, jeuEssai=[7,6,5,-8,0])
        input = t.input

    factor()
    if TRACING:
        t.close()

接下来,你可以找到一个例子,并生成输出:

program.py:

python3 program.py

标准输出:(由Give a number: 7 Product: 7.0 Give a number: 6 Product: 42.0 Give a number: 5 Product: 210.0 Give a number: -8 Product: -1680.0 Give a number: 0 调用)

python3 program.py trace 1>/dev/null
使用Tracer输出

:(由\begin{longtable}{c*{3}{>{\ttfamily}c}} \hline\no ligne & question & number & product \\ \hline 0 & \hit{}- & \hit{}- & \hit{}- \\ 1 & \hit{}Give a number: & - & - \\ 2 & Give a number: & \hit{}7.0 & - \\ 3 & Give a number: & 7.0 & \hit{}1 \\ 4 & Give a number: & 7.0 & 1 \\ 5 & Give a number: & 7.0 & \hit{}7.0 \\ 6 & Give a number: & 7.0 & 7.0 \\ 3 & Give a number: & \hit{}6.0 & 7.0 \\ 4 & Give a number: & 6.0 & 7.0 \\ 5 & Give a number: & 6.0 & \hit{}42.0 \\ 6 & Give a number: & 6.0 & 42.0 \\ 3 & Give a number: & \hit{}5.0 & 42.0 \\ 4 & Give a number: & 5.0 & 42.0 \\ 5 & Give a number: & 5.0 & \hit{}210.0 \\ 6 & Give a number: & 5.0 & 210.0 \\ 3 & Give a number: & \hit{}-8.0 & 210.0 \\ 4 & Give a number: & -8.0 & 210.0 \\ 5 & Give a number: & -8.0 & \hit{}-1680.0 \\ 6 & Give a number: & -8.0 & -1680.0 \\ 3 & Give a number: & \hit{}0.0 & -1680.0 \\ \end{longtable} 调用)

\hit{}

值已更改时插入\newcommand{\hit}{\color{red}}宏。由您来定义相关内容,例如着色宏:CTE