Sun, 26 Jun 2016 16:32:01 +0200
First improvements on debugger speed: Cheaper detection of unwanted files and
some litte style changes to be equal between Python 2/3.
--- a/DebugClients/Python/DebugBase.py Thu Jun 23 19:24:09 2016 +0200 +++ b/DebugClients/Python/DebugBase.py Sun Jun 26 16:32:01 2016 +0200 @@ -48,6 +48,13 @@ Provides simple wrapper methods around bdb for the 'owning' client to call to step etc. """ + # Don't thrust distutils.sysconfig.get_python_lib: possible case mismatch + # on Windows + lib = os.path.dirname(bdb.__file__) + # tuple required because it's accessed a lot of times by startswith method + pathsToSkip = ('<', os.path.dirname(__file__), bdb.__file__[:-1]) + filesToSkip = {} + def __init__(self, dbgClient): """ Constructor @@ -60,6 +67,7 @@ self._mainThread = 1 self.breaks = self._dbgClient.breakpoints + self.tracePythonLibs(0) self.__event = "" self.__isBroken = "" @@ -197,7 +205,8 @@ @param toFrame destination frame (frame) """ if self._dbgClient.callTraceEnabled: - if not self.__skip_it(fromFrame) and not self.__skip_it(toFrame): + if (not self.__skipFrame(fromFrame) and + not self.__skipFrame(toFrame)): if event in ["call", "return"]: fr = fromFrame fromStr = "%s:%s:%s" % ( @@ -248,7 +257,7 @@ return self.trace_dispatch if event == 'c_return': return self.trace_dispatch - print 'DebugBase.trace_dispatch: unknown debugging event:', repr(event) # __IGNORE_WARNING__ + print 'DebugBase.trace_dispatch: unknown debugging event:', repr(event) # __IGNORE_WARNING__ return self.trace_dispatch def dispatch_line(self, frame): @@ -293,7 +302,7 @@ @return local trace function @exception bdb.BdbQuit raised to indicate the end of the debug session """ - if not self.__skip_it(frame): + if not self.__skipFrame(frame): self.user_exception(frame, arg) if self.quitting: raise bdb.BdbQuit @@ -534,8 +543,8 @@ Because eric6 supports only one breakpoint per line, this overwritten method will return this one and only breakpoint. - @param filename filename of the bp to retrieve (string) - @param lineno linenumber of the bp to retrieve (integer) + @param filename the filename of the bp to retrieve (string) + @param lineno the linenumber of the bp to retrieve (integer) @return breakpoint or None, if there is no bp """ filename = self.canonic(filename) @@ -772,8 +781,8 @@ def user_return(self, frame, retval): """ - Public method reimplemented to report program termination to the - debug server. + Public method reimplemented to report program termination to the debug + server. @param frame the frame object @param retval the return value of the program @@ -797,11 +806,30 @@ @param frame the frame object @return flag indicating whether the debugger should stop here """ - if self.__skip_it(frame): + if self.__skipFrame(frame): return 0 return bdb.Bdb.stop_here(self, frame) - def __skip_it(self, frame): + def tracePythonLibs(self, enable): + """ + Public methode to update the settings to trace into Python libraries. + + @param enable let's debug into Python libraries (int) + """ + pathsToSkip = list(self.pathsToSkip) + # don't trace into Python library? + if enable: + pathsToSkip = [x for x in pathsToSkip if not x.endswith( + ("site-packages", "dist-packages", self.lib))] + else: + pathsToSkip.append(self.lib) + localLib = [x for x in sys.path if x.endswith(("site-packages", + "dist-packages")) and not x.startswith(self.lib)] + pathsToSkip.extend(localLib) + + self.pathsToSkip = tuple(pathsToSkip) + + def __skipFrame(self, frame): """ Private method to filter out debugger files. @@ -811,33 +839,15 @@ @param frame the frame object @return flag indicating whether the debugger should skip this frame """ - if frame is None: - return 1 - - fn = self.fix_frame_filename(frame) - - # Eliminate things like <string> and <stdin>. - if fn[0] == '<': - return 1 - - #XXX - think of a better way to do this. It's only a convenience for - #debugging the debugger - when the debugger code is in the current - #directory. - if os.path.basename(fn) in [ - 'AsyncFile.py', 'AsyncIO.py', - 'DebugConfig.py', 'DCTestResult.py', - 'DebugBase.py', 'DebugClientBase.py', - 'DebugClientCapabilities.py', 'DebugClient.py', - 'DebugClientThreads.py', 'DebugProtocol.py', - 'DebugThread.py', 'FlexCompleter.py', - 'PyProfile.py'] or \ - os.path.dirname(fn).endswith("coverage"): - return 1 - - if self._dbgClient.shouldSkip(fn): - return 1 - - return 0 + try: + return self.filesToSkip[frame.f_code.co_filename] + except KeyError: + ret = frame.f_code.co_filename.startswith(self.pathsToSkip) + self.filesToSkip[frame.f_code.co_filename] = ret + return ret + except AttributeError: + # if frame is None + return True def isBroken(self): """
--- a/DebugClients/Python/DebugClientBase.py Thu Jun 23 19:24:09 2016 +0200 +++ b/DebugClients/Python/DebugClientBase.py Sun Jun 26 16:32:01 2016 +0200 @@ -194,8 +194,8 @@ # dictionary of all threads running self.threads = {} - # the "current" thread, basically the thread we are at a - # breakpoint for. + # the "current" thread, basically the thread we are at a breakpoint + # for. self.currentThread = self # special objects representing the main scripts thread and frame @@ -295,9 +295,6 @@ If mainThread is non-zero, then we are attaching to the already started mainthread of the app and the rest of the args are ignored. - This is just an empty function and is overridden in the threaded - debugger. - @param target the start function of the target thread (i.e. the user code) @param args arguments to pass to target @@ -512,7 +509,6 @@ os.chdir(sys.path[1]) else: os.chdir(wd) - tracePython = int(tracePython) self.running = sys.argv[0] self.mainFrame = None self.inRawMode = 0 @@ -526,11 +522,11 @@ sys.excepthook = self.__unhandled_exception self.__interceptSignals() - # clear all old breakpoints, they'll get set after we - # have started + # clear all old breakpoints, they'll get set after we have + # started self.mainThread.clear_all_breaks() - self.mainThread.tracePython = tracePython + self.mainThread.tracePythonLibs(int(tracePython)) # This will eventually enter a local event loop. # Note the use of backquotes to cause a repr of self.running. @@ -573,7 +569,7 @@ sys.excepthook = self.__unhandled_exception self.__interceptSignals() - self.mainThread.tracePython = 0 + self.mainThread.tracePythonLibs(0) self.debugMod.__dict__['__file__'] = sys.argv[0] sys.modules['__main__'] = self.debugMod @@ -770,7 +766,8 @@ if cmd == DebugProtocol.RequestEval: try: value = eval( - arg, self.currentThread.getCurrentFrame().f_globals, + arg, + self.currentThread.getCurrentFrame().f_globals, self.currentThread.getFrameLocals(self.framenr)) self.currentThread.storeFrameLocals(self.framenr) except Exception: @@ -1337,8 +1334,8 @@ @param frmnr distance of frame reported on. 0 is the current frame (int) @param scope 1 to report global variables, 0 for local variables (int) - @param filter the indices of variable types to be filtered (list of - int) + @param filter the indices of variable types to be filtered + (list of int) """ if self.currentThread is None: return @@ -1489,7 +1486,7 @@ oaccess = '' try: exec 'mdict = dict%s.__dict__' % access - ndict.update(mdict) # __IGNORE_WARNING__ + ndict.update(mdict) exec 'obj = dict%s' % access if "PyQt4." in str(type(obj)) or \ "PyQt5." in str(type(obj)): @@ -1501,8 +1498,8 @@ pass try: exec 'mcdict = dict%s.__class__.__dict__' % access - ndict.update(mcdict) # __IGNORE_WARNING__ - if mdict and "sipThis" not in mdict.keys(): # __IGNORE_WARNING__ + ndict.update(mcdict) + if mdict and "sipThis" not in mdict.keys(): del rvar[0:2] access = "" except Exception: @@ -1628,9 +1625,9 @@ def __formatQtVariable(self, value, vtype): """ - Private method to produce a formated output of a simple Qt4/Qt5 type. + Private method to produce a formatted output of a simple Qt4/Qt5 type. - @param value variable to be formated + @param value variable to be formatted @param vtype type of the variable to be formatted (string) @return A tuple consisting of a list of formatted variables. Each variable entry is a tuple of three elements, the variable name, @@ -1977,7 +1974,7 @@ self.debugging = 1 self.attachThread(mainThread=1) - self.mainThread.tracePython = tracePython + self.mainThread.tracePythonLibs(tracePython) # set the system exception handling function to ensure, that # we report on all unhandled exceptions @@ -2035,7 +2032,7 @@ self.__interact() self.attachThread(mainThread=1) - self.mainThread.tracePython = tracePython + self.mainThread.tracePythonLibs(tracePython) # set the system exception handling function to ensure, that # we report on all unhandled exceptions
--- a/DebugClients/Python3/DebugBase.py Thu Jun 23 19:24:09 2016 +0200 +++ b/DebugClients/Python3/DebugBase.py Sun Jun 26 16:32:01 2016 +0200 @@ -49,6 +49,13 @@ Provides simple wrapper methods around bdb for the 'owning' client to call to step etc. """ + # Don't thrust distutils.sysconfig.get_python_lib: possible case mismatch + # on Windows + lib = os.path.dirname(bdb.__file__) + # tuple required because it's accessed a lot of times by startswith method + pathsToSkip = ('<', os.path.dirname(__file__), bdb.__file__[:-1]) + filesToSkip = {} + def __init__(self, dbgClient): """ Constructor @@ -61,6 +68,7 @@ self._mainThread = True self.breaks = self._dbgClient.breakpoints + self.tracePythonLibs(0) self.__event = "" self.__isBroken = "" @@ -198,7 +206,8 @@ @param toFrame destination frame (frame) """ if self._dbgClient.callTraceEnabled: - if not self.__skip_it(fromFrame) and not self.__skip_it(toFrame): + if (not self.__skipFrame(fromFrame) and + not self.__skipFrame(toFrame)): if event in ["call", "return"]: fr = fromFrame fromStr = "{0}:{1}:{2}".format( @@ -297,7 +306,7 @@ @return local trace function @exception bdb.BdbQuit raised to indicate the end of the debug session """ - if not self.__skip_it(frame): + if not self.__skipFrame(frame): # When stepping with next/until/return in a generator frame, # skip the internal StopIteration exception (with no traceback) # triggered by a subiterator run with the 'yield from' @@ -845,12 +854,31 @@ @param frame the frame object @return flag indicating whether the debugger should stop here """ - if self.__skip_it(frame): + if self.__skipFrame(frame): return False return bdb.Bdb.stop_here(self, frame) - def __skip_it(self, frame): + def tracePythonLibs(self, enable): + """ + Public methode to update the settings to trace into Python libraries. + + @param enable let's debug into Python libraries (int) + """ + pathsToSkip = list(self.pathsToSkip) + # don't trace into Python library? + if enable: + pathsToSkip = [x for x in pathsToSkip if not x.endswith( + ("site-packages", "dist-packages", self.lib))] + else: + pathsToSkip.append(self.lib) + localLib = [x for x in sys.path if x.endswith(("site-packages", + "dist-packages")) and not x.startswith(self.lib)] + pathsToSkip.extend(localLib) + + self.pathsToSkip = tuple(pathsToSkip) + + def __skipFrame(self, frame): """ Private method to filter out debugger files. @@ -860,33 +888,15 @@ @param frame the frame object @return flag indicating whether the debugger should skip this frame """ - if frame is None: - return True - - fn = self.fix_frame_filename(frame) - - # Eliminate things like <string> and <stdin>. - if fn[0] == '<': + try: + return self.filesToSkip[frame.f_code.co_filename] + except KeyError: + ret = frame.f_code.co_filename.startswith(self.pathsToSkip) + self.filesToSkip[frame.f_code.co_filename] = ret + return ret + except AttributeError: + # if frame is None return True - - #XXX - think of a better way to do this. It's only a convenience for - #debugging the debugger - when the debugger code is in the current - #directory. - if os.path.basename(fn) in [ - 'AsyncFile.py', 'AsyncIO.py', - 'DebugConfig.py', 'DCTestResult.py', - 'DebugBase.py', 'DebugClientBase.py', - 'DebugClientCapabilities.py', 'DebugClient.py', - 'DebugClientThreads.py', 'DebugProtocol.py', - 'DebugThread.py', 'FlexCompleter.py', - 'PyProfile.py'] or \ - os.path.dirname(fn).endswith("coverage"): - return True - - if self._dbgClient.shouldSkip(fn): - return True - - return False def isBroken(self): """
--- a/DebugClients/Python3/DebugClientBase.py Thu Jun 23 19:24:09 2016 +0200 +++ b/DebugClients/Python3/DebugClientBase.py Sun Jun 26 16:32:01 2016 +0200 @@ -267,8 +267,8 @@ If mainThread is non-zero, then we are attaching to the already started mainthread of the app and the rest of the args are ignored. - @param target the start function of the target thread (i.e. the - user code) + @param target the start function of the target thread (i.e. the user + code) @param args arguments to pass to target @param kwargs keyword arguments to pass to target @param mainThread True, if we are attaching to the already @@ -502,7 +502,6 @@ os.chdir(sys.path[1]) else: os.chdir(wd) - tracePython = int(tracePython) self.running = sys.argv[0] self.mainFrame = None self.inRawMode = False @@ -520,7 +519,7 @@ # started self.mainThread.clear_all_breaks() - self.mainThread.tracePython = tracePython + self.mainThread.tracePythonLibs(int(tracePython)) # This will eventually enter a local event loop. # Note the use of backquotes to cause a repr of self.running. @@ -562,7 +561,7 @@ sys.excepthook = self.__unhandled_exception self.__interceptSignals() - self.mainThread.tracePython = False + self.mainThread.tracePythonLibs(0) self.debugMod.__dict__['__file__'] = sys.argv[0] sys.modules['__main__'] = self.debugMod @@ -2028,7 +2027,7 @@ self.debugging = True self.attachThread(mainThread=True) - self.mainThread.tracePython = tracePython + self.mainThread.tracePythonLibs(tracePython) # set the system exception handling function to ensure, that # we report on all unhandled exceptions @@ -2086,7 +2085,7 @@ self.__interact() self.attachThread(mainThread=True) - self.mainThread.tracePython = tracePython + self.mainThread.tracePythonLibs(tracePython) # set the system exception handling function to ensure, that # we report on all unhandled exceptions