blob: ece8f9609bfe16ca53d42eb95e6e719dba0a26e2 [file] [log] [blame]
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001#!/usr/bin/env python
Ka-Ping Yee1d384632001-03-01 00:24:32 +00002"""Generate Python documentation in HTML or text for interactive use.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00003
Ka-Ping Yeedd175342001-02-27 14:43:46 +00004In the Python interpreter, do "from pydoc import help" to provide online
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00005help. Calling help(thing) on a Python object documents the object.
6
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00007Or, at the shell command line outside of Python:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00008
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00009Run "pydoc <name>" to show documentation on something. <name> may be
10the name of a function, module, package, or a dotted reference to a
11class or function within a module or module in a package. If the
12argument contains a path segment delimiter (e.g. slash on Unix,
13backslash on Windows) it is treated as the path to a Python source file.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000014
Ka-Ping Yee37f7b382001-03-23 00:12:53 +000015Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines
16of all available modules.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000017
Ka-Ping Yee37f7b382001-03-23 00:12:53 +000018Run "pydoc -p <port>" to start an HTTP server on a given port on the
19local machine to generate documentation web pages.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000020
Ka-Ping Yee37f7b382001-03-23 00:12:53 +000021For platforms without a command line, "pydoc -g" starts the HTTP server
22and also pops up a little window for controlling it.
23
24Run "pydoc -w <name>" to write out the HTML documentation for a module
25to a file named "<name>.html".
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000026"""
Ka-Ping Yeedd175342001-02-27 14:43:46 +000027
28__author__ = "Ka-Ping Yee <ping@lfw.org>"
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +000029__date__ = "26 February 2001"
Ka-Ping Yee09d7d9a2001-02-27 22:43:48 +000030__version__ = "$Revision$"
Ka-Ping Yee5e2b1732001-02-27 23:35:09 +000031__credits__ = """Guido van Rossum, for an excellent programming language.
32Tommy Burnette, the original creator of manpy.
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +000033Paul Prescod, for all his work on onlinehelp.
34Richard Chamberlain, for the first implementation of textdoc.
35
Ka-Ping Yee5e2b1732001-02-27 23:35:09 +000036Mynd you, møøse bites Kan be pretty nasti..."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +000037
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000038# Note: this module is designed to deploy instantly and run under any
39# version of Python from 1.5 and up. That's why it's a single file and
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +000040# some 2.0 features (like string methods) are conspicuously absent.
41
42# Known bugs that can't be fixed here:
43# - imp.load_module() cannot be prevented from clobbering existing
44# loaded modules, so calling synopsis() on a binary module file
45# changes the contents of any existing module with the same name.
46# - If the __file__ attribute on a module is a relative path and
47# the current directory is changed with os.chdir(), an incorrect
48# path will be displayed.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000049
Ka-Ping Yeedd175342001-02-27 14:43:46 +000050import sys, imp, os, stat, re, types, inspect
51from repr import Repr
Ka-Ping Yee3bda8792001-03-23 13:17:50 +000052from string import expandtabs, find, join, lower, split, strip, rfind, rstrip
Ka-Ping Yeedd175342001-02-27 14:43:46 +000053
54# --------------------------------------------------------- common routines
55
56def synopsis(filename, cache={}):
57 """Get the one-line summary out of a module file."""
58 mtime = os.stat(filename)[stat.ST_MTIME]
59 lastupdate, result = cache.get(filename, (0, None))
60 if lastupdate < mtime:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +000061 info = inspect.getmoduleinfo(filename)
Ka-Ping Yeedd175342001-02-27 14:43:46 +000062 file = open(filename)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +000063 if info and 'b' in info[2]: # binary modules have to be imported
64 try: module = imp.load_module(info[0], file, filename, info[1:])
65 except: return None
66 result = split(module.__doc__ or '', '\n')[0]
67 else: # text modules can be directly examined
Ka-Ping Yeedd175342001-02-27 14:43:46 +000068 line = file.readline()
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +000069 while line[:1] == '#' or strip(line) == '':
Ka-Ping Yeedd175342001-02-27 14:43:46 +000070 line = file.readline()
71 if not line: break
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +000072 line = strip(line)
73 if line[:4] == 'r"""': line = line[1:]
74 if line[:3] == '"""':
75 line = line[3:]
76 if line[-1:] == '\\': line = line[:-1]
77 while not strip(line):
78 line = file.readline()
79 if not line: break
80 result = strip(split(line, '"""')[0])
81 else: result = None
Ka-Ping Yeedd175342001-02-27 14:43:46 +000082 file.close()
83 cache[filename] = (mtime, result)
84 return result
85
Ka-Ping Yeedd175342001-02-27 14:43:46 +000086def pathdirs():
87 """Convert sys.path into a list of absolute, existing, unique paths."""
88 dirs = []
Ka-Ping Yee1d384632001-03-01 00:24:32 +000089 normdirs = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +000090 for dir in sys.path:
91 dir = os.path.abspath(dir or '.')
Ka-Ping Yee1d384632001-03-01 00:24:32 +000092 normdir = os.path.normcase(dir)
93 if normdir not in normdirs and os.path.isdir(dir):
Ka-Ping Yeedd175342001-02-27 14:43:46 +000094 dirs.append(dir)
Ka-Ping Yee1d384632001-03-01 00:24:32 +000095 normdirs.append(normdir)
Ka-Ping Yeedd175342001-02-27 14:43:46 +000096 return dirs
97
98def getdoc(object):
99 """Get the doc string or comments for an object."""
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000100 result = inspect.getdoc(object) or inspect.getcomments(object)
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000101 return result and re.sub('^ *\n', '', rstrip(result)) or ''
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000102
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000103def splitdoc(doc):
104 """Split a doc string into a synopsis line (if any) and the rest."""
105 lines = split(strip(doc), '\n')
106 if len(lines) == 1:
107 return lines[0], ''
108 elif len(lines) >= 2 and not rstrip(lines[1]):
109 return lines[0], join(lines[2:], '\n')
110 return '', join(lines, '\n')
111
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000112def classname(object, modname):
113 """Get a class name and qualify it with a module name if necessary."""
114 name = object.__name__
115 if object.__module__ != modname:
116 name = object.__module__ + '.' + name
117 return name
118
119def isconstant(object):
120 """Check if an object is of a type that probably means it's a constant."""
121 return type(object) in [
122 types.FloatType, types.IntType, types.ListType, types.LongType,
123 types.StringType, types.TupleType, types.TypeType,
124 hasattr(types, 'UnicodeType') and types.UnicodeType or 0]
125
126def replace(text, *pairs):
127 """Do a series of global replacements on a string."""
128 for old, new in pairs:
129 text = join(split(text, old), new)
130 return text
131
132def cram(text, maxlen):
133 """Omit part of a string if needed to make it fit in a maximum length."""
134 if len(text) > maxlen:
135 pre = max(0, (maxlen-3)/2)
136 post = max(0, maxlen-3-pre)
137 return text[:pre] + '...' + text[len(text)-post:]
138 return text
139
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000140def stripid(text):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000141 """Remove the hexadecimal id from a Python object representation."""
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000142 # The behaviour of %p is implementation-dependent; we check two cases.
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000143 for pattern in [' at 0x[0-9a-f]{6,}>$', ' at [0-9A-F]{8,}>$']:
144 if re.search(pattern, repr(Exception)):
145 return re.sub(pattern, '>', text)
146 return text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000147
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000148def allmethods(cl):
149 methods = {}
150 for key, value in inspect.getmembers(cl, inspect.ismethod):
151 methods[key] = 1
152 for base in cl.__bases__:
153 methods.update(allmethods(base)) # all your base are belong to us
154 for key in methods.keys():
155 methods[key] = getattr(cl, key)
156 return methods
157
158class ErrorDuringImport(Exception):
159 """Errors that occurred while trying to import something to document it."""
160 def __init__(self, filename, (exc, value, tb)):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000161 self.filename = filename
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000162 self.exc = exc
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000163 self.value = value
164 self.tb = tb
165
166 def __str__(self):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000167 exc = self.exc
168 if type(exc) is types.ClassType:
169 exc = exc.__name__
170 return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000171
172def importfile(path):
173 """Import a Python source file or compiled file given its path."""
174 magic = imp.get_magic()
175 file = open(path, 'r')
176 if file.read(len(magic)) == magic:
177 kind = imp.PY_COMPILED
178 else:
179 kind = imp.PY_SOURCE
180 file.close()
181 filename = os.path.basename(path)
182 name, ext = os.path.splitext(filename)
183 file = open(path, 'r')
184 try:
185 module = imp.load_module(name, file, path, (ext, 'r', kind))
186 except:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000187 raise ErrorDuringImport(path, sys.exc_info())
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000188 file.close()
189 return module
190
191def ispackage(path):
192 """Guess whether a path refers to a package directory."""
193 if os.path.isdir(path):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000194 for ext in ['.py', '.pyc', '.pyo']:
195 if os.path.isfile(os.path.join(path, '__init__' + ext)):
196 return 1
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000197
198# ---------------------------------------------------- formatter base class
199
200class Doc:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000201 def document(self, object, name=None, *args):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000202 """Generate documentation for an object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000203 args = (object, name) + args
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000204 if inspect.ismodule(object): return apply(self.docmodule, args)
205 if inspect.isclass(object): return apply(self.docclass, args)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000206 if inspect.isroutine(object): return apply(self.docroutine, args)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000207 return apply(self.docother, args)
208
209 def fail(self, object, name=None, *args):
210 """Raise an exception for unimplemented types."""
211 message = "don't know how to document object%s of type %s" % (
212 name and ' ' + repr(name), type(object).__name__)
213 raise TypeError, message
214
215 docmodule = docclass = docroutine = docother = fail
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000216
217# -------------------------------------------- HTML documentation generator
218
219class HTMLRepr(Repr):
220 """Class for safely making an HTML representation of a Python object."""
221 def __init__(self):
222 Repr.__init__(self)
223 self.maxlist = self.maxtuple = self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000224 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000225
226 def escape(self, text):
227 return replace(text, ('&', '&amp;'), ('<', '&lt;'), ('>', '&gt;'))
228
229 def repr(self, object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000230 return Repr.repr(self, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000231
232 def repr1(self, x, level):
233 methodname = 'repr_' + join(split(type(x).__name__), '_')
234 if hasattr(self, methodname):
235 return getattr(self, methodname)(x, level)
236 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000237 return self.escape(cram(stripid(repr(x)), self.maxother))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000238
239 def repr_string(self, x, level):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000240 test = cram(x, self.maxstring)
241 testrepr = repr(test)
242 if '\\' in test and '\\' not in replace(testrepr, (r'\\', '')):
243 # Backslashes are only literal in the string and are never
244 # needed to make any special characters, so show a raw string.
245 return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000246 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000247 r'<font color="#c040c0">\1</font>',
248 self.escape(testrepr))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000249
250 def repr_instance(self, x, level):
251 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000252 return self.escape(cram(stripid(repr(x)), self.maxstring))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000253 except:
254 return self.escape('<%s instance>' % x.__class__.__name__)
255
256 repr_unicode = repr_string
257
258class HTMLDoc(Doc):
259 """Formatter class for HTML documentation."""
260
261 # ------------------------------------------- HTML formatting utilities
262
263 _repr_instance = HTMLRepr()
264 repr = _repr_instance.repr
265 escape = _repr_instance.escape
266
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000267 def page(self, title, contents):
268 """Format an HTML page."""
269 return '''
270<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000271<html><head><title>Python: %s</title>
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000272<style type="text/css"><!--
273TT { font-family: lucida console, lucida typewriter, courier }
274--></style></head><body bgcolor="#f0f0f8">
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000275%s
276</body></html>''' % (title, contents)
277
278 def heading(self, title, fgcol, bgcol, extras=''):
279 """Format a page heading."""
280 return '''
281<table width="100%%" cellspacing=0 cellpadding=2 border=0>
282<tr bgcolor="%s">
283<td valign=bottom><small>&nbsp;<br></small
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000284><font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000285><td align=right valign=bottom
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000286><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000287 ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
288
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000289 def section(self, title, fgcol, bgcol, contents, width=10,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000290 prelude='', marginalia=None, gap='&nbsp;&nbsp;'):
291 """Format a section with a heading."""
292 if marginalia is None:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000293 marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000294 result = '''
295<p><table width="100%%" cellspacing=0 cellpadding=2 border=0>
296<tr bgcolor="%s">
297<td colspan=3 valign=bottom><small><small>&nbsp;<br></small></small
298><font color="%s" face="helvetica, arial">%s</font></td></tr>
299 ''' % (bgcol, fgcol, title)
300 if prelude:
301 result = result + '''
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000302<tr bgcolor="%s"><td rowspan=2>%s</td>
303<td colspan=2>%s</td></tr>
304<tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
305 else:
306 result = result + '''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000307<tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000308
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000309 return result + '\n<td width="100%%">%s</td></tr></table>' % contents
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000310
311 def bigsection(self, title, *args):
312 """Format a section with a big heading."""
313 title = '<big><strong>%s</strong></big>' % title
314 return apply(self.section, (title,) + args)
315
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000316 def preformat(self, text):
317 """Format literal preformatted text."""
318 text = self.escape(expandtabs(text))
319 return replace(text, ('\n\n', '\n \n'), ('\n\n', '\n \n'),
320 (' ', '&nbsp;'), ('\n', '<br>\n'))
321
322 def multicolumn(self, list, format, cols=4):
323 """Format a list of items into a multi-column list."""
324 result = ''
325 rows = (len(list)+cols-1)/cols
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000326 for col in range(cols):
327 result = result + '<td width="%d%%" valign=top>' % (100/cols)
328 for i in range(rows*col, rows*col+rows):
329 if i < len(list):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000330 result = result + format(list[i]) + '<br>\n'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000331 result = result + '</td>'
332 return '<table width="100%%"><tr>%s</tr></table>' % result
333
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000334 def small(self, text): return '<small>%s</small>' % text
335 def grey(self, text): return '<font color="#909090">%s</font>' % text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000336
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000337 def namelink(self, name, *dicts):
338 """Make a link for an identifier, given name-to-URL mappings."""
339 for dict in dicts:
340 if dict.has_key(name):
341 return '<a href="%s">%s</a>' % (dict[name], name)
342 return name
343
344 def classlink(self, object, modname, *dicts):
345 """Make a link for a class."""
346 name = object.__name__
347 if object.__module__ != modname:
348 name = object.__module__ + '.' + name
349 for dict in dicts:
350 if dict.has_key(object):
351 return '<a href="%s">%s</a>' % (dict[object], name)
352 return name
353
354 def modulelink(self, object):
355 """Make a link for a module."""
356 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
357
358 def modpkglink(self, (name, path, ispackage, shadowed)):
359 """Make a link for a module or package to display in an index."""
360 if shadowed:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000361 return self.grey(name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000362 if path:
363 url = '%s.%s.html' % (path, name)
364 else:
365 url = '%s.html' % name
366 if ispackage:
367 text = '<strong>%s</strong>&nbsp;(package)' % name
368 else:
369 text = name
370 return '<a href="%s">%s</a>' % (url, text)
371
372 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
373 """Mark up some plain text, given a context of symbols to look for.
374 Each context dictionary maps object names to anchor names."""
375 escape = escape or self.escape
376 results = []
377 here = 0
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000378 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
379 r'RFC[- ]?(\d+)|'
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000380 r'PEP[- ]?(\d+)|'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000381 r'(self\.)?(\w+))\b')
382 while 1:
383 match = pattern.search(text, here)
384 if not match: break
385 start, end = match.span()
386 results.append(escape(text[here:start]))
387
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000388 all, scheme, rfc, pep, selfdot, name = match.groups()
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000389 if scheme:
390 results.append('<a href="%s">%s</a>' % (all, escape(all)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000391 elif rfc:
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000392 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
393 results.append('<a href="%s">%s</a>' % (url, escape(all)))
394 elif pep:
395 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000396 results.append('<a href="%s">%s</a>' % (url, escape(all)))
397 elif text[end:end+1] == '(':
398 results.append(self.namelink(name, methods, funcs, classes))
399 elif selfdot:
400 results.append('self.<strong>%s</strong>' % name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000401 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000402 results.append(self.namelink(name, classes))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000403 here = end
404 results.append(escape(text[here:]))
405 return join(results, '')
406
407 # ---------------------------------------------- type-specific routines
408
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000409 def formattree(self, tree, modname, classes={}, parent=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000410 """Produce HTML for a class tree as given by inspect.getclasstree()."""
411 result = ''
412 for entry in tree:
413 if type(entry) is type(()):
414 c, bases = entry
415 result = result + '<dt><font face="helvetica, arial"><small>'
416 result = result + self.classlink(c, modname, classes)
417 if bases and bases != (parent,):
418 parents = []
419 for base in bases:
420 parents.append(self.classlink(base, modname, classes))
421 result = result + '(' + join(parents, ', ') + ')'
422 result = result + '\n</small></font></dt>'
423 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000424 result = result + '<dd>\n%s</dd>\n' % self.formattree(
425 entry, modname, classes, c)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000426 return '<dl>\n%s</dl>\n' % result
427
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000428 def docmodule(self, object, name=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000429 """Produce HTML documentation for a module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000430 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000431 parts = split(name, '.')
432 links = []
433 for i in range(len(parts)-1):
434 links.append(
435 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
436 (join(parts[:i+1], '.'), parts[i]))
437 linkedname = join(links + parts[-1:], '.')
438 head = '<big><big><strong>%s</strong></big></big>' % linkedname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000439 try:
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000440 path = inspect.getabsfile(object)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000441 filelink = '<a href="file:%s">%s</a>' % (path, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000442 except TypeError:
443 filelink = '(built-in)'
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000444 info = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000445 if hasattr(object, '__version__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000446 version = str(object.__version__)
Ka-Ping Yee40c49912001-02-27 22:46:01 +0000447 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
448 version = strip(version[11:-1])
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000449 info.append('version %s' % self.escape(version))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000450 if hasattr(object, '__date__'):
451 info.append(self.escape(str(object.__date__)))
452 if info:
453 head = head + ' (%s)' % join(info, ', ')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000454 result = self.heading(
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000455 head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)
456
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000457 modules = inspect.getmembers(object, inspect.ismodule)
458
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000459 classes, cdict = [], {}
460 for key, value in inspect.getmembers(object, inspect.isclass):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000461 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000462 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000463 cdict[key] = cdict[value] = '#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000464 for key, value in classes:
465 for base in value.__bases__:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000466 key, modname = base.__name__, base.__module__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000467 module = sys.modules.get(modname)
468 if modname != name and module and hasattr(module, key):
469 if getattr(module, key) is base:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000470 if not cdict.has_key(key):
471 cdict[key] = cdict[base] = modname + '.html#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000472 funcs, fdict = [], {}
473 for key, value in inspect.getmembers(object, inspect.isroutine):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000474 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000475 funcs.append((key, value))
476 fdict[key] = '#-' + key
477 if inspect.isfunction(value): fdict[value] = fdict[key]
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000478 constants = []
479 for key, value in inspect.getmembers(object, isconstant):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000480 constants.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000481
482 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
483 doc = doc and '<tt>%s</tt>' % doc
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000484 result = result + '<p>%s</p>\n' % self.small(doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000485
486 if hasattr(object, '__path__'):
487 modpkgs = []
488 modnames = []
489 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000490 path = os.path.join(object.__path__[0], file)
491 modname = inspect.getmodulename(file)
492 if modname and modname not in modnames:
493 modpkgs.append((modname, name, 0, 0))
494 modnames.append(modname)
495 elif ispackage(path):
496 modpkgs.append((file, name, 1, 0))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000497 modpkgs.sort()
498 contents = self.multicolumn(modpkgs, self.modpkglink)
499 result = result + self.bigsection(
500 'Package Contents', '#ffffff', '#aa55cc', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000501 elif modules:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000502 contents = self.multicolumn(
503 modules, lambda (key, value), s=self: s.modulelink(value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000504 result = result + self.bigsection(
505 'Modules', '#fffff', '#aa55cc', contents)
506
507 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000508 classlist = map(lambda (key, value): value, classes)
509 contents = [self.formattree(
510 inspect.getclasstree(classlist, 1), name, cdict)]
511 for key, value in classes:
512 contents.append(self.document(value, key, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000513 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000514 'Classes', '#ffffff', '#ee77aa', join(contents))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000515 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000516 contents = []
517 for key, value in funcs:
518 contents.append(self.document(value, key, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000519 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000520 'Functions', '#ffffff', '#eeaa77', join(contents))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000521 if constants:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000522 contents = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000523 for key, value in constants:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000524 contents.append(self.document(value, key))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000525 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000526 'Constants', '#ffffff', '#55aa55', join(contents, '<br>'))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000527 if hasattr(object, '__author__'):
528 contents = self.markup(str(object.__author__), self.preformat)
529 result = result + self.bigsection(
530 'Author', '#ffffff', '#7799ee', contents)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000531 if hasattr(object, '__credits__'):
532 contents = self.markup(str(object.__credits__), self.preformat)
533 result = result + self.bigsection(
534 'Credits', '#ffffff', '#7799ee', contents)
535
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000536 return result
537
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000538 def docclass(self, object, name=None, funcs={}, classes={}):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000539 """Produce HTML documentation for a class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000540 realname = object.__name__
541 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000542 bases = object.__bases__
543 contents = ''
544
545 methods, mdict = [], {}
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000546 for key, value in allmethods(object).items():
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000547 methods.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000548 mdict[key] = mdict[value] = '#' + name + '-' + key
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000549 methods.sort()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000550 for key, value in methods:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000551 contents = contents + self.document(
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000552 value, key, funcs, classes, mdict, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000553
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000554 if name == realname:
555 title = '<a name="%s">class <strong>%s</strong></a>' % (
556 name, realname)
557 else:
558 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
559 name, name, realname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000560 if bases:
561 parents = []
562 for base in bases:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000563 parents.append(
564 self.classlink(base, object.__module__, classes))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000565 title = title + '(%s)' % join(parents, ', ')
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000566 doc = self.markup(
567 getdoc(object), self.preformat, funcs, classes, mdict)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000568 doc = self.small(doc and '<tt>%s<br>&nbsp;</tt>' % doc or
569 self.small('&nbsp;'))
570 return self.section(title, '#000000', '#ffc8d8', contents, 5, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000571
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000572 def formatvalue(self, object):
573 """Format an argument default value as text."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000574 return self.small(self.grey('=' + self.repr(object)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000575
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000576 def docroutine(self, object, name=None,
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000577 funcs={}, classes={}, methods={}, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000578 """Produce HTML documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000579 realname = object.__name__
580 name = name or realname
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000581 anchor = (cl and cl.__name__ or '') + '-' + name
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000582 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000583 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000584 if inspect.ismethod(object):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000585 if cl:
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000586 if object.im_class is not cl:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000587 base = object.im_class
588 url = '#%s-%s' % (base.__name__, name)
589 basename = base.__name__
590 if base.__module__ != cl.__module__:
591 url = base.__module__ + '.html' + url
592 basename = base.__module__ + '.' + basename
593 note = ' from <a href="%s">%s</a>' % (url, basename)
594 skipdocs = 1
595 else:
596 note = (object.im_self and
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000597 ' method of ' + self.repr(object.im_self) or
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000598 ' unbound %s method' % object.im_class.__name__)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000599 object = object.im_func
600
601 if name == realname:
602 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
603 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000604 if (cl and cl.__dict__.has_key(realname) and
605 cl.__dict__[realname] is object):
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000606 reallink = '<a href="#%s">%s</a>' % (
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000607 cl.__name__ + '-' + realname, realname)
608 skipdocs = 1
609 else:
610 reallink = realname
611 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
612 anchor, name, reallink)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000613 if inspect.isbuiltin(object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000614 argspec = '(...)'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000615 else:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000616 args, varargs, varkw, defaults = inspect.getargspec(object)
617 argspec = inspect.formatargspec(
618 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000619 if realname == '<lambda>':
620 decl = '<em>lambda</em>'
621 argspec = argspec[1:-1] # remove parentheses
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000622
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000623 decl = title + argspec + (note and self.small(self.grey(
624 '<font face="helvetica, arial">%s</font>' % note)))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000625
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000626 if skipdocs:
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000627 return '<dl><dt>%s</dl>\n' % decl
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000628 else:
629 doc = self.markup(
630 getdoc(object), self.preformat, funcs, classes, methods)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000631 doc = doc and '<dd>' + self.small('<tt>%s</tt>' % doc)
632 return '<dl><dt>%s%s</dl>\n' % (decl, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000633
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000634 def docother(self, object, name=None):
635 """Produce HTML documentation for a data object."""
636 return '<strong>%s</strong> = %s' % (name, self.repr(object))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000637
638 def index(self, dir, shadowed=None):
639 """Generate an HTML index for a directory of modules."""
640 modpkgs = []
641 if shadowed is None: shadowed = {}
642 seen = {}
643 files = os.listdir(dir)
644
645 def found(name, ispackage,
646 modpkgs=modpkgs, shadowed=shadowed, seen=seen):
647 if not seen.has_key(name):
648 modpkgs.append((name, '', ispackage, shadowed.has_key(name)))
649 seen[name] = 1
650 shadowed[name] = 1
651
652 # Package spam/__init__.py takes precedence over module spam.py.
653 for file in files:
654 path = os.path.join(dir, file)
655 if ispackage(path): found(file, 1)
656 for file in files:
657 path = os.path.join(dir, file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000658 if os.path.isfile(path):
659 modname = inspect.getmodulename(file)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000660 if modname: found(modname, 0)
661
662 modpkgs.sort()
663 contents = self.multicolumn(modpkgs, self.modpkglink)
664 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
665
666# -------------------------------------------- text documentation generator
667
668class TextRepr(Repr):
669 """Class for safely making a text representation of a Python object."""
670 def __init__(self):
671 Repr.__init__(self)
672 self.maxlist = self.maxtuple = self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000673 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000674
675 def repr1(self, x, level):
676 methodname = 'repr_' + join(split(type(x).__name__), '_')
677 if hasattr(self, methodname):
678 return getattr(self, methodname)(x, level)
679 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000680 return cram(stripid(repr(x)), self.maxother)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000681
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000682 def repr_string(self, x, level):
683 test = cram(x, self.maxstring)
684 testrepr = repr(test)
685 if '\\' in test and '\\' not in replace(testrepr, (r'\\', '')):
686 # Backslashes are only literal in the string and are never
687 # needed to make any special characters, so show a raw string.
688 return 'r' + testrepr[0] + test + testrepr[0]
689 return testrepr
690
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000691 def repr_instance(self, x, level):
692 try:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000693 return cram(stripid(repr(x)), self.maxstring)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000694 except:
695 return '<%s instance>' % x.__class__.__name__
696
697class TextDoc(Doc):
698 """Formatter class for text documentation."""
699
700 # ------------------------------------------- text formatting utilities
701
702 _repr_instance = TextRepr()
703 repr = _repr_instance.repr
704
705 def bold(self, text):
706 """Format a string in bold by overstriking."""
707 return join(map(lambda ch: ch + '\b' + ch, text), '')
708
709 def indent(self, text, prefix=' '):
710 """Indent text by prepending a given prefix to each line."""
711 if not text: return ''
712 lines = split(text, '\n')
713 lines = map(lambda line, prefix=prefix: prefix + line, lines)
714 if lines: lines[-1] = rstrip(lines[-1])
715 return join(lines, '\n')
716
717 def section(self, title, contents):
718 """Format a section with a given heading."""
719 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
720
721 # ---------------------------------------------- type-specific routines
722
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000723 def formattree(self, tree, modname, parent=None, prefix=''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000724 """Render in text a class tree as returned by inspect.getclasstree()."""
725 result = ''
726 for entry in tree:
727 if type(entry) is type(()):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000728 c, bases = entry
729 result = result + prefix + classname(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000730 if bases and bases != (parent,):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000731 parents = map(lambda c, m=modname: classname(c, m), bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000732 result = result + '(%s)' % join(parents, ', ')
733 result = result + '\n'
734 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000735 result = result + self.formattree(
736 entry, modname, c, prefix + ' ')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000737 return result
738
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000739 def docmodule(self, object, name=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000740 """Produce text documentation for a given module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000741 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000742 synop, desc = splitdoc(getdoc(object))
743 result = self.section('NAME', name + (synop and ' - ' + synop))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000744
745 try:
746 file = inspect.getabsfile(object)
747 except TypeError:
748 file = '(built-in)'
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000749 result = result + self.section('FILE', file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000750 if desc:
751 result = result + self.section('DESCRIPTION', desc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000752
753 classes = []
754 for key, value in inspect.getmembers(object, inspect.isclass):
755 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000756 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000757 funcs = []
758 for key, value in inspect.getmembers(object, inspect.isroutine):
759 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000760 funcs.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000761 constants = []
762 for key, value in inspect.getmembers(object, isconstant):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000763 constants.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000764
765 if hasattr(object, '__path__'):
766 modpkgs = []
767 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000768 path = os.path.join(object.__path__[0], file)
769 modname = inspect.getmodulename(file)
770 if modname and modname not in modpkgs:
771 modpkgs.append(modname)
772 elif ispackage(path):
773 modpkgs.append(file + ' (package)')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000774 modpkgs.sort()
775 result = result + self.section(
776 'PACKAGE CONTENTS', join(modpkgs, '\n'))
777
778 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000779 classlist = map(lambda (key, value): value, classes)
780 contents = [self.formattree(
781 inspect.getclasstree(classlist, 1), name)]
782 for key, value in classes:
783 contents.append(self.document(value, key))
784 result = result + self.section('CLASSES', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000785
786 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000787 contents = []
788 for key, value in funcs:
789 contents.append(self.document(value, key))
790 result = result + self.section('FUNCTIONS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000791
792 if constants:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000793 contents = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000794 for key, value in constants:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000795 contents.append(self.docother(value, key, 70))
796 result = result + self.section('CONSTANTS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000797
798 if hasattr(object, '__version__'):
799 version = str(object.__version__)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000800 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
801 version = strip(version[11:-1])
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000802 result = result + self.section('VERSION', version)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000803 if hasattr(object, '__date__'):
804 result = result + self.section('DATE', str(object.__date__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000805 if hasattr(object, '__author__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000806 result = result + self.section('AUTHOR', str(object.__author__))
807 if hasattr(object, '__credits__'):
808 result = result + self.section('CREDITS', str(object.__credits__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000809 return result
810
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000811 def docclass(self, object, name=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000812 """Produce text documentation for a given class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000813 realname = object.__name__
814 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000815 bases = object.__bases__
816
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000817 if name == realname:
818 title = 'class ' + self.bold(realname)
819 else:
820 title = self.bold(name) + ' = class ' + realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000821 if bases:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000822 def makename(c, m=object.__module__): return classname(c, m)
823 parents = map(makename, bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000824 title = title + '(%s)' % join(parents, ', ')
825
826 doc = getdoc(object)
827 contents = doc and doc + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000828 methods = allmethods(object).items()
829 methods.sort()
830 for key, value in methods:
831 contents = contents + '\n' + self.document(value, key, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000832
833 if not contents: return title + '\n'
834 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
835
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000836 def formatvalue(self, object):
837 """Format an argument default value as text."""
838 return '=' + self.repr(object)
839
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000840 def docroutine(self, object, name=None, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000841 """Produce text documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000842 realname = object.__name__
843 name = name or realname
844 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000845 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000846 if inspect.ismethod(object):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000847 if cl:
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000848 if object.im_class is not cl:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000849 base = object.im_class
850 basename = base.__name__
851 if base.__module__ != cl.__module__:
852 basename = base.__module__ + '.' + basename
853 note = ' from %s' % basename
854 skipdocs = 1
855 else:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000856 if object.im_self:
857 note = ' method of %s' % self.repr(object.im_self)
858 else:
859 note = ' unbound %s method' % object.im_class.__name__
860 object = object.im_func
861
862 if name == realname:
863 title = self.bold(realname)
864 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000865 if (cl and cl.__dict__.has_key(realname) and
866 cl.__dict__[realname] is object):
867 skipdocs = 1
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000868 title = self.bold(name) + ' = ' + realname
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000869 if inspect.isbuiltin(object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000870 argspec = '(...)'
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000871 else:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000872 args, varargs, varkw, defaults = inspect.getargspec(object)
873 argspec = inspect.formatargspec(
874 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000875 if realname == '<lambda>':
876 title = 'lambda'
877 argspec = argspec[1:-1] # remove parentheses
878 decl = title + argspec + note
879
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000880 if skipdocs:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000881 return decl + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000882 else:
883 doc = getdoc(object) or ''
884 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000885
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000886 def docother(self, object, name=None, maxlen=None):
887 """Produce text documentation for a data object."""
888 repr = self.repr(object)
889 if maxlen:
890 line = name + ' = ' + repr
891 chop = maxlen - len(line)
892 if chop < 0: repr = repr[:chop] + '...'
893 line = self.bold(name) + ' = ' + repr
894 return line
895
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000896# --------------------------------------------------------- user interfaces
897
898def pager(text):
899 """The first time this is called, determine what kind of pager to use."""
900 global pager
901 pager = getpager()
902 pager(text)
903
904def getpager():
905 """Decide what method to use for paging through text."""
906 if type(sys.stdout) is not types.FileType:
907 return plainpager
908 if not sys.stdin.isatty() or not sys.stdout.isatty():
909 return plainpager
910 if os.environ.has_key('PAGER'):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000911 if sys.platform == 'win32': # pipes completely broken in Windows
912 return lambda a: tempfilepager(a, os.environ['PAGER'])
913 else:
914 return lambda a: pipepager(a, os.environ['PAGER'])
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +0000915 if sys.platform == 'win32':
916 return lambda a: tempfilepager(a, 'more <')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000917 if hasattr(os, 'system') and os.system('less 2>/dev/null') == 0:
918 return lambda a: pipepager(a, 'less')
919
920 import tempfile
921 filename = tempfile.mktemp()
922 open(filename, 'w').close()
923 try:
924 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
925 return lambda text: pipepager(text, 'more')
926 else:
927 return ttypager
928 finally:
929 os.unlink(filename)
930
931def pipepager(text, cmd):
932 """Page through text by feeding it to another program."""
933 pipe = os.popen(cmd, 'w')
934 try:
935 pipe.write(text)
936 pipe.close()
937 except IOError:
938 # Ignore broken pipes caused by quitting the pager program.
939 pass
940
941def tempfilepager(text, cmd):
942 """Page through text by invoking a program on a temporary file."""
943 import tempfile
944 filename = tempfile.mktemp()
945 file = open(filename, 'w')
946 file.write(text)
947 file.close()
948 try:
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +0000949 os.system(cmd + ' ' + filename)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000950 finally:
951 os.unlink(filename)
952
953def plain(text):
954 """Remove boldface formatting from text."""
955 return re.sub('.\b', '', text)
956
957def ttypager(text):
958 """Page through text on a text terminal."""
959 lines = split(plain(text), '\n')
960 try:
961 import tty
962 fd = sys.stdin.fileno()
963 old = tty.tcgetattr(fd)
964 tty.setcbreak(fd)
965 getchar = lambda: sys.stdin.read(1)
Ka-Ping Yee457aab22001-02-27 23:36:29 +0000966 except (ImportError, AttributeError):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000967 tty = None
968 getchar = lambda: sys.stdin.readline()[:-1][:1]
969
970 try:
971 r = inc = os.environ.get('LINES', 25) - 1
972 sys.stdout.write(join(lines[:inc], '\n') + '\n')
973 while lines[r:]:
974 sys.stdout.write('-- more --')
975 sys.stdout.flush()
976 c = getchar()
977
978 if c in ['q', 'Q']:
979 sys.stdout.write('\r \r')
980 break
981 elif c in ['\r', '\n']:
982 sys.stdout.write('\r \r' + lines[r] + '\n')
983 r = r + 1
984 continue
985 if c in ['b', 'B', '\x1b']:
986 r = r - inc - inc
987 if r < 0: r = 0
988 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
989 r = r + inc
990
991 finally:
992 if tty:
993 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
994
995def plainpager(text):
996 """Simply print unformatted text. This is the ultimate fallback."""
997 sys.stdout.write(plain(text))
998
999def describe(thing):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001000 """Produce a short description of the given thing."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001001 if inspect.ismodule(thing):
1002 if thing.__name__ in sys.builtin_module_names:
1003 return 'built-in module ' + thing.__name__
1004 if hasattr(thing, '__path__'):
1005 return 'package ' + thing.__name__
1006 else:
1007 return 'module ' + thing.__name__
1008 if inspect.isbuiltin(thing):
1009 return 'built-in function ' + thing.__name__
1010 if inspect.isclass(thing):
1011 return 'class ' + thing.__name__
1012 if inspect.isfunction(thing):
1013 return 'function ' + thing.__name__
1014 if inspect.ismethod(thing):
1015 return 'method ' + thing.__name__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001016 if type(thing) is types.InstanceType:
1017 return 'instance of ' + thing.__class__.__name__
1018 return type(thing).__name__
1019
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001020def freshimport(name, cache={}):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001021 """Import a module, reloading it if the source file has changed."""
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001022 topmodule = __import__(name)
1023 module = None
1024 for component in split(name, '.'):
1025 if module == None:
1026 module = topmodule
1027 path = split(name, '.')[0]
1028 else:
1029 module = getattr(module, component)
1030 path = path + '.' + component
1031 if hasattr(module, '__file__'):
1032 file = module.__file__
1033 if os.path.exists(file):
1034 info = (file, os.path.getmtime(file), os.path.getsize(file))
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00001035 if cache.get(path) == info:
1036 continue
1037 module = reload(module)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001038 file = module.__file__
1039 if os.path.exists(file):
1040 info = (file, os.path.getmtime(file), os.path.getsize(file))
1041 cache[path] = info
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001042 return module
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001043
1044def locate(path):
1045 """Locate an object by name (or dotted path), importing as necessary."""
1046 if not path: # special case: imp.find_module('') strangely succeeds
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001047 return None
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001048 if type(path) is not types.StringType:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001049 return path
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001050 parts = split(path, '.')
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001051 n = len(parts)
1052 while n > 0:
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001053 path = join(parts[:n], '.')
1054 try:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001055 module = freshimport(path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001056 except:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001057 # Did the error occur before or after the module was found?
1058 (exc, value, tb) = info = sys.exc_info()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001059 if sys.modules.has_key(path):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001060 # An error occured while executing the imported module.
1061 raise ErrorDuringImport(sys.modules[path].__file__, info)
1062 elif exc is SyntaxError:
1063 # A SyntaxError occurred before we could execute the module.
1064 raise ErrorDuringImport(value.filename, info)
1065 elif exc is ImportError and \
1066 split(lower(str(value)))[:2] == ['no', 'module']:
1067 # The module was not found.
1068 n = n - 1
1069 continue
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001070 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001071 # Some other error occurred before executing the module.
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001072 raise ErrorDuringImport(filename, sys.exc_info())
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001073 try:
1074 x = module
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001075 for p in parts[n:]:
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001076 x = getattr(x, p)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001077 return x
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001078 except AttributeError:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001079 n = n - 1
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001080 continue
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001081 if hasattr(__builtins__, path):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001082 return getattr(__builtins__, path)
1083 return None
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001084
1085# --------------------------------------- interactive interpreter interface
1086
1087text = TextDoc()
1088html = HTMLDoc()
1089
1090def doc(thing):
1091 """Display documentation on an object (for interactive use)."""
1092 if type(thing) is type(""):
1093 try:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001094 object = locate(thing)
1095 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001096 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001097 return
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001098 if object:
1099 thing = object
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001100 else:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001101 print 'no Python documentation found for %s' % repr(thing)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001102 return
1103
1104 desc = describe(thing)
1105 module = inspect.getmodule(thing)
1106 if module and module is not thing:
1107 desc = desc + ' in module ' + module.__name__
1108 pager('Help on %s:\n\n' % desc + text.document(thing))
1109
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001110def writedoc(key):
1111 """Write HTML documentation to a file in the current directory."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001112 try:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001113 object = locate(key)
1114 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001115 print value
1116 else:
1117 if object:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001118 page = html.page(describe(object),
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001119 html.document(object, object.__name__))
1120 file = open(key + '.html', 'w')
1121 file.write(page)
1122 file.close()
1123 print 'wrote', key + '.html'
1124 else:
1125 print 'no Python documentation found for %s' % repr(key)
1126
1127def writedocs(dir, pkgpath='', done={}):
1128 """Write out HTML documentation for all modules in a directory tree."""
1129 for file in os.listdir(dir):
1130 path = os.path.join(dir, file)
1131 if ispackage(path):
1132 writedocs(path, pkgpath + file + '.')
1133 elif os.path.isfile(path):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001134 modname = inspect.getmodulename(path)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001135 if modname:
1136 modname = pkgpath + modname
1137 if not done.has_key(modname):
1138 done[modname] = 1
1139 writedoc(modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001140
1141class Helper:
1142 def __repr__(self):
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00001143 return '''Welcome to Python %s!
1144
1145To get help on a Python object, call help(object).
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001146To get help on a module or package, either import it before calling
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00001147help(module) or call help('modulename').''' % sys.version[:3]
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001148
1149 def __call__(self, *args):
1150 if args:
1151 doc(args[0])
1152 else:
1153 print repr(self)
1154
1155help = Helper()
1156
1157def man(key):
1158 """Display documentation on an object in a form similar to man(1)."""
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001159 object = locate(key)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001160 if object:
1161 title = 'Python Library Documentation: ' + describe(object)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001162 lastdot = rfind(key, '.')
1163 if lastdot > 0: title = title + ' in ' + key[:lastdot]
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001164 pager('\n' + title + '\n\n' + text.document(object, key))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001165 found = 1
1166 else:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001167 print 'no Python documentation found for %s' % repr(key)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001168
1169class Scanner:
1170 """A generic tree iterator."""
1171 def __init__(self, roots, children, recurse):
1172 self.roots = roots[:]
1173 self.state = []
1174 self.children = children
1175 self.recurse = recurse
1176
1177 def next(self):
1178 if not self.state:
1179 if not self.roots:
1180 return None
1181 root = self.roots.pop(0)
1182 self.state = [(root, self.children(root))]
1183 node, children = self.state[-1]
1184 if not children:
1185 self.state.pop()
1186 return self.next()
1187 child = children.pop(0)
1188 if self.recurse(child):
1189 self.state.append((child, self.children(child)))
1190 return child
1191
1192class ModuleScanner(Scanner):
1193 """An interruptible scanner that searches module synopses."""
1194 def __init__(self):
1195 roots = map(lambda dir: (dir, ''), pathdirs())
1196 Scanner.__init__(self, roots, self.submodules, self.ispackage)
1197
1198 def submodules(self, (dir, package)):
1199 children = []
1200 for file in os.listdir(dir):
1201 path = os.path.join(dir, file)
Tim Peters30edd232001-03-16 08:29:48 +00001202 if ispackage(path):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001203 children.append((path, package + (package and '.') + file))
1204 else:
1205 children.append((path, package))
1206 children.sort()
1207 return children
1208
1209 def ispackage(self, (dir, package)):
1210 return ispackage(dir)
1211
1212 def run(self, key, callback, completer=None):
1213 self.quit = 0
1214 seen = {}
1215
1216 for modname in sys.builtin_module_names:
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001217 if modname != '__main__':
1218 seen[modname] = 1
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001219 desc = split(freshimport(modname).__doc__ or '', '\n')[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001220 if find(lower(modname + ' - ' + desc), lower(key)) >= 0:
1221 callback(None, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001222
1223 while not self.quit:
1224 node = self.next()
1225 if not node: break
1226 path, package = node
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001227 modname = inspect.getmodulename(path)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001228 if os.path.isfile(path) and modname:
1229 modname = package + (package and '.') + modname
1230 if not seen.has_key(modname):
1231 seen[modname] = 1
1232 desc = synopsis(path) or ''
1233 if find(lower(modname + ' - ' + desc), lower(key)) >= 0:
1234 callback(path, modname, desc)
1235 if completer: completer()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001236
1237def apropos(key):
1238 """Print all the one-line module summaries that contain a substring."""
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001239 def callback(path, modname, desc):
1240 if modname[-9:] == '.__init__':
1241 modname = modname[:-9] + ' (package)'
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001242 print modname, desc and '- ' + desc
1243 try: import warnings
1244 except ImportError: pass
1245 else: warnings.filterwarnings('ignore') # ignore problems during import
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001246 ModuleScanner().run(key, callback)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001247
1248# --------------------------------------------------- web browser interface
1249
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001250def serve(port, callback=None):
1251 import BaseHTTPServer, mimetools, select
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001252
1253 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1254 class Message(mimetools.Message):
1255 def __init__(self, fp, seekable=1):
1256 Message = self.__class__
1257 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1258 self.encodingheader = self.getheader('content-transfer-encoding')
1259 self.typeheader = self.getheader('content-type')
1260 self.parsetype()
1261 self.parseplist()
1262
1263 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1264 def send_document(self, title, contents):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001265 try:
1266 self.send_response(200)
1267 self.send_header('Content-Type', 'text/html')
1268 self.end_headers()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001269 self.wfile.write(html.page(title, contents))
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001270 except IOError: pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001271
1272 def do_GET(self):
1273 path = self.path
1274 if path[-5:] == '.html': path = path[:-5]
1275 if path[:1] == '/': path = path[1:]
1276 if path and path != '.':
1277 try:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001278 obj = locate(path)
1279 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001280 self.send_document(path, html.escape(str(value)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001281 return
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001282 if obj:
1283 self.send_document(describe(obj), html.document(obj, path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001284 else:
1285 self.send_document(path,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001286'no Python documentation found for %s' % repr(path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001287 else:
1288 heading = html.heading(
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001289'<big><big><strong>Python: Index of Modules</strong></big></big>',
1290'#ffffff', '#7799ee')
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001291 def bltinlink(name):
1292 return '<a href="%s.html">%s</a>' % (name, name)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001293 names = filter(lambda x: x != '__main__',
1294 sys.builtin_module_names)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001295 contents = html.multicolumn(names, bltinlink)
1296 indices = ['<p>' + html.bigsection(
1297 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1298
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001299 seen = {}
1300 for dir in pathdirs():
1301 indices.append(html.index(dir, seen))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001302 contents = heading + join(indices) + '''<p align=right>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001303<small><small><font color="#909090" face="helvetica, arial"><strong>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001304pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font></small></small>'''
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001305 self.send_document('Index of Modules', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001306
1307 def log_message(self, *args): pass
1308
1309 class DocServer(BaseHTTPServer.HTTPServer):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001310 def __init__(self, port, callback):
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001311 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001312 self.address = ('', port)
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001313 self.url = 'http://%s:%d/' % (host, port)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001314 self.callback = callback
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001315 self.base.__init__(self, self.address, self.handler)
1316
1317 def serve_until_quit(self):
1318 import select
1319 self.quit = 0
1320 while not self.quit:
1321 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1322 if rd: self.handle_request()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001323
1324 def server_activate(self):
1325 self.base.server_activate(self)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001326 if self.callback: self.callback(self)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001327
1328 DocServer.base = BaseHTTPServer.HTTPServer
1329 DocServer.handler = DocHandler
1330 DocHandler.MessageClass = Message
1331 try:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001332 DocServer(port, callback).serve_until_quit()
1333 except (KeyboardInterrupt, select.error):
1334 pass
1335 print 'server stopped'
1336
1337# ----------------------------------------------------- graphical interface
1338
1339def gui():
1340 """Graphical interface (starts web server and pops up a control window)."""
1341 class GUI:
1342 def __init__(self, window, port=7464):
1343 self.window = window
1344 self.server = None
1345 self.scanner = None
1346
1347 import Tkinter
1348 self.server_frm = Tkinter.Frame(window)
1349 self.title_lbl = Tkinter.Label(self.server_frm,
1350 text='Starting server...\n ')
1351 self.open_btn = Tkinter.Button(self.server_frm,
1352 text='open browser', command=self.open, state='disabled')
1353 self.quit_btn = Tkinter.Button(self.server_frm,
1354 text='quit serving', command=self.quit, state='disabled')
1355
1356 self.search_frm = Tkinter.Frame(window)
1357 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
1358 self.search_ent = Tkinter.Entry(self.search_frm)
1359 self.search_ent.bind('<Return>', self.search)
1360 self.stop_btn = Tkinter.Button(self.search_frm,
1361 text='stop', pady=0, command=self.stop, state='disabled')
1362 if sys.platform == 'win32':
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001363 # Trying to hide and show this button crashes under Windows.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001364 self.stop_btn.pack(side='right')
1365
1366 self.window.title('pydoc')
1367 self.window.protocol('WM_DELETE_WINDOW', self.quit)
1368 self.title_lbl.pack(side='top', fill='x')
1369 self.open_btn.pack(side='left', fill='x', expand=1)
1370 self.quit_btn.pack(side='right', fill='x', expand=1)
1371 self.server_frm.pack(side='top', fill='x')
1372
1373 self.search_lbl.pack(side='left')
1374 self.search_ent.pack(side='right', fill='x', expand=1)
1375 self.search_frm.pack(side='top', fill='x')
1376 self.search_ent.focus_set()
1377
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001378 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001379 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001380 self.result_lst.bind('<Button-1>', self.select)
1381 self.result_lst.bind('<Double-Button-1>', self.goto)
1382 self.result_scr = Tkinter.Scrollbar(window,
1383 orient='vertical', command=self.result_lst.yview)
1384 self.result_lst.config(yscrollcommand=self.result_scr.set)
1385
1386 self.result_frm = Tkinter.Frame(window)
1387 self.goto_btn = Tkinter.Button(self.result_frm,
1388 text='go to selected', command=self.goto)
1389 self.hide_btn = Tkinter.Button(self.result_frm,
1390 text='hide results', command=self.hide)
1391 self.goto_btn.pack(side='left', fill='x', expand=1)
1392 self.hide_btn.pack(side='right', fill='x', expand=1)
1393
1394 self.window.update()
1395 self.minwidth = self.window.winfo_width()
1396 self.minheight = self.window.winfo_height()
1397 self.bigminheight = (self.server_frm.winfo_reqheight() +
1398 self.search_frm.winfo_reqheight() +
1399 self.result_lst.winfo_reqheight() +
1400 self.result_frm.winfo_reqheight())
1401 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
1402 self.expanded = 0
1403 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1404 self.window.wm_minsize(self.minwidth, self.minheight)
1405
1406 import threading
1407 threading.Thread(target=serve, args=(port, self.ready)).start()
1408
1409 def ready(self, server):
1410 self.server = server
1411 self.title_lbl.config(
1412 text='Python documentation server at\n' + server.url)
1413 self.open_btn.config(state='normal')
1414 self.quit_btn.config(state='normal')
1415
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001416 def open(self, event=None, url=None):
1417 url = url or self.server.url
1418 try:
1419 import webbrowser
1420 webbrowser.open(url)
1421 except ImportError: # pre-webbrowser.py compatibility
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001422 if sys.platform == 'win32':
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001423 os.system('start "%s"' % url)
1424 elif sys.platform == 'mac':
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001425 try: import ic
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001426 except ImportError: pass
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001427 else: ic.launchurl(url)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001428 else:
1429 rc = os.system('netscape -remote "openURL(%s)" &' % url)
1430 if rc: os.system('netscape "%s" &' % url)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001431
1432 def quit(self, event=None):
1433 if self.server:
1434 self.server.quit = 1
1435 self.window.quit()
1436
1437 def search(self, event=None):
1438 key = self.search_ent.get()
1439 self.stop_btn.pack(side='right')
1440 self.stop_btn.config(state='normal')
1441 self.search_lbl.config(text='Searching for "%s"...' % key)
1442 self.search_ent.forget()
1443 self.search_lbl.pack(side='left')
1444 self.result_lst.delete(0, 'end')
1445 self.goto_btn.config(state='disabled')
1446 self.expand()
1447
1448 import threading
1449 if self.scanner:
1450 self.scanner.quit = 1
1451 self.scanner = ModuleScanner()
1452 threading.Thread(target=self.scanner.run,
1453 args=(key, self.update, self.done)).start()
1454
1455 def update(self, path, modname, desc):
1456 if modname[-9:] == '.__init__':
1457 modname = modname[:-9] + ' (package)'
1458 self.result_lst.insert('end',
1459 modname + ' - ' + (desc or '(no description)'))
1460
1461 def stop(self, event=None):
1462 if self.scanner:
1463 self.scanner.quit = 1
1464 self.scanner = None
1465
1466 def done(self):
1467 self.scanner = None
1468 self.search_lbl.config(text='Search for')
1469 self.search_lbl.pack(side='left')
1470 self.search_ent.pack(side='right', fill='x', expand=1)
1471 if sys.platform != 'win32': self.stop_btn.forget()
1472 self.stop_btn.config(state='disabled')
1473
1474 def select(self, event=None):
1475 self.goto_btn.config(state='normal')
1476
1477 def goto(self, event=None):
1478 selection = self.result_lst.curselection()
1479 if selection:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001480 modname = split(self.result_lst.get(selection[0]))[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001481 self.open(url=self.server.url + modname + '.html')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001482
1483 def collapse(self):
1484 if not self.expanded: return
1485 self.result_frm.forget()
1486 self.result_scr.forget()
1487 self.result_lst.forget()
1488 self.bigwidth = self.window.winfo_width()
1489 self.bigheight = self.window.winfo_height()
1490 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1491 self.window.wm_minsize(self.minwidth, self.minheight)
1492 self.expanded = 0
1493
1494 def expand(self):
1495 if self.expanded: return
1496 self.result_frm.pack(side='bottom', fill='x')
1497 self.result_scr.pack(side='right', fill='y')
1498 self.result_lst.pack(side='top', fill='both', expand=1)
1499 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
1500 self.window.wm_minsize(self.minwidth, self.bigminheight)
1501 self.expanded = 1
1502
1503 def hide(self, event=None):
1504 self.stop()
1505 self.collapse()
1506
1507 import Tkinter
1508 try:
1509 gui = GUI(Tkinter.Tk())
1510 Tkinter.mainloop()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001511 except KeyboardInterrupt:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001512 pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001513
1514# -------------------------------------------------- command-line interface
1515
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001516def ispath(x):
1517 return type(x) is types.StringType and find(x, os.sep) >= 0
1518
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001519def cli():
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001520 """Command-line interface (looks at sys.argv to decide what to do)."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001521 import getopt
1522 class BadUsage: pass
1523
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001524 # Scripts don't get the current directory in their path by default.
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00001525 scriptdir = os.path.dirname(sys.argv[0])
1526 if scriptdir in sys.path:
1527 sys.path.remove(scriptdir)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001528 sys.path.insert(0, '.')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001529
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001530 try:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001531 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001532 writing = 0
1533
1534 for opt, val in opts:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001535 if opt == '-g':
1536 gui()
1537 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001538 if opt == '-k':
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001539 apropos(val)
1540 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001541 if opt == '-p':
1542 try:
1543 port = int(val)
1544 except ValueError:
1545 raise BadUsage
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001546 def ready(server):
1547 print 'server ready at %s' % server.url
1548 serve(port, ready)
1549 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001550 if opt == '-w':
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001551 writing = 1
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001552
1553 if not args: raise BadUsage
1554 for arg in args:
1555 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001556 if ispath(arg) and os.path.isfile(arg):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001557 arg = importfile(arg)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001558 if writing:
1559 if ispath(arg) and os.path.isdir(arg):
1560 writedocs(arg)
1561 else:
1562 writedoc(arg)
1563 else:
1564 man(arg)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001565 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001566 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001567
1568 except (getopt.error, BadUsage):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001569 cmd = sys.argv[0]
1570 print """pydoc - the Python documentation tool
1571
1572%s <name> ...
1573 Show text documentation on something. <name> may be the name of a
1574 function, module, or package, or a dotted reference to a class or
1575 function within a module or module in a package. If <name> contains
1576 a '%s', it is used as the path to a Python source file to document.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001577
1578%s -k <keyword>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001579 Search for a keyword in the synopsis lines of all available modules.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001580
1581%s -p <port>
1582 Start an HTTP server on the given port on the local machine.
1583
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001584%s -g
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001585 Pop up a graphical interface for finding and serving documentation.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001586
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001587%s -w <name> ...
1588 Write out the HTML documentation for a module to a file in the current
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001589 directory. If <name> contains a '%s', it is treated as a filename; if
1590 it names a directory, documentation is written for all the contents.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001591""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001592
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001593if __name__ == '__main__': cli()