[Python-checkins] r83275 - in python/branches/py3k: Doc/library/bdb.rst Lib/bdb.py Lib/pdb.py Lib/test/test_pdb.py Misc/NEWS

georg.brandl python-checkins at python.org
Fri Jul 30 14:01:20 CEST 2010


Author: georg.brandl
Date: Fri Jul 30 14:01:20 2010
New Revision: 83275

Log:
#809887: improve pdb feedback for breakpoint-related actions.  Also add a functional test for these commands.

Modified:
   python/branches/py3k/Doc/library/bdb.rst
   python/branches/py3k/Lib/bdb.py
   python/branches/py3k/Lib/pdb.py
   python/branches/py3k/Lib/test/test_pdb.py
   python/branches/py3k/Misc/NEWS

Modified: python/branches/py3k/Doc/library/bdb.rst
==============================================================================
--- python/branches/py3k/Doc/library/bdb.rst	(original)
+++ python/branches/py3k/Doc/library/bdb.rst	Fri Jul 30 14:01:20 2010
@@ -267,6 +267,15 @@
 
       Delete all existing breakpoints.
 
+   .. method:: get_bpbynumber(arg)
+
+      Return a breakpoint specified by the given number.  If *arg* is a string,
+      it will be converted to a number.  If *arg* is a non-numeric string, if
+      the given breakpoint never existed or has been deleted, a
+      :exc:`ValueError` is raised.
+
+      .. versionadded:: 3.2
+
    .. method:: get_break(filename, lineno)
 
       Check if there is a breakpoint for *lineno* of *filename*.

Modified: python/branches/py3k/Lib/bdb.py
==============================================================================
--- python/branches/py3k/Lib/bdb.py	(original)
+++ python/branches/py3k/Lib/bdb.py	Fri Jul 30 14:01:20 2010
@@ -270,15 +270,9 @@
 
     def clear_bpbynumber(self, arg):
         try:
-            number = int(arg)
-        except:
-            return 'Non-numeric breakpoint number (%s)' % arg
-        try:
-            bp = Breakpoint.bpbynumber[number]
-        except IndexError:
-            return 'Breakpoint number (%d) out of range' % number
-        if not bp:
-            return 'Breakpoint (%d) already deleted' % number
+            bp = self.get_bpbynumber(arg)
+        except ValueError as err:
+            return str(err)
         self.clear_break(bp.file, bp.line)
 
     def clear_all_file_breaks(self, filename):
@@ -299,6 +293,21 @@
                 bp.deleteMe()
         self.breaks = {}
 
+    def get_bpbynumber(self, arg):
+        if not arg:
+            raise ValueError('Breakpoint number expected')
+        try:
+            number = int(arg)
+        except ValueError:
+            raise ValueError('Non-numeric breakpoint number %s' % arg)
+        try:
+            bp = Breakpoint.bpbynumber[number]
+        except IndexError:
+            raise ValueError('Breakpoint number %d out of range' % number)
+        if bp is None:
+            raise ValueError('Breakpoint %d already deleted' % number)
+        return bp
+
     def get_break(self, filename, lineno):
         filename = self.canonic(filename)
         return filename in self.breaks and \
@@ -510,6 +519,9 @@
             print(('\tbreakpoint already hit %d time%s' %
                           (self.hits, ss)), file=out)
 
+    def __str__(self):
+        return 'breakpoint %s at %s:%s' % (self.number, self.file, self.line)
+
 # -----------end of Breakpoint class----------
 
 def checkfuncname(b, frame):

Modified: python/branches/py3k/Lib/pdb.py
==============================================================================
--- python/branches/py3k/Lib/pdb.py	(original)
+++ python/branches/py3k/Lib/pdb.py	Fri Jul 30 14:01:20 2010
@@ -552,12 +552,12 @@
         Those commands will be executed whenever the breakpoint causes
         the program to stop execution."""
         if not arg:
-            bnum = len(bdb.Breakpoint.bpbynumber)-1
+            bnum = len(bdb.Breakpoint.bpbynumber) - 1
         else:
             try:
                 bnum = int(arg)
             except:
-                print("Usage : commands [bnum]\n        ...\n        end",
+                print("Usage: commands [bnum]\n        ...\n        end",
                       file=self.stdout)
                 return
         self.commands_bnum = bnum
@@ -634,10 +634,9 @@
                     # last thing to try
                     (ok, filename, ln) = self.lineinfo(arg)
                     if not ok:
-                        print('*** The specified object', end=' ', file=self.stdout)
-                        print(repr(arg), end=' ', file=self.stdout)
-                        print('is not a function', file=self.stdout)
-                        print('or was not found along sys.path.', file=self.stdout)
+                        print('*** The specified object %r is not a function '
+                              'or was not found along sys.path.' % arg,
+                              file=self.stdout)
                         return
                     funcname = ok # ok contains a function name
                     lineno = int(ln)
@@ -726,81 +725,56 @@
         args = arg.split()
         for i in args:
             try:
-                i = int(i)
-            except ValueError:
-                print('Breakpoint index %r is not a number' % i, file=self.stdout)
-                continue
-
-            if not (0 <= i < len(bdb.Breakpoint.bpbynumber)):
-                print('No breakpoint numbered', i, file=self.stdout)
-                continue
-
-            bp = bdb.Breakpoint.bpbynumber[i]
-            if bp:
+                bp = self.get_bpbynumber(i)
+            except ValueError as err:
+                print('***', err, file=self.stdout)
+            else:
                 bp.enable()
+                print('Enabled %s' % bp, file=self.stdout)
 
     def do_disable(self, arg):
         args = arg.split()
         for i in args:
             try:
-                i = int(i)
-            except ValueError:
-                print('Breakpoint index %r is not a number' % i, file=self.stdout)
-                continue
-
-            if not (0 <= i < len(bdb.Breakpoint.bpbynumber)):
-                print('No breakpoint numbered', i, file=self.stdout)
-                continue
-
-            bp = bdb.Breakpoint.bpbynumber[i]
-            if bp:
+                bp = self.get_bpbynumber(i)
+            except ValueError as err:
+                print('***', err, file=self.stdout)
+            else:
                 bp.disable()
+                print('Disabled %s' % bp, file=self.stdout)
 
     def do_condition(self, arg):
         # arg is breakpoint number and condition
         args = arg.split(' ', 1)
         try:
-            bpnum = int(args[0].strip())
-        except ValueError:
-            # something went wrong
-            print('Breakpoint index %r is not a number' % args[0], file=self.stdout)
-            return
-        try:
             cond = args[1]
-        except:
+        except IndexError:
             cond = None
         try:
-            bp = bdb.Breakpoint.bpbynumber[bpnum]
-        except IndexError:
-            print('Breakpoint index %r is not valid' % args[0],
-                  file=self.stdout)
-            return
-        if bp:
+            bp = self.get_bpbynumber(args[0].strip())
+        except ValueError as err:
+            print('***', err, file=self.stdout)
+        else:
             bp.cond = cond
             if not cond:
-                print('Breakpoint', bpnum, end=' ', file=self.stdout)
-                print('is now unconditional.', file=self.stdout)
+                print('Breakpoint %d is now unconditional.' % bp.number,
+                      file=self.stdout)
+            else:
+                print('New condition set for breakpoint %d.' % bp.number,
+                      file=self.stdout)
 
-    def do_ignore(self,arg):
+    def do_ignore(self, arg):
         """arg is bp number followed by ignore count."""
         args = arg.split()
         try:
-            bpnum = int(args[0].strip())
-        except ValueError:
-            # something went wrong
-            print('Breakpoint index %r is not a number' % args[0], file=self.stdout)
-            return
-        try:
             count = int(args[1].strip())
         except:
             count = 0
         try:
-            bp = bdb.Breakpoint.bpbynumber[bpnum]
-        except IndexError:
-            print('Breakpoint index %r is not valid' % args[0],
-                  file=self.stdout)
-            return
-        if bp:
+            bp = self.get_bpbynumber(args[0].strip())
+        except ValueError as err:
+            print('***', err, file=self.stdout)
+        else:
             bp.ignore = count
             if count > 0:
                 reply = 'Will ignore next '
@@ -808,10 +782,10 @@
                     reply = reply + '%d crossings' % count
                 else:
                     reply = reply + '1 crossing'
-                print(reply + ' of breakpoint %d.' % bpnum, file=self.stdout)
+                print(reply + ' of breakpoint %d.' % bp.number, file=self.stdout)
             else:
-                print('Will stop next time breakpoint', end=' ', file=self.stdout)
-                print(bpnum, 'is reached.', file=self.stdout)
+                print('Will stop next time breakpoint %d is reached.'
+                      % bp.number, file=self.stdout)
 
     def do_clear(self, arg):
         """Three possibilities, tried in this order:
@@ -825,7 +799,10 @@
                 reply = 'no'
             reply = reply.strip().lower()
             if reply in ('y', 'yes'):
+                bplist = [bp for bp in bdb.Breakpoint.bpbynumber if bp]
                 self.clear_all_breaks()
+                for bp in bplist:
+                    print('Deleted %s' % bp, file=self.stdout)
             return
         if ':' in arg:
             # Make sure it works for "clear C:\foo\bar.py:12"
@@ -837,25 +814,23 @@
             except ValueError:
                 err = "Invalid line number (%s)" % arg
             else:
+                bplist = self.get_breaks(filename, lineno)
                 err = self.clear_break(filename, lineno)
-            if err: print('***', err, file=self.stdout)
+            if err:
+                print('***', err, file=self.stdout)
+            else:
+                for bp in bplist:
+                    print('Deleted %s' % bp, file=self.stdout)
             return
         numberlist = arg.split()
         for i in numberlist:
             try:
-                i = int(i)
-            except ValueError:
-                print('Breakpoint index %r is not a number' % i, file=self.stdout)
-                continue
-
-            if not (0 <= i < len(bdb.Breakpoint.bpbynumber)):
-                print('No breakpoint numbered', i, file=self.stdout)
-                continue
-            err = self.clear_bpbynumber(i)
-            if err:
+                bp = self.get_bpbynumber(i)
+            except ValueError as err:
                 print('***', err, file=self.stdout)
             else:
-                print('Deleted breakpoint', i, file=self.stdout)
+                self.clear_break(bp.file, bp.line)
+                print('Deleted %s' % bp, file=self.stdout)
     do_cl = do_clear # 'c' is already an abbreviation for 'continue'
 
     def do_where(self, arg):

Modified: python/branches/py3k/Lib/test/test_pdb.py
==============================================================================
--- python/branches/py3k/Lib/test/test_pdb.py	(original)
+++ python/branches/py3k/Lib/test/test_pdb.py	Fri Jul 30 14:01:20 2010
@@ -134,7 +134,7 @@
     ...     print(3)
     ...     print(4)
 
-    >>> with PdbTestInput([
+    >>> with PdbTestInput([  # doctest: +ELLIPSIS
     ...     'next',
     ...     'break 7',
     ...     'continue',
@@ -149,7 +149,7 @@
     > <doctest test.test_pdb.test_pdb_continue_in_bottomframe[0]>(5)test_function()
     -> print(1)
     (Pdb) break 7
-    Breakpoint 1 at <doctest test.test_pdb.test_pdb_continue_in_bottomframe[0]>:7
+    Breakpoint ... at <doctest test.test_pdb.test_pdb_continue_in_bottomframe[0]>:7
     (Pdb) continue
     1
     2
@@ -164,6 +164,84 @@
     """
 
 
+def test_pdb_breakpoints():
+    """Test handling of breakpoints.
+
+    >>> def test_function():
+    ...     import pdb; pdb.Pdb().set_trace()
+    ...     print(1)
+    ...     print(2)
+    ...     print(3)
+    ...     print(4)
+
+    First, need to clear bdb state that might be left over from previous tests.
+    Otherwise, the new breakpoints might get assigned different numbers.
+
+    >>> from bdb import Breakpoint
+    >>> Breakpoint.next = 1
+    >>> Breakpoint.bplist = {}
+    >>> Breakpoint.bpbynumber = [None]
+
+    Now test the breakpoint commands.  NORMALIZE_WHITESPACE is needed because
+    the breakpoint list outputs a tab for the "stop only" and "ignore next"
+    lines, which we don't want to put in here.
+
+    >>> with PdbTestInput([  # doctest: +NORMALIZE_WHITESPACE
+    ...     'break 3',
+    ...     'disable 1',
+    ...     'ignore 1 10',
+    ...     'condition 1 1 < 2',
+    ...     'break 4',
+    ...     'break',
+    ...     'condition 1',
+    ...     'enable 1',
+    ...     'clear 1',
+    ...     'commands 2',
+    ...     'print 42',
+    ...     'end',
+    ...     'continue',  # will stop at breakpoint 2
+    ...     'continue',
+    ... ]):
+    ...    test_function()
+    > <doctest test.test_pdb.test_pdb_breakpoints[0]>(3)test_function()
+    -> print(1)
+    (Pdb) break 3
+    Breakpoint 1 at <doctest test.test_pdb.test_pdb_breakpoints[0]>:3
+    (Pdb) disable 1
+    Disabled breakpoint 1 at <doctest test.test_pdb.test_pdb_breakpoints[0]>:3
+    (Pdb) ignore 1 10
+    Will ignore next 10 crossings of breakpoint 1.
+    (Pdb) condition 1 1 < 2
+    New condition set for breakpoint 1.
+    (Pdb) break 4
+    Breakpoint 2 at <doctest test.test_pdb.test_pdb_breakpoints[0]>:4
+    (Pdb) break
+    Num Type         Disp Enb   Where
+    1   breakpoint   keep no    at <doctest test.test_pdb.test_pdb_breakpoints[0]>:3
+            stop only if 1 < 2
+            ignore next 10 hits
+    2   breakpoint   keep yes   at <doctest test.test_pdb.test_pdb_breakpoints[0]>:4
+    (Pdb) condition 1
+    Breakpoint 1 is now unconditional.
+    (Pdb) enable 1
+    Enabled breakpoint 1 at <doctest test.test_pdb.test_pdb_breakpoints[0]>:3
+    (Pdb) clear 1
+    Deleted breakpoint 1 at <doctest test.test_pdb.test_pdb_breakpoints[0]>:3
+    (Pdb) commands 2
+    (com) print 42
+    (com) end
+    (Pdb) continue
+    1
+    42
+    > <doctest test.test_pdb.test_pdb_breakpoints[0]>(4)test_function()
+    -> print(2)
+    (Pdb) continue
+    2
+    3
+    4
+    """
+
+
 def pdb_invoke(method, arg):
     """Run pdb.method(arg)."""
     import pdb; getattr(pdb, method)(arg)

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Fri Jul 30 14:01:20 2010
@@ -475,6 +475,9 @@
 Library
 -------
 
+- Issue #809887: Make the output of pdb's breakpoint deletions more
+  consistent; emit a message when a breakpoint is enabled or disabled.
+
 - Issue #5294: Fix the behavior of pdb's "continue" command when called
   in the top-level debugged frame.
 


More information about the Python-checkins mailing list