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