2255 lines · 87.9 KB · Python
RawDownload
#!/usr/bin/env python# -*- coding: Latin-1 -*-"""Generate Python documentation in HTML or text for interactive use. In the Python interpreter, do "from pydoc import help" to provide onlinehelp.  Calling help(thing) on a Python object documents the object. Or, at the shell command line outside of Python: Run "pydoc <name>" to show documentation on something.  <name> may bethe name of a function, module, package, or a dotted reference to aclass or function within a module or module in a package.  If theargument contains a path segment delimiter (e.g. slash on Unix,backslash on Windows) it is treated as the path to a Python source file. Run "pydoc -k <keyword>" to search for a keyword in the synopsis linesof all available modules. Run "pydoc -p <port>" to start an HTTP server on a given port on thelocal machine to generate documentation web pages. For platforms without a command line, "pydoc -g" starts the HTTP serverand also pops up a little window for controlling it. Run "pydoc -w <name>" to write out the HTML documentation for a moduleto a file named "<name>.html". Module docs for core modules are assumed to be in     http://www.python.org/doc/current/lib/ This can be overridden by setting the PYTHONDOCS environment variableto a different URL or to a local directory containing the LibraryReference Manual pages.""" __author__ = "Ka-Ping Yee <ping@lfw.org>"__date__ = "26 February 2001" __version__ = "$Revision: 54366 $"__credits__ = """Guido van Rossum, for an excellent programming language.Tommy Burnette, the original creator of manpy.Paul Prescod, for all his work on onlinehelp.Richard Chamberlain, for the first implementation of textdoc.""" # Known bugs that can't be fixed here:#   - imp.load_module() cannot be prevented from clobbering existing#     loaded modules, so calling synopsis() on a binary module file#     changes the contents of any existing module with the same name.#   - If the __file__ attribute on a module is a relative path and#     the current directory is changed with os.chdir(), an incorrect#     path will be displayed. import sys, imp, os, re, types, inspect, __builtin__, pkgutilfrom repr import Reprfrom string import expandtabs, find, join, lower, split, strip, rfind, rstriptry:    from collections import dequeexcept ImportError:    # Python 2.3 compatibility    class deque(list):        def popleft(self):            return self.pop(0) # --------------------------------------------------------- common routines def pathdirs():    """Convert sys.path into a list of absolute, existing, unique paths."""    dirs = []    normdirs = []    for dir in sys.path:        dir = os.path.abspath(dir or '.')        normdir = os.path.normcase(dir)        if normdir not in normdirs and os.path.isdir(dir):            dirs.append(dir)            normdirs.append(normdir)    return dirs def getdoc(object):    """Get the doc string or comments for an object."""    result = inspect.getdoc(object) or inspect.getcomments(object)    return result and re.sub('^ *\n', '', rstrip(result)) or '' def splitdoc(doc):    """Split a doc string into a synopsis line (if any) and the rest."""    lines = split(strip(doc), '\n')    if len(lines) == 1:        return lines[0], ''    elif len(lines) >= 2 and not rstrip(lines[1]):        return lines[0], join(lines[2:], '\n')    return '', join(lines, '\n') def classname(object, modname):    """Get a class name and qualify it with a module name if necessary."""    name = object.__name__    if object.__module__ != modname:        name = object.__module__ + '.' + name    return name def isdata(object):    """Check if an object is of a type that probably means it's data."""    return not (inspect.ismodule(object) or inspect.isclass(object) or                inspect.isroutine(object) or inspect.isframe(object) or                inspect.istraceback(object) or inspect.iscode(object)) def replace(text, *pairs):    """Do a series of global replacements on a string."""    while pairs:        text = join(split(text, pairs[0]), pairs[1])        pairs = pairs[2:]    return text def cram(text, maxlen):    """Omit part of a string if needed to make it fit in a maximum length."""    if len(text) > maxlen:        pre = max(0, (maxlen-3)//2)        post = max(0, maxlen-3-pre)        return text[:pre] + '...' + text[len(text)-post:]    return text _re_stripid = re.compile(r' at 0x[0-9a-f]{6,16}(>+)$', re.IGNORECASE)def stripid(text):    """Remove the hexadecimal id from a Python object representation."""    # The behaviour of %p is implementation-dependent in terms of case.    if _re_stripid.search(repr(Exception)):        return _re_stripid.sub(r'\1', text)    return text def _is_some_method(obj):    return inspect.ismethod(obj) or inspect.ismethoddescriptor(obj) def allmethods(cl):    methods = {}    for key, value in inspect.getmembers(cl, _is_some_method):        methods[key] = 1    for base in cl.__bases__:        methods.update(allmethods(base)) # all your base are belong to us    for key in methods.keys():        methods[key] = getattr(cl, key)    return methods def _split_list(s, predicate):    """Split sequence s via predicate, and return pair ([true], [false]).     The return value is a 2-tuple of lists,        ([x for x in s if predicate(x)],         [x for x in s if not predicate(x)])    """     yes = []    no = []    for x in s:        if predicate(x):            yes.append(x)        else:            no.append(x)    return yes, no def visiblename(name, all=None):    """Decide whether to show documentation on a variable."""    # Certain special names are redundant.    if name in ('__builtins__', '__doc__', '__file__', '__path__',                '__module__', '__name__', '__slots__'): return 0    # Private names are hidden, but special names are displayed.    if name.startswith('__') and name.endswith('__'): return 1    if all is not None:        # only document that which the programmer exported in __all__        return name in all    else:        return not name.startswith('_') def classify_class_attrs(object):    """Wrap inspect.classify_class_attrs, with fixup for data descriptors."""    def fixup((name, kind, cls, value)):        if inspect.isdatadescriptor(value):            kind = 'data descriptor'        return name, kind, cls, value    return map(fixup, inspect.classify_class_attrs(object)) # ----------------------------------------------------- module manipulation def ispackage(path):    """Guess whether a path refers to a package directory."""    if os.path.isdir(path):        for ext in ('.py', '.pyc', '.pyo'):            if os.path.isfile(os.path.join(path, '__init__' + ext)):                return True    return False def source_synopsis(file):    line = file.readline()    while line[:1] == '#' or not strip(line):        line = file.readline()        if not line: break    line = strip(line)    if line[:4] == 'r"""': line = line[1:]    if line[:3] == '"""':        line = line[3:]        if line[-1:] == '\\': line = line[:-1]        while not strip(line):            line = file.readline()            if not line: break        result = strip(split(line, '"""')[0])    else: result = None    return result def synopsis(filename, cache={}):    """Get the one-line summary out of a module file."""    mtime = os.stat(filename).st_mtime    lastupdate, result = cache.get(filename, (0, None))    if lastupdate < mtime:        info = inspect.getmoduleinfo(filename)        try:            file = open(filename)        except IOError:            # module can't be opened, so skip it            return None        if info and 'b' in info[2]: # binary modules have to be imported            try: module = imp.load_module('__temp__', file, filename, info[1:])            except: return None            result = (module.__doc__ or '').splitlines()[0]            del sys.modules['__temp__']        else: # text modules can be directly examined            result = source_synopsis(file)            file.close()        cache[filename] = (mtime, result)    return result class ErrorDuringImport(Exception):    """Errors that occurred while trying to import something to document it."""    def __init__(self, filename, (exc, value, tb)):        self.filename = filename        self.exc = exc        self.value = value        self.tb = tb     def __str__(self):        exc = self.exc        if type(exc) is types.ClassType:            exc = exc.__name__        return 'problem in %s - %s: %s' % (self.filename, exc, self.value) def importfile(path):    """Import a Python source file or compiled file given its path."""    magic = imp.get_magic()    file = open(path, 'r')    if file.read(len(magic)) == magic:        kind = imp.PY_COMPILED    else:        kind = imp.PY_SOURCE    file.close()    filename = os.path.basename(path)    name, ext = os.path.splitext(filename)    file = open(path, 'r')    try:        module = imp.load_module(name, file, path, (ext, 'r', kind))    except:        raise ErrorDuringImport(path, sys.exc_info())    file.close()    return module def safeimport(path, forceload=0, cache={}):    """Import a module; handle errors; return None if the module isn't found.     If the module *is* found but an exception occurs, it's wrapped in an    ErrorDuringImport exception and reraised.  Unlike __import__, if a    package path is specified, the module at the end of the path is returned,    not the package at the beginning.  If the optional 'forceload' argument    is 1, we reload the module from disk (unless it's a dynamic extension)."""    try:        # If forceload is 1 and the module has been previously loaded from        # disk, we always have to reload the module.  Checking the file's        # mtime isn't good enough (e.g. the module could contain a class        # that inherits from another module that has changed).        if forceload and path in sys.modules:            if path not in sys.builtin_module_names:                # Avoid simply calling reload() because it leaves names in                # the currently loaded module lying around if they're not                # defined in the new source file.  Instead, remove the                # module from sys.modules and re-import.  Also remove any                # submodules because they won't appear in the newly loaded                # module's namespace if they're already in sys.modules.                subs = [m for m in sys.modules if m.startswith(path + '.')]                for key in [path] + subs:                    # Prevent garbage collection.                    cache[key] = sys.modules[key]                    del sys.modules[key]        module = __import__(path)    except:        # Did the error occur before or after the module was found?        (exc, value, tb) = info = sys.exc_info()        if path in sys.modules:            # An error occurred while executing the imported module.            raise ErrorDuringImport(sys.modules[path].__file__, info)        elif exc is SyntaxError:            # A SyntaxError occurred before we could execute the module.            raise ErrorDuringImport(value.filename, info)        elif exc is ImportError and \             split(lower(str(value)))[:2] == ['no', 'module']:            # The module was not found.            return None        else:            # Some other error occurred during the importing process.            raise ErrorDuringImport(path, sys.exc_info())    for part in split(path, '.')[1:]:        try: module = getattr(module, part)        except AttributeError: return None    return module # ---------------------------------------------------- formatter base class class Doc:    def document(self, object, name=None, *args):        """Generate documentation for an object."""        args = (object, name) + args        # 'try' clause is to attempt to handle the possibility that inspect        # identifies something in a way that pydoc itself has issues handling;        # think 'super' and how it is a descriptor (which raises the exception        # by lacking a __name__ attribute) and an instance.        if inspect.isgetsetdescriptor(object): return self.docdata(*args)        if inspect.ismemberdescriptor(object): return self.docdata(*args)        try:            if inspect.ismodule(object): return self.docmodule(*args)            if inspect.isclass(object): return self.docclass(*args)            if inspect.isroutine(object): return self.docroutine(*args)        except AttributeError:            pass        if isinstance(object, property): return self.docproperty(*args)        return self.docother(*args)     def fail(self, object, name=None, *args):        """Raise an exception for unimplemented types."""        message = "don't know how to document object%s of type %s" % (            name and ' ' + repr(name), type(object).__name__)        raise TypeError, message     docmodule = docclass = docroutine = docother = docproperty = docdata = fail     def getdocloc(self, object):        """Return the location of module docs or None"""         try:            file = inspect.getabsfile(object)        except TypeError:            file = '(built-in)'         docloc = os.environ.get("PYTHONDOCS",                                "http://www.python.org/doc/current/lib")        basedir = os.path.join(sys.exec_prefix, "lib",                               "python"+sys.version[0:3])        if (isinstance(object, type(os)) and            (object.__name__ in ('errno', 'exceptions', 'gc', 'imp',                                 'marshal', 'posix', 'signal', 'sys',                                 'thread', 'zipimport') or             (file.startswith(basedir) and              not file.startswith(os.path.join(basedir, 'site-packages'))))):            htmlfile = "module-%s.html" % object.__name__            if docloc.startswith("http://"):                docloc = "%s/%s" % (docloc.rstrip("/"), htmlfile)            else:                docloc = os.path.join(docloc, htmlfile)        else:            docloc = None        return docloc # -------------------------------------------- HTML documentation generator class HTMLRepr(Repr):    """Class for safely making an HTML representation of a Python object."""    def __init__(self):        Repr.__init__(self)        self.maxlist = self.maxtuple = 20        self.maxdict = 10        self.maxstring = self.maxother = 100     def escape(self, text):        return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')     def repr(self, object):        return Repr.repr(self, object)     def repr1(self, x, level):        if hasattr(type(x), '__name__'):            methodname = 'repr_' + join(split(type(x).__name__), '_')            if hasattr(self, methodname):                return getattr(self, methodname)(x, level)        return self.escape(cram(stripid(repr(x)), self.maxother))     def repr_string(self, x, level):        test = cram(x, self.maxstring)        testrepr = repr(test)        if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):            # Backslashes are only literal in the string and are never            # needed to make any special characters, so show a raw string.            return 'r' + testrepr[0] + self.escape(test) + testrepr[0]        return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',                      r'<font color="#c040c0">\1</font>',                      self.escape(testrepr))     repr_str = repr_string     def repr_instance(self, x, level):        try:            return self.escape(cram(stripid(repr(x)), self.maxstring))        except:            return self.escape('<%s instance>' % x.__class__.__name__)     repr_unicode = repr_string class HTMLDoc(Doc):    """Formatter class for HTML documentation."""     # ------------------------------------------- HTML formatting utilities     _repr_instance = HTMLRepr()    repr = _repr_instance.repr    escape = _repr_instance.escape     def page(self, title, contents):        """Format an HTML page."""        return '''<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><html><head><title>Python: %s</title></head><body bgcolor="#f0f0f8">%s</body></html>''' % (title, contents)     def heading(self, title, fgcol, bgcol, extras=''):        """Format a page heading."""        return '''<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading"><tr bgcolor="%s"><td valign=bottom>&nbsp;<br><font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td><td align=right valign=bottom><font color="%s" face="helvetica, arial">%s</font></td></tr></table>    ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')     def section(self, title, fgcol, bgcol, contents, width=6,                prelude='', marginalia=None, gap='&nbsp;'):        """Format a section with a heading."""        if marginalia is None:            marginalia = '<tt>' + '&nbsp;' * width + '</tt>'        result = '''<p><table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"><tr bgcolor="%s"><td colspan=3 valign=bottom>&nbsp;<br><font color="%s" face="helvetica, arial">%s</font></td></tr>    ''' % (bgcol, fgcol, title)        if prelude:            result = result + '''<tr bgcolor="%s"><td rowspan=2>%s</td><td colspan=2>%s</td></tr><tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)        else:            result = result + '''<tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)         return result + '\n<td width="100%%">%s</td></tr></table>' % contents     def bigsection(self, title, *args):        """Format a section with a big heading."""        title = '<big><strong>%s</strong></big>' % title        return self.section(title, *args)     def preformat(self, text):        """Format literal preformatted text."""        text = self.escape(expandtabs(text))        return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',                             ' ', '&nbsp;', '\n', '<br>\n')     def multicolumn(self, list, format, cols=4):        """Format a list of items into a multi-column list."""        result = ''        rows = (len(list)+cols-1)/cols        for col in range(cols):            result = result + '<td width="%d%%" valign=top>' % (100/cols)            for i in range(rows*col, rows*col+rows):                if i < len(list):                    result = result + format(list[i]) + '<br>\n'            result = result + '</td>'        return '<table width="100%%" summary="list"><tr>%s</tr></table>' % result     def grey(self, text): return '<font color="#909090">%s</font>' % text     def namelink(self, name, *dicts):        """Make a link for an identifier, given name-to-URL mappings."""        for dict in dicts:            if name in dict:                return '<a href="%s">%s</a>' % (dict[name], name)        return name     def classlink(self, object, modname):        """Make a link for a class."""        name, module = object.__name__, sys.modules.get(object.__module__)        if hasattr(module, name) and getattr(module, name) is object:            return '<a href="%s.html#%s">%s</a>' % (                module.__name__, name, classname(object, modname))        return classname(object, modname)

Lines 1–500 of 2255.