DebugClients/Python/DebugBase.py

branch
jsonrpc
changeset 5132
a094eee9f862
parent 4683
1ba6ba86b383
equal deleted inserted replaced
5131:889ed5ff7a68 5132:a094eee9f862
12 import os 12 import os
13 import types 13 import types
14 import atexit 14 import atexit
15 import inspect 15 import inspect
16 import ctypes 16 import ctypes
17 17 from inspect import CO_GENERATOR
18 from DebugProtocol import ResponseClearWatch, ResponseClearBreak, \
19 ResponseLine, ResponseSyntax, ResponseException, CallTrace
20 18
21 gRecursionLimit = 64 19 gRecursionLimit = 64
22 20
23 21
24 def printerr(s): 22 def printerr(s):
55 @param dbgClient the owning client 53 @param dbgClient the owning client
56 """ 54 """
57 bdb.Bdb.__init__(self) 55 bdb.Bdb.__init__(self)
58 56
59 self._dbgClient = dbgClient 57 self._dbgClient = dbgClient
60 self._mainThread = 1 58 self._mainThread = True
61 59
62 self.breaks = self._dbgClient.breakpoints 60 self.breaks = self._dbgClient.breakpoints
63 61
64 self.__event = "" 62 self.__event = ""
65 self.__isBroken = "" 63 self.__isBroken = ""
198 """ 196 """
199 if self._dbgClient.callTraceEnabled: 197 if self._dbgClient.callTraceEnabled:
200 if not self.__skip_it(fromFrame) and not self.__skip_it(toFrame): 198 if not self.__skip_it(fromFrame) and not self.__skip_it(toFrame):
201 if event in ["call", "return"]: 199 if event in ["call", "return"]:
202 fr = fromFrame 200 fr = fromFrame
201 # TODO: change from and to info to a dictionary
203 fromStr = "%s:%s:%s" % ( 202 fromStr = "%s:%s:%s" % (
204 self._dbgClient.absPath(self.fix_frame_filename(fr)), 203 self._dbgClient.absPath(self.fix_frame_filename(fr)),
205 fr.f_lineno, 204 fr.f_lineno,
206 fr.f_code.co_name) 205 fr.f_code.co_name)
207 fr = toFrame 206 fr = toFrame
208 toStr = "%s:%s:%s" % ( 207 toStr = "%s:%s:%s" % (
209 self._dbgClient.absPath(self.fix_frame_filename(fr)), 208 self._dbgClient.absPath(self.fix_frame_filename(fr)),
210 fr.f_lineno, 209 fr.f_lineno,
211 fr.f_code.co_name) 210 fr.f_code.co_name)
212 self._dbgClient.write("%s%s@@%s@@%s\n" % ( 211 self._dbgClient.sendCallTrace(event, fromStr, toStr)
213 CallTrace, event[0], fromStr, toStr))
214 212
215 def trace_dispatch(self, frame, event, arg): 213 def trace_dispatch(self, frame, event, arg):
216 """ 214 """
217 Public method reimplemented from bdb.py to do some special things. 215 Public method reimplemented from bdb.py to do some special things.
218 216
277 @param arg The arguments 275 @param arg The arguments
278 @return local trace function 276 @return local trace function
279 @exception bdb.BdbQuit raised to indicate the end of the debug session 277 @exception bdb.BdbQuit raised to indicate the end of the debug session
280 """ 278 """
281 if self.stop_here(frame) or frame == self.returnframe: 279 if self.stop_here(frame) or frame == self.returnframe:
280 # Ignore return events in generator except when stepping.
281 if self.stopframe and frame.f_code.co_flags & CO_GENERATOR:
282 return self.trace_dispatch
282 self.user_return(frame, arg) 283 self.user_return(frame, arg)
283 if self.quitting and not self._dbgClient.passive: 284 if self.quitting and not self._dbgClient.passive:
284 raise bdb.BdbQuit 285 raise bdb.BdbQuit
285 return self.trace_dispatch 286 return self.trace_dispatch
286 287
292 @param arg The arguments 293 @param arg The arguments
293 @return local trace function 294 @return local trace function
294 @exception bdb.BdbQuit raised to indicate the end of the debug session 295 @exception bdb.BdbQuit raised to indicate the end of the debug session
295 """ 296 """
296 if not self.__skip_it(frame): 297 if not self.__skip_it(frame):
298 # When stepping with next/until/return in a generator frame,
299 # skip the internal StopIteration exception (with no traceback)
300 # triggered by a subiterator run with the 'yield from'
301 # statement.
302 if not (frame.f_code.co_flags & CO_GENERATOR and
303 arg[0] is StopIteration and arg[2] is None):
304 self.user_exception(frame, arg)
305 if self.quitting:
306 raise bdb.BdbQuit
307
308 # Stop at the StopIteration or GeneratorExit exception when the user
309 # has set stopframe in a generator by issuing a return command, or a
310 # next/until command at the last statement in the generator before the
311 # exception.
312 elif (self.stopframe and frame is not self.stopframe and
313 self.stopframe.f_code.co_flags & CO_GENERATOR and
314 arg[0] in (StopIteration, GeneratorExit)):
297 self.user_exception(frame, arg) 315 self.user_exception(frame, arg)
298 if self.quitting: 316 if self.quitting:
299 raise bdb.BdbQuit 317 raise bdb.BdbQuit
318
300 return self.trace_dispatch 319 return self.trace_dispatch
301 320
302 def set_trace(self, frame=None): 321 def set_trace(self, frame=None):
303 """ 322 """
304 Public method reimplemented from bdb.py to do some special setup. 323 Public method reimplemented from bdb.py to do some special setup.
316 @param special flag indicating a special continue operation 335 @param special flag indicating a special continue operation
317 """ 336 """
318 # Modified version of the one found in bdb.py 337 # Modified version of the one found in bdb.py
319 # Here we only set a new stop frame if it is a normal continue. 338 # Here we only set a new stop frame if it is a normal continue.
320 if not special: 339 if not special:
321 self.stopframe = self.botframe 340 self._set_stopinfo(self.botframe, None)
322 self.returnframe = None 341 else:
323 self.quitting = 0 342 self._set_stopinfo(self.stopframe, None)
324 343
325 def set_quit(self): 344 def set_quit(self):
326 """ 345 """
327 Public method to quit. 346 Public method to quit.
328 347
353 # get module name from __file__ 372 # get module name from __file__
354 if '__file__' in frame.f_globals and \ 373 if '__file__' in frame.f_globals and \
355 frame.f_globals['__file__'] and \ 374 frame.f_globals['__file__'] and \
356 frame.f_globals['__file__'] == frame.f_code.co_filename: 375 frame.f_globals['__file__'] == frame.f_code.co_filename:
357 root, ext = os.path.splitext(frame.f_globals['__file__']) 376 root, ext = os.path.splitext(frame.f_globals['__file__'])
358 if ext == '.pyc' or ext == '.py' or ext == '.pyo': 377 if ext in ['.pyc', '.py', '.py2', '.pyo']:
359 fixedName = root + '.py' 378 fixedName = root + '.py'
379 if os.path.exists(fixedName):
380 return fixedName
381
382 fixedName = root + '.py2'
360 if os.path.exists(fixedName): 383 if os.path.exists(fixedName):
361 return fixedName 384 return fixedName
362 385
363 return frame.f_code.co_filename 386 return frame.f_code.co_filename
364 387
418 Private method called to clear a temporary watch expression. 441 Private method called to clear a temporary watch expression.
419 442
420 @param cond expression of the watch expression to be cleared (string) 443 @param cond expression of the watch expression to be cleared (string)
421 """ 444 """
422 self.clear_watch(cond) 445 self.clear_watch(cond)
423 self._dbgClient.write('%s%s\n' % (ResponseClearWatch, cond)) 446 self._dbgClient.sendClearTemporaryWatch(cond)
424 447
425 def __effective(self, frame): 448 def __effective(self, frame):
426 """ 449 """
427 Private method to determine, if a watch expression is effective. 450 Private method to determine, if a watch expression is effective.
428 451
444 if b.special: 467 if b.special:
445 if b.special == '??created??': 468 if b.special == '??created??':
446 if b.values[frame][0] == 0: 469 if b.values[frame][0] == 0:
447 b.values[frame][0] = 1 470 b.values[frame][0] = 1
448 b.values[frame][1] = val 471 b.values[frame][1] = val
449 return (b, 1) 472 return (b, True)
450 else: 473 else:
451 continue 474 continue
452 b.values[frame][0] = 1 475 b.values[frame][0] = 1
453 if b.special == '??changed??': 476 if b.special == '??changed??':
454 if b.values[frame][1] != val: 477 if b.values[frame][1] != val:
455 b.values[frame][1] = val 478 b.values[frame][1] = val
456 if b.values[frame][2] > 0: 479 if b.values[frame][2] > 0:
457 b.values[frame][2] -= 1 480 b.values[frame][2] -= 1
458 continue 481 continue
459 else: 482 else:
460 return (b, 1) 483 return (b, True)
461 else: 484 else:
462 continue 485 continue
463 continue 486 continue
464 if val: 487 if val:
465 if b.ignore > 0: 488 if b.ignore > 0:
472 try: 495 try:
473 b.values[frame][0] = 0 496 b.values[frame][0] = 0
474 except KeyError: 497 except KeyError:
475 b.values[frame] = [0, None, b.ignore] 498 b.values[frame] = [0, None, b.ignore]
476 continue 499 continue
477 return (None, None) 500 return (None, False)
478 501
479 def break_here(self, frame): 502 def break_here(self, frame):
480 """ 503 """
481 Public method reimplemented from bdb.py to fix the filename from the 504 Public method reimplemented from bdb.py to fix the filename from the
482 frame. 505 frame.
486 @param frame the frame object 509 @param frame the frame object
487 @return flag indicating the break status (boolean) 510 @return flag indicating the break status (boolean)
488 """ 511 """
489 filename = self.canonic(self.fix_frame_filename(frame)) 512 filename = self.canonic(self.fix_frame_filename(frame))
490 if filename not in self.breaks and "Watch" not in self.breaks: 513 if filename not in self.breaks and "Watch" not in self.breaks:
491 return 0 514 return False
492 515
493 if filename in self.breaks: 516 if filename in self.breaks:
494 lineno = frame.f_lineno 517 lineno = frame.f_lineno
518 if lineno not in self.breaks[filename]:
519 # The line itself has no breakpoint, but maybe the line is the
520 # first line of a function with breakpoint set by function
521 # name.
522 lineno = frame.f_code.co_firstlineno
495 if lineno in self.breaks[filename]: 523 if lineno in self.breaks[filename]:
496 # flag says ok to delete temp. bp 524 # flag says ok to delete temp. breakpoint
497 (bp, flag) = bdb.effective(filename, lineno, frame) 525 (bp, flag) = bdb.effective(filename, lineno, frame)
498 if bp: 526 if bp:
499 self.currentbp = bp.number 527 self.currentbp = bp.number
500 if (flag and bp.temporary): 528 if (flag and bp.temporary):
501 self.__do_clear(filename, lineno) 529 self.__do_clear(filename, lineno)
502 return 1 530 return True
503 531
504 if "Watch" in self.breaks: 532 if "Watch" in self.breaks:
505 # flag says ok to delete temp. bp 533 # flag says ok to delete temp. watch
506 (bp, flag) = self.__effective(frame) 534 (bp, flag) = self.__effective(frame)
507 if bp: 535 if bp:
508 self.currentbp = bp.number 536 self.currentbp = bp.number
509 if (flag and bp.temporary): 537 if (flag and bp.temporary):
510 self.__do_clearWatch(bp.cond) 538 self.__do_clearWatch(bp.cond)
511 return 1 539 return True
512 540
513 return 0 541 return False
514 542
515 def break_anywhere(self, frame): 543 def break_anywhere(self, frame):
516 """ 544 """
517 Public method reimplemented from bdb.py to do some special things. 545 Public method reimplemented from bdb.py to do some special things.
518 546
549 577
550 @param filename name of the file the bp belongs to 578 @param filename name of the file the bp belongs to
551 @param lineno linenumber of the bp 579 @param lineno linenumber of the bp
552 """ 580 """
553 self.clear_break(filename, lineno) 581 self.clear_break(filename, lineno)
554 self._dbgClient.write('%s%s,%d\n' % (ResponseClearBreak, filename, 582 self._dbgClient.sendClearTemporaryBreakpoint(filename, lineno)
555 lineno))
556 583
557 def getStack(self): 584 def getStack(self):
558 """ 585 """
559 Public method to get the stack. 586 Public method to get the stack.
560 587
610 if self._dbgClient.mainFrame is None: 637 if self._dbgClient.mainFrame is None:
611 if fn != self._dbgClient.getRunning(): 638 if fn != self._dbgClient.getRunning():
612 return 639 return
613 fr = frame 640 fr = frame
614 while (fr is not None and 641 while (fr is not None and
615 fr.f_code != self._dbgClient.handleLine.func_code): 642 fr.f_code not in [
643 self._dbgClient.handleLine.func_code,
644 self._dbgClient.handleJsonCommand.func_code]):
616 self._dbgClient.mainFrame = fr 645 self._dbgClient.mainFrame = fr
617 fr = fr.f_back 646 fr = fr.f_back
618 647
619 self.currentFrame = frame 648 self.currentFrame = frame
620 649
652 else: 681 else:
653 fr = fr.f_back 682 fr = fr.f_back
654 683
655 self.__isBroken = True 684 self.__isBroken = True
656 685
657 self._dbgClient.write('%s%s\n' % (ResponseLine, unicode(stack))) 686 self._dbgClient.sendResponseLine(stack)
658 self._dbgClient.eventLoop() 687 self._dbgClient.eventLoop()
659 688
660 def user_exception(self, frame, (exctype, excval, exctb), unhandled=0): 689 def user_exception(self, frame, (exctype, excval, exctb), unhandled=0):
661 """ 690 """
662 Public method reimplemented to report an exception to the debug server. 691 Public method reimplemented to report an exception to the debug server.
672 return 701 return
673 702
674 if exctype in [SystemExit, bdb.BdbQuit]: 703 if exctype in [SystemExit, bdb.BdbQuit]:
675 atexit._run_exitfuncs() 704 atexit._run_exitfuncs()
676 if excval is None: 705 if excval is None:
677 excval = 0 706 exitcode = 0
707 message = ""
678 elif isinstance(excval, (unicode, str)): 708 elif isinstance(excval, (unicode, str)):
679 self._dbgClient.write(excval) 709 exitcode = 1
680 excval = 1 710 message = excval
681 if isinstance(excval, int): 711 elif isinstance(excval, int):
682 self._dbgClient.progTerminated(excval) 712 exitcode = excval
713 message = ""
714 elif isinstance(excval, SystemExit):
715 code = excval.code
716 if isinstance(code, (unicode, str)):
717 exitcode = 1
718 message = code
719 elif isinstance(code, int):
720 exitcode = code
721 message = ""
722 else:
723 exitcode = 1
724 message = str(code)
683 else: 725 else:
684 self._dbgClient.progTerminated(excval.code) 726 exitcode = 1
727 message = str(excval)
728 self._dbgClient.progTerminated(exitcode, message)
685 return 729 return
686 730
687 if exctype in [SyntaxError, IndentationError]: 731 if exctype in [SyntaxError, IndentationError]:
688 try: 732 try:
689 message, (filename, linenr, charnr, text) = excval 733 message, (filename, lineno, charno, text) = excval
690 except ValueError: 734 except ValueError:
691 exclist = [] 735 message = ""
736 filename = ""
737 lineno = 0
738 charno = 0
692 realSyntaxError = True 739 realSyntaxError = True
693 else:
694 exclist = [message, [filename, linenr, charnr]]
695 realSyntaxError = os.path.exists(filename)
696 740
697 if realSyntaxError: 741 if realSyntaxError:
698 self._dbgClient.write("%s%s\n" % (ResponseSyntax, 742 self._dbgClient.sendSyntaxError(
699 unicode(exclist))) 743 message, filename, lineno, charno)
700 self._dbgClient.eventLoop() 744 self._dbgClient.eventLoop()
701 return 745 return
702 746
703 if type(exctype) in [types.ClassType, # Python up to 2.4 747 if type(exctype) in [types.ClassType, # Python up to 2.4
704 types.TypeType]: # Python 2.5+ 748 types.TypeType]: # Python 2.5+
710 if unhandled: 754 if unhandled:
711 exctypetxt = "unhandled %s" % unicode(exctype) 755 exctypetxt = "unhandled %s" % unicode(exctype)
712 else: 756 else:
713 exctypetxt = unicode(exctype) 757 exctypetxt = unicode(exctype)
714 try: 758 try:
715 exclist = [exctypetxt, 759 excvaltxt = unicode(excval).encode(self._dbgClient.getCoding())
716 unicode(excval).encode(self._dbgClient.getCoding())]
717 except TypeError: 760 except TypeError:
718 exclist = [exctypetxt, str(excval)] 761 excvaltxt = str(excval)
719 762
763 stack = []
720 if exctb: 764 if exctb:
721 frlist = self.__extract_stack(exctb) 765 frlist = self.__extract_stack(exctb)
722 frlist.reverse() 766 frlist.reverse()
723 767
724 self.currentFrame = frlist[0] 768 self.currentFrame = frlist[0]
744 except Exception: 788 except Exception:
745 fargs = "" 789 fargs = ""
746 else: 790 else:
747 fargs = "" 791 fargs = ""
748 792
749 exclist.append([filename, linenr, ffunc, fargs]) 793 stack.append([filename, linenr, ffunc, fargs])
750 794
751 self._dbgClient.write("%s%s\n" % (ResponseException, unicode(exclist))) 795 self._dbgClient.sendException(exctypetxt, excvaltxt, stack)
752 796
753 if exctb is None: 797 if exctb is None:
754 return 798 return
755 799
756 self._dbgClient.eventLoop() 800 self._dbgClient.eventLoop()
796 840
797 @param frame the frame object 841 @param frame the frame object
798 @return flag indicating whether the debugger should stop here 842 @return flag indicating whether the debugger should stop here
799 """ 843 """
800 if self.__skip_it(frame): 844 if self.__skip_it(frame):
801 return 0 845 return False
802 return bdb.Bdb.stop_here(self, frame) 846 return bdb.Bdb.stop_here(self, frame)
803 847
804 def __skip_it(self, frame): 848 def __skip_it(self, frame):
805 """ 849 """
806 Private method to filter out debugger files. 850 Private method to filter out debugger files.
810 854
811 @param frame the frame object 855 @param frame the frame object
812 @return flag indicating whether the debugger should skip this frame 856 @return flag indicating whether the debugger should skip this frame
813 """ 857 """
814 if frame is None: 858 if frame is None:
815 return 1 859 return True
816 860
817 fn = self.fix_frame_filename(frame) 861 fn = self.fix_frame_filename(frame)
818 862
819 # Eliminate things like <string> and <stdin>. 863 # Eliminate things like <string> and <stdin>.
820 if fn[0] == '<': 864 if fn[0] == '<':
821 return 1 865 return True
822 866
823 #XXX - think of a better way to do this. It's only a convenience for 867 #XXX - think of a better way to do this. It's only a convenience for
824 #debugging the debugger - when the debugger code is in the current 868 #debugging the debugger - when the debugger code is in the current
825 #directory. 869 #directory.
826 if os.path.basename(fn) in [ 870 if os.path.basename(fn) in [
827 'AsyncFile.py', 'AsyncIO.py', 871 'AsyncFile.py', 'DCTestResult.py',
828 'DebugConfig.py', 'DCTestResult.py', 872 'DebugBase.py', 'DebugClient.py',
829 'DebugBase.py', 'DebugClientBase.py', 873 'DebugClientBase.py',
830 'DebugClientCapabilities.py', 'DebugClient.py', 874 'DebugClientCapabilities.py',
831 'DebugClientThreads.py', 'DebugProtocol.py', 875 'DebugClientThreads.py',
832 'DebugThread.py', 'FlexCompleter.py', 876 'DebugConfig.py', 'DebugThread.py',
877 'DebugUtilities.py', 'FlexCompleter.py',
833 'PyProfile.py'] or \ 878 'PyProfile.py'] or \
834 os.path.dirname(fn).endswith("coverage"): 879 os.path.dirname(fn).endswith("coverage"):
835 return 1 880 return True
836 881
837 if self._dbgClient.shouldSkip(fn): 882 if self._dbgClient.shouldSkip(fn):
838 return 1 883 return True
839 884
840 return 0 885 return False
841 886
842 def isBroken(self): 887 def isBroken(self):
843 """ 888 """
844 Public method to return the broken state of the debugger. 889 Public method to return the broken state of the debugger.
845 890

eric ide

mercurial