简体   繁体   中英

traceback shows only one line of a multiline command

I have added a small debugging aid to my server. It logs a stack trace obtained from traceback.format_stack()

It contains few incomplete lines like this:

File "/home/...../base/loop.py", line 361, in run
    self.outputs.fd_list, (), sleep)

which is not that much helpfull.

The source lines 360 and 361:

rlist, wlist, unused = select.select(self.inputs.fd_list,
                                     self.outputs.fd_list, (), sleep)

If only one line can be part of the stack trace, I would say the line 360 with the function name (here select.select ) is the right one, because the stack is created by calling functions.

Anyway, I would prefer the whole (logical) line to be printed. Or at least some context (eg 2 lines before). Is that possible? I mean with just an adequate effort, of course.

Tried to add a line continuation character \\ , but without success.


EPILOGUE : Based on Jean-François Fabre's answer and his code I'm going to use this function:

def print_trace():
    for fname, lnum, func, line in traceback.extract_stack()[:-1]:
        print('File "{}", line {}, in {}'.format(fname, lnum, func))
        try:
            with open(fname) as f:
                rl = f.readlines()
        except OSError:
            if line is not None:
                print("    " + line + "  <===")
            continue
        first = max(0, lnum-3)
        # read 2 lines before and 2 lines after
        for i, line in enumerate(rl[first:lnum+2]):
            line = line.rstrip()
            if i + first + 1 == lnum:
                print("    " + line + "  <===")
            elif line:
                print("    " + line)

"just with adequate effort" this can be done. But it's hack-like

check this example:

import traceback,re,os,sys

r = re.compile(r'File\s"(.*)",\sline\s(\d+)')

def print_trace():
    # discard the 2 deepest entries since they're a call to print_trace()
    lines = [str.split(x,"\n")[0] for x in traceback.format_stack()][:-2]

    for l in lines:
        m = r.search(l)
        if m != None:
            sys.stdout.write(l+"\n")
            file = m.group(1)
            line = int(m.group(2))-1
            if os.path.exists(file):
                with open(file,"r") as f:
                    rl = f.readlines()
                    tblines = rl[max(line-2,0):min(line+3,len(rl))]
                    # read 2 lines before and 2 lines after
                    for i,tl in enumerate(tblines):
                        tl = tl.rstrip()

                        if i==2:
                            sys.stdout.write("    "+tl+" <====\n")
                        elif tl:
                            sys.stdout.write("    "+tl+"\n")


def foo():
    print_trace()

foo()

output:

  File "C:\Users\dartypc\AppData\Roaming\PyScripter\remserver.py", line 63, in <module>
    if __name__ == "__main__":
        main() <====
  File "C:\Users\dartypc\AppData\Roaming\PyScripter\remserver.py", line 60, in main
        t = SimpleServer(ModSlaveService, port = port, auto_register = False)
        t.start() <====
    if __name__ == "__main__":
  File "C:\Program Files\PyScripter\Lib\rpyc.zip\rpyc\utils\server.py", line 227, in start
  File "C:\Program Files\PyScripter\Lib\rpyc.zip\rpyc\utils\server.py", line 139, in accept
  File "C:\Users\dartypc\AppData\Roaming\PyScripter\remserver.py", line 14, in _accept_method
    class SimpleServer(Server):
        def _accept_method(self, sock):
            self._serve_client(sock, None) <====
    class ModSlaveService(SlaveService):
  File "C:\Program Files\PyScripter\Lib\rpyc.zip\rpyc\utils\server.py", line 191, in _serve_client
  File "C:\Program Files\PyScripter\Lib\rpyc.zip\rpyc\core\protocol.py", line 391, in serve_all
  File "C:\Program Files\PyScripter\Lib\rpyc.zip\rpyc\core\protocol.py", line 382, in serve
  File "C:\Program Files\PyScripter\Lib\rpyc.zip\rpyc\core\protocol.py", line 350, in _dispatch
  File "C:\Program Files\PyScripter\Lib\rpyc.zip\rpyc\core\protocol.py", line 298, in _dispatch_request
  File "C:\Program Files\PyScripter\Lib\rpyc.zip\rpyc\core\protocol.py", line 528, in _handle_call
  File "<string>", line 420, in run_nodebug
  File "C:\DATA\jff\data\python\stackoverflow\traceback_test.py", line 31, in <module>
        print_trace()
    foo() <====

EDIT: VPfB suggested the use of extract_stack which is a little less "hacky", no need to parse a string, just get the quadruplet with traceback info (needs to rebuild the text message, but that's better)

import traceback,os,sys


def print_trace():
    # discard the 2 deepest entries since they're a call to print_trace()

    for file,line,w1,w2 in traceback.extract_stack()[:-2]:
        sys.stdout.write('  File "{}", line {}, in {}\n'.format(file,line,w1))

        if os.path.exists(file):
            line -= 1
            with open(file,"r") as f:
                rl = f.readlines()
                tblines = rl[max(line-2,0):min(line+3,len(rl))]
                # read 2 lines before and 2 lines after
                for i,tl in enumerate(tblines):
                    tl = tl.rstrip()

                    if i==2:
                        sys.stdout.write("    "+tl+" <====\n")
                    elif tl:
                        sys.stdout.write("    "+tl+"\n")


def foo():
    print_trace()

foo()

traceback.format_exception_only函数只格式化一行,除了SyntaxError 的情况,所以……

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM