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