[Python-checkins] r47266 - sandbox/trunk/pdb/README.txt sandbox/trunk/pdb/mpdb.py sandbox/trunk/pdb/mthread.py

matt.fleming python-checkins at python.org
Thu Jul 6 12:11:03 CEST 2006


Author: matt.fleming
Date: Thu Jul  6 12:11:02 2006
New Revision: 47266

Modified:
   sandbox/trunk/pdb/README.txt
   sandbox/trunk/pdb/mpdb.py
   sandbox/trunk/pdb/mthread.py
Log:
Some changes to the thread code.


Modified: sandbox/trunk/pdb/README.txt
==============================================================================
--- sandbox/trunk/pdb/README.txt	(original)
+++ sandbox/trunk/pdb/README.txt	Thu Jul  6 12:11:02 2006
@@ -53,4 +53,5 @@
 * Run command does not pass arguments across a remote connection, i.e.
       `run 2' does not pass the argument 2 to the script.
 * Allow thread debugging to be turned on with a command line switch.
-
+* Allow reading commands from .mpdbrc file 
+* Changed the name of the default history file from ~/.pydbhist to ~/.mpdbhist
\ No newline at end of file

Modified: sandbox/trunk/pdb/mpdb.py
==============================================================================
--- sandbox/trunk/pdb/mpdb.py	(original)
+++ sandbox/trunk/pdb/mpdb.py	Thu Jul  6 12:11:02 2006
@@ -164,6 +164,9 @@
         else:
             pydb.Pdb.info_helper(self, cmd)
 
+    def help_mpdb(self, *arg):
+        help()
+
     # Debugger commands
     def do_attach(self, addr):
         """ Attach to a process or file outside of Pdb.

Modified: sandbox/trunk/pdb/mthread.py
==============================================================================
--- sandbox/trunk/pdb/mthread.py	(original)
+++ sandbox/trunk/pdb/mthread.py	Thu Jul  6 12:11:02 2006
@@ -7,249 +7,38 @@
 import sys
 import threading
 
-class MTracer(object):
+from pydb.gdb import Gdb
+
+# Global variables
+tracers = []       # The MTracer objects that are tracing threads
+threads = []       # List of threads we're tracing
+
+# This global variable keeps track of the 'main' debugger which, when one
+# of the MTracer objects encounters a breakpoint in its thread, is used to
+# place a call to that main debugger asking it to stop and examine the frame
+# said thread stopped at.
+_main_debugger = None
+
+class MTracer(Gdb):
     """ A class to trace a thread. Breakpoints can be passed from
     a main debugger to this debugger through the constructor
     which is useful, for instance, if a breakpoint occurs inside
     a thread's run() method.
     """
-    def __init__(self, msg, errmsg, breaks={}, filename=None):
+    def __init__(self):
+        Gdb.__init__(self)
         self.thread = threading.currentThread()
-        self.msg = msg
-        self.errmsg = errmsg
         # Each tracer instance must keep track of its own breakpoints
-        self.breaks = breaks
         self.fncache = {}
-        self.filename = filename
         self.lineno = 0
         self.curframe = None
+        self.stopframe = None
+        self.botframe = None
+        self.quitting = False
 
-    def canonic(self, filename):
-        if filename == "<" + filename[1:-1] + ">":
-            return filename
-        canonic = self.fncache.get(filename)
-        if not canonic:
-            canonic = os.path.abspath(filename)
-            canonic = os.path.normcase(canonic)
-            self.fncache[filename] = canonic
-        return canonic
-
-    def info(self, arg):
-        args = arg.split()
-        if 'break'.startswith(args[0]):
-            return self.breaks
-    
-    def get_break(self, filename, lineno):
-        """ Return the breakpoint at [filename:]lineno. """
-        filename = self.canonic(filename)
-        return filename in self.breaks and \
-               lineno in self.breaks[filename]
-
-    def get_breaks(self, filename, lineno):
-        """ Return all the breakpoints set in this thread. """
-        filename = self.canonic(filename)
-        return filename in self.breaks and \
-               lineno in self.breaks[filename] and \
-               Breakpoint.bplist[filename, lineno] or []
-        
-    def set_break(self, filename, lineno, temporary=0, cond = None,
-                  funcname=None):
-        """ Set a breakpoint in this thread. """
-
-        # Derived classes and clients can call the following methods
-        # to manipulate breakpoints.  These methods return an
-        # error message is something went wrong, None if all is well.
-        # Set_break prints out the breakpoint line and file:lineno.
-        # Call self.get_*break*() to see the breakpoints or better
-        # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint().
-
-        filename = self.canonic(filename)
-        import linecache # Import as late as possible
-        line = linecache.getline(filename, lineno)
-        if not line:
-            return 'Line %s:%d does not exist' % (filename,
-                                   lineno)
-        if not filename in self.breaks:
-            self.breaks[filename] = []
-        list = self.breaks[filename]
-        if not lineno in list:
-            list.append(lineno)
-        bp = Breakpoint(filename, lineno, temporary, cond, funcname)
-
-    def do_break(self, arg=None):
-        """ thread-specific breakpoint information. """
-        # XXX For now we don't support temporary breakpoints in threads
-        temporary = cond = False
-        if arg is None:
-            if self.lineno is None:
-                lineno = max(1, inspect.lineno(self.curframe))
-            else:
-                lineno = self.lineno + 1
-            filename = self.curframe.f_code.co_filename
-        else:
-            filename = None
-            lineno = None
-            comma = arg.find(',')
-            if comma > 0:
-                # parse stuff after comma: "condition"
-                cond = arg[comma+1:].lstrip()
-                arg = arg[:comma].rstrip()
-            (funcname, filename, lineno) = self.__parse_filepos(arg)
-            if lineno is None: return
-            
-        # FIXME This default setting doesn't match that used in
-        # do_clear. Perhaps one is non-optimial.
-        if not filename:
-            filename = self.defaultFile()
-
-        # Check for reasonable breakpoint
-        line = self.checkline(filename, lineno)
-        if line:
-            # now set the break point
-            # Python 2.3.5 takes 5 args rather than 6.
-            # There is another way in configure to test for the version,
-            # but this works too.
-            try:
-               err = self.set_break(filename, line, temporary, cond, funcname)
-            except TypeError:
-               err = self.set_break(filename, line, temporary, cond)
-
-            if err: self.msg, err
-            else:
-                bp = self.get_breaks(filename, line)[-1]
-                self.msg, "Breakpoint %d set in file %s, line %d." \
-                         % (bp.number, self.filename(bp.file), bp.line)
-
-    def __parse_filepos(self, arg):
-        """__parse_filepos(self,arg)->(fn, filename, lineno)
-        
-        Parse arg as [filename:]lineno | function
-        Make sure it works for C:\foo\bar.py:12
-        """
-        colon = arg.rfind(':')
-        if colon >= 0:
-            filename = arg[:colon].rstrip()
-            f = self.lookupmodule(filename)
-            if not f:
-                self.msg("%s not found from sys.path" %
-                         self._saferepr(filename))
-                return (None, None, None)
-            else:
-                filename = f
-                arg = arg[colon+1:].lstrip()
-            try:
-                lineno = int(arg)
-            except ValueError, msg:
-                self.msg('Bad lineno: %s' % str(arg))
-                return (None, filename, None)
-            return (None, filename, lineno)
-        else:
-            # no colon; can be lineno or function
-            return self.__get_brkpt_lineno(arg)
-
-    def __get_brkpt_lineno(self, arg):
-        """__get_brkpt_lineno(self,arg)->(filename, file, lineno)
-
-        See if arg is a line number or a function name.  Return what
-        we've found. None can be returned as a value in the triple."""
-        funcname, filename = (None, None)
-        try:
-            # First try as an integer
-            lineno = int(arg)
-            filename = self.curframe.f_code.co_filename
-        except ValueError:
-            try:
-                func = eval(arg, self.curframe.f_globals,
-                            self.curframe.f_locals)
-            except:
-                func = arg
-            try:
-                if hasattr(func, 'im_func'):
-                    func = func.im_func
-                code = func.func_code
-                #use co_name to identify the bkpt (function names
-                #could be aliased, but co_name is invariant)
-                funcname = code.co_name
-                lineno = code.co_firstlineno
-                filename = code.co_filename
-            except:
-                # last thing to try
-                (ok, filename, ln) = self.lineinfo(arg)
-                if not ok:
-                    self.msg('The specified object %s is not ' \
-                             ' a function, or not found' \
-                             ' along sys.path or no line given.' %
-                    str(repr(arg)))
-
-                    return (None, None, None)
-                funcname = ok # ok contains a function name
-                lineno = int(ln)
-        return (funcname, filename, lineno)
-
-    def lineinfo(self, identifier):
-        failed = (None, None, None)
-        # Input is identifier, may be in single quotes
-        idstring = identifier.split("'")
-        if len(idstring) == 1:
-            # not in single quotes
-            id = idstring[0].strip()
-        elif len(idstring) == 3:
-            # quoted
-            id = idstring[1].strip()
-        else:
-            return failed
-        if id == '': return failed
-        parts = id.split('.')
-        # Protection for derived debuggers
-        if parts[0] == 'self':
-            del parts[0]
-            if len(parts) == 0:
-                return failed
-        # Best first guess at file to look at
-        fname = self.defaultFile()
-        if len(parts) == 1:
-            item = parts[0]
-        else:
-            # More than one part.
-            # First is module, second is method/class
-            f = self.lookupmodule(parts[0])
-            if f:
-                fname = f
-            item = parts[1]
-        answer = find_function(item, fname)
-        return answer or failed
-
-    def defaultFile(self):
-        """Produce a reasonable default."""
-        filename = self.curframe.f_code.co_filename
-        # Consider using is_exec_stmt(). I just don't understand
-        # the conditions under which the below test is true.
-        if filename == '<string>' and self.mainpyfile:
-            filename = self.mainpyfile
-        return filename
-
-    def checkline(self, filename, lineno):
-        """Check whether specified line seems to be executable.
-
-        Return `lineno` if it is, 0 if not (e.g. a docstring, comment, blank
-        line or EOF). Warning: testing is not comprehensive.
-        """
-        line = linecache.getline(filename, lineno)
-        if not line:
-            self.errmsg('End of file')
-            return 0
-        line = line.strip()
-        # Don't allow setting breakpoint at a blank line
-        if (not line or (line[0] == '#') or
-             (line[:3] == '"""') or line[:3] == "'''"):
-            self.errmsg('Blank or comment')
-            return 0
-        return lineno
-    
     def trace_dispatch(self, frame, event, arg):
-        self.curframe = frame
+        self.currentframe = frame
         if event == 'line':
-            self.msg('%s *** line' % self.thread.getName())
             return self.dispatch_line
         if event == 'call':
             self.msg('%s *** call' % self.thread.getName())
@@ -272,53 +61,66 @@
         print 'bdb.Bdb.dispatch: unknown debugging event:', repr(event)
         return self.trace_dispatch
 
-    def dispatch_line(self, frame, event, arg):
-        print frame.f_code.co_filename, self.thread.getName()
+    def dispatch_line(self, frame, arg):
+        if self.stop_here(frame) or self.break_here(frame):
+            # Signal to the main debugger that we've hit a breakpoint
+            print 'bang'
+            _main_debugger.user_line(frame)
+            if self.quitting: raise BdbQuit
+        return self.trace_dispatch
+
+    def dispatch_call(self, frame, arg):
+        # XXX 'arg' is no longer used
+        if self.botframe is None:
+            # First call of dispatch since reset()
+            self.botframe = frame.f_back # (CT) Note that this may also be None!
+            return self.trace_dispatch
+        if not (self.stop_here(frame) or self.break_anywhere(frame)):
+            # No need to trace this function
+            return # None
+        _main_debugger.user_call(frame, arg)
+        if self.quitting: raise BdbQuit
+        return self.trace_dispatch
+
+    def dispatch_return(self, frame, arg):
+        if self.stop_here(frame) or frame == self.returnframe:
+            _main_debugger.user_return(frame, arg)
+            if self.quitting: raise BdbQuit
         return self.trace_dispatch
 
-class ThreadDebug(object):
-    def __init__(self, msg, errmsg):
-        self.msg = msg
-        self.errmsg = errmsg
-        self.threads = []
-        self.tracers = []
-        self.current_thread = None
-
-    def trace_dispatch_init(self, frame, event, arg):
-        t = threading.currentThread()
-        self.threads.append(t)
-        m = MTracer(self.msg, self.errmsg)
-        self.tracers.append(m)
-
-        sys.settrace(m.trace_dispatch)
-
-    def get_current_thread(self):
-        self.msg('Current thread is %d (%s)' % \
-                 (self.threads.index(self.current_thread)+1,
-                  self.current_thread))
-        return
-
-    def set_current_thread(self, args):
-        # XXX Switch to a different thread, although this doesn't
-        # actually do anything yet.
-        t_num = int(args)-1
-        if t_num > len(self.threads):
-            self.errmsg('Thread ID %d not known.' % t_num+1)
-            return
-        self.current_thread = self.threads[t_num]
-        self.msg('Switching to thread %d (%s)' % (t_num+1, \
-                                                  self.current_thread))
-        return
-
-def init(msg, errmsg):
-    """ This method sets up thread debugging by creating a ThreadDebug
-    object that creates MTracer objects for every thread that is created.
-    'msg' is a method to write standard output to, and 'errmsg' is a method
-    to write error output to.
+    def dispatch_exception(self, frame, arg):
+        if self.stop_here(frame):
+            _main_debugger.user_exception(frame, arg)
+            if self.quitting: raise BdbQuit
+        return self.trace_dispatch
+
+def trace_dispatch_init(frame, event, arg):
+    """ This method is called by a sys.settrace when a thread is running
+    for the first time. Setup this thread with a tracer object and
+    set this thread's tracing function to that object's trace_dispatch
+    method.
     """
-    t = ThreadDebug(msg, errmsg)
-    threading.settrace(t.trace_dispatch_init)
-    #sys.settrace(t.trace_dispatch_init)
+    global threads, tracers
+    threads.append(threading.currentThread())
+    t = MTracer()
+
+    tracers.append(t)
+    threading.settrace(t.trace_dispatch)
+    sys.settrace(t.trace_dispatch)
+    
+
+def init(debugger):
+    """ This method intialises thread debugging. It sets up a tracing
+    method for newly created threads so that they call trace_dispatch_init,
+    which hooks them up with a MTracer object. The argument 'debugger' is
+    the debugger that is debugging the MainThread, i.e. the Python
+    interpreter.
+    """
+    global _main_debugger
+    _main_debugger = debugger
+    threading.settrace(trace_dispatch_init)
+    
+
 
 
 


More information about the Python-checkins mailing list