Mon, 20 Feb 2017 19:32:48 +0100
Merged with changes done by Tobias.
--- a/DebugClients/Python/BreakpointWatch.py Mon Feb 20 19:27:37 2017 +0100 +++ b/DebugClients/Python/BreakpointWatch.py Mon Feb 20 19:32:48 2017 +0100 @@ -50,21 +50,21 @@ self.enabled = True self.ignore = 0 self.hits = 0 - self.breaks[filename, lineno] = self - lines = self.breakInFile.setdefault(filename, []) + Breakpoint.breaks[(filename, lineno)] = self + lines = Breakpoint.breakInFile.setdefault(filename, []) if lineno not in lines: lines.append(lineno) - self.breakInFrameCache.clear() + Breakpoint.breakInFrameCache.clear() def deleteMe(self): """ Public method to clear this breakpoint. """ try: - del self.breaks[(self.file, self.line)] - self.breakInFile[self.file].remove(self.line) - if not self.breakInFile[self.file]: - del self.breakInFile[self.file] + del Breakpoint.breaks[(self.file, self.line)] + Breakpoint.breakInFile[self.file].remove(self.line) + if not Breakpoint.breakInFile[self.file]: + del Breakpoint.breakInFile[self.file] except KeyError: pass @@ -100,8 +100,8 @@ """ Public method to clear all breakpoints. """ - for bp in Breakpoint.breaks.copy(): - bp.deleteMe() + Breakpoint.breaks.clear() + Breakpoint.breakInFile.clear() Breakpoint.breakInFrameCache.clear() @staticmethod @@ -222,14 +222,14 @@ self.changed = True self.values = {} - self.watches.append(self) + Watch.watches.append(self) def deleteMe(self): """ Public method to clear this watch expression. """ try: - del self.watches[self] + del Watch.watches[self] except ValueError: pass
--- a/DebugClients/Python/DebugBase.py Mon Feb 20 19:27:37 2017 +0100 +++ b/DebugClients/Python/DebugBase.py Mon Feb 20 19:32:48 2017 +0100 @@ -327,20 +327,6 @@ return self.trace_dispatch if event == 'return': - if self.stop_here(frame) or frame == self.botframe: - # Ignore return events in generator except when stepping. - if self.stopframe and frame.f_code.co_flags & CO_GENERATOR: - return - # The program has finished if we have just left the first frame - if frame == self.botframe: - if self.isMainThread: - atexit._run_exitfuncs() - self._dbgClient.progTerminated(arg) - else: - self._dbgClient.threadTerminated(self.id) - - if self.quitting and not self._dbgClient.passive: - raise SystemExit return if event == 'exception': @@ -351,7 +337,7 @@ # statement. if not (frame.f_code.co_flags & CO_GENERATOR and arg[0] is StopIteration and arg[2] is None): - self.user_exception(frame, arg) + self.user_exception(arg) # Stop at the StopIteration or GeneratorExit exception when the # user has set stopframe in a generator by issuing a return # command, or a next/until command at the last statement in the @@ -359,7 +345,7 @@ elif (self.stopframe and frame is not self.stopframe and self.stopframe.f_code.co_flags & CO_GENERATOR and arg[0] in (StopIteration, GeneratorExit)): - self.user_exception(frame, arg) + self.user_exception(arg) return if event == 'c_call': @@ -435,12 +421,14 @@ frame.f_trace = self.trace_dispatch target(*args, **kwargs) - except SystemExit: - pass + except Exception: + excinfo = sys.exc_info() + self.user_exception(excinfo, True) finally: + sys.settrace(None) sys.setprofile(None) - def run(self, cmd, globals=None, locals=None): + def run(self, cmd, globals=None, locals=None, debug=True): """ Public method to start a given command under debugger control. @@ -458,14 +446,24 @@ if locals is None: locals = globals - sys.settrace(self.trace_dispatch) if not isinstance(cmd, types.CodeType): cmd = compile(cmd, "<string>", "exec") + if debug: + sys.settrace(self.trace_dispatch) + try: exec(cmd, globals, locals) + atexit._run_exitfuncs() + self._dbgClient.progTerminated(0) except SystemExit: - pass + atexit._run_exitfuncs() + excinfo = sys.exc_info() + exitcode, message = self.__extractSystemExitMessage(excinfo) + self._dbgClient.progTerminated(exitcode, message) + except Exception: + excinfo = sys.exc_info() + self.user_exception(excinfo, True) finally: self.quitting = True sys.settrace(None) @@ -767,12 +765,10 @@ self.isBroken = False self._dbgClient.unlockClient() - def user_exception(self, frame, excinfo, unhandled=False): + def user_exception(self, excinfo, unhandled=False): """ Public method reimplemented to report an exception to the debug server. - @param frame the frame object - @type frame object @param excinfo details about the exception @type tuple(Exception, excval object, traceback frame object) @keyparam unhandled flag indicating an uncaught exception @@ -780,44 +776,10 @@ """ exctype, excval, exctb = excinfo - if exctype in [GeneratorExit, StopIteration]: + if exctype in [GeneratorExit, StopIteration, SystemExit]: # ignore these return - if exctype == SystemExit: - atexit._run_exitfuncs() - if excval is None: - exitcode = 0 - message = "" - elif isinstance(excval, basestring): - exitcode = 1 - message = excval - elif isinstance(excval, bytes): - exitcode = 1 - message = excval.decode() - elif isinstance(excval, int): - exitcode = excval - message = "" - elif isinstance(excval, SystemExit): - code = excval.code - if isinstance(code, basestring): - exitcode = 1 - message = code - elif isinstance(code, bytes): - exitcode = 1 - message = code.decode() - elif isinstance(code, int): - exitcode = code - message = "" - else: - exitcode = 1 - message = str(code) - else: - exitcode = 1 - message = str(excval) - self._dbgClient.progTerminated(exitcode, message) - return - if exctype in [SyntaxError, IndentationError]: try: # tuple could only occure on Python 2, but not always! @@ -934,6 +896,48 @@ tb = None return stack + def __extractSystemExitMessage(self, excinfo): + """ + Private method to get the SystemExit code and message. + + @param excinfo details about the SystemExit exception + @type tuple(Exception, excval object, traceback frame object) + @return SystemExit code and message + @rtype int, str + """ + exctype, excval, exctb = excinfo + if excval is None: + exitcode = 0 + message = "" + elif isinstance(excval, basestring): + exitcode = 1 + message = excval + elif isinstance(excval, bytes): + exitcode = 1 + message = excval.decode() + elif isinstance(excval, int): + exitcode = excval + message = "" + elif isinstance(excval, SystemExit): + code = excval.code + if isinstance(code, basestring): + exitcode = 1 + message = code + elif isinstance(code, bytes): + exitcode = 1 + message = code.decode() + elif isinstance(code, int): + exitcode = code + message = "" + else: + exitcode = 1 + message = str(code) + else: + exitcode = 1 + message = str(excval) + + return exitcode, message + def stop_here(self, frame): """ Public method reimplemented to filter out debugger files.
--- a/DebugClients/Python/DebugClientBase.py Mon Feb 20 19:27:37 2017 +0100 +++ b/DebugClients/Python/DebugClientBase.py Mon Feb 20 19:32:48 2017 +0100 @@ -494,8 +494,7 @@ code = self.__compileFileSource(self.running) if code: sys.setprofile(self.callTraceEnabled) - res = self.mainThread.run(code, self.debugMod.__dict__) - self.progTerminated(res) + self.mainThread.run(code, self.debugMod.__dict__, debug=True) elif method == "RequestRun": sys.argv = [] @@ -529,13 +528,7 @@ res = 0 code = self.__compileFileSource(self.running) if code: - try: - exec(code, self.debugMod.__dict__) - except SystemExit as exc: - res = exc.code - atexit._run_exitfuncs() - self.writestream.flush() - self.progTerminated(res) + self.mainThread.run(code, self.debugMod.__dict__, debug=False) elif method == "RequestCoverage": from coverage import coverage @@ -567,17 +560,10 @@ code = self.__compileFileSource(sys.argv[0]) if code: self.running = sys.argv[0] - res = 0 self.cover.start() - try: - exec(code, self.debugMod.__dict__) - except SystemExit as exc: - res = exc.code - atexit._run_exitfuncs() + self.mainThread.run(code, self.debugMod.__dict__, debug=False) self.cover.stop() self.cover.save() - self.writestream.flush() - self.progTerminated(res) elif method == "RequestProfile": sys.setprofile(None) @@ -618,12 +604,15 @@ res = 0 try: self.prof.run(script) + atexit._run_exitfuncs() except SystemExit as exc: res = exc.code - - atexit._run_exitfuncs() + atexit._run_exitfuncs() + except Exception: + excinfo = sys.exc_info() + self.__unhandled_exception(*excinfo) + self.prof.save() - self.writestream.flush() self.progTerminated(res) elif method == "ExecuteStatement": @@ -1189,7 +1178,7 @@ @param excval data about the exception @param exctb traceback for the exception """ - self.mainThread.user_exception(None, (exctype, excval, exctb), True) + self.mainThread.user_exception((exctype, excval, exctb), True) def __interceptSignals(self): """