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 sys.platform == 'uefi':
1330            return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
1331        elif os.environ.get('TERM') in ('dumb', 'emacs'):
1332            return lambda text: pipepager(plain(text), os.environ['PAGER'])
1333        else:
1334            return lambda text: pipepager(text, os.environ['PAGER'])
1335    if os.environ.get('TERM') in ('dumb', 'emacs'):
1336        return plainpager
1337    if sys.platform == 'uefi':
1338        return plainpager
1339    if sys.platform == 'win32' or sys.platform.startswith('os2'):
1340        return lambda text: tempfilepager(plain(text), 'more <')
1341    if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0:
1342        return lambda text: pipepager(text, 'less')
1343
1344    import tempfile
1345    (fd, filename) = tempfile.mkstemp()
1346    os.close(fd)
1347    try:
1348        if hasattr(os, 'system') and os.system('more "%s"' % filename) == 0:
1349            return lambda text: pipepager(text, 'more')
1350        else:
1351            return ttypager
1352    finally:
1353        os.unlink(filename)
1354
1355def plain(text):
1356    """Remove boldface formatting from text."""
1357    return re.sub('.\b', '', text)
1358
1359def pipepager(text, cmd):
1360    """Page through text by feeding it to another program."""
1361    pipe = os.popen(cmd, 'w')
1362    try:
1363        pipe.write(text)
1364        pipe.close()
1365    except IOError:
1366        pass # Ignore broken pipes caused by quitting the pager program.
1367
1368def tempfilepager(text, cmd):
1369    """Page through text by invoking a program on a temporary file."""
1370    import tempfile
1371    filename = tempfile.mktemp()
1372    file = open(filename, 'w')
1373    file.write(text)
1374    file.close()
1375    try:
1376        os.system(cmd + ' "' + filename + '"')
1377    finally:
1378        os.unlink(filename)
1379
1380def ttypager(text):
1381    """Page through text on a text terminal."""
1382    lines = split(plain(text), '\n')
1383    try:
1384        import tty
1385        fd = sys.stdin.fileno()
1386        old = tty.tcgetattr(fd)
1387        tty.setcbreak(fd)
1388        getchar = lambda: sys.stdin.read(1)
1389    except (ImportError, AttributeError):
1390        tty = None
1391        getchar = lambda: sys.stdin.readline()[:-1][:1]
1392
1393    try:
1394        r = inc = os.environ.get('LINES', 25) - 1
1395        sys.stdout.write(join(lines[:inc], '\n') + '\n')
1396        while lines[r:]:
1397            sys.stdout.write('-- more --')
1398            sys.stdout.flush()
1399            c = getchar()
1400
1401            if c in ('q', 'Q'):
1402                sys.stdout.write('\r          \r')
1403                break
1404            elif c in ('\r', '\n'):
1405                sys.stdout.write('\r          \r' + lines[r] + '\n')
1406                r = r + 1
1407                continue
1408            if c in ('b', 'B', '\x1b'):
1409                r = r - inc - inc
1410                if r < 0: r = 0
1411            sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1412            r = r + inc
1413
1414    finally:
1415        if tty:
1416            tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1417
1418def plainpager(text):
1419    """Simply print unformatted text.  This is the ultimate fallback."""
1420    sys.stdout.write(plain(text))
1421
1422def describe(thing):
1423    """Produce a short description of the given thing."""
1424    if inspect.ismodule(thing):
1425        if thing.__name__ in sys.builtin_module_names:
1426            return 'built-in module ' + thing.__name__
1427        if hasattr(thing, '__path__'):
1428            return 'package ' + thing.__name__
1429        else:
1430            return 'module ' + thing.__name__
1431    if inspect.isbuiltin(thing):
1432        return 'built-in function ' + thing.__name__
1433    if inspect.isgetsetdescriptor(thing):
1434        return 'getset descriptor %s.%s.%s' % (
1435            thing.__objclass__.__module__, thing.__objclass__.__name__,
1436            thing.__name__)
1437    if inspect.ismemberdescriptor(thing):
1438        return 'member descriptor %s.%s.%s' % (
1439            thing.__objclass__.__module__, thing.__objclass__.__name__,
1440            thing.__name__)
1441    if inspect.isclass(thing):
1442        return 'class ' + thing.__name__
1443    if inspect.isfunction(thing):
1444        return 'function ' + thing.__name__
1445    if inspect.ismethod(thing):
1446        return 'method ' + thing.__name__
1447    if type(thing) is types.InstanceType:
1448        return 'instance of ' + thing.__class__.__name__
1449    return type(thing).__name__
1450
1451def locate(path, forceload=0):
1452    """Locate an object by name or dotted path, importing as necessary."""
1453    parts = [part for part in split(path, '.') if part]
1454    module, n = None, 0
1455    while n < len(parts):
1456        nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
1457        if nextmodule: module, n = nextmodule, n + 1
1458        else: break
1459    if module:
1460        object = module
1461        for part in parts[n:]:
1462            try: object = getattr(object, part)
1463            except AttributeError: return None
1464        return object
1465    else:
1466        if hasattr(__builtin__, path):
1467            return getattr(__builtin__, path)
1468
1469# --------------------------------------- interactive interpreter interface
1470
1471text = TextDoc()
1472html = HTMLDoc()
1473
1474class _OldStyleClass: pass
1475_OLD_INSTANCE_TYPE = type(_OldStyleClass())
1476
1477def resolve(thing, forceload=0):
1478    """Given an object or a path to an object, get the object and its name."""
1479    if isinstance(thing, str):
1480        object = locate(thing, forceload)
1481        if not object:
1482            raise ImportError, 'no Python documentation found for %r' % thing
1483        return object, thing
1484    else:
1485        return thing, getattr(thing, '__name__', None)
1486
1487def render_doc(thing, title='Python Library Documentation: %s', forceload=0):
1488    """Render text documentation, given an object or a path to an object."""
1489    object, name = resolve(thing, forceload)
1490    desc = describe(object)
1491    module = inspect.getmodule(object)
1492    if name and '.' in name:
1493        desc += ' in ' + name[:name.rfind('.')]
1494    elif module and module is not object:
1495        desc += ' in module ' + module.__name__
1496    if type(object) is _OLD_INSTANCE_TYPE:
1497        # If the passed object is an instance of an old-style class,
1498        # document its available methods instead of its value.
1499        object = object.__class__
1500    elif not (inspect.ismodule(object) or
1501              inspect.isclass(object) or
1502              inspect.isroutine(object) or
1503              inspect.isgetsetdescriptor(object) or
1504              inspect.ismemberdescriptor(object) or
1505              isinstance(object, property)):
1506        # If the passed object is a piece of data or an instance,
1507        # document its available methods instead of its value.
1508        object = type(object)
1509        desc += ' object'
1510    return title % desc + '\n\n' + text.document(object, name)
1511
1512def doc(thing, title='Python Library Documentation: %s', forceload=0):
1513    """Display text documentation, given an object or a path to an object."""
1514    try:
1515        pager(render_doc(thing, title, forceload))
1516    except (ImportError, ErrorDuringImport), value:
1517        print value
1518
1519def writedoc(thing, forceload=0):
1520    """Write HTML documentation to a file in the current directory."""
1521    try:
1522        object, name = resolve(thing, forceload)
1523        page = html.page(describe(object), html.document(object, name))
1524        file = open(name + '.html', 'w')
1525        file.write(page)
1526        file.close()
1527        print 'wrote', name + '.html'
1528    except (ImportError, ErrorDuringImport), value:
1529        print value
1530
1531def writedocs(dir, pkgpath='', done=None):
1532    """Write out HTML documentation for all modules in a directory tree."""
1533    if done is None: done = {}
1534    for importer, modname, ispkg in pkgutil.walk_packages([dir], pkgpath):
1535        writedoc(modname)
1536    return
1537
1538class Helper:
1539
1540    # These dictionaries map a topic name to either an alias, or a tuple
1541    # (label, seealso-items).  The "label" is the label of the corresponding
1542    # section in the .rst file under Doc/ and an index into the dictionary
1543    # in pydoc_data/topics.py.
1544    #
1545    # CAUTION: if you change one of these dictionaries, be sure to adapt the
1546    #          list of needed labels in Doc/tools/sphinxext/pyspecific.py and
1547    #          regenerate the pydoc_data/topics.py file by running
1548    #              make pydoc-topics
1549    #          in Doc/ and copying the output file into the Lib/ directory.
1550
1551    keywords = {
1552        'and': 'BOOLEAN',
1553        'as': 'with',
1554        'assert': ('assert', ''),
1555        'break': ('break', 'while for'),
1556        'class': ('class', 'CLASSES SPECIALMETHODS'),
1557        'continue': ('continue', 'while for'),
1558        'def': ('function', ''),
1559        'del': ('del', 'BASICMETHODS'),
1560        'elif': 'if',
1561        'else': ('else', 'while for'),
1562        'except': 'try',
1563        'exec': ('exec', ''),
1564        'finally': 'try',
1565        'for': ('for', 'break continue while'),
1566        'from': 'import',
1567        'global': ('global', 'NAMESPACES'),
1568        'if': ('if', 'TRUTHVALUE'),
1569        'import': ('import', 'MODULES'),
1570        'in': ('in', 'SEQUENCEMETHODS2'),
1571        'is': 'COMPARISON',
1572        'lambda': ('lambda', 'FUNCTIONS'),
1573        'not': 'BOOLEAN',
1574        'or': 'BOOLEAN',
1575        'pass': ('pass', ''),
1576        'print': ('print', ''),
1577        'raise': ('raise', 'EXCEPTIONS'),
1578        'return': ('return', 'FUNCTIONS'),
1579        'try': ('try', 'EXCEPTIONS'),
1580        'while': ('while', 'break continue if TRUTHVALUE'),
1581        'with': ('with', 'CONTEXTMANAGERS EXCEPTIONS yield'),
1582        'yield': ('yield', ''),
1583    }
1584    # Either add symbols to this dictionary or to the symbols dictionary
1585    # directly: Whichever is easier. They are merged later.
1586    _symbols_inverse = {
1587        'STRINGS' : ("'", "'''", "r'", "u'", '"""', '"', 'r"', 'u"'),
1588        'OPERATORS' : ('+', '-', '*', '**', '/', '//', '%', '<<', '>>', '&',
1589                       '|', '^', '~', '<', '>', '<=', '>=', '==', '!=', '<>'),
1590        'COMPARISON' : ('<', '>', '<=', '>=', '==', '!=', '<>'),
1591        'UNARY' : ('-', '~'),
1592        'AUGMENTEDASSIGNMENT' : ('+=', '-=', '*=', '/=', '%=', '&=', '|=',
1593                                '^=', '<<=', '>>=', '**=', '//='),
1594        'BITWISE' : ('<<', '>>', '&', '|', '^', '~'),
1595        'COMPLEX' : ('j', 'J')
1596    }
1597    symbols = {
1598        '%': 'OPERATORS FORMATTING',
1599        '**': 'POWER',
1600        ',': 'TUPLES LISTS FUNCTIONS',
1601        '.': 'ATTRIBUTES FLOAT MODULES OBJECTS',
1602        '...': 'ELLIPSIS',
1603        ':': 'SLICINGS DICTIONARYLITERALS',
1604        '@': 'def class',
1605        '\\': 'STRINGS',
1606        '_': 'PRIVATENAMES',
1607        '__': 'PRIVATENAMES SPECIALMETHODS',
1608        '`': 'BACKQUOTES',
1609        '(': 'TUPLES FUNCTIONS CALLS',
1610        ')': 'TUPLES FUNCTIONS CALLS',
1611        '[': 'LISTS SUBSCRIPTS SLICINGS',
1612        ']': 'LISTS SUBSCRIPTS SLICINGS'
1613    }
1614    for topic, symbols_ in _symbols_inverse.iteritems():
1615        for symbol in symbols_:
1616            topics = symbols.get(symbol, topic)
1617            if topic not in topics:
1618                topics = topics + ' ' + topic
1619            symbols[symbol] = topics
1620
1621    topics = {
1622        'TYPES': ('types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS '
1623                  'FUNCTIONS CLASSES MODULES FILES inspect'),
1624        'STRINGS': ('strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING '
1625                    'TYPES'),
1626        'STRINGMETHODS': ('string-methods', 'STRINGS FORMATTING'),
1627        'FORMATTING': ('formatstrings', 'OPERATORS'),
1628        'UNICODE': ('strings', 'encodings unicode SEQUENCES STRINGMETHODS '
1629                    'FORMATTING TYPES'),
1630        'NUMBERS': ('numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1631        'INTEGER': ('integers', 'int range'),
1632        'FLOAT': ('floating', 'float math'),
1633        'COMPLEX': ('imaginary', 'complex cmath'),
1634        'SEQUENCES': ('typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
1635        'MAPPINGS': 'DICTIONARIES',
1636        'FUNCTIONS': ('typesfunctions', 'def TYPES'),
1637        'METHODS': ('typesmethods', 'class def CLASSES TYPES'),
1638        'CODEOBJECTS': ('bltin-code-objects', 'compile FUNCTIONS TYPES'),
1639        'TYPEOBJECTS': ('bltin-type-objects', 'types TYPES'),
1640        'FRAMEOBJECTS': 'TYPES',
1641        'TRACEBACKS': 'TYPES',
1642        'NONE': ('bltin-null-object', ''),
1643        'ELLIPSIS': ('bltin-ellipsis-object', 'SLICINGS'),
1644        'FILES': ('bltin-file-objects', ''),
1645        'SPECIALATTRIBUTES': ('specialattrs', ''),
1646        'CLASSES': ('types', 'class SPECIALMETHODS PRIVATENAMES'),
1647        'MODULES': ('typesmodules', 'import'),
1648        'PACKAGES': 'import',
1649        'EXPRESSIONS': ('operator-summary', 'lambda or and not in is BOOLEAN '
1650                        'COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER '
1651                        'UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES '
1652                        'LISTS DICTIONARIES BACKQUOTES'),
1653        'OPERATORS': 'EXPRESSIONS',
1654        'PRECEDENCE': 'EXPRESSIONS',
1655        'OBJECTS': ('objects', 'TYPES'),
1656        'SPECIALMETHODS': ('specialnames', 'BASICMETHODS ATTRIBUTEMETHODS '
1657                           'CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS '
1658                           'SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
1659        'BASICMETHODS': ('customization', 'cmp hash repr str SPECIALMETHODS'),
1660        'ATTRIBUTEMETHODS': ('attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1661        'CALLABLEMETHODS': ('callable-types', 'CALLS SPECIALMETHODS'),
1662        'SEQUENCEMETHODS1': ('sequence-types', 'SEQUENCES SEQUENCEMETHODS2 '
1663                             'SPECIALMETHODS'),
1664        'SEQUENCEMETHODS2': ('sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 '
1665                             'SPECIALMETHODS'),
1666        'MAPPINGMETHODS': ('sequence-types', 'MAPPINGS SPECIALMETHODS'),
1667        'NUMBERMETHODS': ('numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT '
1668                          'SPECIALMETHODS'),
1669        'EXECUTION': ('execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'),
1670        'NAMESPACES': ('naming', 'global ASSIGNMENT DELETION DYNAMICFEATURES'),
1671        'DYNAMICFEATURES': ('dynamic-features', ''),
1672        'SCOPING': 'NAMESPACES',
1673        'FRAMES': 'NAMESPACES',
1674        'EXCEPTIONS': ('exceptions', 'try except finally raise'),
1675        'COERCIONS': ('coercion-rules','CONVERSIONS'),
1676        'CONVERSIONS': ('conversions', 'COERCIONS'),
1677        'IDENTIFIERS': ('identifiers', 'keywords SPECIALIDENTIFIERS'),
1678        'SPECIALIDENTIFIERS': ('id-classes', ''),
1679        'PRIVATENAMES': ('atom-identifiers', ''),
1680        'LITERALS': ('atom-literals', 'STRINGS BACKQUOTES NUMBERS '
1681                     'TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1682        'TUPLES': 'SEQUENCES',
1683        'TUPLELITERALS': ('exprlists', 'TUPLES LITERALS'),
1684        'LISTS': ('typesseq-mutable', 'LISTLITERALS'),
1685        'LISTLITERALS': ('lists', 'LISTS LITERALS'),
1686        'DICTIONARIES': ('typesmapping', 'DICTIONARYLITERALS'),
1687        'DICTIONARYLITERALS': ('dict', 'DICTIONARIES LITERALS'),
1688        'BACKQUOTES': ('string-conversions', 'repr str STRINGS LITERALS'),
1689        'ATTRIBUTES': ('attribute-references', 'getattr hasattr setattr '
1690                       'ATTRIBUTEMETHODS'),
1691        'SUBSCRIPTS': ('subscriptions', 'SEQUENCEMETHODS1'),
1692        'SLICINGS': ('slicings', 'SEQUENCEMETHODS2'),
1693        'CALLS': ('calls', 'EXPRESSIONS'),
1694        'POWER': ('power', 'EXPRESSIONS'),
1695        'UNARY': ('unary', 'EXPRESSIONS'),
1696        'BINARY': ('binary', 'EXPRESSIONS'),
1697        'SHIFTING': ('shifting', 'EXPRESSIONS'),
1698        'BITWISE': ('bitwise', 'EXPRESSIONS'),
1699        'COMPARISON': ('comparisons', 'EXPRESSIONS BASICMETHODS'),
1700        'BOOLEAN': ('booleans', 'EXPRESSIONS TRUTHVALUE'),
1701        'ASSERTION': 'assert',
1702        'ASSIGNMENT': ('assignment', 'AUGMENTEDASSIGNMENT'),
1703        'AUGMENTEDASSIGNMENT': ('augassign', 'NUMBERMETHODS'),
1704        'DELETION': 'del',
1705        'PRINTING': 'print',
1706        'RETURNING': 'return',
1707        'IMPORTING': 'import',
1708        'CONDITIONAL': 'if',
1709        'LOOPING': ('compound', 'for while break continue'),
1710        'TRUTHVALUE': ('truth', 'if while and or not BASICMETHODS'),
1711        'DEBUGGING': ('debugger', 'pdb'),
1712        'CONTEXTMANAGERS': ('context-managers', 'with'),
1713    }
1714
1715    def __init__(self, input=None, output=None):
1716        self._input = input
1717        self._output = output
1718
1719    input  = property(lambda self: self._input or sys.stdin)
1720    output = property(lambda self: self._output or sys.stdout)
1721
1722    def __repr__(self):
1723        if inspect.stack()[1][3] == '?':
1724            self()
1725            return ''
1726        return '<pydoc.Helper instance>'
1727
1728    _GoInteractive = object()
1729    def __call__(self, request=_GoInteractive):
1730        if request is not self._GoInteractive:
1731            self.help(request)
1732        else:
1733            self.intro()
1734            self.interact()
1735            self.output.write('''
1736You are now leaving help and returning to the Python interpreter.
1737If you want to ask for help on a particular object directly from the
1738interpreter, you can type "help(object)".  Executing "help('string')"
1739has the same effect as typing a particular string at the help> prompt.
1740''')
1741
1742    def interact(self):
1743        self.output.write('\n')
1744        while True:
1745            try:
1746                request = self.getline('help> ')
1747                if not request: break
1748            except (KeyboardInterrupt, EOFError):
1749                break
1750            request = strip(replace(request, '"', '', "'", ''))
1751            if lower(request) in ('q', 'quit'): break
1752            self.help(request)
1753
1754    def getline(self, prompt):
1755        """Read one line, using raw_input when available."""
1756        if self.input is sys.stdin:
1757            return raw_input(prompt)
1758        else:
1759            self.output.write(prompt)
1760            self.output.flush()
1761            return self.input.readline()
1762
1763    def help(self, request):
1764        if type(request) is type(''):
1765            request = request.strip()
1766            if request == 'help': self.intro()
1767            elif request == 'keywords': self.listkeywords()
1768            elif request == 'symbols': self.listsymbols()
1769            elif request == 'topics': self.listtopics()
1770            elif request == 'modules': self.listmodules()
1771            elif request[:8] == 'modules ':
1772                self.listmodules(split(request)[1])
1773            elif request in self.symbols: self.showsymbol(request)
1774            elif request in self.keywords: self.showtopic(request)
1775            elif request in self.topics: self.showtopic(request)
1776            elif request: doc(request, 'Help on %s:')
1777        elif isinstance(request, Helper): self()
1778        else: doc(request, 'Help on %s:')
1779        self.output.write('\n')
1780
1781    def intro(self):
1782        self.output.write('''
1783Welcome to Python %s!  This is the online help utility.
1784
1785If this is your first time using Python, you should definitely check out
1786the tutorial on the Internet at http://docs.python.org/tutorial/.
1787
1788Enter the name of any module, keyword, or topic to get help on writing
1789Python programs and using Python modules.  To quit this help utility and
1790return to the interpreter, just type "quit".
1791
1792To get a list of available modules, keywords, or topics, type "modules",
1793"keywords", or "topics".  Each module also comes with a one-line summary
1794of what it does; to list the modules whose summaries contain a given word
1795such as "spam", type "modules spam".
1796''' % sys.version[:3])
1797
1798    def list(self, items, columns=4, width=80):
1799        items = items[:]
1800        items.sort()
1801        colw = width / columns
1802        rows = (len(items) + columns - 1) / columns
1803        for row in range(rows):
1804            for col in range(columns):
1805                i = col * rows + row
1806                if i < len(items):
1807                    self.output.write(items[i])
1808                    if col < columns - 1:
1809                        self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1810            self.output.write('\n')
1811
1812    def listkeywords(self):
1813        self.output.write('''
1814Here is a list of the Python keywords.  Enter any keyword to get more help.
1815
1816''')
1817        self.list(self.keywords.keys())
1818
1819    def listsymbols(self):
1820        self.output.write('''
1821Here is a list of the punctuation symbols which Python assigns special meaning
1822to. Enter any symbol to get more help.
1823
1824''')
1825        self.list(self.symbols.keys())
1826
1827    def listtopics(self):
1828        self.output.write('''
1829Here is a list of available topics.  Enter any topic name to get more help.
1830
1831''')
1832        self.list(self.topics.keys())
1833
1834    def showtopic(self, topic, more_xrefs=''):
1835        try:
1836            import pydoc_data.topics
1837        except ImportError:
1838            self.output.write('''
1839Sorry, topic and keyword documentation is not available because the
1840module "pydoc_data.topics" could not be found.
1841''')
1842            return
1843        target = self.topics.get(topic, self.keywords.get(topic))
1844        if not target:
1845            self.output.write('no documentation found for %s\n' % repr(topic))
1846            return
1847        if type(target) is type(''):
1848            return self.showtopic(target, more_xrefs)
1849
1850        label, xrefs = target
1851        try:
1852            doc = pydoc_data.topics.topics[label]
1853        except KeyError:
1854            self.output.write('no documentation found for %s\n' % repr(topic))
1855            return
1856        pager(strip(doc) + '\n')
1857        if more_xrefs:
1858            xrefs = (xrefs or '') + ' ' + more_xrefs
1859        if xrefs:
1860            import StringIO, formatter
1861            buffer = StringIO.StringIO()
1862            formatter.DumbWriter(buffer).send_flowing_data(
1863                'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1864            self.output.write('\n%s\n' % buffer.getvalue())
1865
1866    def showsymbol(self, symbol):
1867        target = self.symbols[symbol]
1868        topic, _, xrefs = target.partition(' ')
1869        self.showtopic(topic, xrefs)
1870
1871    def listmodules(self, key=''):
1872        if key:
1873            self.output.write('''
1874Here is a list of matching modules.  Enter any module name to get more help.
1875
1876''')
1877            apropos(key)
1878        else:
1879            self.output.write('''
1880Please wait a moment while I gather a list of all available modules...
1881
1882''')
1883            modules = {}
1884            def callback(path, modname, desc, modules=modules):
1885                if modname and modname[-9:] == '.__init__':
1886                    modname = modname[:-9] + ' (package)'
1887                if find(modname, '.') < 0:
1888                    modules[modname] = 1
1889            def onerror(modname):
1890                callback(None, modname, None)
1891            ModuleScanner().run(callback, onerror=onerror)
1892            self.list(modules.keys())
1893            self.output.write('''
1894Enter any module name to get more help.  Or, type "modules spam" to search
1895for modules whose descriptions contain the word "spam".
1896''')
1897
1898help = Helper()
1899
1900class Scanner:
1901    """A generic tree iterator."""
1902    def __init__(self, roots, children, descendp):
1903        self.roots = roots[:]
1904        self.state = []
1905        self.children = children
1906        self.descendp = descendp
1907
1908    def next(self):
1909        if not self.state:
1910            if not self.roots:
1911                return None
1912            root = self.roots.pop(0)
1913            self.state = [(root, self.children(root))]
1914        node, children = self.state[-1]
1915        if not children:
1916            self.state.pop()
1917            return self.next()
1918        child = children.pop(0)
1919        if self.descendp(child):
1920            self.state.append((child, self.children(child)))
1921        return child
1922
1923
1924class ModuleScanner:
1925    """An interruptible scanner that searches module synopses."""
1926
1927    def run(self, callback, key=None, completer=None, onerror=None):
1928        if key: key = lower(key)
1929        self.quit = False
1930        seen = {}
1931
1932        for modname in sys.builtin_module_names:
1933            if modname != '__main__':
1934                seen[modname] = 1
1935                if key is None:
1936                    callback(None, modname, '')
1937                else:
1938                    desc = split(__import__(modname).__doc__ or '', '\n')[0]
1939                    if find(lower(modname + ' - ' + desc), key) >= 0:
1940                        callback(None, modname, desc)
1941
1942        for importer, modname, ispkg in pkgutil.walk_packages(onerror=onerror):
1943            if self.quit:
1944                break
1945            if key is None:
1946                callback(None, modname, '')
1947            else:
1948                loader = importer.find_module(modname)
1949                if hasattr(loader,'get_source'):
1950                    import StringIO
1951                    desc = source_synopsis(
1952                        StringIO.StringIO(loader.get_source(modname))
1953                    ) or ''
1954                    if hasattr(loader,'get_filename'):
1955                        path = loader.get_filename(modname)
1956                    else:
1957                        path = None
1958                else:
1959                    module = loader.load_module(modname)
1960                    desc = (module.__doc__ or '').splitlines()[0]
1961                    path = getattr(module,'__file__',None)
1962                if find(lower(modname + ' - ' + desc), key) >= 0:
1963                    callback(path, modname, desc)
1964
1965        if completer:
1966            completer()
1967
1968def apropos(key):
1969    """Print all the one-line module summaries that contain a substring."""
1970    def callback(path, modname, desc):
1971        if modname[-9:] == '.__init__':
1972            modname = modname[:-9] + ' (package)'
1973        print modname, desc and '- ' + desc
1974    try: import warnings
1975    except ImportError: pass
1976    else: warnings.filterwarnings('ignore') # ignore problems during import
1977    ModuleScanner().run(callback, key)
1978
1979# --------------------------------------------------- web browser interface
1980
1981def serve(port, callback=None, completer=None):
1982    import BaseHTTPServer, mimetools, select
1983
1984    # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1985    class Message(mimetools.Message):
1986        def __init__(self, fp, seekable=1):
1987            Message = self.__class__
1988            Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1989            self.encodingheader = self.getheader('content-transfer-encoding')
1990            self.typeheader = self.getheader('content-type')
1991            self.parsetype()
1992            self.parseplist()
1993
1994    class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1995        def send_document(self, title, contents):
1996            try:
1997                self.send_response(200)
1998                self.send_header('Content-Type', 'text/html')
1999                self.end_headers()
2000                self.wfile.write(html.page(title, contents))
2001            except IOError: pass
2002
2003        def do_GET(self):
2004            path = self.path
2005            if path[-5:] == '.html': path = path[:-5]
2006            if path[:1] == '/': path = path[1:]
2007            if path and path != '.':
2008                try:
2009                    obj = locate(path, forceload=1)
2010                except ErrorDuringImport, value:
2011                    self.send_document(path, html.escape(str(value)))
2012                    return
2013                if obj:
2014                    self.send_document(describe(obj), html.document(obj, path))
2015                else:
2016                    self.send_document(path,
2017'no Python documentation found for %s' % repr(path))
2018            else:
2019                heading = html.heading(
2020'<big><big><strong>Python: Index of Modules</strong></big></big>',
2021'#ffffff', '#7799ee')
2022                def bltinlink(name):
2023                    return '<a href="%s.html">%s</a>' % (name, name)
2024                names = filter(lambda x: x != '__main__',
2025                               sys.builtin_module_names)
2026                contents = html.multicolumn(names, bltinlink)
2027                indices = ['<p>' + html.bigsection(
2028                    'Built-in Modules', '#ffffff', '#ee77aa', contents)]
2029
2030                seen = {}
2031                for dir in sys.path:
2032                    indices.append(html.index(dir, seen))
2033                contents = heading + join(indices) + '''<p align=right>
2034<font color="#909090" face="helvetica, arial"><strong>
2035pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font>'''
2036                self.send_document('Index of Modules', contents)
2037
2038        def log_message(self, *args): pass
2039
2040    class DocServer(BaseHTTPServer.HTTPServer):
2041        def __init__(self, port, callback):
2042            host = 'localhost'
2043            self.address = (host, port)
2044            self.url = 'http://%s:%d/' % (host, port)
2045            self.callback = callback
2046            self.base.__init__(self, self.address, self.handler)
2047
2048        def serve_until_quit(self):
2049            import select
2050            self.quit = False
2051            while not self.quit:
2052                rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
2053                if rd: self.handle_request()
2054
2055        def server_activate(self):
2056            self.base.server_activate(self)
2057            if self.callback: self.callback(self)
2058
2059    DocServer.base = BaseHTTPServer.HTTPServer
2060    DocServer.handler = DocHandler
2061    DocHandler.MessageClass = Message
2062    try:
2063        try:
2064            DocServer(port, callback).serve_until_quit()
2065        except (KeyboardInterrupt, select.error):
2066            pass
2067    finally:
2068        if completer: completer()
2069
2070# ----------------------------------------------------- graphical interface
2071
2072def gui():
2073    """Graphical interface (starts web server and pops up a control window)."""
2074    class GUI:
2075        def __init__(self, window, port=7464):
2076            self.window = window
2077            self.server = None
2078            self.scanner = None
2079
2080            import Tkinter
2081            self.server_frm = Tkinter.Frame(window)
2082            self.title_lbl = Tkinter.Label(self.server_frm,
2083                text='Starting server...\n ')
2084            self.open_btn = Tkinter.Button(self.server_frm,
2085                text='open browser', command=self.open, state='disabled')
2086            self.quit_btn = Tkinter.Button(self.server_frm,
2087                text='quit serving', command=self.quit, state='disabled')
2088
2089            self.search_frm = Tkinter.Frame(window)
2090            self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
2091            self.search_ent = Tkinter.Entry(self.search_frm)
2092            self.search_ent.bind('<Return>', self.search)
2093            self.stop_btn = Tkinter.Button(self.search_frm,
2094                text='stop', pady=0, command=self.stop, state='disabled')
2095            if sys.platform == 'win32':
2096                # Trying to hide and show this button crashes under Windows.
2097                self.stop_btn.pack(side='right')
2098
2099            self.window.title('pydoc')
2100            self.window.protocol('WM_DELETE_WINDOW', self.quit)
2101            self.title_lbl.pack(side='top', fill='x')
2102            self.open_btn.pack(side='left', fill='x', expand=1)
2103            self.quit_btn.pack(side='right', fill='x', expand=1)
2104            self.server_frm.pack(side='top', fill='x')
2105
2106            self.search_lbl.pack(side='left')
2107            self.search_ent.pack(side='right', fill='x', expand=1)
2108            self.search_frm.pack(side='top', fill='x')
2109            self.search_ent.focus_set()
2110
2111            font = ('helvetica', sys.platform == 'win32' and 8 or 10)
2112            self.result_lst = Tkinter.Listbox(window, font=font, height=6)
2113            self.result_lst.bind('<Button-1>', self.select)
2114            self.result_lst.bind('<Double-Button-1>', self.goto)
2115            self.result_scr = Tkinter.Scrollbar(window,
2116                orient='vertical', command=self.result_lst.yview)
2117            self.result_lst.config(yscrollcommand=self.result_scr.set)
2118
2119            self.result_frm = Tkinter.Frame(window)
2120            self.goto_btn = Tkinter.Button(self.result_frm,
2121                text='go to selected', command=self.goto)
2122            self.hide_btn = Tkinter.Button(self.result_frm,
2123                text='hide results', command=self.hide)
2124            self.goto_btn.pack(side='left', fill='x', expand=1)
2125            self.hide_btn.pack(side='right', fill='x', expand=1)
2126
2127            self.window.update()
2128            self.minwidth = self.window.winfo_width()
2129            self.minheight = self.window.winfo_height()
2130            self.bigminheight = (self.server_frm.winfo_reqheight() +
2131                                 self.search_frm.winfo_reqheight() +
2132                                 self.result_lst.winfo_reqheight() +
2133                                 self.result_frm.winfo_reqheight())
2134            self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
2135            self.expanded = 0
2136            self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
2137            self.window.wm_minsize(self.minwidth, self.minheight)
2138            self.window.tk.willdispatch()
2139
2140            import threading
2141            threading.Thread(
2142                target=serve, args=(port, self.ready, self.quit)).start()
2143
2144        def ready(self, server):
2145            self.server = server
2146            self.title_lbl.config(
2147                text='Python documentation server at\n' + server.url)
2148            self.open_btn.config(state='normal')
2149            self.quit_btn.config(state='normal')
2150
2151        def open(self, event=None, url=None):
2152            url = url or self.server.url
2153            try:
2154                import webbrowser
2155                webbrowser.open(url)
2156            except ImportError: # pre-webbrowser.py compatibility
2157                if sys.platform == 'win32':
2158                    os.system('start "%s"' % url)
2159                else:
2160                    rc = os.system('netscape -remote "openURL(%s)" &' % url)
2161                    if rc: os.system('netscape "%s" &' % url)
2162
2163        def quit(self, event=None):
2164            if self.server:
2165                self.server.quit = 1
2166            self.window.quit()
2167
2168        def search(self, event=None):
2169            key = self.search_ent.get()
2170            self.stop_btn.pack(side='right')
2171            self.stop_btn.config(state='normal')
2172            self.search_lbl.config(text='Searching for "%s"...' % key)
2173            self.search_ent.forget()
2174            self.search_lbl.pack(side='left')
2175            self.result_lst.delete(0, 'end')
2176            self.goto_btn.config(state='disabled')
2177            self.expand()
2178
2179            import threading
2180            if self.scanner:
2181                self.scanner.quit = 1
2182            self.scanner = ModuleScanner()
2183            threading.Thread(target=self.scanner.run,
2184                             args=(self.update, key, self.done)).start()
2185
2186        def update(self, path, modname, desc):
2187            if modname[-9:] == '.__init__':
2188                modname = modname[:-9] + ' (package)'
2189            self.result_lst.insert('end',
2190                modname + ' - ' + (desc or '(no description)'))
2191
2192        def stop(self, event=None):
2193            if self.scanner:
2194                self.scanner.quit = 1
2195                self.scanner = None
2196
2197        def done(self):
2198            self.scanner = None
2199            self.search_lbl.config(text='Search for')
2200            self.search_lbl.pack(side='left')
2201            self.search_ent.pack(side='right', fill='x', expand=1)
2202            if sys.platform != 'win32': self.stop_btn.forget()
2203            self.stop_btn.config(state='disabled')
2204
2205        def select(self, event=None):
2206            self.goto_btn.config(state='normal')
2207
2208        def goto(self, event=None):
2209            selection = self.result_lst.curselection()
2210            if selection:
2211                modname = split(self.result_lst.get(selection[0]))[0]
2212                self.open(url=self.server.url + modname + '.html')
2213
2214        def collapse(self):
2215            if not self.expanded: return
2216            self.result_frm.forget()
2217            self.result_scr.forget()
2218            self.result_lst.forget()
2219            self.bigwidth = self.window.winfo_width()
2220            self.bigheight = self.window.winfo_height()
2221            self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
2222            self.window.wm_minsize(self.minwidth, self.minheight)
2223            self.expanded = 0
2224
2225        def expand(self):
2226            if self.expanded: return
2227            self.result_frm.pack(side='bottom', fill='x')
2228            self.result_scr.pack(side='right', fill='y')
2229            self.result_lst.pack(side='top', fill='both', expand=1)
2230            self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
2231            self.window.wm_minsize(self.minwidth, self.bigminheight)
2232            self.expanded = 1
2233
2234        def hide(self, event=None):
2235            self.stop()
2236            self.collapse()
2237
2238    import Tkinter
2239    try:
2240        root = Tkinter.Tk()
2241        # Tk will crash if pythonw.exe has an XP .manifest
2242        # file and the root has is not destroyed explicitly.
2243        # If the problem is ever fixed in Tk, the explicit
2244        # destroy can go.
2245        try:
2246            gui = GUI(root)
2247            root.mainloop()
2248        finally:
2249            root.destroy()
2250    except KeyboardInterrupt:
2251        pass
2252
2253# -------------------------------------------------- command-line interface
2254
2255def ispath(x):
2256    return isinstance(x, str) and find(x, os.sep) >= 0
2257
2258def cli():
2259    """Command-line interface (looks at sys.argv to decide what to do)."""
2260    import getopt
2261    class BadUsage: pass
2262
2263    # Scripts don't get the current directory in their path by default
2264    # unless they are run with the '-m' switch
2265    if '' not in sys.path:
2266        scriptdir = os.path.dirname(sys.argv[0])
2267        if scriptdir in sys.path:
2268            sys.path.remove(scriptdir)
2269        sys.path.insert(0, '.')
2270
2271    try:
2272        opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
2273        writing = 0
2274
2275        for opt, val in opts:
2276            if opt == '-g':
2277                gui()
2278                return
2279            if opt == '-k':
2280                apropos(val)
2281                return
2282            if opt == '-p':
2283                try:
2284                    port = int(val)
2285                except ValueError:
2286                    raise BadUsage
2287                def ready(server):
2288                    print 'pydoc server ready at %s' % server.url
2289                def stopped():
2290                    print 'pydoc server stopped'
2291                serve(port, ready, stopped)
2292                return
2293            if opt == '-w':
2294                writing = 1
2295
2296        if not args: raise BadUsage
2297        for arg in args:
2298            if ispath(arg) and not os.path.exists(arg):
2299                print 'file %r does not exist' % arg
2300                break
2301            try:
2302                if ispath(arg) and os.path.isfile(arg):
2303                    arg = importfile(arg)
2304                if writing:
2305                    if ispath(arg) and os.path.isdir(arg):
2306                        writedocs(arg)
2307                    else:
2308                        writedoc(arg)
2309                else:
2310                    help.help(arg)
2311            except ErrorDuringImport, value:
2312                print value
2313
2314    except (getopt.error, BadUsage):
2315        cmd = os.path.basename(sys.argv[0])
2316        print """pydoc - the Python documentation tool
2317
2318%s <name> ...
2319    Show text documentation on something.  <name> may be the name of a
2320    Python keyword, topic, function, module, or package, or a dotted
2321    reference to a class or function within a module or module in a
2322    package.  If <name> contains a '%s', it is used as the path to a
2323    Python source file to document. If name is 'keywords', 'topics',
2324    or 'modules', a listing of these things is displayed.
2325
2326%s -k <keyword>
2327    Search for a keyword in the synopsis lines of all available modules.
2328
2329%s -p <port>
2330    Start an HTTP server on the given port on the local machine.
2331
2332%s -g
2333    Pop up a graphical interface for finding and serving documentation.
2334
2335%s -w <name> ...
2336    Write out the HTML documentation for a module to a file in the current
2337    directory.  If <name> contains a '%s', it is treated as a filename; if
2338    it names a directory, documentation is written for all the contents.
2339""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
2340
2341if __name__ == '__main__': cli()
2342