eric6/DebugClients/Python/DebugBase.py

branch
maintenance
changeset 8043
0acf98cd089a
parent 7924
8a96736d465e
parent 7986
2971d5d19951
child 8273
698ae46f40a4
equal deleted inserted replaced
7991:866adc8c315b 8043:0acf98cd089a
12 import types 12 import types
13 import atexit 13 import atexit
14 import inspect 14 import inspect
15 import ctypes 15 import ctypes
16 import time 16 import time
17 from inspect import CO_GENERATOR
18 import dis 17 import dis
19 18
20 from BreakpointWatch import Breakpoint, Watch 19 from BreakpointWatch import Breakpoint, Watch
21 20
22 import _thread 21 import _thread
23 from DebugUtilities import getargvalues, formatargvalues 22 from DebugUtilities import getargvalues, formatargvalues
24 23
25 gRecursionLimit = 64 24 gRecursionLimit = 64
25
26 try:
27 GENERATOR_AND_COROUTINE_FLAGS = (
28 inspect.CO_GENERATOR | inspect.CO_COROUTINE |
29 inspect.CO_ASYNC_GENERATOR
30 )
31 except AttributeError:
32 # Python < 3.7
33 GENERATOR_AND_COROUTINE_FLAGS = inspect.CO_GENERATOR
26 34
27 35
28 def printerr(s): 36 def printerr(s):
29 """ 37 """
30 Module function used for debugging the debug client. 38 Module function used for debugging the debug client.
82 90
83 # Special handling of a recursion error 91 # Special handling of a recursion error
84 self.skipFrames = 0 92 self.skipFrames = 0
85 93
86 self.isBroken = False 94 self.isBroken = False
95 self.isException = False
87 self.cFrame = None 96 self.cFrame = None
88 97
89 # current frame we are at 98 # current frame we are at
90 self.currentFrame = None 99 self.currentFrame = None
91 100
103 112
104 # provide a hook to perform a hard breakpoint 113 # provide a hook to perform a hard breakpoint
105 # Use it like this: 114 # Use it like this:
106 # if hasattr(sys, 'breakpoint): sys.breakpoint() 115 # if hasattr(sys, 'breakpoint): sys.breakpoint()
107 sys.breakpoint = self.set_trace 116 sys.breakpoint = self.set_trace
108 if sys.version_info[:2] >= (3, 7): 117 if sys.version_info >= (3, 7):
109 sys.breakpointhook = self.set_trace 118 sys.breakpointhook = self.set_trace
110 119
111 def __eventPollTimer(self): 120 def __eventPollTimer(self):
112 """ 121 """
113 Private method to set a flag every 0.5 s to check for new messages. 122 Private method to set a flag every 0.5 s to check for new messages.
134 def getFrameLocals(self, frmnr=0): 143 def getFrameLocals(self, frmnr=0):
135 """ 144 """
136 Public method to return the locals dictionary of the current frame 145 Public method to return the locals dictionary of the current frame
137 or a frame below. 146 or a frame below.
138 147
139 @keyparam frmnr distance of frame to get locals dictionary of. 0 is 148 @param frmnr distance of frame to get locals dictionary of. 0 is
140 the current frame (int) 149 the current frame (int)
141 @return locals dictionary of the frame 150 @return locals dictionary of the frame
142 """ 151 """
143 f = self.currentFrame 152 f = self.currentFrame
144 while f is not None and frmnr > 0: 153 while f is not None and frmnr > 0:
149 def storeFrameLocals(self, frmnr=0): 158 def storeFrameLocals(self, frmnr=0):
150 """ 159 """
151 Public method to store the locals into the frame, so an access to 160 Public method to store the locals into the frame, so an access to
152 frame.f_locals returns the last data. 161 frame.f_locals returns the last data.
153 162
154 @keyparam frmnr distance of frame to store locals dictionary to. 0 is 163 @param frmnr distance of frame to store locals dictionary to. 0 is
155 the current frame (int) 164 the current frame (int)
156 """ 165 """
157 cf = self.currentFrame 166 cf = self.currentFrame
158 while cf is not None and frmnr > 0: 167 while cf is not None and frmnr > 0:
159 cf = cf.f_back 168 cf = cf.f_back
307 if self.quitting: 316 if self.quitting:
308 raise SystemExit 317 raise SystemExit
309 318
310 if event == 'line': 319 if event == 'line':
311 if self.stop_here(frame) or self.break_here(frame): 320 if self.stop_here(frame) or self.break_here(frame):
312 if (self.stop_everywhere and frame.f_back and 321 if (
313 frame.f_back.f_code.co_name == "prepareJsonCommand"): 322 self.stop_everywhere and
323 frame.f_back and
324 frame.f_back.f_code.co_name == "prepareJsonCommand"
325 ):
314 # Just stepped into print statement, so skip these frames 326 # Just stepped into print statement, so skip these frames
315 self._set_stopinfo(None, frame.f_back) 327 self._set_stopinfo(None, frame.f_back)
316 else: 328 else:
317 self.user_line(frame) 329 self.user_line(frame)
318 return self.trace_dispatch 330 return self.trace_dispatch
319 331
320 if event == 'call': 332 if event == 'call':
321 if (self.stop_here(frame) or 333 if (
322 self.__checkBreakInFrame(frame) or 334 self.stop_here(frame) or
323 Watch.watches != []): 335 self.__checkBreakInFrame(frame) or
336 Watch.watches != []
337 ):
338 return self.trace_dispatch
339 elif (
340 self.stopframe and
341 frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS
342 ):
324 return self.trace_dispatch 343 return self.trace_dispatch
325 else: 344 else:
326 # No need to trace this function 345 # No need to trace this function
327 return None 346 return None
328 347
329 if event == 'return': 348 if event == 'return':
330 if frame == self.returnframe: 349 if self.stop_here(frame) or frame == self.returnframe:
331 # Only true if we didn't stopped in this frame, because it's 350 # Ignore return events in generator except when stepping.
351 if (
352 self.stopframe and
353 frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS
354 ):
355 return self.trace_dispatch
356 # Only true if we didn't stop in this frame, because it's
332 # belonging to the eric debugger. 357 # belonging to the eric debugger.
333 self._set_stopinfo(None, frame.f_back) 358 if self.stopframe is frame and self.stoplineno != -1:
359 self._set_stopinfo(None, frame.f_back)
334 return None 360 return None
335 361
336 if event == 'exception': 362 if event == 'exception':
337 if not self.__skipFrame(frame): 363 if not self.__skipFrame(frame):
338 # When stepping with next/until/return in a generator frame, 364 # When stepping with next/until/return in a generator frame,
339 # skip the internal StopIteration exception (with no traceback) 365 # skip the internal StopIteration exception (with no traceback)
340 # triggered by a subiterator run with the 'yield from' 366 # triggered by a subiterator run with the 'yield from'
341 # statement. 367 # statement.
342 if not (frame.f_code.co_flags & CO_GENERATOR and 368 if not (
343 arg[0] is StopIteration and arg[2] is None): 369 frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS and
370 arg[0] is StopIteration and
371 arg[2] is None
372 ):
344 self.user_exception(arg) 373 self.user_exception(arg)
345 # Stop at the StopIteration or GeneratorExit exception when the 374 # Stop at the StopIteration or GeneratorExit exception when the
346 # user has set stopframe in a generator by issuing a return 375 # user has set stopframe in a generator by issuing a return
347 # command, or a next/until command at the last statement in the 376 # command, or a next/until command at the last statement in the
348 # generator before the exception. 377 # generator before the exception.
349 elif (self.stopframe and frame is not self.stopframe and 378 elif (
350 self.stopframe.f_code.co_flags & CO_GENERATOR and 379 self.stopframe and
351 arg[0] in (StopIteration, GeneratorExit)): 380 frame is not self.stopframe and
381 (self.stopframe.f_code.co_flags &
382 GENERATOR_AND_COROUTINE_FLAGS) and
383 arg[0] in (StopIteration, GeneratorExit)
384 ):
352 self.user_exception(arg) 385 self.user_exception(arg)
353 return None 386 return None
354 387
355 if event == 'c_call': 388 if event == 'c_call':
356 return None 389 return None
360 return None 393 return None
361 394
362 print('DebugBase.trace_dispatch:' # __IGNORE_WARNING_M801__ 395 print('DebugBase.trace_dispatch:' # __IGNORE_WARNING_M801__
363 ' unknown debugging event: ', 396 ' unknown debugging event: ',
364 repr(event)) 397 repr(event))
398
365 return self.trace_dispatch 399 return self.trace_dispatch
366 400
367 def set_trace(self, frame=None): 401 def set_trace(self, frame=None):
368 """ 402 """
369 Public method to start debugging from 'frame'. 403 Public method to start debugging from 'frame'.
370 404
371 If frame is not specified, debugging starts from caller's frame. 405 If frame is not specified, debugging starts from caller's frame.
372 Because of jump optimizations it's not possible to use sys.breakpoint() 406 Because of jump optimizations it's not possible to use sys.breakpoint()
373 as last instruction in a function or method. 407 as last instruction in a function or method.
374 408
375 @keyparam frame frame to start debugging from 409 @param frame frame to start debugging from
376 @type frame object 410 @type frame object
377 """ 411 """
378 if frame is None: 412 if frame is None:
379 frame = sys._getframe().f_back # Skip set_trace method 413 frame = sys._getframe().f_back # Skip set_trace method
380 414
381 stopOnHandleCommand = self._dbgClient.handleJsonCommand.__code__ 415 stopOnHandleCommand = self._dbgClient.handleJsonCommand.__code__
382 416
383 frame.f_trace = self.trace_dispatch 417 frame.f_trace = self.trace_dispatch
384 while frame.f_back is not None: 418 while frame.f_back is not None:
385 # stop at eric's debugger frame or a threading bootstrap 419 # stop at eric's debugger frame or a threading bootstrap
386 if (frame.f_back.f_code == stopOnHandleCommand): 420 if frame.f_back.f_code == stopOnHandleCommand:
387 frame.f_trace = self.trace_dispatch 421 frame.f_trace = self.trace_dispatch
388 break 422 break
389 423
390 frame = frame.f_back 424 frame = frame.f_back
391 425
418 self.user_exception(excinfo, True) 452 self.user_exception(excinfo, True)
419 finally: 453 finally:
420 sys.settrace(None) 454 sys.settrace(None)
421 sys.setprofile(None) 455 sys.setprofile(None)
422 456
423 def run(self, cmd, globalsDict=None, localsDict=None, debug=True): 457 def run(self, cmd, globalsDict=None, localsDict=None, debug=True,
458 closeSession=True):
424 """ 459 """
425 Public method to start a given command under debugger control. 460 Public method to start a given command under debugger control.
426 461
427 @param cmd command / code to execute under debugger control 462 @param cmd command / code to execute under debugger control
428 @type str or CodeType 463 @type str or CodeType
429 @keyparam globalsDict dictionary of global variables for cmd 464 @param globalsDict dictionary of global variables for cmd
430 @type dict 465 @type dict
431 @keyparam localsDict dictionary of local variables for cmd 466 @param localsDict dictionary of local variables for cmd
432 @type dict 467 @type dict
433 @keyparam debug flag if command should run under debugger control 468 @param debug flag if command should run under debugger control
469 @type bool
470 @return exit code of the program
471 @rtype int
472 @param closeSession flag indicating to close the debugger session
473 at exit
434 @type bool 474 @type bool
435 """ 475 """
436 if globalsDict is None: 476 if globalsDict is None:
437 import __main__ 477 import __main__
438 globalsDict = __main__.__dict__ 478 globalsDict = __main__.__dict__
451 sys.settrace(self.trace_dispatch) 491 sys.settrace(self.trace_dispatch)
452 492
453 try: 493 try:
454 exec(cmd, globalsDict, localsDict) # secok 494 exec(cmd, globalsDict, localsDict) # secok
455 atexit._run_exitfuncs() 495 atexit._run_exitfuncs()
456 self._dbgClient.progTerminated(0) 496 self._dbgClient.progTerminated(0, closeSession=closeSession)
497 exitcode = 0
457 except SystemExit: 498 except SystemExit:
458 atexit._run_exitfuncs() 499 atexit._run_exitfuncs()
459 excinfo = sys.exc_info() 500 excinfo = sys.exc_info()
460 exitcode, message = self.__extractSystemExitMessage(excinfo) 501 exitcode, message = self.__extractSystemExitMessage(excinfo)
461 self._dbgClient.progTerminated(exitcode, message) 502 self._dbgClient.progTerminated(exitcode, message=message,
503 closeSession=closeSession)
462 except Exception: 504 except Exception:
463 excinfo = sys.exc_info() 505 excinfo = sys.exc_info()
464 self.user_exception(excinfo, True) 506 self.user_exception(excinfo, True)
507 exitcode = 242
465 finally: 508 finally:
466 self.quitting = True 509 self.quitting = True
467 sys.settrace(None) 510 sys.settrace(None)
468 511 return exitcode
469 def _set_stopinfo(self, stopframe, returnframe): 512
513 def _set_stopinfo(self, stopframe, returnframe, stoplineno=0):
470 """ 514 """
471 Protected method to update the frame pointers. 515 Protected method to update the frame pointers.
472 516
473 @param stopframe the frame object where to stop 517 @param stopframe the frame object where to stop
474 @type frame object 518 @type frame object
475 @param returnframe the frame object where to stop on a function return 519 @param returnframe the frame object where to stop on a function return
476 @type frame object 520 @type frame object
521 @param stoplineno line number to stop at. If stoplineno is greater than
522 or equal to 0, then stop at line greater than or equal to the
523 stopline. If stoplineno is -1, then don't stop at all.
524 @type int
477 """ 525 """
478 self.stopframe = stopframe 526 self.stopframe = stopframe
479 self.returnframe = returnframe 527 self.returnframe = returnframe
528 # stoplineno >= 0 means: stop at line >= the stoplineno
529 # stoplineno -1 means: don't stop at all
530 self.stoplineno = stoplineno
531
480 if returnframe is not None: 532 if returnframe is not None:
481 # Ensure to be able to stop on the return frame 533 # Ensure to be able to stop on the return frame
482 returnframe.f_trace = self.trace_dispatch 534 returnframe.f_trace = self.trace_dispatch
483 self.stop_everywhere = False 535 self.stop_everywhere = False
484 536
489 @param special flag indicating a special continue operation 541 @param special flag indicating a special continue operation
490 @type bool 542 @type bool
491 """ 543 """
492 # Here we only set a new stop frame if it is a normal continue. 544 # Here we only set a new stop frame if it is a normal continue.
493 if not special: 545 if not special:
494 self._set_stopinfo(None, None) 546 self._set_stopinfo(None, None, -1)
495 547
496 # Disable tracing if not started in debug mode 548 # Disable tracing if not started in debug mode
497 if not self._dbgClient.debugging: 549 if not self._dbgClient.debugging:
498 sys.settrace(None) 550 sys.settrace(None)
499 sys.setprofile(None) 551 sys.setprofile(None)
500 552
553 def set_until(self, frame=None, lineno=None):
554 """
555 Public method to stop when the line with the lineno greater than the
556 current one is reached or when returning from current frame.
557
558 @param frame reference to the frame object
559 @type frame object
560 @param lineno line number to continue to
561 @type int
562 """
563 # the name "until" is borrowed from gdb
564 if frame is None:
565 frame = self.currentFrame
566 if lineno is None:
567 lineno = frame.f_lineno + 1
568 self._set_stopinfo(frame, frame, lineno)
569
501 def set_step(self): 570 def set_step(self):
502 """ 571 """
503 Public method to stop after one line of code. 572 Public method to stop after one line of code.
504 """ 573 """
505 self._set_stopinfo(None, None) 574 self._set_stopinfo(None, None)
532 @type int 601 @type int
533 """ 602 """
534 try: 603 try:
535 self.currentFrame.f_lineno = lineno 604 self.currentFrame.f_lineno = lineno
536 stack = self.getStack(self.currentFrame) 605 stack = self.getStack(self.currentFrame)
537 self._dbgClient.sendResponseLine(stack) 606 self._dbgClient.sendResponseLine(stack, self.name)
538 except Exception as e: 607 except Exception as e:
539 printerr(e) 608 printerr(e)
540 609
541 def set_quit(self): 610 def set_quit(self):
542 """ 611 """
690 759
691 def getStack(self, frame=None, applyTrace=False): 760 def getStack(self, frame=None, applyTrace=False):
692 """ 761 """
693 Public method to get the stack. 762 Public method to get the stack.
694 763
695 @keyparam frame frame object to inspect 764 @param frame frame object to inspect
696 @type frame object or list 765 @type frame object or list
697 @keyparam applyTrace flag to assign trace function to fr.f_trace 766 @param applyTrace flag to assign trace function to fr.f_trace
698 @type bool 767 @type bool
699 @return list of lists with file name (string), line number (integer) 768 @return list of lists with file name (string), line number (integer)
700 and function name (string) 769 and function name (string)
701 """ 770 """
702 tb_lineno = None 771 tb_lineno = None
718 fr.f_trace = self.trace_dispatch 787 fr.f_trace = self.trace_dispatch
719 788
720 fname = self._dbgClient.absPath(self.fix_frame_filename(fr)) 789 fname = self._dbgClient.absPath(self.fix_frame_filename(fr))
721 # Always show at least one stack frame, even if it's from eric. 790 # Always show at least one stack frame, even if it's from eric.
722 if stack and os.path.basename(fname).startswith( 791 if stack and os.path.basename(fname).startswith(
723 ("DebugBase.py", "DebugClientBase.py", 792 ("DebugBase.py", "DebugClientBase.py",
724 "ThreadExtension.py", "threading.py")): 793 "ThreadExtension.py", "threading.py")
794 ):
725 break 795 break
726 796
727 fline = tb_lineno or fr.f_lineno 797 fline = tb_lineno or fr.f_lineno
728 ffunc = fr.f_code.co_name 798 ffunc = fr.f_code.co_name
729 799
771 841
772 self._dbgClient.lockClient() 842 self._dbgClient.lockClient()
773 self._dbgClient.currentThread = self 843 self._dbgClient.currentThread = self
774 self._dbgClient.currentThreadExec = self 844 self._dbgClient.currentThreadExec = self
775 845
776 self._dbgClient.sendResponseLine(stack) 846 self._dbgClient.sendResponseLine(stack, self.name)
777 self._dbgClient.eventLoop() 847 self._dbgClient.eventLoop()
778 848
779 self.isBroken = False 849 self.isBroken = False
780 self._dbgClient.unlockClient() 850 self._dbgClient.unlockClient()
781 851
852 self._dbgClient.dumpThreadList()
853
782 def user_exception(self, excinfo, unhandled=False): 854 def user_exception(self, excinfo, unhandled=False):
783 """ 855 """
784 Public method reimplemented to report an exception to the debug server. 856 Public method reimplemented to report an exception to the debug server.
785 857
786 @param excinfo details about the exception 858 @param excinfo details about the exception
787 @type tuple(Exception, excval object, traceback frame object) 859 @type tuple(Exception, excval object, traceback frame object)
788 @keyparam unhandled flag indicating an uncaught exception 860 @param unhandled flag indicating an uncaught exception
789 @type bool 861 @type bool
790 """ 862 """
791 exctype, excval, exctb = excinfo 863 exctype, excval, exctb = excinfo
792 864
793 if ((exctype in [GeneratorExit, StopIteration] and 865 if ((exctype in [GeneratorExit, StopIteration] and
823 charno = 0 895 charno = 0
824 realSyntaxError = True 896 realSyntaxError = True
825 897
826 if realSyntaxError: 898 if realSyntaxError:
827 self._dbgClient.sendSyntaxError( 899 self._dbgClient.sendSyntaxError(
828 message, filename, lineno, charno) 900 message, filename, lineno, charno, self.name)
829 self._dbgClient.eventLoop() 901 self._dbgClient.eventLoop()
830 return 902 return
831 903
832 self.skipFrames = 0 904 self.skipFrames = 0
833 if (exctype == RuntimeError and 905 if (exctype == RuntimeError and
863 # Don't step into libraries, which are used by our debugger methods 935 # Don't step into libraries, which are used by our debugger methods
864 if exctb is not None: 936 if exctb is not None:
865 self.stop_everywhere = False 937 self.stop_everywhere = False
866 938
867 self.isBroken = True 939 self.isBroken = True
940 self.isException = True
868 941
869 disassembly = None 942 disassembly = None
870 stack = [] 943 stack = []
871 if exctb: 944 if exctb:
872 frlist = self.__extract_stack(exctb) 945 frlist = self.__extract_stack(exctb)
877 stack = self.getStack(frlist[self.skipFrames:]) 950 stack = self.getStack(frlist[self.skipFrames:])
878 951
879 self._dbgClient.lockClient() 952 self._dbgClient.lockClient()
880 self._dbgClient.currentThread = self 953 self._dbgClient.currentThread = self
881 self._dbgClient.currentThreadExec = self 954 self._dbgClient.currentThreadExec = self
882 self._dbgClient.sendException(exctypetxt, excvaltxt, stack) 955 self._dbgClient.sendException(exctypetxt, excvaltxt, stack, self.name)
883 self._dbgClient.setDisassembly(disassembly) 956 self._dbgClient.setDisassembly(disassembly)
884 self._dbgClient.dumpThreadList() 957 self._dbgClient.dumpThreadList()
885 958
886 if exctb is not None: 959 if exctb is not None:
887 # When polling kept enabled, it isn't possible to resume after an 960 # When polling kept enabled, it isn't possible to resume after an
889 self._dbgClient.eventLoop(True) 962 self._dbgClient.eventLoop(True)
890 963
891 self.skipFrames = 0 964 self.skipFrames = 0
892 965
893 self.isBroken = False 966 self.isBroken = False
967 self.isException = False
894 stop_everywhere = self.stop_everywhere 968 stop_everywhere = self.stop_everywhere
895 self.stop_everywhere = False 969 self.stop_everywhere = False
896 self.eventPollFlag = False 970 self.eventPollFlag = False
897 self._dbgClient.unlockClient() 971 self._dbgClient.unlockClient()
898 self.stop_everywhere = stop_everywhere 972 self.stop_everywhere = stop_everywhere
973
974 self._dbgClient.dumpThreadList()
899 975
900 def __extractExceptionName(self, exctype): 976 def __extractExceptionName(self, exctype):
901 """ 977 """
902 Private method to extract the exception name given the exception 978 Private method to extract the exception name given the exception
903 type object. 979 type object.
1007 exitcode = 1 1083 exitcode = 1
1008 message = code.decode() 1084 message = code.decode()
1009 elif isinstance(code, int): 1085 elif isinstance(code, int):
1010 exitcode = code 1086 exitcode = code
1011 message = "" 1087 message = ""
1088 elif code is None:
1089 exitcode = 0
1090 message = ""
1012 else: 1091 else:
1013 exitcode = 1 1092 exitcode = 1
1014 message = str(code) 1093 message = str(code)
1015 else: 1094 else:
1016 exitcode = 1 1095 exitcode = 1
1017 message = str(excval) 1096 message = str(excval)
1018 1097
1019 return exitcode, message 1098 return exitcode, message
1020 1099
1021 def stop_here(self, frame): 1100 def stop_here(self, frame):
1022 """ 1101 """
1023 Public method reimplemented to filter out debugger files. 1102 Public method reimplemented to filter out debugger files.
1024 1103
1025 Tracing is turned off for files that are part of the 1104 Tracing is turned off for files that are part of the
1031 @rtype bool 1110 @rtype bool
1032 """ 1111 """
1033 if self.__skipFrame(frame): 1112 if self.__skipFrame(frame):
1034 return False 1113 return False
1035 1114
1036 return (self.stop_everywhere or 1115 if frame is self.stopframe:
1037 frame is self.stopframe or 1116 if self.stoplineno == -1:
1038 frame is self.returnframe) 1117 return False
1118 return frame.f_lineno >= self.stoplineno
1119 return self.stop_everywhere or frame is self.returnframe
1039 1120
1040 def tracePythonLibs(self, enable): 1121 def tracePythonLibs(self, enable):
1041 """ 1122 """
1042 Public method to update the settings to trace into Python libraries. 1123 Public method to update the settings to trace into Python libraries.
1043 1124

eric ide

mercurial