blob: 4a21bc3eefb283008f33e598cb3a648d7a63cba9 [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 Yee9aa0d902001-04-12 10:50:23 +000069 while line[:1] == '#' or not 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."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000128 while pairs:
129 text = join(split(text, pairs[0]), pairs[1])
130 pairs = pairs[2:]
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000131 return text
132
133def cram(text, maxlen):
134 """Omit part of a string if needed to make it fit in a maximum length."""
135 if len(text) > maxlen:
136 pre = max(0, (maxlen-3)/2)
137 post = max(0, maxlen-3-pre)
138 return text[:pre] + '...' + text[len(text)-post:]
139 return text
140
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000141def stripid(text):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000142 """Remove the hexadecimal id from a Python object representation."""
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000143 # The behaviour of %p is implementation-dependent; we check two cases.
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000144 for pattern in [' at 0x[0-9a-f]{6,}>$', ' at [0-9A-F]{8,}>$']:
145 if re.search(pattern, repr(Exception)):
146 return re.sub(pattern, '>', text)
147 return text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000148
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000149def allmethods(cl):
150 methods = {}
151 for key, value in inspect.getmembers(cl, inspect.ismethod):
152 methods[key] = 1
153 for base in cl.__bases__:
154 methods.update(allmethods(base)) # all your base are belong to us
155 for key in methods.keys():
156 methods[key] = getattr(cl, key)
157 return methods
158
159class ErrorDuringImport(Exception):
160 """Errors that occurred while trying to import something to document it."""
161 def __init__(self, filename, (exc, value, tb)):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000162 self.filename = filename
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000163 self.exc = exc
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000164 self.value = value
165 self.tb = tb
166
167 def __str__(self):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000168 exc = self.exc
169 if type(exc) is types.ClassType:
170 exc = exc.__name__
171 return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000172
173def importfile(path):
174 """Import a Python source file or compiled file given its path."""
175 magic = imp.get_magic()
176 file = open(path, 'r')
177 if file.read(len(magic)) == magic:
178 kind = imp.PY_COMPILED
179 else:
180 kind = imp.PY_SOURCE
181 file.close()
182 filename = os.path.basename(path)
183 name, ext = os.path.splitext(filename)
184 file = open(path, 'r')
185 try:
186 module = imp.load_module(name, file, path, (ext, 'r', kind))
187 except:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000188 raise ErrorDuringImport(path, sys.exc_info())
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000189 file.close()
190 return module
191
192def ispackage(path):
193 """Guess whether a path refers to a package directory."""
194 if os.path.isdir(path):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000195 for ext in ['.py', '.pyc', '.pyo']:
196 if os.path.isfile(os.path.join(path, '__init__' + ext)):
197 return 1
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000198
199# ---------------------------------------------------- formatter base class
200
201class Doc:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000202 def document(self, object, name=None, *args):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000203 """Generate documentation for an object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000204 args = (object, name) + args
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000205 if inspect.ismodule(object): return apply(self.docmodule, args)
206 if inspect.isclass(object): return apply(self.docclass, args)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000207 if inspect.isroutine(object): return apply(self.docroutine, args)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000208 return apply(self.docother, args)
209
210 def fail(self, object, name=None, *args):
211 """Raise an exception for unimplemented types."""
212 message = "don't know how to document object%s of type %s" % (
213 name and ' ' + repr(name), type(object).__name__)
214 raise TypeError, message
215
216 docmodule = docclass = docroutine = docother = fail
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000217
218# -------------------------------------------- HTML documentation generator
219
220class HTMLRepr(Repr):
221 """Class for safely making an HTML representation of a Python object."""
222 def __init__(self):
223 Repr.__init__(self)
224 self.maxlist = self.maxtuple = self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000225 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000226
227 def escape(self, text):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000228 return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000229
230 def repr(self, object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000231 return Repr.repr(self, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000232
233 def repr1(self, x, level):
234 methodname = 'repr_' + join(split(type(x).__name__), '_')
235 if hasattr(self, methodname):
236 return getattr(self, methodname)(x, level)
237 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000238 return self.escape(cram(stripid(repr(x)), self.maxother))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000239
240 def repr_string(self, x, level):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000241 test = cram(x, self.maxstring)
242 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000243 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000244 # Backslashes are only literal in the string and are never
245 # needed to make any special characters, so show a raw string.
246 return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000247 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000248 r'<font color="#c040c0">\1</font>',
249 self.escape(testrepr))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000250
251 def repr_instance(self, x, level):
252 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000253 return self.escape(cram(stripid(repr(x)), self.maxstring))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000254 except:
255 return self.escape('<%s instance>' % x.__class__.__name__)
256
257 repr_unicode = repr_string
258
259class HTMLDoc(Doc):
260 """Formatter class for HTML documentation."""
261
262 # ------------------------------------------- HTML formatting utilities
263
264 _repr_instance = HTMLRepr()
265 repr = _repr_instance.repr
266 escape = _repr_instance.escape
267
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000268 def page(self, title, contents):
269 """Format an HTML page."""
270 return '''
271<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000272<html><head><title>Python: %s</title>
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000273<style type="text/css"><!--
274TT { font-family: lucida console, lucida typewriter, courier }
275--></style></head><body bgcolor="#f0f0f8">
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000276%s
277</body></html>''' % (title, contents)
278
279 def heading(self, title, fgcol, bgcol, extras=''):
280 """Format a page heading."""
281 return '''
282<table width="100%%" cellspacing=0 cellpadding=2 border=0>
283<tr bgcolor="%s">
284<td valign=bottom><small>&nbsp;<br></small
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000285><font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000286><td align=right valign=bottom
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000287><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000288 ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
289
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000290 def section(self, title, fgcol, bgcol, contents, width=10,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000291 prelude='', marginalia=None, gap='&nbsp;&nbsp;'):
292 """Format a section with a heading."""
293 if marginalia is None:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000294 marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000295 result = '''
296<p><table width="100%%" cellspacing=0 cellpadding=2 border=0>
297<tr bgcolor="%s">
298<td colspan=3 valign=bottom><small><small>&nbsp;<br></small></small
299><font color="%s" face="helvetica, arial">%s</font></td></tr>
300 ''' % (bgcol, fgcol, title)
301 if prelude:
302 result = result + '''
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000303<tr bgcolor="%s"><td rowspan=2>%s</td>
304<td colspan=2>%s</td></tr>
305<tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
306 else:
307 result = result + '''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000308<tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000309
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000310 return result + '\n<td width="100%%">%s</td></tr></table>' % contents
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000311
312 def bigsection(self, title, *args):
313 """Format a section with a big heading."""
314 title = '<big><strong>%s</strong></big>' % title
315 return apply(self.section, (title,) + args)
316
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000317 def preformat(self, text):
318 """Format literal preformatted text."""
319 text = self.escape(expandtabs(text))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000320 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
321 ' ', '&nbsp;', '\n', '<br>\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000322
323 def multicolumn(self, list, format, cols=4):
324 """Format a list of items into a multi-column list."""
325 result = ''
326 rows = (len(list)+cols-1)/cols
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000327 for col in range(cols):
328 result = result + '<td width="%d%%" valign=top>' % (100/cols)
329 for i in range(rows*col, rows*col+rows):
330 if i < len(list):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000331 result = result + format(list[i]) + '<br>\n'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000332 result = result + '</td>'
333 return '<table width="100%%"><tr>%s</tr></table>' % result
334
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000335 def small(self, text): return '<small>%s</small>' % text
336 def grey(self, text): return '<font color="#909090">%s</font>' % text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000337
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000338 def namelink(self, name, *dicts):
339 """Make a link for an identifier, given name-to-URL mappings."""
340 for dict in dicts:
341 if dict.has_key(name):
342 return '<a href="%s">%s</a>' % (dict[name], name)
343 return name
344
345 def classlink(self, object, modname, *dicts):
346 """Make a link for a class."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000347 name = classname(object, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000348 for dict in dicts:
349 if dict.has_key(object):
350 return '<a href="%s">%s</a>' % (dict[object], name)
351 return name
352
353 def modulelink(self, object):
354 """Make a link for a module."""
355 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
356
357 def modpkglink(self, (name, path, ispackage, shadowed)):
358 """Make a link for a module or package to display in an index."""
359 if shadowed:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000360 return self.grey(name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000361 if path:
362 url = '%s.%s.html' % (path, name)
363 else:
364 url = '%s.html' % name
365 if ispackage:
366 text = '<strong>%s</strong>&nbsp;(package)' % name
367 else:
368 text = name
369 return '<a href="%s">%s</a>' % (url, text)
370
371 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
372 """Mark up some plain text, given a context of symbols to look for.
373 Each context dictionary maps object names to anchor names."""
374 escape = escape or self.escape
375 results = []
376 here = 0
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000377 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
378 r'RFC[- ]?(\d+)|'
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000379 r'PEP[- ]?(\d+)|'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000380 r'(self\.)?(\w+))\b')
381 while 1:
382 match = pattern.search(text, here)
383 if not match: break
384 start, end = match.span()
385 results.append(escape(text[here:start]))
386
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000387 all, scheme, rfc, pep, selfdot, name = match.groups()
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000388 if scheme:
389 results.append('<a href="%s">%s</a>' % (all, escape(all)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000390 elif rfc:
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000391 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
392 results.append('<a href="%s">%s</a>' % (url, escape(all)))
393 elif pep:
394 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000395 results.append('<a href="%s">%s</a>' % (url, escape(all)))
396 elif text[end:end+1] == '(':
397 results.append(self.namelink(name, methods, funcs, classes))
398 elif selfdot:
399 results.append('self.<strong>%s</strong>' % name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000400 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000401 results.append(self.namelink(name, classes))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000402 here = end
403 results.append(escape(text[here:]))
404 return join(results, '')
405
406 # ---------------------------------------------- type-specific routines
407
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000408 def formattree(self, tree, modname, classes={}, parent=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000409 """Produce HTML for a class tree as given by inspect.getclasstree()."""
410 result = ''
411 for entry in tree:
412 if type(entry) is type(()):
413 c, bases = entry
414 result = result + '<dt><font face="helvetica, arial"><small>'
415 result = result + self.classlink(c, modname, classes)
416 if bases and bases != (parent,):
417 parents = []
418 for base in bases:
419 parents.append(self.classlink(base, modname, classes))
420 result = result + '(' + join(parents, ', ') + ')'
421 result = result + '\n</small></font></dt>'
422 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000423 result = result + '<dd>\n%s</dd>\n' % self.formattree(
424 entry, modname, classes, c)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000425 return '<dl>\n%s</dl>\n' % result
426
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000427 def docmodule(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000428 """Produce HTML documentation for a module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000429 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000430 parts = split(name, '.')
431 links = []
432 for i in range(len(parts)-1):
433 links.append(
434 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
435 (join(parts[:i+1], '.'), parts[i]))
436 linkedname = join(links + parts[-1:], '.')
437 head = '<big><big><strong>%s</strong></big></big>' % linkedname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000438 try:
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000439 path = inspect.getabsfile(object)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000440 filelink = '<a href="file:%s">%s</a>' % (path, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000441 except TypeError:
442 filelink = '(built-in)'
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000443 info = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000444 if hasattr(object, '__version__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000445 version = str(object.__version__)
Ka-Ping Yee40c49912001-02-27 22:46:01 +0000446 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
447 version = strip(version[11:-1])
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000448 info.append('version %s' % self.escape(version))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000449 if hasattr(object, '__date__'):
450 info.append(self.escape(str(object.__date__)))
451 if info:
452 head = head + ' (%s)' % join(info, ', ')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000453 result = self.heading(
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000454 head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)
455
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000456 modules = inspect.getmembers(object, inspect.ismodule)
457
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000458 classes, cdict = [], {}
459 for key, value in inspect.getmembers(object, inspect.isclass):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000460 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000461 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000462 cdict[key] = cdict[value] = '#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000463 for key, value in classes:
464 for base in value.__bases__:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000465 key, modname = base.__name__, base.__module__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000466 module = sys.modules.get(modname)
467 if modname != name and module and hasattr(module, key):
468 if getattr(module, key) is base:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000469 if not cdict.has_key(key):
470 cdict[key] = cdict[base] = modname + '.html#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000471 funcs, fdict = [], {}
472 for key, value in inspect.getmembers(object, inspect.isroutine):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000473 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000474 funcs.append((key, value))
475 fdict[key] = '#-' + key
476 if inspect.isfunction(value): fdict[value] = fdict[key]
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000477 constants = []
478 for key, value in inspect.getmembers(object, isconstant):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000479 constants.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000480
481 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
482 doc = doc and '<tt>%s</tt>' % doc
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000483 result = result + '<p>%s</p>\n' % self.small(doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000484
485 if hasattr(object, '__path__'):
486 modpkgs = []
487 modnames = []
488 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000489 path = os.path.join(object.__path__[0], file)
490 modname = inspect.getmodulename(file)
491 if modname and modname not in modnames:
492 modpkgs.append((modname, name, 0, 0))
493 modnames.append(modname)
494 elif ispackage(path):
495 modpkgs.append((file, name, 1, 0))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000496 modpkgs.sort()
497 contents = self.multicolumn(modpkgs, self.modpkglink)
498 result = result + self.bigsection(
499 'Package Contents', '#ffffff', '#aa55cc', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000500 elif modules:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000501 contents = self.multicolumn(
502 modules, lambda (key, value), s=self: s.modulelink(value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000503 result = result + self.bigsection(
504 'Modules', '#fffff', '#aa55cc', contents)
505
506 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000507 classlist = map(lambda (key, value): value, classes)
508 contents = [self.formattree(
509 inspect.getclasstree(classlist, 1), name, cdict)]
510 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000511 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000512 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000513 'Classes', '#ffffff', '#ee77aa', join(contents))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000514 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000515 contents = []
516 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000517 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000518 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000519 'Functions', '#ffffff', '#eeaa77', join(contents))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000520 if constants:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000521 contents = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000522 for key, value in constants:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000523 contents.append(self.document(value, key))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000524 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000525 'Constants', '#ffffff', '#55aa55', join(contents, '<br>'))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000526 if hasattr(object, '__author__'):
527 contents = self.markup(str(object.__author__), self.preformat)
528 result = result + self.bigsection(
529 'Author', '#ffffff', '#7799ee', contents)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000530 if hasattr(object, '__credits__'):
531 contents = self.markup(str(object.__credits__), self.preformat)
532 result = result + self.bigsection(
533 'Credits', '#ffffff', '#7799ee', contents)
534
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000535 return result
536
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000537 def docclass(self, object, name=None, mod=None, funcs={}, classes={}):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000538 """Produce HTML documentation for a class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000539 realname = object.__name__
540 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000541 bases = object.__bases__
542 contents = ''
543
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000544 methods, mdict = allmethods(object).items(), {}
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000545 methods.sort()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000546 for key, value in methods:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000547 mdict[key] = mdict[value] = '#' + name + '-' + key
548 for key, value in methods:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000549 contents = contents + self.document(
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000550 value, key, mod, 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 Yee5a804ed2001-04-10 11:46:02 +0000566 doc = self.small(doc and '<tt>%s<br>&nbsp;</tt>' % doc or
567 self.small('&nbsp;'))
568 return self.section(title, '#000000', '#ffc8d8', contents, 5, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000569
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000570 def formatvalue(self, object):
571 """Format an argument default value as text."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000572 return self.small(self.grey('=' + self.repr(object)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000573
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000574 def docroutine(self, object, name=None, mod=None,
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000575 funcs={}, classes={}, methods={}, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000576 """Produce HTML documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000577 realname = object.__name__
578 name = name or realname
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000579 anchor = (cl and cl.__name__ or '') + '-' + name
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000580 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000581 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000582 if inspect.ismethod(object):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000583 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000584 imclass = object.im_class
585 if imclass is not cl:
586 url = '%s.html#%s-%s' % (
587 imclass.__module__, imclass.__name__, name)
588 note = ' from <a href="%s">%s</a>' % (
589 url, classname(imclass, mod))
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000590 skipdocs = 1
591 else:
592 note = (object.im_self and
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000593 ' method of %s instance' + object.im_self.__class__ or
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000594 ' unbound %s method' % object.im_class.__name__)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000595 object = object.im_func
596
597 if name == realname:
598 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
599 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000600 if (cl and cl.__dict__.has_key(realname) and
601 cl.__dict__[realname] is object):
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000602 reallink = '<a href="#%s">%s</a>' % (
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000603 cl.__name__ + '-' + realname, realname)
604 skipdocs = 1
605 else:
606 reallink = realname
607 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
608 anchor, name, reallink)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000609 if inspect.isbuiltin(object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000610 argspec = '(...)'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000611 else:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000612 args, varargs, varkw, defaults = inspect.getargspec(object)
613 argspec = inspect.formatargspec(
614 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000615 if realname == '<lambda>':
616 decl = '<em>lambda</em>'
617 argspec = argspec[1:-1] # remove parentheses
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000618
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000619 decl = title + argspec + (note and self.small(self.grey(
620 '<font face="helvetica, arial">%s</font>' % note)))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000621
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000622 if skipdocs:
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000623 return '<dl><dt>%s</dl>\n' % decl
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000624 else:
625 doc = self.markup(
626 getdoc(object), self.preformat, funcs, classes, methods)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000627 doc = doc and '<dd>' + self.small('<tt>%s</tt>' % doc)
628 return '<dl><dt>%s%s</dl>\n' % (decl, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000629
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000630 def docother(self, object, name=None, mod=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000631 """Produce HTML documentation for a data object."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000632 lhs = name and '<strong>%s</strong> = ' % name or ''
633 return lhs + 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)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000655 if os.path.isfile(path):
656 modname = inspect.getmodulename(file)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000657 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)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000682 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000683 # 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 Yee9aa0d902001-04-12 10:50:23 +0000736 def docmodule(self, object, name=None, mod=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 Yee5a804ed2001-04-10 11:46:02 +0000739 synop, desc = splitdoc(getdoc(object))
740 result = self.section('NAME', name + (synop and ' - ' + synop))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000741
742 try:
743 file = inspect.getabsfile(object)
744 except TypeError:
745 file = '(built-in)'
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000746 result = result + self.section('FILE', file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000747 if desc:
748 result = result + self.section('DESCRIPTION', desc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000749
750 classes = []
751 for key, value in inspect.getmembers(object, inspect.isclass):
752 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000753 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000754 funcs = []
755 for key, value in inspect.getmembers(object, inspect.isroutine):
756 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000757 funcs.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000758 constants = []
759 for key, value in inspect.getmembers(object, isconstant):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000760 constants.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000761
762 if hasattr(object, '__path__'):
763 modpkgs = []
764 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000765 path = os.path.join(object.__path__[0], file)
766 modname = inspect.getmodulename(file)
767 if modname and modname not in modpkgs:
768 modpkgs.append(modname)
769 elif ispackage(path):
770 modpkgs.append(file + ' (package)')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000771 modpkgs.sort()
772 result = result + self.section(
773 'PACKAGE CONTENTS', join(modpkgs, '\n'))
774
775 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000776 classlist = map(lambda (key, value): value, classes)
777 contents = [self.formattree(
778 inspect.getclasstree(classlist, 1), name)]
779 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000780 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000781 result = result + self.section('CLASSES', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000782
783 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000784 contents = []
785 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000786 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000787 result = result + self.section('FUNCTIONS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000788
789 if constants:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000790 contents = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000791 for key, value in constants:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000792 contents.append(self.docother(value, key, name, 70))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000793 result = result + self.section('CONSTANTS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000794
795 if hasattr(object, '__version__'):
796 version = str(object.__version__)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000797 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
798 version = strip(version[11:-1])
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000799 result = result + self.section('VERSION', version)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000800 if hasattr(object, '__date__'):
801 result = result + self.section('DATE', str(object.__date__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000802 if hasattr(object, '__author__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000803 result = result + self.section('AUTHOR', str(object.__author__))
804 if hasattr(object, '__credits__'):
805 result = result + self.section('CREDITS', str(object.__credits__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000806 return result
807
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000808 def docclass(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000809 """Produce text documentation for a given class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000810 realname = object.__name__
811 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000812 bases = object.__bases__
813
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000814 if name == realname:
815 title = 'class ' + self.bold(realname)
816 else:
817 title = self.bold(name) + ' = class ' + realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000818 if bases:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000819 def makename(c, m=object.__module__): return classname(c, m)
820 parents = map(makename, bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000821 title = title + '(%s)' % join(parents, ', ')
822
823 doc = getdoc(object)
824 contents = doc and doc + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000825 methods = allmethods(object).items()
826 methods.sort()
827 for key, value in methods:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000828 contents = contents + '\n' + self.document(value, key, mod, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000829
830 if not contents: return title + '\n'
831 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
832
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000833 def formatvalue(self, object):
834 """Format an argument default value as text."""
835 return '=' + self.repr(object)
836
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000837 def docroutine(self, object, name=None, mod=None, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000838 """Produce text documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000839 realname = object.__name__
840 name = name or realname
841 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000842 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000843 if inspect.ismethod(object):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000844 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000845 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000846 if imclass is not cl:
847 note = ' from ' + classname(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000848 skipdocs = 1
849 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000850 note = (object.im_self and
851 ' method of %s instance' + object.im_self.__class__ or
852 ' unbound %s method' % classname(imclass, mod))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000853 object = object.im_func
854
855 if name == realname:
856 title = self.bold(realname)
857 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000858 if (cl and cl.__dict__.has_key(realname) and
859 cl.__dict__[realname] is object):
860 skipdocs = 1
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000861 title = self.bold(name) + ' = ' + realname
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000862 if inspect.isbuiltin(object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000863 argspec = '(...)'
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000864 else:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000865 args, varargs, varkw, defaults = inspect.getargspec(object)
866 argspec = inspect.formatargspec(
867 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000868 if realname == '<lambda>':
869 title = 'lambda'
870 argspec = argspec[1:-1] # remove parentheses
871 decl = title + argspec + note
872
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000873 if skipdocs:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000874 return decl + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000875 else:
876 doc = getdoc(object) or ''
877 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000878
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000879 def docother(self, object, name=None, mod=None, maxlen=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000880 """Produce text documentation for a data object."""
881 repr = self.repr(object)
882 if maxlen:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000883 line = (name and name + ' = ' or '') + repr
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000884 chop = maxlen - len(line)
885 if chop < 0: repr = repr[:chop] + '...'
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000886 line = (name and self.bold(name) + ' = ' or '') + repr
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000887 return line
888
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000889# --------------------------------------------------------- user interfaces
890
891def pager(text):
892 """The first time this is called, determine what kind of pager to use."""
893 global pager
894 pager = getpager()
895 pager(text)
896
897def getpager():
898 """Decide what method to use for paging through text."""
899 if type(sys.stdout) is not types.FileType:
900 return plainpager
901 if not sys.stdin.isatty() or not sys.stdout.isatty():
902 return plainpager
903 if os.environ.has_key('PAGER'):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000904 if sys.platform == 'win32': # pipes completely broken in Windows
905 return lambda a: tempfilepager(a, os.environ['PAGER'])
906 else:
907 return lambda a: pipepager(a, os.environ['PAGER'])
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +0000908 if sys.platform == 'win32':
909 return lambda a: tempfilepager(a, 'more <')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000910 if hasattr(os, 'system') and os.system('less 2>/dev/null') == 0:
911 return lambda a: pipepager(a, 'less')
912
913 import tempfile
914 filename = tempfile.mktemp()
915 open(filename, 'w').close()
916 try:
917 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
918 return lambda text: pipepager(text, 'more')
919 else:
920 return ttypager
921 finally:
922 os.unlink(filename)
923
924def pipepager(text, cmd):
925 """Page through text by feeding it to another program."""
926 pipe = os.popen(cmd, 'w')
927 try:
928 pipe.write(text)
929 pipe.close()
930 except IOError:
931 # Ignore broken pipes caused by quitting the pager program.
932 pass
933
934def tempfilepager(text, cmd):
935 """Page through text by invoking a program on a temporary file."""
936 import tempfile
937 filename = tempfile.mktemp()
938 file = open(filename, 'w')
939 file.write(text)
940 file.close()
941 try:
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +0000942 os.system(cmd + ' ' + filename)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000943 finally:
944 os.unlink(filename)
945
946def plain(text):
947 """Remove boldface formatting from text."""
948 return re.sub('.\b', '', text)
949
950def ttypager(text):
951 """Page through text on a text terminal."""
952 lines = split(plain(text), '\n')
953 try:
954 import tty
955 fd = sys.stdin.fileno()
956 old = tty.tcgetattr(fd)
957 tty.setcbreak(fd)
958 getchar = lambda: sys.stdin.read(1)
Ka-Ping Yee457aab22001-02-27 23:36:29 +0000959 except (ImportError, AttributeError):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000960 tty = None
961 getchar = lambda: sys.stdin.readline()[:-1][:1]
962
963 try:
964 r = inc = os.environ.get('LINES', 25) - 1
965 sys.stdout.write(join(lines[:inc], '\n') + '\n')
966 while lines[r:]:
967 sys.stdout.write('-- more --')
968 sys.stdout.flush()
969 c = getchar()
970
971 if c in ['q', 'Q']:
972 sys.stdout.write('\r \r')
973 break
974 elif c in ['\r', '\n']:
975 sys.stdout.write('\r \r' + lines[r] + '\n')
976 r = r + 1
977 continue
978 if c in ['b', 'B', '\x1b']:
979 r = r - inc - inc
980 if r < 0: r = 0
981 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
982 r = r + inc
983
984 finally:
985 if tty:
986 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
987
988def plainpager(text):
989 """Simply print unformatted text. This is the ultimate fallback."""
990 sys.stdout.write(plain(text))
991
992def describe(thing):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000993 """Produce a short description of the given thing."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000994 if inspect.ismodule(thing):
995 if thing.__name__ in sys.builtin_module_names:
996 return 'built-in module ' + thing.__name__
997 if hasattr(thing, '__path__'):
998 return 'package ' + thing.__name__
999 else:
1000 return 'module ' + thing.__name__
1001 if inspect.isbuiltin(thing):
1002 return 'built-in function ' + thing.__name__
1003 if inspect.isclass(thing):
1004 return 'class ' + thing.__name__
1005 if inspect.isfunction(thing):
1006 return 'function ' + thing.__name__
1007 if inspect.ismethod(thing):
1008 return 'method ' + thing.__name__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001009 if type(thing) is types.InstanceType:
1010 return 'instance of ' + thing.__class__.__name__
1011 return type(thing).__name__
1012
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001013def freshimport(path, cache={}):
1014 """Import a module freshly from disk, making sure it's up to date."""
1015 if sys.modules.has_key(path):
1016 # This is the only way to be sure. Checking the mtime of the file
1017 # isn't good enough (e.g. what if the module contains a class that
1018 # inherits from another module that has changed?).
1019 if path not in sys.builtin_module_names:
Ka-Ping Yee13121622001-04-12 13:37:39 +00001020 # Python never loads a dynamic extension a second time from the
1021 # same path, even if the file is changed or missing. Deleting
1022 # the entry in sys.modules doesn't help for dynamic extensions,
1023 # so we're not even going to try to keep them up to date.
1024 info = inspect.getmoduleinfo(sys.modules[path].__file__)
1025 if info[3] != imp.C_EXTENSION:
1026 del sys.modules[path]
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001027 try:
1028 module = __import__(path)
1029 except:
1030 # Did the error occur before or after the module was found?
1031 (exc, value, tb) = info = sys.exc_info()
1032 if sys.modules.has_key(path):
1033 # An error occured while executing the imported module.
1034 raise ErrorDuringImport(sys.modules[path].__file__, info)
1035 elif exc is SyntaxError:
1036 # A SyntaxError occurred before we could execute the module.
1037 raise ErrorDuringImport(value.filename, info)
1038 elif exc is ImportError and \
1039 split(lower(str(value)))[:2] == ['no', 'module']:
1040 # The module was not found.
1041 return None
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001042 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001043 # Some other error occurred during the importing process.
1044 raise ErrorDuringImport(path, sys.exc_info())
1045 for part in split(path, '.')[1:]:
1046 try: module = getattr(module, part)
1047 except AttributeError: return None
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001048 return module
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001049
1050def locate(path):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001051 """Locate an object by name or dotted path, importing as necessary."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001052 parts = split(path, '.')
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001053 module, n = None, 0
1054 while n < len(parts):
1055 nextmodule = freshimport(join(parts[:n+1], '.'))
1056 if nextmodule: module, n = nextmodule, n + 1
1057 else: break
1058 if module:
1059 object = module
1060 for part in parts[n:]:
1061 try: object = getattr(object, part)
1062 except AttributeError: return None
1063 return object
1064 else:
1065 import __builtin__
1066 if hasattr(__builtin__, path):
1067 return getattr(__builtin__, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001068
1069# --------------------------------------- interactive interpreter interface
1070
1071text = TextDoc()
1072html = HTMLDoc()
1073
Ka-Ping Yee66246962001-04-12 11:59:50 +00001074def doc(thing, title='Python Library Documentation: %s'):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001075 """Display text documentation, given an object or a path to an object."""
1076 suffix, name = '', None
1077 if type(thing) is type(''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001078 try:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001079 object = locate(thing)
1080 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001081 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001082 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001083 if not object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001084 print 'no Python documentation found for %s' % repr(thing)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001085 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001086 parts = split(thing, '.')
1087 if len(parts) > 1: suffix = ' in ' + join(parts[:-1], '.')
1088 name = parts[-1]
1089 thing = object
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001090
1091 desc = describe(thing)
1092 module = inspect.getmodule(thing)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001093 if not suffix and module and module is not thing:
1094 suffix = ' in module ' + module.__name__
Ka-Ping Yee66246962001-04-12 11:59:50 +00001095 pager(title % (desc + suffix) + '\n\n' + text.document(thing, name))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001096
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001097def writedoc(key):
1098 """Write HTML documentation to a file in the current directory."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001099 try:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001100 object = locate(key)
1101 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001102 print value
1103 else:
1104 if object:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001105 page = html.page(describe(object),
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001106 html.document(object, object.__name__))
1107 file = open(key + '.html', 'w')
1108 file.write(page)
1109 file.close()
1110 print 'wrote', key + '.html'
1111 else:
1112 print 'no Python documentation found for %s' % repr(key)
1113
1114def writedocs(dir, pkgpath='', done={}):
1115 """Write out HTML documentation for all modules in a directory tree."""
1116 for file in os.listdir(dir):
1117 path = os.path.join(dir, file)
1118 if ispackage(path):
1119 writedocs(path, pkgpath + file + '.')
1120 elif os.path.isfile(path):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001121 modname = inspect.getmodulename(path)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001122 if modname:
1123 modname = pkgpath + modname
1124 if not done.has_key(modname):
1125 done[modname] = 1
1126 writedoc(modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001127
1128class Helper:
1129 def __repr__(self):
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00001130 return '''Welcome to Python %s!
1131
1132To get help on a Python object, call help(object).
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001133To get help on a module or package, either import it before calling
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00001134help(module) or call help('modulename').''' % sys.version[:3]
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001135
1136 def __call__(self, *args):
1137 if args:
1138 doc(args[0])
1139 else:
1140 print repr(self)
1141
1142help = Helper()
1143
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001144class Scanner:
1145 """A generic tree iterator."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001146 def __init__(self, roots, children, descendp):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001147 self.roots = roots[:]
1148 self.state = []
1149 self.children = children
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001150 self.descendp = descendp
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001151
1152 def next(self):
1153 if not self.state:
1154 if not self.roots:
1155 return None
1156 root = self.roots.pop(0)
1157 self.state = [(root, self.children(root))]
1158 node, children = self.state[-1]
1159 if not children:
1160 self.state.pop()
1161 return self.next()
1162 child = children.pop(0)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001163 if self.descendp(child):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001164 self.state.append((child, self.children(child)))
1165 return child
1166
1167class ModuleScanner(Scanner):
1168 """An interruptible scanner that searches module synopses."""
1169 def __init__(self):
1170 roots = map(lambda dir: (dir, ''), pathdirs())
1171 Scanner.__init__(self, roots, self.submodules, self.ispackage)
1172
1173 def submodules(self, (dir, package)):
1174 children = []
1175 for file in os.listdir(dir):
1176 path = os.path.join(dir, file)
Tim Peters30edd232001-03-16 08:29:48 +00001177 if ispackage(path):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001178 children.append((path, package + (package and '.') + file))
1179 else:
1180 children.append((path, package))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001181 children.sort() # so that spam.py comes before spam.pyc or spam.pyo
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001182 return children
1183
1184 def ispackage(self, (dir, package)):
1185 return ispackage(dir)
1186
Ka-Ping Yee66246962001-04-12 11:59:50 +00001187 def run(self, callback, key=None, completer=None):
1188 if key: key = lower(key)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001189 self.quit = 0
1190 seen = {}
1191
1192 for modname in sys.builtin_module_names:
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001193 if modname != '__main__':
1194 seen[modname] = 1
Ka-Ping Yee66246962001-04-12 11:59:50 +00001195 if key is None:
1196 callback(None, modname, '')
1197 else:
1198 desc = split(freshimport(modname).__doc__ or '', '\n')[0]
1199 if find(lower(modname + ' - ' + desc), key) >= 0:
1200 callback(None, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001201
1202 while not self.quit:
1203 node = self.next()
1204 if not node: break
1205 path, package = node
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001206 modname = inspect.getmodulename(path)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001207 if os.path.isfile(path) and modname:
1208 modname = package + (package and '.') + modname
1209 if not seen.has_key(modname):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001210 seen[modname] = 1 # if we see spam.py, skip spam.pyc
Ka-Ping Yee66246962001-04-12 11:59:50 +00001211 if key is None:
1212 callback(path, modname, '')
1213 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001214 desc = synopsis(path) or ''
1215 if find(lower(modname + ' - ' + desc), key) >= 0:
1216 callback(path, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001217 if completer: completer()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001218
1219def apropos(key):
1220 """Print all the one-line module summaries that contain a substring."""
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001221 def callback(path, modname, desc):
1222 if modname[-9:] == '.__init__':
1223 modname = modname[:-9] + ' (package)'
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001224 print modname, desc and '- ' + desc
1225 try: import warnings
1226 except ImportError: pass
1227 else: warnings.filterwarnings('ignore') # ignore problems during import
Ka-Ping Yee66246962001-04-12 11:59:50 +00001228 ModuleScanner().run(callback, key)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001229
1230# --------------------------------------------------- web browser interface
1231
Ka-Ping Yee66246962001-04-12 11:59:50 +00001232def serve(port, callback=None, completer=None):
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001233 import BaseHTTPServer, mimetools, select
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001234
1235 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1236 class Message(mimetools.Message):
1237 def __init__(self, fp, seekable=1):
1238 Message = self.__class__
1239 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1240 self.encodingheader = self.getheader('content-transfer-encoding')
1241 self.typeheader = self.getheader('content-type')
1242 self.parsetype()
1243 self.parseplist()
1244
1245 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1246 def send_document(self, title, contents):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001247 try:
1248 self.send_response(200)
1249 self.send_header('Content-Type', 'text/html')
1250 self.end_headers()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001251 self.wfile.write(html.page(title, contents))
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001252 except IOError: pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001253
1254 def do_GET(self):
1255 path = self.path
1256 if path[-5:] == '.html': path = path[:-5]
1257 if path[:1] == '/': path = path[1:]
1258 if path and path != '.':
1259 try:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001260 obj = locate(path)
1261 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001262 self.send_document(path, html.escape(str(value)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001263 return
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001264 if obj:
1265 self.send_document(describe(obj), html.document(obj, path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001266 else:
1267 self.send_document(path,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001268'no Python documentation found for %s' % repr(path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001269 else:
1270 heading = html.heading(
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001271'<big><big><strong>Python: Index of Modules</strong></big></big>',
1272'#ffffff', '#7799ee')
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001273 def bltinlink(name):
1274 return '<a href="%s.html">%s</a>' % (name, name)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001275 names = filter(lambda x: x != '__main__',
1276 sys.builtin_module_names)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001277 contents = html.multicolumn(names, bltinlink)
1278 indices = ['<p>' + html.bigsection(
1279 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1280
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001281 seen = {}
1282 for dir in pathdirs():
1283 indices.append(html.index(dir, seen))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001284 contents = heading + join(indices) + '''<p align=right>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001285<small><small><font color="#909090" face="helvetica, arial"><strong>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001286pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font></small></small>'''
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001287 self.send_document('Index of Modules', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001288
1289 def log_message(self, *args): pass
1290
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001291 class DocServer(BaseHTTPServer.HTTPServer):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001292 def __init__(self, port, callback):
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001293 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001294 self.address = ('', port)
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001295 self.url = 'http://%s:%d/' % (host, port)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001296 self.callback = callback
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001297 self.base.__init__(self, self.address, self.handler)
1298
1299 def serve_until_quit(self):
1300 import select
1301 self.quit = 0
1302 while not self.quit:
1303 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1304 if rd: self.handle_request()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001305
1306 def server_activate(self):
1307 self.base.server_activate(self)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001308 if self.callback: self.callback(self)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001309
1310 DocServer.base = BaseHTTPServer.HTTPServer
1311 DocServer.handler = DocHandler
1312 DocHandler.MessageClass = Message
1313 try:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001314 try:
1315 DocServer(port, callback).serve_until_quit()
1316 except (KeyboardInterrupt, select.error):
1317 pass
1318 finally:
Ka-Ping Yee66246962001-04-12 11:59:50 +00001319 if completer: completer()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001320
1321# ----------------------------------------------------- graphical interface
1322
1323def gui():
1324 """Graphical interface (starts web server and pops up a control window)."""
1325 class GUI:
1326 def __init__(self, window, port=7464):
1327 self.window = window
1328 self.server = None
1329 self.scanner = None
1330
1331 import Tkinter
1332 self.server_frm = Tkinter.Frame(window)
1333 self.title_lbl = Tkinter.Label(self.server_frm,
1334 text='Starting server...\n ')
1335 self.open_btn = Tkinter.Button(self.server_frm,
1336 text='open browser', command=self.open, state='disabled')
1337 self.quit_btn = Tkinter.Button(self.server_frm,
1338 text='quit serving', command=self.quit, state='disabled')
1339
1340 self.search_frm = Tkinter.Frame(window)
1341 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
1342 self.search_ent = Tkinter.Entry(self.search_frm)
1343 self.search_ent.bind('<Return>', self.search)
1344 self.stop_btn = Tkinter.Button(self.search_frm,
1345 text='stop', pady=0, command=self.stop, state='disabled')
1346 if sys.platform == 'win32':
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001347 # Trying to hide and show this button crashes under Windows.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001348 self.stop_btn.pack(side='right')
1349
1350 self.window.title('pydoc')
1351 self.window.protocol('WM_DELETE_WINDOW', self.quit)
1352 self.title_lbl.pack(side='top', fill='x')
1353 self.open_btn.pack(side='left', fill='x', expand=1)
1354 self.quit_btn.pack(side='right', fill='x', expand=1)
1355 self.server_frm.pack(side='top', fill='x')
1356
1357 self.search_lbl.pack(side='left')
1358 self.search_ent.pack(side='right', fill='x', expand=1)
1359 self.search_frm.pack(side='top', fill='x')
1360 self.search_ent.focus_set()
1361
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001362 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001363 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001364 self.result_lst.bind('<Button-1>', self.select)
1365 self.result_lst.bind('<Double-Button-1>', self.goto)
1366 self.result_scr = Tkinter.Scrollbar(window,
1367 orient='vertical', command=self.result_lst.yview)
1368 self.result_lst.config(yscrollcommand=self.result_scr.set)
1369
1370 self.result_frm = Tkinter.Frame(window)
1371 self.goto_btn = Tkinter.Button(self.result_frm,
1372 text='go to selected', command=self.goto)
1373 self.hide_btn = Tkinter.Button(self.result_frm,
1374 text='hide results', command=self.hide)
1375 self.goto_btn.pack(side='left', fill='x', expand=1)
1376 self.hide_btn.pack(side='right', fill='x', expand=1)
1377
1378 self.window.update()
1379 self.minwidth = self.window.winfo_width()
1380 self.minheight = self.window.winfo_height()
1381 self.bigminheight = (self.server_frm.winfo_reqheight() +
1382 self.search_frm.winfo_reqheight() +
1383 self.result_lst.winfo_reqheight() +
1384 self.result_frm.winfo_reqheight())
1385 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
1386 self.expanded = 0
1387 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1388 self.window.wm_minsize(self.minwidth, self.minheight)
1389
1390 import threading
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001391 threading.Thread(
1392 target=serve, args=(port, self.ready, self.quit)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001393
1394 def ready(self, server):
1395 self.server = server
1396 self.title_lbl.config(
1397 text='Python documentation server at\n' + server.url)
1398 self.open_btn.config(state='normal')
1399 self.quit_btn.config(state='normal')
1400
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001401 def open(self, event=None, url=None):
1402 url = url or self.server.url
1403 try:
1404 import webbrowser
1405 webbrowser.open(url)
1406 except ImportError: # pre-webbrowser.py compatibility
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001407 if sys.platform == 'win32':
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001408 os.system('start "%s"' % url)
1409 elif sys.platform == 'mac':
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001410 try: import ic
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001411 except ImportError: pass
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001412 else: ic.launchurl(url)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001413 else:
1414 rc = os.system('netscape -remote "openURL(%s)" &' % url)
1415 if rc: os.system('netscape "%s" &' % url)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001416
1417 def quit(self, event=None):
1418 if self.server:
1419 self.server.quit = 1
1420 self.window.quit()
1421
1422 def search(self, event=None):
1423 key = self.search_ent.get()
1424 self.stop_btn.pack(side='right')
1425 self.stop_btn.config(state='normal')
1426 self.search_lbl.config(text='Searching for "%s"...' % key)
1427 self.search_ent.forget()
1428 self.search_lbl.pack(side='left')
1429 self.result_lst.delete(0, 'end')
1430 self.goto_btn.config(state='disabled')
1431 self.expand()
1432
1433 import threading
1434 if self.scanner:
1435 self.scanner.quit = 1
1436 self.scanner = ModuleScanner()
1437 threading.Thread(target=self.scanner.run,
1438 args=(key, self.update, self.done)).start()
1439
1440 def update(self, path, modname, desc):
1441 if modname[-9:] == '.__init__':
1442 modname = modname[:-9] + ' (package)'
1443 self.result_lst.insert('end',
1444 modname + ' - ' + (desc or '(no description)'))
1445
1446 def stop(self, event=None):
1447 if self.scanner:
1448 self.scanner.quit = 1
1449 self.scanner = None
1450
1451 def done(self):
1452 self.scanner = None
1453 self.search_lbl.config(text='Search for')
1454 self.search_lbl.pack(side='left')
1455 self.search_ent.pack(side='right', fill='x', expand=1)
1456 if sys.platform != 'win32': self.stop_btn.forget()
1457 self.stop_btn.config(state='disabled')
1458
1459 def select(self, event=None):
1460 self.goto_btn.config(state='normal')
1461
1462 def goto(self, event=None):
1463 selection = self.result_lst.curselection()
1464 if selection:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001465 modname = split(self.result_lst.get(selection[0]))[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001466 self.open(url=self.server.url + modname + '.html')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001467
1468 def collapse(self):
1469 if not self.expanded: return
1470 self.result_frm.forget()
1471 self.result_scr.forget()
1472 self.result_lst.forget()
1473 self.bigwidth = self.window.winfo_width()
1474 self.bigheight = self.window.winfo_height()
1475 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1476 self.window.wm_minsize(self.minwidth, self.minheight)
1477 self.expanded = 0
1478
1479 def expand(self):
1480 if self.expanded: return
1481 self.result_frm.pack(side='bottom', fill='x')
1482 self.result_scr.pack(side='right', fill='y')
1483 self.result_lst.pack(side='top', fill='both', expand=1)
1484 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
1485 self.window.wm_minsize(self.minwidth, self.bigminheight)
1486 self.expanded = 1
1487
1488 def hide(self, event=None):
1489 self.stop()
1490 self.collapse()
1491
1492 import Tkinter
1493 try:
1494 gui = GUI(Tkinter.Tk())
1495 Tkinter.mainloop()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001496 except KeyboardInterrupt:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001497 pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001498
1499# -------------------------------------------------- command-line interface
1500
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001501def ispath(x):
1502 return type(x) is types.StringType and find(x, os.sep) >= 0
1503
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001504def cli():
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001505 """Command-line interface (looks at sys.argv to decide what to do)."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001506 import getopt
1507 class BadUsage: pass
1508
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001509 # Scripts don't get the current directory in their path by default.
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00001510 scriptdir = os.path.dirname(sys.argv[0])
1511 if scriptdir in sys.path:
1512 sys.path.remove(scriptdir)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001513 sys.path.insert(0, '.')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001514
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001515 try:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001516 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001517 writing = 0
1518
1519 for opt, val in opts:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001520 if opt == '-g':
1521 gui()
1522 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001523 if opt == '-k':
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001524 apropos(val)
1525 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001526 if opt == '-p':
1527 try:
1528 port = int(val)
1529 except ValueError:
1530 raise BadUsage
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001531 def ready(server):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001532 print 'pydoc server ready at %s' % server.url
1533 def stopped():
1534 print 'pydoc server stopped'
1535 serve(port, ready, stopped)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001536 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001537 if opt == '-w':
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001538 writing = 1
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001539
1540 if not args: raise BadUsage
1541 for arg in args:
1542 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001543 if ispath(arg) and os.path.isfile(arg):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001544 arg = importfile(arg)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001545 if writing:
1546 if ispath(arg) and os.path.isdir(arg):
1547 writedocs(arg)
1548 else:
1549 writedoc(arg)
1550 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001551 doc(arg)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001552 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001553 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001554
1555 except (getopt.error, BadUsage):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001556 cmd = sys.argv[0]
1557 print """pydoc - the Python documentation tool
1558
1559%s <name> ...
1560 Show text documentation on something. <name> may be the name of a
1561 function, module, or package, or a dotted reference to a class or
1562 function within a module or module in a package. If <name> contains
1563 a '%s', it is used as the path to a Python source file to document.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001564
1565%s -k <keyword>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001566 Search for a keyword in the synopsis lines of all available modules.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001567
1568%s -p <port>
1569 Start an HTTP server on the given port on the local machine.
1570
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001571%s -g
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001572 Pop up a graphical interface for finding and serving documentation.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001573
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001574%s -w <name> ...
1575 Write out the HTML documentation for a module to a file in the current
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001576 directory. If <name> contains a '%s', it is treated as a filename; if
1577 it names a directory, documentation is written for all the contents.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001578""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001579
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001580if __name__ == '__main__': cli()