Thu, 06 Oct 2016 22:28:12 +0200
Relocated some methods from Debug*Thread and the remaining modules into a new one.
# -*- coding: utf-8 -*- # Copyright (c) 2014 - 2016 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing an import hook patching thread modules to get debugged too. """ import os.path import sys if sys.version_info[0] == 2: import thread as _thread else: import _thread import threading from DebugBase import DebugBase class ThreadExtension(object): """ Class implementing the thread support for the debugger. Provides methods for intercepting thread creation, retriving the running threads and their name and state. """ def __init__(self): """ Constructor """ self.threadNumber = 1 self._original_start_new_thread = None self.clientLock = threading.RLock() # dictionary of all threads running {id: DebugBase} self.threads = {} # the "current" thread, basically for variables view self.currentThread = self # special objects representing the main scripts thread and frame self.mainThread = self if sys.version_info[0] == 2: self.threadModName = 'thread' else: self.threadModName = '_thread' # reset already imported thread module to apply hooks at next import del sys.modules[self.threadModName] del sys.modules['threading'] # provide a hook to perform a hard breakpoint # Use it like this: # if hasattr(sys, 'breakpoint): sys.breakpoint() sys.breakpoint = self.set_trace def attachThread(self, target=None, args=None, kwargs=None, mainThread=False): """ Public method to setup a thread for DebugClient to debug. If mainThread is True, 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 args arguments to pass to target @param kwargs keyword arguments to pass to target @param mainThread True, if we are attaching to the already started mainthread of the app @return identifier of the created thread """ return try: self.lockClient() newThread = DebugThread(self, target, args, kwargs, mainThread) ident = -1 if mainThread: ident = _thread.get_ident() self.mainThread = newThread if self.debugging: sys.setprofile(newThread.profile) else: ident = _original_start_thread(newThread.bootstrap, ()) if self.mainThread is not None: self.tracePython = self.mainThread.tracePython newThread.set_ident(ident) self.threads[newThread.get_ident()] = newThread finally: self.unlockClient() return ident def threadTerminated(self, dbgThread): """ Public method called when a DebugThread has exited. @param dbgThread the DebugThread that has exited """ self.lockClient() try: del self.threads[dbgThread.get_ident()] except KeyError: pass finally: self.unlockClient() def lockClient(self, blocking=True): """ Public method to acquire the lock for this client. @param blocking flag to indicating a blocking lock @type bool @return flag indicating successful locking @rtype bool """ if blocking: self.clientLock.acquire() else: return self.clientLock.acquire(blocking) def unlockClient(self): """ Public method to release the lock for this client. """ try: self.clientLock.release() except AssertionError: pass def setCurrentThread(self, id): """ Public method to set the current thread. @param id the id the current thread should be set to. """ try: self.lockClient() if id is None or id not in self.threads: self.currentThread = None else: self.currentThread = self.threads[id] finally: self.unlockClient() def dumpThreadList(self): """ Public method to send the list of threads. """ threadList = [] if self.threads and self.currentThread: # indication for the threaded debugger currentId = self.currentThread.get_ident() for t in self.threads.values(): d = {} d["id"] = t.get_ident() d["name"] = t.get_name() d["broken"] = t.isBroken threadList.append(d) else: currentId = -1 d = {} d["id"] = -1 d["name"] = "MainThread" if hasattr(self, "isBroken"): d["broken"] = self.isBroken else: d["broken"] = False threadList.append(d) self.sendJsonCommand("ResponseThreadList", { "currentID": currentId, "threadList": threadList, }) # # eflag: noqa = M702