symbian-qemu-0.9.1-12/python-win32-2.6.1/lib/pydoc.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: latin-1 -*-
       
     3 """Generate Python documentation in HTML or text for interactive use.
       
     4 
       
     5 In the Python interpreter, do "from pydoc import help" to provide online
       
     6 help.  Calling help(thing) on a Python object documents the object.
       
     7 
       
     8 Or, at the shell command line outside of Python:
       
     9 
       
    10 Run "pydoc <name>" to show documentation on something.  <name> may be
       
    11 the name of a function, module, package, or a dotted reference to a
       
    12 class or function within a module or module in a package.  If the
       
    13 argument contains a path segment delimiter (e.g. slash on Unix,
       
    14 backslash on Windows) it is treated as the path to a Python source file.
       
    15 
       
    16 Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines
       
    17 of all available modules.
       
    18 
       
    19 Run "pydoc -p <port>" to start an HTTP server on a given port on the
       
    20 local machine to generate documentation web pages.
       
    21 
       
    22 For platforms without a command line, "pydoc -g" starts the HTTP server
       
    23 and also pops up a little window for controlling it.
       
    24 
       
    25 Run "pydoc -w <name>" to write out the HTML documentation for a module
       
    26 to a file named "<name>.html".
       
    27 
       
    28 Module docs for core modules are assumed to be in
       
    29 
       
    30     http://docs.python.org/library/
       
    31 
       
    32 This can be overridden by setting the PYTHONDOCS environment variable
       
    33 to a different URL or to a local directory containing the Library
       
    34 Reference Manual pages.
       
    35 """
       
    36 
       
    37 __author__ = "Ka-Ping Yee <ping@lfw.org>"
       
    38 __date__ = "26 February 2001"
       
    39 
       
    40 __version__ = "$Revision: 66076 $"
       
    41 __credits__ = """Guido van Rossum, for an excellent programming language.
       
    42 Tommy Burnette, the original creator of manpy.
       
    43 Paul Prescod, for all his work on onlinehelp.
       
    44 Richard Chamberlain, for the first implementation of textdoc.
       
    45 """
       
    46 
       
    47 # Known bugs that can't be fixed here:
       
    48 #   - imp.load_module() cannot be prevented from clobbering existing
       
    49 #     loaded modules, so calling synopsis() on a binary module file
       
    50 #     changes the contents of any existing module with the same name.
       
    51 #   - If the __file__ attribute on a module is a relative path and
       
    52 #     the current directory is changed with os.chdir(), an incorrect
       
    53 #     path will be displayed.
       
    54 
       
    55 import sys, imp, os, re, types, inspect, __builtin__, pkgutil
       
    56 from repr import Repr
       
    57 from string import expandtabs, find, join, lower, split, strip, rfind, rstrip
       
    58 try:
       
    59     from collections import deque
       
    60 except ImportError:
       
    61     # Python 2.3 compatibility
       
    62     class deque(list):
       
    63         def popleft(self):
       
    64             return self.pop(0)
       
    65 
       
    66 # --------------------------------------------------------- common routines
       
    67 
       
    68 def pathdirs():
       
    69     """Convert sys.path into a list of absolute, existing, unique paths."""
       
    70     dirs = []
       
    71     normdirs = []
       
    72     for dir in sys.path:
       
    73         dir = os.path.abspath(dir or '.')
       
    74         normdir = os.path.normcase(dir)
       
    75         if normdir not in normdirs and os.path.isdir(dir):
       
    76             dirs.append(dir)
       
    77             normdirs.append(normdir)
       
    78     return dirs
       
    79 
       
    80 def getdoc(object):
       
    81     """Get the doc string or comments for an object."""
       
    82     result = inspect.getdoc(object) or inspect.getcomments(object)
       
    83     return result and re.sub('^ *\n', '', rstrip(result)) or ''
       
    84 
       
    85 def splitdoc(doc):
       
    86     """Split a doc string into a synopsis line (if any) and the rest."""
       
    87     lines = split(strip(doc), '\n')
       
    88     if len(lines) == 1:
       
    89         return lines[0], ''
       
    90     elif len(lines) >= 2 and not rstrip(lines[1]):
       
    91         return lines[0], join(lines[2:], '\n')
       
    92     return '', join(lines, '\n')
       
    93 
       
    94 def classname(object, modname):
       
    95     """Get a class name and qualify it with a module name if necessary."""
       
    96     name = object.__name__
       
    97     if object.__module__ != modname:
       
    98         name = object.__module__ + '.' + name
       
    99     return name
       
   100 
       
   101 def isdata(object):
       
   102     """Check if an object is of a type that probably means it's data."""
       
   103     return not (inspect.ismodule(object) or inspect.isclass(object) or
       
   104                 inspect.isroutine(object) or inspect.isframe(object) or
       
   105                 inspect.istraceback(object) or inspect.iscode(object))
       
   106 
       
   107 def replace(text, *pairs):
       
   108     """Do a series of global replacements on a string."""
       
   109     while pairs:
       
   110         text = join(split(text, pairs[0]), pairs[1])
       
   111         pairs = pairs[2:]
       
   112     return text
       
   113 
       
   114 def cram(text, maxlen):
       
   115     """Omit part of a string if needed to make it fit in a maximum length."""
       
   116     if len(text) > maxlen:
       
   117         pre = max(0, (maxlen-3)//2)
       
   118         post = max(0, maxlen-3-pre)
       
   119         return text[:pre] + '...' + text[len(text)-post:]
       
   120     return text
       
   121 
       
   122 _re_stripid = re.compile(r' at 0x[0-9a-f]{6,16}(>+)$', re.IGNORECASE)
       
   123 def stripid(text):
       
   124     """Remove the hexadecimal id from a Python object representation."""
       
   125     # The behaviour of %p is implementation-dependent in terms of case.
       
   126     if _re_stripid.search(repr(Exception)):
       
   127         return _re_stripid.sub(r'\1', text)
       
   128     return text
       
   129 
       
   130 def _is_some_method(obj):
       
   131     return inspect.ismethod(obj) or inspect.ismethoddescriptor(obj)
       
   132 
       
   133 def allmethods(cl):
       
   134     methods = {}
       
   135     for key, value in inspect.getmembers(cl, _is_some_method):
       
   136         methods[key] = 1
       
   137     for base in cl.__bases__:
       
   138         methods.update(allmethods(base)) # all your base are belong to us
       
   139     for key in methods.keys():
       
   140         methods[key] = getattr(cl, key)
       
   141     return methods
       
   142 
       
   143 def _split_list(s, predicate):
       
   144     """Split sequence s via predicate, and return pair ([true], [false]).
       
   145 
       
   146     The return value is a 2-tuple of lists,
       
   147         ([x for x in s if predicate(x)],
       
   148          [x for x in s if not predicate(x)])
       
   149     """
       
   150 
       
   151     yes = []
       
   152     no = []
       
   153     for x in s:
       
   154         if predicate(x):
       
   155             yes.append(x)
       
   156         else:
       
   157             no.append(x)
       
   158     return yes, no
       
   159 
       
   160 def visiblename(name, all=None):
       
   161     """Decide whether to show documentation on a variable."""
       
   162     # Certain special names are redundant.
       
   163     _hidden_names = ('__builtins__', '__doc__', '__file__', '__path__',
       
   164                      '__module__', '__name__', '__slots__', '__package__')
       
   165     if name in _hidden_names: return 0
       
   166     # Private names are hidden, but special names are displayed.
       
   167     if name.startswith('__') and name.endswith('__'): return 1
       
   168     if all is not None:
       
   169         # only document that which the programmer exported in __all__
       
   170         return name in all
       
   171     else:
       
   172         return not name.startswith('_')
       
   173 
       
   174 def classify_class_attrs(object):
       
   175     """Wrap inspect.classify_class_attrs, with fixup for data descriptors."""
       
   176     def fixup(data):
       
   177         name, kind, cls, value = data
       
   178         if inspect.isdatadescriptor(value):
       
   179             kind = 'data descriptor'
       
   180         return name, kind, cls, value
       
   181     return map(fixup, inspect.classify_class_attrs(object))
       
   182 
       
   183 # ----------------------------------------------------- module manipulation
       
   184 
       
   185 def ispackage(path):
       
   186     """Guess whether a path refers to a package directory."""
       
   187     if os.path.isdir(path):
       
   188         for ext in ('.py', '.pyc', '.pyo'):
       
   189             if os.path.isfile(os.path.join(path, '__init__' + ext)):
       
   190                 return True
       
   191     return False
       
   192 
       
   193 def source_synopsis(file):
       
   194     line = file.readline()
       
   195     while line[:1] == '#' or not strip(line):
       
   196         line = file.readline()
       
   197         if not line: break
       
   198     line = strip(line)
       
   199     if line[:4] == 'r"""': line = line[1:]
       
   200     if line[:3] == '"""':
       
   201         line = line[3:]
       
   202         if line[-1:] == '\\': line = line[:-1]
       
   203         while not strip(line):
       
   204             line = file.readline()
       
   205             if not line: break
       
   206         result = strip(split(line, '"""')[0])
       
   207     else: result = None
       
   208     return result
       
   209 
       
   210 def synopsis(filename, cache={}):
       
   211     """Get the one-line summary out of a module file."""
       
   212     mtime = os.stat(filename).st_mtime
       
   213     lastupdate, result = cache.get(filename, (0, None))
       
   214     if lastupdate < mtime:
       
   215         info = inspect.getmoduleinfo(filename)
       
   216         try:
       
   217             file = open(filename)
       
   218         except IOError:
       
   219             # module can't be opened, so skip it
       
   220             return None
       
   221         if info and 'b' in info[2]: # binary modules have to be imported
       
   222             try: module = imp.load_module('__temp__', file, filename, info[1:])
       
   223             except: return None
       
   224             result = (module.__doc__ or '').splitlines()[0]
       
   225             del sys.modules['__temp__']
       
   226         else: # text modules can be directly examined
       
   227             result = source_synopsis(file)
       
   228             file.close()
       
   229         cache[filename] = (mtime, result)
       
   230     return result
       
   231 
       
   232 class ErrorDuringImport(Exception):
       
   233     """Errors that occurred while trying to import something to document it."""
       
   234     def __init__(self, filename, exc_info):
       
   235         exc, value, tb = exc_info
       
   236         self.filename = filename
       
   237         self.exc = exc
       
   238         self.value = value
       
   239         self.tb = tb
       
   240 
       
   241     def __str__(self):
       
   242         exc = self.exc
       
   243         if type(exc) is types.ClassType:
       
   244             exc = exc.__name__
       
   245         return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
       
   246 
       
   247 def importfile(path):
       
   248     """Import a Python source file or compiled file given its path."""
       
   249     magic = imp.get_magic()
       
   250     file = open(path, 'r')
       
   251     if file.read(len(magic)) == magic:
       
   252         kind = imp.PY_COMPILED
       
   253     else:
       
   254         kind = imp.PY_SOURCE
       
   255     file.close()
       
   256     filename = os.path.basename(path)
       
   257     name, ext = os.path.splitext(filename)
       
   258     file = open(path, 'r')
       
   259     try:
       
   260         module = imp.load_module(name, file, path, (ext, 'r', kind))
       
   261     except:
       
   262         raise ErrorDuringImport(path, sys.exc_info())
       
   263     file.close()
       
   264     return module
       
   265 
       
   266 def safeimport(path, forceload=0, cache={}):
       
   267     """Import a module; handle errors; return None if the module isn't found.
       
   268 
       
   269     If the module *is* found but an exception occurs, it's wrapped in an
       
   270     ErrorDuringImport exception and reraised.  Unlike __import__, if a
       
   271     package path is specified, the module at the end of the path is returned,
       
   272     not the package at the beginning.  If the optional 'forceload' argument
       
   273     is 1, we reload the module from disk (unless it's a dynamic extension)."""
       
   274     try:
       
   275         # If forceload is 1 and the module has been previously loaded from
       
   276         # disk, we always have to reload the module.  Checking the file's
       
   277         # mtime isn't good enough (e.g. the module could contain a class
       
   278         # that inherits from another module that has changed).
       
   279         if forceload and path in sys.modules:
       
   280             if path not in sys.builtin_module_names:
       
   281                 # Avoid simply calling reload() because it leaves names in
       
   282                 # the currently loaded module lying around if they're not
       
   283                 # defined in the new source file.  Instead, remove the
       
   284                 # module from sys.modules and re-import.  Also remove any
       
   285                 # submodules because they won't appear in the newly loaded
       
   286                 # module's namespace if they're already in sys.modules.
       
   287                 subs = [m for m in sys.modules if m.startswith(path + '.')]
       
   288                 for key in [path] + subs:
       
   289                     # Prevent garbage collection.
       
   290                     cache[key] = sys.modules[key]
       
   291                     del sys.modules[key]
       
   292         module = __import__(path)
       
   293     except:
       
   294         # Did the error occur before or after the module was found?
       
   295         (exc, value, tb) = info = sys.exc_info()
       
   296         if path in sys.modules:
       
   297             # An error occurred while executing the imported module.
       
   298             raise ErrorDuringImport(sys.modules[path].__file__, info)
       
   299         elif exc is SyntaxError:
       
   300             # A SyntaxError occurred before we could execute the module.
       
   301             raise ErrorDuringImport(value.filename, info)
       
   302         elif exc is ImportError and \
       
   303              split(lower(str(value)))[:2] == ['no', 'module']:
       
   304             # The module was not found.
       
   305             return None
       
   306         else:
       
   307             # Some other error occurred during the importing process.
       
   308             raise ErrorDuringImport(path, sys.exc_info())
       
   309     for part in split(path, '.')[1:]:
       
   310         try: module = getattr(module, part)
       
   311         except AttributeError: return None
       
   312     return module
       
   313 
       
   314 # ---------------------------------------------------- formatter base class
       
   315 
       
   316 class Doc:
       
   317     def document(self, object, name=None, *args):
       
   318         """Generate documentation for an object."""
       
   319         args = (object, name) + args
       
   320         # 'try' clause is to attempt to handle the possibility that inspect
       
   321         # identifies something in a way that pydoc itself has issues handling;
       
   322         # think 'super' and how it is a descriptor (which raises the exception
       
   323         # by lacking a __name__ attribute) and an instance.
       
   324         if inspect.isgetsetdescriptor(object): return self.docdata(*args)
       
   325         if inspect.ismemberdescriptor(object): return self.docdata(*args)
       
   326         try:
       
   327             if inspect.ismodule(object): return self.docmodule(*args)
       
   328             if inspect.isclass(object): return self.docclass(*args)
       
   329             if inspect.isroutine(object): return self.docroutine(*args)
       
   330         except AttributeError:
       
   331             pass
       
   332         if isinstance(object, property): return self.docproperty(*args)
       
   333         return self.docother(*args)
       
   334 
       
   335     def fail(self, object, name=None, *args):
       
   336         """Raise an exception for unimplemented types."""
       
   337         message = "don't know how to document object%s of type %s" % (
       
   338             name and ' ' + repr(name), type(object).__name__)
       
   339         raise TypeError, message
       
   340 
       
   341     docmodule = docclass = docroutine = docother = docproperty = docdata = fail
       
   342 
       
   343     def getdocloc(self, object):
       
   344         """Return the location of module docs or None"""
       
   345 
       
   346         try:
       
   347             file = inspect.getabsfile(object)
       
   348         except TypeError:
       
   349             file = '(built-in)'
       
   350 
       
   351         docloc = os.environ.get("PYTHONDOCS",
       
   352                                 "http://docs.python.org/library")
       
   353         basedir = os.path.join(sys.exec_prefix, "lib",
       
   354                                "python"+sys.version[0:3])
       
   355         if (isinstance(object, type(os)) and
       
   356             (object.__name__ in ('errno', 'exceptions', 'gc', 'imp',
       
   357                                  'marshal', 'posix', 'signal', 'sys',
       
   358                                  'thread', 'zipimport') or
       
   359              (file.startswith(basedir) and
       
   360               not file.startswith(os.path.join(basedir, 'site-packages'))))):
       
   361             if docloc.startswith("http://"):
       
   362                 docloc = "%s/%s" % (docloc.rstrip("/"), object.__name__)
       
   363             else:
       
   364                 docloc = os.path.join(docloc, object.__name__ + ".html")
       
   365         else:
       
   366             docloc = None
       
   367         return docloc
       
   368 
       
   369 # -------------------------------------------- HTML documentation generator
       
   370 
       
   371 class HTMLRepr(Repr):
       
   372     """Class for safely making an HTML representation of a Python object."""
       
   373     def __init__(self):
       
   374         Repr.__init__(self)
       
   375         self.maxlist = self.maxtuple = 20
       
   376         self.maxdict = 10
       
   377         self.maxstring = self.maxother = 100
       
   378 
       
   379     def escape(self, text):
       
   380         return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')
       
   381 
       
   382     def repr(self, object):
       
   383         return Repr.repr(self, object)
       
   384 
       
   385     def repr1(self, x, level):
       
   386         if hasattr(type(x), '__name__'):
       
   387             methodname = 'repr_' + join(split(type(x).__name__), '_')
       
   388             if hasattr(self, methodname):
       
   389                 return getattr(self, methodname)(x, level)
       
   390         return self.escape(cram(stripid(repr(x)), self.maxother))
       
   391 
       
   392     def repr_string(self, x, level):
       
   393         test = cram(x, self.maxstring)
       
   394         testrepr = repr(test)
       
   395         if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
       
   396             # Backslashes are only literal in the string and are never
       
   397             # needed to make any special characters, so show a raw string.
       
   398             return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
       
   399         return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
       
   400                       r'<font color="#c040c0">\1</font>',
       
   401                       self.escape(testrepr))
       
   402 
       
   403     repr_str = repr_string
       
   404 
       
   405     def repr_instance(self, x, level):
       
   406         try:
       
   407             return self.escape(cram(stripid(repr(x)), self.maxstring))
       
   408         except:
       
   409             return self.escape('<%s instance>' % x.__class__.__name__)
       
   410 
       
   411     repr_unicode = repr_string
       
   412 
       
   413 class HTMLDoc(Doc):
       
   414     """Formatter class for HTML documentation."""
       
   415 
       
   416     # ------------------------------------------- HTML formatting utilities
       
   417 
       
   418     _repr_instance = HTMLRepr()
       
   419     repr = _repr_instance.repr
       
   420     escape = _repr_instance.escape
       
   421 
       
   422     def page(self, title, contents):
       
   423         """Format an HTML page."""
       
   424         return '''
       
   425 <!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
       
   426 <html><head><title>Python: %s</title>
       
   427 </head><body bgcolor="#f0f0f8">
       
   428 %s
       
   429 </body></html>''' % (title, contents)
       
   430 
       
   431     def heading(self, title, fgcol, bgcol, extras=''):
       
   432         """Format a page heading."""
       
   433         return '''
       
   434 <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading">
       
   435 <tr bgcolor="%s">
       
   436 <td valign=bottom>&nbsp;<br>
       
   437 <font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
       
   438 ><td align=right valign=bottom
       
   439 ><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
       
   440     ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
       
   441 
       
   442     def section(self, title, fgcol, bgcol, contents, width=6,
       
   443                 prelude='', marginalia=None, gap='&nbsp;'):
       
   444         """Format a section with a heading."""
       
   445         if marginalia is None:
       
   446             marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
       
   447         result = '''<p>
       
   448 <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
       
   449 <tr bgcolor="%s">
       
   450 <td colspan=3 valign=bottom>&nbsp;<br>
       
   451 <font color="%s" face="helvetica, arial">%s</font></td></tr>
       
   452     ''' % (bgcol, fgcol, title)
       
   453         if prelude:
       
   454             result = result + '''
       
   455 <tr bgcolor="%s"><td rowspan=2>%s</td>
       
   456 <td colspan=2>%s</td></tr>
       
   457 <tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
       
   458         else:
       
   459             result = result + '''
       
   460 <tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
       
   461 
       
   462         return result + '\n<td width="100%%">%s</td></tr></table>' % contents
       
   463 
       
   464     def bigsection(self, title, *args):
       
   465         """Format a section with a big heading."""
       
   466         title = '<big><strong>%s</strong></big>' % title
       
   467         return self.section(title, *args)
       
   468 
       
   469     def preformat(self, text):
       
   470         """Format literal preformatted text."""
       
   471         text = self.escape(expandtabs(text))
       
   472         return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
       
   473                              ' ', '&nbsp;', '\n', '<br>\n')
       
   474 
       
   475     def multicolumn(self, list, format, cols=4):
       
   476         """Format a list of items into a multi-column list."""
       
   477         result = ''
       
   478         rows = (len(list)+cols-1)/cols
       
   479         for col in range(cols):
       
   480             result = result + '<td width="%d%%" valign=top>' % (100/cols)
       
   481             for i in range(rows*col, rows*col+rows):
       
   482                 if i < len(list):
       
   483                     result = result + format(list[i]) + '<br>\n'
       
   484             result = result + '</td>'
       
   485         return '<table width="100%%" summary="list"><tr>%s</tr></table>' % result
       
   486 
       
   487     def grey(self, text): return '<font color="#909090">%s</font>' % text
       
   488 
       
   489     def namelink(self, name, *dicts):
       
   490         """Make a link for an identifier, given name-to-URL mappings."""
       
   491         for dict in dicts:
       
   492             if name in dict:
       
   493                 return '<a href="%s">%s</a>' % (dict[name], name)
       
   494         return name
       
   495 
       
   496     def classlink(self, object, modname):
       
   497         """Make a link for a class."""
       
   498         name, module = object.__name__, sys.modules.get(object.__module__)
       
   499         if hasattr(module, name) and getattr(module, name) is object:
       
   500             return '<a href="%s.html#%s">%s</a>' % (
       
   501                 module.__name__, name, classname(object, modname))
       
   502         return classname(object, modname)
       
   503 
       
   504     def modulelink(self, object):
       
   505         """Make a link for a module."""
       
   506         return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
       
   507 
       
   508     def modpkglink(self, data):
       
   509         """Make a link for a module or package to display in an index."""
       
   510         name, path, ispackage, shadowed = data
       
   511         if shadowed:
       
   512             return self.grey(name)
       
   513         if path:
       
   514             url = '%s.%s.html' % (path, name)
       
   515         else:
       
   516             url = '%s.html' % name
       
   517         if ispackage:
       
   518             text = '<strong>%s</strong>&nbsp;(package)' % name
       
   519         else:
       
   520             text = name
       
   521         return '<a href="%s">%s</a>' % (url, text)
       
   522 
       
   523     def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
       
   524         """Mark up some plain text, given a context of symbols to look for.
       
   525         Each context dictionary maps object names to anchor names."""
       
   526         escape = escape or self.escape
       
   527         results = []
       
   528         here = 0
       
   529         pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
       
   530                                 r'RFC[- ]?(\d+)|'
       
   531                                 r'PEP[- ]?(\d+)|'
       
   532                                 r'(self\.)?(\w+))')
       
   533         while True:
       
   534             match = pattern.search(text, here)
       
   535             if not match: break
       
   536             start, end = match.span()
       
   537             results.append(escape(text[here:start]))
       
   538 
       
   539             all, scheme, rfc, pep, selfdot, name = match.groups()
       
   540             if scheme:
       
   541                 url = escape(all).replace('"', '&quot;')
       
   542                 results.append('<a href="%s">%s</a>' % (url, url))
       
   543             elif rfc:
       
   544                 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
       
   545                 results.append('<a href="%s">%s</a>' % (url, escape(all)))
       
   546             elif pep:
       
   547                 url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep)
       
   548                 results.append('<a href="%s">%s</a>' % (url, escape(all)))
       
   549             elif text[end:end+1] == '(':
       
   550                 results.append(self.namelink(name, methods, funcs, classes))
       
   551             elif selfdot:
       
   552                 results.append('self.<strong>%s</strong>' % name)
       
   553             else:
       
   554                 results.append(self.namelink(name, classes))
       
   555             here = end
       
   556         results.append(escape(text[here:]))
       
   557         return join(results, '')
       
   558 
       
   559     # ---------------------------------------------- type-specific routines
       
   560 
       
   561     def formattree(self, tree, modname, parent=None):
       
   562         """Produce HTML for a class tree as given by inspect.getclasstree()."""
       
   563         result = ''
       
   564         for entry in tree:
       
   565             if type(entry) is type(()):
       
   566                 c, bases = entry
       
   567                 result = result + '<dt><font face="helvetica, arial">'
       
   568                 result = result + self.classlink(c, modname)
       
   569                 if bases and bases != (parent,):
       
   570                     parents = []
       
   571                     for base in bases:
       
   572                         parents.append(self.classlink(base, modname))
       
   573                     result = result + '(' + join(parents, ', ') + ')'
       
   574                 result = result + '\n</font></dt>'
       
   575             elif type(entry) is type([]):
       
   576                 result = result + '<dd>\n%s</dd>\n' % self.formattree(
       
   577                     entry, modname, c)
       
   578         return '<dl>\n%s</dl>\n' % result
       
   579 
       
   580     def docmodule(self, object, name=None, mod=None, *ignored):
       
   581         """Produce HTML documentation for a module object."""
       
   582         name = object.__name__ # ignore the passed-in name
       
   583         try:
       
   584             all = object.__all__
       
   585         except AttributeError:
       
   586             all = None
       
   587         parts = split(name, '.')
       
   588         links = []
       
   589         for i in range(len(parts)-1):
       
   590             links.append(
       
   591                 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
       
   592                 (join(parts[:i+1], '.'), parts[i]))
       
   593         linkedname = join(links + parts[-1:], '.')
       
   594         head = '<big><big><strong>%s</strong></big></big>' % linkedname
       
   595         try:
       
   596             path = inspect.getabsfile(object)
       
   597             url = path
       
   598             if sys.platform == 'win32':
       
   599                 import nturl2path
       
   600                 url = nturl2path.pathname2url(path)
       
   601             filelink = '<a href="file:%s">%s</a>' % (url, path)
       
   602         except TypeError:
       
   603             filelink = '(built-in)'
       
   604         info = []
       
   605         if hasattr(object, '__version__'):
       
   606             version = str(object.__version__)
       
   607             if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
       
   608                 version = strip(version[11:-1])
       
   609             info.append('version %s' % self.escape(version))
       
   610         if hasattr(object, '__date__'):
       
   611             info.append(self.escape(str(object.__date__)))
       
   612         if info:
       
   613             head = head + ' (%s)' % join(info, ', ')
       
   614         docloc = self.getdocloc(object)
       
   615         if docloc is not None:
       
   616             docloc = '<br><a href="%(docloc)s">Module Docs</a>' % locals()
       
   617         else:
       
   618             docloc = ''
       
   619         result = self.heading(
       
   620             head, '#ffffff', '#7799ee',
       
   621             '<a href=".">index</a><br>' + filelink + docloc)
       
   622 
       
   623         modules = inspect.getmembers(object, inspect.ismodule)
       
   624 
       
   625         classes, cdict = [], {}
       
   626         for key, value in inspect.getmembers(object, inspect.isclass):
       
   627             # if __all__ exists, believe it.  Otherwise use old heuristic.
       
   628             if (all is not None or
       
   629                 (inspect.getmodule(value) or object) is object):
       
   630                 if visiblename(key, all):
       
   631                     classes.append((key, value))
       
   632                     cdict[key] = cdict[value] = '#' + key
       
   633         for key, value in classes:
       
   634             for base in value.__bases__:
       
   635                 key, modname = base.__name__, base.__module__
       
   636                 module = sys.modules.get(modname)
       
   637                 if modname != name and module and hasattr(module, key):
       
   638                     if getattr(module, key) is base:
       
   639                         if not key in cdict:
       
   640                             cdict[key] = cdict[base] = modname + '.html#' + key
       
   641         funcs, fdict = [], {}
       
   642         for key, value in inspect.getmembers(object, inspect.isroutine):
       
   643             # if __all__ exists, believe it.  Otherwise use old heuristic.
       
   644             if (all is not None or
       
   645                 inspect.isbuiltin(value) or inspect.getmodule(value) is object):
       
   646                 if visiblename(key, all):
       
   647                     funcs.append((key, value))
       
   648                     fdict[key] = '#-' + key
       
   649                     if inspect.isfunction(value): fdict[value] = fdict[key]
       
   650         data = []
       
   651         for key, value in inspect.getmembers(object, isdata):
       
   652             if visiblename(key, all):
       
   653                 data.append((key, value))
       
   654 
       
   655         doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
       
   656         doc = doc and '<tt>%s</tt>' % doc
       
   657         result = result + '<p>%s</p>\n' % doc
       
   658 
       
   659         if hasattr(object, '__path__'):
       
   660             modpkgs = []
       
   661             for importer, modname, ispkg in pkgutil.iter_modules(object.__path__):
       
   662                 modpkgs.append((modname, name, ispkg, 0))
       
   663             modpkgs.sort()
       
   664             contents = self.multicolumn(modpkgs, self.modpkglink)
       
   665             result = result + self.bigsection(
       
   666                 'Package Contents', '#ffffff', '#aa55cc', contents)
       
   667         elif modules:
       
   668             contents = self.multicolumn(
       
   669                 modules, lambda key_value, s=self: s.modulelink(key_value[1]))
       
   670             result = result + self.bigsection(
       
   671                 'Modules', '#ffffff', '#aa55cc', contents)
       
   672 
       
   673         if classes:
       
   674             classlist = map(lambda key_value: key_value[1], classes)
       
   675             contents = [
       
   676                 self.formattree(inspect.getclasstree(classlist, 1), name)]
       
   677             for key, value in classes:
       
   678                 contents.append(self.document(value, key, name, fdict, cdict))
       
   679             result = result + self.bigsection(
       
   680                 'Classes', '#ffffff', '#ee77aa', join(contents))
       
   681         if funcs:
       
   682             contents = []
       
   683             for key, value in funcs:
       
   684                 contents.append(self.document(value, key, name, fdict, cdict))
       
   685             result = result + self.bigsection(
       
   686                 'Functions', '#ffffff', '#eeaa77', join(contents))
       
   687         if data:
       
   688             contents = []
       
   689             for key, value in data:
       
   690                 contents.append(self.document(value, key))
       
   691             result = result + self.bigsection(
       
   692                 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))
       
   693         if hasattr(object, '__author__'):
       
   694             contents = self.markup(str(object.__author__), self.preformat)
       
   695             result = result + self.bigsection(
       
   696                 'Author', '#ffffff', '#7799ee', contents)
       
   697         if hasattr(object, '__credits__'):
       
   698             contents = self.markup(str(object.__credits__), self.preformat)
       
   699             result = result + self.bigsection(
       
   700                 'Credits', '#ffffff', '#7799ee', contents)
       
   701 
       
   702         return result
       
   703 
       
   704     def docclass(self, object, name=None, mod=None, funcs={}, classes={},
       
   705                  *ignored):
       
   706         """Produce HTML documentation for a class object."""
       
   707         realname = object.__name__
       
   708         name = name or realname
       
   709         bases = object.__bases__
       
   710 
       
   711         contents = []
       
   712         push = contents.append
       
   713 
       
   714         # Cute little class to pump out a horizontal rule between sections.
       
   715         class HorizontalRule:
       
   716             def __init__(self):
       
   717                 self.needone = 0
       
   718             def maybe(self):
       
   719                 if self.needone:
       
   720                     push('<hr>\n')
       
   721                 self.needone = 1
       
   722         hr = HorizontalRule()
       
   723 
       
   724         # List the mro, if non-trivial.
       
   725         mro = deque(inspect.getmro(object))
       
   726         if len(mro) > 2:
       
   727             hr.maybe()
       
   728             push('<dl><dt>Method resolution order:</dt>\n')
       
   729             for base in mro:
       
   730                 push('<dd>%s</dd>\n' % self.classlink(base,
       
   731                                                       object.__module__))
       
   732             push('</dl>\n')
       
   733 
       
   734         def spill(msg, attrs, predicate):
       
   735             ok, attrs = _split_list(attrs, predicate)
       
   736             if ok:
       
   737                 hr.maybe()
       
   738                 push(msg)
       
   739                 for name, kind, homecls, value in ok:
       
   740                     push(self.document(getattr(object, name), name, mod,
       
   741                                        funcs, classes, mdict, object))
       
   742                     push('\n')
       
   743             return attrs
       
   744 
       
   745         def spilldescriptors(msg, attrs, predicate):
       
   746             ok, attrs = _split_list(attrs, predicate)
       
   747             if ok:
       
   748                 hr.maybe()
       
   749                 push(msg)
       
   750                 for name, kind, homecls, value in ok:
       
   751                     push(self._docdescriptor(name, value, mod))
       
   752             return attrs
       
   753 
       
   754         def spilldata(msg, attrs, predicate):
       
   755             ok, attrs = _split_list(attrs, predicate)
       
   756             if ok:
       
   757                 hr.maybe()
       
   758                 push(msg)
       
   759                 for name, kind, homecls, value in ok:
       
   760                     base = self.docother(getattr(object, name), name, mod)
       
   761                     if (hasattr(value, '__call__') or
       
   762                             inspect.isdatadescriptor(value)):
       
   763                         doc = getattr(value, "__doc__", None)
       
   764                     else:
       
   765                         doc = None
       
   766                     if doc is None:
       
   767                         push('<dl><dt>%s</dl>\n' % base)
       
   768                     else:
       
   769                         doc = self.markup(getdoc(value), self.preformat,
       
   770                                           funcs, classes, mdict)
       
   771                         doc = '<dd><tt>%s</tt>' % doc
       
   772                         push('<dl><dt>%s%s</dl>\n' % (base, doc))
       
   773                     push('\n')
       
   774             return attrs
       
   775 
       
   776         attrs = filter(lambda data: visiblename(data[0]),
       
   777                        classify_class_attrs(object))
       
   778         mdict = {}
       
   779         for key, kind, homecls, value in attrs:
       
   780             mdict[key] = anchor = '#' + name + '-' + key
       
   781             value = getattr(object, key)
       
   782             try:
       
   783                 # The value may not be hashable (e.g., a data attr with
       
   784                 # a dict or list value).
       
   785                 mdict[value] = anchor
       
   786             except TypeError:
       
   787                 pass
       
   788 
       
   789         while attrs:
       
   790             if mro:
       
   791                 thisclass = mro.popleft()
       
   792             else:
       
   793                 thisclass = attrs[0][2]
       
   794             attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
       
   795 
       
   796             if thisclass is __builtin__.object:
       
   797                 attrs = inherited
       
   798                 continue
       
   799             elif thisclass is object:
       
   800                 tag = 'defined here'
       
   801             else:
       
   802                 tag = 'inherited from %s' % self.classlink(thisclass,
       
   803                                                            object.__module__)
       
   804             tag += ':<br>\n'
       
   805 
       
   806             # Sort attrs by name.
       
   807             try:
       
   808                 attrs.sort(key=lambda t: t[0])
       
   809             except TypeError:
       
   810                 attrs.sort(lambda t1, t2: cmp(t1[0], t2[0]))    # 2.3 compat
       
   811 
       
   812             # Pump out the attrs, segregated by kind.
       
   813             attrs = spill('Methods %s' % tag, attrs,
       
   814                           lambda t: t[1] == 'method')
       
   815             attrs = spill('Class methods %s' % tag, attrs,
       
   816                           lambda t: t[1] == 'class method')
       
   817             attrs = spill('Static methods %s' % tag, attrs,
       
   818                           lambda t: t[1] == 'static method')
       
   819             attrs = spilldescriptors('Data descriptors %s' % tag, attrs,
       
   820                                      lambda t: t[1] == 'data descriptor')
       
   821             attrs = spilldata('Data and other attributes %s' % tag, attrs,
       
   822                               lambda t: t[1] == 'data')
       
   823             assert attrs == []
       
   824             attrs = inherited
       
   825 
       
   826         contents = ''.join(contents)
       
   827 
       
   828         if name == realname:
       
   829             title = '<a name="%s">class <strong>%s</strong></a>' % (
       
   830                 name, realname)
       
   831         else:
       
   832             title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
       
   833                 name, name, realname)
       
   834         if bases:
       
   835             parents = []
       
   836             for base in bases:
       
   837                 parents.append(self.classlink(base, object.__module__))
       
   838             title = title + '(%s)' % join(parents, ', ')
       
   839         doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict)
       
   840         doc = doc and '<tt>%s<br>&nbsp;</tt>' % doc
       
   841 
       
   842         return self.section(title, '#000000', '#ffc8d8', contents, 3, doc)
       
   843 
       
   844     def formatvalue(self, object):
       
   845         """Format an argument default value as text."""
       
   846         return self.grey('=' + self.repr(object))
       
   847 
       
   848     def docroutine(self, object, name=None, mod=None,
       
   849                    funcs={}, classes={}, methods={}, cl=None):
       
   850         """Produce HTML documentation for a function or method object."""
       
   851         realname = object.__name__
       
   852         name = name or realname
       
   853         anchor = (cl and cl.__name__ or '') + '-' + name
       
   854         note = ''
       
   855         skipdocs = 0
       
   856         if inspect.ismethod(object):
       
   857             imclass = object.im_class
       
   858             if cl:
       
   859                 if imclass is not cl:
       
   860                     note = ' from ' + self.classlink(imclass, mod)
       
   861             else:
       
   862                 if object.im_self is not None:
       
   863                     note = ' method of %s instance' % self.classlink(
       
   864                         object.im_self.__class__, mod)
       
   865                 else:
       
   866                     note = ' unbound %s method' % self.classlink(imclass,mod)
       
   867             object = object.im_func
       
   868 
       
   869         if name == realname:
       
   870             title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
       
   871         else:
       
   872             if (cl and realname in cl.__dict__ and
       
   873                 cl.__dict__[realname] is object):
       
   874                 reallink = '<a href="#%s">%s</a>' % (
       
   875                     cl.__name__ + '-' + realname, realname)
       
   876                 skipdocs = 1
       
   877             else:
       
   878                 reallink = realname
       
   879             title = '<a name="%s"><strong>%s</strong></a> = %s' % (
       
   880                 anchor, name, reallink)
       
   881         if inspect.isfunction(object):
       
   882             args, varargs, varkw, defaults = inspect.getargspec(object)
       
   883             argspec = inspect.formatargspec(
       
   884                 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
       
   885             if realname == '<lambda>':
       
   886                 title = '<strong>%s</strong> <em>lambda</em> ' % name
       
   887                 argspec = argspec[1:-1] # remove parentheses
       
   888         else:
       
   889             argspec = '(...)'
       
   890 
       
   891         decl = title + argspec + (note and self.grey(
       
   892                '<font face="helvetica, arial">%s</font>' % note))
       
   893 
       
   894         if skipdocs:
       
   895             return '<dl><dt>%s</dt></dl>\n' % decl
       
   896         else:
       
   897             doc = self.markup(
       
   898                 getdoc(object), self.preformat, funcs, classes, methods)
       
   899             doc = doc and '<dd><tt>%s</tt></dd>' % doc
       
   900             return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
       
   901 
       
   902     def _docdescriptor(self, name, value, mod):
       
   903         results = []
       
   904         push = results.append
       
   905 
       
   906         if name:
       
   907             push('<dl><dt><strong>%s</strong></dt>\n' % name)
       
   908         if value.__doc__ is not None:
       
   909             doc = self.markup(getdoc(value), self.preformat)
       
   910             push('<dd><tt>%s</tt></dd>\n' % doc)
       
   911         push('</dl>\n')
       
   912 
       
   913         return ''.join(results)
       
   914 
       
   915     def docproperty(self, object, name=None, mod=None, cl=None):
       
   916         """Produce html documentation for a property."""
       
   917         return self._docdescriptor(name, object, mod)
       
   918 
       
   919     def docother(self, object, name=None, mod=None, *ignored):
       
   920         """Produce HTML documentation for a data object."""
       
   921         lhs = name and '<strong>%s</strong> = ' % name or ''
       
   922         return lhs + self.repr(object)
       
   923 
       
   924     def docdata(self, object, name=None, mod=None, cl=None):
       
   925         """Produce html documentation for a data descriptor."""
       
   926         return self._docdescriptor(name, object, mod)
       
   927 
       
   928     def index(self, dir, shadowed=None):
       
   929         """Generate an HTML index for a directory of modules."""
       
   930         modpkgs = []
       
   931         if shadowed is None: shadowed = {}
       
   932         for importer, name, ispkg in pkgutil.iter_modules([dir]):
       
   933             modpkgs.append((name, '', ispkg, name in shadowed))
       
   934             shadowed[name] = 1
       
   935 
       
   936         modpkgs.sort()
       
   937         contents = self.multicolumn(modpkgs, self.modpkglink)
       
   938         return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
       
   939 
       
   940 # -------------------------------------------- text documentation generator
       
   941 
       
   942 class TextRepr(Repr):
       
   943     """Class for safely making a text representation of a Python object."""
       
   944     def __init__(self):
       
   945         Repr.__init__(self)
       
   946         self.maxlist = self.maxtuple = 20
       
   947         self.maxdict = 10
       
   948         self.maxstring = self.maxother = 100
       
   949 
       
   950     def repr1(self, x, level):
       
   951         if hasattr(type(x), '__name__'):
       
   952             methodname = 'repr_' + join(split(type(x).__name__), '_')
       
   953             if hasattr(self, methodname):
       
   954                 return getattr(self, methodname)(x, level)
       
   955         return cram(stripid(repr(x)), self.maxother)
       
   956 
       
   957     def repr_string(self, x, level):
       
   958         test = cram(x, self.maxstring)
       
   959         testrepr = repr(test)
       
   960         if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
       
   961             # Backslashes are only literal in the string and are never
       
   962             # needed to make any special characters, so show a raw string.
       
   963             return 'r' + testrepr[0] + test + testrepr[0]
       
   964         return testrepr
       
   965 
       
   966     repr_str = repr_string
       
   967 
       
   968     def repr_instance(self, x, level):
       
   969         try:
       
   970             return cram(stripid(repr(x)), self.maxstring)
       
   971         except:
       
   972             return '<%s instance>' % x.__class__.__name__
       
   973 
       
   974 class TextDoc(Doc):
       
   975     """Formatter class for text documentation."""
       
   976 
       
   977     # ------------------------------------------- text formatting utilities
       
   978 
       
   979     _repr_instance = TextRepr()
       
   980     repr = _repr_instance.repr
       
   981 
       
   982     def bold(self, text):
       
   983         """Format a string in bold by overstriking."""
       
   984         return join(map(lambda ch: ch + '\b' + ch, text), '')
       
   985 
       
   986     def indent(self, text, prefix='    '):
       
   987         """Indent text by prepending a given prefix to each line."""
       
   988         if not text: return ''
       
   989         lines = split(text, '\n')
       
   990         lines = map(lambda line, prefix=prefix: prefix + line, lines)
       
   991         if lines: lines[-1] = rstrip(lines[-1])
       
   992         return join(lines, '\n')
       
   993 
       
   994     def section(self, title, contents):
       
   995         """Format a section with a given heading."""
       
   996         return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
       
   997 
       
   998     # ---------------------------------------------- type-specific routines
       
   999 
       
  1000     def formattree(self, tree, modname, parent=None, prefix=''):
       
  1001         """Render in text a class tree as returned by inspect.getclasstree()."""
       
  1002         result = ''
       
  1003         for entry in tree:
       
  1004             if type(entry) is type(()):
       
  1005                 c, bases = entry
       
  1006                 result = result + prefix + classname(c, modname)
       
  1007                 if bases and bases != (parent,):
       
  1008                     parents = map(lambda c, m=modname: classname(c, m), bases)
       
  1009                     result = result + '(%s)' % join(parents, ', ')
       
  1010                 result = result + '\n'
       
  1011             elif type(entry) is type([]):
       
  1012                 result = result + self.formattree(
       
  1013                     entry, modname, c, prefix + '    ')
       
  1014         return result
       
  1015 
       
  1016     def docmodule(self, object, name=None, mod=None):
       
  1017         """Produce text documentation for a given module object."""
       
  1018         name = object.__name__ # ignore the passed-in name
       
  1019         synop, desc = splitdoc(getdoc(object))
       
  1020         result = self.section('NAME', name + (synop and ' - ' + synop))
       
  1021 
       
  1022         try:
       
  1023             all = object.__all__
       
  1024         except AttributeError:
       
  1025             all = None
       
  1026 
       
  1027         try:
       
  1028             file = inspect.getabsfile(object)
       
  1029         except TypeError:
       
  1030             file = '(built-in)'
       
  1031         result = result + self.section('FILE', file)
       
  1032 
       
  1033         docloc = self.getdocloc(object)
       
  1034         if docloc is not None:
       
  1035             result = result + self.section('MODULE DOCS', docloc)
       
  1036 
       
  1037         if desc:
       
  1038             result = result + self.section('DESCRIPTION', desc)
       
  1039 
       
  1040         classes = []
       
  1041         for key, value in inspect.getmembers(object, inspect.isclass):
       
  1042             # if __all__ exists, believe it.  Otherwise use old heuristic.
       
  1043             if (all is not None
       
  1044                 or (inspect.getmodule(value) or object) is object):
       
  1045                 if visiblename(key, all):
       
  1046                     classes.append((key, value))
       
  1047         funcs = []
       
  1048         for key, value in inspect.getmembers(object, inspect.isroutine):
       
  1049             # if __all__ exists, believe it.  Otherwise use old heuristic.
       
  1050             if (all is not None or
       
  1051                 inspect.isbuiltin(value) or inspect.getmodule(value) is object):
       
  1052                 if visiblename(key, all):
       
  1053                     funcs.append((key, value))
       
  1054         data = []
       
  1055         for key, value in inspect.getmembers(object, isdata):
       
  1056             if visiblename(key, all):
       
  1057                 data.append((key, value))
       
  1058 
       
  1059         modpkgs = []
       
  1060         modpkgs_names = set()
       
  1061         if hasattr(object, '__path__'):
       
  1062             for importer, modname, ispkg in pkgutil.iter_modules(object.__path__):
       
  1063                 modpkgs_names.add(modname)
       
  1064                 if ispkg:
       
  1065                     modpkgs.append(modname + ' (package)')
       
  1066                 else:
       
  1067                     modpkgs.append(modname)
       
  1068 
       
  1069             modpkgs.sort()
       
  1070             result = result + self.section(
       
  1071                 'PACKAGE CONTENTS', join(modpkgs, '\n'))
       
  1072 
       
  1073         # Detect submodules as sometimes created by C extensions
       
  1074         submodules = []
       
  1075         for key, value in inspect.getmembers(object, inspect.ismodule):
       
  1076             if value.__name__.startswith(name + '.') and key not in modpkgs_names:
       
  1077                 submodules.append(key)
       
  1078         if submodules:
       
  1079             submodules.sort()
       
  1080             result = result + self.section(
       
  1081                 'SUBMODULES', join(submodules, '\n'))
       
  1082 
       
  1083         if classes:
       
  1084             classlist = map(lambda key_value: key_value[1], classes)
       
  1085             contents = [self.formattree(
       
  1086                 inspect.getclasstree(classlist, 1), name)]
       
  1087             for key, value in classes:
       
  1088                 contents.append(self.document(value, key, name))
       
  1089             result = result + self.section('CLASSES', join(contents, '\n'))
       
  1090 
       
  1091         if funcs:
       
  1092             contents = []
       
  1093             for key, value in funcs:
       
  1094                 contents.append(self.document(value, key, name))
       
  1095             result = result + self.section('FUNCTIONS', join(contents, '\n'))
       
  1096 
       
  1097         if data:
       
  1098             contents = []
       
  1099             for key, value in data:
       
  1100                 contents.append(self.docother(value, key, name, maxlen=70))
       
  1101             result = result + self.section('DATA', join(contents, '\n'))
       
  1102 
       
  1103         if hasattr(object, '__version__'):
       
  1104             version = str(object.__version__)
       
  1105             if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
       
  1106                 version = strip(version[11:-1])
       
  1107             result = result + self.section('VERSION', version)
       
  1108         if hasattr(object, '__date__'):
       
  1109             result = result + self.section('DATE', str(object.__date__))
       
  1110         if hasattr(object, '__author__'):
       
  1111             result = result + self.section('AUTHOR', str(object.__author__))
       
  1112         if hasattr(object, '__credits__'):
       
  1113             result = result + self.section('CREDITS', str(object.__credits__))
       
  1114         return result
       
  1115 
       
  1116     def docclass(self, object, name=None, mod=None):
       
  1117         """Produce text documentation for a given class object."""
       
  1118         realname = object.__name__
       
  1119         name = name or realname
       
  1120         bases = object.__bases__
       
  1121 
       
  1122         def makename(c, m=object.__module__):
       
  1123             return classname(c, m)
       
  1124 
       
  1125         if name == realname:
       
  1126             title = 'class ' + self.bold(realname)
       
  1127         else:
       
  1128             title = self.bold(name) + ' = class ' + realname
       
  1129         if bases:
       
  1130             parents = map(makename, bases)
       
  1131             title = title + '(%s)' % join(parents, ', ')
       
  1132 
       
  1133         doc = getdoc(object)
       
  1134         contents = doc and [doc + '\n'] or []
       
  1135         push = contents.append
       
  1136 
       
  1137         # List the mro, if non-trivial.
       
  1138         mro = deque(inspect.getmro(object))
       
  1139         if len(mro) > 2:
       
  1140             push("Method resolution order:")
       
  1141             for base in mro:
       
  1142                 push('    ' + makename(base))
       
  1143             push('')
       
  1144 
       
  1145         # Cute little class to pump out a horizontal rule between sections.
       
  1146         class HorizontalRule:
       
  1147             def __init__(self):
       
  1148                 self.needone = 0
       
  1149             def maybe(self):
       
  1150                 if self.needone:
       
  1151                     push('-' * 70)
       
  1152                 self.needone = 1
       
  1153         hr = HorizontalRule()
       
  1154 
       
  1155         def spill(msg, attrs, predicate):
       
  1156             ok, attrs = _split_list(attrs, predicate)
       
  1157             if ok:
       
  1158                 hr.maybe()
       
  1159                 push(msg)
       
  1160                 for name, kind, homecls, value in ok:
       
  1161                     push(self.document(getattr(object, name),
       
  1162                                        name, mod, object))
       
  1163             return attrs
       
  1164 
       
  1165         def spilldescriptors(msg, attrs, predicate):
       
  1166             ok, attrs = _split_list(attrs, predicate)
       
  1167             if ok:
       
  1168                 hr.maybe()
       
  1169                 push(msg)
       
  1170                 for name, kind, homecls, value in ok:
       
  1171                     push(self._docdescriptor(name, value, mod))
       
  1172             return attrs
       
  1173 
       
  1174         def spilldata(msg, attrs, predicate):
       
  1175             ok, attrs = _split_list(attrs, predicate)
       
  1176             if ok:
       
  1177                 hr.maybe()
       
  1178                 push(msg)
       
  1179                 for name, kind, homecls, value in ok:
       
  1180                     if (hasattr(value, '__call__') or
       
  1181                             inspect.isdatadescriptor(value)):
       
  1182                         doc = getdoc(value)
       
  1183                     else:
       
  1184                         doc = None
       
  1185                     push(self.docother(getattr(object, name),
       
  1186                                        name, mod, maxlen=70, doc=doc) + '\n')
       
  1187             return attrs
       
  1188 
       
  1189         attrs = filter(lambda data: visiblename(data[0]),
       
  1190                        classify_class_attrs(object))
       
  1191         while attrs:
       
  1192             if mro:
       
  1193                 thisclass = mro.popleft()
       
  1194             else:
       
  1195                 thisclass = attrs[0][2]
       
  1196             attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
       
  1197 
       
  1198             if thisclass is __builtin__.object:
       
  1199                 attrs = inherited
       
  1200                 continue
       
  1201             elif thisclass is object:
       
  1202                 tag = "defined here"
       
  1203             else:
       
  1204                 tag = "inherited from %s" % classname(thisclass,
       
  1205                                                       object.__module__)
       
  1206 
       
  1207             # Sort attrs by name.
       
  1208             attrs.sort()
       
  1209 
       
  1210             # Pump out the attrs, segregated by kind.
       
  1211             attrs = spill("Methods %s:\n" % tag, attrs,
       
  1212                           lambda t: t[1] == 'method')
       
  1213             attrs = spill("Class methods %s:\n" % tag, attrs,
       
  1214                           lambda t: t[1] == 'class method')
       
  1215             attrs = spill("Static methods %s:\n" % tag, attrs,
       
  1216                           lambda t: t[1] == 'static method')
       
  1217             attrs = spilldescriptors("Data descriptors %s:\n" % tag, attrs,
       
  1218                                      lambda t: t[1] == 'data descriptor')
       
  1219             attrs = spilldata("Data and other attributes %s:\n" % tag, attrs,
       
  1220                               lambda t: t[1] == 'data')
       
  1221             assert attrs == []
       
  1222             attrs = inherited
       
  1223 
       
  1224         contents = '\n'.join(contents)
       
  1225         if not contents:
       
  1226             return title + '\n'
       
  1227         return title + '\n' + self.indent(rstrip(contents), ' |  ') + '\n'
       
  1228 
       
  1229     def formatvalue(self, object):
       
  1230         """Format an argument default value as text."""
       
  1231         return '=' + self.repr(object)
       
  1232 
       
  1233     def docroutine(self, object, name=None, mod=None, cl=None):
       
  1234         """Produce text documentation for a function or method object."""
       
  1235         realname = object.__name__
       
  1236         name = name or realname
       
  1237         note = ''
       
  1238         skipdocs = 0
       
  1239         if inspect.ismethod(object):
       
  1240             imclass = object.im_class
       
  1241             if cl:
       
  1242                 if imclass is not cl:
       
  1243                     note = ' from ' + classname(imclass, mod)
       
  1244             else:
       
  1245                 if object.im_self is not None:
       
  1246                     note = ' method of %s instance' % classname(
       
  1247                         object.im_self.__class__, mod)
       
  1248                 else:
       
  1249                     note = ' unbound %s method' % classname(imclass,mod)
       
  1250             object = object.im_func
       
  1251 
       
  1252         if name == realname:
       
  1253             title = self.bold(realname)
       
  1254         else:
       
  1255             if (cl and realname in cl.__dict__ and
       
  1256                 cl.__dict__[realname] is object):
       
  1257                 skipdocs = 1
       
  1258             title = self.bold(name) + ' = ' + realname
       
  1259         if inspect.isfunction(object):
       
  1260             args, varargs, varkw, defaults = inspect.getargspec(object)
       
  1261             argspec = inspect.formatargspec(
       
  1262                 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
       
  1263             if realname == '<lambda>':
       
  1264                 title = self.bold(name) + ' lambda '
       
  1265                 argspec = argspec[1:-1] # remove parentheses
       
  1266         else:
       
  1267             argspec = '(...)'
       
  1268         decl = title + argspec + note
       
  1269 
       
  1270         if skipdocs:
       
  1271             return decl + '\n'
       
  1272         else:
       
  1273             doc = getdoc(object) or ''
       
  1274             return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
       
  1275 
       
  1276     def _docdescriptor(self, name, value, mod):
       
  1277         results = []
       
  1278         push = results.append
       
  1279 
       
  1280         if name:
       
  1281             push(self.bold(name))
       
  1282             push('\n')
       
  1283         doc = getdoc(value) or ''
       
  1284         if doc:
       
  1285             push(self.indent(doc))
       
  1286             push('\n')
       
  1287         return ''.join(results)
       
  1288 
       
  1289     def docproperty(self, object, name=None, mod=None, cl=None):
       
  1290         """Produce text documentation for a property."""
       
  1291         return self._docdescriptor(name, object, mod)
       
  1292 
       
  1293     def docdata(self, object, name=None, mod=None, cl=None):
       
  1294         """Produce text documentation for a data descriptor."""
       
  1295         return self._docdescriptor(name, object, mod)
       
  1296 
       
  1297     def docother(self, object, name=None, mod=None, parent=None, maxlen=None, doc=None):
       
  1298         """Produce text documentation for a data object."""
       
  1299         repr = self.repr(object)
       
  1300         if maxlen:
       
  1301             line = (name and name + ' = ' or '') + repr
       
  1302             chop = maxlen - len(line)
       
  1303             if chop < 0: repr = repr[:chop] + '...'
       
  1304         line = (name and self.bold(name) + ' = ' or '') + repr
       
  1305         if doc is not None:
       
  1306             line += '\n' + self.indent(str(doc))
       
  1307         return line
       
  1308 
       
  1309 # --------------------------------------------------------- user interfaces
       
  1310 
       
  1311 def pager(text):
       
  1312     """The first time this is called, determine what kind of pager to use."""
       
  1313     global pager
       
  1314     pager = getpager()
       
  1315     pager(text)
       
  1316 
       
  1317 def getpager():
       
  1318     """Decide what method to use for paging through text."""
       
  1319     if type(sys.stdout) is not types.FileType:
       
  1320         return plainpager
       
  1321     if not sys.stdin.isatty() or not sys.stdout.isatty():
       
  1322         return plainpager
       
  1323     if 'PAGER' in os.environ:
       
  1324         if sys.platform == 'win32': # pipes completely broken in Windows
       
  1325             return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
       
  1326         elif os.environ.get('TERM') in ('dumb', 'emacs'):
       
  1327             return lambda text: pipepager(plain(text), os.environ['PAGER'])
       
  1328         else:
       
  1329             return lambda text: pipepager(text, os.environ['PAGER'])
       
  1330     if os.environ.get('TERM') in ('dumb', 'emacs'):
       
  1331         return plainpager
       
  1332     if sys.platform == 'win32' or sys.platform.startswith('os2'):
       
  1333         return lambda text: tempfilepager(plain(text), 'more <')
       
  1334     if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0:
       
  1335         return lambda text: pipepager(text, 'less')
       
  1336 
       
  1337     import tempfile
       
  1338     (fd, filename) = tempfile.mkstemp()
       
  1339     os.close(fd)
       
  1340     try:
       
  1341         if hasattr(os, 'system') and os.system('more "%s"' % filename) == 0:
       
  1342             return lambda text: pipepager(text, 'more')
       
  1343         else:
       
  1344             return ttypager
       
  1345     finally:
       
  1346         os.unlink(filename)
       
  1347 
       
  1348 def plain(text):
       
  1349     """Remove boldface formatting from text."""
       
  1350     return re.sub('.\b', '', text)
       
  1351 
       
  1352 def pipepager(text, cmd):
       
  1353     """Page through text by feeding it to another program."""
       
  1354     pipe = os.popen(cmd, 'w')
       
  1355     try:
       
  1356         pipe.write(text)
       
  1357         pipe.close()
       
  1358     except IOError:
       
  1359         pass # Ignore broken pipes caused by quitting the pager program.
       
  1360 
       
  1361 def tempfilepager(text, cmd):
       
  1362     """Page through text by invoking a program on a temporary file."""
       
  1363     import tempfile
       
  1364     filename = tempfile.mktemp()
       
  1365     file = open(filename, 'w')
       
  1366     file.write(text)
       
  1367     file.close()
       
  1368     try:
       
  1369         os.system(cmd + ' "' + filename + '"')
       
  1370     finally:
       
  1371         os.unlink(filename)
       
  1372 
       
  1373 def ttypager(text):
       
  1374     """Page through text on a text terminal."""
       
  1375     lines = split(plain(text), '\n')
       
  1376     try:
       
  1377         import tty
       
  1378         fd = sys.stdin.fileno()
       
  1379         old = tty.tcgetattr(fd)
       
  1380         tty.setcbreak(fd)
       
  1381         getchar = lambda: sys.stdin.read(1)
       
  1382     except (ImportError, AttributeError):
       
  1383         tty = None
       
  1384         getchar = lambda: sys.stdin.readline()[:-1][:1]
       
  1385 
       
  1386     try:
       
  1387         r = inc = os.environ.get('LINES', 25) - 1
       
  1388         sys.stdout.write(join(lines[:inc], '\n') + '\n')
       
  1389         while lines[r:]:
       
  1390             sys.stdout.write('-- more --')
       
  1391             sys.stdout.flush()
       
  1392             c = getchar()
       
  1393 
       
  1394             if c in ('q', 'Q'):
       
  1395                 sys.stdout.write('\r          \r')
       
  1396                 break
       
  1397             elif c in ('\r', '\n'):
       
  1398                 sys.stdout.write('\r          \r' + lines[r] + '\n')
       
  1399                 r = r + 1
       
  1400                 continue
       
  1401             if c in ('b', 'B', '\x1b'):
       
  1402                 r = r - inc - inc
       
  1403                 if r < 0: r = 0
       
  1404             sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
       
  1405             r = r + inc
       
  1406 
       
  1407     finally:
       
  1408         if tty:
       
  1409             tty.tcsetattr(fd, tty.TCSAFLUSH, old)
       
  1410 
       
  1411 def plainpager(text):
       
  1412     """Simply print unformatted text.  This is the ultimate fallback."""
       
  1413     sys.stdout.write(plain(text))
       
  1414 
       
  1415 def describe(thing):
       
  1416     """Produce a short description of the given thing."""
       
  1417     if inspect.ismodule(thing):
       
  1418         if thing.__name__ in sys.builtin_module_names:
       
  1419             return 'built-in module ' + thing.__name__
       
  1420         if hasattr(thing, '__path__'):
       
  1421             return 'package ' + thing.__name__
       
  1422         else:
       
  1423             return 'module ' + thing.__name__
       
  1424     if inspect.isbuiltin(thing):
       
  1425         return 'built-in function ' + thing.__name__
       
  1426     if inspect.isgetsetdescriptor(thing):
       
  1427         return 'getset descriptor %s.%s.%s' % (
       
  1428             thing.__objclass__.__module__, thing.__objclass__.__name__,
       
  1429             thing.__name__)
       
  1430     if inspect.ismemberdescriptor(thing):
       
  1431         return 'member descriptor %s.%s.%s' % (
       
  1432             thing.__objclass__.__module__, thing.__objclass__.__name__,
       
  1433             thing.__name__)
       
  1434     if inspect.isclass(thing):
       
  1435         return 'class ' + thing.__name__
       
  1436     if inspect.isfunction(thing):
       
  1437         return 'function ' + thing.__name__
       
  1438     if inspect.ismethod(thing):
       
  1439         return 'method ' + thing.__name__
       
  1440     if type(thing) is types.InstanceType:
       
  1441         return 'instance of ' + thing.__class__.__name__
       
  1442     return type(thing).__name__
       
  1443 
       
  1444 def locate(path, forceload=0):
       
  1445     """Locate an object by name or dotted path, importing as necessary."""
       
  1446     parts = [part for part in split(path, '.') if part]
       
  1447     module, n = None, 0
       
  1448     while n < len(parts):
       
  1449         nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
       
  1450         if nextmodule: module, n = nextmodule, n + 1
       
  1451         else: break
       
  1452     if module:
       
  1453         object = module
       
  1454         for part in parts[n:]:
       
  1455             try: object = getattr(object, part)
       
  1456             except AttributeError: return None
       
  1457         return object
       
  1458     else:
       
  1459         if hasattr(__builtin__, path):
       
  1460             return getattr(__builtin__, path)
       
  1461 
       
  1462 # --------------------------------------- interactive interpreter interface
       
  1463 
       
  1464 text = TextDoc()
       
  1465 html = HTMLDoc()
       
  1466 
       
  1467 class _OldStyleClass: pass
       
  1468 _OLD_INSTANCE_TYPE = type(_OldStyleClass())
       
  1469 
       
  1470 def resolve(thing, forceload=0):
       
  1471     """Given an object or a path to an object, get the object and its name."""
       
  1472     if isinstance(thing, str):
       
  1473         object = locate(thing, forceload)
       
  1474         if not object:
       
  1475             raise ImportError, 'no Python documentation found for %r' % thing
       
  1476         return object, thing
       
  1477     else:
       
  1478         return thing, getattr(thing, '__name__', None)
       
  1479 
       
  1480 def render_doc(thing, title='Python Library Documentation: %s', forceload=0):
       
  1481     """Render text documentation, given an object or a path to an object."""
       
  1482     object, name = resolve(thing, forceload)
       
  1483     desc = describe(object)
       
  1484     module = inspect.getmodule(object)
       
  1485     if name and '.' in name:
       
  1486         desc += ' in ' + name[:name.rfind('.')]
       
  1487     elif module and module is not object:
       
  1488         desc += ' in module ' + module.__name__
       
  1489     if type(object) is _OLD_INSTANCE_TYPE:
       
  1490         # If the passed object is an instance of an old-style class,
       
  1491         # document its available methods instead of its value.
       
  1492         object = object.__class__
       
  1493     elif not (inspect.ismodule(object) or
       
  1494               inspect.isclass(object) or
       
  1495               inspect.isroutine(object) or
       
  1496               inspect.isgetsetdescriptor(object) or
       
  1497               inspect.ismemberdescriptor(object) or
       
  1498               isinstance(object, property)):
       
  1499         # If the passed object is a piece of data or an instance,
       
  1500         # document its available methods instead of its value.
       
  1501         object = type(object)
       
  1502         desc += ' object'
       
  1503     return title % desc + '\n\n' + text.document(object, name)
       
  1504 
       
  1505 def doc(thing, title='Python Library Documentation: %s', forceload=0):
       
  1506     """Display text documentation, given an object or a path to an object."""
       
  1507     try:
       
  1508         pager(render_doc(thing, title, forceload))
       
  1509     except (ImportError, ErrorDuringImport), value:
       
  1510         print value
       
  1511 
       
  1512 def writedoc(thing, forceload=0):
       
  1513     """Write HTML documentation to a file in the current directory."""
       
  1514     try:
       
  1515         object, name = resolve(thing, forceload)
       
  1516         page = html.page(describe(object), html.document(object, name))
       
  1517         file = open(name + '.html', 'w')
       
  1518         file.write(page)
       
  1519         file.close()
       
  1520         print 'wrote', name + '.html'
       
  1521     except (ImportError, ErrorDuringImport), value:
       
  1522         print value
       
  1523 
       
  1524 def writedocs(dir, pkgpath='', done=None):
       
  1525     """Write out HTML documentation for all modules in a directory tree."""
       
  1526     if done is None: done = {}
       
  1527     for importer, modname, ispkg in pkgutil.walk_packages([dir], pkgpath):
       
  1528         writedoc(modname)
       
  1529     return
       
  1530 
       
  1531 class Helper:
       
  1532 
       
  1533     # These dictionaries map a topic name to either an alias, or a tuple
       
  1534     # (label, seealso-items).  The "label" is the label of the corresponding
       
  1535     # section in the .rst file under Doc/ and an index into the dictionary
       
  1536     # in pydoc_topics.py.
       
  1537     #
       
  1538     # CAUTION: if you change one of these dictionaries, be sure to adapt the
       
  1539     #          list of needed labels in Doc/tools/sphinxext/pyspecific.py and
       
  1540     #          regenerate the pydoc_topics.py file by running
       
  1541     #              make pydoc-topics
       
  1542     #          in Doc/ and copying the output file into the Lib/ directory.
       
  1543 
       
  1544     keywords = {
       
  1545         'and': 'BOOLEAN',
       
  1546         'as': 'with',
       
  1547         'assert': ('assert', ''),
       
  1548         'break': ('break', 'while for'),
       
  1549         'class': ('class', 'CLASSES SPECIALMETHODS'),
       
  1550         'continue': ('continue', 'while for'),
       
  1551         'def': ('function', ''),
       
  1552         'del': ('del', 'BASICMETHODS'),
       
  1553         'elif': 'if',
       
  1554         'else': ('else', 'while for'),
       
  1555         'except': 'try',
       
  1556         'exec': ('exec', ''),
       
  1557         'finally': 'try',
       
  1558         'for': ('for', 'break continue while'),
       
  1559         'from': 'import',
       
  1560         'global': ('global', 'NAMESPACES'),
       
  1561         'if': ('if', 'TRUTHVALUE'),
       
  1562         'import': ('import', 'MODULES'),
       
  1563         'in': ('in', 'SEQUENCEMETHODS2'),
       
  1564         'is': 'COMPARISON',
       
  1565         'lambda': ('lambda', 'FUNCTIONS'),
       
  1566         'not': 'BOOLEAN',
       
  1567         'or': 'BOOLEAN',
       
  1568         'pass': ('pass', ''),
       
  1569         'print': ('print', ''),
       
  1570         'raise': ('raise', 'EXCEPTIONS'),
       
  1571         'return': ('return', 'FUNCTIONS'),
       
  1572         'try': ('try', 'EXCEPTIONS'),
       
  1573         'while': ('while', 'break continue if TRUTHVALUE'),
       
  1574         'with': ('with', 'CONTEXTMANAGERS EXCEPTIONS yield'),
       
  1575         'yield': ('yield', ''),
       
  1576     }
       
  1577 
       
  1578     topics = {
       
  1579         'TYPES': ('types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS '
       
  1580                   'FUNCTIONS CLASSES MODULES FILES inspect'),
       
  1581         'STRINGS': ('strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING '
       
  1582                     'TYPES'),
       
  1583         'STRINGMETHODS': ('string-methods', 'STRINGS FORMATTING'),
       
  1584         'FORMATTING': ('formatstrings', 'OPERATORS'),
       
  1585         'UNICODE': ('strings', 'encodings unicode SEQUENCES STRINGMETHODS '
       
  1586                     'FORMATTING TYPES'),
       
  1587         'NUMBERS': ('numbers', 'INTEGER FLOAT COMPLEX TYPES'),
       
  1588         'INTEGER': ('integers', 'int range'),
       
  1589         'FLOAT': ('floating', 'float math'),
       
  1590         'COMPLEX': ('imaginary', 'complex cmath'),
       
  1591         'SEQUENCES': ('typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
       
  1592         'MAPPINGS': 'DICTIONARIES',
       
  1593         'FUNCTIONS': ('typesfunctions', 'def TYPES'),
       
  1594         'METHODS': ('typesmethods', 'class def CLASSES TYPES'),
       
  1595         'CODEOBJECTS': ('bltin-code-objects', 'compile FUNCTIONS TYPES'),
       
  1596         'TYPEOBJECTS': ('bltin-type-objects', 'types TYPES'),
       
  1597         'FRAMEOBJECTS': 'TYPES',
       
  1598         'TRACEBACKS': 'TYPES',
       
  1599         'NONE': ('bltin-null-object', ''),
       
  1600         'ELLIPSIS': ('bltin-ellipsis-object', 'SLICINGS'),
       
  1601         'FILES': ('bltin-file-objects', ''),
       
  1602         'SPECIALATTRIBUTES': ('specialattrs', ''),
       
  1603         'CLASSES': ('types', 'class SPECIALMETHODS PRIVATENAMES'),
       
  1604         'MODULES': ('typesmodules', 'import'),
       
  1605         'PACKAGES': 'import',
       
  1606         'EXPRESSIONS': ('operator-summary', 'lambda or and not in is BOOLEAN '
       
  1607                         'COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER '
       
  1608                         'UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES '
       
  1609                         'LISTS DICTIONARIES BACKQUOTES'),
       
  1610         'OPERATORS': 'EXPRESSIONS',
       
  1611         'PRECEDENCE': 'EXPRESSIONS',
       
  1612         'OBJECTS': ('objects', 'TYPES'),
       
  1613         'SPECIALMETHODS': ('specialnames', 'BASICMETHODS ATTRIBUTEMETHODS '
       
  1614                            'CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS '
       
  1615                            'SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
       
  1616         'BASICMETHODS': ('customization', 'cmp hash repr str SPECIALMETHODS'),
       
  1617         'ATTRIBUTEMETHODS': ('attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
       
  1618         'CALLABLEMETHODS': ('callable-types', 'CALLS SPECIALMETHODS'),
       
  1619         'SEQUENCEMETHODS1': ('sequence-types', 'SEQUENCES SEQUENCEMETHODS2 '
       
  1620                              'SPECIALMETHODS'),
       
  1621         'SEQUENCEMETHODS2': ('sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 '
       
  1622                              'SPECIALMETHODS'),
       
  1623         'MAPPINGMETHODS': ('sequence-types', 'MAPPINGS SPECIALMETHODS'),
       
  1624         'NUMBERMETHODS': ('numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT '
       
  1625                           'SPECIALMETHODS'),
       
  1626         'EXECUTION': ('execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'),
       
  1627         'NAMESPACES': ('naming', 'global ASSIGNMENT DELETION DYNAMICFEATURES'),
       
  1628         'DYNAMICFEATURES': ('dynamic-features', ''),
       
  1629         'SCOPING': 'NAMESPACES',
       
  1630         'FRAMES': 'NAMESPACES',
       
  1631         'EXCEPTIONS': ('exceptions', 'try except finally raise'),
       
  1632         'COERCIONS': ('coercion-rules','CONVERSIONS'),
       
  1633         'CONVERSIONS': ('conversions', 'COERCIONS'),
       
  1634         'IDENTIFIERS': ('identifiers', 'keywords SPECIALIDENTIFIERS'),
       
  1635         'SPECIALIDENTIFIERS': ('id-classes', ''),
       
  1636         'PRIVATENAMES': ('atom-identifiers', ''),
       
  1637         'LITERALS': ('atom-literals', 'STRINGS BACKQUOTES NUMBERS '
       
  1638                      'TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
       
  1639         'TUPLES': 'SEQUENCES',
       
  1640         'TUPLELITERALS': ('exprlists', 'TUPLES LITERALS'),
       
  1641         'LISTS': ('typesseq-mutable', 'LISTLITERALS'),
       
  1642         'LISTLITERALS': ('lists', 'LISTS LITERALS'),
       
  1643         'DICTIONARIES': ('typesmapping', 'DICTIONARYLITERALS'),
       
  1644         'DICTIONARYLITERALS': ('dict', 'DICTIONARIES LITERALS'),
       
  1645         'BACKQUOTES': ('string-conversions', 'repr str STRINGS LITERALS'),
       
  1646         'ATTRIBUTES': ('attribute-references', 'getattr hasattr setattr '
       
  1647                        'ATTRIBUTEMETHODS'),
       
  1648         'SUBSCRIPTS': ('subscriptions', 'SEQUENCEMETHODS1'),
       
  1649         'SLICINGS': ('slicings', 'SEQUENCEMETHODS2'),
       
  1650         'CALLS': ('calls', 'EXPRESSIONS'),
       
  1651         'POWER': ('power', 'EXPRESSIONS'),
       
  1652         'UNARY': ('unary', 'EXPRESSIONS'),
       
  1653         'BINARY': ('binary', 'EXPRESSIONS'),
       
  1654         'SHIFTING': ('shifting', 'EXPRESSIONS'),
       
  1655         'BITWISE': ('bitwise', 'EXPRESSIONS'),
       
  1656         'COMPARISON': ('comparisons', 'EXPRESSIONS BASICMETHODS'),
       
  1657         'BOOLEAN': ('booleans', 'EXPRESSIONS TRUTHVALUE'),
       
  1658         'ASSERTION': 'assert',
       
  1659         'ASSIGNMENT': ('assignment', 'AUGMENTEDASSIGNMENT'),
       
  1660         'AUGMENTEDASSIGNMENT': ('augassign', 'NUMBERMETHODS'),
       
  1661         'DELETION': 'del',
       
  1662         'PRINTING': 'print',
       
  1663         'RETURNING': 'return',
       
  1664         'IMPORTING': 'import',
       
  1665         'CONDITIONAL': 'if',
       
  1666         'LOOPING': ('compound', 'for while break continue'),
       
  1667         'TRUTHVALUE': ('truth', 'if while and or not BASICMETHODS'),
       
  1668         'DEBUGGING': ('debugger', 'pdb'),
       
  1669         'CONTEXTMANAGERS': ('context-managers', 'with'),
       
  1670     }
       
  1671 
       
  1672     def __init__(self, input, output):
       
  1673         self.input = input
       
  1674         self.output = output
       
  1675 
       
  1676     def __repr__(self):
       
  1677         if inspect.stack()[1][3] == '?':
       
  1678             self()
       
  1679             return ''
       
  1680         return '<pydoc.Helper instance>'
       
  1681 
       
  1682     def __call__(self, request=None):
       
  1683         if request is not None:
       
  1684             self.help(request)
       
  1685         else:
       
  1686             self.intro()
       
  1687             self.interact()
       
  1688             self.output.write('''
       
  1689 You are now leaving help and returning to the Python interpreter.
       
  1690 If you want to ask for help on a particular object directly from the
       
  1691 interpreter, you can type "help(object)".  Executing "help('string')"
       
  1692 has the same effect as typing a particular string at the help> prompt.
       
  1693 ''')
       
  1694 
       
  1695     def interact(self):
       
  1696         self.output.write('\n')
       
  1697         while True:
       
  1698             try:
       
  1699                 request = self.getline('help> ')
       
  1700                 if not request: break
       
  1701             except (KeyboardInterrupt, EOFError):
       
  1702                 break
       
  1703             request = strip(replace(request, '"', '', "'", ''))
       
  1704             if lower(request) in ('q', 'quit'): break
       
  1705             self.help(request)
       
  1706 
       
  1707     def getline(self, prompt):
       
  1708         """Read one line, using raw_input when available."""
       
  1709         if self.input is sys.stdin:
       
  1710             return raw_input(prompt)
       
  1711         else:
       
  1712             self.output.write(prompt)
       
  1713             self.output.flush()
       
  1714             return self.input.readline()
       
  1715 
       
  1716     def help(self, request):
       
  1717         if type(request) is type(''):
       
  1718             if request == 'help': self.intro()
       
  1719             elif request == 'keywords': self.listkeywords()
       
  1720             elif request == 'topics': self.listtopics()
       
  1721             elif request == 'modules': self.listmodules()
       
  1722             elif request[:8] == 'modules ':
       
  1723                 self.listmodules(split(request)[1])
       
  1724             elif request in self.keywords: self.showtopic(request)
       
  1725             elif request in self.topics: self.showtopic(request)
       
  1726             elif request: doc(request, 'Help on %s:')
       
  1727         elif isinstance(request, Helper): self()
       
  1728         else: doc(request, 'Help on %s:')
       
  1729         self.output.write('\n')
       
  1730 
       
  1731     def intro(self):
       
  1732         self.output.write('''
       
  1733 Welcome to Python %s!  This is the online help utility.
       
  1734 
       
  1735 If this is your first time using Python, you should definitely check out
       
  1736 the tutorial on the Internet at http://docs.python.org/tutorial/.
       
  1737 
       
  1738 Enter the name of any module, keyword, or topic to get help on writing
       
  1739 Python programs and using Python modules.  To quit this help utility and
       
  1740 return to the interpreter, just type "quit".
       
  1741 
       
  1742 To get a list of available modules, keywords, or topics, type "modules",
       
  1743 "keywords", or "topics".  Each module also comes with a one-line summary
       
  1744 of what it does; to list the modules whose summaries contain a given word
       
  1745 such as "spam", type "modules spam".
       
  1746 ''' % sys.version[:3])
       
  1747 
       
  1748     def list(self, items, columns=4, width=80):
       
  1749         items = items[:]
       
  1750         items.sort()
       
  1751         colw = width / columns
       
  1752         rows = (len(items) + columns - 1) / columns
       
  1753         for row in range(rows):
       
  1754             for col in range(columns):
       
  1755                 i = col * rows + row
       
  1756                 if i < len(items):
       
  1757                     self.output.write(items[i])
       
  1758                     if col < columns - 1:
       
  1759                         self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
       
  1760             self.output.write('\n')
       
  1761 
       
  1762     def listkeywords(self):
       
  1763         self.output.write('''
       
  1764 Here is a list of the Python keywords.  Enter any keyword to get more help.
       
  1765 
       
  1766 ''')
       
  1767         self.list(self.keywords.keys())
       
  1768 
       
  1769     def listtopics(self):
       
  1770         self.output.write('''
       
  1771 Here is a list of available topics.  Enter any topic name to get more help.
       
  1772 
       
  1773 ''')
       
  1774         self.list(self.topics.keys())
       
  1775 
       
  1776     def showtopic(self, topic):
       
  1777         try:
       
  1778             import pydoc_topics
       
  1779         except ImportError:
       
  1780             self.output.write('''
       
  1781 Sorry, topic and keyword documentation is not available because the
       
  1782 module "pydoc_topics" could not be found.
       
  1783 ''')
       
  1784             return
       
  1785         target = self.topics.get(topic, self.keywords.get(topic))
       
  1786         if not target:
       
  1787             self.output.write('no documentation found for %s\n' % repr(topic))
       
  1788             return
       
  1789         if type(target) is type(''):
       
  1790             return self.showtopic(target)
       
  1791 
       
  1792         label, xrefs = target
       
  1793         try:
       
  1794             doc = pydoc_topics.topics[label]
       
  1795         except KeyError:
       
  1796             self.output.write('no documentation found for %s\n' % repr(topic))
       
  1797             return
       
  1798         pager(strip(doc) + '\n')
       
  1799         if xrefs:
       
  1800             import StringIO, formatter
       
  1801             buffer = StringIO.StringIO()
       
  1802             formatter.DumbWriter(buffer).send_flowing_data(
       
  1803                 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
       
  1804             self.output.write('\n%s\n' % buffer.getvalue())
       
  1805 
       
  1806     def listmodules(self, key=''):
       
  1807         if key:
       
  1808             self.output.write('''
       
  1809 Here is a list of matching modules.  Enter any module name to get more help.
       
  1810 
       
  1811 ''')
       
  1812             apropos(key)
       
  1813         else:
       
  1814             self.output.write('''
       
  1815 Please wait a moment while I gather a list of all available modules...
       
  1816 
       
  1817 ''')
       
  1818             modules = {}
       
  1819             def callback(path, modname, desc, modules=modules):
       
  1820                 if modname and modname[-9:] == '.__init__':
       
  1821                     modname = modname[:-9] + ' (package)'
       
  1822                 if find(modname, '.') < 0:
       
  1823                     modules[modname] = 1
       
  1824             def onerror(modname):
       
  1825                 callback(None, modname, None)
       
  1826             ModuleScanner().run(callback, onerror=onerror)
       
  1827             self.list(modules.keys())
       
  1828             self.output.write('''
       
  1829 Enter any module name to get more help.  Or, type "modules spam" to search
       
  1830 for modules whose descriptions contain the word "spam".
       
  1831 ''')
       
  1832 
       
  1833 help = Helper(sys.stdin, sys.stdout)
       
  1834 
       
  1835 class Scanner:
       
  1836     """A generic tree iterator."""
       
  1837     def __init__(self, roots, children, descendp):
       
  1838         self.roots = roots[:]
       
  1839         self.state = []
       
  1840         self.children = children
       
  1841         self.descendp = descendp
       
  1842 
       
  1843     def next(self):
       
  1844         if not self.state:
       
  1845             if not self.roots:
       
  1846                 return None
       
  1847             root = self.roots.pop(0)
       
  1848             self.state = [(root, self.children(root))]
       
  1849         node, children = self.state[-1]
       
  1850         if not children:
       
  1851             self.state.pop()
       
  1852             return self.next()
       
  1853         child = children.pop(0)
       
  1854         if self.descendp(child):
       
  1855             self.state.append((child, self.children(child)))
       
  1856         return child
       
  1857 
       
  1858 
       
  1859 class ModuleScanner:
       
  1860     """An interruptible scanner that searches module synopses."""
       
  1861 
       
  1862     def run(self, callback, key=None, completer=None, onerror=None):
       
  1863         if key: key = lower(key)
       
  1864         self.quit = False
       
  1865         seen = {}
       
  1866 
       
  1867         for modname in sys.builtin_module_names:
       
  1868             if modname != '__main__':
       
  1869                 seen[modname] = 1
       
  1870                 if key is None:
       
  1871                     callback(None, modname, '')
       
  1872                 else:
       
  1873                     desc = split(__import__(modname).__doc__ or '', '\n')[0]
       
  1874                     if find(lower(modname + ' - ' + desc), key) >= 0:
       
  1875                         callback(None, modname, desc)
       
  1876 
       
  1877         for importer, modname, ispkg in pkgutil.walk_packages(onerror=onerror):
       
  1878             if self.quit:
       
  1879                 break
       
  1880             if key is None:
       
  1881                 callback(None, modname, '')
       
  1882             else:
       
  1883                 loader = importer.find_module(modname)
       
  1884                 if hasattr(loader,'get_source'):
       
  1885                     import StringIO
       
  1886                     desc = source_synopsis(
       
  1887                         StringIO.StringIO(loader.get_source(modname))
       
  1888                     ) or ''
       
  1889                     if hasattr(loader,'get_filename'):
       
  1890                         path = loader.get_filename(modname)
       
  1891                     else:
       
  1892                         path = None
       
  1893                 else:
       
  1894                     module = loader.load_module(modname)
       
  1895                     desc = (module.__doc__ or '').splitlines()[0]
       
  1896                     path = getattr(module,'__file__',None)
       
  1897                 if find(lower(modname + ' - ' + desc), key) >= 0:
       
  1898                     callback(path, modname, desc)
       
  1899 
       
  1900         if completer:
       
  1901             completer()
       
  1902 
       
  1903 def apropos(key):
       
  1904     """Print all the one-line module summaries that contain a substring."""
       
  1905     def callback(path, modname, desc):
       
  1906         if modname[-9:] == '.__init__':
       
  1907             modname = modname[:-9] + ' (package)'
       
  1908         print modname, desc and '- ' + desc
       
  1909     try: import warnings
       
  1910     except ImportError: pass
       
  1911     else: warnings.filterwarnings('ignore') # ignore problems during import
       
  1912     ModuleScanner().run(callback, key)
       
  1913 
       
  1914 # --------------------------------------------------- web browser interface
       
  1915 
       
  1916 def serve(port, callback=None, completer=None):
       
  1917     import BaseHTTPServer, mimetools, select
       
  1918 
       
  1919     # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
       
  1920     class Message(mimetools.Message):
       
  1921         def __init__(self, fp, seekable=1):
       
  1922             Message = self.__class__
       
  1923             Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
       
  1924             self.encodingheader = self.getheader('content-transfer-encoding')
       
  1925             self.typeheader = self.getheader('content-type')
       
  1926             self.parsetype()
       
  1927             self.parseplist()
       
  1928 
       
  1929     class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
       
  1930         def send_document(self, title, contents):
       
  1931             try:
       
  1932                 self.send_response(200)
       
  1933                 self.send_header('Content-Type', 'text/html')
       
  1934                 self.end_headers()
       
  1935                 self.wfile.write(html.page(title, contents))
       
  1936             except IOError: pass
       
  1937 
       
  1938         def do_GET(self):
       
  1939             path = self.path
       
  1940             if path[-5:] == '.html': path = path[:-5]
       
  1941             if path[:1] == '/': path = path[1:]
       
  1942             if path and path != '.':
       
  1943                 try:
       
  1944                     obj = locate(path, forceload=1)
       
  1945                 except ErrorDuringImport, value:
       
  1946                     self.send_document(path, html.escape(str(value)))
       
  1947                     return
       
  1948                 if obj:
       
  1949                     self.send_document(describe(obj), html.document(obj, path))
       
  1950                 else:
       
  1951                     self.send_document(path,
       
  1952 'no Python documentation found for %s' % repr(path))
       
  1953             else:
       
  1954                 heading = html.heading(
       
  1955 '<big><big><strong>Python: Index of Modules</strong></big></big>',
       
  1956 '#ffffff', '#7799ee')
       
  1957                 def bltinlink(name):
       
  1958                     return '<a href="%s.html">%s</a>' % (name, name)
       
  1959                 names = filter(lambda x: x != '__main__',
       
  1960                                sys.builtin_module_names)
       
  1961                 contents = html.multicolumn(names, bltinlink)
       
  1962                 indices = ['<p>' + html.bigsection(
       
  1963                     'Built-in Modules', '#ffffff', '#ee77aa', contents)]
       
  1964 
       
  1965                 seen = {}
       
  1966                 for dir in sys.path:
       
  1967                     indices.append(html.index(dir, seen))
       
  1968                 contents = heading + join(indices) + '''<p align=right>
       
  1969 <font color="#909090" face="helvetica, arial"><strong>
       
  1970 pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font>'''
       
  1971                 self.send_document('Index of Modules', contents)
       
  1972 
       
  1973         def log_message(self, *args): pass
       
  1974 
       
  1975     class DocServer(BaseHTTPServer.HTTPServer):
       
  1976         def __init__(self, port, callback):
       
  1977             host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
       
  1978             self.address = ('', port)
       
  1979             self.url = 'http://%s:%d/' % (host, port)
       
  1980             self.callback = callback
       
  1981             self.base.__init__(self, self.address, self.handler)
       
  1982 
       
  1983         def serve_until_quit(self):
       
  1984             import select
       
  1985             self.quit = False
       
  1986             while not self.quit:
       
  1987                 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
       
  1988                 if rd: self.handle_request()
       
  1989 
       
  1990         def server_activate(self):
       
  1991             self.base.server_activate(self)
       
  1992             if self.callback: self.callback(self)
       
  1993 
       
  1994     DocServer.base = BaseHTTPServer.HTTPServer
       
  1995     DocServer.handler = DocHandler
       
  1996     DocHandler.MessageClass = Message
       
  1997     try:
       
  1998         try:
       
  1999             DocServer(port, callback).serve_until_quit()
       
  2000         except (KeyboardInterrupt, select.error):
       
  2001             pass
       
  2002     finally:
       
  2003         if completer: completer()
       
  2004 
       
  2005 # ----------------------------------------------------- graphical interface
       
  2006 
       
  2007 def gui():
       
  2008     """Graphical interface (starts web server and pops up a control window)."""
       
  2009     class GUI:
       
  2010         def __init__(self, window, port=7464):
       
  2011             self.window = window
       
  2012             self.server = None
       
  2013             self.scanner = None
       
  2014 
       
  2015             import Tkinter
       
  2016             self.server_frm = Tkinter.Frame(window)
       
  2017             self.title_lbl = Tkinter.Label(self.server_frm,
       
  2018                 text='Starting server...\n ')
       
  2019             self.open_btn = Tkinter.Button(self.server_frm,
       
  2020                 text='open browser', command=self.open, state='disabled')
       
  2021             self.quit_btn = Tkinter.Button(self.server_frm,
       
  2022                 text='quit serving', command=self.quit, state='disabled')
       
  2023 
       
  2024             self.search_frm = Tkinter.Frame(window)
       
  2025             self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
       
  2026             self.search_ent = Tkinter.Entry(self.search_frm)
       
  2027             self.search_ent.bind('<Return>', self.search)
       
  2028             self.stop_btn = Tkinter.Button(self.search_frm,
       
  2029                 text='stop', pady=0, command=self.stop, state='disabled')
       
  2030             if sys.platform == 'win32':
       
  2031                 # Trying to hide and show this button crashes under Windows.
       
  2032                 self.stop_btn.pack(side='right')
       
  2033 
       
  2034             self.window.title('pydoc')
       
  2035             self.window.protocol('WM_DELETE_WINDOW', self.quit)
       
  2036             self.title_lbl.pack(side='top', fill='x')
       
  2037             self.open_btn.pack(side='left', fill='x', expand=1)
       
  2038             self.quit_btn.pack(side='right', fill='x', expand=1)
       
  2039             self.server_frm.pack(side='top', fill='x')
       
  2040 
       
  2041             self.search_lbl.pack(side='left')
       
  2042             self.search_ent.pack(side='right', fill='x', expand=1)
       
  2043             self.search_frm.pack(side='top', fill='x')
       
  2044             self.search_ent.focus_set()
       
  2045 
       
  2046             font = ('helvetica', sys.platform == 'win32' and 8 or 10)
       
  2047             self.result_lst = Tkinter.Listbox(window, font=font, height=6)
       
  2048             self.result_lst.bind('<Button-1>', self.select)
       
  2049             self.result_lst.bind('<Double-Button-1>', self.goto)
       
  2050             self.result_scr = Tkinter.Scrollbar(window,
       
  2051                 orient='vertical', command=self.result_lst.yview)
       
  2052             self.result_lst.config(yscrollcommand=self.result_scr.set)
       
  2053 
       
  2054             self.result_frm = Tkinter.Frame(window)
       
  2055             self.goto_btn = Tkinter.Button(self.result_frm,
       
  2056                 text='go to selected', command=self.goto)
       
  2057             self.hide_btn = Tkinter.Button(self.result_frm,
       
  2058                 text='hide results', command=self.hide)
       
  2059             self.goto_btn.pack(side='left', fill='x', expand=1)
       
  2060             self.hide_btn.pack(side='right', fill='x', expand=1)
       
  2061 
       
  2062             self.window.update()
       
  2063             self.minwidth = self.window.winfo_width()
       
  2064             self.minheight = self.window.winfo_height()
       
  2065             self.bigminheight = (self.server_frm.winfo_reqheight() +
       
  2066                                  self.search_frm.winfo_reqheight() +
       
  2067                                  self.result_lst.winfo_reqheight() +
       
  2068                                  self.result_frm.winfo_reqheight())
       
  2069             self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
       
  2070             self.expanded = 0
       
  2071             self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
       
  2072             self.window.wm_minsize(self.minwidth, self.minheight)
       
  2073             self.window.tk.willdispatch()
       
  2074 
       
  2075             import threading
       
  2076             threading.Thread(
       
  2077                 target=serve, args=(port, self.ready, self.quit)).start()
       
  2078 
       
  2079         def ready(self, server):
       
  2080             self.server = server
       
  2081             self.title_lbl.config(
       
  2082                 text='Python documentation server at\n' + server.url)
       
  2083             self.open_btn.config(state='normal')
       
  2084             self.quit_btn.config(state='normal')
       
  2085 
       
  2086         def open(self, event=None, url=None):
       
  2087             url = url or self.server.url
       
  2088             try:
       
  2089                 import webbrowser
       
  2090                 webbrowser.open(url)
       
  2091             except ImportError: # pre-webbrowser.py compatibility
       
  2092                 if sys.platform == 'win32':
       
  2093                     os.system('start "%s"' % url)
       
  2094                 elif sys.platform == 'mac':
       
  2095                     try: import ic
       
  2096                     except ImportError: pass
       
  2097                     else: ic.launchurl(url)
       
  2098                 else:
       
  2099                     rc = os.system('netscape -remote "openURL(%s)" &' % url)
       
  2100                     if rc: os.system('netscape "%s" &' % url)
       
  2101 
       
  2102         def quit(self, event=None):
       
  2103             if self.server:
       
  2104                 self.server.quit = 1
       
  2105             self.window.quit()
       
  2106 
       
  2107         def search(self, event=None):
       
  2108             key = self.search_ent.get()
       
  2109             self.stop_btn.pack(side='right')
       
  2110             self.stop_btn.config(state='normal')
       
  2111             self.search_lbl.config(text='Searching for "%s"...' % key)
       
  2112             self.search_ent.forget()
       
  2113             self.search_lbl.pack(side='left')
       
  2114             self.result_lst.delete(0, 'end')
       
  2115             self.goto_btn.config(state='disabled')
       
  2116             self.expand()
       
  2117 
       
  2118             import threading
       
  2119             if self.scanner:
       
  2120                 self.scanner.quit = 1
       
  2121             self.scanner = ModuleScanner()
       
  2122             threading.Thread(target=self.scanner.run,
       
  2123                              args=(self.update, key, self.done)).start()
       
  2124 
       
  2125         def update(self, path, modname, desc):
       
  2126             if modname[-9:] == '.__init__':
       
  2127                 modname = modname[:-9] + ' (package)'
       
  2128             self.result_lst.insert('end',
       
  2129                 modname + ' - ' + (desc or '(no description)'))
       
  2130 
       
  2131         def stop(self, event=None):
       
  2132             if self.scanner:
       
  2133                 self.scanner.quit = 1
       
  2134                 self.scanner = None
       
  2135 
       
  2136         def done(self):
       
  2137             self.scanner = None
       
  2138             self.search_lbl.config(text='Search for')
       
  2139             self.search_lbl.pack(side='left')
       
  2140             self.search_ent.pack(side='right', fill='x', expand=1)
       
  2141             if sys.platform != 'win32': self.stop_btn.forget()
       
  2142             self.stop_btn.config(state='disabled')
       
  2143 
       
  2144         def select(self, event=None):
       
  2145             self.goto_btn.config(state='normal')
       
  2146 
       
  2147         def goto(self, event=None):
       
  2148             selection = self.result_lst.curselection()
       
  2149             if selection:
       
  2150                 modname = split(self.result_lst.get(selection[0]))[0]
       
  2151                 self.open(url=self.server.url + modname + '.html')
       
  2152 
       
  2153         def collapse(self):
       
  2154             if not self.expanded: return
       
  2155             self.result_frm.forget()
       
  2156             self.result_scr.forget()
       
  2157             self.result_lst.forget()
       
  2158             self.bigwidth = self.window.winfo_width()
       
  2159             self.bigheight = self.window.winfo_height()
       
  2160             self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
       
  2161             self.window.wm_minsize(self.minwidth, self.minheight)
       
  2162             self.expanded = 0
       
  2163 
       
  2164         def expand(self):
       
  2165             if self.expanded: return
       
  2166             self.result_frm.pack(side='bottom', fill='x')
       
  2167             self.result_scr.pack(side='right', fill='y')
       
  2168             self.result_lst.pack(side='top', fill='both', expand=1)
       
  2169             self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
       
  2170             self.window.wm_minsize(self.minwidth, self.bigminheight)
       
  2171             self.expanded = 1
       
  2172 
       
  2173         def hide(self, event=None):
       
  2174             self.stop()
       
  2175             self.collapse()
       
  2176 
       
  2177     import Tkinter
       
  2178     try:
       
  2179         root = Tkinter.Tk()
       
  2180         # Tk will crash if pythonw.exe has an XP .manifest
       
  2181         # file and the root has is not destroyed explicitly.
       
  2182         # If the problem is ever fixed in Tk, the explicit
       
  2183         # destroy can go.
       
  2184         try:
       
  2185             gui = GUI(root)
       
  2186             root.mainloop()
       
  2187         finally:
       
  2188             root.destroy()
       
  2189     except KeyboardInterrupt:
       
  2190         pass
       
  2191 
       
  2192 # -------------------------------------------------- command-line interface
       
  2193 
       
  2194 def ispath(x):
       
  2195     return isinstance(x, str) and find(x, os.sep) >= 0
       
  2196 
       
  2197 def cli():
       
  2198     """Command-line interface (looks at sys.argv to decide what to do)."""
       
  2199     import getopt
       
  2200     class BadUsage: pass
       
  2201 
       
  2202     # Scripts don't get the current directory in their path by default.
       
  2203     scriptdir = os.path.dirname(sys.argv[0])
       
  2204     if scriptdir in sys.path:
       
  2205         sys.path.remove(scriptdir)
       
  2206     sys.path.insert(0, '.')
       
  2207 
       
  2208     try:
       
  2209         opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
       
  2210         writing = 0
       
  2211 
       
  2212         for opt, val in opts:
       
  2213             if opt == '-g':
       
  2214                 gui()
       
  2215                 return
       
  2216             if opt == '-k':
       
  2217                 apropos(val)
       
  2218                 return
       
  2219             if opt == '-p':
       
  2220                 try:
       
  2221                     port = int(val)
       
  2222                 except ValueError:
       
  2223                     raise BadUsage
       
  2224                 def ready(server):
       
  2225                     print 'pydoc server ready at %s' % server.url
       
  2226                 def stopped():
       
  2227                     print 'pydoc server stopped'
       
  2228                 serve(port, ready, stopped)
       
  2229                 return
       
  2230             if opt == '-w':
       
  2231                 writing = 1
       
  2232 
       
  2233         if not args: raise BadUsage
       
  2234         for arg in args:
       
  2235             if ispath(arg) and not os.path.exists(arg):
       
  2236                 print 'file %r does not exist' % arg
       
  2237                 break
       
  2238             try:
       
  2239                 if ispath(arg) and os.path.isfile(arg):
       
  2240                     arg = importfile(arg)
       
  2241                 if writing:
       
  2242                     if ispath(arg) and os.path.isdir(arg):
       
  2243                         writedocs(arg)
       
  2244                     else:
       
  2245                         writedoc(arg)
       
  2246                 else:
       
  2247                     help.help(arg)
       
  2248             except ErrorDuringImport, value:
       
  2249                 print value
       
  2250 
       
  2251     except (getopt.error, BadUsage):
       
  2252         cmd = os.path.basename(sys.argv[0])
       
  2253         print """pydoc - the Python documentation tool
       
  2254 
       
  2255 %s <name> ...
       
  2256     Show text documentation on something.  <name> may be the name of a
       
  2257     Python keyword, topic, function, module, or package, or a dotted
       
  2258     reference to a class or function within a module or module in a
       
  2259     package.  If <name> contains a '%s', it is used as the path to a
       
  2260     Python source file to document. If name is 'keywords', 'topics',
       
  2261     or 'modules', a listing of these things is displayed.
       
  2262 
       
  2263 %s -k <keyword>
       
  2264     Search for a keyword in the synopsis lines of all available modules.
       
  2265 
       
  2266 %s -p <port>
       
  2267     Start an HTTP server on the given port on the local machine.
       
  2268 
       
  2269 %s -g
       
  2270     Pop up a graphical interface for finding and serving documentation.
       
  2271 
       
  2272 %s -w <name> ...
       
  2273     Write out the HTML documentation for a module to a file in the current
       
  2274     directory.  If <name> contains a '%s', it is treated as a filename; if
       
  2275     it names a directory, documentation is written for all the contents.
       
  2276 """ % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
       
  2277 
       
  2278 if __name__ == '__main__': cli()