PyXR

c:\python24\lib \ pydoc.py



0001 #!/usr/bin/env python
0002 # -*- coding: Latin-1 -*-
0003 """Generate Python documentation in HTML or text for interactive use.
0004 
0005 In the Python interpreter, do "from pydoc import help" to provide online
0006 help.  Calling help(thing) on a Python object documents the object.
0007 
0008 Or, at the shell command line outside of Python:
0009 
0010 Run "pydoc <name>" to show documentation on something.  <name> may be
0011 the name of a function, module, package, or a dotted reference to a
0012 class or function within a module or module in a package.  If the
0013 argument contains a path segment delimiter (e.g. slash on Unix,
0014 backslash on Windows) it is treated as the path to a Python source file.
0015 
0016 Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines
0017 of all available modules.
0018 
0019 Run "pydoc -p <port>" to start an HTTP server on a given port on the
0020 local machine to generate documentation web pages.
0021 
0022 For platforms without a command line, "pydoc -g" starts the HTTP server
0023 and also pops up a little window for controlling it.
0024 
0025 Run "pydoc -w <name>" to write out the HTML documentation for a module
0026 to a file named "<name>.html".
0027 
0028 Module docs for core modules are assumed to be in
0029 
0030     http://www.python.org/doc/current/lib/
0031 
0032 This can be overridden by setting the PYTHONDOCS environment variable
0033 to a different URL or to a local directory containing the Library
0034 Reference Manual pages.
0035 """
0036 
0037 __author__ = "Ka-Ping Yee <ping@lfw.org>"
0038 __date__ = "26 February 2001"
0039 __version__ = "$Revision: 1.98 $"
0040 __credits__ = u"""Guido van Rossum, for an excellent programming language.
0041 Tommy Burnette, the original creator of manpy.
0042 Paul Prescod, for all his work on onlinehelp.
0043 Richard Chamberlain, for the first implementation of textdoc.
0044 
0045 Mynd you, møøse bites Kan be pretty nasti..."""
0046 
0047 # Known bugs that can't be fixed here:
0048 #   - imp.load_module() cannot be prevented from clobbering existing
0049 #     loaded modules, so calling synopsis() on a binary module file
0050 #     changes the contents of any existing module with the same name.
0051 #   - If the __file__ attribute on a module is a relative path and
0052 #     the current directory is changed with os.chdir(), an incorrect
0053 #     path will be displayed.
0054 
0055 import sys, imp, os, re, types, inspect, __builtin__
0056 from repr import Repr
0057 from string import expandtabs, find, join, lower, split, strip, rfind, rstrip
0058 from collections import deque
0059 
0060 # --------------------------------------------------------- common routines
0061 
0062 def pathdirs():
0063     """Convert sys.path into a list of absolute, existing, unique paths."""
0064     dirs = []
0065     normdirs = []
0066     for dir in sys.path:
0067         dir = os.path.abspath(dir or '.')
0068         normdir = os.path.normcase(dir)
0069         if normdir not in normdirs and os.path.isdir(dir):
0070             dirs.append(dir)
0071             normdirs.append(normdir)
0072     return dirs
0073 
0074 def getdoc(object):
0075     """Get the doc string or comments for an object."""
0076     result = inspect.getdoc(object) or inspect.getcomments(object)
0077     return result and re.sub('^ *\n', '', rstrip(result)) or ''
0078 
0079 def splitdoc(doc):
0080     """Split a doc string into a synopsis line (if any) and the rest."""
0081     lines = split(strip(doc), '\n')
0082     if len(lines) == 1:
0083         return lines[0], ''
0084     elif len(lines) >= 2 and not rstrip(lines[1]):
0085         return lines[0], join(lines[2:], '\n')
0086     return '', join(lines, '\n')
0087 
0088 def classname(object, modname):
0089     """Get a class name and qualify it with a module name if necessary."""
0090     name = object.__name__
0091     if object.__module__ != modname:
0092         name = object.__module__ + '.' + name
0093     return name
0094 
0095 def isdata(object):
0096     """Check if an object is of a type that probably means it's data."""
0097     return not (inspect.ismodule(object) or inspect.isclass(object) or
0098                 inspect.isroutine(object) or inspect.isframe(object) or
0099                 inspect.istraceback(object) or inspect.iscode(object))
0100 
0101 def replace(text, *pairs):
0102     """Do a series of global replacements on a string."""
0103     while pairs:
0104         text = join(split(text, pairs[0]), pairs[1])
0105         pairs = pairs[2:]
0106     return text
0107 
0108 def cram(text, maxlen):
0109     """Omit part of a string if needed to make it fit in a maximum length."""
0110     if len(text) > maxlen:
0111         pre = max(0, (maxlen-3)//2)
0112         post = max(0, maxlen-3-pre)
0113         return text[:pre] + '...' + text[len(text)-post:]
0114     return text
0115 
0116 _re_stripid = re.compile(r' at 0x[0-9a-f]{6,16}(>+)$', re.IGNORECASE)
0117 def stripid(text):
0118     """Remove the hexadecimal id from a Python object representation."""
0119     # The behaviour of %p is implementation-dependent in terms of case.
0120     if _re_stripid.search(repr(Exception)):
0121         return _re_stripid.sub(r'\1', text)
0122     return text
0123 
0124 def _is_some_method(obj):
0125     return inspect.ismethod(obj) or inspect.ismethoddescriptor(obj)
0126 
0127 def allmethods(cl):
0128     methods = {}
0129     for key, value in inspect.getmembers(cl, _is_some_method):
0130         methods[key] = 1
0131     for base in cl.__bases__:
0132         methods.update(allmethods(base)) # all your base are belong to us
0133     for key in methods.keys():
0134         methods[key] = getattr(cl, key)
0135     return methods
0136 
0137 def _split_list(s, predicate):
0138     """Split sequence s via predicate, and return pair ([true], [false]).
0139 
0140     The return value is a 2-tuple of lists,
0141         ([x for x in s if predicate(x)],
0142          [x for x in s if not predicate(x)])
0143     """
0144 
0145     yes = []
0146     no = []
0147     for x in s:
0148         if predicate(x):
0149             yes.append(x)
0150         else:
0151             no.append(x)
0152     return yes, no
0153 
0154 def visiblename(name, all=None):
0155     """Decide whether to show documentation on a variable."""
0156     # Certain special names are redundant.
0157     if name in ['__builtins__', '__doc__', '__file__', '__path__',
0158                 '__module__', '__name__']: return 0
0159     # Private names are hidden, but special names are displayed.
0160     if name.startswith('__') and name.endswith('__'): return 1
0161     if all is not None:
0162         # only document that which the programmer exported in __all__
0163         return name in all
0164     else:
0165         return not name.startswith('_')
0166 
0167 # ----------------------------------------------------- module manipulation
0168 
0169 def ispackage(path):
0170     """Guess whether a path refers to a package directory."""
0171     if os.path.isdir(path):
0172         for ext in ['.py', '.pyc', '.pyo']:
0173             if os.path.isfile(os.path.join(path, '__init__' + ext)):
0174                 return True
0175     return False
0176 
0177 def synopsis(filename, cache={}):
0178     """Get the one-line summary out of a module file."""
0179     mtime = os.stat(filename).st_mtime
0180     lastupdate, result = cache.get(filename, (0, None))
0181     if lastupdate < mtime:
0182         info = inspect.getmoduleinfo(filename)
0183         file = open(filename)
0184         if info and 'b' in info[2]: # binary modules have to be imported
0185             try: module = imp.load_module('__temp__', file, filename, info[1:])
0186             except: return None
0187             result = split(module.__doc__ or '', '\n')[0]
0188             del sys.modules['__temp__']
0189         else: # text modules can be directly examined
0190             line = file.readline()
0191             while line[:1] == '#' or not strip(line):
0192                 line = file.readline()
0193                 if not line: break
0194             line = strip(line)
0195             if line[:4] == 'r"""': line = line[1:]
0196             if line[:3] == '"""':
0197                 line = line[3:]
0198                 if line[-1:] == '\\': line = line[:-1]
0199                 while not strip(line):
0200                     line = file.readline()
0201                     if not line: break
0202                 result = strip(split(line, '"""')[0])
0203             else: result = None
0204         file.close()
0205         cache[filename] = (mtime, result)
0206     return result
0207 
0208 class ErrorDuringImport(Exception):
0209     """Errors that occurred while trying to import something to document it."""
0210     def __init__(self, filename, (exc, value, tb)):
0211         self.filename = filename
0212         self.exc = exc
0213         self.value = value
0214         self.tb = tb
0215 
0216     def __str__(self):
0217         exc = self.exc
0218         if type(exc) is types.ClassType:
0219             exc = exc.__name__
0220         return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
0221 
0222 def importfile(path):
0223     """Import a Python source file or compiled file given its path."""
0224     magic = imp.get_magic()
0225     file = open(path, 'r')
0226     if file.read(len(magic)) == magic:
0227         kind = imp.PY_COMPILED
0228     else:
0229         kind = imp.PY_SOURCE
0230     file.close()
0231     filename = os.path.basename(path)
0232     name, ext = os.path.splitext(filename)
0233     file = open(path, 'r')
0234     try:
0235         module = imp.load_module(name, file, path, (ext, 'r', kind))
0236     except:
0237         raise ErrorDuringImport(path, sys.exc_info())
0238     file.close()
0239     return module
0240 
0241 def safeimport(path, forceload=0, cache={}):
0242     """Import a module; handle errors; return None if the module isn't found.
0243 
0244     If the module *is* found but an exception occurs, it's wrapped in an
0245     ErrorDuringImport exception and reraised.  Unlike __import__, if a
0246     package path is specified, the module at the end of the path is returned,
0247     not the package at the beginning.  If the optional 'forceload' argument
0248     is 1, we reload the module from disk (unless it's a dynamic extension)."""
0249     if forceload and path in sys.modules:
0250         # This is the only way to be sure.  Checking the mtime of the file
0251         # isn't good enough (e.g. what if the module contains a class that
0252         # inherits from another module that has changed?).
0253         if path not in sys.builtin_module_names:
0254             # Python never loads a dynamic extension a second time from the
0255             # same path, even if the file is changed or missing.  Deleting
0256             # the entry in sys.modules doesn't help for dynamic extensions,
0257             # so we're not even going to try to keep them up to date.
0258             info = inspect.getmoduleinfo(sys.modules[path].__file__)
0259             if info[3] != imp.C_EXTENSION:
0260                 cache[path] = sys.modules[path] # prevent module from clearing
0261                 del sys.modules[path]
0262     try:
0263         module = __import__(path)
0264     except:
0265         # Did the error occur before or after the module was found?
0266         (exc, value, tb) = info = sys.exc_info()
0267         if path in sys.modules:
0268             # An error occured while executing the imported module.
0269             raise ErrorDuringImport(sys.modules[path].__file__, info)
0270         elif exc is SyntaxError:
0271             # A SyntaxError occurred before we could execute the module.
0272             raise ErrorDuringImport(value.filename, info)
0273         elif exc is ImportError and \
0274              split(lower(str(value)))[:2] == ['no', 'module']:
0275             # The module was not found.
0276             return None
0277         else:
0278             # Some other error occurred during the importing process.
0279             raise ErrorDuringImport(path, sys.exc_info())
0280     for part in split(path, '.')[1:]:
0281         try: module = getattr(module, part)
0282         except AttributeError: return None
0283     return module
0284 
0285 # ---------------------------------------------------- formatter base class
0286 
0287 class Doc:
0288     def document(self, object, name=None, *args):
0289         """Generate documentation for an object."""
0290         args = (object, name) + args
0291         # 'try' clause is to attempt to handle the possibility that inspect
0292         # identifies something in a way that pydoc itself has issues handling;
0293         # think 'super' and how it is a descriptor (which raises the exception
0294         # by lacking a __name__ attribute) and an instance.
0295         try:
0296             if inspect.ismodule(object): return self.docmodule(*args)
0297             if inspect.isclass(object): return self.docclass(*args)
0298             if inspect.isroutine(object): return self.docroutine(*args)
0299         except AttributeError:
0300             pass
0301         return self.docother(*args)
0302 
0303     def fail(self, object, name=None, *args):
0304         """Raise an exception for unimplemented types."""
0305         message = "don't know how to document object%s of type %s" % (
0306             name and ' ' + repr(name), type(object).__name__)
0307         raise TypeError, message
0308 
0309     docmodule = docclass = docroutine = docother = fail
0310 
0311     def getdocloc(self, object):
0312         """Return the location of module docs or None"""
0313 
0314         try:
0315             file = inspect.getabsfile(object)
0316         except TypeError:
0317             file = '(built-in)'
0318 
0319         docloc = os.environ.get("PYTHONDOCS",
0320                                 "http://www.python.org/doc/current/lib")
0321         basedir = os.path.join(sys.exec_prefix, "lib",
0322                                "python"+sys.version[0:3])
0323         if (isinstance(object, type(os)) and
0324             (object.__name__ in ('errno', 'exceptions', 'gc', 'imp',
0325                                  'marshal', 'posix', 'signal', 'sys',
0326                                  'thread', 'zipimport') or
0327              (file.startswith(basedir) and
0328               not file.startswith(os.path.join(basedir, 'site-packages'))))):
0329             htmlfile = "module-%s.html" % object.__name__
0330             if docloc.startswith("http://"):
0331                 docloc = "%s/%s" % (docloc.rstrip("/"), htmlfile)
0332             else:
0333                 docloc = os.path.join(docloc, htmlfile)
0334         else:
0335             docloc = None
0336         return docloc
0337 
0338 # -------------------------------------------- HTML documentation generator
0339 
0340 class HTMLRepr(Repr):
0341     """Class for safely making an HTML representation of a Python object."""
0342     def __init__(self):
0343         Repr.__init__(self)
0344         self.maxlist = self.maxtuple = 20
0345         self.maxdict = 10
0346         self.maxstring = self.maxother = 100
0347 
0348     def escape(self, text):
0349         return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')
0350 
0351     def repr(self, object):
0352         return Repr.repr(self, object)
0353 
0354     def repr1(self, x, level):
0355         if hasattr(type(x), '__name__'):
0356             methodname = 'repr_' + join(split(type(x).__name__), '_')
0357             if hasattr(self, methodname):
0358                 return getattr(self, methodname)(x, level)
0359         return self.escape(cram(stripid(repr(x)), self.maxother))
0360 
0361     def repr_string(self, x, level):
0362         test = cram(x, self.maxstring)
0363         testrepr = repr(test)
0364         if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
0365             # Backslashes are only literal in the string and are never
0366             # needed to make any special characters, so show a raw string.
0367             return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
0368         return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
0369                       r'<font color="#c040c0">\1</font>',
0370                       self.escape(testrepr))
0371 
0372     repr_str = repr_string
0373 
0374     def repr_instance(self, x, level):
0375         try:
0376             return self.escape(cram(stripid(repr(x)), self.maxstring))
0377         except:
0378             return self.escape('<%s instance>' % x.__class__.__name__)
0379 
0380     repr_unicode = repr_string
0381 
0382 class HTMLDoc(Doc):
0383     """Formatter class for HTML documentation."""
0384 
0385     # ------------------------------------------- HTML formatting utilities
0386 
0387     _repr_instance = HTMLRepr()
0388     repr = _repr_instance.repr
0389     escape = _repr_instance.escape
0390 
0391     def page(self, title, contents):
0392         """Format an HTML page."""
0393         return '''
0394 <!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
0395 <html><head><title>Python: %s</title>
0396 </head><body bgcolor="#f0f0f8">
0397 %s
0398 </body></html>''' % (title, contents)
0399 
0400     def heading(self, title, fgcol, bgcol, extras=''):
0401         """Format a page heading."""
0402         return '''
0403 <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading">
0404 <tr bgcolor="%s">
0405 <td valign=bottom>&nbsp;<br>
0406 <font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
0407 ><td align=right valign=bottom
0408 ><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
0409     ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
0410 
0411     def section(self, title, fgcol, bgcol, contents, width=6,
0412                 prelude='', marginalia=None, gap='&nbsp;'):
0413         """Format a section with a heading."""
0414         if marginalia is None:
0415             marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
0416         result = '''<p>
0417 <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
0418 <tr bgcolor="%s">
0419 <td colspan=3 valign=bottom>&nbsp;<br>
0420 <font color="%s" face="helvetica, arial">%s</font></td></tr>
0421     ''' % (bgcol, fgcol, title)
0422         if prelude:
0423             result = result + '''
0424 <tr bgcolor="%s"><td rowspan=2>%s</td>
0425 <td colspan=2>%s</td></tr>
0426 <tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
0427         else:
0428             result = result + '''
0429 <tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
0430 
0431         return result + '\n<td width="100%%">%s</td></tr></table>' % contents
0432 
0433     def bigsection(self, title, *args):
0434         """Format a section with a big heading."""
0435         title = '<big><strong>%s</strong></big>' % title
0436         return self.section(title, *args)
0437 
0438     def preformat(self, text):
0439         """Format literal preformatted text."""
0440         text = self.escape(expandtabs(text))
0441         return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
0442                              ' ', '&nbsp;', '\n', '<br>\n')
0443 
0444     def multicolumn(self, list, format, cols=4):
0445         """Format a list of items into a multi-column list."""
0446         result = ''
0447         rows = (len(list)+cols-1)/cols
0448         for col in range(cols):
0449             result = result + '<td width="%d%%" valign=top>' % (100/cols)
0450             for i in range(rows*col, rows*col+rows):
0451                 if i < len(list):
0452                     result = result + format(list[i]) + '<br>\n'
0453             result = result + '</td>'
0454         return '<table width="100%%" summary="list"><tr>%s</tr></table>' % result
0455 
0456     def grey(self, text): return '<font color="#909090">%s</font>' % text
0457 
0458     def namelink(self, name, *dicts):
0459         """Make a link for an identifier, given name-to-URL mappings."""
0460         for dict in dicts:
0461             if name in dict:
0462                 return '<a href="%s">%s</a>' % (dict[name], name)
0463         return name
0464 
0465     def classlink(self, object, modname):
0466         """Make a link for a class."""
0467         name, module = object.__name__, sys.modules.get(object.__module__)
0468         if hasattr(module, name) and getattr(module, name) is object:
0469             return '<a href="%s.html#%s">%s</a>' % (
0470                 module.__name__, name, classname(object, modname))
0471         return classname(object, modname)
0472 
0473     def modulelink(self, object):
0474         """Make a link for a module."""
0475         return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
0476 
0477     def modpkglink(self, (name, path, ispackage, shadowed)):
0478         """Make a link for a module or package to display in an index."""
0479         if shadowed:
0480             return self.grey(name)
0481         if path:
0482             url = '%s.%s.html' % (path, name)
0483         else:
0484             url = '%s.html' % name
0485         if ispackage:
0486             text = '<strong>%s</strong>&nbsp;(package)' % name
0487         else:
0488             text = name
0489         return '<a href="%s">%s</a>' % (url, text)
0490 
0491     def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
0492         """Mark up some plain text, given a context of symbols to look for.
0493         Each context dictionary maps object names to anchor names."""
0494         escape = escape or self.escape
0495         results = []
0496         here = 0
0497         pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
0498                                 r'RFC[- ]?(\d+)|'
0499                                 r'PEP[- ]?(\d+)|'
0500                                 r'(self\.)?(\w+))')
0501         while True:
0502             match = pattern.search(text, here)
0503             if not match: break
0504             start, end = match.span()
0505             results.append(escape(text[here:start]))
0506 
0507             all, scheme, rfc, pep, selfdot, name = match.groups()
0508             if scheme:
0509                 url = escape(all).replace('"', '&quot;')
0510                 results.append('<a href="%s">%s</a>' % (url, url))
0511             elif rfc:
0512                 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
0513                 results.append('<a href="%s">%s</a>' % (url, escape(all)))
0514             elif pep:
0515                 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
0516                 results.append('<a href="%s">%s</a>' % (url, escape(all)))
0517             elif text[end:end+1] == '(':
0518                 results.append(self.namelink(name, methods, funcs, classes))
0519             elif selfdot:
0520                 results.append('self.<strong>%s</strong>' % name)
0521             else:
0522                 results.append(self.namelink(name, classes))
0523             here = end
0524         results.append(escape(text[here:]))
0525         return join(results, '')
0526 
0527     # ---------------------------------------------- type-specific routines
0528 
0529     def formattree(self, tree, modname, parent=None):
0530         """Produce HTML for a class tree as given by inspect.getclasstree()."""
0531         result = ''
0532         for entry in tree:
0533             if type(entry) is type(()):
0534                 c, bases = entry
0535                 result = result + '<dt><font face="helvetica, arial">'
0536                 result = result + self.classlink(c, modname)
0537                 if bases and bases != (parent,):
0538                     parents = []
0539                     for base in bases:
0540                         parents.append(self.classlink(base, modname))
0541                     result = result + '(' + join(parents, ', ') + ')'
0542                 result = result + '\n</font></dt>'
0543             elif type(entry) is type([]):
0544                 result = result + '<dd>\n%s</dd>\n' % self.formattree(
0545                     entry, modname, c)
0546         return '<dl>\n%s</dl>\n' % result
0547 
0548     def docmodule(self, object, name=None, mod=None, *ignored):
0549         """Produce HTML documentation for a module object."""
0550         name = object.__name__ # ignore the passed-in name
0551         try:
0552             all = object.__all__
0553         except AttributeError:
0554             all = None
0555         parts = split(name, '.')
0556         links = []
0557         for i in range(len(parts)-1):
0558             links.append(
0559                 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
0560                 (join(parts[:i+1], '.'), parts[i]))
0561         linkedname = join(links + parts[-1:], '.')
0562         head = '<big><big><strong>%s</strong></big></big>' % linkedname
0563         try:
0564             path = inspect.getabsfile(object)
0565             url = path
0566             if sys.platform == 'win32':
0567                 import nturl2path
0568                 url = nturl2path.pathname2url(path)
0569             filelink = '<a href="file:%s">%s</a>' % (url, path)
0570         except TypeError:
0571             filelink = '(built-in)'
0572         info = []
0573         if hasattr(object, '__version__'):
0574             version = str(object.__version__)
0575             if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
0576                 version = strip(version[11:-1])
0577             info.append('version %s' % self.escape(version))
0578         if hasattr(object, '__date__'):
0579             info.append(self.escape(str(object.__date__)))
0580         if info:
0581             head = head + ' (%s)' % join(info, ', ')
0582         docloc = self.getdocloc(object)
0583         if docloc is not None:
0584             docloc = '<br><a href="%(docloc)s">Module Docs</a>' % locals()
0585         else:
0586             docloc = ''
0587         result = self.heading(
0588             head, '#ffffff', '#7799ee',
0589             '<a href=".">index</a><br>' + filelink + docloc)
0590 
0591         modules = inspect.getmembers(object, inspect.ismodule)
0592 
0593         classes, cdict = [], {}
0594         for key, value in inspect.getmembers(object, inspect.isclass):
0595             # if __all__ exists, believe it.  Otherwise use old heuristic.
0596             if (all is not None or
0597                 (inspect.getmodule(value) or object) is object):
0598                 if visiblename(key, all):
0599                     classes.append((key, value))
0600                     cdict[key] = cdict[value] = '#' + key
0601         for key, value in classes:
0602             for base in value.__bases__:
0603                 key, modname = base.__name__, base.__module__
0604                 module = sys.modules.get(modname)
0605                 if modname != name and module and hasattr(module, key):
0606                     if getattr(module, key) is base:
0607                         if not key in cdict:
0608                             cdict[key] = cdict[base] = modname + '.html#' + key
0609         funcs, fdict = [], {}
0610         for key, value in inspect.getmembers(object, inspect.isroutine):
0611             # if __all__ exists, believe it.  Otherwise use old heuristic.
0612             if (all is not None or
0613                 inspect.isbuiltin(value) or inspect.getmodule(value) is object):
0614                 if visiblename(key, all):
0615                     funcs.append((key, value))
0616                     fdict[key] = '#-' + key
0617                     if inspect.isfunction(value): fdict[value] = fdict[key]
0618         data = []
0619         for key, value in inspect.getmembers(object, isdata):
0620             if visiblename(key, all):
0621                 data.append((key, value))
0622 
0623         doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
0624         doc = doc and '<tt>%s</tt>' % doc
0625         result = result + '<p>%s</p>\n' % doc
0626 
0627         if hasattr(object, '__path__'):
0628             modpkgs = []
0629             modnames = []
0630             for file in os.listdir(object.__path__[0]):
0631                 path = os.path.join(object.__path__[0], file)
0632                 modname = inspect.getmodulename(file)
0633                 if modname != '__init__':
0634                     if modname and modname not in modnames:
0635                         modpkgs.append((modname, name, 0, 0))
0636                         modnames.append(modname)
0637                     elif ispackage(path):
0638                         modpkgs.append((file, name, 1, 0))
0639             modpkgs.sort()
0640             contents = self.multicolumn(modpkgs, self.modpkglink)
0641             result = result + self.bigsection(
0642                 'Package Contents', '#ffffff', '#aa55cc', contents)
0643         elif modules:
0644             contents = self.multicolumn(
0645                 modules, lambda (key, value), s=self: s.modulelink(value))
0646             result = result + self.bigsection(
0647                 'Modules', '#fffff', '#aa55cc', contents)
0648 
0649         if classes:
0650             classlist = map(lambda (key, value): value, classes)
0651             contents = [
0652                 self.formattree(inspect.getclasstree(classlist, 1), name)]
0653             for key, value in classes:
0654                 contents.append(self.document(value, key, name, fdict, cdict))
0655             result = result + self.bigsection(
0656                 'Classes', '#ffffff', '#ee77aa', join(contents))
0657         if funcs:
0658             contents = []
0659             for key, value in funcs:
0660                 contents.append(self.document(value, key, name, fdict, cdict))
0661             result = result + self.bigsection(
0662                 'Functions', '#ffffff', '#eeaa77', join(contents))
0663         if data:
0664             contents = []
0665             for key, value in data:
0666                 contents.append(self.document(value, key))
0667             result = result + self.bigsection(
0668                 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))
0669         if hasattr(object, '__author__'):
0670             contents = self.markup(str(object.__author__), self.preformat)
0671             result = result + self.bigsection(
0672                 'Author', '#ffffff', '#7799ee', contents)
0673         if hasattr(object, '__credits__'):
0674             contents = self.markup(str(object.__credits__), self.preformat)
0675             result = result + self.bigsection(
0676                 'Credits', '#ffffff', '#7799ee', contents)
0677 
0678         return result
0679 
0680     def docclass(self, object, name=None, mod=None, funcs={}, classes={},
0681                  *ignored):
0682         """Produce HTML documentation for a class object."""
0683         realname = object.__name__
0684         name = name or realname
0685         bases = object.__bases__
0686 
0687         contents = []
0688         push = contents.append
0689 
0690         # Cute little class to pump out a horizontal rule between sections.
0691         class HorizontalRule:
0692             def __init__(self):
0693                 self.needone = 0
0694             def maybe(self):
0695                 if self.needone:
0696                     push('<hr>\n')
0697                 self.needone = 1
0698         hr = HorizontalRule()
0699 
0700         # List the mro, if non-trivial.
0701         mro = deque(inspect.getmro(object))
0702         if len(mro) > 2:
0703             hr.maybe()
0704             push('<dl><dt>Method resolution order:</dt>\n')
0705             for base in mro:
0706                 push('<dd>%s</dd>\n' % self.classlink(base,
0707                                                       object.__module__))
0708             push('</dl>\n')
0709 
0710         def spill(msg, attrs, predicate):
0711             ok, attrs = _split_list(attrs, predicate)
0712             if ok:
0713                 hr.maybe()
0714                 push(msg)
0715                 for name, kind, homecls, value in ok:
0716                     push(self.document(getattr(object, name), name, mod,
0717                                        funcs, classes, mdict, object))
0718                     push('\n')
0719             return attrs
0720 
0721         def spillproperties(msg, attrs, predicate):
0722             ok, attrs = _split_list(attrs, predicate)
0723             if ok:
0724                 hr.maybe()
0725                 push(msg)
0726                 for name, kind, homecls, value in ok:
0727                     push('<dl><dt><strong>%s</strong></dt>\n' % name)
0728                     if value.__doc__ is not None:
0729                         doc = self.markup(value.__doc__, self.preformat,
0730                                           funcs, classes, mdict)
0731                         push('<dd><tt>%s</tt></dd>\n' % doc)
0732                     for attr, tag in [('fget', '<em>get</em>'),
0733                                       ('fset', '<em>set</em>'),
0734                                       ('fdel', '<em>delete</em>')]:
0735                         func = getattr(value, attr)
0736                         if func is not None:
0737                             base = self.document(func, tag, mod,
0738                                                  funcs, classes, mdict, object)
0739                             push('<dd>%s</dd>\n' % base)
0740                     push('</dl>\n')
0741             return attrs
0742 
0743         def spilldata(msg, attrs, predicate):
0744             ok, attrs = _split_list(attrs, predicate)
0745             if ok:
0746                 hr.maybe()
0747                 push(msg)
0748                 for name, kind, homecls, value in ok:
0749                     base = self.docother(getattr(object, name), name, mod)
0750                     if callable(value) or inspect.isdatadescriptor(value):
0751                         doc = getattr(value, "__doc__", None)
0752                     else:
0753                         doc = None
0754                     if doc is None:
0755                         push('<dl><dt>%s</dl>\n' % base)
0756                     else:
0757                         doc = self.markup(getdoc(value), self.preformat,
0758                                           funcs, classes, mdict)
0759                         doc = '<dd><tt>%s</tt>' % doc
0760                         push('<dl><dt>%s%s</dl>\n' % (base, doc))
0761                     push('\n')
0762             return attrs
0763 
0764         attrs = filter(lambda (name, kind, cls, value): visiblename(name),
0765                        inspect.classify_class_attrs(object))
0766         mdict = {}
0767         for key, kind, homecls, value in attrs:
0768             mdict[key] = anchor = '#' + name + '-' + key
0769             value = getattr(object, key)
0770             try:
0771                 # The value may not be hashable (e.g., a data attr with
0772                 # a dict or list value).
0773                 mdict[value] = anchor
0774             except TypeError:
0775                 pass
0776 
0777         while attrs:
0778             if mro:
0779                 thisclass = mro.popleft()
0780             else:
0781                 thisclass = attrs[0][2]
0782             attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
0783 
0784             if thisclass is __builtin__.object:
0785                 attrs = inherited
0786                 continue
0787             elif thisclass is object:
0788                 tag = 'defined here'
0789             else:
0790                 tag = 'inherited from %s' % self.classlink(thisclass,
0791                                                            object.__module__)
0792             tag += ':<br>\n'
0793 
0794             # Sort attrs by name.
0795             attrs.sort(key=lambda t: t[0])
0796 
0797             # Pump out the attrs, segregated by kind.
0798             attrs = spill('Methods %s' % tag, attrs,
0799                           lambda t: t[1] == 'method')
0800             attrs = spill('Class methods %s' % tag, attrs,
0801                           lambda t: t[1] == 'class method')
0802             attrs = spill('Static methods %s' % tag, attrs,
0803                           lambda t: t[1] == 'static method')
0804             attrs = spillproperties('Properties %s' % tag, attrs,
0805                                     lambda t: t[1] == 'property')
0806             attrs = spilldata('Data and other attributes %s' % tag, attrs,
0807                               lambda t: t[1] == 'data')
0808             assert attrs == []
0809             attrs = inherited
0810 
0811         contents = ''.join(contents)
0812 
0813         if name == realname:
0814             title = '<a name="%s">class <strong>%s</strong></a>' % (
0815                 name, realname)
0816         else:
0817             title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
0818                 name, name, realname)
0819         if bases:
0820             parents = []
0821             for base in bases:
0822                 parents.append(self.classlink(base, object.__module__))
0823             title = title + '(%s)' % join(parents, ', ')
0824         doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict)
0825         doc = doc and '<tt>%s<br>&nbsp;</tt>' % doc
0826 
0827         return self.section(title, '#000000', '#ffc8d8', contents, 3, doc)
0828 
0829     def formatvalue(self, object):
0830         """Format an argument default value as text."""
0831         return self.grey('=' + self.repr(object))
0832 
0833     def docroutine(self, object, name=None, mod=None,
0834                    funcs={}, classes={}, methods={}, cl=None):
0835         """Produce HTML documentation for a function or method object."""
0836         realname = object.__name__
0837         name = name or realname
0838         anchor = (cl and cl.__name__ or '') + '-' + name
0839         note = ''
0840         skipdocs = 0
0841         if inspect.ismethod(object):
0842             imclass = object.im_class
0843             if cl:
0844                 if imclass is not cl:
0845                     note = ' from ' + self.classlink(imclass, mod)
0846             else:
0847                 if object.im_self:
0848                     note = ' method of %s instance' % self.classlink(
0849                         object.im_self.__class__, mod)
0850                 else:
0851                     note = ' unbound %s method' % self.classlink(imclass,mod)
0852             object = object.im_func
0853 
0854         if name == realname:
0855             title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
0856         else:
0857             if (cl and realname in cl.__dict__ and
0858                 cl.__dict__[realname] is object):
0859                 reallink = '<a href="#%s">%s</a>' % (
0860                     cl.__name__ + '-' + realname, realname)
0861                 skipdocs = 1
0862             else:
0863                 reallink = realname
0864             title = '<a name="%s"><strong>%s</strong></a> = %s' % (
0865                 anchor, name, reallink)
0866         if inspect.isfunction(object):
0867             args, varargs, varkw, defaults = inspect.getargspec(object)
0868             argspec = inspect.formatargspec(
0869                 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
0870             if realname == '<lambda>':
0871                 title = '<strong>%s</strong> <em>lambda</em> ' % name
0872                 argspec = argspec[1:-1] # remove parentheses
0873         else:
0874             argspec = '(...)'
0875 
0876         decl = title + argspec + (note and self.grey(
0877                '<font face="helvetica, arial">%s</font>' % note))
0878 
0879         if skipdocs:
0880             return '<dl><dt>%s</dt></dl>\n' % decl
0881         else:
0882             doc = self.markup(
0883                 getdoc(object), self.preformat, funcs, classes, methods)
0884             doc = doc and '<dd><tt>%s</tt></dd>' % doc
0885             return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
0886 
0887     def docother(self, object, name=None, mod=None, *ignored):
0888         """Produce HTML documentation for a data object."""
0889         lhs = name and '<strong>%s</strong> = ' % name or ''
0890         return lhs + self.repr(object)
0891 
0892     def index(self, dir, shadowed=None):
0893         """Generate an HTML index for a directory of modules."""
0894         modpkgs = []
0895         if shadowed is None: shadowed = {}
0896         seen = {}
0897         files = os.listdir(dir)
0898 
0899         def found(name, ispackage,
0900                   modpkgs=modpkgs, shadowed=shadowed, seen=seen):
0901             if name not in seen:
0902                 modpkgs.append((name, '', ispackage, name in shadowed))
0903                 seen[name] = 1
0904                 shadowed[name] = 1
0905 
0906         # Package spam/__init__.py takes precedence over module spam.py.
0907         for file in files:
0908             path = os.path.join(dir, file)
0909             if ispackage(path): found(file, 1)
0910         for file in files:
0911             path = os.path.join(dir, file)
0912             if os.path.isfile(path):
0913                 modname = inspect.getmodulename(file)
0914                 if modname: found(modname, 0)
0915 
0916         modpkgs.sort()
0917         contents = self.multicolumn(modpkgs, self.modpkglink)
0918         return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
0919 
0920 # -------------------------------------------- text documentation generator
0921 
0922 class TextRepr(Repr):
0923     """Class for safely making a text representation of a Python object."""
0924     def __init__(self):
0925         Repr.__init__(self)
0926         self.maxlist = self.maxtuple = 20
0927         self.maxdict = 10
0928         self.maxstring = self.maxother = 100
0929 
0930     def repr1(self, x, level):
0931         if hasattr(type(x), '__name__'):
0932             methodname = 'repr_' + join(split(type(x).__name__), '_')
0933             if hasattr(self, methodname):
0934                 return getattr(self, methodname)(x, level)
0935         return cram(stripid(repr(x)), self.maxother)
0936 
0937     def repr_string(self, x, level):
0938         test = cram(x, self.maxstring)
0939         testrepr = repr(test)
0940         if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
0941             # Backslashes are only literal in the string and are never
0942             # needed to make any special characters, so show a raw string.
0943             return 'r' + testrepr[0] + test + testrepr[0]
0944         return testrepr
0945 
0946     repr_str = repr_string
0947 
0948     def repr_instance(self, x, level):
0949         try:
0950             return cram(stripid(repr(x)), self.maxstring)
0951         except:
0952             return '<%s instance>' % x.__class__.__name__
0953 
0954 class TextDoc(Doc):
0955     """Formatter class for text documentation."""
0956 
0957     # ------------------------------------------- text formatting utilities
0958 
0959     _repr_instance = TextRepr()
0960     repr = _repr_instance.repr
0961 
0962     def bold(self, text):
0963         """Format a string in bold by overstriking."""
0964         return join(map(lambda ch: ch + '\b' + ch, text), '')
0965 
0966     def indent(self, text, prefix='    '):
0967         """Indent text by prepending a given prefix to each line."""
0968         if not text: return ''
0969         lines = split(text, '\n')
0970         lines = map(lambda line, prefix=prefix: prefix + line, lines)
0971         if lines: lines[-1] = rstrip(lines[-1])
0972         return join(lines, '\n')
0973 
0974     def section(self, title, contents):
0975         """Format a section with a given heading."""
0976         return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
0977 
0978     # ---------------------------------------------- type-specific routines
0979 
0980     def formattree(self, tree, modname, parent=None, prefix=''):
0981         """Render in text a class tree as returned by inspect.getclasstree()."""
0982         result = ''
0983         for entry in tree:
0984             if type(entry) is type(()):
0985                 c, bases = entry
0986                 result = result + prefix + classname(c, modname)
0987                 if bases and bases != (parent,):
0988                     parents = map(lambda c, m=modname: classname(c, m), bases)
0989                     result = result + '(%s)' % join(parents, ', ')
0990                 result = result + '\n'
0991             elif type(entry) is type([]):
0992                 result = result + self.formattree(
0993                     entry, modname, c, prefix + '    ')
0994         return result
0995 
0996     def docmodule(self, object, name=None, mod=None):
0997         """Produce text documentation for a given module object."""
0998         name = object.__name__ # ignore the passed-in name
0999         synop, desc = splitdoc(getdoc(object))
1000         result = self.section('NAME', name + (synop and ' - ' + synop))
1001 
1002         try:
1003             all = object.__all__
1004         except AttributeError:
1005             all = None
1006 
1007         try:
1008             file = inspect.getabsfile(object)
1009         except TypeError:
1010             file = '(built-in)'
1011         result = result + self.section('FILE', file)
1012 
1013         docloc = self.getdocloc(object)
1014         if docloc is not None:
1015             result = result + self.section('MODULE DOCS', docloc)
1016 
1017         if desc:
1018             result = result + self.section('DESCRIPTION', desc)
1019 
1020         classes = []
1021         for key, value in inspect.getmembers(object, inspect.isclass):
1022             # if __all__ exists, believe it.  Otherwise use old heuristic.
1023             if (all is not None
1024                 or (inspect.getmodule(value) or object) is object):
1025                 if visiblename(key, all):
1026                     classes.append((key, value))
1027         funcs = []
1028         for key, value in inspect.getmembers(object, inspect.isroutine):
1029             # if __all__ exists, believe it.  Otherwise use old heuristic.
1030             if (all is not None or
1031                 inspect.isbuiltin(value) or inspect.getmodule(value) is object):
1032                 if visiblename(key, all):
1033                     funcs.append((key, value))
1034         data = []
1035         for key, value in inspect.getmembers(object, isdata):
1036             if visiblename(key, all):
1037                 data.append((key, value))
1038 
1039         if hasattr(object, '__path__'):
1040             modpkgs = []
1041             for file in os.listdir(object.__path__[0]):
1042                 path = os.path.join(object.__path__[0], file)
1043                 modname = inspect.getmodulename(file)
1044                 if modname != '__init__':
1045                     if modname and modname not in modpkgs:
1046                         modpkgs.append(modname)
1047                     elif ispackage(path):
1048                         modpkgs.append(file + ' (package)')
1049             modpkgs.sort()
1050             result = result + self.section(
1051                 'PACKAGE CONTENTS', join(modpkgs, '\n'))
1052 
1053         if classes:
1054             classlist = map(lambda (key, value): value, classes)
1055             contents = [self.formattree(
1056                 inspect.getclasstree(classlist, 1), name)]
1057             for key, value in classes:
1058                 contents.append(self.document(value, key, name))
1059             result = result + self.section('CLASSES', join(contents, '\n'))
1060 
1061         if funcs:
1062             contents = []
1063             for key, value in funcs:
1064                 contents.append(self.document(value, key, name))
1065             result = result + self.section('FUNCTIONS', join(contents, '\n'))
1066 
1067         if data:
1068             contents = []
1069             for key, value in data:
1070                 contents.append(self.docother(value, key, name, 70))
1071             result = result + self.section('DATA', join(contents, '\n'))
1072 
1073         if hasattr(object, '__version__'):
1074             version = str(object.__version__)
1075             if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
1076                 version = strip(version[11:-1])
1077             result = result + self.section('VERSION', version)
1078         if hasattr(object, '__date__'):
1079             result = result + self.section('DATE', str(object.__date__))
1080         if hasattr(object, '__author__'):
1081             result = result + self.section('AUTHOR', str(object.__author__))
1082         if hasattr(object, '__credits__'):
1083             result = result + self.section('CREDITS', str(object.__credits__))
1084         return result
1085 
1086     def docclass(self, object, name=None, mod=None):
1087         """Produce text documentation for a given class object."""
1088         realname = object.__name__
1089         name = name or realname
1090         bases = object.__bases__
1091 
1092         def makename(c, m=object.__module__):
1093             return classname(c, m)
1094 
1095         if name == realname:
1096             title = 'class ' + self.bold(realname)
1097         else:
1098             title = self.bold(name) + ' = class ' + realname
1099         if bases:
1100             parents = map(makename, bases)
1101             title = title + '(%s)' % join(parents, ', ')
1102 
1103         doc = getdoc(object)
1104         contents = doc and [doc + '\n'] or []
1105         push = contents.append
1106 
1107         # List the mro, if non-trivial.
1108         mro = deque(inspect.getmro(object))
1109         if len(mro) > 2:
1110             push("Method resolution order:")
1111             for base in mro:
1112                 push('    ' + makename(base))
1113             push('')
1114 
1115         # Cute little class to pump out a horizontal rule between sections.
1116         class HorizontalRule:
1117             def __init__(self):
1118                 self.needone = 0
1119             def maybe(self):
1120                 if self.needone:
1121                     push('-' * 70)
1122                 self.needone = 1
1123         hr = HorizontalRule()
1124 
1125         def spill(msg, attrs, predicate):
1126             ok, attrs = _split_list(attrs, predicate)
1127             if ok:
1128                 hr.maybe()
1129                 push(msg)
1130                 for name, kind, homecls, value in ok:
1131                     push(self.document(getattr(object, name),
1132                                        name, mod, object))
1133             return attrs
1134 
1135         def spillproperties(msg, attrs, predicate):
1136             ok, attrs = _split_list(attrs, predicate)
1137             if ok:
1138                 hr.maybe()
1139                 push(msg)
1140                 for name, kind, homecls, value in ok:
1141                     push(name)
1142                     need_blank_after_doc = 0
1143                     doc = getdoc(value) or ''
1144                     if doc:
1145                         push(self.indent(doc))
1146                         need_blank_after_doc = 1
1147                     for attr, tag in [('fget', '<get>'),
1148                                       ('fset', '<set>'),
1149                                       ('fdel', '<delete>')]:
1150                         func = getattr(value, attr)
1151                         if func is not None:
1152                             if need_blank_after_doc:
1153                                 push('')
1154                                 need_blank_after_doc = 0
1155                             base = self.document(func, tag, mod)
1156                             push(self.indent(base))
1157             return attrs
1158 
1159         def spilldata(msg, attrs, predicate):
1160             ok, attrs = _split_list(attrs, predicate)
1161             if ok:
1162                 hr.maybe()
1163                 push(msg)
1164                 for name, kind, homecls, value in ok:
1165                     if callable(value) or inspect.isdatadescriptor(value):
1166                         doc = getattr(value, "__doc__", None)
1167                     else:
1168                         doc = None
1169                     push(self.docother(getattr(object, name),
1170                                        name, mod, 70, doc) + '\n')
1171             return attrs
1172 
1173         attrs = filter(lambda (name, kind, cls, value): visiblename(name),
1174                        inspect.classify_class_attrs(object))
1175         while attrs:
1176             if mro:
1177                 thisclass = mro.popleft()
1178             else:
1179                 thisclass = attrs[0][2]
1180             attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
1181 
1182             if thisclass is __builtin__.object:
1183                 attrs = inherited
1184                 continue
1185             elif thisclass is object:
1186                 tag = "defined here"
1187             else:
1188                 tag = "inherited from %s" % classname(thisclass,
1189                                                       object.__module__)
1190             filter(lambda t: not t[0].startswith('_'), attrs)
1191 
1192             # Sort attrs by name.
1193             attrs.sort()
1194 
1195             # Pump out the attrs, segregated by kind.
1196             attrs = spill("Methods %s:\n" % tag, attrs,
1197                           lambda t: t[1] == 'method')
1198             attrs = spill("Class methods %s:\n" % tag, attrs,
1199                           lambda t: t[1] == 'class method')
1200             attrs = spill("Static methods %s:\n" % tag, attrs,
1201                           lambda t: t[1] == 'static method')
1202             attrs = spillproperties("Properties %s:\n" % tag, attrs,
1203                                     lambda t: t[1] == 'property')
1204             attrs = spilldata("Data and other attributes %s:\n" % tag, attrs,
1205                               lambda t: t[1] == 'data')
1206             assert attrs == []
1207             attrs = inherited
1208 
1209         contents = '\n'.join(contents)
1210         if not contents:
1211             return title + '\n'
1212         return title + '\n' + self.indent(rstrip(contents), ' |  ') + '\n'
1213 
1214     def formatvalue(self, object):
1215         """Format an argument default value as text."""
1216         return '=' + self.repr(object)
1217 
1218     def docroutine(self, object, name=None, mod=None, cl=None):
1219         """Produce text documentation for a function or method object."""
1220         realname = object.__name__
1221         name = name or realname
1222         note = ''
1223         skipdocs = 0
1224         if inspect.ismethod(object):
1225             imclass = object.im_class
1226             if cl:
1227                 if imclass is not cl:
1228                     note = ' from ' + classname(imclass, mod)
1229             else:
1230                 if object.im_self:
1231                     note = ' method of %s instance' % classname(
1232                         object.im_self.__class__, mod)
1233                 else:
1234                     note = ' unbound %s method' % classname(imclass,mod)
1235             object = object.im_func
1236 
1237         if name == realname:
1238             title = self.bold(realname)
1239         else:
1240             if (cl and realname in cl.__dict__ and
1241                 cl.__dict__[realname] is object):
1242                 skipdocs = 1
1243             title = self.bold(name) + ' = ' + realname
1244         if inspect.isfunction(object):
1245             args, varargs, varkw, defaults = inspect.getargspec(object)
1246             argspec = inspect.formatargspec(
1247                 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
1248             if realname == '<lambda>':
1249                 title = 'lambda'
1250                 argspec = argspec[1:-1] # remove parentheses
1251         else:
1252             argspec = '(...)'
1253         decl = title + argspec + note
1254 
1255         if skipdocs:
1256             return decl + '\n'
1257         else:
1258             doc = getdoc(object) or ''
1259             return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
1260 
1261     def docother(self, object, name=None, mod=None, maxlen=None, doc=None):
1262         """Produce text documentation for a data object."""
1263         repr = self.repr(object)
1264         if maxlen:
1265             line = (name and name + ' = ' or '') + repr
1266             chop = maxlen - len(line)
1267             if chop < 0: repr = repr[:chop] + '...'
1268         line = (name and self.bold(name) + ' = ' or '') + repr
1269         if doc is not None:
1270             line += '\n' + self.indent(str(doc))
1271         return line
1272 
1273 # --------------------------------------------------------- user interfaces
1274 
1275 def pager(text):
1276     """The first time this is called, determine what kind of pager to use."""
1277     global pager
1278     pager = getpager()
1279     pager(text)
1280 
1281 def getpager():
1282     """Decide what method to use for paging through text."""
1283     if type(sys.stdout) is not types.FileType:
1284         return plainpager
1285     if not sys.stdin.isatty() or not sys.stdout.isatty():
1286         return plainpager
1287     if os.environ.get('TERM') in ['dumb', 'emacs']:
1288         return plainpager
1289     if 'PAGER' in os.environ:
1290         if sys.platform == 'win32': # pipes completely broken in Windows
1291             return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
1292         elif os.environ.get('TERM') in ['dumb', 'emacs']:
1293             return lambda text: pipepager(plain(text), os.environ['PAGER'])
1294         else:
1295             return lambda text: pipepager(text, os.environ['PAGER'])
1296     if sys.platform == 'win32' or sys.platform.startswith('os2'):
1297         return lambda text: tempfilepager(plain(text), 'more <')
1298     if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0:
1299         return lambda text: pipepager(text, 'less')
1300 
1301     import tempfile
1302     (fd, filename) = tempfile.mkstemp()
1303     os.close(fd)
1304     try:
1305         if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
1306             return lambda text: pipepager(text, 'more')
1307         else:
1308             return ttypager
1309     finally:
1310         os.unlink(filename)
1311 
1312 def plain(text):
1313     """Remove boldface formatting from text."""
1314     return re.sub('.\b', '', text)
1315 
1316 def pipepager(text, cmd):
1317     """Page through text by feeding it to another program."""
1318     pipe = os.popen(cmd, 'w')
1319     try:
1320         pipe.write(text)
1321         pipe.close()
1322     except IOError:
1323         pass # Ignore broken pipes caused by quitting the pager program.
1324 
1325 def tempfilepager(text, cmd):
1326     """Page through text by invoking a program on a temporary file."""
1327     import tempfile
1328     filename = tempfile.mktemp()
1329     file = open(filename, 'w')
1330     file.write(text)
1331     file.close()
1332     try:
1333         os.system(cmd + ' ' + filename)
1334     finally:
1335         os.unlink(filename)
1336 
1337 def ttypager(text):
1338     """Page through text on a text terminal."""
1339     lines = split(plain(text), '\n')
1340     try:
1341         import tty
1342         fd = sys.stdin.fileno()
1343         old = tty.tcgetattr(fd)
1344         tty.setcbreak(fd)
1345         getchar = lambda: sys.stdin.read(1)
1346     except (ImportError, AttributeError):
1347         tty = None
1348         getchar = lambda: sys.stdin.readline()[:-1][:1]
1349 
1350     try:
1351         r = inc = os.environ.get('LINES', 25) - 1
1352         sys.stdout.write(join(lines[:inc], '\n') + '\n')
1353         while lines[r:]:
1354             sys.stdout.write('-- more --')
1355             sys.stdout.flush()
1356             c = getchar()
1357 
1358             if c in ['q', 'Q']:
1359                 sys.stdout.write('\r          \r')
1360                 break
1361             elif c in ['\r', '\n']:
1362                 sys.stdout.write('\r          \r' + lines[r] + '\n')
1363                 r = r + 1
1364                 continue
1365             if c in ['b', 'B', '\x1b']:
1366                 r = r - inc - inc
1367                 if r < 0: r = 0
1368             sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1369             r = r + inc
1370 
1371     finally:
1372         if tty:
1373             tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1374 
1375 def plainpager(text):
1376     """Simply print unformatted text.  This is the ultimate fallback."""
1377     sys.stdout.write(plain(text))
1378 
1379 def describe(thing):
1380     """Produce a short description of the given thing."""
1381     if inspect.ismodule(thing):
1382         if thing.__name__ in sys.builtin_module_names:
1383             return 'built-in module ' + thing.__name__
1384         if hasattr(thing, '__path__'):
1385             return 'package ' + thing.__name__
1386         else:
1387             return 'module ' + thing.__name__
1388     if inspect.isbuiltin(thing):
1389         return 'built-in function ' + thing.__name__
1390     if inspect.isclass(thing):
1391         return 'class ' + thing.__name__
1392     if inspect.isfunction(thing):
1393         return 'function ' + thing.__name__
1394     if inspect.ismethod(thing):
1395         return 'method ' + thing.__name__
1396     if type(thing) is types.InstanceType:
1397         return 'instance of ' + thing.__class__.__name__
1398     return type(thing).__name__
1399 
1400 def locate(path, forceload=0):
1401     """Locate an object by name or dotted path, importing as necessary."""
1402     parts = [part for part in split(path, '.') if part]
1403     module, n = None, 0
1404     while n < len(parts):
1405         nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
1406         if nextmodule: module, n = nextmodule, n + 1
1407         else: break
1408     if module:
1409         object = module
1410         for part in parts[n:]:
1411             try: object = getattr(object, part)
1412             except AttributeError: return None
1413         return object
1414     else:
1415         if hasattr(__builtin__, path):
1416             return getattr(__builtin__, path)
1417 
1418 # --------------------------------------- interactive interpreter interface
1419 
1420 text = TextDoc()
1421 html = HTMLDoc()
1422 
1423 def resolve(thing, forceload=0):
1424     """Given an object or a path to an object, get the object and its name."""
1425     if isinstance(thing, str):
1426         object = locate(thing, forceload)
1427         if not object:
1428             raise ImportError, 'no Python documentation found for %r' % thing
1429         return object, thing
1430     else:
1431         return thing, getattr(thing, '__name__', None)
1432 
1433 def doc(thing, title='Python Library Documentation: %s', forceload=0):
1434     """Display text documentation, given an object or a path to an object."""
1435     try:
1436         object, name = resolve(thing, forceload)
1437         desc = describe(object)
1438         module = inspect.getmodule(object)
1439         if name and '.' in name:
1440             desc += ' in ' + name[:name.rfind('.')]
1441         elif module and module is not object:
1442             desc += ' in module ' + module.__name__
1443         pager(title % desc + '\n\n' + text.document(object, name))
1444     except (ImportError, ErrorDuringImport), value:
1445         print value
1446 
1447 def writedoc(thing, forceload=0):
1448     """Write HTML documentation to a file in the current directory."""
1449     try:
1450         object, name = resolve(thing, forceload)
1451         page = html.page(describe(object), html.document(object, name))
1452         file = open(name + '.html', 'w')
1453         file.write(page)
1454         file.close()
1455         print 'wrote', name + '.html'
1456     except (ImportError, ErrorDuringImport), value:
1457         print value
1458 
1459 def writedocs(dir, pkgpath='', done=None):
1460     """Write out HTML documentation for all modules in a directory tree."""
1461     if done is None: done = {}
1462     for file in os.listdir(dir):
1463         path = os.path.join(dir, file)
1464         if ispackage(path):
1465             writedocs(path, pkgpath + file + '.', done)
1466         elif os.path.isfile(path):
1467             modname = inspect.getmodulename(path)
1468             if modname:
1469                 if modname == '__init__':
1470                     modname = pkgpath[:-1] # remove trailing period
1471                 else:
1472                     modname = pkgpath + modname
1473                 if modname not in done:
1474                     done[modname] = 1
1475                     writedoc(modname)
1476 
1477 class Helper:
1478     keywords = {
1479         'and': 'BOOLEAN',
1480         'assert': ('ref/assert', ''),
1481         'break': ('ref/break', 'while for'),
1482         'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1483         'continue': ('ref/continue', 'while for'),
1484         'def': ('ref/function', ''),
1485         'del': ('ref/del', 'BASICMETHODS'),
1486         'elif': 'if',
1487         'else': ('ref/if', 'while for'),
1488         'except': 'try',
1489         'exec': ('ref/exec', ''),
1490         'finally': 'try',
1491         'for': ('ref/for', 'break continue while'),
1492         'from': 'import',
1493         'global': ('ref/global', 'NAMESPACES'),
1494         'if': ('ref/if', 'TRUTHVALUE'),
1495         'import': ('ref/import', 'MODULES'),
1496         'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1497         'is': 'COMPARISON',
1498         'lambda': ('ref/lambdas', 'FUNCTIONS'),
1499         'not': 'BOOLEAN',
1500         'or': 'BOOLEAN',
1501         'pass': ('ref/pass', ''),
1502         'print': ('ref/print', ''),
1503         'raise': ('ref/raise', 'EXCEPTIONS'),
1504         'return': ('ref/return', 'FUNCTIONS'),
1505         'try': ('ref/try', 'EXCEPTIONS'),
1506         'while': ('ref/while', 'break continue if TRUTHVALUE'),
1507         'yield': ('ref/yield', ''),
1508     }
1509 
1510     topics = {
1511         'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'),
1512         'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'),
1513         'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'),
1514         'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'),
1515         'UNICODE': ('ref/strings', 'encodings unicode SEQUENCES STRINGMETHODS FORMATTING TYPES'),
1516         'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1517         'INTEGER': ('ref/integers', 'int range'),
1518         'FLOAT': ('ref/floating', 'float math'),
1519         'COMPLEX': ('ref/imaginary', 'complex cmath'),
1520         'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
1521         'MAPPINGS': 'DICTIONARIES',
1522         'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'),
1523         'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'),
1524         'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'),
1525         'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'),
1526         'FRAMEOBJECTS': 'TYPES',
1527         'TRACEBACKS': 'TYPES',
1528         'NONE': ('lib/bltin-null-object', ''),
1529         'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'),
1530         'FILES': ('lib/bltin-file-objects', ''),
1531         'SPECIALATTRIBUTES': ('lib/specialattrs', ''),
1532         'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'),
1533         'MODULES': ('lib/typesmodules', 'import'),
1534         'PACKAGES': 'import',
1535         'EXPRESSIONS': ('ref/summary', 'lambda or and not in is BOOLEAN COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES LISTS DICTIONARIES BACKQUOTES'),
1536         'OPERATORS': 'EXPRESSIONS',
1537         'PRECEDENCE': 'EXPRESSIONS',
1538         'OBJECTS': ('ref/objects', 'TYPES'),
1539         'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
1540         'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'),
1541         'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1542         'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'),
1543         'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'),
1544         'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'),
1545         'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'),
1546         'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'),
1547         'EXECUTION': ('ref/execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'),
1548         'NAMESPACES': ('ref/naming', 'global ASSIGNMENT DELETION DYNAMICFEATURES'),
1549         'DYNAMICFEATURES': ('ref/dynamic-features', ''),
1550         'SCOPING': 'NAMESPACES',
1551         'FRAMES': 'NAMESPACES',
1552         'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'),
1553         'COERCIONS': ('ref/coercion-rules','CONVERSIONS'),
1554         'CONVERSIONS': ('ref/conversions', 'COERCIONS'),
1555         'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'),
1556         'SPECIALIDENTIFIERS': ('ref/id-classes', ''),
1557         'PRIVATENAMES': ('ref/atom-identifiers', ''),
1558         'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1559         'TUPLES': 'SEQUENCES',
1560         'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'),
1561         'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'),
1562         'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'),
1563         'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'),
1564         'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'),
1565         'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'),
1566         'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1567         'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'),
1568         'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'),
1569         'CALLS': ('ref/calls', 'EXPRESSIONS'),
1570         'POWER': ('ref/power', 'EXPRESSIONS'),
1571         'UNARY': ('ref/unary', 'EXPRESSIONS'),
1572         'BINARY': ('ref/binary', 'EXPRESSIONS'),
1573         'SHIFTING': ('ref/shifting', 'EXPRESSIONS'),
1574         'BITWISE': ('ref/bitwise', 'EXPRESSIONS'),
1575         'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'),
1576         'BOOLEAN': ('ref/Booleans', 'EXPRESSIONS TRUTHVALUE'),
1577         'ASSERTION': 'assert',
1578         'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'),
1579         'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'),
1580         'DELETION': 'del',
1581         'PRINTING': 'print',
1582         'RETURNING': 'return',
1583         'IMPORTING': 'import',
1584         'CONDITIONAL': 'if',
1585         'LOOPING': ('ref/compound', 'for while break continue'),
1586         'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'),
1587         'DEBUGGING': ('lib/module-pdb', 'pdb'),
1588     }
1589 
1590     def __init__(self, input, output):
1591         self.input = input
1592         self.output = output
1593         self.docdir = None
1594         execdir = os.path.dirname(sys.executable)
1595         homedir = os.environ.get('PYTHONHOME')
1596         for dir in [os.environ.get('PYTHONDOCS'),
1597                     homedir and os.path.join(homedir, 'doc'),
1598                     os.path.join(execdir, 'doc'),
1599                     '/usr/doc/python-docs-' + split(sys.version)[0],
1600                     '/usr/doc/python-' + split(sys.version)[0],
1601                     '/usr/doc/python-docs-' + sys.version[:3],
1602                     '/usr/doc/python-' + sys.version[:3],
1603                     os.path.join(sys.prefix, 'Resources/English.lproj/Documentation')]:
1604             if dir and os.path.isdir(os.path.join(dir, 'lib')):
1605                 self.docdir = dir
1606 
1607     def __repr__(self):
1608         if inspect.stack()[1][3] == '?':
1609             self()
1610             return ''
1611         return '<pydoc.Helper instance>'
1612 
1613     def __call__(self, request=None):
1614         if request is not None:
1615             self.help(request)
1616         else:
1617             self.intro()
1618             self.interact()
1619             self.output.write('''
1620 You are now leaving help and returning to the Python interpreter.
1621 If you want to ask for help on a particular object directly from the
1622 interpreter, you can type "help(object)".  Executing "help('string')"
1623 has the same effect as typing a particular string at the help> prompt.
1624 ''')
1625 
1626     def interact(self):
1627         self.output.write('\n')
1628         while True:
1629             try:
1630                 request = self.getline('help> ')
1631                 if not request: break
1632             except (KeyboardInterrupt, EOFError):
1633                 break
1634             request = strip(replace(request, '"', '', "'", ''))
1635             if lower(request) in ['q', 'quit']: break
1636             self.help(request)
1637 
1638     def getline(self, prompt):
1639         """Read one line, using raw_input when available."""
1640         if self.input is sys.stdin:
1641             return raw_input(prompt)
1642         else:
1643             self.output.write(prompt)
1644             self.output.flush()
1645             return self.input.readline()
1646 
1647     def help(self, request):
1648         if type(request) is type(''):
1649             if request == 'help': self.intro()
1650             elif request == 'keywords': self.listkeywords()
1651             elif request == 'topics': self.listtopics()
1652             elif request == 'modules': self.listmodules()
1653             elif request[:8] == 'modules ':
1654                 self.listmodules(split(request)[1])
1655             elif request in self.keywords: self.showtopic(request)
1656             elif request in self.topics: self.showtopic(request)
1657             elif request: doc(request, 'Help on %s:')
1658         elif isinstance(request, Helper): self()
1659         else: doc(request, 'Help on %s:')
1660         self.output.write('\n')
1661 
1662     def intro(self):
1663         self.output.write('''
1664 Welcome to Python %s!  This is the online help utility.
1665 
1666 If this is your first time using Python, you should definitely check out
1667 the tutorial on the Internet at http://www.python.org/doc/tut/.
1668 
1669 Enter the name of any module, keyword, or topic to get help on writing
1670 Python programs and using Python modules.  To quit this help utility and
1671 return to the interpreter, just type "quit".
1672 
1673 To get a list of available modules, keywords, or topics, type "modules",
1674 "keywords", or "topics".  Each module also comes with a one-line summary
1675 of what it does; to list the modules whose summaries contain a given word
1676 such as "spam", type "modules spam".
1677 ''' % sys.version[:3])
1678 
1679     def list(self, items, columns=4, width=80):
1680         items = items[:]
1681         items.sort()
1682         colw = width / columns
1683         rows = (len(items) + columns - 1) / columns
1684         for row in range(rows):
1685             for col in range(columns):
1686                 i = col * rows + row
1687                 if i < len(items):
1688                     self.output.write(items[i])
1689                     if col < columns - 1:
1690                         self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1691             self.output.write('\n')
1692 
1693     def listkeywords(self):
1694         self.output.write('''
1695 Here is a list of the Python keywords.  Enter any keyword to get more help.
1696 
1697 ''')
1698         self.list(self.keywords.keys())
1699 
1700     def listtopics(self):
1701         self.output.write('''
1702 Here is a list of available topics.  Enter any topic name to get more help.
1703 
1704 ''')
1705         self.list(self.topics.keys())
1706 
1707     def showtopic(self, topic):
1708         if not self.docdir:
1709             self.output.write('''
1710 Sorry, topic and keyword documentation is not available because the Python
1711 HTML documentation files could not be found.  If you have installed them,
1712 please set the environment variable PYTHONDOCS to indicate their location.
1713 ''')
1714             return
1715         target = self.topics.get(topic, self.keywords.get(topic))
1716         if not target:
1717             self.output.write('no documentation found for %s\n' % repr(topic))
1718             return
1719         if type(target) is type(''):
1720             return self.showtopic(target)
1721 
1722         filename, xrefs = target
1723         filename = self.docdir + '/' + filename + '.html'
1724         try:
1725             file = open(filename)
1726         except:
1727             self.output.write('could not read docs from %s\n' % filename)
1728             return
1729 
1730         divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
1731         addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
1732         document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1733         file.close()
1734 
1735         import htmllib, formatter, StringIO
1736         buffer = StringIO.StringIO()
1737         parser = htmllib.HTMLParser(
1738             formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1739         parser.start_table = parser.do_p
1740         parser.end_table = lambda parser=parser: parser.do_p({})
1741         parser.start_tr = parser.do_br
1742         parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1743         parser.feed(document)
1744         buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n  ')
1745         pager('  ' + strip(buffer) + '\n')
1746         if xrefs:
1747             buffer = StringIO.StringIO()
1748             formatter.DumbWriter(buffer).send_flowing_data(
1749                 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1750             self.output.write('\n%s\n' % buffer.getvalue())
1751 
1752     def listmodules(self, key=''):
1753         if key:
1754             self.output.write('''
1755 Here is a list of matching modules.  Enter any module name to get more help.
1756 
1757 ''')
1758             apropos(key)
1759         else:
1760             self.output.write('''
1761 Please wait a moment while I gather a list of all available modules...
1762 
1763 ''')
1764             modules = {}
1765             def callback(path, modname, desc, modules=modules):
1766                 if modname and modname[-9:] == '.__init__':
1767                     modname = modname[:-9] + ' (package)'
1768                 if find(modname, '.') < 0:
1769                     modules[modname] = 1
1770             ModuleScanner().run(callback)
1771             self.list(modules.keys())
1772             self.output.write('''
1773 Enter any module name to get more help.  Or, type "modules spam" to search
1774 for modules whose descriptions contain the word "spam".
1775 ''')
1776 
1777 help = Helper(sys.stdin, sys.stdout)
1778 
1779 class Scanner:
1780     """A generic tree iterator."""
1781     def __init__(self, roots, children, descendp):
1782         self.roots = roots[:]
1783         self.state = []
1784         self.children = children
1785         self.descendp = descendp
1786 
1787     def next(self):
1788         if not self.state:
1789             if not self.roots:
1790                 return None
1791             root = self.roots.pop(0)
1792             self.state = [(root, self.children(root))]
1793         node, children = self.state[-1]
1794         if not children:
1795             self.state.pop()
1796             return self.next()
1797         child = children.pop(0)
1798         if self.descendp(child):
1799             self.state.append((child, self.children(child)))
1800         return child
1801 
1802 class ModuleScanner(Scanner):
1803     """An interruptible scanner that searches module synopses."""
1804     def __init__(self):
1805         roots = map(lambda dir: (dir, ''), pathdirs())
1806         Scanner.__init__(self, roots, self.submodules, self.isnewpackage)
1807         self.inodes = map(lambda (dir, pkg): os.stat(dir).st_ino, roots)
1808 
1809     def submodules(self, (dir, package)):
1810         children = []
1811         for file in os.listdir(dir):
1812             path = os.path.join(dir, file)
1813             if ispackage(path):
1814                 children.append((path, package + (package and '.') + file))
1815             else:
1816                 children.append((path, package))
1817         children.sort() # so that spam.py comes before spam.pyc or spam.pyo
1818         return children
1819 
1820     def isnewpackage(self, (dir, package)):
1821         inode = os.path.exists(dir) and os.stat(dir).st_ino
1822         if not (os.path.islink(dir) and inode in self.inodes):
1823             self.inodes.append(inode) # detect circular symbolic links
1824             return ispackage(dir)
1825         return False
1826 
1827     def run(self, callback, key=None, completer=None):
1828         if key: key = lower(key)
1829         self.quit = False
1830         seen = {}
1831 
1832         for modname in sys.builtin_module_names:
1833             if modname != '__main__':
1834                 seen[modname] = 1
1835                 if key is None:
1836                     callback(None, modname, '')
1837                 else:
1838                     desc = split(__import__(modname).__doc__ or '', '\n')[0]
1839                     if find(lower(modname + ' - ' + desc), key) >= 0:
1840                         callback(None, modname, desc)
1841 
1842         while not self.quit:
1843             node = self.next()
1844             if not node: break
1845             path, package = node
1846             modname = inspect.getmodulename(path)
1847             if os.path.isfile(path) and modname:
1848                 modname = package + (package and '.') + modname
1849                 if not modname in seen:
1850                     seen[modname] = 1 # if we see spam.py, skip spam.pyc
1851                     if key is None:
1852                         callback(path, modname, '')
1853                     else:
1854                         desc = synopsis(path) or ''
1855                         if find(lower(modname + ' - ' + desc), key) >= 0:
1856                             callback(path, modname, desc)
1857         if completer: completer()
1858 
1859 def apropos(key):
1860     """Print all the one-line module summaries that contain a substring."""
1861     def callback(path, modname, desc):
1862         if modname[-9:] == '.__init__':
1863             modname = modname[:-9] + ' (package)'
1864         print modname, desc and '- ' + desc
1865     try: import warnings
1866     except ImportError: pass
1867     else: warnings.filterwarnings('ignore') # ignore problems during import
1868     ModuleScanner().run(callback, key)
1869 
1870 # --------------------------------------------------- web browser interface
1871 
1872 def serve(port, callback=None, completer=None):
1873     import BaseHTTPServer, mimetools, select
1874 
1875     # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1876     class Message(mimetools.Message):
1877         def __init__(self, fp, seekable=1):
1878             Message = self.__class__
1879             Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1880             self.encodingheader = self.getheader('content-transfer-encoding')
1881             self.typeheader = self.getheader('content-type')
1882             self.parsetype()
1883             self.parseplist()
1884 
1885     class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1886         def send_document(self, title, contents):
1887             try:
1888                 self.send_response(200)
1889                 self.send_header('Content-Type', 'text/html')
1890                 self.end_headers()
1891                 self.wfile.write(html.page(title, contents))
1892             except IOError: pass
1893 
1894         def do_GET(self):
1895             path = self.path
1896             if path[-5:] == '.html': path = path[:-5]
1897             if path[:1] == '/': path = path[1:]
1898             if path and path != '.':
1899                 try:
1900                     obj = locate(path, forceload=1)
1901                 except ErrorDuringImport, value:
1902                     self.send_document(path, html.escape(str(value)))
1903                     return
1904                 if obj:
1905                     self.send_document(describe(obj), html.document(obj, path))
1906                 else:
1907                     self.send_document(path,
1908 'no Python documentation found for %s' % repr(path))
1909             else:
1910                 heading = html.heading(
1911 '<big><big><strong>Python: Index of Modules</strong></big></big>',
1912 '#ffffff', '#7799ee')
1913                 def bltinlink(name):
1914                     return '<a href="%s.html">%s</a>' % (name, name)
1915                 names = filter(lambda x: x != '__main__',
1916                                sys.builtin_module_names)
1917                 contents = html.multicolumn(names, bltinlink)
1918                 indices = ['<p>' + html.bigsection(
1919                     'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1920 
1921                 seen = {}
1922                 for dir in pathdirs():
1923                     indices.append(html.index(dir, seen))
1924                 contents = heading + join(indices) + '''<p align=right>
1925 <font color="#909090" face="helvetica, arial"><strong>
1926 pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font>'''
1927                 self.send_document('Index of Modules', contents)
1928 
1929         def log_message(self, *args): pass
1930 
1931     class DocServer(BaseHTTPServer.HTTPServer):
1932         def __init__(self, port, callback):
1933             host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
1934             self.address = ('', port)
1935             self.url = 'http://%s:%d/' % (host, port)
1936             self.callback = callback
1937             self.base.__init__(self, self.address, self.handler)
1938 
1939         def serve_until_quit(self):
1940             import select
1941             self.quit = False
1942             while not self.quit:
1943                 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1944                 if rd: self.handle_request()
1945 
1946         def server_activate(self):
1947             self.base.server_activate(self)
1948             if self.callback: self.callback(self)
1949 
1950     DocServer.base = BaseHTTPServer.HTTPServer
1951     DocServer.handler = DocHandler
1952     DocHandler.MessageClass = Message
1953     try:
1954         try:
1955             DocServer(port, callback).serve_until_quit()
1956         except (KeyboardInterrupt, select.error):
1957             pass
1958     finally:
1959         if completer: completer()
1960 
1961 # ----------------------------------------------------- graphical interface
1962 
1963 def gui():
1964     """Graphical interface (starts web server and pops up a control window)."""
1965     class GUI:
1966         def __init__(self, window, port=7464):
1967             self.window = window
1968             self.server = None
1969             self.scanner = None
1970 
1971             import Tkinter
1972             self.server_frm = Tkinter.Frame(window)
1973             self.title_lbl = Tkinter.Label(self.server_frm,
1974                 text='Starting server...\n ')
1975             self.open_btn = Tkinter.Button(self.server_frm,
1976                 text='open browser', command=self.open, state='disabled')
1977             self.quit_btn = Tkinter.Button(self.server_frm,
1978                 text='quit serving', command=self.quit, state='disabled')
1979 
1980             self.search_frm = Tkinter.Frame(window)
1981             self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
1982             self.search_ent = Tkinter.Entry(self.search_frm)
1983             self.search_ent.bind('<Return>', self.search)
1984             self.stop_btn = Tkinter.Button(self.search_frm,
1985                 text='stop', pady=0, command=self.stop, state='disabled')
1986             if sys.platform == 'win32':
1987                 # Trying to hide and show this button crashes under Windows.
1988                 self.stop_btn.pack(side='right')
1989 
1990             self.window.title('pydoc')
1991             self.window.protocol('WM_DELETE_WINDOW', self.quit)
1992             self.title_lbl.pack(side='top', fill='x')
1993             self.open_btn.pack(side='left', fill='x', expand=1)
1994             self.quit_btn.pack(side='right', fill='x', expand=1)
1995             self.server_frm.pack(side='top', fill='x')
1996 
1997             self.search_lbl.pack(side='left')
1998             self.search_ent.pack(side='right', fill='x', expand=1)
1999             self.search_frm.pack(side='top', fill='x')
2000             self.search_ent.focus_set()
2001 
2002             font = ('helvetica', sys.platform == 'win32' and 8 or 10)
2003             self.result_lst = Tkinter.Listbox(window, font=font, height=6)
2004             self.result_lst.bind('<Button-1>', self.select)
2005             self.result_lst.bind('<Double-Button-1>', self.goto)
2006             self.result_scr = Tkinter.Scrollbar(window,
2007                 orient='vertical', command=self.result_lst.yview)
2008             self.result_lst.config(yscrollcommand=self.result_scr.set)
2009 
2010             self.result_frm = Tkinter.Frame(window)
2011             self.goto_btn = Tkinter.Button(self.result_frm,
2012                 text='go to selected', command=self.goto)
2013             self.hide_btn = Tkinter.Button(self.result_frm,
2014                 text='hide results', command=self.hide)
2015             self.goto_btn.pack(side='left', fill='x', expand=1)
2016             self.hide_btn.pack(side='right', fill='x', expand=1)
2017 
2018             self.window.update()
2019             self.minwidth = self.window.winfo_width()
2020             self.minheight = self.window.winfo_height()
2021             self.bigminheight = (self.server_frm.winfo_reqheight() +
2022                                  self.search_frm.winfo_reqheight() +
2023                                  self.result_lst.winfo_reqheight() +
2024                                  self.result_frm.winfo_reqheight())
2025             self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
2026             self.expanded = 0
2027             self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
2028             self.window.wm_minsize(self.minwidth, self.minheight)
2029             self.window.tk.willdispatch()
2030 
2031             import threading
2032             threading.Thread(
2033                 target=serve, args=(port, self.ready, self.quit)).start()
2034 
2035         def ready(self, server):
2036             self.server = server
2037             self.title_lbl.config(
2038                 text='Python documentation server at\n' + server.url)
2039             self.open_btn.config(state='normal')
2040             self.quit_btn.config(state='normal')
2041 
2042         def open(self, event=None, url=None):
2043             url = url or self.server.url
2044             try:
2045                 import webbrowser
2046                 webbrowser.open(url)
2047             except ImportError: # pre-webbrowser.py compatibility
2048                 if sys.platform == 'win32':
2049                     os.system('start "%s"' % url)
2050                 elif sys.platform == 'mac':
2051                     try: import ic
2052                     except ImportError: pass
2053                     else: ic.launchurl(url)
2054                 else:
2055                     rc = os.system('netscape -remote "openURL(%s)" &' % url)
2056                     if rc: os.system('netscape "%s" &' % url)
2057 
2058         def quit(self, event=None):
2059             if self.server:
2060                 self.server.quit = 1
2061             self.window.quit()
2062 
2063         def search(self, event=None):
2064             key = self.search_ent.get()
2065             self.stop_btn.pack(side='right')
2066             self.stop_btn.config(state='normal')
2067             self.search_lbl.config(text='Searching for "%s"...' % key)
2068             self.search_ent.forget()
2069             self.search_lbl.pack(side='left')
2070             self.result_lst.delete(0, 'end')
2071             self.goto_btn.config(state='disabled')
2072             self.expand()
2073 
2074             import threading
2075             if self.scanner:
2076                 self.scanner.quit = 1
2077             self.scanner = ModuleScanner()
2078             threading.Thread(target=self.scanner.run,
2079                              args=(self.update, key, self.done)).start()
2080 
2081         def update(self, path, modname, desc):
2082             if modname[-9:] == '.__init__':
2083                 modname = modname[:-9] + ' (package)'
2084             self.result_lst.insert('end',
2085                 modname + ' - ' + (desc or '(no description)'))
2086 
2087         def stop(self, event=None):
2088             if self.scanner:
2089                 self.scanner.quit = 1
2090                 self.scanner = None
2091 
2092         def done(self):
2093             self.scanner = None
2094             self.search_lbl.config(text='Search for')
2095             self.search_lbl.pack(side='left')
2096             self.search_ent.pack(side='right', fill='x', expand=1)
2097             if sys.platform != 'win32': self.stop_btn.forget()
2098             self.stop_btn.config(state='disabled')
2099 
2100         def select(self, event=None):
2101             self.goto_btn.config(state='normal')
2102 
2103         def goto(self, event=None):
2104             selection = self.result_lst.curselection()
2105             if selection:
2106                 modname = split(self.result_lst.get(selection[0]))[0]
2107                 self.open(url=self.server.url + modname + '.html')
2108 
2109         def collapse(self):
2110             if not self.expanded: return
2111             self.result_frm.forget()
2112             self.result_scr.forget()
2113             self.result_lst.forget()
2114             self.bigwidth = self.window.winfo_width()
2115             self.bigheight = self.window.winfo_height()
2116             self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
2117             self.window.wm_minsize(self.minwidth, self.minheight)
2118             self.expanded = 0
2119 
2120         def expand(self):
2121             if self.expanded: return
2122             self.result_frm.pack(side='bottom', fill='x')
2123             self.result_scr.pack(side='right', fill='y')
2124             self.result_lst.pack(side='top', fill='both', expand=1)
2125             self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
2126             self.window.wm_minsize(self.minwidth, self.bigminheight)
2127             self.expanded = 1
2128 
2129         def hide(self, event=None):
2130             self.stop()
2131             self.collapse()
2132 
2133     import Tkinter
2134     try:
2135         root = Tkinter.Tk()
2136         # Tk will crash if pythonw.exe has an XP .manifest
2137         # file and the root has is not destroyed explicitly.
2138         # If the problem is ever fixed in Tk, the explicit
2139         # destroy can go.
2140         try:
2141             gui = GUI(root)
2142             root.mainloop()
2143         finally:
2144             root.destroy()
2145     except KeyboardInterrupt:
2146         pass
2147 
2148 # -------------------------------------------------- command-line interface
2149 
2150 def ispath(x):
2151     return isinstance(x, str) and find(x, os.sep) >= 0
2152 
2153 def cli():
2154     """Command-line interface (looks at sys.argv to decide what to do)."""
2155     import getopt
2156     class BadUsage: pass
2157 
2158     # Scripts don't get the current directory in their path by default.
2159     scriptdir = os.path.dirname(sys.argv[0])
2160     if scriptdir in sys.path:
2161         sys.path.remove(scriptdir)
2162     sys.path.insert(0, '.')
2163 
2164     try:
2165         opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
2166         writing = 0
2167 
2168         for opt, val in opts:
2169             if opt == '-g':
2170                 gui()
2171                 return
2172             if opt == '-k':
2173                 apropos(val)
2174                 return
2175             if opt == '-p':
2176                 try:
2177                     port = int(val)
2178                 except ValueError:
2179                     raise BadUsage
2180                 def ready(server):
2181                     print 'pydoc server ready at %s' % server.url
2182                 def stopped():
2183                     print 'pydoc server stopped'
2184                 serve(port, ready, stopped)
2185                 return
2186             if opt == '-w':
2187                 writing = 1
2188 
2189         if not args: raise BadUsage
2190         for arg in args:
2191             if ispath(arg) and not os.path.exists(arg):
2192                 print 'file %r does not exist' % arg
2193                 break
2194             try:
2195                 if ispath(arg) and os.path.isfile(arg):
2196                     arg = importfile(arg)
2197                 if writing:
2198                     if ispath(arg) and os.path.isdir(arg):
2199                         writedocs(arg)
2200                     else:
2201                         writedoc(arg)
2202                 else:
2203                     help.help(arg)
2204             except ErrorDuringImport, value:
2205                 print value
2206 
2207     except (getopt.error, BadUsage):
2208         cmd = os.path.basename(sys.argv[0])
2209         print """pydoc - the Python documentation tool
2210 
2211 %s <name> ...
2212     Show text documentation on something.  <name> may be the name of a
2213     Python keyword, topic, function, module, or package, or a dotted
2214     reference to a class or function within a module or module in a
2215     package.  If <name> contains a '%s', it is used as the path to a
2216     Python source file to document. If name is 'keywords', 'topics',
2217     or 'modules', a listing of these things is displayed.
2218 
2219 %s -k <keyword>
2220     Search for a keyword in the synopsis lines of all available modules.
2221 
2222 %s -p <port>
2223     Start an HTTP server on the given port on the local machine.
2224 
2225 %s -g
2226     Pop up a graphical interface for finding and serving documentation.
2227 
2228 %s -w <name> ...
2229     Write out the HTML documentation for a module to a file in the current
2230     directory.  If <name> contains a '%s', it is treated as a filename; if
2231     it names a directory, documentation is written for all the contents.
2232 """ % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
2233 
2234 if __name__ == '__main__': cli()
2235 

Generated by PyXR 0.9.4
SourceForge.net Logo