Show inherited methods, with hyperlinks to the base class they came from.
Font adjustment to improve viewing in Windows (the default monospaced font,
    Courier New, seems to have no reasonable size in IE!)
Improve error handling.  Try very hard to distinguish between failure to
    find a module and failure during the module importing process.
Improve reloading behaviour.  (Still needs some work.)
Add '.' to sys.path when running as a script at the command-line.
Don't automatically assume '-g' based on the platform.  We'll just have
    the batch file supply -g.
diff --git a/Lib/pydoc.py b/Lib/pydoc.py
index baa1985..f011d6d 100755
--- a/Lib/pydoc.py
+++ b/Lib/pydoc.py
@@ -41,7 +41,7 @@
 
 import sys, imp, os, stat, re, types, inspect
 from repr import Repr
-from string import expandtabs, find, join, lower, split, strip, rstrip
+from string import expandtabs, find, join, lower, split, strip, rfind, rstrip
 
 # --------------------------------------------------------- common routines
 
@@ -84,10 +84,7 @@
 
 def getdoc(object):
     """Get the doc string or comments for an object."""
-    result = inspect.getdoc(object)
-    if not result:
-        try: result = inspect.getcomments(object)
-        except: pass
+    result = inspect.getdoc(object) or inspect.getcomments(object)
     return result and re.sub('^ *\n', '', rstrip(result)) or ''
 
 def classname(object, modname):
@@ -137,19 +134,29 @@
         if len(filename) > length and filename[-length:] == suffix:
             return filename[:-length]
 
-class DocImportError(Exception):
-    """Class for errors while trying to import something to document it."""
-    def __init__(self, filename, (type, value, tb)):
+def allmethods(cl):
+    methods = {}
+    for key, value in inspect.getmembers(cl, inspect.ismethod):
+        methods[key] = 1
+    for base in cl.__bases__:
+        methods.update(allmethods(base)) # all your base are belong to us
+    for key in methods.keys():
+        methods[key] = getattr(cl, key)
+    return methods
+
+class ErrorDuringImport(Exception):
+    """Errors that occurred while trying to import something to document it."""
+    def __init__(self, filename, (exc, value, tb)):
         self.filename = filename
-        self.type = type
+        self.exc = exc
         self.value = value
         self.tb = tb
 
     def __str__(self):
-        t = self.type
-        if type(t) is types.ClassType:
-            t = t.__name__
-        return 'problem in %s - %s: %s' % (self.filename, t, self.value)
+        exc = self.exc
+        if type(exc) is types.ClassType:
+            exc = exc.__name__
+        return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
 
 def importfile(path):
     """Import a Python source file or compiled file given its path."""
@@ -166,7 +173,7 @@
     try:
         module = imp.load_module(name, file, path, (ext, 'r', kind))
     except:
-        raise DocImportError(path, sys.exc_info())
+        raise ErrorDuringImport(path, sys.exc_info())
     file.close()
     return module
 
@@ -250,7 +257,9 @@
         """Format an HTML page."""
         return '''
 <!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
-<html><title>Python: %s</title><body bgcolor="#f0f0f8">
+<html><head><title>Python: %s</title>
+<style>TT { font-family: lucida console, lucida typewriter, courier }</style>
+</head><body bgcolor="#f0f0f8">
 %s
 </body></html>''' % (title, contents)
 
@@ -279,11 +288,9 @@
         if prelude:
             result = result + '''
 <tr bgcolor="%s"><td>%s</td>
-<td colspan=2>%s</td></tr>
-    ''' % (bgcol, marginalia, prelude)
+<td colspan=2>%s</td></tr>''' % (bgcol, marginalia, prelude)
         result = result + '''
-<tr><td bgcolor="%s">%s</td><td>%s</td>
-    ''' % (bgcol, marginalia, gap)
+<tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
 
         return result + '<td width="100%%">%s</td></tr></table>' % contents
 
@@ -306,7 +313,7 @@
             result = result + '<td width="%d%%" valign=top>' % (100/cols)
             for i in range(rows*col, rows*col+rows):
                 if i < len(list):
-                    result = result + format(list[i]) + '<br>'
+                    result = result + format(list[i]) + '<br>\n'
             result = result + '</td>'
         return '<table width="100%%"><tr>%s</tr></table>' % result
 
@@ -354,8 +361,8 @@
         escape = escape or self.escape
         results = []
         here = 0
-        pattern = re.compile(r'\b(((http|ftp)://\S+[\w/])|'
-                                r'(RFC[- ]?(\d+))|'
+        pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
+                                r'RFC[- ]?(\d+)|'
                                 r'(self\.)?(\w+))\b')
         while 1:
             match = pattern.search(text, here)
@@ -363,19 +370,18 @@
             start, end = match.span()
             results.append(escape(text[here:start]))
 
-            all, url, scheme, rfc, rfcnum, selfdot, name = match.groups()
-            if url:
-                results.append('<a href="%s">%s</a>' % (url, escape(url)))
+            all, scheme, rfc, selfdot, name = match.groups()
+            if scheme:
+                results.append('<a href="%s">%s</a>' % (all, escape(all)))
             elif rfc:
-                url = 'http://www.rfc-editor.org/rfc/rfc%s.txt' % rfcnum
-                results.append('<a href="%s">%s</a>' % (url, escape(rfc)))
+                url = 'http://www.rfc-editor.org/rfc/rfc%s.txt' % rfc
+                results.append('<a href="%s">%s</a>' % (url, escape(all)))
+            elif text[end:end+1] == '(':
+                results.append(self.namelink(name, methods, funcs, classes))
+            elif selfdot:
+                results.append('self.<strong>%s</strong>' % name)
             else:
-                if text[end:end+1] == '(':
-                    results.append(self.namelink(name, methods, funcs, classes))
-                elif selfdot:
-                    results.append('self.<strong>%s</strong>' % name)
-                else:
-                    results.append(self.namelink(name, classes))
+                results.append(self.namelink(name, classes))
             here = end
         results.append(escape(text[here:]))
         return join(results, '')
@@ -528,12 +534,13 @@
         contents = ''
 
         methods, mdict = [], {}
-        for key, value in inspect.getmembers(object, inspect.ismethod):
+        for key, value in allmethods(object).items():
             methods.append((key, value))
             mdict[key] = mdict[value] = '#' + name + '-' + key
+        methods.sort()
         for key, value in methods:
             contents = contents + self.document(
-                value, key, funcs, classes, mdict, name)
+                value, key, funcs, classes, mdict, object)
 
         if name == realname:
             title = '<a name="%s">class <strong>%s</strong></a>' % (
@@ -541,7 +548,6 @@
         else:
             title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
                 name, name, realname)
-
         if bases:
             parents = []
             for base in bases:
@@ -550,7 +556,7 @@
             title = title + '(%s)' % join(parents, ', ')
         doc = self.markup(
             getdoc(object), self.preformat, funcs, classes, mdict)
-        if doc: doc = self.small('<tt>%s</tt>' % doc)
+        doc = self.small('<tt>%s<br>&nbsp;</tt>' % doc)
         return self.section(title, '#000000', '#ffc8d8', contents, 10, doc)
 
     def formatvalue(self, object):
@@ -558,25 +564,42 @@
         return self.small(self.grey('=' + self.repr(object)))
 
     def docroutine(self, object, name=None,
-                   funcs={}, classes={}, methods={}, clname=''):
+                   funcs={}, classes={}, methods={}, cl=None):
         """Produce HTML documentation for a function or method object."""
         realname = object.__name__
         name = name or realname
-        anchor = clname + '-' + realname
+        anchor = (cl and cl.__name__ or '') + '-' + name
         note = ''
+        skipdocs = 0
         if inspect.ismethod(object):
-            if not clname:
-                note = self.small(self.grey(
-                    object.im_self and
+            if cl:
+                if not cl.__dict__.has_key(name):
+                    base = object.im_class
+                    url = '#%s-%s' % (base.__name__, name)
+                    basename = base.__name__
+                    if base.__module__ != cl.__module__:
+                        url = base.__module__ + '.html' + url
+                        basename = base.__module__ + '.' + basename
+                    note = ' from <a href="%s">%s</a>' % (url, basename)
+                    skipdocs = 1
+            else:
+                note = (object.im_self and
                         'method of ' + self.repr(object.im_self) or
-                        ' unbound %s method' % object.im_class.__name__))
+                        ' unbound %s method' % object.im_class.__name__)
             object = object.im_func
 
         if name == realname:
             title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
         else:
-            title = '<strong>%s</strong> = <a name="%s">%s</a>' % (
-                name, anchor, realname)
+            if (cl and cl.__dict__.has_key(realname) and
+                cl.__dict__[realname] is object):
+                reallink = '<a href="%s">%s</a>' % (
+                    cl.__name__ + '-' + realname, realname)
+                skipdocs = 1
+            else:
+                reallink = realname
+            title = '<a name="%s"><strong>%s</strong></a> = %s' % (
+                anchor, name, reallink)
         if inspect.isbuiltin(object):
             argspec = '(...)'
         else:
@@ -587,13 +610,15 @@
                 decl = '<em>lambda</em>'
                 argspec = argspec[1:-1] # remove parentheses
 
-        decl = title + argspec + note
+        decl = title + argspec + (note and self.small(self.grey(note)))
 
-        doc = self.markup(
-            getdoc(object), self.preformat, funcs, classes, methods)
-        doc = replace(doc, ('<br>\n', '</tt></small\n><dd><small><tt>'))
-        doc = doc and '<tt>%s</tt>' % doc
-        return '<dl><dt>%s<dd>%s</dl>' % (decl, self.small(doc))
+        if skipdocs:
+            return '<dl><dt>%s</dl>' % decl
+        else:
+            doc = self.markup(
+                getdoc(object), self.preformat, funcs, classes, methods)
+            doc = doc and '<tt>%s</tt>' % doc
+            return '<dl><dt>%s<dd>%s</dl>' % (decl, self.small(doc))
 
     def docother(self, object, name=None):
         """Produce HTML documentation for a data object."""
@@ -703,14 +728,15 @@
     def docmodule(self, object, name=None):
         """Produce text documentation for a given module object."""
         name = object.__name__ # ignore the passed-in name
+        namesec = name
         lines = split(strip(getdoc(object)), '\n')
         if len(lines) == 1:
-            if lines[0]: name = name + ' - ' + lines[0]
+            if lines[0]: namesec = namesec + ' - ' + lines[0]
             lines = []
         elif len(lines) >= 2 and not rstrip(lines[1]):
-            if lines[0]: name = name + ' - ' + lines[0]
+            if lines[0]: namesec = namesec + ' - ' + lines[0]
             lines = lines[2:]
-        result = self.section('NAME', name)
+        result = self.section('NAME', namesec)
 
         try:
             file = inspect.getabsfile(object)
@@ -791,13 +817,16 @@
         else:
             title = self.bold(name) + ' = class ' + realname
         if bases:
-            parents = map(lambda c, m=object.__module__: classname(c, m), bases)
+            def makename(c, m=object.__module__): return classname(c, m)
+            parents = map(makename, bases)
             title = title + '(%s)' % join(parents, ', ')
 
         doc = getdoc(object)
         contents = doc and doc + '\n'
-        for key, value in inspect.getmembers(object, inspect.ismethod):
-            contents = contents + '\n' + self.document(value, key, name)
+        methods = allmethods(object).items()
+        methods.sort()
+        for key, value in methods:
+            contents = contents + '\n' + self.document(value, key, object)
 
         if not contents: return title + '\n'
         return title + '\n' + self.indent(rstrip(contents), ' |  ') + '\n'
@@ -806,13 +835,22 @@
         """Format an argument default value as text."""
         return '=' + self.repr(object)
 
-    def docroutine(self, object, name=None, clname=None):
+    def docroutine(self, object, name=None, cl=None):
         """Produce text documentation for a function or method object."""
         realname = object.__name__
         name = name or realname
         note = ''
+        skipdocs = 0
         if inspect.ismethod(object):
-            if not clname:
+            if cl:
+                if not cl.__dict__.has_key(name):
+                    base = object.im_class
+                    basename = base.__name__
+                    if base.__module__ != cl.__module__:
+                        basename = base.__module__ + '.' + basename
+                    note = ' from %s' % basename
+                    skipdocs = 1
+            else:
                 if object.im_self:
                     note = ' method of %s' % self.repr(object.im_self)
                 else:
@@ -822,6 +860,9 @@
         if name == realname:
             title = self.bold(realname)
         else:
+            if (cl and cl.__dict__.has_key(realname) and
+                cl.__dict__[realname] is object):
+                skipdocs = 1
             title = self.bold(name) + ' = ' + realname
         if inspect.isbuiltin(object):
             argspec = '(...)'
@@ -834,11 +875,11 @@
                 argspec = argspec[1:-1] # remove parentheses
         decl = title + argspec + note
 
-        doc = getdoc(object)
-        if doc:
-            return decl + '\n' + rstrip(self.indent(doc)) + '\n'
-        else:
+        if skipdocs:
             return decl + '\n'
+        else:
+            doc = getdoc(object) or ''
+            return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
 
     def docother(self, object, name=None, maxlen=None):
         """Produce text documentation for a data object."""
@@ -951,7 +992,7 @@
     sys.stdout.write(plain(text))
 
 def describe(thing):
-    """Produce a short description of the given kind of thing."""
+    """Produce a short description of the given thing."""
     if inspect.ismodule(thing):
         if thing.__name__ in sys.builtin_module_names:
             return 'built-in module ' + thing.__name__
@@ -971,52 +1012,69 @@
         return 'instance of ' + thing.__class__.__name__
     return type(thing).__name__
 
-def freshimp(path, cache={}):
+def freshimport(name, cache={}):
     """Import a module, reloading it if the source file has changed."""
-    module = __import__(path)
-    if hasattr(module, '__file__'):
-        file = module.__file__
-        info = (file, os.path.getmtime(file), os.path.getsize(file))
-        if cache.has_key(path):
-            if cache[path] != info:
+    topmodule = __import__(name)
+    module = None
+    for component in split(name, '.'):
+        if module == None:
+            module = topmodule
+            path = split(name, '.')[0]
+        else:
+            module = getattr(module, component)
+            path = path + '.' + component
+        if hasattr(module, '__file__'):
+            file = module.__file__
+            if os.path.exists(file):
+                info = (file, os.path.getmtime(file), os.path.getsize(file))
+            if cache.has_key(path) and cache[path] != info:
                 module = reload(module)
-        cache[path] = info
+            file = module.__file__
+            if os.path.exists(file):
+                info = (file, os.path.getmtime(file), os.path.getsize(file))
+            cache[path] = info
     return module
 
 def locate(path):
     """Locate an object by name (or dotted path), importing as necessary."""
     if not path: # special case: imp.find_module('') strangely succeeds
-        return None, None
+        return None
     if type(path) is not types.StringType:
-        return None, path
+        return path
     parts = split(path, '.')
-    n = 1
-    while n <= len(parts):
+    n = len(parts)
+    while n > 0:
         path = join(parts[:n], '.')
         try:
-            module = freshimp(path)
+            module = freshimport(path)
         except:
-            # determine if error occurred before or after module was found
+            # Did the error occur before or after the module was found?
+            (exc, value, tb) = info = sys.exc_info()
             if sys.modules.has_key(path):
-                filename = sys.modules[path].__file__
-            elif sys.exc_type is SyntaxError:
-                filename = sys.exc_value.filename
+                # An error occured while executing the imported module.
+                raise ErrorDuringImport(sys.modules[path].__file__, info)
+            elif exc is SyntaxError:
+                # A SyntaxError occurred before we could execute the module.
+                raise ErrorDuringImport(value.filename, info)
+            elif exc is ImportError and \
+                 split(lower(str(value)))[:2] == ['no', 'module']:
+                # The module was not found.
+                n = n - 1
+                continue
             else:
-                # module not found, so stop looking
-                break
-            # error occurred in the imported module, so report it
-            raise DocImportError(filename, sys.exc_info())
+                # Some other error occurred before executing the module.
+                raise DocImportError(filename, sys.exc_info())
         try:
             x = module
-            for p in parts[1:]:
+            for p in parts[n:]:
                 x = getattr(x, p)
-            return join(parts[:-1], '.'), x
+            return x
         except AttributeError:
-            n = n + 1
+            n = n - 1
             continue
     if hasattr(__builtins__, path):
-        return None, getattr(__builtins__, path)
-    return None, None
+        return getattr(__builtins__, path)
+    return None
 
 # --------------------------------------- interactive interpreter interface
 
@@ -1027,12 +1085,12 @@
     """Display documentation on an object (for interactive use)."""
     if type(thing) is type(""):
         try:
-            path, x = locate(thing)
-        except DocImportError, value:
+            object = locate(thing)
+        except ErrorDuringImport, value:
             print value
             return
-        if x:
-            thing = x
+        if object:
+            thing = object
         else:
             print 'no Python documentation found for %s' % repr(thing)
             return
@@ -1046,8 +1104,8 @@
 def writedoc(key):
     """Write HTML documentation to a file in the current directory."""
     try:
-        path, object = locate(key)
-    except DocImportError, value:
+        object = locate(key)
+    except ErrorDuringImport, value:
         print value
     else:
         if object:
@@ -1090,10 +1148,11 @@
 
 def man(key):
     """Display documentation on an object in a form similar to man(1)."""
-    path, object = locate(key)
+    object = locate(key)
     if object:
         title = 'Python Library Documentation: ' + describe(object)
-        if path: title = title + ' in ' + path
+        lastdot = rfind(key, '.')
+        if lastdot > 0: title = title + ' in ' + key[:lastdot]
         pager('\n' + title + '\n\n' + text.document(object, key))
         found = 1
     else:
@@ -1149,7 +1208,7 @@
         for modname in sys.builtin_module_names:
             if modname != '__main__':
                 seen[modname] = 1
-                desc = split(freshimp(modname).__doc__ or '', '\n')[0]
+                desc = split(freshimport(modname).__doc__ or '', '\n')[0]
                 if find(lower(modname + ' - ' + desc), lower(key)) >= 0:
                     callback(None, modname, desc)
 
@@ -1205,12 +1264,12 @@
             if path[:1] == '/': path = path[1:]
             if path and path != '.':
                 try:
-                    p, x = locate(path)
-                except DocImportError, value:
+                    obj = locate(path)
+                except ErrorDuringImport, value:
                     self.send_document(path, html.escape(str(value)))
                     return
-                if x:
-                    self.send_document(describe(x), html.document(x, path))
+                if obj:
+                    self.send_document(describe(obj), html.document(obj, path))
                 else:
                     self.send_document(path,
 'no Python documentation found for %s' % repr(path))
@@ -1220,13 +1279,12 @@
 '#ffffff', '#7799ee')
                 def bltinlink(name):
                     return '<a href="%s.html">%s</a>' % (name, name)
-                names = filter(lambda x: x != '__main__', sys.builtin_module_names)
+                names = filter(lambda x: x != '__main__',
+                               sys.builtin_module_names)
                 contents = html.multicolumn(names, bltinlink)
                 indices = ['<p>' + html.bigsection(
                     'Built-in Modules', '#ffffff', '#ee77aa', contents)]
 
-                # for skip in ['', '.', os.getcwd(), os.path.dirname(sys.argv[0])]:
-                #     if skip in sys.path: sys.path.remove(skip)
                 seen = {}
                 for dir in pathdirs():
                     indices.append(html.index(dir, seen))
@@ -1453,12 +1511,10 @@
     import getopt
     class BadUsage: pass
 
-    try:
-        if sys.platform in ['mac', 'win32'] and not sys.argv[1:]:
-            # graphical platforms with threading (and no CLI)
-            gui()
-            return
+    # Scripts don't get the current directory in their path by default.
+    sys.path.insert(0, '.')
 
+    try:
         opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
         writing = 0
 
@@ -1493,7 +1549,7 @@
                         writedoc(arg)
                 else:
                     man(arg)
-            except DocImportError, value:
+            except ErrorDuringImport, value:
                 print value
 
     except (getopt.error, BadUsage):