blob: 5f4cc1c65d0c534d1a06f85ec117e3dffb61e6e3 [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
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000345 def classlink(self, object, modname):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000346 """Make a link for a class."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000347 name = classname(object, modname)
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000348 if sys.modules.has_key(object.__module__) and \
349 getattr(sys.modules[object.__module__], object.__name__) is object:
350 return '<a href="%s.html#%s">%s</a>' % (
351 object.__module__, object.__name__, name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000352 return name
353
354 def modulelink(self, object):
355 """Make a link for a module."""
356 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
357
358 def modpkglink(self, (name, path, ispackage, shadowed)):
359 """Make a link for a module or package to display in an index."""
360 if shadowed:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000361 return self.grey(name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000362 if path:
363 url = '%s.%s.html' % (path, name)
364 else:
365 url = '%s.html' % name
366 if ispackage:
367 text = '<strong>%s</strong>&nbsp;(package)' % name
368 else:
369 text = name
370 return '<a href="%s">%s</a>' % (url, text)
371
372 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
373 """Mark up some plain text, given a context of symbols to look for.
374 Each context dictionary maps object names to anchor names."""
375 escape = escape or self.escape
376 results = []
377 here = 0
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000378 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
379 r'RFC[- ]?(\d+)|'
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000380 r'PEP[- ]?(\d+)|'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000381 r'(self\.)?(\w+))\b')
382 while 1:
383 match = pattern.search(text, here)
384 if not match: break
385 start, end = match.span()
386 results.append(escape(text[here:start]))
387
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000388 all, scheme, rfc, pep, selfdot, name = match.groups()
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000389 if scheme:
390 results.append('<a href="%s">%s</a>' % (all, escape(all)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000391 elif rfc:
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000392 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
393 results.append('<a href="%s">%s</a>' % (url, escape(all)))
394 elif pep:
395 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000396 results.append('<a href="%s">%s</a>' % (url, escape(all)))
397 elif text[end:end+1] == '(':
398 results.append(self.namelink(name, methods, funcs, classes))
399 elif selfdot:
400 results.append('self.<strong>%s</strong>' % name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000401 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000402 results.append(self.namelink(name, classes))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000403 here = end
404 results.append(escape(text[here:]))
405 return join(results, '')
406
407 # ---------------------------------------------- type-specific routines
408
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000409 def formattree(self, tree, modname, parent=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000410 """Produce HTML for a class tree as given by inspect.getclasstree()."""
411 result = ''
412 for entry in tree:
413 if type(entry) is type(()):
414 c, bases = entry
415 result = result + '<dt><font face="helvetica, arial"><small>'
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000416 result = result + self.classlink(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000417 if bases and bases != (parent,):
418 parents = []
419 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000420 parents.append(self.classlink(base, modname))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000421 result = result + '(' + join(parents, ', ') + ')'
422 result = result + '\n</small></font></dt>'
423 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000424 result = result + '<dd>\n%s</dd>\n' % self.formattree(
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000425 entry, modname, c)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000426 return '<dl>\n%s</dl>\n' % result
427
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000428 def docmodule(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000429 """Produce HTML documentation for a module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000430 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000431 parts = split(name, '.')
432 links = []
433 for i in range(len(parts)-1):
434 links.append(
435 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
436 (join(parts[:i+1], '.'), parts[i]))
437 linkedname = join(links + parts[-1:], '.')
438 head = '<big><big><strong>%s</strong></big></big>' % linkedname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000439 try:
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000440 path = inspect.getabsfile(object)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000441 filelink = '<a href="file:%s">%s</a>' % (path, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000442 except TypeError:
443 filelink = '(built-in)'
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000444 info = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000445 if hasattr(object, '__version__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000446 version = str(object.__version__)
Ka-Ping Yee40c49912001-02-27 22:46:01 +0000447 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
448 version = strip(version[11:-1])
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000449 info.append('version %s' % self.escape(version))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000450 if hasattr(object, '__date__'):
451 info.append(self.escape(str(object.__date__)))
452 if info:
453 head = head + ' (%s)' % join(info, ', ')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000454 result = self.heading(
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000455 head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)
456
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000457 modules = inspect.getmembers(object, inspect.ismodule)
458
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000459 classes, cdict = [], {}
460 for key, value in inspect.getmembers(object, inspect.isclass):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000461 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000462 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000463 cdict[key] = cdict[value] = '#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000464 for key, value in classes:
465 for base in value.__bases__:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000466 key, modname = base.__name__, base.__module__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000467 module = sys.modules.get(modname)
468 if modname != name and module and hasattr(module, key):
469 if getattr(module, key) is base:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000470 if not cdict.has_key(key):
471 cdict[key] = cdict[base] = modname + '.html#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000472 funcs, fdict = [], {}
473 for key, value in inspect.getmembers(object, inspect.isroutine):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000474 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000475 funcs.append((key, value))
476 fdict[key] = '#-' + key
477 if inspect.isfunction(value): fdict[value] = fdict[key]
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000478 constants = []
479 for key, value in inspect.getmembers(object, isconstant):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000480 constants.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000481
482 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
483 doc = doc and '<tt>%s</tt>' % doc
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000484 result = result + '<p>%s</p>\n' % self.small(doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000485
486 if hasattr(object, '__path__'):
487 modpkgs = []
488 modnames = []
489 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000490 path = os.path.join(object.__path__[0], file)
491 modname = inspect.getmodulename(file)
492 if modname and modname not in modnames:
493 modpkgs.append((modname, name, 0, 0))
494 modnames.append(modname)
495 elif ispackage(path):
496 modpkgs.append((file, name, 1, 0))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000497 modpkgs.sort()
498 contents = self.multicolumn(modpkgs, self.modpkglink)
499 result = result + self.bigsection(
500 'Package Contents', '#ffffff', '#aa55cc', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000501 elif modules:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000502 contents = self.multicolumn(
503 modules, lambda (key, value), s=self: s.modulelink(value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000504 result = result + self.bigsection(
505 'Modules', '#fffff', '#aa55cc', contents)
506
507 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000508 classlist = map(lambda (key, value): value, classes)
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000509 contents = [
510 self.formattree(inspect.getclasstree(classlist, 1), name)]
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000511 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000512 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000513 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000514 'Classes', '#ffffff', '#ee77aa', join(contents))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000515 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000516 contents = []
517 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000518 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000519 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000520 'Functions', '#ffffff', '#eeaa77', join(contents))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000521 if constants:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000522 contents = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000523 for key, value in constants:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000524 contents.append(self.document(value, key))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000525 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000526 'Constants', '#ffffff', '#55aa55', join(contents, '<br>'))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000527 if hasattr(object, '__author__'):
528 contents = self.markup(str(object.__author__), self.preformat)
529 result = result + self.bigsection(
530 'Author', '#ffffff', '#7799ee', contents)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000531 if hasattr(object, '__credits__'):
532 contents = self.markup(str(object.__credits__), self.preformat)
533 result = result + self.bigsection(
534 'Credits', '#ffffff', '#7799ee', contents)
535
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000536 return result
537
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000538 def docclass(self, object, name=None, mod=None, funcs={}, classes={}):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000539 """Produce HTML documentation for a class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000540 realname = object.__name__
541 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000542 bases = object.__bases__
543 contents = ''
544
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000545 methods, mdict = allmethods(object).items(), {}
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000546 methods.sort()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000547 for key, value in methods:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000548 mdict[key] = mdict[value] = '#' + name + '-' + key
549 for key, value in methods:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000550 contents = contents + self.document(
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000551 value, key, mod, funcs, classes, mdict, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000552
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000553 if name == realname:
554 title = '<a name="%s">class <strong>%s</strong></a>' % (
555 name, realname)
556 else:
557 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
558 name, name, realname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000559 if bases:
560 parents = []
561 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000562 parents.append(self.classlink(base, object.__module__))
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 Yee6dcfa382001-04-12 20:27:31 +0000583 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000584 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000585 if imclass is not cl:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000586 note = ' from ' + self.classlink(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000587 skipdocs = 1
588 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000589 if object.im_self:
590 note = ' method of %s instance' % self.classlink(
591 object.im_self.__class__, mod)
592 else:
593 note = ' unbound %s method' % self.classlink(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000594 object = object.im_func
595
596 if name == realname:
597 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
598 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000599 if (cl and cl.__dict__.has_key(realname) and
600 cl.__dict__[realname] is object):
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000601 reallink = '<a href="#%s">%s</a>' % (
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000602 cl.__name__ + '-' + realname, realname)
603 skipdocs = 1
604 else:
605 reallink = realname
606 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
607 anchor, name, reallink)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000608 if inspect.isbuiltin(object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000609 argspec = '(...)'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000610 else:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000611 args, varargs, varkw, defaults = inspect.getargspec(object)
612 argspec = inspect.formatargspec(
613 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000614 if realname == '<lambda>':
615 decl = '<em>lambda</em>'
616 argspec = argspec[1:-1] # remove parentheses
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000617
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000618 decl = title + argspec + (note and self.small(self.grey(
619 '<font face="helvetica, arial">%s</font>' % note)))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000620
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000621 if skipdocs:
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000622 return '<dl><dt>%s</dl>\n' % decl
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000623 else:
624 doc = self.markup(
625 getdoc(object), self.preformat, funcs, classes, methods)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000626 doc = doc and '<dd>' + self.small('<tt>%s</tt>' % doc)
627 return '<dl><dt>%s%s</dl>\n' % (decl, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000628
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000629 def docother(self, object, name=None, mod=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000630 """Produce HTML documentation for a data object."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000631 lhs = name and '<strong>%s</strong> = ' % name or ''
632 return lhs + self.repr(object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000633
634 def index(self, dir, shadowed=None):
635 """Generate an HTML index for a directory of modules."""
636 modpkgs = []
637 if shadowed is None: shadowed = {}
638 seen = {}
639 files = os.listdir(dir)
640
641 def found(name, ispackage,
642 modpkgs=modpkgs, shadowed=shadowed, seen=seen):
643 if not seen.has_key(name):
644 modpkgs.append((name, '', ispackage, shadowed.has_key(name)))
645 seen[name] = 1
646 shadowed[name] = 1
647
648 # Package spam/__init__.py takes precedence over module spam.py.
649 for file in files:
650 path = os.path.join(dir, file)
651 if ispackage(path): found(file, 1)
652 for file in files:
653 path = os.path.join(dir, file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000654 if os.path.isfile(path):
655 modname = inspect.getmodulename(file)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000656 if modname: found(modname, 0)
657
658 modpkgs.sort()
659 contents = self.multicolumn(modpkgs, self.modpkglink)
660 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
661
662# -------------------------------------------- text documentation generator
663
664class TextRepr(Repr):
665 """Class for safely making a text representation of a Python object."""
666 def __init__(self):
667 Repr.__init__(self)
668 self.maxlist = self.maxtuple = self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000669 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000670
671 def repr1(self, x, level):
672 methodname = 'repr_' + join(split(type(x).__name__), '_')
673 if hasattr(self, methodname):
674 return getattr(self, methodname)(x, level)
675 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000676 return cram(stripid(repr(x)), self.maxother)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000677
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000678 def repr_string(self, x, level):
679 test = cram(x, self.maxstring)
680 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000681 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000682 # Backslashes are only literal in the string and are never
683 # needed to make any special characters, so show a raw string.
684 return 'r' + testrepr[0] + test + testrepr[0]
685 return testrepr
686
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000687 def repr_instance(self, x, level):
688 try:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000689 return cram(stripid(repr(x)), self.maxstring)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000690 except:
691 return '<%s instance>' % x.__class__.__name__
692
693class TextDoc(Doc):
694 """Formatter class for text documentation."""
695
696 # ------------------------------------------- text formatting utilities
697
698 _repr_instance = TextRepr()
699 repr = _repr_instance.repr
700
701 def bold(self, text):
702 """Format a string in bold by overstriking."""
703 return join(map(lambda ch: ch + '\b' + ch, text), '')
704
705 def indent(self, text, prefix=' '):
706 """Indent text by prepending a given prefix to each line."""
707 if not text: return ''
708 lines = split(text, '\n')
709 lines = map(lambda line, prefix=prefix: prefix + line, lines)
710 if lines: lines[-1] = rstrip(lines[-1])
711 return join(lines, '\n')
712
713 def section(self, title, contents):
714 """Format a section with a given heading."""
715 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
716
717 # ---------------------------------------------- type-specific routines
718
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000719 def formattree(self, tree, modname, parent=None, prefix=''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000720 """Render in text a class tree as returned by inspect.getclasstree()."""
721 result = ''
722 for entry in tree:
723 if type(entry) is type(()):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000724 c, bases = entry
725 result = result + prefix + classname(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000726 if bases and bases != (parent,):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000727 parents = map(lambda c, m=modname: classname(c, m), bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000728 result = result + '(%s)' % join(parents, ', ')
729 result = result + '\n'
730 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000731 result = result + self.formattree(
732 entry, modname, c, prefix + ' ')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000733 return result
734
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000735 def docmodule(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000736 """Produce text documentation for a given module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000737 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000738 synop, desc = splitdoc(getdoc(object))
739 result = self.section('NAME', name + (synop and ' - ' + synop))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000740
741 try:
742 file = inspect.getabsfile(object)
743 except TypeError:
744 file = '(built-in)'
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000745 result = result + self.section('FILE', file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000746 if desc:
747 result = result + self.section('DESCRIPTION', desc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000748
749 classes = []
750 for key, value in inspect.getmembers(object, inspect.isclass):
751 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000752 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000753 funcs = []
754 for key, value in inspect.getmembers(object, inspect.isroutine):
755 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000756 funcs.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000757 constants = []
758 for key, value in inspect.getmembers(object, isconstant):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000759 constants.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000760
761 if hasattr(object, '__path__'):
762 modpkgs = []
763 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000764 path = os.path.join(object.__path__[0], file)
765 modname = inspect.getmodulename(file)
766 if modname and modname not in modpkgs:
767 modpkgs.append(modname)
768 elif ispackage(path):
769 modpkgs.append(file + ' (package)')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000770 modpkgs.sort()
771 result = result + self.section(
772 'PACKAGE CONTENTS', join(modpkgs, '\n'))
773
774 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000775 classlist = map(lambda (key, value): value, classes)
776 contents = [self.formattree(
777 inspect.getclasstree(classlist, 1), name)]
778 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000779 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000780 result = result + self.section('CLASSES', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000781
782 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000783 contents = []
784 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000785 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000786 result = result + self.section('FUNCTIONS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000787
788 if constants:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000789 contents = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000790 for key, value in constants:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000791 contents.append(self.docother(value, key, name, 70))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000792 result = result + self.section('CONSTANTS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000793
794 if hasattr(object, '__version__'):
795 version = str(object.__version__)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000796 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
797 version = strip(version[11:-1])
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000798 result = result + self.section('VERSION', version)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000799 if hasattr(object, '__date__'):
800 result = result + self.section('DATE', str(object.__date__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000801 if hasattr(object, '__author__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000802 result = result + self.section('AUTHOR', str(object.__author__))
803 if hasattr(object, '__credits__'):
804 result = result + self.section('CREDITS', str(object.__credits__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000805 return result
806
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000807 def docclass(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000808 """Produce text documentation for a given class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000809 realname = object.__name__
810 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000811 bases = object.__bases__
812
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000813 if name == realname:
814 title = 'class ' + self.bold(realname)
815 else:
816 title = self.bold(name) + ' = class ' + realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000817 if bases:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000818 def makename(c, m=object.__module__): return classname(c, m)
819 parents = map(makename, bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000820 title = title + '(%s)' % join(parents, ', ')
821
822 doc = getdoc(object)
823 contents = doc and doc + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000824 methods = allmethods(object).items()
825 methods.sort()
826 for key, value in methods:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000827 contents = contents + '\n' + self.document(value, key, mod, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000828
829 if not contents: return title + '\n'
830 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
831
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000832 def formatvalue(self, object):
833 """Format an argument default value as text."""
834 return '=' + self.repr(object)
835
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000836 def docroutine(self, object, name=None, mod=None, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000837 """Produce text documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000838 realname = object.__name__
839 name = name or realname
840 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000841 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000842 if inspect.ismethod(object):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000843 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000844 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000845 if imclass is not cl:
846 note = ' from ' + classname(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000847 skipdocs = 1
848 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000849 if object.im_self:
850 note = ' method of %s instance' % classname(
851 object.im_self.__class__, mod)
852 else:
853 note = ' unbound %s method' % classname(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000854 object = object.im_func
855
856 if name == realname:
857 title = self.bold(realname)
858 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000859 if (cl and cl.__dict__.has_key(realname) and
860 cl.__dict__[realname] is object):
861 skipdocs = 1
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000862 title = self.bold(name) + ' = ' + realname
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000863 if inspect.isbuiltin(object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000864 argspec = '(...)'
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000865 else:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000866 args, varargs, varkw, defaults = inspect.getargspec(object)
867 argspec = inspect.formatargspec(
868 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000869 if realname == '<lambda>':
870 title = 'lambda'
871 argspec = argspec[1:-1] # remove parentheses
872 decl = title + argspec + note
873
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000874 if skipdocs:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000875 return decl + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000876 else:
877 doc = getdoc(object) or ''
878 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000879
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000880 def docother(self, object, name=None, mod=None, maxlen=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000881 """Produce text documentation for a data object."""
882 repr = self.repr(object)
883 if maxlen:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000884 line = (name and name + ' = ' or '') + repr
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000885 chop = maxlen - len(line)
886 if chop < 0: repr = repr[:chop] + '...'
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000887 line = (name and self.bold(name) + ' = ' or '') + repr
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000888 return line
889
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000890# --------------------------------------------------------- user interfaces
891
892def pager(text):
893 """The first time this is called, determine what kind of pager to use."""
894 global pager
895 pager = getpager()
896 pager(text)
897
898def getpager():
899 """Decide what method to use for paging through text."""
900 if type(sys.stdout) is not types.FileType:
901 return plainpager
902 if not sys.stdin.isatty() or not sys.stdout.isatty():
903 return plainpager
904 if os.environ.has_key('PAGER'):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000905 if sys.platform == 'win32': # pipes completely broken in Windows
906 return lambda a: tempfilepager(a, os.environ['PAGER'])
907 else:
908 return lambda a: pipepager(a, os.environ['PAGER'])
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +0000909 if sys.platform == 'win32':
910 return lambda a: tempfilepager(a, 'more <')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000911 if hasattr(os, 'system') and os.system('less 2>/dev/null') == 0:
912 return lambda a: pipepager(a, 'less')
913
914 import tempfile
915 filename = tempfile.mktemp()
916 open(filename, 'w').close()
917 try:
918 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
919 return lambda text: pipepager(text, 'more')
920 else:
921 return ttypager
922 finally:
923 os.unlink(filename)
924
925def pipepager(text, cmd):
926 """Page through text by feeding it to another program."""
927 pipe = os.popen(cmd, 'w')
928 try:
929 pipe.write(text)
930 pipe.close()
931 except IOError:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +0000932 pass # Ignore broken pipes caused by quitting the pager program.
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000933
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:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001129 keywords = {
1130 'and': 'BOOLEAN',
1131 'assert': 'ASSERT',
1132 'break': ('ref/break', 'while for'),
1133 'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1134 'continue': ('ref/continue', 'while for'),
1135 'def': ('ref/function', ''),
1136 'del': ('ref/del', 'BASICMETHODS'),
1137 'elif': 'if',
1138 'else': ('ref/if', 'while for'),
1139 'except': 'try',
1140 'exec': ('ref/exec', ''),
1141 'finally': 'try',
1142 'for': ('ref/for', 'break continue while'),
1143 'from': 'import',
1144 'global': ('ref/global', 'NAMESPACES'),
1145 'if': ('ref/if', 'TRUTHVALUE'),
1146 'import': ('ref/import', 'MODULES'),
1147 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1148 'is': 'COMPARISON',
1149 'lambda': ('ref/lambda', 'FUNCTIONS'),
1150 'not': 'BOOLEAN',
1151 'or': 'BOOLEAN',
1152 'pass': 'PASS',
1153 'print': ('ref/print', ''),
1154 'raise': ('ref/raise', 'EXCEPTIONS'),
1155 'return': ('ref/return', ''),
1156 'try': ('ref/try', 'EXCEPTIONS'),
1157 'while': ('ref/while', 'break continue if TRUTHVALUE'),
1158 }
1159
1160 topics = {
1161 'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'),
1162 'STRINGS': ('ref/strings', 'UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'),
1163 'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'),
1164 'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'),
1165 'UNICODE': ('ref/unicode', 'TYPES STRING'),
1166 'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1167 'INTEGER': ('ref/integers', 'int range'),
1168 'FLOAT': ('ref/floating', 'float math'),
1169 'COMPLEX': ('ref/imaginary', 'complex cmath'),
1170 'SEQUENCES': ('lib/typesseq', 'LISTS'),
1171 'MAPPINGS': 'DICTIONARIES',
1172 'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'),
1173 'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'),
1174 'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'),
1175 'TYPEOBJECTS': ('lib/bltin-type-objects', 'TYPES'),
1176 'FRAMEOBJECTS': 'TYPES',
1177 'TRACEBACKS': 'TYPES',
1178 'NONE': ('lib/bltin-null-object', ''),
1179 'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'),
1180 'FILES': ('lib/bltin-file-objects', ''),
1181 'SPECIALATTRIBUTES': ('lib/specialattrs', ''),
1182 'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'),
1183 'MODULES': ('lib/typesmodules', 'import'),
1184 'PACKAGES': 'import',
1185 'EXPRESSIONS': ('ref/summary', 'lambda or and not in is BOOLEAN COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES LISTS DICTIONARIES BACKQUOTES'),
1186 'OPERATORS': 'EXPRESSIONS',
1187 'PRECEDENCE': 'EXPRESSIONS',
1188 'OBJECTS': ('ref/objects', 'TYPES'),
1189 'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
1190 'BASICMETHODS': ('ref/customization', 'SPECIALMETHODS'),
1191 'ATTRIBUTEMETHODS': ('ref/attribute-access', 'SPECIALMETHODS'),
1192 'CALLABLEMETHODS': ('ref/callable-types', 'SPECIALMETHODS'),
1193 'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCEMETHODS2'),
1194 'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCEMETHODS1'),
1195 'MAPPINGMETHODS': ('ref/sequence-types', 'SPECIALMETHODS'),
1196 'NUMBERMETHODS': ('ref/numeric-types', 'SPECIALMETHODS'),
1197 'EXECUTION': ('ref/execframes', ''),
1198 'NAMESPACES': ('ref/execframes', 'global ASSIGNMENT DELETION'),
1199 'SCOPING': 'NAMESPACES',
1200 'FRAMES': 'NAMESPACES',
1201 'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'),
1202 'COERCIONS': 'CONVERSIONS',
1203 'CONVERSIONS': ('ref/conversions', ''),
1204 'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'),
1205 'SPECIALIDENTIFIERS': ('ref/id-classes', ''),
1206 'PRIVATENAMES': ('ref/identifiers', ''),
1207 'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1208 'TUPLES': 'SEQUENCES',
1209 'TUPLELITERALS': ('ref/exprlists', 'LITERALS'),
1210 'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'),
1211 'LISTLITERALS': ('ref/lists', 'LITERALS'),
1212 'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'),
1213 'DICTIONARYLITERALS': ('ref/dict', 'LITERALS'),
1214 'BACKQUOTES': ('ref/string-conversions', 'LITERALS'),
1215 'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1216 'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'),
1217 'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'),
1218 'CALLS': ('ref/calls', 'EXPRESSIONS'),
1219 'POWER': ('ref/power', 'EXPRESSIONS'),
1220 'UNARY': ('ref/unary', 'EXPRESSIONS'),
1221 'BINARY': ('ref/binary', 'EXPRESSIONS'),
1222 'SHIFTING': ('ref/shifting', 'EXPRESSIONS'),
1223 'BITWISE': ('ref/bitwise', 'EXPRESSIONS'),
1224 'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'),
1225 'BOOLEAN': ('ref/lambda', 'EXPRESSIONS'),
1226 'ASSERTION': 'assert',
1227 'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'),
1228 'AUGMENTEDASSIGNMENT': ('ref/augassign', ''),
1229 'DELETION': 'del',
1230 'PRINTING': 'print',
1231 'RETURNING': 'return',
1232 'IMPORTING': 'import',
1233 'CONDITIONAL': 'if',
1234 'LOOPING': ('ref/compound', 'for while break continue'),
1235 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'),
1236 }
1237
1238 def __init__(self, input, output):
1239 self.input = input
1240 self.output = output
1241 self.docdir = None
1242 execdir = os.path.dirname(sys.executable)
1243 homedir = os.environ.get('PYTHONHOME')
1244 for dir in [os.environ.get('PYTHONDOCS'),
1245 homedir and os.path.join(homedir, 'doc'),
1246 os.path.join(execdir, 'doc'),
1247 '/usr/doc/python-docs-' + split(sys.version)[0],
1248 '/usr/doc/python-' + split(sys.version)[0],
1249 '/usr/doc/python-docs-' + sys.version[:3],
1250 '/usr/doc/python-' + sys.version[:3]]:
1251 if dir and os.path.isdir(os.path.join(dir, 'lib')):
1252 self.docdir = dir
1253
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001254 def __repr__(self):
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001255 self()
1256 return ''
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00001257
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001258 def __call__(self, request=None):
1259 if request is not None:
1260 self.help(request)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001261 else:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001262 self.intro()
1263 self.output.write('\n')
1264 while 1:
1265 self.output.write('help> ')
1266 self.output.flush()
1267 try:
1268 request = self.input.readline()
1269 if not request: break
1270 except KeyboardInterrupt: break
1271 request = strip(replace(request, '"', '', "'", ''))
1272 if lower(request) in ['q', 'quit']: break
1273 self.help(request)
1274 self.output.write('''
1275You're now leaving help and returning to the Python interpreter.
1276If you want to ask for help on a particular object directly from the
1277interpreter, you can type "help(object)". Executing "help('string')"
1278has the same effect as typing a particular string at the help> prompt.
1279''')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001280
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001281 def help(self, request):
1282 if type(request) is type(''):
1283 if request == 'help': self.intro()
1284 elif request == 'keywords': self.listkeywords()
1285 elif request == 'topics': self.listtopics()
1286 elif request == 'modules': self.listmodules()
1287 elif request[:8] == 'modules ':
1288 self.listmodules(split(request)[1])
1289 elif self.keywords.has_key(request): self.showtopic(request)
1290 elif self.topics.has_key(request): self.showtopic(request)
1291 elif request: doc(request, 'Help on %s:')
1292 elif isinstance(request, Helper): self()
1293 else: doc(request, 'Help on %s:')
1294 self.output.write('\n')
1295
1296 def intro(self):
1297 self.output.write('''
1298Welcome to Python %s! This is the online help utility.
1299
1300If this is your first time using Python, you should definitely check out
1301the tutorial on the Internet at http://www.python.org/doc/tut/.
1302
1303Enter the name of any module, keyword, or topic to get help on writing
1304Python programs and using Python modules. To quit this help utility and
1305return to the interpreter, just type "quit".
1306
1307To get a list of available modules, keywords, or topics, type "modules",
1308"keywords", or "topics". Each module also comes with a one-line summary
1309of what it does; to list the modules whose summaries contain a given word
1310such as "spam", type "modules spam".
1311''' % sys.version[:3])
1312
1313 def list(self, items, columns=4, width=80):
1314 items = items[:]
1315 items.sort()
1316 colw = width / columns
1317 rows = (len(items) + columns - 1) / columns
1318 for row in range(rows):
1319 for col in range(columns):
1320 i = col * rows + row
1321 if i < len(items):
1322 self.output.write(items[i])
1323 if col < columns - 1:
1324 self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1325 self.output.write('\n')
1326
1327 def listkeywords(self):
1328 self.output.write('''
1329Here is a list of the Python keywords. Enter any keyword to get more help.
1330
1331''')
1332 self.list(self.keywords.keys())
1333
1334 def listtopics(self):
1335 self.output.write('''
1336Here is a list of available topics. Enter any topic name to get more help.
1337
1338''')
1339 self.list(self.topics.keys())
1340
1341 def showtopic(self, topic):
1342 if not self.docdir:
1343 self.output.write('''
1344Sorry, topic and keyword documentation is not available because the Python
1345HTML documentation files could not be found. If you have installed them,
1346please set the environment variable PYTHONDOCS to indicate their location.
1347''')
1348 return
1349 target = self.topics.get(topic, self.keywords.get(topic))
1350 if not target:
1351 self.output.write('no documentation found for %s\n' % repr(topic))
1352 return
1353 if type(target) is type(''):
1354 return self.showtopic(target)
1355
1356 filename, xrefs = target
1357 filename = self.docdir + '/' + filename + '.html'
1358 try:
1359 file = open(filename)
1360 except:
1361 self.output.write('could not read docs from %s\n' % filename)
1362 return
1363
1364 divpat = re.compile('<div[^>]*navigat.*?</div[^>]*>', re.I | re.S)
1365 addrpat = re.compile('<address[^>]*>.*?</address[^>]*>', re.I | re.S)
1366 document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1367 file.close()
1368
1369 import htmllib, formatter, StringIO
1370 buffer = StringIO.StringIO()
1371 parser = htmllib.HTMLParser(
1372 formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1373 parser.start_table = parser.do_p
1374 parser.end_table = lambda parser=parser: parser.do_p({})
1375 parser.start_tr = parser.do_br
1376 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1377 parser.feed(document)
1378 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
1379 pager(' ' + strip(buffer) + '\n')
1380 if xrefs: self.output.write('\nRelated help topics: %s\n' % xrefs)
1381
1382 def listmodules(self, key=''):
1383 if key:
1384 self.output.write('''
1385Here is a list of matching modules. Enter any module name to get more help.
1386
1387''')
1388 apropos(key)
1389 else:
1390 self.output.write('''
1391Please wait a moment while I gather a list of all available modules...
1392
1393''')
1394 modules = {}
1395 def callback(path, modname, desc, modules=modules):
1396 if modname and modname[-9:] == '.__init__':
1397 modname = modname[:-9] + ' (package)'
1398 if find(modname, '.') < 0:
1399 modules[modname] = 1
1400 ModuleScanner().run(callback)
1401 self.list(modules.keys())
1402 self.output.write('''
1403Enter any module name to get more help. Or, type "modules spam" to search
1404for modules whose descriptions contain the word "spam".
1405''')
1406
1407help = Helper(sys.stdin, sys.stdout)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001408
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001409class Scanner:
1410 """A generic tree iterator."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001411 def __init__(self, roots, children, descendp):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001412 self.roots = roots[:]
1413 self.state = []
1414 self.children = children
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001415 self.descendp = descendp
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001416
1417 def next(self):
1418 if not self.state:
1419 if not self.roots:
1420 return None
1421 root = self.roots.pop(0)
1422 self.state = [(root, self.children(root))]
1423 node, children = self.state[-1]
1424 if not children:
1425 self.state.pop()
1426 return self.next()
1427 child = children.pop(0)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001428 if self.descendp(child):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001429 self.state.append((child, self.children(child)))
1430 return child
1431
1432class ModuleScanner(Scanner):
1433 """An interruptible scanner that searches module synopses."""
1434 def __init__(self):
1435 roots = map(lambda dir: (dir, ''), pathdirs())
1436 Scanner.__init__(self, roots, self.submodules, self.ispackage)
1437
1438 def submodules(self, (dir, package)):
1439 children = []
1440 for file in os.listdir(dir):
1441 path = os.path.join(dir, file)
Tim Peters30edd232001-03-16 08:29:48 +00001442 if ispackage(path):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001443 children.append((path, package + (package and '.') + file))
1444 else:
1445 children.append((path, package))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001446 children.sort() # so that spam.py comes before spam.pyc or spam.pyo
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001447 return children
1448
1449 def ispackage(self, (dir, package)):
1450 return ispackage(dir)
1451
Ka-Ping Yee66246962001-04-12 11:59:50 +00001452 def run(self, callback, key=None, completer=None):
1453 if key: key = lower(key)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001454 self.quit = 0
1455 seen = {}
1456
1457 for modname in sys.builtin_module_names:
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001458 if modname != '__main__':
1459 seen[modname] = 1
Ka-Ping Yee66246962001-04-12 11:59:50 +00001460 if key is None:
1461 callback(None, modname, '')
1462 else:
1463 desc = split(freshimport(modname).__doc__ or '', '\n')[0]
1464 if find(lower(modname + ' - ' + desc), key) >= 0:
1465 callback(None, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001466
1467 while not self.quit:
1468 node = self.next()
1469 if not node: break
1470 path, package = node
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001471 modname = inspect.getmodulename(path)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001472 if os.path.isfile(path) and modname:
1473 modname = package + (package and '.') + modname
1474 if not seen.has_key(modname):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001475 seen[modname] = 1 # if we see spam.py, skip spam.pyc
Ka-Ping Yee66246962001-04-12 11:59:50 +00001476 if key is None:
1477 callback(path, modname, '')
1478 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001479 desc = synopsis(path) or ''
1480 if find(lower(modname + ' - ' + desc), key) >= 0:
1481 callback(path, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001482 if completer: completer()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001483
1484def apropos(key):
1485 """Print all the one-line module summaries that contain a substring."""
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001486 def callback(path, modname, desc):
1487 if modname[-9:] == '.__init__':
1488 modname = modname[:-9] + ' (package)'
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001489 print modname, desc and '- ' + desc
1490 try: import warnings
1491 except ImportError: pass
1492 else: warnings.filterwarnings('ignore') # ignore problems during import
Ka-Ping Yee66246962001-04-12 11:59:50 +00001493 ModuleScanner().run(callback, key)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001494
1495# --------------------------------------------------- web browser interface
1496
Ka-Ping Yee66246962001-04-12 11:59:50 +00001497def serve(port, callback=None, completer=None):
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001498 import BaseHTTPServer, mimetools, select
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001499
1500 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1501 class Message(mimetools.Message):
1502 def __init__(self, fp, seekable=1):
1503 Message = self.__class__
1504 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1505 self.encodingheader = self.getheader('content-transfer-encoding')
1506 self.typeheader = self.getheader('content-type')
1507 self.parsetype()
1508 self.parseplist()
1509
1510 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1511 def send_document(self, title, contents):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001512 try:
1513 self.send_response(200)
1514 self.send_header('Content-Type', 'text/html')
1515 self.end_headers()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001516 self.wfile.write(html.page(title, contents))
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001517 except IOError: pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001518
1519 def do_GET(self):
1520 path = self.path
1521 if path[-5:] == '.html': path = path[:-5]
1522 if path[:1] == '/': path = path[1:]
1523 if path and path != '.':
1524 try:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001525 obj = locate(path)
1526 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001527 self.send_document(path, html.escape(str(value)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001528 return
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001529 if obj:
1530 self.send_document(describe(obj), html.document(obj, path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001531 else:
1532 self.send_document(path,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001533'no Python documentation found for %s' % repr(path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001534 else:
1535 heading = html.heading(
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001536'<big><big><strong>Python: Index of Modules</strong></big></big>',
1537'#ffffff', '#7799ee')
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001538 def bltinlink(name):
1539 return '<a href="%s.html">%s</a>' % (name, name)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001540 names = filter(lambda x: x != '__main__',
1541 sys.builtin_module_names)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001542 contents = html.multicolumn(names, bltinlink)
1543 indices = ['<p>' + html.bigsection(
1544 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1545
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001546 seen = {}
1547 for dir in pathdirs():
1548 indices.append(html.index(dir, seen))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001549 contents = heading + join(indices) + '''<p align=right>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001550<small><small><font color="#909090" face="helvetica, arial"><strong>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001551pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font></small></small>'''
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001552 self.send_document('Index of Modules', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001553
1554 def log_message(self, *args): pass
1555
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001556 class DocServer(BaseHTTPServer.HTTPServer):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001557 def __init__(self, port, callback):
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001558 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001559 self.address = ('', port)
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001560 self.url = 'http://%s:%d/' % (host, port)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001561 self.callback = callback
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001562 self.base.__init__(self, self.address, self.handler)
1563
1564 def serve_until_quit(self):
1565 import select
1566 self.quit = 0
1567 while not self.quit:
1568 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1569 if rd: self.handle_request()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001570
1571 def server_activate(self):
1572 self.base.server_activate(self)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001573 if self.callback: self.callback(self)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001574
1575 DocServer.base = BaseHTTPServer.HTTPServer
1576 DocServer.handler = DocHandler
1577 DocHandler.MessageClass = Message
1578 try:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001579 try:
1580 DocServer(port, callback).serve_until_quit()
1581 except (KeyboardInterrupt, select.error):
1582 pass
1583 finally:
Ka-Ping Yee66246962001-04-12 11:59:50 +00001584 if completer: completer()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001585
1586# ----------------------------------------------------- graphical interface
1587
1588def gui():
1589 """Graphical interface (starts web server and pops up a control window)."""
1590 class GUI:
1591 def __init__(self, window, port=7464):
1592 self.window = window
1593 self.server = None
1594 self.scanner = None
1595
1596 import Tkinter
1597 self.server_frm = Tkinter.Frame(window)
1598 self.title_lbl = Tkinter.Label(self.server_frm,
1599 text='Starting server...\n ')
1600 self.open_btn = Tkinter.Button(self.server_frm,
1601 text='open browser', command=self.open, state='disabled')
1602 self.quit_btn = Tkinter.Button(self.server_frm,
1603 text='quit serving', command=self.quit, state='disabled')
1604
1605 self.search_frm = Tkinter.Frame(window)
1606 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
1607 self.search_ent = Tkinter.Entry(self.search_frm)
1608 self.search_ent.bind('<Return>', self.search)
1609 self.stop_btn = Tkinter.Button(self.search_frm,
1610 text='stop', pady=0, command=self.stop, state='disabled')
1611 if sys.platform == 'win32':
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001612 # Trying to hide and show this button crashes under Windows.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001613 self.stop_btn.pack(side='right')
1614
1615 self.window.title('pydoc')
1616 self.window.protocol('WM_DELETE_WINDOW', self.quit)
1617 self.title_lbl.pack(side='top', fill='x')
1618 self.open_btn.pack(side='left', fill='x', expand=1)
1619 self.quit_btn.pack(side='right', fill='x', expand=1)
1620 self.server_frm.pack(side='top', fill='x')
1621
1622 self.search_lbl.pack(side='left')
1623 self.search_ent.pack(side='right', fill='x', expand=1)
1624 self.search_frm.pack(side='top', fill='x')
1625 self.search_ent.focus_set()
1626
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001627 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001628 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001629 self.result_lst.bind('<Button-1>', self.select)
1630 self.result_lst.bind('<Double-Button-1>', self.goto)
1631 self.result_scr = Tkinter.Scrollbar(window,
1632 orient='vertical', command=self.result_lst.yview)
1633 self.result_lst.config(yscrollcommand=self.result_scr.set)
1634
1635 self.result_frm = Tkinter.Frame(window)
1636 self.goto_btn = Tkinter.Button(self.result_frm,
1637 text='go to selected', command=self.goto)
1638 self.hide_btn = Tkinter.Button(self.result_frm,
1639 text='hide results', command=self.hide)
1640 self.goto_btn.pack(side='left', fill='x', expand=1)
1641 self.hide_btn.pack(side='right', fill='x', expand=1)
1642
1643 self.window.update()
1644 self.minwidth = self.window.winfo_width()
1645 self.minheight = self.window.winfo_height()
1646 self.bigminheight = (self.server_frm.winfo_reqheight() +
1647 self.search_frm.winfo_reqheight() +
1648 self.result_lst.winfo_reqheight() +
1649 self.result_frm.winfo_reqheight())
1650 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
1651 self.expanded = 0
1652 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1653 self.window.wm_minsize(self.minwidth, self.minheight)
1654
1655 import threading
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001656 threading.Thread(
1657 target=serve, args=(port, self.ready, self.quit)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001658
1659 def ready(self, server):
1660 self.server = server
1661 self.title_lbl.config(
1662 text='Python documentation server at\n' + server.url)
1663 self.open_btn.config(state='normal')
1664 self.quit_btn.config(state='normal')
1665
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001666 def open(self, event=None, url=None):
1667 url = url or self.server.url
1668 try:
1669 import webbrowser
1670 webbrowser.open(url)
1671 except ImportError: # pre-webbrowser.py compatibility
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001672 if sys.platform == 'win32':
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001673 os.system('start "%s"' % url)
1674 elif sys.platform == 'mac':
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001675 try: import ic
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001676 except ImportError: pass
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001677 else: ic.launchurl(url)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001678 else:
1679 rc = os.system('netscape -remote "openURL(%s)" &' % url)
1680 if rc: os.system('netscape "%s" &' % url)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001681
1682 def quit(self, event=None):
1683 if self.server:
1684 self.server.quit = 1
1685 self.window.quit()
1686
1687 def search(self, event=None):
1688 key = self.search_ent.get()
1689 self.stop_btn.pack(side='right')
1690 self.stop_btn.config(state='normal')
1691 self.search_lbl.config(text='Searching for "%s"...' % key)
1692 self.search_ent.forget()
1693 self.search_lbl.pack(side='left')
1694 self.result_lst.delete(0, 'end')
1695 self.goto_btn.config(state='disabled')
1696 self.expand()
1697
1698 import threading
1699 if self.scanner:
1700 self.scanner.quit = 1
1701 self.scanner = ModuleScanner()
1702 threading.Thread(target=self.scanner.run,
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +00001703 args=(self.update, key, self.done)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001704
1705 def update(self, path, modname, desc):
1706 if modname[-9:] == '.__init__':
1707 modname = modname[:-9] + ' (package)'
1708 self.result_lst.insert('end',
1709 modname + ' - ' + (desc or '(no description)'))
1710
1711 def stop(self, event=None):
1712 if self.scanner:
1713 self.scanner.quit = 1
1714 self.scanner = None
1715
1716 def done(self):
1717 self.scanner = None
1718 self.search_lbl.config(text='Search for')
1719 self.search_lbl.pack(side='left')
1720 self.search_ent.pack(side='right', fill='x', expand=1)
1721 if sys.platform != 'win32': self.stop_btn.forget()
1722 self.stop_btn.config(state='disabled')
1723
1724 def select(self, event=None):
1725 self.goto_btn.config(state='normal')
1726
1727 def goto(self, event=None):
1728 selection = self.result_lst.curselection()
1729 if selection:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001730 modname = split(self.result_lst.get(selection[0]))[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001731 self.open(url=self.server.url + modname + '.html')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001732
1733 def collapse(self):
1734 if not self.expanded: return
1735 self.result_frm.forget()
1736 self.result_scr.forget()
1737 self.result_lst.forget()
1738 self.bigwidth = self.window.winfo_width()
1739 self.bigheight = self.window.winfo_height()
1740 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1741 self.window.wm_minsize(self.minwidth, self.minheight)
1742 self.expanded = 0
1743
1744 def expand(self):
1745 if self.expanded: return
1746 self.result_frm.pack(side='bottom', fill='x')
1747 self.result_scr.pack(side='right', fill='y')
1748 self.result_lst.pack(side='top', fill='both', expand=1)
1749 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
1750 self.window.wm_minsize(self.minwidth, self.bigminheight)
1751 self.expanded = 1
1752
1753 def hide(self, event=None):
1754 self.stop()
1755 self.collapse()
1756
1757 import Tkinter
1758 try:
1759 gui = GUI(Tkinter.Tk())
1760 Tkinter.mainloop()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001761 except KeyboardInterrupt:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001762 pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001763
1764# -------------------------------------------------- command-line interface
1765
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001766def ispath(x):
1767 return type(x) is types.StringType and find(x, os.sep) >= 0
1768
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001769def cli():
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001770 """Command-line interface (looks at sys.argv to decide what to do)."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001771 import getopt
1772 class BadUsage: pass
1773
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001774 # Scripts don't get the current directory in their path by default.
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00001775 scriptdir = os.path.dirname(sys.argv[0])
1776 if scriptdir in sys.path:
1777 sys.path.remove(scriptdir)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001778 sys.path.insert(0, '.')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001779
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001780 try:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001781 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001782 writing = 0
1783
1784 for opt, val in opts:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001785 if opt == '-g':
1786 gui()
1787 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001788 if opt == '-k':
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001789 apropos(val)
1790 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001791 if opt == '-p':
1792 try:
1793 port = int(val)
1794 except ValueError:
1795 raise BadUsage
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001796 def ready(server):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001797 print 'pydoc server ready at %s' % server.url
1798 def stopped():
1799 print 'pydoc server stopped'
1800 serve(port, ready, stopped)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001801 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001802 if opt == '-w':
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001803 writing = 1
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001804
1805 if not args: raise BadUsage
1806 for arg in args:
1807 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001808 if ispath(arg) and os.path.isfile(arg):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001809 arg = importfile(arg)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001810 if writing:
1811 if ispath(arg) and os.path.isdir(arg):
1812 writedocs(arg)
1813 else:
1814 writedoc(arg)
1815 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001816 doc(arg)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001817 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001818 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001819
1820 except (getopt.error, BadUsage):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001821 cmd = sys.argv[0]
1822 print """pydoc - the Python documentation tool
1823
1824%s <name> ...
1825 Show text documentation on something. <name> may be the name of a
1826 function, module, or package, or a dotted reference to a class or
1827 function within a module or module in a package. If <name> contains
1828 a '%s', it is used as the path to a Python source file to document.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001829
1830%s -k <keyword>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001831 Search for a keyword in the synopsis lines of all available modules.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001832
1833%s -p <port>
1834 Start an HTTP server on the given port on the local machine.
1835
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001836%s -g
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001837 Pop up a graphical interface for finding and serving documentation.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001838
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001839%s -w <name> ...
1840 Write out the HTML documentation for a module to a file in the current
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001841 directory. If <name> contains a '%s', it is treated as a filename; if
1842 it names a directory, documentation is written for all the contents.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001843""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001844
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001845if __name__ == '__main__': cli()