# -*- coding: utf-8 -*-

# Copyright (c) 2002 - 2005 Detlev Offenbach <detlev@die-offenbachs.de>
# Copyright (c) 2000 Phil Thompson <phil@river-bank.demon.co.uk>
#

"""
Module implementing the debug thread.
"""

import bdb
import types
import os
import traceback
import sys

from DebugBase import *

class DebugThread(DebugBase):
    """
    Class implementing a debug thread.

    It represents a thread in the python interpreter that we are tracing.
    
    Provides simple wrapper methods around bdb for the 'owning' client to
    call to step etc.
    """
    def __init__(self, dbgClient, targ=None, args=None, kwargs=None, mainThread=0):
        """
        Constructor
        
        @param dbgClient the owning client
        @param targ the target method in the run thread
        @param args  arguments to be passed to the thread
        @param kwargs arguments to be passed to the thread
        @param mainThread 0 if this thread is not the mainscripts thread
        """
        DebugBase.__init__(self, dbgClient)
        
        self._target = targ 
        self._args = args
        self._kwargs = kwargs
        self._mainThread = mainThread
        # thread running tracks execution state of client code
        # it will always be 0 for main thread as that is tracked
        # by DebugClientThreads and Bdb...
        self._threadRunning = 0 
        
        self.ident = None # id of this thread.
        
    def set_ident(self, id):
        """
        Public method to set the id for this thread.
        
        @param id id for this thread (int)
        """
        self.ident = id
      
    def get_ident(self):
        """
        Public method to return the id for this thread.
        
        @return the id of this thread (int)
        """
        return self.ident
    
    def traceThread(self):
        """
        Private method to setup tracing for this thread.
        """
        self.set_trace()
        if not self._mainThread:
            self.set_continue(0)
    
    def bootstrap(self):
        """
        Private method to bootstrap the thread.
        
        It wraps the call to the user function to enable tracing 
        before hand.
        """
        try:
            try:
                self._threadRunning = 1
                self.traceThread()
                apply(self._target, self._args, self._kwargs)
            except bdb.BdbQuit:
                pass
        finally:
            self._threadRunning = 0
            self.quitting = 1
            self._dbgClient.threadTerminated(self)
            sys.settrace(None)
            
    def trace_dispatch(self, frame, event, arg):
        """
        Private method wraping the trace_dispatch of bdb.py.
        
        It wraps the call to dispatch tracing into
        bdb to make sure we have locked the client to prevent multiple
        threads from entering the client event loop.
        """
        try:
            self._dbgClient.lockClient()
            # if this thread came out of a lock, and we are quitting 
            # and we are still running, then get rid of tracing for this thread 
            # and raise a quit to abort the thread. 
            if self.quitting and self._threadRunning:
                sys.settrace(None)
                raise bdb.BdbQuit
            retval = DebugBase.trace_dispatch(self, frame, event, arg)
        finally:
            self._dbgClient.unlockClient()
            
        return retval

