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