blob: c8e1fbf2c91843d8ceb828d694d180a1ec36404a [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
40# some 2.0 features (like string methods) are conspicuously avoided.
41
Ka-Ping Yeedd175342001-02-27 14:43:46 +000042import sys, imp, os, stat, re, types, inspect
43from repr import Repr
Ka-Ping Yee3bda8792001-03-23 13:17:50 +000044from string import expandtabs, find, join, lower, split, strip, rfind, rstrip
Ka-Ping Yeedd175342001-02-27 14:43:46 +000045
46# --------------------------------------------------------- common routines
47
48def synopsis(filename, cache={}):
49 """Get the one-line summary out of a module file."""
50 mtime = os.stat(filename)[stat.ST_MTIME]
51 lastupdate, result = cache.get(filename, (0, None))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +000052 # XXX what if ext is 'rb' type in imp_getsuffixes?
Ka-Ping Yeedd175342001-02-27 14:43:46 +000053 if lastupdate < mtime:
54 file = open(filename)
55 line = file.readline()
56 while line[:1] == '#' or strip(line) == '':
57 line = file.readline()
58 if not line: break
59 if line[-2:] == '\\\n':
60 line = line[:-2] + file.readline()
61 line = strip(line)
62 if line[:3] == '"""':
63 line = line[3:]
64 while strip(line) == '':
65 line = file.readline()
66 if not line: break
Ka-Ping Yee239432a2001-03-02 02:45:08 +000067 result = strip(split(line, '"""')[0])
Ka-Ping Yeedd175342001-02-27 14:43:46 +000068 else: result = None
69 file.close()
70 cache[filename] = (mtime, result)
71 return result
72
Ka-Ping Yeedd175342001-02-27 14:43:46 +000073def pathdirs():
74 """Convert sys.path into a list of absolute, existing, unique paths."""
75 dirs = []
Ka-Ping Yee1d384632001-03-01 00:24:32 +000076 normdirs = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +000077 for dir in sys.path:
78 dir = os.path.abspath(dir or '.')
Ka-Ping Yee1d384632001-03-01 00:24:32 +000079 normdir = os.path.normcase(dir)
80 if normdir not in normdirs and os.path.isdir(dir):
Ka-Ping Yeedd175342001-02-27 14:43:46 +000081 dirs.append(dir)
Ka-Ping Yee1d384632001-03-01 00:24:32 +000082 normdirs.append(normdir)
Ka-Ping Yeedd175342001-02-27 14:43:46 +000083 return dirs
84
85def getdoc(object):
86 """Get the doc string or comments for an object."""
Ka-Ping Yee3bda8792001-03-23 13:17:50 +000087 result = inspect.getdoc(object) or inspect.getcomments(object)
Ka-Ping Yee239432a2001-03-02 02:45:08 +000088 return result and re.sub('^ *\n', '', rstrip(result)) or ''
Ka-Ping Yeedd175342001-02-27 14:43:46 +000089
90def classname(object, modname):
91 """Get a class name and qualify it with a module name if necessary."""
92 name = object.__name__
93 if object.__module__ != modname:
94 name = object.__module__ + '.' + name
95 return name
96
97def isconstant(object):
98 """Check if an object is of a type that probably means it's a constant."""
99 return type(object) in [
100 types.FloatType, types.IntType, types.ListType, types.LongType,
101 types.StringType, types.TupleType, types.TypeType,
102 hasattr(types, 'UnicodeType') and types.UnicodeType or 0]
103
104def replace(text, *pairs):
105 """Do a series of global replacements on a string."""
106 for old, new in pairs:
107 text = join(split(text, old), new)
108 return text
109
110def cram(text, maxlen):
111 """Omit part of a string if needed to make it fit in a maximum length."""
112 if len(text) > maxlen:
113 pre = max(0, (maxlen-3)/2)
114 post = max(0, maxlen-3-pre)
115 return text[:pre] + '...' + text[len(text)-post:]
116 return text
117
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000118def stripid(text):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000119 """Remove the hexadecimal id from a Python object representation."""
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000120 # The behaviour of %p is implementation-dependent, so we need an example.
121 for pattern in [' at 0x[0-9a-f]{6,}>$', ' at [0-9A-F]{8,}>$']:
122 if re.search(pattern, repr(Exception)):
123 return re.sub(pattern, '>', text)
124 return text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000125
126def modulename(path):
127 """Return the Python module name for a given path, or None."""
128 filename = os.path.basename(path)
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000129 suffixes = map(lambda (suffix, mode, kind): (len(suffix), suffix),
130 imp.get_suffixes())
131 suffixes.sort()
132 suffixes.reverse() # try longest suffixes first, in case they overlap
133 for length, suffix in suffixes:
134 if len(filename) > length and filename[-length:] == suffix:
135 return filename[:-length]
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000136
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000137def allmethods(cl):
138 methods = {}
139 for key, value in inspect.getmembers(cl, inspect.ismethod):
140 methods[key] = 1
141 for base in cl.__bases__:
142 methods.update(allmethods(base)) # all your base are belong to us
143 for key in methods.keys():
144 methods[key] = getattr(cl, key)
145 return methods
146
147class ErrorDuringImport(Exception):
148 """Errors that occurred while trying to import something to document it."""
149 def __init__(self, filename, (exc, value, tb)):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000150 self.filename = filename
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000151 self.exc = exc
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000152 self.value = value
153 self.tb = tb
154
155 def __str__(self):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000156 exc = self.exc
157 if type(exc) is types.ClassType:
158 exc = exc.__name__
159 return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000160
161def importfile(path):
162 """Import a Python source file or compiled file given its path."""
163 magic = imp.get_magic()
164 file = open(path, 'r')
165 if file.read(len(magic)) == magic:
166 kind = imp.PY_COMPILED
167 else:
168 kind = imp.PY_SOURCE
169 file.close()
170 filename = os.path.basename(path)
171 name, ext = os.path.splitext(filename)
172 file = open(path, 'r')
173 try:
174 module = imp.load_module(name, file, path, (ext, 'r', kind))
175 except:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000176 raise ErrorDuringImport(path, sys.exc_info())
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000177 file.close()
178 return module
179
180def ispackage(path):
181 """Guess whether a path refers to a package directory."""
182 if os.path.isdir(path):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000183 for ext in ['.py', '.pyc', '.pyo']:
184 if os.path.isfile(os.path.join(path, '__init__' + ext)):
185 return 1
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000186
187# ---------------------------------------------------- formatter base class
188
189class Doc:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000190 def document(self, object, name=None, *args):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000191 """Generate documentation for an object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000192 args = (object, name) + args
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000193 if inspect.ismodule(object): return apply(self.docmodule, args)
194 if inspect.isclass(object): return apply(self.docclass, args)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000195 if inspect.isroutine(object): return apply(self.docroutine, args)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000196 return apply(self.docother, args)
197
198 def fail(self, object, name=None, *args):
199 """Raise an exception for unimplemented types."""
200 message = "don't know how to document object%s of type %s" % (
201 name and ' ' + repr(name), type(object).__name__)
202 raise TypeError, message
203
204 docmodule = docclass = docroutine = docother = fail
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000205
206# -------------------------------------------- HTML documentation generator
207
208class HTMLRepr(Repr):
209 """Class for safely making an HTML representation of a Python object."""
210 def __init__(self):
211 Repr.__init__(self)
212 self.maxlist = self.maxtuple = self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000213 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000214
215 def escape(self, text):
216 return replace(text, ('&', '&amp;'), ('<', '&lt;'), ('>', '&gt;'))
217
218 def repr(self, object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000219 return Repr.repr(self, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000220
221 def repr1(self, x, level):
222 methodname = 'repr_' + join(split(type(x).__name__), '_')
223 if hasattr(self, methodname):
224 return getattr(self, methodname)(x, level)
225 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000226 return self.escape(cram(stripid(repr(x)), self.maxother))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000227
228 def repr_string(self, x, level):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000229 test = cram(x, self.maxstring)
230 testrepr = repr(test)
231 if '\\' in test and '\\' not in replace(testrepr, (r'\\', '')):
232 # Backslashes are only literal in the string and are never
233 # needed to make any special characters, so show a raw string.
234 return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
235 return re.sub(r'((\\[\\abfnrtv\'"]|\\x..|\\u....)+)',
236 r'<font color="#c040c0">\1</font>',
237 self.escape(testrepr))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000238
239 def repr_instance(self, x, level):
240 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000241 return self.escape(cram(stripid(repr(x)), self.maxstring))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000242 except:
243 return self.escape('<%s instance>' % x.__class__.__name__)
244
245 repr_unicode = repr_string
246
247class HTMLDoc(Doc):
248 """Formatter class for HTML documentation."""
249
250 # ------------------------------------------- HTML formatting utilities
251
252 _repr_instance = HTMLRepr()
253 repr = _repr_instance.repr
254 escape = _repr_instance.escape
255
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000256 def page(self, title, contents):
257 """Format an HTML page."""
258 return '''
259<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000260<html><head><title>Python: %s</title>
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000261<style type="text/css"><!--
262TT { font-family: lucida console, lucida typewriter, courier }
263--></style></head><body bgcolor="#f0f0f8">
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000264%s
265</body></html>''' % (title, contents)
266
267 def heading(self, title, fgcol, bgcol, extras=''):
268 """Format a page heading."""
269 return '''
270<table width="100%%" cellspacing=0 cellpadding=2 border=0>
271<tr bgcolor="%s">
272<td valign=bottom><small>&nbsp;<br></small
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000273><font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000274><td align=right valign=bottom
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000275><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000276 ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
277
278 def section(self, title, fgcol, bgcol, contents, width=20,
279 prelude='', marginalia=None, gap='&nbsp;&nbsp;'):
280 """Format a section with a heading."""
281 if marginalia is None:
282 marginalia = '&nbsp;' * width
283 result = '''
284<p><table width="100%%" cellspacing=0 cellpadding=2 border=0>
285<tr bgcolor="%s">
286<td colspan=3 valign=bottom><small><small>&nbsp;<br></small></small
287><font color="%s" face="helvetica, arial">%s</font></td></tr>
288 ''' % (bgcol, fgcol, title)
289 if prelude:
290 result = result + '''
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000291<tr bgcolor="%s"><td rowspan=2>%s</td>
292<td colspan=2>%s</td></tr>
293<tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
294 else:
295 result = result + '''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000296<tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000297
298 return result + '<td width="100%%">%s</td></tr></table>' % contents
299
300 def bigsection(self, title, *args):
301 """Format a section with a big heading."""
302 title = '<big><strong>%s</strong></big>' % title
303 return apply(self.section, (title,) + args)
304
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000305 def preformat(self, text):
306 """Format literal preformatted text."""
307 text = self.escape(expandtabs(text))
308 return replace(text, ('\n\n', '\n \n'), ('\n\n', '\n \n'),
309 (' ', '&nbsp;'), ('\n', '<br>\n'))
310
311 def multicolumn(self, list, format, cols=4):
312 """Format a list of items into a multi-column list."""
313 result = ''
314 rows = (len(list)+cols-1)/cols
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000315 for col in range(cols):
316 result = result + '<td width="%d%%" valign=top>' % (100/cols)
317 for i in range(rows*col, rows*col+rows):
318 if i < len(list):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000319 result = result + format(list[i]) + '<br>\n'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000320 result = result + '</td>'
321 return '<table width="100%%"><tr>%s</tr></table>' % result
322
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000323 def small(self, text): return '<small>%s</small>' % text
324 def grey(self, text): return '<font color="#909090">%s</font>' % text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000325
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000326 def namelink(self, name, *dicts):
327 """Make a link for an identifier, given name-to-URL mappings."""
328 for dict in dicts:
329 if dict.has_key(name):
330 return '<a href="%s">%s</a>' % (dict[name], name)
331 return name
332
333 def classlink(self, object, modname, *dicts):
334 """Make a link for a class."""
335 name = object.__name__
336 if object.__module__ != modname:
337 name = object.__module__ + '.' + name
338 for dict in dicts:
339 if dict.has_key(object):
340 return '<a href="%s">%s</a>' % (dict[object], name)
341 return name
342
343 def modulelink(self, object):
344 """Make a link for a module."""
345 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
346
347 def modpkglink(self, (name, path, ispackage, shadowed)):
348 """Make a link for a module or package to display in an index."""
349 if shadowed:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000350 return self.grey(name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000351 if path:
352 url = '%s.%s.html' % (path, name)
353 else:
354 url = '%s.html' % name
355 if ispackage:
356 text = '<strong>%s</strong>&nbsp;(package)' % name
357 else:
358 text = name
359 return '<a href="%s">%s</a>' % (url, text)
360
361 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
362 """Mark up some plain text, given a context of symbols to look for.
363 Each context dictionary maps object names to anchor names."""
364 escape = escape or self.escape
365 results = []
366 here = 0
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000367 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
368 r'RFC[- ]?(\d+)|'
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000369 r'PEP[- ]?(\d+)|'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000370 r'(self\.)?(\w+))\b')
371 while 1:
372 match = pattern.search(text, here)
373 if not match: break
374 start, end = match.span()
375 results.append(escape(text[here:start]))
376
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000377 all, scheme, rfc, pep, selfdot, name = match.groups()
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000378 if scheme:
379 results.append('<a href="%s">%s</a>' % (all, escape(all)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000380 elif rfc:
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000381 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
382 results.append('<a href="%s">%s</a>' % (url, escape(all)))
383 elif pep:
384 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000385 results.append('<a href="%s">%s</a>' % (url, escape(all)))
386 elif text[end:end+1] == '(':
387 results.append(self.namelink(name, methods, funcs, classes))
388 elif selfdot:
389 results.append('self.<strong>%s</strong>' % name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000390 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000391 results.append(self.namelink(name, classes))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000392 here = end
393 results.append(escape(text[here:]))
394 return join(results, '')
395
396 # ---------------------------------------------- type-specific routines
397
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000398 def formattree(self, tree, modname, classes={}, parent=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000399 """Produce HTML for a class tree as given by inspect.getclasstree()."""
400 result = ''
401 for entry in tree:
402 if type(entry) is type(()):
403 c, bases = entry
404 result = result + '<dt><font face="helvetica, arial"><small>'
405 result = result + self.classlink(c, modname, classes)
406 if bases and bases != (parent,):
407 parents = []
408 for base in bases:
409 parents.append(self.classlink(base, modname, classes))
410 result = result + '(' + join(parents, ', ') + ')'
411 result = result + '\n</small></font></dt>'
412 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000413 result = result + '<dd>\n%s</dd>\n' % self.formattree(
414 entry, modname, classes, c)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000415 return '<dl>\n%s</dl>\n' % result
416
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000417 def docmodule(self, object, name=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000418 """Produce HTML documentation for a module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000419 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000420 parts = split(name, '.')
421 links = []
422 for i in range(len(parts)-1):
423 links.append(
424 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
425 (join(parts[:i+1], '.'), parts[i]))
426 linkedname = join(links + parts[-1:], '.')
427 head = '<big><big><strong>%s</strong></big></big>' % linkedname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000428 try:
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000429 path = inspect.getabsfile(object)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000430 filelink = '<a href="file:%s">%s</a>' % (path, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000431 except TypeError:
432 filelink = '(built-in)'
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000433 info = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000434 if hasattr(object, '__version__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000435 version = str(object.__version__)
Ka-Ping Yee40c49912001-02-27 22:46:01 +0000436 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
437 version = strip(version[11:-1])
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000438 info.append('version %s' % self.escape(version))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000439 if hasattr(object, '__date__'):
440 info.append(self.escape(str(object.__date__)))
441 if info:
442 head = head + ' (%s)' % join(info, ', ')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000443 result = self.heading(
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000444 head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)
445
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000446 modules = inspect.getmembers(object, inspect.ismodule)
447
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000448 if 0 and hasattr(object, '__all__'): # disabled for now
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000449 visible = lambda key, all=object.__all__: key in all
450 else:
451 visible = lambda key: key[:1] != '_'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000452
453 classes, cdict = [], {}
454 for key, value in inspect.getmembers(object, inspect.isclass):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000455 if visible(key) and (
456 inspect.getmodule(value) or object) is object:
457 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000458 cdict[key] = cdict[value] = '#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000459 for key, value in classes:
460 for base in value.__bases__:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000461 key, modname = base.__name__, base.__module__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000462 module = sys.modules.get(modname)
463 if modname != name and module and hasattr(module, key):
464 if getattr(module, key) is base:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000465 if not cdict.has_key(key):
466 cdict[key] = cdict[base] = modname + '.html#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000467 funcs, fdict = [], {}
468 for key, value in inspect.getmembers(object, inspect.isroutine):
469 if visible(key) and (inspect.isbuiltin(value) or
470 inspect.getmodule(value) is object):
471 funcs.append((key, value))
472 fdict[key] = '#-' + key
473 if inspect.isfunction(value): fdict[value] = fdict[key]
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000474 constants = []
475 for key, value in inspect.getmembers(object, isconstant):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000476 if visible(key):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000477 constants.append((key, value))
478
479 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
480 doc = doc and '<tt>%s</tt>' % doc
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000481 result = result + '<p>%s</p>\n' % self.small(doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000482
483 if hasattr(object, '__path__'):
484 modpkgs = []
485 modnames = []
486 for file in os.listdir(object.__path__[0]):
487 if file[:1] != '_':
488 path = os.path.join(object.__path__[0], file)
489 modname = modulename(file)
490 if modname and modname not in modnames:
491 modpkgs.append((modname, name, 0, 0))
492 modnames.append(modname)
493 elif ispackage(path):
Tim Peters85ba6732001-02-28 08:26:44 +0000494 modpkgs.append((file, name, 1, 0))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000495 modpkgs.sort()
496 contents = self.multicolumn(modpkgs, self.modpkglink)
497 result = result + self.bigsection(
498 'Package Contents', '#ffffff', '#aa55cc', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000499 elif modules:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000500 contents = self.multicolumn(
501 modules, lambda (key, value), s=self: s.modulelink(value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000502 result = result + self.bigsection(
503 'Modules', '#fffff', '#aa55cc', contents)
504
505 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000506 classlist = map(lambda (key, value): value, classes)
507 contents = [self.formattree(
508 inspect.getclasstree(classlist, 1), name, cdict)]
509 for key, value in classes:
510 contents.append(self.document(value, key, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000511 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000512 'Classes', '#ffffff', '#ee77aa', join(contents))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000513 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000514 contents = []
515 for key, value in funcs:
516 contents.append(self.document(value, key, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000517 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000518 'Functions', '#ffffff', '#eeaa77', join(contents))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000519 if constants:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000520 contents = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000521 for key, value in constants:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000522 contents.append(self.document(value, key))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000523 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000524 'Constants', '#ffffff', '#55aa55', join(contents, '<br>'))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000525 if hasattr(object, '__author__'):
526 contents = self.markup(str(object.__author__), self.preformat)
527 result = result + self.bigsection(
528 'Author', '#ffffff', '#7799ee', contents)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000529 if hasattr(object, '__credits__'):
530 contents = self.markup(str(object.__credits__), self.preformat)
531 result = result + self.bigsection(
532 'Credits', '#ffffff', '#7799ee', contents)
533
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000534 return result
535
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000536 def docclass(self, object, name=None, funcs={}, classes={}):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000537 """Produce HTML documentation for a class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000538 realname = object.__name__
539 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000540 bases = object.__bases__
541 contents = ''
542
543 methods, mdict = [], {}
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000544 for key, value in allmethods(object).items():
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000545 methods.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000546 mdict[key] = mdict[value] = '#' + name + '-' + key
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000547 methods.sort()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000548 for key, value in methods:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000549 contents = contents + self.document(
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000550 value, key, funcs, classes, mdict, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000551
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000552 if name == realname:
553 title = '<a name="%s">class <strong>%s</strong></a>' % (
554 name, realname)
555 else:
556 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
557 name, name, realname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000558 if bases:
559 parents = []
560 for base in bases:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000561 parents.append(
562 self.classlink(base, object.__module__, classes))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000563 title = title + '(%s)' % join(parents, ', ')
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000564 doc = self.markup(
565 getdoc(object), self.preformat, funcs, classes, mdict)
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000566 doc = self.small(doc and '<tt>%s<br>&nbsp;</tt>' % doc or '<tt>&nbsp;</tt>')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000567 return self.section(title, '#000000', '#ffc8d8', contents, 10, doc)
568
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000569 def formatvalue(self, object):
570 """Format an argument default value as text."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000571 return self.small(self.grey('=' + self.repr(object)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000572
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000573 def docroutine(self, object, name=None,
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000574 funcs={}, classes={}, methods={}, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000575 """Produce HTML documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000576 realname = object.__name__
577 name = name or realname
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000578 anchor = (cl and cl.__name__ or '') + '-' + name
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000579 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000580 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000581 if inspect.ismethod(object):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000582 if cl:
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000583 if object.im_class is not cl:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000584 base = object.im_class
585 url = '#%s-%s' % (base.__name__, name)
586 basename = base.__name__
587 if base.__module__ != cl.__module__:
588 url = base.__module__ + '.html' + url
589 basename = base.__module__ + '.' + basename
590 note = ' from <a href="%s">%s</a>' % (url, basename)
591 skipdocs = 1
592 else:
593 note = (object.im_self and
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000594 ' method of ' + self.repr(object.im_self) or
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000595 ' unbound %s method' % object.im_class.__name__)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000596 object = object.im_func
597
598 if name == realname:
599 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
600 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000601 if (cl and cl.__dict__.has_key(realname) and
602 cl.__dict__[realname] is object):
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000603 reallink = '<a href="#%s">%s</a>' % (
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000604 cl.__name__ + '-' + realname, realname)
605 skipdocs = 1
606 else:
607 reallink = realname
608 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
609 anchor, name, reallink)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000610 if inspect.isbuiltin(object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000611 argspec = '(...)'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000612 else:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000613 args, varargs, varkw, defaults = inspect.getargspec(object)
614 argspec = inspect.formatargspec(
615 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000616 if realname == '<lambda>':
617 decl = '<em>lambda</em>'
618 argspec = argspec[1:-1] # remove parentheses
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000619
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000620 decl = title + argspec + (note and self.small(self.grey(
621 '<font face="helvetica, arial">%s</font>' % note)))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000622
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000623 if skipdocs:
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000624 return '<dl><dt>%s</dl>\n' % decl
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000625 else:
626 doc = self.markup(
627 getdoc(object), self.preformat, funcs, classes, methods)
628 doc = doc and '<tt>%s</tt>' % doc
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000629 return '<dl><dt>%s<dd>%s</dl>\n' % (decl, self.small(doc))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000630
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000631 def docother(self, object, name=None):
632 """Produce HTML documentation for a data object."""
633 return '<strong>%s</strong> = %s' % (name, self.repr(object))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000634
635 def index(self, dir, shadowed=None):
636 """Generate an HTML index for a directory of modules."""
637 modpkgs = []
638 if shadowed is None: shadowed = {}
639 seen = {}
640 files = os.listdir(dir)
641
642 def found(name, ispackage,
643 modpkgs=modpkgs, shadowed=shadowed, seen=seen):
644 if not seen.has_key(name):
645 modpkgs.append((name, '', ispackage, shadowed.has_key(name)))
646 seen[name] = 1
647 shadowed[name] = 1
648
649 # Package spam/__init__.py takes precedence over module spam.py.
650 for file in files:
651 path = os.path.join(dir, file)
652 if ispackage(path): found(file, 1)
653 for file in files:
654 path = os.path.join(dir, file)
Tim Peters85ba6732001-02-28 08:26:44 +0000655 if file[:1] != '_' and os.path.isfile(path):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000656 modname = modulename(file)
657 if modname: found(modname, 0)
658
659 modpkgs.sort()
660 contents = self.multicolumn(modpkgs, self.modpkglink)
661 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
662
663# -------------------------------------------- text documentation generator
664
665class TextRepr(Repr):
666 """Class for safely making a text representation of a Python object."""
667 def __init__(self):
668 Repr.__init__(self)
669 self.maxlist = self.maxtuple = self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000670 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000671
672 def repr1(self, x, level):
673 methodname = 'repr_' + join(split(type(x).__name__), '_')
674 if hasattr(self, methodname):
675 return getattr(self, methodname)(x, level)
676 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000677 return cram(stripid(repr(x)), self.maxother)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000678
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000679 def repr_string(self, x, level):
680 test = cram(x, self.maxstring)
681 testrepr = repr(test)
682 if '\\' in test and '\\' not in replace(testrepr, (r'\\', '')):
683 # Backslashes are only literal in the string and are never
684 # needed to make any special characters, so show a raw string.
685 return 'r' + testrepr[0] + test + testrepr[0]
686 return testrepr
687
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000688 def repr_instance(self, x, level):
689 try:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000690 return cram(stripid(repr(x)), self.maxstring)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000691 except:
692 return '<%s instance>' % x.__class__.__name__
693
694class TextDoc(Doc):
695 """Formatter class for text documentation."""
696
697 # ------------------------------------------- text formatting utilities
698
699 _repr_instance = TextRepr()
700 repr = _repr_instance.repr
701
702 def bold(self, text):
703 """Format a string in bold by overstriking."""
704 return join(map(lambda ch: ch + '\b' + ch, text), '')
705
706 def indent(self, text, prefix=' '):
707 """Indent text by prepending a given prefix to each line."""
708 if not text: return ''
709 lines = split(text, '\n')
710 lines = map(lambda line, prefix=prefix: prefix + line, lines)
711 if lines: lines[-1] = rstrip(lines[-1])
712 return join(lines, '\n')
713
714 def section(self, title, contents):
715 """Format a section with a given heading."""
716 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
717
718 # ---------------------------------------------- type-specific routines
719
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000720 def formattree(self, tree, modname, parent=None, prefix=''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000721 """Render in text a class tree as returned by inspect.getclasstree()."""
722 result = ''
723 for entry in tree:
724 if type(entry) is type(()):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000725 c, bases = entry
726 result = result + prefix + classname(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000727 if bases and bases != (parent,):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000728 parents = map(lambda c, m=modname: classname(c, m), bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000729 result = result + '(%s)' % join(parents, ', ')
730 result = result + '\n'
731 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000732 result = result + self.formattree(
733 entry, modname, c, prefix + ' ')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000734 return result
735
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000736 def docmodule(self, object, name=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000737 """Produce text documentation for a given module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000738 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000739 namesec = name
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000740 lines = split(strip(getdoc(object)), '\n')
741 if len(lines) == 1:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000742 if lines[0]: namesec = namesec + ' - ' + lines[0]
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000743 lines = []
744 elif len(lines) >= 2 and not rstrip(lines[1]):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000745 if lines[0]: namesec = namesec + ' - ' + lines[0]
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000746 lines = lines[2:]
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000747 result = self.section('NAME', namesec)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000748
749 try:
750 file = inspect.getabsfile(object)
751 except TypeError:
752 file = '(built-in)'
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000753 result = result + self.section('FILE', file)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000754 if lines:
755 result = result + self.section('DESCRIPTION', join(lines, '\n'))
756
757 classes = []
758 for key, value in inspect.getmembers(object, inspect.isclass):
759 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000760 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000761 funcs = []
762 for key, value in inspect.getmembers(object, inspect.isroutine):
763 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000764 funcs.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000765 constants = []
766 for key, value in inspect.getmembers(object, isconstant):
767 if key[:1] != '_':
768 constants.append((key, value))
769
770 if hasattr(object, '__path__'):
771 modpkgs = []
772 for file in os.listdir(object.__path__[0]):
773 if file[:1] != '_':
774 path = os.path.join(object.__path__[0], file)
775 modname = modulename(file)
776 if modname and modname not in modpkgs:
777 modpkgs.append(modname)
778 elif ispackage(path):
779 modpkgs.append(file + ' (package)')
780 modpkgs.sort()
781 result = result + self.section(
782 'PACKAGE CONTENTS', join(modpkgs, '\n'))
783
784 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000785 classlist = map(lambda (key, value): value, classes)
786 contents = [self.formattree(
787 inspect.getclasstree(classlist, 1), name)]
788 for key, value in classes:
789 contents.append(self.document(value, key))
790 result = result + self.section('CLASSES', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000791
792 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000793 contents = []
794 for key, value in funcs:
795 contents.append(self.document(value, key))
796 result = result + self.section('FUNCTIONS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000797
798 if constants:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000799 contents = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000800 for key, value in constants:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000801 contents.append(self.docother(value, key, 70))
802 result = result + self.section('CONSTANTS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000803
804 if hasattr(object, '__version__'):
805 version = str(object.__version__)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000806 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
807 version = strip(version[11:-1])
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000808 result = result + self.section('VERSION', version)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000809 if hasattr(object, '__date__'):
810 result = result + self.section('DATE', str(object.__date__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000811 if hasattr(object, '__author__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000812 result = result + self.section('AUTHOR', str(object.__author__))
813 if hasattr(object, '__credits__'):
814 result = result + self.section('CREDITS', str(object.__credits__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000815 return result
816
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000817 def docclass(self, object, name=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000818 """Produce text documentation for a given class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000819 realname = object.__name__
820 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000821 bases = object.__bases__
822
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000823 if name == realname:
824 title = 'class ' + self.bold(realname)
825 else:
826 title = self.bold(name) + ' = class ' + realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000827 if bases:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000828 def makename(c, m=object.__module__): return classname(c, m)
829 parents = map(makename, bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000830 title = title + '(%s)' % join(parents, ', ')
831
832 doc = getdoc(object)
833 contents = doc and doc + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000834 methods = allmethods(object).items()
835 methods.sort()
836 for key, value in methods:
837 contents = contents + '\n' + self.document(value, key, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000838
839 if not contents: return title + '\n'
840 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
841
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000842 def formatvalue(self, object):
843 """Format an argument default value as text."""
844 return '=' + self.repr(object)
845
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000846 def docroutine(self, object, name=None, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000847 """Produce text documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000848 realname = object.__name__
849 name = name or realname
850 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000851 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000852 if inspect.ismethod(object):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000853 if cl:
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000854 if object.im_class is not cl:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000855 base = object.im_class
856 basename = base.__name__
857 if base.__module__ != cl.__module__:
858 basename = base.__module__ + '.' + basename
859 note = ' from %s' % basename
860 skipdocs = 1
861 else:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000862 if object.im_self:
863 note = ' method of %s' % self.repr(object.im_self)
864 else:
865 note = ' unbound %s method' % object.im_class.__name__
866 object = object.im_func
867
868 if name == realname:
869 title = self.bold(realname)
870 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000871 if (cl and cl.__dict__.has_key(realname) and
872 cl.__dict__[realname] is object):
873 skipdocs = 1
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000874 title = self.bold(name) + ' = ' + realname
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000875 if inspect.isbuiltin(object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000876 argspec = '(...)'
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000877 else:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000878 args, varargs, varkw, defaults = inspect.getargspec(object)
879 argspec = inspect.formatargspec(
880 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000881 if realname == '<lambda>':
882 title = 'lambda'
883 argspec = argspec[1:-1] # remove parentheses
884 decl = title + argspec + note
885
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000886 if skipdocs:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000887 return decl + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000888 else:
889 doc = getdoc(object) or ''
890 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000891
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000892 def docother(self, object, name=None, maxlen=None):
893 """Produce text documentation for a data object."""
894 repr = self.repr(object)
895 if maxlen:
896 line = name + ' = ' + repr
897 chop = maxlen - len(line)
898 if chop < 0: repr = repr[:chop] + '...'
899 line = self.bold(name) + ' = ' + repr
900 return line
901
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000902# --------------------------------------------------------- user interfaces
903
904def pager(text):
905 """The first time this is called, determine what kind of pager to use."""
906 global pager
907 pager = getpager()
908 pager(text)
909
910def getpager():
911 """Decide what method to use for paging through text."""
912 if type(sys.stdout) is not types.FileType:
913 return plainpager
914 if not sys.stdin.isatty() or not sys.stdout.isatty():
915 return plainpager
916 if os.environ.has_key('PAGER'):
917 return lambda a: pipepager(a, os.environ['PAGER'])
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +0000918 if sys.platform == 'win32':
919 return lambda a: tempfilepager(a, 'more <')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000920 if hasattr(os, 'system') and os.system('less 2>/dev/null') == 0:
921 return lambda a: pipepager(a, 'less')
922
923 import tempfile
924 filename = tempfile.mktemp()
925 open(filename, 'w').close()
926 try:
927 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
928 return lambda text: pipepager(text, 'more')
929 else:
930 return ttypager
931 finally:
932 os.unlink(filename)
933
934def pipepager(text, cmd):
935 """Page through text by feeding it to another program."""
936 pipe = os.popen(cmd, 'w')
937 try:
938 pipe.write(text)
939 pipe.close()
940 except IOError:
941 # Ignore broken pipes caused by quitting the pager program.
942 pass
943
944def tempfilepager(text, cmd):
945 """Page through text by invoking a program on a temporary file."""
946 import tempfile
947 filename = tempfile.mktemp()
948 file = open(filename, 'w')
949 file.write(text)
950 file.close()
951 try:
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +0000952 os.system(cmd + ' ' + filename)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000953 finally:
954 os.unlink(filename)
955
956def plain(text):
957 """Remove boldface formatting from text."""
958 return re.sub('.\b', '', text)
959
960def ttypager(text):
961 """Page through text on a text terminal."""
962 lines = split(plain(text), '\n')
963 try:
964 import tty
965 fd = sys.stdin.fileno()
966 old = tty.tcgetattr(fd)
967 tty.setcbreak(fd)
968 getchar = lambda: sys.stdin.read(1)
Ka-Ping Yee457aab22001-02-27 23:36:29 +0000969 except (ImportError, AttributeError):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000970 tty = None
971 getchar = lambda: sys.stdin.readline()[:-1][:1]
972
973 try:
974 r = inc = os.environ.get('LINES', 25) - 1
975 sys.stdout.write(join(lines[:inc], '\n') + '\n')
976 while lines[r:]:
977 sys.stdout.write('-- more --')
978 sys.stdout.flush()
979 c = getchar()
980
981 if c in ['q', 'Q']:
982 sys.stdout.write('\r \r')
983 break
984 elif c in ['\r', '\n']:
985 sys.stdout.write('\r \r' + lines[r] + '\n')
986 r = r + 1
987 continue
988 if c in ['b', 'B', '\x1b']:
989 r = r - inc - inc
990 if r < 0: r = 0
991 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
992 r = r + inc
993
994 finally:
995 if tty:
996 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
997
998def plainpager(text):
999 """Simply print unformatted text. This is the ultimate fallback."""
1000 sys.stdout.write(plain(text))
1001
1002def describe(thing):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001003 """Produce a short description of the given thing."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001004 if inspect.ismodule(thing):
1005 if thing.__name__ in sys.builtin_module_names:
1006 return 'built-in module ' + thing.__name__
1007 if hasattr(thing, '__path__'):
1008 return 'package ' + thing.__name__
1009 else:
1010 return 'module ' + thing.__name__
1011 if inspect.isbuiltin(thing):
1012 return 'built-in function ' + thing.__name__
1013 if inspect.isclass(thing):
1014 return 'class ' + thing.__name__
1015 if inspect.isfunction(thing):
1016 return 'function ' + thing.__name__
1017 if inspect.ismethod(thing):
1018 return 'method ' + thing.__name__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001019 if type(thing) is types.InstanceType:
1020 return 'instance of ' + thing.__class__.__name__
1021 return type(thing).__name__
1022
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001023def freshimport(name, cache={}):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001024 """Import a module, reloading it if the source file has changed."""
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001025 topmodule = __import__(name)
1026 module = None
1027 for component in split(name, '.'):
1028 if module == None:
1029 module = topmodule
1030 path = split(name, '.')[0]
1031 else:
1032 module = getattr(module, component)
1033 path = path + '.' + component
1034 if hasattr(module, '__file__'):
1035 file = module.__file__
1036 if os.path.exists(file):
1037 info = (file, os.path.getmtime(file), os.path.getsize(file))
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00001038 if cache.get(path) == info:
1039 continue
1040 module = reload(module)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001041 file = module.__file__
1042 if os.path.exists(file):
1043 info = (file, os.path.getmtime(file), os.path.getsize(file))
1044 cache[path] = info
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001045 return module
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001046
1047def locate(path):
1048 """Locate an object by name (or dotted path), importing as necessary."""
1049 if not path: # special case: imp.find_module('') strangely succeeds
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001050 return None
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001051 if type(path) is not types.StringType:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001052 return path
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001053 parts = split(path, '.')
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001054 n = len(parts)
1055 while n > 0:
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001056 path = join(parts[:n], '.')
1057 try:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001058 module = freshimport(path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001059 except:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001060 # Did the error occur before or after the module was found?
1061 (exc, value, tb) = info = sys.exc_info()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001062 if sys.modules.has_key(path):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001063 # An error occured while executing the imported module.
1064 raise ErrorDuringImport(sys.modules[path].__file__, info)
1065 elif exc is SyntaxError:
1066 # A SyntaxError occurred before we could execute the module.
1067 raise ErrorDuringImport(value.filename, info)
1068 elif exc is ImportError and \
1069 split(lower(str(value)))[:2] == ['no', 'module']:
1070 # The module was not found.
1071 n = n - 1
1072 continue
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001073 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001074 # Some other error occurred before executing the module.
1075 raise DocImportError(filename, sys.exc_info())
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001076 try:
1077 x = module
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001078 for p in parts[n:]:
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001079 x = getattr(x, p)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001080 return x
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001081 except AttributeError:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001082 n = n - 1
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001083 continue
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001084 if hasattr(__builtins__, path):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001085 return getattr(__builtins__, path)
1086 return None
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001087
1088# --------------------------------------- interactive interpreter interface
1089
1090text = TextDoc()
1091html = HTMLDoc()
1092
1093def doc(thing):
1094 """Display documentation on an object (for interactive use)."""
1095 if type(thing) is type(""):
1096 try:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001097 object = locate(thing)
1098 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001099 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001100 return
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001101 if object:
1102 thing = object
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001103 else:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001104 print 'no Python documentation found for %s' % repr(thing)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001105 return
1106
1107 desc = describe(thing)
1108 module = inspect.getmodule(thing)
1109 if module and module is not thing:
1110 desc = desc + ' in module ' + module.__name__
1111 pager('Help on %s:\n\n' % desc + text.document(thing))
1112
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001113def writedoc(key):
1114 """Write HTML documentation to a file in the current directory."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001115 try:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001116 object = locate(key)
1117 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001118 print value
1119 else:
1120 if object:
1121 page = html.page('Python: ' + describe(object),
1122 html.document(object, object.__name__))
1123 file = open(key + '.html', 'w')
1124 file.write(page)
1125 file.close()
1126 print 'wrote', key + '.html'
1127 else:
1128 print 'no Python documentation found for %s' % repr(key)
1129
1130def writedocs(dir, pkgpath='', done={}):
1131 """Write out HTML documentation for all modules in a directory tree."""
1132 for file in os.listdir(dir):
1133 path = os.path.join(dir, file)
1134 if ispackage(path):
1135 writedocs(path, pkgpath + file + '.')
1136 elif os.path.isfile(path):
1137 modname = modulename(path)
1138 if modname:
1139 modname = pkgpath + modname
1140 if not done.has_key(modname):
1141 done[modname] = 1
1142 writedoc(modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001143
1144class Helper:
1145 def __repr__(self):
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00001146 return '''Welcome to Python %s!
1147
1148To get help on a Python object, call help(object).
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001149To get help on a module or package, either import it before calling
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00001150help(module) or call help('modulename').''' % sys.version[:3]
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001151
1152 def __call__(self, *args):
1153 if args:
1154 doc(args[0])
1155 else:
1156 print repr(self)
1157
1158help = Helper()
1159
1160def man(key):
1161 """Display documentation on an object in a form similar to man(1)."""
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001162 object = locate(key)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001163 if object:
1164 title = 'Python Library Documentation: ' + describe(object)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001165 lastdot = rfind(key, '.')
1166 if lastdot > 0: title = title + ' in ' + key[:lastdot]
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001167 pager('\n' + title + '\n\n' + text.document(object, key))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001168 found = 1
1169 else:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001170 print 'no Python documentation found for %s' % repr(key)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001171
1172class Scanner:
1173 """A generic tree iterator."""
1174 def __init__(self, roots, children, recurse):
1175 self.roots = roots[:]
1176 self.state = []
1177 self.children = children
1178 self.recurse = recurse
1179
1180 def next(self):
1181 if not self.state:
1182 if not self.roots:
1183 return None
1184 root = self.roots.pop(0)
1185 self.state = [(root, self.children(root))]
1186 node, children = self.state[-1]
1187 if not children:
1188 self.state.pop()
1189 return self.next()
1190 child = children.pop(0)
1191 if self.recurse(child):
1192 self.state.append((child, self.children(child)))
1193 return child
1194
1195class ModuleScanner(Scanner):
1196 """An interruptible scanner that searches module synopses."""
1197 def __init__(self):
1198 roots = map(lambda dir: (dir, ''), pathdirs())
1199 Scanner.__init__(self, roots, self.submodules, self.ispackage)
1200
1201 def submodules(self, (dir, package)):
1202 children = []
1203 for file in os.listdir(dir):
1204 path = os.path.join(dir, file)
Tim Peters30edd232001-03-16 08:29:48 +00001205 if ispackage(path):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001206 children.append((path, package + (package and '.') + file))
1207 else:
1208 children.append((path, package))
1209 children.sort()
1210 return children
1211
1212 def ispackage(self, (dir, package)):
1213 return ispackage(dir)
1214
1215 def run(self, key, callback, completer=None):
1216 self.quit = 0
1217 seen = {}
1218
1219 for modname in sys.builtin_module_names:
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001220 if modname != '__main__':
1221 seen[modname] = 1
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001222 desc = split(freshimport(modname).__doc__ or '', '\n')[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001223 if find(lower(modname + ' - ' + desc), lower(key)) >= 0:
1224 callback(None, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001225
1226 while not self.quit:
1227 node = self.next()
1228 if not node: break
1229 path, package = node
1230 modname = modulename(path)
1231 if os.path.isfile(path) and modname:
1232 modname = package + (package and '.') + modname
1233 if not seen.has_key(modname):
1234 seen[modname] = 1
1235 desc = synopsis(path) or ''
1236 if find(lower(modname + ' - ' + desc), lower(key)) >= 0:
1237 callback(path, modname, desc)
1238 if completer: completer()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001239
1240def apropos(key):
1241 """Print all the one-line module summaries that contain a substring."""
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001242 def callback(path, modname, desc):
1243 if modname[-9:] == '.__init__':
1244 modname = modname[:-9] + ' (package)'
1245 print modname, '-', desc or '(no description)'
1246 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':
1425 try:
1426 import ic
1427 ic.launchurl(url)
1428 except ImportError: pass
1429 else:
1430 rc = os.system('netscape -remote "openURL(%s)" &' % url)
1431 if rc: os.system('netscape "%s" &' % url)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001432
1433 def quit(self, event=None):
1434 if self.server:
1435 self.server.quit = 1
1436 self.window.quit()
1437
1438 def search(self, event=None):
1439 key = self.search_ent.get()
1440 self.stop_btn.pack(side='right')
1441 self.stop_btn.config(state='normal')
1442 self.search_lbl.config(text='Searching for "%s"...' % key)
1443 self.search_ent.forget()
1444 self.search_lbl.pack(side='left')
1445 self.result_lst.delete(0, 'end')
1446 self.goto_btn.config(state='disabled')
1447 self.expand()
1448
1449 import threading
1450 if self.scanner:
1451 self.scanner.quit = 1
1452 self.scanner = ModuleScanner()
1453 threading.Thread(target=self.scanner.run,
1454 args=(key, self.update, self.done)).start()
1455
1456 def update(self, path, modname, desc):
1457 if modname[-9:] == '.__init__':
1458 modname = modname[:-9] + ' (package)'
1459 self.result_lst.insert('end',
1460 modname + ' - ' + (desc or '(no description)'))
1461
1462 def stop(self, event=None):
1463 if self.scanner:
1464 self.scanner.quit = 1
1465 self.scanner = None
1466
1467 def done(self):
1468 self.scanner = None
1469 self.search_lbl.config(text='Search for')
1470 self.search_lbl.pack(side='left')
1471 self.search_ent.pack(side='right', fill='x', expand=1)
1472 if sys.platform != 'win32': self.stop_btn.forget()
1473 self.stop_btn.config(state='disabled')
1474
1475 def select(self, event=None):
1476 self.goto_btn.config(state='normal')
1477
1478 def goto(self, event=None):
1479 selection = self.result_lst.curselection()
1480 if selection:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001481 modname = split(self.result_lst.get(selection[0]))[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001482 self.open(url=self.server.url + modname + '.html')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001483
1484 def collapse(self):
1485 if not self.expanded: return
1486 self.result_frm.forget()
1487 self.result_scr.forget()
1488 self.result_lst.forget()
1489 self.bigwidth = self.window.winfo_width()
1490 self.bigheight = self.window.winfo_height()
1491 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1492 self.window.wm_minsize(self.minwidth, self.minheight)
1493 self.expanded = 0
1494
1495 def expand(self):
1496 if self.expanded: return
1497 self.result_frm.pack(side='bottom', fill='x')
1498 self.result_scr.pack(side='right', fill='y')
1499 self.result_lst.pack(side='top', fill='both', expand=1)
1500 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
1501 self.window.wm_minsize(self.minwidth, self.bigminheight)
1502 self.expanded = 1
1503
1504 def hide(self, event=None):
1505 self.stop()
1506 self.collapse()
1507
1508 import Tkinter
1509 try:
1510 gui = GUI(Tkinter.Tk())
1511 Tkinter.mainloop()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001512 except KeyboardInterrupt:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001513 pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001514
1515# -------------------------------------------------- command-line interface
1516
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001517def ispath(x):
1518 return type(x) is types.StringType and find(x, os.sep) >= 0
1519
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001520def cli():
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001521 """Command-line interface (looks at sys.argv to decide what to do)."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001522 import getopt
1523 class BadUsage: pass
1524
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001525 # Scripts don't get the current directory in their path by default.
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00001526 scriptdir = os.path.dirname(sys.argv[0])
1527 if scriptdir in sys.path:
1528 sys.path.remove(scriptdir)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001529 sys.path.insert(0, '.')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001530
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001531 try:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001532 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001533 writing = 0
1534
1535 for opt, val in opts:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001536 if opt == '-g':
1537 gui()
1538 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001539 if opt == '-k':
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001540 apropos(val)
1541 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001542 if opt == '-p':
1543 try:
1544 port = int(val)
1545 except ValueError:
1546 raise BadUsage
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001547 def ready(server):
1548 print 'server ready at %s' % server.url
1549 serve(port, ready)
1550 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001551 if opt == '-w':
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001552 writing = 1
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001553
1554 if not args: raise BadUsage
1555 for arg in args:
1556 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001557 if ispath(arg) and os.path.isfile(arg):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001558 arg = importfile(arg)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001559 if writing:
1560 if ispath(arg) and os.path.isdir(arg):
1561 writedocs(arg)
1562 else:
1563 writedoc(arg)
1564 else:
1565 man(arg)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001566 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001567 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001568
1569 except (getopt.error, BadUsage):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001570 cmd = sys.argv[0]
1571 print """pydoc - the Python documentation tool
1572
1573%s <name> ...
1574 Show text documentation on something. <name> may be the name of a
1575 function, module, or package, or a dotted reference to a class or
1576 function within a module or module in a package. If <name> contains
1577 a '%s', it is used as the path to a Python source file to document.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001578
1579%s -k <keyword>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001580 Search for a keyword in the synopsis lines of all available modules.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001581
1582%s -p <port>
1583 Start an HTTP server on the given port on the local machine.
1584
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001585%s -g
1586 Pop up a graphical interface for serving and finding documentation.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001587
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001588%s -w <name> ...
1589 Write out the HTML documentation for a module to a file in the current
1590 directory. If <name> contains a '%s', it is treated as a filename.
1591""" % (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()