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: 88564 $"
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, warnings
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, (None, None))
216    if lastupdate is None or 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                    try:
744                        value = getattr(object, name)
745                    except Exception:
746                        # Some descriptors may meet a failure in their __get__.
747                        # (bug #1785)
748                        push(self._docdescriptor(name, value, mod))
749                    else:
750                        push(self.document(value, name, mod,
751                                        funcs, classes, mdict, object))
752                    push('\n')
753            return attrs
754
755        def spilldescriptors(msg, attrs, predicate):
756            ok, attrs = _split_list(attrs, predicate)
757            if ok:
758                hr.maybe()
759                push(msg)
760                for name, kind, homecls, value in ok:
761                    push(self._docdescriptor(name, value, mod))
762            return attrs
763
764        def spilldata(msg, attrs, predicate):
765            ok, attrs = _split_list(attrs, predicate)
766            if ok:
767                hr.maybe()
768                push(msg)
769                for name, kind, homecls, value in ok:
770                    base = self.docother(getattr(object, name), name, mod)
771                    if (hasattr(value, '__call__') or
772                            inspect.isdatadescriptor(value)):
773                        doc = getattr(value, "__doc__", None)
774                    else:
775                        doc = None
776                    if doc is None:
777                        push('<dl><dt>%s</dl>\n' % base)
778                    else:
779                        doc = self.markup(getdoc(value), self.preformat,
780                                          funcs, classes, mdict)
781                        doc = '<dd><tt>%s</tt>' % doc
782                        push('<dl><dt>%s%s</dl>\n' % (base, doc))
783                    push('\n')
784            return attrs
785
786        attrs = filter(lambda data: visiblename(data[0], obj=object),
787                       classify_class_attrs(object))
788        mdict = {}
789        for key, kind, homecls, value in attrs:
790            mdict[key] = anchor = '#' + name + '-' + key
791            try:
792                value = getattr(object, name)
793            except Exception:
794                # Some descriptors may meet a failure in their __get__.
795                # (bug #1785)
796                pass
797            try:
798                # The value may not be hashable (e.g., a data attr with
799                # a dict or list value).
800                mdict[value] = anchor
801            except TypeError:
802                pass
803
804        while attrs:
805            if mro:
806                thisclass = mro.popleft()
807            else:
808                thisclass = attrs[0][2]
809            attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
810
811            if thisclass is __builtin__.object:
812                attrs = inherited
813                continue
814            elif thisclass is object:
815                tag = 'defined here'
816            else:
817                tag = 'inherited from %s' % self.classlink(thisclass,
818                                                           object.__module__)
819            tag += ':<br>\n'
820
821            # Sort attrs by name.
822            try:
823                attrs.sort(key=lambda t: t[0])
824            except TypeError:
825                attrs.sort(lambda t1, t2: cmp(t1[0], t2[0]))    # 2.3 compat
826
827            # Pump out the attrs, segregated by kind.
828            attrs = spill('Methods %s' % tag, attrs,
829                          lambda t: t[1] == 'method')
830            attrs = spill('Class methods %s' % tag, attrs,
831                          lambda t: t[1] == 'class method')
832            attrs = spill('Static methods %s' % tag, attrs,
833                          lambda t: t[1] == 'static method')
834            attrs = spilldescriptors('Data descriptors %s' % tag, attrs,
835                                     lambda t: t[1] == 'data descriptor')
836            attrs = spilldata('Data and other attributes %s' % tag, attrs,
837                              lambda t: t[1] == 'data')
838            assert attrs == []
839            attrs = inherited
840
841        contents = ''.join(contents)
842
843        if name == realname:
844            title = '<a name="%s">class <strong>%s</strong></a>' % (
845                name, realname)
846        else:
847            title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
848                name, name, realname)
849        if bases:
850            parents = []
851            for base in bases:
852                parents.append(self.classlink(base, object.__module__))
853            title = title + '(%s)' % join(parents, ', ')
854        doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict)
855        doc = doc and '<tt>%s<br>&nbsp;</tt>' % doc
856
857        return self.section(title, '#000000', '#ffc8d8', contents, 3, doc)
858
859    def formatvalue(self, object):
860        """Format an argument default value as text."""
861        return self.grey('=' + self.repr(object))
862
863    def docroutine(self, object, name=None, mod=None,
864                   funcs={}, classes={}, methods={}, cl=None):
865        """Produce HTML documentation for a function or method object."""
866        realname = object.__name__
867        name = name or realname
868        anchor = (cl and cl.__name__ or '') + '-' + name
869        note = ''
870        skipdocs = 0
871        if inspect.ismethod(object):
872            imclass = object.im_class
873            if cl:
874                if imclass is not cl:
875                    note = ' from ' + self.classlink(imclass, mod)
876            else:
877                if object.im_self is not None:
878                    note = ' method of %s instance' % self.classlink(
879                        object.im_self.__class__, mod)
880                else:
881                    note = ' unbound %s method' % self.classlink(imclass,mod)
882            object = object.im_func
883
884        if name == realname:
885            title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
886        else:
887            if (cl and realname in cl.__dict__ and
888                cl.__dict__[realname] is object):
889                reallink = '<a href="#%s">%s</a>' % (
890                    cl.__name__ + '-' + realname, realname)
891                skipdocs = 1
892            else:
893                reallink = realname
894            title = '<a name="%s"><strong>%s</strong></a> = %s' % (
895                anchor, name, reallink)
896        if inspect.isfunction(object):
897            args, varargs, varkw, defaults = inspect.getargspec(object)
898            argspec = inspect.formatargspec(
899                args, varargs, varkw, defaults, formatvalue=self.formatvalue)
900            if realname == '<lambda>':
901                title = '<strong>%s</strong> <em>lambda</em> ' % name
902                argspec = argspec[1:-1] # remove parentheses
903        else:
904            argspec = '(...)'
905
906        decl = title + argspec + (note and self.grey(
907               '<font face="helvetica, arial">%s</font>' % note))
908
909        if skipdocs:
910            return '<dl><dt>%s</dt></dl>\n' % decl
911        else:
912            doc = self.markup(
913                getdoc(object), self.preformat, funcs, classes, methods)
914            doc = doc and '<dd><tt>%s</tt></dd>' % doc
915            return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
916
917    def _docdescriptor(self, name, value, mod):
918        results = []
919        push = results.append
920
921        if name:
922            push('<dl><dt><strong>%s</strong></dt>\n' % name)
923        if value.__doc__ is not None:
924            doc = self.markup(getdoc(value), self.preformat)
925            push('<dd><tt>%s</tt></dd>\n' % doc)
926        push('</dl>\n')
927
928        return ''.join(results)
929
930    def docproperty(self, object, name=None, mod=None, cl=None):
931        """Produce html documentation for a property."""
932        return self._docdescriptor(name, object, mod)
933
934    def docother(self, object, name=None, mod=None, *ignored):
935        """Produce HTML documentation for a data object."""
936        lhs = name and '<strong>%s</strong> = ' % name or ''
937        return lhs + self.repr(object)
938
939    def docdata(self, object, name=None, mod=None, cl=None):
940        """Produce html documentation for a data descriptor."""
941        return self._docdescriptor(name, object, mod)
942
943    def index(self, dir, shadowed=None):
944        """Generate an HTML index for a directory of modules."""
945        modpkgs = []
946        if shadowed is None: shadowed = {}
947        for importer, name, ispkg in pkgutil.iter_modules([dir]):
948            modpkgs.append((name, '', ispkg, name in shadowed))
949            shadowed[name] = 1
950
951        modpkgs.sort()
952        contents = self.multicolumn(modpkgs, self.modpkglink)
953        return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
954
955# -------------------------------------------- text documentation generator
956
957class TextRepr(Repr):
958    """Class for safely making a text representation of a Python object."""
959    def __init__(self):
960        Repr.__init__(self)
961        self.maxlist = self.maxtuple = 20
962        self.maxdict = 10
963        self.maxstring = self.maxother = 100
964
965    def repr1(self, x, level):
966        if hasattr(type(x), '__name__'):
967            methodname = 'repr_' + join(split(type(x).__name__), '_')
968            if hasattr(self, methodname):
969                return getattr(self, methodname)(x, level)
970        return cram(stripid(repr(x)), self.maxother)
971
972    def repr_string(self, x, level):
973        test = cram(x, self.maxstring)
974        testrepr = repr(test)
975        if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
976            # Backslashes are only literal in the string and are never
977            # needed to make any special characters, so show a raw string.
978            return 'r' + testrepr[0] + test + testrepr[0]
979        return testrepr
980
981    repr_str = repr_string
982
983    def repr_instance(self, x, level):
984        try:
985            return cram(stripid(repr(x)), self.maxstring)
986        except:
987            return '<%s instance>' % x.__class__.__name__
988
989class TextDoc(Doc):
990    """Formatter class for text documentation."""
991
992    # ------------------------------------------- text formatting utilities
993
994    _repr_instance = TextRepr()
995    repr = _repr_instance.repr
996
997    def bold(self, text):
998        """Format a string in bold by overstriking."""
999        return join(map(lambda ch: ch + '\b' + ch, text), '')
1000
1001    def indent(self, text, prefix='    '):
1002        """Indent text by prepending a given prefix to each line."""
1003        if not text: return ''
1004        lines = split(text, '\n')
1005        lines = map(lambda line, prefix=prefix: prefix + line, lines)
1006        if lines: lines[-1] = rstrip(lines[-1])
1007        return join(lines, '\n')
1008
1009    def section(self, title, contents):
1010        """Format a section with a given heading."""
1011        return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
1012
1013    # ---------------------------------------------- type-specific routines
1014
1015    def formattree(self, tree, modname, parent=None, prefix=''):
1016        """Render in text a class tree as returned by inspect.getclasstree()."""
1017        result = ''
1018        for entry in tree:
1019            if type(entry) is type(()):
1020                c, bases = entry
1021                result = result + prefix + classname(c, modname)
1022                if bases and bases != (parent,):
1023                    parents = map(lambda c, m=modname: classname(c, m), bases)
1024                    result = result + '(%s)' % join(parents, ', ')
1025                result = result + '\n'
1026            elif type(entry) is type([]):
1027                result = result + self.formattree(
1028                    entry, modname, c, prefix + '    ')
1029        return result
1030
1031    def docmodule(self, object, name=None, mod=None):
1032        """Produce text documentation for a given module object."""
1033        name = object.__name__ # ignore the passed-in name
1034        synop, desc = splitdoc(getdoc(object))
1035        result = self.section('NAME', name + (synop and ' - ' + synop))
1036
1037        try:
1038            all = object.__all__
1039        except AttributeError:
1040            all = None
1041
1042        try:
1043            file = inspect.getabsfile(object)
1044        except TypeError:
1045            file = '(built-in)'
1046        result = result + self.section('FILE', file)
1047
1048        docloc = self.getdocloc(object)
1049        if docloc is not None:
1050            result = result + self.section('MODULE DOCS', docloc)
1051
1052        if desc:
1053            result = result + self.section('DESCRIPTION', desc)
1054
1055        classes = []
1056        for key, value in inspect.getmembers(object, inspect.isclass):
1057            # if __all__ exists, believe it.  Otherwise use old heuristic.
1058            if (all is not None
1059                or (inspect.getmodule(value) or object) is object):
1060                if visiblename(key, all, object):
1061                    classes.append((key, value))
1062        funcs = []
1063        for key, value in inspect.getmembers(object, inspect.isroutine):
1064            # if __all__ exists, believe it.  Otherwise use old heuristic.
1065            if (all is not None or
1066                inspect.isbuiltin(value) or inspect.getmodule(value) is object):
1067                if visiblename(key, all, object):
1068                    funcs.append((key, value))
1069        data = []
1070        for key, value in inspect.getmembers(object, isdata):
1071            if visiblename(key, all, object):
1072                data.append((key, value))
1073
1074        modpkgs = []
1075        modpkgs_names = set()
1076        if hasattr(object, '__path__'):
1077            for importer, modname, ispkg in pkgutil.iter_modules(object.__path__):
1078                modpkgs_names.add(modname)
1079                if ispkg:
1080                    modpkgs.append(modname + ' (package)')
1081                else:
1082                    modpkgs.append(modname)
1083
1084            modpkgs.sort()
1085            result = result + self.section(
1086                'PACKAGE CONTENTS', join(modpkgs, '\n'))
1087
1088        # Detect submodules as sometimes created by C extensions
1089        submodules = []
1090        for key, value in inspect.getmembers(object, inspect.ismodule):
1091            if value.__name__.startswith(name + '.') and key not in modpkgs_names:
1092                submodules.append(key)
1093        if submodules:
1094            submodules.sort()
1095            result = result + self.section(
1096                'SUBMODULES', join(submodules, '\n'))
1097
1098        if classes:
1099            classlist = map(lambda key_value: key_value[1], classes)
1100            contents = [self.formattree(
1101                inspect.getclasstree(classlist, 1), name)]
1102            for key, value in classes:
1103                contents.append(self.document(value, key, name))
1104            result = result + self.section('CLASSES', join(contents, '\n'))
1105
1106        if funcs:
1107            contents = []
1108            for key, value in funcs:
1109                contents.append(self.document(value, key, name))
1110            result = result + self.section('FUNCTIONS', join(contents, '\n'))
1111
1112        if data:
1113            contents = []
1114            for key, value in data:
1115                contents.append(self.docother(value, key, name, maxlen=70))
1116            result = result + self.section('DATA', join(contents, '\n'))
1117
1118        if hasattr(object, '__version__'):
1119            version = str(object.__version__)
1120            if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
1121                version = strip(version[11:-1])
1122            result = result + self.section('VERSION', version)
1123        if hasattr(object, '__date__'):
1124            result = result + self.section('DATE', str(object.__date__))
1125        if hasattr(object, '__author__'):
1126            result = result + self.section('AUTHOR', str(object.__author__))
1127        if hasattr(object, '__credits__'):
1128            result = result + self.section('CREDITS', str(object.__credits__))
1129        return result
1130
1131    def docclass(self, object, name=None, mod=None, *ignored):
1132        """Produce text documentation for a given class object."""
1133        realname = object.__name__
1134        name = name or realname
1135        bases = object.__bases__
1136
1137        def makename(c, m=object.__module__):
1138            return classname(c, m)
1139
1140        if name == realname:
1141            title = 'class ' + self.bold(realname)
1142        else:
1143            title = self.bold(name) + ' = class ' + realname
1144        if bases:
1145            parents = map(makename, bases)
1146            title = title + '(%s)' % join(parents, ', ')
1147
1148        doc = getdoc(object)
1149        contents = doc and [doc + '\n'] or []
1150        push = contents.append
1151
1152        # List the mro, if non-trivial.
1153        mro = deque(inspect.getmro(object))
1154        if len(mro) > 2:
1155            push("Method resolution order:")
1156            for base in mro:
1157                push('    ' + makename(base))
1158            push('')
1159
1160        # Cute little class to pump out a horizontal rule between sections.
1161        class HorizontalRule:
1162            def __init__(self):
1163                self.needone = 0
1164            def maybe(self):
1165                if self.needone:
1166                    push('-' * 70)
1167                self.needone = 1
1168        hr = HorizontalRule()
1169
1170        def spill(msg, attrs, predicate):
1171            ok, attrs = _split_list(attrs, predicate)
1172            if ok:
1173                hr.maybe()
1174                push(msg)
1175                for name, kind, homecls, value in ok:
1176                    try:
1177                        value = getattr(object, name)
1178                    except Exception:
1179                        # Some descriptors may meet a failure in their __get__.
1180                        # (bug #1785)
1181                        push(self._docdescriptor(name, value, mod))
1182                    else:
1183                        push(self.document(value,
1184                                        name, mod, object))
1185            return attrs
1186
1187        def spilldescriptors(msg, attrs, predicate):
1188            ok, attrs = _split_list(attrs, predicate)
1189            if ok:
1190                hr.maybe()
1191                push(msg)
1192                for name, kind, homecls, value in ok:
1193                    push(self._docdescriptor(name, value, mod))
1194            return attrs
1195
1196        def spilldata(msg, attrs, predicate):
1197            ok, attrs = _split_list(attrs, predicate)
1198            if ok:
1199                hr.maybe()
1200                push(msg)
1201                for name, kind, homecls, value in ok:
1202                    if (hasattr(value, '__call__') or
1203                            inspect.isdatadescriptor(value)):
1204                        doc = getdoc(value)
1205                    else:
1206                        doc = None
1207                    push(self.docother(getattr(object, name),
1208                                       name, mod, maxlen=70, doc=doc) + '\n')
1209            return attrs
1210
1211        attrs = filter(lambda data: visiblename(data[0], obj=object),
1212                       classify_class_attrs(object))
1213        while attrs:
1214            if mro:
1215                thisclass = mro.popleft()
1216            else:
1217                thisclass = attrs[0][2]
1218            attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
1219
1220            if thisclass is __builtin__.object:
1221                attrs = inherited
1222                continue
1223            elif thisclass is object:
1224                tag = "defined here"
1225            else:
1226                tag = "inherited from %s" % classname(thisclass,
1227                                                      object.__module__)
1228
1229            # Sort attrs by name.
1230            attrs.sort()
1231
1232            # Pump out the attrs, segregated by kind.
1233            attrs = spill("Methods %s:\n" % tag, attrs,
1234                          lambda t: t[1] == 'method')
1235            attrs = spill("Class methods %s:\n" % tag, attrs,
1236                          lambda t: t[1] == 'class method')
1237            attrs = spill("Static methods %s:\n" % tag, attrs,
1238                          lambda t: t[1] == 'static method')
1239            attrs = spilldescriptors("Data descriptors %s:\n" % tag, attrs,
1240                                     lambda t: t[1] == 'data descriptor')
1241            attrs = spilldata("Data and other attributes %s:\n" % tag, attrs,
1242                              lambda t: t[1] == 'data')
1243            assert attrs == []
1244            attrs = inherited
1245
1246        contents = '\n'.join(contents)
1247        if not contents:
1248            return title + '\n'
1249        return title + '\n' + self.indent(rstrip(contents), ' |  ') + '\n'
1250
1251    def formatvalue(self, object):
1252        """Format an argument default value as text."""
1253        return '=' + self.repr(object)
1254
1255    def docroutine(self, object, name=None, mod=None, cl=None):
1256        """Produce text documentation for a function or method object."""
1257        realname = object.__name__
1258        name = name or realname
1259        note = ''
1260        skipdocs = 0
1261        if inspect.ismethod(object):
1262            imclass = object.im_class
1263            if cl:
1264                if imclass is not cl:
1265                    note = ' from ' + classname(imclass, mod)
1266            else:
1267                if object.im_self is not None:
1268                    note = ' method of %s instance' % classname(
1269                        object.im_self.__class__, mod)
1270                else:
1271                    note = ' unbound %s method' % classname(imclass,mod)
1272            object = object.im_func
1273
1274        if name == realname:
1275            title = self.bold(realname)
1276        else:
1277            if (cl and realname in cl.__dict__ and
1278                cl.__dict__[realname] is object):
1279                skipdocs = 1
1280            title = self.bold(name) + ' = ' + realname
1281        if inspect.isfunction(object):
1282            args, varargs, varkw, defaults = inspect.getargspec(object)
1283            argspec = inspect.formatargspec(
1284                args, varargs, varkw, defaults, formatvalue=self.formatvalue)
1285            if realname == '<lambda>':
1286                title = self.bold(name) + ' lambda '
1287                argspec = argspec[1:-1] # remove parentheses
1288        else:
1289            argspec = '(...)'
1290        decl = title + argspec + note
1291
1292        if skipdocs:
1293            return decl + '\n'
1294        else:
1295            doc = getdoc(object) or ''
1296            return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
1297
1298    def _docdescriptor(self, name, value, mod):
1299        results = []
1300        push = results.append
1301
1302        if name:
1303            push(self.bold(name))
1304            push('\n')
1305        doc = getdoc(value) or ''
1306        if doc:
1307            push(self.indent(doc))
1308            push('\n')
1309        return ''.join(results)
1310
1311    def docproperty(self, object, name=None, mod=None, cl=None):
1312        """Produce text documentation for a property."""
1313        return self._docdescriptor(name, object, mod)
1314
1315    def docdata(self, object, name=None, mod=None, cl=None):
1316        """Produce text documentation for a data descriptor."""
1317        return self._docdescriptor(name, object, mod)
1318
1319    def docother(self, object, name=None, mod=None, parent=None, maxlen=None, doc=None):
1320        """Produce text documentation for a data object."""
1321        repr = self.repr(object)
1322        if maxlen:
1323            line = (name and name + ' = ' or '') + repr
1324            chop = maxlen - len(line)
1325            if chop < 0: repr = repr[:chop] + '...'
1326        line = (name and self.bold(name) + ' = ' or '') + repr
1327        if doc is not None:
1328            line += '\n' + self.indent(str(doc))
1329        return line
1330
1331# --------------------------------------------------------- user interfaces
1332
1333def pager(text):
1334    """The first time this is called, determine what kind of pager to use."""
1335    global pager
1336    pager = getpager()
1337    pager(text)
1338
1339def getpager():
1340    """Decide what method to use for paging through text."""
1341    if type(sys.stdout) is not types.FileType:
1342        return plainpager
1343    if not sys.stdin.isatty() or not sys.stdout.isatty():
1344        return plainpager
1345    if 'PAGER' in os.environ:
1346        if sys.platform == 'win32': # pipes completely broken in Windows
1347            return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
1348        elif os.environ.get('TERM') in ('dumb', 'emacs'):
1349            return lambda text: pipepager(plain(text), os.environ['PAGER'])
1350        else:
1351            return lambda text: pipepager(text, os.environ['PAGER'])
1352    if os.environ.get('TERM') in ('dumb', 'emacs'):
1353        return plainpager
1354    if sys.platform == 'win32' or sys.platform.startswith('os2'):
1355        return lambda text: tempfilepager(plain(text), 'more <')
1356    if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0:
1357        return lambda text: pipepager(text, 'less')
1358
1359    import tempfile
1360    (fd, filename) = tempfile.mkstemp()
1361    os.close(fd)
1362    try:
1363        if hasattr(os, 'system') and os.system('more "%s"' % filename) == 0:
1364            return lambda text: pipepager(text, 'more')
1365        else:
1366            return ttypager
1367    finally:
1368        os.unlink(filename)
1369
1370def plain(text):
1371    """Remove boldface formatting from text."""
1372    return re.sub('.\b', '', text)
1373
1374def pipepager(text, cmd):
1375    """Page through text by feeding it to another program."""
1376    pipe = os.popen(cmd, 'w')
1377    try:
1378        pipe.write(text)
1379        pipe.close()
1380    except IOError:
1381        pass # Ignore broken pipes caused by quitting the pager program.
1382
1383def tempfilepager(text, cmd):
1384    """Page through text by invoking a program on a temporary file."""
1385    import tempfile
1386    filename = tempfile.mktemp()
1387    file = open(filename, 'w')
1388    file.write(text)
1389    file.close()
1390    try:
1391        os.system(cmd + ' "' + filename + '"')
1392    finally:
1393        os.unlink(filename)
1394
1395def ttypager(text):
1396    """Page through text on a text terminal."""
1397    lines = split(plain(text), '\n')
1398    try:
1399        import tty
1400        fd = sys.stdin.fileno()
1401        old = tty.tcgetattr(fd)
1402        tty.setcbreak(fd)
1403        getchar = lambda: sys.stdin.read(1)
1404    except (ImportError, AttributeError):
1405        tty = None
1406        getchar = lambda: sys.stdin.readline()[:-1][:1]
1407
1408    try:
1409        r = inc = os.environ.get('LINES', 25) - 1
1410        sys.stdout.write(join(lines[:inc], '\n') + '\n')
1411        while lines[r:]:
1412            sys.stdout.write('-- more --')
1413            sys.stdout.flush()
1414            c = getchar()
1415
1416            if c in ('q', 'Q'):
1417                sys.stdout.write('\r          \r')
1418                break
1419            elif c in ('\r', '\n'):
1420                sys.stdout.write('\r          \r' + lines[r] + '\n')
1421                r = r + 1
1422                continue
1423            if c in ('b', 'B', '\x1b'):
1424                r = r - inc - inc
1425                if r < 0: r = 0
1426            sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1427            r = r + inc
1428
1429    finally:
1430        if tty:
1431            tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1432
1433def plainpager(text):
1434    """Simply print unformatted text.  This is the ultimate fallback."""
1435    sys.stdout.write(plain(text))
1436
1437def describe(thing):
1438    """Produce a short description of the given thing."""
1439    if inspect.ismodule(thing):
1440        if thing.__name__ in sys.builtin_module_names:
1441            return 'built-in module ' + thing.__name__
1442        if hasattr(thing, '__path__'):
1443            return 'package ' + thing.__name__
1444        else:
1445            return 'module ' + thing.__name__
1446    if inspect.isbuiltin(thing):
1447        return 'built-in function ' + thing.__name__
1448    if inspect.isgetsetdescriptor(thing):
1449        return 'getset descriptor %s.%s.%s' % (
1450            thing.__objclass__.__module__, thing.__objclass__.__name__,
1451            thing.__name__)
1452    if inspect.ismemberdescriptor(thing):
1453        return 'member descriptor %s.%s.%s' % (
1454            thing.__objclass__.__module__, thing.__objclass__.__name__,
1455            thing.__name__)
1456    if inspect.isclass(thing):
1457        return 'class ' + thing.__name__
1458    if inspect.isfunction(thing):
1459        return 'function ' + thing.__name__
1460    if inspect.ismethod(thing):
1461        return 'method ' + thing.__name__
1462    if type(thing) is types.InstanceType:
1463        return 'instance of ' + thing.__class__.__name__
1464    return type(thing).__name__
1465
1466def locate(path, forceload=0):
1467    """Locate an object by name or dotted path, importing as necessary."""
1468    parts = [part for part in split(path, '.') if part]
1469    module, n = None, 0
1470    while n < len(parts):
1471        nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
1472        if nextmodule: module, n = nextmodule, n + 1
1473        else: break
1474    if module:
1475        object = module
1476    else:
1477        object = __builtin__
1478    for part in parts[n:]:
1479        try:
1480            object = getattr(object, part)
1481        except AttributeError:
1482            return None
1483    return object
1484
1485# --------------------------------------- interactive interpreter interface
1486
1487text = TextDoc()
1488html = HTMLDoc()
1489
1490class _OldStyleClass: pass
1491_OLD_INSTANCE_TYPE = type(_OldStyleClass())
1492
1493def resolve(thing, forceload=0):
1494    """Given an object or a path to an object, get the object and its name."""
1495    if isinstance(thing, str):
1496        object = locate(thing, forceload)
1497        if not object:
1498            raise ImportError, 'no Python documentation found for %r' % thing
1499        return object, thing
1500    else:
1501        name = getattr(thing, '__name__', None)
1502        return thing, name if isinstance(name, str) else None
1503
1504def render_doc(thing, title='Python Library Documentation: %s', forceload=0):
1505    """Render text documentation, given an object or a path to an object."""
1506    object, name = resolve(thing, forceload)
1507    desc = describe(object)
1508    module = inspect.getmodule(object)
1509    if name and '.' in name:
1510        desc += ' in ' + name[:name.rfind('.')]
1511    elif module and module is not object:
1512        desc += ' in module ' + module.__name__
1513    if type(object) is _OLD_INSTANCE_TYPE:
1514        # If the passed object is an instance of an old-style class,
1515        # document its available methods instead of its value.
1516        object = object.__class__
1517    elif not (inspect.ismodule(object) or
1518              inspect.isclass(object) or
1519              inspect.isroutine(object) or
1520              inspect.isgetsetdescriptor(object) or
1521              inspect.ismemberdescriptor(object) or
1522              isinstance(object, property)):
1523        # If the passed object is a piece of data or an instance,
1524        # document its available methods instead of its value.
1525        object = type(object)
1526        desc += ' object'
1527    return title % desc + '\n\n' + text.document(object, name)
1528
1529def doc(thing, title='Python Library Documentation: %s', forceload=0):
1530    """Display text documentation, given an object or a path to an object."""
1531    try:
1532        pager(render_doc(thing, title, forceload))
1533    except (ImportError, ErrorDuringImport), value:
1534        print value
1535
1536def writedoc(thing, forceload=0):
1537    """Write HTML documentation to a file in the current directory."""
1538    try:
1539        object, name = resolve(thing, forceload)
1540        page = html.page(describe(object), html.document(object, name))
1541        file = open(name + '.html', 'w')
1542        file.write(page)
1543        file.close()
1544        print 'wrote', name + '.html'
1545    except (ImportError, ErrorDuringImport), value:
1546        print value
1547
1548def writedocs(dir, pkgpath='', done=None):
1549    """Write out HTML documentation for all modules in a directory tree."""
1550    if done is None: done = {}
1551    for importer, modname, ispkg in pkgutil.walk_packages([dir], pkgpath):
1552        writedoc(modname)
1553    return
1554
1555class Helper:
1556
1557    # These dictionaries map a topic name to either an alias, or a tuple
1558    # (label, seealso-items).  The "label" is the label of the corresponding
1559    # section in the .rst file under Doc/ and an index into the dictionary
1560    # in pydoc_data/topics.py.
1561    #
1562    # CAUTION: if you change one of these dictionaries, be sure to adapt the
1563    #          list of needed labels in Doc/tools/sphinxext/pyspecific.py and
1564    #          regenerate the pydoc_data/topics.py file by running
1565    #              make pydoc-topics
1566    #          in Doc/ and copying the output file into the Lib/ directory.
1567
1568    keywords = {
1569        'and': 'BOOLEAN',
1570        'as': 'with',
1571        'assert': ('assert', ''),
1572        'break': ('break', 'while for'),
1573        'class': ('class', 'CLASSES SPECIALMETHODS'),
1574        'continue': ('continue', 'while for'),
1575        'def': ('function', ''),
1576        'del': ('del', 'BASICMETHODS'),
1577        'elif': 'if',
1578        'else': ('else', 'while for'),
1579        'except': 'try',
1580        'exec': ('exec', ''),
1581        'finally': 'try',
1582        'for': ('for', 'break continue while'),
1583        'from': 'import',
1584        'global': ('global', 'NAMESPACES'),
1585        'if': ('if', 'TRUTHVALUE'),
1586        'import': ('import', 'MODULES'),
1587        'in': ('in', 'SEQUENCEMETHODS2'),
1588        'is': 'COMPARISON',
1589        'lambda': ('lambda', 'FUNCTIONS'),
1590        'not': 'BOOLEAN',
1591        'or': 'BOOLEAN',
1592        'pass': ('pass', ''),
1593        'print': ('print', ''),
1594        'raise': ('raise', 'EXCEPTIONS'),
1595        'return': ('return', 'FUNCTIONS'),
1596        'try': ('try', 'EXCEPTIONS'),
1597        'while': ('while', 'break continue if TRUTHVALUE'),
1598        'with': ('with', 'CONTEXTMANAGERS EXCEPTIONS yield'),
1599        'yield': ('yield', ''),
1600    }
1601    # Either add symbols to this dictionary or to the symbols dictionary
1602    # directly: Whichever is easier. They are merged later.
1603    _symbols_inverse = {
1604        'STRINGS' : ("'", "'''", "r'", "u'", '"""', '"', 'r"', 'u"'),
1605        'OPERATORS' : ('+', '-', '*', '**', '/', '//', '%', '<<', '>>', '&',
1606                       '|', '^', '~', '<', '>', '<=', '>=', '==', '!=', '<>'),
1607        'COMPARISON' : ('<', '>', '<=', '>=', '==', '!=', '<>'),
1608        'UNARY' : ('-', '~'),
1609        'AUGMENTEDASSIGNMENT' : ('+=', '-=', '*=', '/=', '%=', '&=', '|=',
1610                                '^=', '<<=', '>>=', '**=', '//='),
1611        'BITWISE' : ('<<', '>>', '&', '|', '^', '~'),
1612        'COMPLEX' : ('j', 'J')
1613    }
1614    symbols = {
1615        '%': 'OPERATORS FORMATTING',
1616        '**': 'POWER',
1617        ',': 'TUPLES LISTS FUNCTIONS',
1618        '.': 'ATTRIBUTES FLOAT MODULES OBJECTS',
1619        '...': 'ELLIPSIS',
1620        ':': 'SLICINGS DICTIONARYLITERALS',
1621        '@': 'def class',
1622        '\\': 'STRINGS',
1623        '_': 'PRIVATENAMES',
1624        '__': 'PRIVATENAMES SPECIALMETHODS',
1625        '`': 'BACKQUOTES',
1626        '(': 'TUPLES FUNCTIONS CALLS',
1627        ')': 'TUPLES FUNCTIONS CALLS',
1628        '[': 'LISTS SUBSCRIPTS SLICINGS',
1629        ']': 'LISTS SUBSCRIPTS SLICINGS'
1630    }
1631    for topic, symbols_ in _symbols_inverse.iteritems():
1632        for symbol in symbols_:
1633            topics = symbols.get(symbol, topic)
1634            if topic not in topics:
1635                topics = topics + ' ' + topic
1636            symbols[symbol] = topics
1637
1638    topics = {
1639        'TYPES': ('types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS '
1640                  'FUNCTIONS CLASSES MODULES FILES inspect'),
1641        'STRINGS': ('strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING '
1642                    'TYPES'),
1643        'STRINGMETHODS': ('string-methods', 'STRINGS FORMATTING'),
1644        'FORMATTING': ('formatstrings', 'OPERATORS'),
1645        'UNICODE': ('strings', 'encodings unicode SEQUENCES STRINGMETHODS '
1646                    'FORMATTING TYPES'),
1647        'NUMBERS': ('numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1648        'INTEGER': ('integers', 'int range'),
1649        'FLOAT': ('floating', 'float math'),
1650        'COMPLEX': ('imaginary', 'complex cmath'),
1651        'SEQUENCES': ('typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
1652        'MAPPINGS': 'DICTIONARIES',
1653        'FUNCTIONS': ('typesfunctions', 'def TYPES'),
1654        'METHODS': ('typesmethods', 'class def CLASSES TYPES'),
1655        'CODEOBJECTS': ('bltin-code-objects', 'compile FUNCTIONS TYPES'),
1656        'TYPEOBJECTS': ('bltin-type-objects', 'types TYPES'),
1657        'FRAMEOBJECTS': 'TYPES',
1658        'TRACEBACKS': 'TYPES',
1659        'NONE': ('bltin-null-object', ''),
1660        'ELLIPSIS': ('bltin-ellipsis-object', 'SLICINGS'),
1661        'FILES': ('bltin-file-objects', ''),
1662        'SPECIALATTRIBUTES': ('specialattrs', ''),
1663        'CLASSES': ('types', 'class SPECIALMETHODS PRIVATENAMES'),
1664        'MODULES': ('typesmodules', 'import'),
1665        'PACKAGES': 'import',
1666        'EXPRESSIONS': ('operator-summary', 'lambda or and not in is BOOLEAN '
1667                        'COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER '
1668                        'UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES '
1669                        'LISTS DICTIONARIES BACKQUOTES'),
1670        'OPERATORS': 'EXPRESSIONS',
1671        'PRECEDENCE': 'EXPRESSIONS',
1672        'OBJECTS': ('objects', 'TYPES'),
1673        'SPECIALMETHODS': ('specialnames', 'BASICMETHODS ATTRIBUTEMETHODS '
1674                           'CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS '
1675                           'SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
1676        'BASICMETHODS': ('customization', 'cmp hash repr str SPECIALMETHODS'),
1677        'ATTRIBUTEMETHODS': ('attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1678        'CALLABLEMETHODS': ('callable-types', 'CALLS SPECIALMETHODS'),
1679        'SEQUENCEMETHODS1': ('sequence-types', 'SEQUENCES SEQUENCEMETHODS2 '
1680                             'SPECIALMETHODS'),
1681        'SEQUENCEMETHODS2': ('sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 '
1682                             'SPECIALMETHODS'),
1683        'MAPPINGMETHODS': ('sequence-types', 'MAPPINGS SPECIALMETHODS'),
1684        'NUMBERMETHODS': ('numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT '
1685                          'SPECIALMETHODS'),
1686        'EXECUTION': ('execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'),
1687        'NAMESPACES': ('naming', 'global ASSIGNMENT DELETION DYNAMICFEATURES'),
1688        'DYNAMICFEATURES': ('dynamic-features', ''),
1689        'SCOPING': 'NAMESPACES',
1690        'FRAMES': 'NAMESPACES',
1691        'EXCEPTIONS': ('exceptions', 'try except finally raise'),
1692        'COERCIONS': ('coercion-rules','CONVERSIONS'),
1693        'CONVERSIONS': ('conversions', 'COERCIONS'),
1694        'IDENTIFIERS': ('identifiers', 'keywords SPECIALIDENTIFIERS'),
1695        'SPECIALIDENTIFIERS': ('id-classes', ''),
1696        'PRIVATENAMES': ('atom-identifiers', ''),
1697        'LITERALS': ('atom-literals', 'STRINGS BACKQUOTES NUMBERS '
1698                     'TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1699        'TUPLES': 'SEQUENCES',
1700        'TUPLELITERALS': ('exprlists', 'TUPLES LITERALS'),
1701        'LISTS': ('typesseq-mutable', 'LISTLITERALS'),
1702        'LISTLITERALS': ('lists', 'LISTS LITERALS'),
1703        'DICTIONARIES': ('typesmapping', 'DICTIONARYLITERALS'),
1704        'DICTIONARYLITERALS': ('dict', 'DICTIONARIES LITERALS'),
1705        'BACKQUOTES': ('string-conversions', 'repr str STRINGS LITERALS'),
1706        'ATTRIBUTES': ('attribute-references', 'getattr hasattr setattr '
1707                       'ATTRIBUTEMETHODS'),
1708        'SUBSCRIPTS': ('subscriptions', 'SEQUENCEMETHODS1'),
1709        'SLICINGS': ('slicings', 'SEQUENCEMETHODS2'),
1710        'CALLS': ('calls', 'EXPRESSIONS'),
1711        'POWER': ('power', 'EXPRESSIONS'),
1712        'UNARY': ('unary', 'EXPRESSIONS'),
1713        'BINARY': ('binary', 'EXPRESSIONS'),
1714        'SHIFTING': ('shifting', 'EXPRESSIONS'),
1715        'BITWISE': ('bitwise', 'EXPRESSIONS'),
1716        'COMPARISON': ('comparisons', 'EXPRESSIONS BASICMETHODS'),
1717        'BOOLEAN': ('booleans', 'EXPRESSIONS TRUTHVALUE'),
1718        'ASSERTION': 'assert',
1719        'ASSIGNMENT': ('assignment', 'AUGMENTEDASSIGNMENT'),
1720        'AUGMENTEDASSIGNMENT': ('augassign', 'NUMBERMETHODS'),
1721        'DELETION': 'del',
1722        'PRINTING': 'print',
1723        'RETURNING': 'return',
1724        'IMPORTING': 'import',
1725        'CONDITIONAL': 'if',
1726        'LOOPING': ('compound', 'for while break continue'),
1727        'TRUTHVALUE': ('truth', 'if while and or not BASICMETHODS'),
1728        'DEBUGGING': ('debugger', 'pdb'),
1729        'CONTEXTMANAGERS': ('context-managers', 'with'),
1730    }
1731
1732    def __init__(self, input=None, output=None):
1733        self._input = input
1734        self._output = output
1735
1736    input  = property(lambda self: self._input or sys.stdin)
1737    output = property(lambda self: self._output or sys.stdout)
1738
1739    def __repr__(self):
1740        if inspect.stack()[1][3] == '?':
1741            self()
1742            return ''
1743        return '<pydoc.Helper instance>'
1744
1745    _GoInteractive = object()
1746    def __call__(self, request=_GoInteractive):
1747        if request is not self._GoInteractive:
1748            self.help(request)
1749        else:
1750            self.intro()
1751            self.interact()
1752            self.output.write('''
1753You are now leaving help and returning to the Python interpreter.
1754If you want to ask for help on a particular object directly from the
1755interpreter, you can type "help(object)".  Executing "help('string')"
1756has the same effect as typing a particular string at the help> prompt.
1757''')
1758
1759    def interact(self):
1760        self.output.write('\n')
1761        while True:
1762            try:
1763                request = self.getline('help> ')
1764                if not request: break
1765            except (KeyboardInterrupt, EOFError):
1766                break
1767            request = strip(replace(request, '"', '', "'", ''))
1768            if lower(request) in ('q', 'quit'): break
1769            self.help(request)
1770
1771    def getline(self, prompt):
1772        """Read one line, using raw_input when available."""
1773        if self.input is sys.stdin:
1774            return raw_input(prompt)
1775        else:
1776            self.output.write(prompt)
1777            self.output.flush()
1778            return self.input.readline()
1779
1780    def help(self, request):
1781        if type(request) is type(''):
1782            request = request.strip()
1783            if request == 'help': self.intro()
1784            elif request == 'keywords': self.listkeywords()
1785            elif request == 'symbols': self.listsymbols()
1786            elif request == 'topics': self.listtopics()
1787            elif request == 'modules': self.listmodules()
1788            elif request[:8] == 'modules ':
1789                self.listmodules(split(request)[1])
1790            elif request in self.symbols: self.showsymbol(request)
1791            elif request in self.keywords: self.showtopic(request)
1792            elif request in self.topics: self.showtopic(request)
1793            elif request: doc(request, 'Help on %s:')
1794        elif isinstance(request, Helper): self()
1795        else: doc(request, 'Help on %s:')
1796        self.output.write('\n')
1797
1798    def intro(self):
1799        self.output.write('''
1800Welcome to Python %s!  This is the online help utility.
1801
1802If this is your first time using Python, you should definitely check out
1803the tutorial on the Internet at http://docs.python.org/%s/tutorial/.
1804
1805Enter the name of any module, keyword, or topic to get help on writing
1806Python programs and using Python modules.  To quit this help utility and
1807return to the interpreter, just type "quit".
1808
1809To get a list of available modules, keywords, or topics, type "modules",
1810"keywords", or "topics".  Each module also comes with a one-line summary
1811of what it does; to list the modules whose summaries contain a given word
1812such as "spam", type "modules spam".
1813''' % tuple([sys.version[:3]]*2))
1814
1815    def list(self, items, columns=4, width=80):
1816        items = items[:]
1817        items.sort()
1818        colw = width / columns
1819        rows = (len(items) + columns - 1) / columns
1820        for row in range(rows):
1821            for col in range(columns):
1822                i = col * rows + row
1823                if i < len(items):
1824                    self.output.write(items[i])
1825                    if col < columns - 1:
1826                        self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1827            self.output.write('\n')
1828
1829    def listkeywords(self):
1830        self.output.write('''
1831Here is a list of the Python keywords.  Enter any keyword to get more help.
1832
1833''')
1834        self.list(self.keywords.keys())
1835
1836    def listsymbols(self):
1837        self.output.write('''
1838Here is a list of the punctuation symbols which Python assigns special meaning
1839to. Enter any symbol to get more help.
1840
1841''')
1842        self.list(self.symbols.keys())
1843
1844    def listtopics(self):
1845        self.output.write('''
1846Here is a list of available topics.  Enter any topic name to get more help.
1847
1848''')
1849        self.list(self.topics.keys())
1850
1851    def showtopic(self, topic, more_xrefs=''):
1852        try:
1853            import pydoc_data.topics
1854        except ImportError:
1855            self.output.write('''
1856Sorry, topic and keyword documentation is not available because the
1857module "pydoc_data.topics" could not be found.
1858''')
1859            return
1860        target = self.topics.get(topic, self.keywords.get(topic))
1861        if not target:
1862            self.output.write('no documentation found for %s\n' % repr(topic))
1863            return
1864        if type(target) is type(''):
1865            return self.showtopic(target, more_xrefs)
1866
1867        label, xrefs = target
1868        try:
1869            doc = pydoc_data.topics.topics[label]
1870        except KeyError:
1871            self.output.write('no documentation found for %s\n' % repr(topic))
1872            return
1873        pager(strip(doc) + '\n')
1874        if more_xrefs:
1875            xrefs = (xrefs or '') + ' ' + more_xrefs
1876        if xrefs:
1877            import StringIO, formatter
1878            buffer = StringIO.StringIO()
1879            formatter.DumbWriter(buffer).send_flowing_data(
1880                'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1881            self.output.write('\n%s\n' % buffer.getvalue())
1882
1883    def showsymbol(self, symbol):
1884        target = self.symbols[symbol]
1885        topic, _, xrefs = target.partition(' ')
1886        self.showtopic(topic, xrefs)
1887
1888    def listmodules(self, key=''):
1889        if key:
1890            self.output.write('''
1891Here is a list of matching modules.  Enter any module name to get more help.
1892
1893''')
1894            apropos(key)
1895        else:
1896            self.output.write('''
1897Please wait a moment while I gather a list of all available modules...
1898
1899''')
1900            modules = {}
1901            def callback(path, modname, desc, modules=modules):
1902                if modname and modname[-9:] == '.__init__':
1903                    modname = modname[:-9] + ' (package)'
1904                if find(modname, '.') < 0:
1905                    modules[modname] = 1
1906            def onerror(modname):
1907                callback(None, modname, None)
1908            ModuleScanner().run(callback, onerror=onerror)
1909            self.list(modules.keys())
1910            self.output.write('''
1911Enter any module name to get more help.  Or, type "modules spam" to search
1912for modules whose descriptions contain the word "spam".
1913''')
1914
1915help = Helper()
1916
1917class Scanner:
1918    """A generic tree iterator."""
1919    def __init__(self, roots, children, descendp):
1920        self.roots = roots[:]
1921        self.state = []
1922        self.children = children
1923        self.descendp = descendp
1924
1925    def next(self):
1926        if not self.state:
1927            if not self.roots:
1928                return None
1929            root = self.roots.pop(0)
1930            self.state = [(root, self.children(root))]
1931        node, children = self.state[-1]
1932        if not children:
1933            self.state.pop()
1934            return self.next()
1935        child = children.pop(0)
1936        if self.descendp(child):
1937            self.state.append((child, self.children(child)))
1938        return child
1939
1940
1941class ModuleScanner:
1942    """An interruptible scanner that searches module synopses."""
1943
1944    def run(self, callback, key=None, completer=None, onerror=None):
1945        if key: key = lower(key)
1946        self.quit = False
1947        seen = {}
1948
1949        for modname in sys.builtin_module_names:
1950            if modname != '__main__':
1951                seen[modname] = 1
1952                if key is None:
1953                    callback(None, modname, '')
1954                else:
1955                    desc = split(__import__(modname).__doc__ or '', '\n')[0]
1956                    if find(lower(modname + ' - ' + desc), key) >= 0:
1957                        callback(None, modname, desc)
1958
1959        for importer, modname, ispkg in pkgutil.walk_packages(onerror=onerror):
1960            if self.quit:
1961                break
1962            if key is None:
1963                callback(None, modname, '')
1964            else:
1965                loader = importer.find_module(modname)
1966                if hasattr(loader,'get_source'):
1967                    import StringIO
1968                    desc = source_synopsis(
1969                        StringIO.StringIO(loader.get_source(modname))
1970                    ) or ''
1971                    if hasattr(loader,'get_filename'):
1972                        path = loader.get_filename(modname)
1973                    else:
1974                        path = None
1975                else:
1976                    module = loader.load_module(modname)
1977                    desc = (module.__doc__ or '').splitlines()[0]
1978                    path = getattr(module,'__file__',None)
1979                if find(lower(modname + ' - ' + desc), key) >= 0:
1980                    callback(path, modname, desc)
1981
1982        if completer:
1983            completer()
1984
1985def apropos(key):
1986    """Print all the one-line module summaries that contain a substring."""
1987    def callback(path, modname, desc):
1988        if modname[-9:] == '.__init__':
1989            modname = modname[:-9] + ' (package)'
1990        print modname, desc and '- ' + desc
1991    def onerror(modname):
1992        pass
1993    with warnings.catch_warnings():
1994        warnings.filterwarnings('ignore') # ignore problems during import
1995        ModuleScanner().run(callback, key, onerror=onerror)
1996
1997# --------------------------------------------------- web browser interface
1998
1999def serve(port, callback=None, completer=None):
2000    import BaseHTTPServer, mimetools, select
2001
2002    # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
2003    class Message(mimetools.Message):
2004        def __init__(self, fp, seekable=1):
2005            Message = self.__class__
2006            Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
2007            self.encodingheader = self.getheader('content-transfer-encoding')
2008            self.typeheader = self.getheader('content-type')
2009            self.parsetype()
2010            self.parseplist()
2011
2012    class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
2013        def send_document(self, title, contents):
2014            try:
2015                self.send_response(200)
2016                self.send_header('Content-Type', 'text/html')
2017                self.end_headers()
2018                self.wfile.write(html.page(title, contents))
2019            except IOError: pass
2020
2021        def do_GET(self):
2022            path = self.path
2023            if path[-5:] == '.html': path = path[:-5]
2024            if path[:1] == '/': path = path[1:]
2025            if path and path != '.':
2026                try:
2027                    obj = locate(path, forceload=1)
2028                except ErrorDuringImport, value:
2029                    self.send_document(path, html.escape(str(value)))
2030                    return
2031                if obj:
2032                    self.send_document(describe(obj), html.document(obj, path))
2033                else:
2034                    self.send_document(path,
2035'no Python documentation found for %s' % repr(path))
2036            else:
2037                heading = html.heading(
2038'<big><big><strong>Python: Index of Modules</strong></big></big>',
2039'#ffffff', '#7799ee')
2040                def bltinlink(name):
2041                    return '<a href="%s.html">%s</a>' % (name, name)
2042                names = filter(lambda x: x != '__main__',
2043                               sys.builtin_module_names)
2044                contents = html.multicolumn(names, bltinlink)
2045                indices = ['<p>' + html.bigsection(
2046                    'Built-in Modules', '#ffffff', '#ee77aa', contents)]
2047
2048                seen = {}
2049                for dir in sys.path:
2050                    indices.append(html.index(dir, seen))
2051                contents = heading + join(indices) + '''<p align=right>
2052<font color="#909090" face="helvetica, arial"><strong>
2053pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font>'''
2054                self.send_document('Index of Modules', contents)
2055
2056        def log_message(self, *args): pass
2057
2058    class DocServer(BaseHTTPServer.HTTPServer):
2059        def __init__(self, port, callback):
2060            host = 'localhost'
2061            self.address = (host, port)
2062            self.url = 'http://%s:%d/' % (host, port)
2063            self.callback = callback
2064            self.base.__init__(self, self.address, self.handler)
2065
2066        def serve_until_quit(self):
2067            import select
2068            self.quit = False
2069            while not self.quit:
2070                rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
2071                if rd: self.handle_request()
2072
2073        def server_activate(self):
2074            self.base.server_activate(self)
2075            if self.callback: self.callback(self)
2076
2077    DocServer.base = BaseHTTPServer.HTTPServer
2078    DocServer.handler = DocHandler
2079    DocHandler.MessageClass = Message
2080    try:
2081        try:
2082            DocServer(port, callback).serve_until_quit()
2083        except (KeyboardInterrupt, select.error):
2084            pass
2085    finally:
2086        if completer: completer()
2087
2088# ----------------------------------------------------- graphical interface
2089
2090def gui():
2091    """Graphical interface (starts web server and pops up a control window)."""
2092    class GUI:
2093        def __init__(self, window, port=7464):
2094            self.window = window
2095            self.server = None
2096            self.scanner = None
2097
2098            import Tkinter
2099            self.server_frm = Tkinter.Frame(window)
2100            self.title_lbl = Tkinter.Label(self.server_frm,
2101                text='Starting server...\n ')
2102            self.open_btn = Tkinter.Button(self.server_frm,
2103                text='open browser', command=self.open, state='disabled')
2104            self.quit_btn = Tkinter.Button(self.server_frm,
2105                text='quit serving', command=self.quit, state='disabled')
2106
2107            self.search_frm = Tkinter.Frame(window)
2108            self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
2109            self.search_ent = Tkinter.Entry(self.search_frm)
2110            self.search_ent.bind('<Return>', self.search)
2111            self.stop_btn = Tkinter.Button(self.search_frm,
2112                text='stop', pady=0, command=self.stop, state='disabled')
2113            if sys.platform == 'win32':
2114                # Trying to hide and show this button crashes under Windows.
2115                self.stop_btn.pack(side='right')
2116
2117            self.window.title('pydoc')
2118            self.window.protocol('WM_DELETE_WINDOW', self.quit)
2119            self.title_lbl.pack(side='top', fill='x')
2120            self.open_btn.pack(side='left', fill='x', expand=1)
2121            self.quit_btn.pack(side='right', fill='x', expand=1)
2122            self.server_frm.pack(side='top', fill='x')
2123
2124            self.search_lbl.pack(side='left')
2125            self.search_ent.pack(side='right', fill='x', expand=1)
2126            self.search_frm.pack(side='top', fill='x')
2127            self.search_ent.focus_set()
2128
2129            font = ('helvetica', sys.platform == 'win32' and 8 or 10)
2130            self.result_lst = Tkinter.Listbox(window, font=font, height=6)
2131            self.result_lst.bind('<Button-1>', self.select)
2132            self.result_lst.bind('<Double-Button-1>', self.goto)
2133            self.result_scr = Tkinter.Scrollbar(window,
2134                orient='vertical', command=self.result_lst.yview)
2135            self.result_lst.config(yscrollcommand=self.result_scr.set)
2136
2137            self.result_frm = Tkinter.Frame(window)
2138            self.goto_btn = Tkinter.Button(self.result_frm,
2139                text='go to selected', command=self.goto)
2140            self.hide_btn = Tkinter.Button(self.result_frm,
2141                text='hide results', command=self.hide)
2142            self.goto_btn.pack(side='left', fill='x', expand=1)
2143            self.hide_btn.pack(side='right', fill='x', expand=1)
2144
2145            self.window.update()
2146            self.minwidth = self.window.winfo_width()
2147            self.minheight = self.window.winfo_height()
2148            self.bigminheight = (self.server_frm.winfo_reqheight() +
2149                                 self.search_frm.winfo_reqheight() +
2150                                 self.result_lst.winfo_reqheight() +
2151                                 self.result_frm.winfo_reqheight())
2152            self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
2153            self.expanded = 0
2154            self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
2155            self.window.wm_minsize(self.minwidth, self.minheight)
2156            self.window.tk.willdispatch()
2157
2158            import threading
2159            threading.Thread(
2160                target=serve, args=(port, self.ready, self.quit)).start()
2161
2162        def ready(self, server):
2163            self.server = server
2164            self.title_lbl.config(
2165                text='Python documentation server at\n' + server.url)
2166            self.open_btn.config(state='normal')
2167            self.quit_btn.config(state='normal')
2168
2169        def open(self, event=None, url=None):
2170            url = url or self.server.url
2171            try:
2172                import webbrowser
2173                webbrowser.open(url)
2174            except ImportError: # pre-webbrowser.py compatibility
2175                if sys.platform == 'win32':
2176                    os.system('start "%s"' % url)
2177                else:
2178                    rc = os.system('netscape -remote "openURL(%s)" &' % url)
2179                    if rc: os.system('netscape "%s" &' % url)
2180
2181        def quit(self, event=None):
2182            if self.server:
2183                self.server.quit = 1
2184            self.window.quit()
2185
2186        def search(self, event=None):
2187            key = self.search_ent.get()
2188            self.stop_btn.pack(side='right')
2189            self.stop_btn.config(state='normal')
2190            self.search_lbl.config(text='Searching for "%s"...' % key)
2191            self.search_ent.forget()
2192            self.search_lbl.pack(side='left')
2193            self.result_lst.delete(0, 'end')
2194            self.goto_btn.config(state='disabled')
2195            self.expand()
2196
2197            import threading
2198            if self.scanner:
2199                self.scanner.quit = 1
2200            self.scanner = ModuleScanner()
2201            threading.Thread(target=self.scanner.run,
2202                             args=(self.update, key, self.done)).start()
2203
2204        def update(self, path, modname, desc):
2205            if modname[-9:] == '.__init__':
2206                modname = modname[:-9] + ' (package)'
2207            self.result_lst.insert('end',
2208                modname + ' - ' + (desc or '(no description)'))
2209
2210        def stop(self, event=None):
2211            if self.scanner:
2212                self.scanner.quit = 1
2213                self.scanner = None
2214
2215        def done(self):
2216            self.scanner = None
2217            self.search_lbl.config(text='Search for')
2218            self.search_lbl.pack(side='left')
2219            self.search_ent.pack(side='right', fill='x', expand=1)
2220            if sys.platform != 'win32': self.stop_btn.forget()
2221            self.stop_btn.config(state='disabled')
2222
2223        def select(self, event=None):
2224            self.goto_btn.config(state='normal')
2225
2226        def goto(self, event=None):
2227            selection = self.result_lst.curselection()
2228            if selection:
2229                modname = split(self.result_lst.get(selection[0]))[0]
2230                self.open(url=self.server.url + modname + '.html')
2231
2232        def collapse(self):
2233            if not self.expanded: return
2234            self.result_frm.forget()
2235            self.result_scr.forget()
2236            self.result_lst.forget()
2237            self.bigwidth = self.window.winfo_width()
2238            self.bigheight = self.window.winfo_height()
2239            self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
2240            self.window.wm_minsize(self.minwidth, self.minheight)
2241            self.expanded = 0
2242
2243        def expand(self):
2244            if self.expanded: return
2245            self.result_frm.pack(side='bottom', fill='x')
2246            self.result_scr.pack(side='right', fill='y')
2247            self.result_lst.pack(side='top', fill='both', expand=1)
2248            self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
2249            self.window.wm_minsize(self.minwidth, self.bigminheight)
2250            self.expanded = 1
2251
2252        def hide(self, event=None):
2253            self.stop()
2254            self.collapse()
2255
2256    import Tkinter
2257    try:
2258        root = Tkinter.Tk()
2259        # Tk will crash if pythonw.exe has an XP .manifest
2260        # file and the root has is not destroyed explicitly.
2261        # If the problem is ever fixed in Tk, the explicit
2262        # destroy can go.
2263        try:
2264            gui = GUI(root)
2265            root.mainloop()
2266        finally:
2267            root.destroy()
2268    except KeyboardInterrupt:
2269        pass
2270
2271# -------------------------------------------------- command-line interface
2272
2273def ispath(x):
2274    return isinstance(x, str) and find(x, os.sep) >= 0
2275
2276def cli():
2277    """Command-line interface (looks at sys.argv to decide what to do)."""
2278    import getopt
2279    class BadUsage: pass
2280
2281    # Scripts don't get the current directory in their path by default
2282    # unless they are run with the '-m' switch
2283    if '' not in sys.path:
2284        scriptdir = os.path.dirname(sys.argv[0])
2285        if scriptdir in sys.path:
2286            sys.path.remove(scriptdir)
2287        sys.path.insert(0, '.')
2288
2289    try:
2290        opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
2291        writing = 0
2292
2293        for opt, val in opts:
2294            if opt == '-g':
2295                gui()
2296                return
2297            if opt == '-k':
2298                apropos(val)
2299                return
2300            if opt == '-p':
2301                try:
2302                    port = int(val)
2303                except ValueError:
2304                    raise BadUsage
2305                def ready(server):
2306                    print 'pydoc server ready at %s' % server.url
2307                def stopped():
2308                    print 'pydoc server stopped'
2309                serve(port, ready, stopped)
2310                return
2311            if opt == '-w':
2312                writing = 1
2313
2314        if not args: raise BadUsage
2315        for arg in args:
2316            if ispath(arg) and not os.path.exists(arg):
2317                print 'file %r does not exist' % arg
2318                break
2319            try:
2320                if ispath(arg) and os.path.isfile(arg):
2321                    arg = importfile(arg)
2322                if writing:
2323                    if ispath(arg) and os.path.isdir(arg):
2324                        writedocs(arg)
2325                    else:
2326                        writedoc(arg)
2327                else:
2328                    help.help(arg)
2329            except ErrorDuringImport, value:
2330                print value
2331
2332    except (getopt.error, BadUsage):
2333        cmd = os.path.basename(sys.argv[0])
2334        print """pydoc - the Python documentation tool
2335
2336%s <name> ...
2337    Show text documentation on something.  <name> may be the name of a
2338    Python keyword, topic, function, module, or package, or a dotted
2339    reference to a class or function within a module or module in a
2340    package.  If <name> contains a '%s', it is used as the path to a
2341    Python source file to document. If name is 'keywords', 'topics',
2342    or 'modules', a listing of these things is displayed.
2343
2344%s -k <keyword>
2345    Search for a keyword in the synopsis lines of all available modules.
2346
2347%s -p <port>
2348    Start an HTTP server on the given port on the local machine.
2349
2350%s -g
2351    Pop up a graphical interface for finding and serving documentation.
2352
2353%s -w <name> ...
2354    Write out the HTML documentation for a module to a file in the current
2355    directory.  If <name> contains a '%s', it is treated as a filename; if
2356    it names a directory, documentation is written for all the contents.
2357""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
2358
2359if __name__ == '__main__': cli()
2360