]> granicus.if.org Git - python/commitdiff
bpo-30211: bdb: add docstrings (#1350)
authorcsabella <chekat2@gmail.com>
Tue, 16 May 2017 22:28:02 +0000 (18:28 -0400)
committerterryjreedy <tjreedy@udel.edu>
Tue, 16 May 2017 22:28:02 +0000 (18:28 -0400)
Lib/bdb.py

index 5a80fa8ba537d5cdb62b0aacdcc3e7a0a5f52a22..eb2fa4fb3e97eea388018a069ca1625302d2ab76 100644 (file)
@@ -7,6 +7,7 @@ from inspect import CO_GENERATOR
 
 __all__ = ["BdbQuit", "Bdb", "Breakpoint"]
 
+
 class BdbQuit(Exception):
     """Exception to give up completely."""
 
@@ -17,6 +18,12 @@ class Bdb:
     This class takes care of details of the trace facility;
     a derived class should implement user interaction.
     The standard debugger class (pdb.Pdb) is an example.
+
+    The optional skip argument must be an iterable of glob-style
+    module name patterns.  The debugger will not step into frames
+    that originate in a module that matches one of these patterns.
+    Whether a frame is considered to originate in a certain module
+    is determined by the __name__ in the frame globals.
     """
 
     def __init__(self, skip=None):
@@ -26,6 +33,13 @@ class Bdb:
         self.frame_returning = None
 
     def canonic(self, filename):
+        """Return canonical form of filename.
+
+        For real filenames, the canonical form is a case-normalized (on
+        case insenstive filesystems) absolute path.  'Filenames' with
+        angle brackets, such as "<stdin>", generated in interactive
+        mode, are returned unchanged.
+        """
         if filename == "<" + filename[1:-1] + ">":
             return filename
         canonic = self.fncache.get(filename)
@@ -36,12 +50,36 @@ class Bdb:
         return canonic
 
     def reset(self):
+        """Set values of attributes as ready to start debugging."""
         import linecache
         linecache.checkcache()
         self.botframe = None
         self._set_stopinfo(None, None)
 
     def trace_dispatch(self, frame, event, arg):
+        """Dispatch a trace function for debugged frames based on the event.
+
+        This function is installed as the trace function for debugged
+        frames. Its return value is the new trace function, which is
+        usually itself. The default implementation decides how to
+        dispatch a frame, depending on the type of event (passed in as a
+        string) that is about to be executed.
+
+        The event can be one of the following:
+            line: A new line of code is going to be executed.
+            call: A function is about to be called or another code block
+                  is entered.
+            return: A function or other code block is about to return.
+            exception: An exception has occurred.
+            c_call: A C function is about to be called.
+            c_return: A C functon has returned.
+            c_exception: A C function has raised an exception.
+
+        For the Python events, specialized functions (see the dispatch_*()
+        methods) are called.  For the C events, no action is taken.
+
+        The arg parameter depends on the previous event.
+        """
         if self.quitting:
             return # None
         if event == 'line':
@@ -62,12 +100,24 @@ class Bdb:
         return self.trace_dispatch
 
     def dispatch_line(self, frame):
+        """Invoke user function and return trace function for line event.
+
+        If the debugger stops on the current line, invoke
+        self.user_line(). Raise BdbQuit if self.quitting is set.
+        Return self.trace_dispatch to continue tracing in this scope.
+        """
         if self.stop_here(frame) or self.break_here(frame):
             self.user_line(frame)
             if self.quitting: raise BdbQuit
         return self.trace_dispatch
 
     def dispatch_call(self, frame, arg):
+        """Invoke user function and return trace function for call event.
+
+        If the debugger stops on this function call, invoke
+        self.user_call(). Raise BbdQuit if self.quitting is set.
+        Return self.trace_dispatch to continue tracing in this scope.
+        """
         # XXX 'arg' is no longer used
         if self.botframe is None:
             # First call of dispatch since reset()
@@ -84,6 +134,12 @@ class Bdb:
         return self.trace_dispatch
 
     def dispatch_return(self, frame, arg):
+        """Invoke user function and return trace function for return event.
+
+        If the debugger stops on this function return, invoke
+        self.user_return(). Raise BdbQuit if self.quitting is set.
+        Return self.trace_dispatch to continue tracing in this scope.
+        """
         if self.stop_here(frame) or frame == self.returnframe:
             # Ignore return events in generator except when stepping.
             if self.stopframe and frame.f_code.co_flags & CO_GENERATOR:
@@ -100,6 +156,12 @@ class Bdb:
         return self.trace_dispatch
 
     def dispatch_exception(self, frame, arg):
+        """Invoke user function and return trace function for exception event.
+
+        If the debugger stops on this exception, invoke
+        self.user_exception(). Raise BdbQuit if self.quitting is set.
+        Return self.trace_dispatch to continue tracing in this scope.
+        """
         if self.stop_here(frame):
             # When stepping with next/until/return in a generator frame, skip
             # the internal StopIteration exception (with no traceback)
@@ -125,12 +187,14 @@ class Bdb:
     # definition of stopping and breakpoints.
 
     def is_skipped_module(self, module_name):
+        "Return True if module_name matches any skip pattern."
         for pattern in self.skip:
             if fnmatch.fnmatch(module_name, pattern):
                 return True
         return False
 
     def stop_here(self, frame):
+        "Return True if frame is below the starting frame in the stack."
         # (CT) stopframe may now also be None, see dispatch_call.
         # (CT) the former test for None is therefore removed from here.
         if self.skip and \
@@ -145,6 +209,11 @@ class Bdb:
         return False
 
     def break_here(self, frame):
+        """Return True if there is an effective breakpoint for this line.
+
+        Check for line or function breakpoint and if in effect.
+        Delete temporary breakpoints if effective() says to.
+        """
         filename = self.canonic(frame.f_code.co_filename)
         if filename not in self.breaks:
             return False
@@ -167,33 +236,43 @@ class Bdb:
             return False
 
     def do_clear(self, arg):
+        """Remove temporary breakpoint.
+
+        Must implement in derived classes or get NotImplementedError.
+        """
         raise NotImplementedError("subclass of bdb must implement do_clear()")
 
     def break_anywhere(self, frame):
+        """Return True if there is any breakpoint for frame's filename.
+        """
         return self.canonic(frame.f_code.co_filename) in self.breaks
 
     # Derived classes should override the user_* methods
     # to gain control.
 
     def user_call(self, frame, argument_list):
-        """This method is called when there is the remote possibility
-        that we ever need to stop in this function."""
+        """Called if we might stop in a function."""
         pass
 
     def user_line(self, frame):
-        """This method is called when we stop or break at this line."""
+        """Called when when we stop or break at a line."""
         pass
 
     def user_return(self, frame, return_value):
-        """This method is called when a return trap is set here."""
+        """Called when a return trap is set here."""
         pass
 
     def user_exception(self, frame, exc_info):
-        """This method is called if an exception occurs,
-        but only if we are to stop at or just below this level."""
+        """Called when we stop on an exception."""
         pass
 
     def _set_stopinfo(self, stopframe, returnframe, stoplineno=0):
+        """Set the attributes for stopping.
+
+        If stoplineno is greater than or equal to 0, then stop at line
+        greater than or equal to the stopline.  If stoplineno is -1, then
+        don't stop at all.
+        """
         self.stopframe = stopframe
         self.returnframe = returnframe
         self.quitting = False
@@ -205,8 +284,8 @@ class Bdb:
     # to affect the stepping state.
 
     def set_until(self, frame, lineno=None):
-        """Stop when the line with the line no greater than the current one is
-        reached or when returning from current frame"""
+        """Stop when the line with the lineno greater than the current one is
+        reached or when returning from current frame."""
         # the name "until" is borrowed from gdb
         if lineno is None:
             lineno = frame.f_lineno + 1
@@ -236,7 +315,7 @@ class Bdb:
             self._set_stopinfo(frame.f_back, frame)
 
     def set_trace(self, frame=None):
-        """Start debugging from `frame`.
+        """Start debugging from frame.
 
         If frame is not specified, debugging starts from caller's frame.
         """
@@ -251,6 +330,10 @@ class Bdb:
         sys.settrace(self.trace_dispatch)
 
     def set_continue(self):
+        """Stop only at breakpoints or when finished.
+
+        If there are no breakpoints, set the system trace function to None.
+        """
         # Don't stop except at breakpoints or when finished
         self._set_stopinfo(self.botframe, None, -1)
         if not self.breaks:
@@ -262,6 +345,10 @@ class Bdb:
                 frame = frame.f_back
 
     def set_quit(self):
+        """Set quitting attribute to True.
+
+        Raises BdbQuit exception in the next call to a dispatch_*() method.
+        """
         self.stopframe = self.botframe
         self.returnframe = None
         self.quitting = True
@@ -269,13 +356,18 @@ class Bdb:
 
     # 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.
+    # error message if 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().
 
     def set_break(self, filename, lineno, temporary=False, cond=None,
                   funcname=None):
+        """Set a new breakpoint for filename:lineno.
+
+        If lineno doesn't exist for the filename, return an error message.
+        The filename should be in canonical form.
+        """
         filename = self.canonic(filename)
         import linecache # Import as late as possible
         line = linecache.getline(filename, lineno)
@@ -285,14 +377,26 @@ class Bdb:
         if lineno not in list:
             list.append(lineno)
         bp = Breakpoint(filename, lineno, temporary, cond, funcname)
+        return None
 
     def _prune_breaks(self, filename, lineno):
+        """Prune breakpoints for filname:lineno.
+
+        A list of breakpoints is maintained in the Bdb instance and in
+        the Breakpoint class.  If a breakpoint in the Bdb instance no
+        longer exists in the Breakpoint class, then it's removed from the
+        Bdb instance.
+        """
         if (filename, lineno) not in Breakpoint.bplist:
             self.breaks[filename].remove(lineno)
         if not self.breaks[filename]:
             del self.breaks[filename]
 
     def clear_break(self, filename, lineno):
+        """Delete breakpoints for filename:lineno.
+
+        If no breakpoints were set, return an error message.
+        """
         filename = self.canonic(filename)
         if filename not in self.breaks:
             return 'There are no breakpoints in %s' % filename
@@ -303,16 +407,26 @@ class Bdb:
         for bp in Breakpoint.bplist[filename, lineno][:]:
             bp.deleteMe()
         self._prune_breaks(filename, lineno)
+        return None
 
     def clear_bpbynumber(self, arg):
+        """Delete a breakpoint by its index in Breakpoint.bpbynumber.
+
+        If arg is invalid, return an error message.
+        """
         try:
             bp = self.get_bpbynumber(arg)
         except ValueError as err:
             return str(err)
         bp.deleteMe()
         self._prune_breaks(bp.file, bp.line)
+        return None
 
     def clear_all_file_breaks(self, filename):
+        """Delete all breakpoints in filename.
+
+        If none were set, return an error message.
+        """
         filename = self.canonic(filename)
         if filename not in self.breaks:
             return 'There are no breakpoints in %s' % filename
@@ -321,16 +435,27 @@ class Bdb:
             for bp in blist:
                 bp.deleteMe()
         del self.breaks[filename]
+        return None
 
     def clear_all_breaks(self):
+        """Delete all existing breakpoints.
+
+        If none were set, return an error message.
+        """
         if not self.breaks:
             return 'There are no breakpoints'
         for bp in Breakpoint.bpbynumber:
             if bp:
                 bp.deleteMe()
         self.breaks = {}
+        return None
 
     def get_bpbynumber(self, arg):
+        """Return a breakpoint by its index in Breakpoint.bybpnumber.
+
+        For invalid arg values or if the breakpoint doesn't exist,
+        raise a ValueError.
+        """
         if not arg:
             raise ValueError('Breakpoint number expected')
         try:
@@ -346,17 +471,26 @@ class Bdb:
         return bp
 
     def get_break(self, filename, lineno):
+        """Return True if there is a breakpoint for 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 breakpoints for filename:lineno.
+
+        If no breakpoints are set, return an empty list.
+        """
         filename = self.canonic(filename)
         return filename in self.breaks and \
             lineno in self.breaks[filename] and \
             Breakpoint.bplist[filename, lineno] or []
 
     def get_file_breaks(self, filename):
+        """Return all lines with breakpoints for filename.
+
+        If no breakpoints are set, return an empty list.
+        """
         filename = self.canonic(filename)
         if filename in self.breaks:
             return self.breaks[filename]
@@ -364,12 +498,18 @@ class Bdb:
             return []
 
     def get_all_breaks(self):
+        """Return all breakpoints that are set."""
         return self.breaks
 
     # Derived classes and clients can call the following method
     # to get a data structure representing a stack trace.
 
     def get_stack(self, f, t):
+        """Return a list of (frame, lineno) in a stack trace and a size.
+
+        List starts with original calling frame, if there is one.
+        Size may be number of frames above or below f.
+        """
         stack = []
         if t and t.tb_frame is f:
             t = t.tb_next
@@ -388,6 +528,14 @@ class Bdb:
         return stack, i
 
     def format_stack_entry(self, frame_lineno, lprefix=': '):
+        """Return a string with information about a stack entry.
+
+        The stack entry frame_lineno is a (frame, lineno) tuple.  The
+        return string contains the canonical filename, the function name
+        or '<lambda>', the input arguments, the return value, and the
+        line of code (if it exists).
+
+        """
         import linecache, reprlib
         frame, lineno = frame_lineno
         filename = self.canonic(frame.f_code.co_filename)
@@ -418,6 +566,10 @@ class Bdb:
     # Both can be given as a string, or a code object.
 
     def run(self, cmd, globals=None, locals=None):
+        """Debug a statement executed via the exec() function.
+
+        globals defaults to __main__.dict; locals defaults to globals.
+        """
         if globals is None:
             import __main__
             globals = __main__.__dict__
@@ -436,6 +588,10 @@ class Bdb:
             sys.settrace(None)
 
     def runeval(self, expr, globals=None, locals=None):
+        """Debug an expression executed via the eval() function.
+
+        globals defaults to __main__.dict; locals defaults to globals.
+        """
         if globals is None:
             import __main__
             globals = __main__.__dict__
@@ -452,12 +608,17 @@ class Bdb:
             sys.settrace(None)
 
     def runctx(self, cmd, globals, locals):
+        """For backwards-compatibility.  Defers to run()."""
         # B/W compatibility
         self.run(cmd, globals, locals)
 
     # This method is more useful to debug a single function call.
 
     def runcall(self, func, *args, **kwds):
+        """Debug a single function call.
+
+        Return the result of the function call.
+        """
         self.reset()
         sys.settrace(self.trace_dispatch)
         res = None
@@ -472,6 +633,7 @@ class Bdb:
 
 
 def set_trace():
+    """Start debugging with a Bdb instance from the caller's frame."""
     Bdb().set_trace()
 
 
@@ -482,11 +644,15 @@ class Breakpoint:
     (re)-enabling, and conditionals.
 
     Breakpoints are indexed by number through bpbynumber and by
-    the file,line tuple using bplist.  The former points to a
+    the (file, line) tuple using bplist.  The former points to a
     single instance of class Breakpoint.  The latter points to a
     list of such instances since there may be more than one
     breakpoint per line.
 
+    When creating a breakpoint, its associated filename should be
+    in canonical form.  If funcname is defined, a breakpoint hit will be
+    counted when the first line of that function is executed.  A
+    conditional breakpoint always counts a hit.
     """
 
     # XXX Keeping state in the class is a mistake -- this means
@@ -519,6 +685,12 @@ class Breakpoint:
             self.bplist[file, line] = [self]
 
     def deleteMe(self):
+        """Delete the breakpoint from the list associated to a file:line.
+
+        If it is the last breakpoint in that position, it also deletes
+        the entry for the file:line.
+        """
+
         index = (self.file, self.line)
         self.bpbynumber[self.number] = None   # No longer in list
         self.bplist[index].remove(self)
@@ -527,17 +699,31 @@ class Breakpoint:
             del self.bplist[index]
 
     def enable(self):
+        """Mark the breakpoint as enabled."""
         self.enabled = True
 
     def disable(self):
+        """Mark the breakpoint as disabled."""
         self.enabled = False
 
     def bpprint(self, out=None):
+        """Print the output of bpformat().
+
+        The optional out argument directs where the output is sent
+        and defaults to standard output.
+        """
         if out is None:
             out = sys.stdout
         print(self.bpformat(), file=out)
 
     def bpformat(self):
+        """Return a string with information about the breakpoint.
+
+        The information includes the breakpoint number, temporary
+        status, file:line position, break condition, number of times to
+        ignore, and number of times hit.
+
+        """
         if self.temporary:
             disp = 'del  '
         else:
@@ -561,12 +747,20 @@ class Breakpoint:
         return ret
 
     def __str__(self):
+        "Return a condensed description of the breakpoint."
         return 'breakpoint %s at %s:%s' % (self.number, self.file, self.line)
 
 # -----------end of Breakpoint class----------
 
+
 def checkfuncname(b, frame):
-    """Check whether we should break here because of `b.funcname`."""
+    """Return True if break should happen here.
+
+    Whether a break should happen depends on the way that b (the breakpoint)
+    was set.  If it was set via line number, check if b.line is the same as
+    the one in the frame.  If it was set via function name, check if this is
+    the right function and if it is on the first executable line.
+    """
     if not b.funcname:
         # Breakpoint was set via line number.
         if b.line != frame.f_lineno:
@@ -576,7 +770,6 @@ def checkfuncname(b, frame):
         return True
 
     # Breakpoint set via function name.
-
     if frame.f_code.co_name != b.funcname:
         # It's not a function call, but rather execution of def statement.
         return False
@@ -586,20 +779,21 @@ def checkfuncname(b, frame):
         # The function is entered for the 1st time.
         b.func_first_executable_line = frame.f_lineno
 
-    if  b.func_first_executable_line != frame.f_lineno:
+    if b.func_first_executable_line != frame.f_lineno:
         # But we are not at the first line number: don't break.
         return False
     return True
 
+
 # Determines if there is an effective (active) breakpoint at this
 # line of code.  Returns breakpoint number or 0 if none
 def effective(file, line, frame):
     """Determine which breakpoint for this file:line is to be acted upon.
 
-    Called only if we know there is a bpt at this
-    location.  Returns breakpoint that was triggered and a flag
-    that indicates if it is ok to delete a temporary bp.
-
+    Called only if we know there is a breakpoint at this location.  Return
+    the breakpoint that was triggered and a boolean that indicates if it is
+    ok to delete a temporary breakpoint.  Return (None, None) if there is no
+    matching breakpoint.
     """
     possibles = Breakpoint.bplist[file, line]
     for b in possibles: