blob: e5f7d800cbfa150ec1ab6946e24eb50e7dd2565a [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
Ka-Ping Yeedd175342001-02-27 14:43:46 +000056def pathdirs():
57 """Convert sys.path into a list of absolute, existing, unique paths."""
58 dirs = []
Ka-Ping Yee1d384632001-03-01 00:24:32 +000059 normdirs = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +000060 for dir in sys.path:
61 dir = os.path.abspath(dir or '.')
Ka-Ping Yee1d384632001-03-01 00:24:32 +000062 normdir = os.path.normcase(dir)
63 if normdir not in normdirs and os.path.isdir(dir):
Ka-Ping Yeedd175342001-02-27 14:43:46 +000064 dirs.append(dir)
Ka-Ping Yee1d384632001-03-01 00:24:32 +000065 normdirs.append(normdir)
Ka-Ping Yeedd175342001-02-27 14:43:46 +000066 return dirs
67
68def getdoc(object):
69 """Get the doc string or comments for an object."""
Ka-Ping Yee3bda8792001-03-23 13:17:50 +000070 result = inspect.getdoc(object) or inspect.getcomments(object)
Ka-Ping Yee239432a2001-03-02 02:45:08 +000071 return result and re.sub('^ *\n', '', rstrip(result)) or ''
Ka-Ping Yeedd175342001-02-27 14:43:46 +000072
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +000073def splitdoc(doc):
74 """Split a doc string into a synopsis line (if any) and the rest."""
75 lines = split(strip(doc), '\n')
76 if len(lines) == 1:
77 return lines[0], ''
78 elif len(lines) >= 2 and not rstrip(lines[1]):
79 return lines[0], join(lines[2:], '\n')
80 return '', join(lines, '\n')
81
Ka-Ping Yeedd175342001-02-27 14:43:46 +000082def classname(object, modname):
83 """Get a class name and qualify it with a module name if necessary."""
84 name = object.__name__
85 if object.__module__ != modname:
86 name = object.__module__ + '.' + name
87 return name
88
Ka-Ping Yeedec96e92001-04-13 09:55:49 +000089def isdata(object):
90 """Check if an object is of a type that probably means it's data."""
91 return not (inspect.ismodule(object) or inspect.isclass(object) or
92 inspect.isroutine(object) or inspect.isframe(object) or
93 inspect.istraceback(object) or inspect.iscode(object))
Ka-Ping Yeedd175342001-02-27 14:43:46 +000094
95def replace(text, *pairs):
96 """Do a series of global replacements on a string."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +000097 while pairs:
98 text = join(split(text, pairs[0]), pairs[1])
99 pairs = pairs[2:]
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000100 return text
101
102def cram(text, maxlen):
103 """Omit part of a string if needed to make it fit in a maximum length."""
104 if len(text) > maxlen:
105 pre = max(0, (maxlen-3)/2)
106 post = max(0, maxlen-3-pre)
107 return text[:pre] + '...' + text[len(text)-post:]
108 return text
109
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000110def stripid(text):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000111 """Remove the hexadecimal id from a Python object representation."""
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000112 # The behaviour of %p is implementation-dependent; we check two cases.
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000113 for pattern in [' at 0x[0-9a-f]{6,}>$', ' at [0-9A-F]{8,}>$']:
114 if re.search(pattern, repr(Exception)):
115 return re.sub(pattern, '>', text)
116 return text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000117
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000118def allmethods(cl):
119 methods = {}
120 for key, value in inspect.getmembers(cl, inspect.ismethod):
121 methods[key] = 1
122 for base in cl.__bases__:
123 methods.update(allmethods(base)) # all your base are belong to us
124 for key in methods.keys():
125 methods[key] = getattr(cl, key)
126 return methods
127
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000128# ----------------------------------------------------- module manipulation
129
130def ispackage(path):
131 """Guess whether a path refers to a package directory."""
132 if os.path.isdir(path):
133 for ext in ['.py', '.pyc', '.pyo']:
134 if os.path.isfile(os.path.join(path, '__init__' + ext)):
135 return 1
136
137def synopsis(filename, cache={}):
138 """Get the one-line summary out of a module file."""
139 mtime = os.stat(filename)[stat.ST_MTIME]
140 lastupdate, result = cache.get(filename, (0, None))
141 if lastupdate < mtime:
142 info = inspect.getmoduleinfo(filename)
143 file = open(filename)
144 if info and 'b' in info[2]: # binary modules have to be imported
145 try: module = imp.load_module('__temp__', file, filename, info[1:])
146 except: return None
147 result = split(module.__doc__ or '', '\n')[0]
148 del sys.modules['__temp__']
149 else: # text modules can be directly examined
150 line = file.readline()
151 while line[:1] == '#' or not strip(line):
152 line = file.readline()
153 if not line: break
154 line = strip(line)
155 if line[:4] == 'r"""': line = line[1:]
156 if line[:3] == '"""':
157 line = line[3:]
158 if line[-1:] == '\\': line = line[:-1]
159 while not strip(line):
160 line = file.readline()
161 if not line: break
162 result = strip(split(line, '"""')[0])
163 else: result = None
164 file.close()
165 cache[filename] = (mtime, result)
166 return result
167
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000168class ErrorDuringImport(Exception):
169 """Errors that occurred while trying to import something to document it."""
170 def __init__(self, filename, (exc, value, tb)):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000171 self.filename = filename
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000172 self.exc = exc
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000173 self.value = value
174 self.tb = tb
175
176 def __str__(self):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000177 exc = self.exc
178 if type(exc) is types.ClassType:
179 exc = exc.__name__
180 return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000181
182def importfile(path):
183 """Import a Python source file or compiled file given its path."""
184 magic = imp.get_magic()
185 file = open(path, 'r')
186 if file.read(len(magic)) == magic:
187 kind = imp.PY_COMPILED
188 else:
189 kind = imp.PY_SOURCE
190 file.close()
191 filename = os.path.basename(path)
192 name, ext = os.path.splitext(filename)
193 file = open(path, 'r')
194 try:
195 module = imp.load_module(name, file, path, (ext, 'r', kind))
196 except:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000197 raise ErrorDuringImport(path, sys.exc_info())
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000198 file.close()
199 return module
200
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000201def safeimport(path, forceload=0, cache={}):
202 """Import a module; handle errors; return None if the module isn't found.
203
204 If the module *is* found but an exception occurs, it's wrapped in an
205 ErrorDuringImport exception and reraised. Unlike __import__, if a
206 package path is specified, the module at the end of the path is returned,
207 not the package at the beginning. If the optional 'forceload' argument
208 is 1, we reload the module from disk (unless it's a dynamic extension)."""
209 if forceload and sys.modules.has_key(path):
210 # This is the only way to be sure. Checking the mtime of the file
211 # isn't good enough (e.g. what if the module contains a class that
212 # inherits from another module that has changed?).
213 if path not in sys.builtin_module_names:
214 # Python never loads a dynamic extension a second time from the
215 # same path, even if the file is changed or missing. Deleting
216 # the entry in sys.modules doesn't help for dynamic extensions,
217 # so we're not even going to try to keep them up to date.
218 info = inspect.getmoduleinfo(sys.modules[path].__file__)
219 if info[3] != imp.C_EXTENSION:
220 cache[path] = sys.modules[path] # prevent module from clearing
221 del sys.modules[path]
222 try:
223 module = __import__(path)
224 except:
225 # Did the error occur before or after the module was found?
226 (exc, value, tb) = info = sys.exc_info()
227 if sys.modules.has_key(path):
228 # An error occured while executing the imported module.
229 raise ErrorDuringImport(sys.modules[path].__file__, info)
230 elif exc is SyntaxError:
231 # A SyntaxError occurred before we could execute the module.
232 raise ErrorDuringImport(value.filename, info)
233 elif exc is ImportError and \
234 split(lower(str(value)))[:2] == ['no', 'module']:
235 # The module was not found.
236 return None
237 else:
238 # Some other error occurred during the importing process.
239 raise ErrorDuringImport(path, sys.exc_info())
240 for part in split(path, '.')[1:]:
241 try: module = getattr(module, part)
242 except AttributeError: return None
243 return module
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000244
245# ---------------------------------------------------- formatter base class
246
247class Doc:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000248 def document(self, object, name=None, *args):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000249 """Generate documentation for an object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000250 args = (object, name) + args
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000251 if inspect.ismodule(object): return apply(self.docmodule, args)
252 if inspect.isclass(object): return apply(self.docclass, args)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000253 if inspect.isroutine(object): return apply(self.docroutine, args)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000254 return apply(self.docother, args)
255
256 def fail(self, object, name=None, *args):
257 """Raise an exception for unimplemented types."""
258 message = "don't know how to document object%s of type %s" % (
259 name and ' ' + repr(name), type(object).__name__)
260 raise TypeError, message
261
262 docmodule = docclass = docroutine = docother = fail
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000263
264# -------------------------------------------- HTML documentation generator
265
266class HTMLRepr(Repr):
267 """Class for safely making an HTML representation of a Python object."""
268 def __init__(self):
269 Repr.__init__(self)
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000270 self.maxlist = self.maxtuple = 20
271 self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000272 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000273
274 def escape(self, text):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000275 return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000276
277 def repr(self, object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000278 return Repr.repr(self, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000279
280 def repr1(self, x, level):
281 methodname = 'repr_' + join(split(type(x).__name__), '_')
282 if hasattr(self, methodname):
283 return getattr(self, methodname)(x, level)
284 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000285 return self.escape(cram(stripid(repr(x)), self.maxother))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000286
287 def repr_string(self, x, level):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000288 test = cram(x, self.maxstring)
289 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000290 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000291 # Backslashes are only literal in the string and are never
292 # needed to make any special characters, so show a raw string.
293 return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000294 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000295 r'<font color="#c040c0">\1</font>',
296 self.escape(testrepr))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000297
298 def repr_instance(self, x, level):
299 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000300 return self.escape(cram(stripid(repr(x)), self.maxstring))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000301 except:
302 return self.escape('<%s instance>' % x.__class__.__name__)
303
304 repr_unicode = repr_string
305
306class HTMLDoc(Doc):
307 """Formatter class for HTML documentation."""
308
309 # ------------------------------------------- HTML formatting utilities
310
311 _repr_instance = HTMLRepr()
312 repr = _repr_instance.repr
313 escape = _repr_instance.escape
314
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000315 def page(self, title, contents):
316 """Format an HTML page."""
317 return '''
318<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000319<html><head><title>Python: %s</title>
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000320<style type="text/css"><!--
Ka-Ping Yee59685c52001-04-13 12:11:19 +0000321TT { font-family: lucida typewriter, lucida console, courier }
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000322--></style></head><body bgcolor="#f0f0f8">
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000323%s
324</body></html>''' % (title, contents)
325
326 def heading(self, title, fgcol, bgcol, extras=''):
327 """Format a page heading."""
328 return '''
329<table width="100%%" cellspacing=0 cellpadding=2 border=0>
330<tr bgcolor="%s">
331<td valign=bottom><small>&nbsp;<br></small
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000332><font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000333><td align=right valign=bottom
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000334><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000335 ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
336
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000337 def section(self, title, fgcol, bgcol, contents, width=10,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000338 prelude='', marginalia=None, gap='&nbsp;&nbsp;'):
339 """Format a section with a heading."""
340 if marginalia is None:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000341 marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000342 result = '''
343<p><table width="100%%" cellspacing=0 cellpadding=2 border=0>
344<tr bgcolor="%s">
345<td colspan=3 valign=bottom><small><small>&nbsp;<br></small></small
346><font color="%s" face="helvetica, arial">%s</font></td></tr>
347 ''' % (bgcol, fgcol, title)
348 if prelude:
349 result = result + '''
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000350<tr bgcolor="%s"><td rowspan=2>%s</td>
351<td colspan=2>%s</td></tr>
352<tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
353 else:
354 result = result + '''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000355<tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000356
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000357 return result + '\n<td width="100%%">%s</td></tr></table>' % contents
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000358
359 def bigsection(self, title, *args):
360 """Format a section with a big heading."""
361 title = '<big><strong>%s</strong></big>' % title
362 return apply(self.section, (title,) + args)
363
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000364 def preformat(self, text):
365 """Format literal preformatted text."""
366 text = self.escape(expandtabs(text))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000367 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
368 ' ', '&nbsp;', '\n', '<br>\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000369
370 def multicolumn(self, list, format, cols=4):
371 """Format a list of items into a multi-column list."""
372 result = ''
373 rows = (len(list)+cols-1)/cols
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000374 for col in range(cols):
375 result = result + '<td width="%d%%" valign=top>' % (100/cols)
376 for i in range(rows*col, rows*col+rows):
377 if i < len(list):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000378 result = result + format(list[i]) + '<br>\n'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000379 result = result + '</td>'
380 return '<table width="100%%"><tr>%s</tr></table>' % result
381
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000382 def small(self, text): return '<small>%s</small>' % text
383 def grey(self, text): return '<font color="#909090">%s</font>' % text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000384
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000385 def namelink(self, name, *dicts):
386 """Make a link for an identifier, given name-to-URL mappings."""
387 for dict in dicts:
388 if dict.has_key(name):
389 return '<a href="%s">%s</a>' % (dict[name], name)
390 return name
391
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000392 def classlink(self, object, modname):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000393 """Make a link for a class."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000394 name, module = object.__name__, sys.modules.get(object.__module__)
395 if hasattr(module, name) and getattr(module, name) is object:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000396 return '<a href="%s.html#%s">%s</a>' % (
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000397 module.__name__, name, classname(object, modname))
398 return classname(object, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000399
400 def modulelink(self, object):
401 """Make a link for a module."""
402 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
403
404 def modpkglink(self, (name, path, ispackage, shadowed)):
405 """Make a link for a module or package to display in an index."""
406 if shadowed:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000407 return self.grey(name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000408 if path:
409 url = '%s.%s.html' % (path, name)
410 else:
411 url = '%s.html' % name
412 if ispackage:
413 text = '<strong>%s</strong>&nbsp;(package)' % name
414 else:
415 text = name
416 return '<a href="%s">%s</a>' % (url, text)
417
418 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
419 """Mark up some plain text, given a context of symbols to look for.
420 Each context dictionary maps object names to anchor names."""
421 escape = escape or self.escape
422 results = []
423 here = 0
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000424 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
425 r'RFC[- ]?(\d+)|'
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000426 r'PEP[- ]?(\d+)|'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000427 r'(self\.)?(\w+))\b')
428 while 1:
429 match = pattern.search(text, here)
430 if not match: break
431 start, end = match.span()
432 results.append(escape(text[here:start]))
433
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000434 all, scheme, rfc, pep, selfdot, name = match.groups()
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000435 if scheme:
436 results.append('<a href="%s">%s</a>' % (all, escape(all)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000437 elif rfc:
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000438 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
439 results.append('<a href="%s">%s</a>' % (url, escape(all)))
440 elif pep:
441 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000442 results.append('<a href="%s">%s</a>' % (url, escape(all)))
443 elif text[end:end+1] == '(':
444 results.append(self.namelink(name, methods, funcs, classes))
445 elif selfdot:
446 results.append('self.<strong>%s</strong>' % name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000447 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000448 results.append(self.namelink(name, classes))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000449 here = end
450 results.append(escape(text[here:]))
451 return join(results, '')
452
453 # ---------------------------------------------- type-specific routines
454
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000455 def formattree(self, tree, modname, parent=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000456 """Produce HTML for a class tree as given by inspect.getclasstree()."""
457 result = ''
458 for entry in tree:
459 if type(entry) is type(()):
460 c, bases = entry
461 result = result + '<dt><font face="helvetica, arial"><small>'
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000462 result = result + self.classlink(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000463 if bases and bases != (parent,):
464 parents = []
465 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000466 parents.append(self.classlink(base, modname))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000467 result = result + '(' + join(parents, ', ') + ')'
468 result = result + '\n</small></font></dt>'
469 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000470 result = result + '<dd>\n%s</dd>\n' % self.formattree(
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000471 entry, modname, c)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000472 return '<dl>\n%s</dl>\n' % result
473
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000474 def docmodule(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000475 """Produce HTML documentation for a module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000476 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000477 parts = split(name, '.')
478 links = []
479 for i in range(len(parts)-1):
480 links.append(
481 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
482 (join(parts[:i+1], '.'), parts[i]))
483 linkedname = join(links + parts[-1:], '.')
484 head = '<big><big><strong>%s</strong></big></big>' % linkedname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000485 try:
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000486 path = inspect.getabsfile(object)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000487 filelink = '<a href="file:%s">%s</a>' % (path, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000488 except TypeError:
489 filelink = '(built-in)'
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000490 info = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000491 if hasattr(object, '__version__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000492 version = str(object.__version__)
Ka-Ping Yee40c49912001-02-27 22:46:01 +0000493 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
494 version = strip(version[11:-1])
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000495 info.append('version %s' % self.escape(version))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000496 if hasattr(object, '__date__'):
497 info.append(self.escape(str(object.__date__)))
498 if info:
499 head = head + ' (%s)' % join(info, ', ')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000500 result = self.heading(
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000501 head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)
502
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000503 modules = inspect.getmembers(object, inspect.ismodule)
504
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000505 classes, cdict = [], {}
506 for key, value in inspect.getmembers(object, inspect.isclass):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000507 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000508 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000509 cdict[key] = cdict[value] = '#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000510 for key, value in classes:
511 for base in value.__bases__:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000512 key, modname = base.__name__, base.__module__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000513 module = sys.modules.get(modname)
514 if modname != name and module and hasattr(module, key):
515 if getattr(module, key) is base:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000516 if not cdict.has_key(key):
517 cdict[key] = cdict[base] = modname + '.html#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000518 funcs, fdict = [], {}
519 for key, value in inspect.getmembers(object, inspect.isroutine):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000520 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000521 funcs.append((key, value))
522 fdict[key] = '#-' + key
523 if inspect.isfunction(value): fdict[value] = fdict[key]
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000524 data = []
525 for key, value in inspect.getmembers(object, isdata):
526 if key not in ['__builtins__', '__doc__']:
527 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000528
529 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
530 doc = doc and '<tt>%s</tt>' % doc
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000531 result = result + '<p>%s</p>\n' % self.small(doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000532
533 if hasattr(object, '__path__'):
534 modpkgs = []
535 modnames = []
536 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000537 path = os.path.join(object.__path__[0], file)
538 modname = inspect.getmodulename(file)
539 if modname and modname not in modnames:
540 modpkgs.append((modname, name, 0, 0))
541 modnames.append(modname)
542 elif ispackage(path):
543 modpkgs.append((file, name, 1, 0))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000544 modpkgs.sort()
545 contents = self.multicolumn(modpkgs, self.modpkglink)
546 result = result + self.bigsection(
547 'Package Contents', '#ffffff', '#aa55cc', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000548 elif modules:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000549 contents = self.multicolumn(
550 modules, lambda (key, value), s=self: s.modulelink(value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000551 result = result + self.bigsection(
552 'Modules', '#fffff', '#aa55cc', contents)
553
554 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000555 classlist = map(lambda (key, value): value, classes)
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000556 contents = [
557 self.formattree(inspect.getclasstree(classlist, 1), name)]
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000558 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000559 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000560 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000561 'Classes', '#ffffff', '#ee77aa', join(contents))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000562 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000563 contents = []
564 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000565 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000566 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000567 'Functions', '#ffffff', '#eeaa77', join(contents))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000568 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000569 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000570 for key, value in data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000571 contents.append(self.document(value, key))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000572 result = result + self.bigsection(
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000573 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000574 if hasattr(object, '__author__'):
575 contents = self.markup(str(object.__author__), self.preformat)
576 result = result + self.bigsection(
577 'Author', '#ffffff', '#7799ee', contents)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000578 if hasattr(object, '__credits__'):
579 contents = self.markup(str(object.__credits__), self.preformat)
580 result = result + self.bigsection(
581 'Credits', '#ffffff', '#7799ee', contents)
582
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000583 return result
584
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000585 def docclass(self, object, name=None, mod=None, funcs={}, classes={}):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000586 """Produce HTML documentation for a class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000587 realname = object.__name__
588 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000589 bases = object.__bases__
590 contents = ''
591
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000592 methods, mdict = allmethods(object).items(), {}
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000593 methods.sort()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000594 for key, value in methods:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000595 mdict[key] = mdict[value] = '#' + name + '-' + key
596 for key, value in methods:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000597 contents = contents + self.document(
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000598 value, key, mod, funcs, classes, mdict, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000599
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000600 if name == realname:
601 title = '<a name="%s">class <strong>%s</strong></a>' % (
602 name, realname)
603 else:
604 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
605 name, name, realname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000606 if bases:
607 parents = []
608 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000609 parents.append(self.classlink(base, object.__module__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000610 title = title + '(%s)' % join(parents, ', ')
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000611 doc = self.markup(
612 getdoc(object), self.preformat, funcs, classes, mdict)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000613 doc = self.small(doc and '<tt>%s<br>&nbsp;</tt>' % doc or
614 self.small('&nbsp;'))
615 return self.section(title, '#000000', '#ffc8d8', contents, 5, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000616
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000617 def formatvalue(self, object):
618 """Format an argument default value as text."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000619 return self.small(self.grey('=' + self.repr(object)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000620
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000621 def docroutine(self, object, name=None, mod=None,
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000622 funcs={}, classes={}, methods={}, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000623 """Produce HTML documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000624 realname = object.__name__
625 name = name or realname
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000626 anchor = (cl and cl.__name__ or '') + '-' + name
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000627 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000628 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000629 if inspect.ismethod(object):
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +0000630 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000631 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000632 if imclass is not cl:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000633 note = ' from ' + self.classlink(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000634 skipdocs = 1
635 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000636 if object.im_self:
637 note = ' method of %s instance' % self.classlink(
638 object.im_self.__class__, mod)
639 else:
640 note = ' unbound %s method' % self.classlink(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000641 object = object.im_func
642
643 if name == realname:
644 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
645 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000646 if (cl and cl.__dict__.has_key(realname) and
647 cl.__dict__[realname] is object):
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000648 reallink = '<a href="#%s">%s</a>' % (
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000649 cl.__name__ + '-' + realname, realname)
650 skipdocs = 1
651 else:
652 reallink = realname
653 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
654 anchor, name, reallink)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000655 if inspect.isbuiltin(object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000656 argspec = '(...)'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000657 else:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000658 args, varargs, varkw, defaults = inspect.getargspec(object)
659 argspec = inspect.formatargspec(
660 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000661 if realname == '<lambda>':
662 decl = '<em>lambda</em>'
663 argspec = argspec[1:-1] # remove parentheses
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000664
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000665 decl = title + argspec + (note and self.small(self.grey(
666 '<font face="helvetica, arial">%s</font>' % note)))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000667
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000668 if skipdocs:
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000669 return '<dl><dt>%s</dl>\n' % decl
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000670 else:
671 doc = self.markup(
672 getdoc(object), self.preformat, funcs, classes, methods)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000673 doc = doc and '<dd>' + self.small('<tt>%s</tt>' % doc)
674 return '<dl><dt>%s%s</dl>\n' % (decl, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000675
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000676 def docother(self, object, name=None, mod=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000677 """Produce HTML documentation for a data object."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000678 lhs = name and '<strong>%s</strong> = ' % name or ''
679 return lhs + self.repr(object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000680
681 def index(self, dir, shadowed=None):
682 """Generate an HTML index for a directory of modules."""
683 modpkgs = []
684 if shadowed is None: shadowed = {}
685 seen = {}
686 files = os.listdir(dir)
687
688 def found(name, ispackage,
689 modpkgs=modpkgs, shadowed=shadowed, seen=seen):
690 if not seen.has_key(name):
691 modpkgs.append((name, '', ispackage, shadowed.has_key(name)))
692 seen[name] = 1
693 shadowed[name] = 1
694
695 # Package spam/__init__.py takes precedence over module spam.py.
696 for file in files:
697 path = os.path.join(dir, file)
698 if ispackage(path): found(file, 1)
699 for file in files:
700 path = os.path.join(dir, file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000701 if os.path.isfile(path):
702 modname = inspect.getmodulename(file)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000703 if modname: found(modname, 0)
704
705 modpkgs.sort()
706 contents = self.multicolumn(modpkgs, self.modpkglink)
707 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
708
709# -------------------------------------------- text documentation generator
710
711class TextRepr(Repr):
712 """Class for safely making a text representation of a Python object."""
713 def __init__(self):
714 Repr.__init__(self)
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000715 self.maxlist = self.maxtuple = 20
716 self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000717 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000718
719 def repr1(self, x, level):
720 methodname = 'repr_' + join(split(type(x).__name__), '_')
721 if hasattr(self, methodname):
722 return getattr(self, methodname)(x, level)
723 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000724 return cram(stripid(repr(x)), self.maxother)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000725
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000726 def repr_string(self, x, level):
727 test = cram(x, self.maxstring)
728 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000729 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000730 # Backslashes are only literal in the string and are never
731 # needed to make any special characters, so show a raw string.
732 return 'r' + testrepr[0] + test + testrepr[0]
733 return testrepr
734
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000735 def repr_instance(self, x, level):
736 try:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000737 return cram(stripid(repr(x)), self.maxstring)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000738 except:
739 return '<%s instance>' % x.__class__.__name__
740
741class TextDoc(Doc):
742 """Formatter class for text documentation."""
743
744 # ------------------------------------------- text formatting utilities
745
746 _repr_instance = TextRepr()
747 repr = _repr_instance.repr
748
749 def bold(self, text):
750 """Format a string in bold by overstriking."""
751 return join(map(lambda ch: ch + '\b' + ch, text), '')
752
753 def indent(self, text, prefix=' '):
754 """Indent text by prepending a given prefix to each line."""
755 if not text: return ''
756 lines = split(text, '\n')
757 lines = map(lambda line, prefix=prefix: prefix + line, lines)
758 if lines: lines[-1] = rstrip(lines[-1])
759 return join(lines, '\n')
760
761 def section(self, title, contents):
762 """Format a section with a given heading."""
763 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
764
765 # ---------------------------------------------- type-specific routines
766
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000767 def formattree(self, tree, modname, parent=None, prefix=''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000768 """Render in text a class tree as returned by inspect.getclasstree()."""
769 result = ''
770 for entry in tree:
771 if type(entry) is type(()):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000772 c, bases = entry
773 result = result + prefix + classname(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000774 if bases and bases != (parent,):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000775 parents = map(lambda c, m=modname: classname(c, m), bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000776 result = result + '(%s)' % join(parents, ', ')
777 result = result + '\n'
778 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000779 result = result + self.formattree(
780 entry, modname, c, prefix + ' ')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000781 return result
782
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000783 def docmodule(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000784 """Produce text documentation for a given module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000785 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000786 synop, desc = splitdoc(getdoc(object))
787 result = self.section('NAME', name + (synop and ' - ' + synop))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000788
789 try:
790 file = inspect.getabsfile(object)
791 except TypeError:
792 file = '(built-in)'
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000793 result = result + self.section('FILE', file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000794 if desc:
795 result = result + self.section('DESCRIPTION', desc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000796
797 classes = []
798 for key, value in inspect.getmembers(object, inspect.isclass):
799 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000800 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000801 funcs = []
802 for key, value in inspect.getmembers(object, inspect.isroutine):
803 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000804 funcs.append((key, value))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000805 data = []
806 for key, value in inspect.getmembers(object, isdata):
807 if key not in ['__builtins__', '__doc__']:
808 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000809
810 if hasattr(object, '__path__'):
811 modpkgs = []
812 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000813 path = os.path.join(object.__path__[0], file)
814 modname = inspect.getmodulename(file)
815 if modname and modname not in modpkgs:
816 modpkgs.append(modname)
817 elif ispackage(path):
818 modpkgs.append(file + ' (package)')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000819 modpkgs.sort()
820 result = result + self.section(
821 'PACKAGE CONTENTS', join(modpkgs, '\n'))
822
823 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000824 classlist = map(lambda (key, value): value, classes)
825 contents = [self.formattree(
826 inspect.getclasstree(classlist, 1), name)]
827 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000828 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000829 result = result + self.section('CLASSES', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000830
831 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000832 contents = []
833 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000834 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000835 result = result + self.section('FUNCTIONS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000836
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000837 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000838 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000839 for key, value in data:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000840 contents.append(self.docother(value, key, name, 70))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000841 result = result + self.section('DATA', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000842
843 if hasattr(object, '__version__'):
844 version = str(object.__version__)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000845 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
846 version = strip(version[11:-1])
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000847 result = result + self.section('VERSION', version)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000848 if hasattr(object, '__date__'):
849 result = result + self.section('DATE', str(object.__date__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000850 if hasattr(object, '__author__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000851 result = result + self.section('AUTHOR', str(object.__author__))
852 if hasattr(object, '__credits__'):
853 result = result + self.section('CREDITS', str(object.__credits__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000854 return result
855
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000856 def docclass(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000857 """Produce text documentation for a given class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000858 realname = object.__name__
859 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000860 bases = object.__bases__
861
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000862 if name == realname:
863 title = 'class ' + self.bold(realname)
864 else:
865 title = self.bold(name) + ' = class ' + realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000866 if bases:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000867 def makename(c, m=object.__module__): return classname(c, m)
868 parents = map(makename, bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000869 title = title + '(%s)' % join(parents, ', ')
870
871 doc = getdoc(object)
872 contents = doc and doc + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000873 methods = allmethods(object).items()
874 methods.sort()
875 for key, value in methods:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000876 contents = contents + '\n' + self.document(value, key, mod, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000877
878 if not contents: return title + '\n'
879 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
880
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000881 def formatvalue(self, object):
882 """Format an argument default value as text."""
883 return '=' + self.repr(object)
884
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000885 def docroutine(self, object, name=None, mod=None, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000886 """Produce text documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000887 realname = object.__name__
888 name = name or realname
889 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000890 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000891 if inspect.ismethod(object):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000892 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000893 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000894 if imclass is not cl:
895 note = ' from ' + classname(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000896 skipdocs = 1
897 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000898 if object.im_self:
899 note = ' method of %s instance' % classname(
900 object.im_self.__class__, mod)
901 else:
902 note = ' unbound %s method' % classname(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000903 object = object.im_func
904
905 if name == realname:
906 title = self.bold(realname)
907 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000908 if (cl and cl.__dict__.has_key(realname) and
909 cl.__dict__[realname] is object):
910 skipdocs = 1
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000911 title = self.bold(name) + ' = ' + realname
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000912 if inspect.isbuiltin(object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000913 argspec = '(...)'
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000914 else:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000915 args, varargs, varkw, defaults = inspect.getargspec(object)
916 argspec = inspect.formatargspec(
917 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000918 if realname == '<lambda>':
919 title = 'lambda'
920 argspec = argspec[1:-1] # remove parentheses
921 decl = title + argspec + note
922
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000923 if skipdocs:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000924 return decl + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000925 else:
926 doc = getdoc(object) or ''
927 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000928
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000929 def docother(self, object, name=None, mod=None, maxlen=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000930 """Produce text documentation for a data object."""
931 repr = self.repr(object)
932 if maxlen:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000933 line = (name and name + ' = ' or '') + repr
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000934 chop = maxlen - len(line)
935 if chop < 0: repr = repr[:chop] + '...'
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000936 line = (name and self.bold(name) + ' = ' or '') + repr
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000937 return line
938
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000939# --------------------------------------------------------- user interfaces
940
941def pager(text):
942 """The first time this is called, determine what kind of pager to use."""
943 global pager
944 pager = getpager()
945 pager(text)
946
947def getpager():
948 """Decide what method to use for paging through text."""
949 if type(sys.stdout) is not types.FileType:
950 return plainpager
951 if not sys.stdin.isatty() or not sys.stdout.isatty():
952 return plainpager
953 if os.environ.has_key('PAGER'):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000954 if sys.platform == 'win32': # pipes completely broken in Windows
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000955 return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
956 elif os.environ.get('TERM') in ['dumb', 'emacs']:
957 return lambda text: pipepager(plain(text), os.environ['PAGER'])
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000958 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000959 return lambda text: pipepager(text, os.environ['PAGER'])
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +0000960 if sys.platform == 'win32':
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000961 return lambda text: tempfilepager(plain(text), 'more <')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000962 if hasattr(os, 'system') and os.system('less 2>/dev/null') == 0:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000963 return lambda text: pipepager(text, 'less')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000964
965 import tempfile
966 filename = tempfile.mktemp()
967 open(filename, 'w').close()
968 try:
969 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
970 return lambda text: pipepager(text, 'more')
971 else:
972 return ttypager
973 finally:
974 os.unlink(filename)
975
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000976def plain(text):
977 """Remove boldface formatting from text."""
978 return re.sub('.\b', '', text)
979
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000980def pipepager(text, cmd):
981 """Page through text by feeding it to another program."""
982 pipe = os.popen(cmd, 'w')
983 try:
984 pipe.write(text)
985 pipe.close()
986 except IOError:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +0000987 pass # Ignore broken pipes caused by quitting the pager program.
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000988
989def tempfilepager(text, cmd):
990 """Page through text by invoking a program on a temporary file."""
991 import tempfile
992 filename = tempfile.mktemp()
993 file = open(filename, 'w')
994 file.write(text)
995 file.close()
996 try:
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +0000997 os.system(cmd + ' ' + filename)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000998 finally:
999 os.unlink(filename)
1000
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001001def ttypager(text):
1002 """Page through text on a text terminal."""
1003 lines = split(plain(text), '\n')
1004 try:
1005 import tty
1006 fd = sys.stdin.fileno()
1007 old = tty.tcgetattr(fd)
1008 tty.setcbreak(fd)
1009 getchar = lambda: sys.stdin.read(1)
Ka-Ping Yee457aab22001-02-27 23:36:29 +00001010 except (ImportError, AttributeError):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001011 tty = None
1012 getchar = lambda: sys.stdin.readline()[:-1][:1]
1013
1014 try:
1015 r = inc = os.environ.get('LINES', 25) - 1
1016 sys.stdout.write(join(lines[:inc], '\n') + '\n')
1017 while lines[r:]:
1018 sys.stdout.write('-- more --')
1019 sys.stdout.flush()
1020 c = getchar()
1021
1022 if c in ['q', 'Q']:
1023 sys.stdout.write('\r \r')
1024 break
1025 elif c in ['\r', '\n']:
1026 sys.stdout.write('\r \r' + lines[r] + '\n')
1027 r = r + 1
1028 continue
1029 if c in ['b', 'B', '\x1b']:
1030 r = r - inc - inc
1031 if r < 0: r = 0
1032 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1033 r = r + inc
1034
1035 finally:
1036 if tty:
1037 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1038
1039def plainpager(text):
1040 """Simply print unformatted text. This is the ultimate fallback."""
1041 sys.stdout.write(plain(text))
1042
1043def describe(thing):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001044 """Produce a short description of the given thing."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001045 if inspect.ismodule(thing):
1046 if thing.__name__ in sys.builtin_module_names:
1047 return 'built-in module ' + thing.__name__
1048 if hasattr(thing, '__path__'):
1049 return 'package ' + thing.__name__
1050 else:
1051 return 'module ' + thing.__name__
1052 if inspect.isbuiltin(thing):
1053 return 'built-in function ' + thing.__name__
1054 if inspect.isclass(thing):
1055 return 'class ' + thing.__name__
1056 if inspect.isfunction(thing):
1057 return 'function ' + thing.__name__
1058 if inspect.ismethod(thing):
1059 return 'method ' + thing.__name__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001060 if type(thing) is types.InstanceType:
1061 return 'instance of ' + thing.__class__.__name__
1062 return type(thing).__name__
1063
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001064def locate(path, forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001065 """Locate an object by name or dotted path, importing as necessary."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001066 parts = split(path, '.')
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001067 module, n = None, 0
1068 while n < len(parts):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001069 nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001070 if nextmodule: module, n = nextmodule, n + 1
1071 else: break
1072 if module:
1073 object = module
1074 for part in parts[n:]:
1075 try: object = getattr(object, part)
1076 except AttributeError: return None
1077 return object
1078 else:
1079 import __builtin__
1080 if hasattr(__builtin__, path):
1081 return getattr(__builtin__, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001082
1083# --------------------------------------- interactive interpreter interface
1084
1085text = TextDoc()
1086html = HTMLDoc()
1087
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001088def doc(thing, title='Python Library Documentation: %s', forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001089 """Display text documentation, given an object or a path to an object."""
1090 suffix, name = '', None
1091 if type(thing) is type(''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001092 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001093 object = locate(thing, forceload)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001094 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001095 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001096 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001097 if not object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001098 print 'no Python documentation found for %s' % repr(thing)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001099 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001100 parts = split(thing, '.')
1101 if len(parts) > 1: suffix = ' in ' + join(parts[:-1], '.')
1102 name = parts[-1]
1103 thing = object
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001104
1105 desc = describe(thing)
1106 module = inspect.getmodule(thing)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001107 if not suffix and module and module is not thing:
1108 suffix = ' in module ' + module.__name__
Ka-Ping Yee66246962001-04-12 11:59:50 +00001109 pager(title % (desc + suffix) + '\n\n' + text.document(thing, name))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001110
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001111def writedoc(key, forceload=0):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001112 """Write HTML documentation to a file in the current directory."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001113 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001114 object = locate(key, forceload)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001115 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001116 print value
1117 else:
1118 if object:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001119 page = html.page(describe(object),
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001120 html.document(object, object.__name__))
1121 file = open(key + '.html', 'w')
1122 file.write(page)
1123 file.close()
1124 print 'wrote', key + '.html'
1125 else:
1126 print 'no Python documentation found for %s' % repr(key)
1127
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001128def writedocs(dir, pkgpath='', done=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001129 """Write out HTML documentation for all modules in a directory tree."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001130 if done is None: done = {}
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001131 for file in os.listdir(dir):
1132 path = os.path.join(dir, file)
1133 if ispackage(path):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001134 writedocs(path, pkgpath + file + '.', done)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001135 elif os.path.isfile(path):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001136 modname = inspect.getmodulename(path)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001137 if modname:
1138 modname = pkgpath + modname
1139 if not done.has_key(modname):
1140 done[modname] = 1
1141 writedoc(modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001142
1143class Helper:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001144 keywords = {
1145 'and': 'BOOLEAN',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001146 'assert': ('ref/assert', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001147 'break': ('ref/break', 'while for'),
1148 'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1149 'continue': ('ref/continue', 'while for'),
1150 'def': ('ref/function', ''),
1151 'del': ('ref/del', 'BASICMETHODS'),
1152 'elif': 'if',
1153 'else': ('ref/if', 'while for'),
1154 'except': 'try',
1155 'exec': ('ref/exec', ''),
1156 'finally': 'try',
1157 'for': ('ref/for', 'break continue while'),
1158 'from': 'import',
1159 'global': ('ref/global', 'NAMESPACES'),
1160 'if': ('ref/if', 'TRUTHVALUE'),
1161 'import': ('ref/import', 'MODULES'),
1162 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1163 'is': 'COMPARISON',
1164 'lambda': ('ref/lambda', 'FUNCTIONS'),
1165 'not': 'BOOLEAN',
1166 'or': 'BOOLEAN',
1167 'pass': 'PASS',
1168 'print': ('ref/print', ''),
1169 'raise': ('ref/raise', 'EXCEPTIONS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001170 'return': ('ref/return', 'FUNCTIONS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001171 'try': ('ref/try', 'EXCEPTIONS'),
1172 'while': ('ref/while', 'break continue if TRUTHVALUE'),
1173 }
1174
1175 topics = {
1176 'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001177 'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001178 'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'),
1179 'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001180 'UNICODE': ('ref/unicode', 'encodings unicode TYPES STRING'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001181 'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1182 'INTEGER': ('ref/integers', 'int range'),
1183 'FLOAT': ('ref/floating', 'float math'),
1184 'COMPLEX': ('ref/imaginary', 'complex cmath'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001185 'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001186 'MAPPINGS': 'DICTIONARIES',
1187 'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'),
1188 'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'),
1189 'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001190 'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001191 'FRAMEOBJECTS': 'TYPES',
1192 'TRACEBACKS': 'TYPES',
1193 'NONE': ('lib/bltin-null-object', ''),
1194 'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'),
1195 'FILES': ('lib/bltin-file-objects', ''),
1196 'SPECIALATTRIBUTES': ('lib/specialattrs', ''),
1197 'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'),
1198 'MODULES': ('lib/typesmodules', 'import'),
1199 'PACKAGES': 'import',
1200 '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'),
1201 'OPERATORS': 'EXPRESSIONS',
1202 'PRECEDENCE': 'EXPRESSIONS',
1203 'OBJECTS': ('ref/objects', 'TYPES'),
1204 'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001205 'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'),
1206 'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1207 'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'),
1208 'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'),
1209 'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'),
1210 'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'),
1211 'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001212 'EXECUTION': ('ref/execframes', ''),
1213 'NAMESPACES': ('ref/execframes', 'global ASSIGNMENT DELETION'),
1214 'SCOPING': 'NAMESPACES',
1215 'FRAMES': 'NAMESPACES',
1216 'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'),
1217 'COERCIONS': 'CONVERSIONS',
1218 'CONVERSIONS': ('ref/conversions', ''),
1219 'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'),
1220 'SPECIALIDENTIFIERS': ('ref/id-classes', ''),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001221 'PRIVATENAMES': ('ref/atom-identifiers', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001222 'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1223 'TUPLES': 'SEQUENCES',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001224 'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001225 'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001226 'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001227 'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001228 'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'),
1229 'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001230 'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1231 'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'),
1232 'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'),
1233 'CALLS': ('ref/calls', 'EXPRESSIONS'),
1234 'POWER': ('ref/power', 'EXPRESSIONS'),
1235 'UNARY': ('ref/unary', 'EXPRESSIONS'),
1236 'BINARY': ('ref/binary', 'EXPRESSIONS'),
1237 'SHIFTING': ('ref/shifting', 'EXPRESSIONS'),
1238 'BITWISE': ('ref/bitwise', 'EXPRESSIONS'),
1239 'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001240 'BOOLEAN': ('ref/lambda', 'EXPRESSIONS TRUTHVALUE'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001241 'ASSERTION': 'assert',
1242 'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001243 'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001244 'DELETION': 'del',
1245 'PRINTING': 'print',
1246 'RETURNING': 'return',
1247 'IMPORTING': 'import',
1248 'CONDITIONAL': 'if',
1249 'LOOPING': ('ref/compound', 'for while break continue'),
1250 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001251 'DEBUGGING': ('lib/module-pdb', 'pdb'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001252 }
1253
1254 def __init__(self, input, output):
1255 self.input = input
1256 self.output = output
1257 self.docdir = None
1258 execdir = os.path.dirname(sys.executable)
1259 homedir = os.environ.get('PYTHONHOME')
1260 for dir in [os.environ.get('PYTHONDOCS'),
1261 homedir and os.path.join(homedir, 'doc'),
1262 os.path.join(execdir, 'doc'),
1263 '/usr/doc/python-docs-' + split(sys.version)[0],
1264 '/usr/doc/python-' + split(sys.version)[0],
1265 '/usr/doc/python-docs-' + sys.version[:3],
1266 '/usr/doc/python-' + sys.version[:3]]:
1267 if dir and os.path.isdir(os.path.join(dir, 'lib')):
1268 self.docdir = dir
1269
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001270 def __repr__(self):
1271 if len(inspect.stack()) <= 2:
1272 self()
1273 return ''
1274 return '<pydoc.Helper instance at %p>' % id(self)
1275
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001276 def __call__(self, request=None):
1277 if request is not None:
1278 self.help(request)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001279 else:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001280 self.intro()
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001281 self.interact()
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001282 self.output.write('''
1283You're now leaving help and returning to the Python interpreter.
1284If you want to ask for help on a particular object directly from the
1285interpreter, you can type "help(object)". Executing "help('string')"
1286has the same effect as typing a particular string at the help> prompt.
1287''')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001288
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001289 def interact(self):
1290 self.output.write('\n')
1291 while 1:
1292 self.output.write('help> ')
1293 self.output.flush()
1294 try:
1295 request = self.input.readline()
1296 if not request: break
1297 except KeyboardInterrupt: break
1298 request = strip(replace(request, '"', '', "'", ''))
1299 if lower(request) in ['q', 'quit']: break
1300 self.help(request)
1301
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001302 def help(self, request):
1303 if type(request) is type(''):
1304 if request == 'help': self.intro()
1305 elif request == 'keywords': self.listkeywords()
1306 elif request == 'topics': self.listtopics()
1307 elif request == 'modules': self.listmodules()
1308 elif request[:8] == 'modules ':
1309 self.listmodules(split(request)[1])
1310 elif self.keywords.has_key(request): self.showtopic(request)
1311 elif self.topics.has_key(request): self.showtopic(request)
1312 elif request: doc(request, 'Help on %s:')
1313 elif isinstance(request, Helper): self()
1314 else: doc(request, 'Help on %s:')
1315 self.output.write('\n')
1316
1317 def intro(self):
1318 self.output.write('''
1319Welcome to Python %s! This is the online help utility.
1320
1321If this is your first time using Python, you should definitely check out
1322the tutorial on the Internet at http://www.python.org/doc/tut/.
1323
1324Enter the name of any module, keyword, or topic to get help on writing
1325Python programs and using Python modules. To quit this help utility and
1326return to the interpreter, just type "quit".
1327
1328To get a list of available modules, keywords, or topics, type "modules",
1329"keywords", or "topics". Each module also comes with a one-line summary
1330of what it does; to list the modules whose summaries contain a given word
1331such as "spam", type "modules spam".
1332''' % sys.version[:3])
1333
1334 def list(self, items, columns=4, width=80):
1335 items = items[:]
1336 items.sort()
1337 colw = width / columns
1338 rows = (len(items) + columns - 1) / columns
1339 for row in range(rows):
1340 for col in range(columns):
1341 i = col * rows + row
1342 if i < len(items):
1343 self.output.write(items[i])
1344 if col < columns - 1:
1345 self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1346 self.output.write('\n')
1347
1348 def listkeywords(self):
1349 self.output.write('''
1350Here is a list of the Python keywords. Enter any keyword to get more help.
1351
1352''')
1353 self.list(self.keywords.keys())
1354
1355 def listtopics(self):
1356 self.output.write('''
1357Here is a list of available topics. Enter any topic name to get more help.
1358
1359''')
1360 self.list(self.topics.keys())
1361
1362 def showtopic(self, topic):
1363 if not self.docdir:
1364 self.output.write('''
1365Sorry, topic and keyword documentation is not available because the Python
1366HTML documentation files could not be found. If you have installed them,
1367please set the environment variable PYTHONDOCS to indicate their location.
1368''')
1369 return
1370 target = self.topics.get(topic, self.keywords.get(topic))
1371 if not target:
1372 self.output.write('no documentation found for %s\n' % repr(topic))
1373 return
1374 if type(target) is type(''):
1375 return self.showtopic(target)
1376
1377 filename, xrefs = target
1378 filename = self.docdir + '/' + filename + '.html'
1379 try:
1380 file = open(filename)
1381 except:
1382 self.output.write('could not read docs from %s\n' % filename)
1383 return
1384
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001385 divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
1386 addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001387 document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1388 file.close()
1389
1390 import htmllib, formatter, StringIO
1391 buffer = StringIO.StringIO()
1392 parser = htmllib.HTMLParser(
1393 formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1394 parser.start_table = parser.do_p
1395 parser.end_table = lambda parser=parser: parser.do_p({})
1396 parser.start_tr = parser.do_br
1397 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1398 parser.feed(document)
1399 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
1400 pager(' ' + strip(buffer) + '\n')
Ka-Ping Yeeda793892001-04-13 11:02:51 +00001401 if xrefs:
1402 buffer = StringIO.StringIO()
1403 formatter.DumbWriter(buffer).send_flowing_data(
1404 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1405 self.output.write('\n%s\n' % buffer.getvalue())
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001406
1407 def listmodules(self, key=''):
1408 if key:
1409 self.output.write('''
1410Here is a list of matching modules. Enter any module name to get more help.
1411
1412''')
1413 apropos(key)
1414 else:
1415 self.output.write('''
1416Please wait a moment while I gather a list of all available modules...
1417
1418''')
1419 modules = {}
1420 def callback(path, modname, desc, modules=modules):
1421 if modname and modname[-9:] == '.__init__':
1422 modname = modname[:-9] + ' (package)'
1423 if find(modname, '.') < 0:
1424 modules[modname] = 1
1425 ModuleScanner().run(callback)
1426 self.list(modules.keys())
1427 self.output.write('''
1428Enter any module name to get more help. Or, type "modules spam" to search
1429for modules whose descriptions contain the word "spam".
1430''')
1431
1432help = Helper(sys.stdin, sys.stdout)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001433
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001434class Scanner:
1435 """A generic tree iterator."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001436 def __init__(self, roots, children, descendp):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001437 self.roots = roots[:]
1438 self.state = []
1439 self.children = children
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001440 self.descendp = descendp
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001441
1442 def next(self):
1443 if not self.state:
1444 if not self.roots:
1445 return None
1446 root = self.roots.pop(0)
1447 self.state = [(root, self.children(root))]
1448 node, children = self.state[-1]
1449 if not children:
1450 self.state.pop()
1451 return self.next()
1452 child = children.pop(0)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001453 if self.descendp(child):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001454 self.state.append((child, self.children(child)))
1455 return child
1456
1457class ModuleScanner(Scanner):
1458 """An interruptible scanner that searches module synopses."""
1459 def __init__(self):
1460 roots = map(lambda dir: (dir, ''), pathdirs())
1461 Scanner.__init__(self, roots, self.submodules, self.ispackage)
1462
1463 def submodules(self, (dir, package)):
1464 children = []
1465 for file in os.listdir(dir):
1466 path = os.path.join(dir, file)
Tim Peters30edd232001-03-16 08:29:48 +00001467 if ispackage(path):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001468 children.append((path, package + (package and '.') + file))
1469 else:
1470 children.append((path, package))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001471 children.sort() # so that spam.py comes before spam.pyc or spam.pyo
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001472 return children
1473
1474 def ispackage(self, (dir, package)):
1475 return ispackage(dir)
1476
Ka-Ping Yee66246962001-04-12 11:59:50 +00001477 def run(self, callback, key=None, completer=None):
1478 if key: key = lower(key)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001479 self.quit = 0
1480 seen = {}
1481
1482 for modname in sys.builtin_module_names:
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001483 if modname != '__main__':
1484 seen[modname] = 1
Ka-Ping Yee66246962001-04-12 11:59:50 +00001485 if key is None:
1486 callback(None, modname, '')
1487 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001488 desc = split(__import__(modname).__doc__ or '', '\n')[0]
Ka-Ping Yee66246962001-04-12 11:59:50 +00001489 if find(lower(modname + ' - ' + desc), key) >= 0:
1490 callback(None, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001491
1492 while not self.quit:
1493 node = self.next()
1494 if not node: break
1495 path, package = node
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001496 modname = inspect.getmodulename(path)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001497 if os.path.isfile(path) and modname:
1498 modname = package + (package and '.') + modname
1499 if not seen.has_key(modname):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001500 seen[modname] = 1 # if we see spam.py, skip spam.pyc
Ka-Ping Yee66246962001-04-12 11:59:50 +00001501 if key is None:
1502 callback(path, modname, '')
1503 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001504 desc = synopsis(path) or ''
1505 if find(lower(modname + ' - ' + desc), key) >= 0:
1506 callback(path, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001507 if completer: completer()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001508
1509def apropos(key):
1510 """Print all the one-line module summaries that contain a substring."""
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001511 def callback(path, modname, desc):
1512 if modname[-9:] == '.__init__':
1513 modname = modname[:-9] + ' (package)'
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001514 print modname, desc and '- ' + desc
1515 try: import warnings
1516 except ImportError: pass
1517 else: warnings.filterwarnings('ignore') # ignore problems during import
Ka-Ping Yee66246962001-04-12 11:59:50 +00001518 ModuleScanner().run(callback, key)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001519
1520# --------------------------------------------------- web browser interface
1521
Ka-Ping Yee66246962001-04-12 11:59:50 +00001522def serve(port, callback=None, completer=None):
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001523 import BaseHTTPServer, mimetools, select
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001524
1525 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1526 class Message(mimetools.Message):
1527 def __init__(self, fp, seekable=1):
1528 Message = self.__class__
1529 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1530 self.encodingheader = self.getheader('content-transfer-encoding')
1531 self.typeheader = self.getheader('content-type')
1532 self.parsetype()
1533 self.parseplist()
1534
1535 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1536 def send_document(self, title, contents):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001537 try:
1538 self.send_response(200)
1539 self.send_header('Content-Type', 'text/html')
1540 self.end_headers()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001541 self.wfile.write(html.page(title, contents))
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001542 except IOError: pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001543
1544 def do_GET(self):
1545 path = self.path
1546 if path[-5:] == '.html': path = path[:-5]
1547 if path[:1] == '/': path = path[1:]
1548 if path and path != '.':
1549 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001550 obj = locate(path, forceload=1)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001551 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001552 self.send_document(path, html.escape(str(value)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001553 return
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001554 if obj:
1555 self.send_document(describe(obj), html.document(obj, path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001556 else:
1557 self.send_document(path,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001558'no Python documentation found for %s' % repr(path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001559 else:
1560 heading = html.heading(
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001561'<big><big><strong>Python: Index of Modules</strong></big></big>',
1562'#ffffff', '#7799ee')
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001563 def bltinlink(name):
1564 return '<a href="%s.html">%s</a>' % (name, name)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001565 names = filter(lambda x: x != '__main__',
1566 sys.builtin_module_names)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001567 contents = html.multicolumn(names, bltinlink)
1568 indices = ['<p>' + html.bigsection(
1569 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1570
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001571 seen = {}
1572 for dir in pathdirs():
1573 indices.append(html.index(dir, seen))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001574 contents = heading + join(indices) + '''<p align=right>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001575<small><small><font color="#909090" face="helvetica, arial"><strong>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001576pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font></small></small>'''
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001577 self.send_document('Index of Modules', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001578
1579 def log_message(self, *args): pass
1580
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001581 class DocServer(BaseHTTPServer.HTTPServer):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001582 def __init__(self, port, callback):
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001583 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001584 self.address = ('', port)
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001585 self.url = 'http://%s:%d/' % (host, port)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001586 self.callback = callback
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001587 self.base.__init__(self, self.address, self.handler)
1588
1589 def serve_until_quit(self):
1590 import select
1591 self.quit = 0
1592 while not self.quit:
1593 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1594 if rd: self.handle_request()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001595
1596 def server_activate(self):
1597 self.base.server_activate(self)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001598 if self.callback: self.callback(self)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001599
1600 DocServer.base = BaseHTTPServer.HTTPServer
1601 DocServer.handler = DocHandler
1602 DocHandler.MessageClass = Message
1603 try:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001604 try:
1605 DocServer(port, callback).serve_until_quit()
1606 except (KeyboardInterrupt, select.error):
1607 pass
1608 finally:
Ka-Ping Yee66246962001-04-12 11:59:50 +00001609 if completer: completer()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001610
1611# ----------------------------------------------------- graphical interface
1612
1613def gui():
1614 """Graphical interface (starts web server and pops up a control window)."""
1615 class GUI:
1616 def __init__(self, window, port=7464):
1617 self.window = window
1618 self.server = None
1619 self.scanner = None
1620
1621 import Tkinter
1622 self.server_frm = Tkinter.Frame(window)
1623 self.title_lbl = Tkinter.Label(self.server_frm,
1624 text='Starting server...\n ')
1625 self.open_btn = Tkinter.Button(self.server_frm,
1626 text='open browser', command=self.open, state='disabled')
1627 self.quit_btn = Tkinter.Button(self.server_frm,
1628 text='quit serving', command=self.quit, state='disabled')
1629
1630 self.search_frm = Tkinter.Frame(window)
1631 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
1632 self.search_ent = Tkinter.Entry(self.search_frm)
1633 self.search_ent.bind('<Return>', self.search)
1634 self.stop_btn = Tkinter.Button(self.search_frm,
1635 text='stop', pady=0, command=self.stop, state='disabled')
1636 if sys.platform == 'win32':
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001637 # Trying to hide and show this button crashes under Windows.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001638 self.stop_btn.pack(side='right')
1639
1640 self.window.title('pydoc')
1641 self.window.protocol('WM_DELETE_WINDOW', self.quit)
1642 self.title_lbl.pack(side='top', fill='x')
1643 self.open_btn.pack(side='left', fill='x', expand=1)
1644 self.quit_btn.pack(side='right', fill='x', expand=1)
1645 self.server_frm.pack(side='top', fill='x')
1646
1647 self.search_lbl.pack(side='left')
1648 self.search_ent.pack(side='right', fill='x', expand=1)
1649 self.search_frm.pack(side='top', fill='x')
1650 self.search_ent.focus_set()
1651
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001652 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001653 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001654 self.result_lst.bind('<Button-1>', self.select)
1655 self.result_lst.bind('<Double-Button-1>', self.goto)
1656 self.result_scr = Tkinter.Scrollbar(window,
1657 orient='vertical', command=self.result_lst.yview)
1658 self.result_lst.config(yscrollcommand=self.result_scr.set)
1659
1660 self.result_frm = Tkinter.Frame(window)
1661 self.goto_btn = Tkinter.Button(self.result_frm,
1662 text='go to selected', command=self.goto)
1663 self.hide_btn = Tkinter.Button(self.result_frm,
1664 text='hide results', command=self.hide)
1665 self.goto_btn.pack(side='left', fill='x', expand=1)
1666 self.hide_btn.pack(side='right', fill='x', expand=1)
1667
1668 self.window.update()
1669 self.minwidth = self.window.winfo_width()
1670 self.minheight = self.window.winfo_height()
1671 self.bigminheight = (self.server_frm.winfo_reqheight() +
1672 self.search_frm.winfo_reqheight() +
1673 self.result_lst.winfo_reqheight() +
1674 self.result_frm.winfo_reqheight())
1675 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
1676 self.expanded = 0
1677 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1678 self.window.wm_minsize(self.minwidth, self.minheight)
1679
1680 import threading
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001681 threading.Thread(
1682 target=serve, args=(port, self.ready, self.quit)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001683
1684 def ready(self, server):
1685 self.server = server
1686 self.title_lbl.config(
1687 text='Python documentation server at\n' + server.url)
1688 self.open_btn.config(state='normal')
1689 self.quit_btn.config(state='normal')
1690
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001691 def open(self, event=None, url=None):
1692 url = url or self.server.url
1693 try:
1694 import webbrowser
1695 webbrowser.open(url)
1696 except ImportError: # pre-webbrowser.py compatibility
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001697 if sys.platform == 'win32':
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001698 os.system('start "%s"' % url)
1699 elif sys.platform == 'mac':
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001700 try: import ic
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001701 except ImportError: pass
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001702 else: ic.launchurl(url)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001703 else:
1704 rc = os.system('netscape -remote "openURL(%s)" &' % url)
1705 if rc: os.system('netscape "%s" &' % url)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001706
1707 def quit(self, event=None):
1708 if self.server:
1709 self.server.quit = 1
1710 self.window.quit()
1711
1712 def search(self, event=None):
1713 key = self.search_ent.get()
1714 self.stop_btn.pack(side='right')
1715 self.stop_btn.config(state='normal')
1716 self.search_lbl.config(text='Searching for "%s"...' % key)
1717 self.search_ent.forget()
1718 self.search_lbl.pack(side='left')
1719 self.result_lst.delete(0, 'end')
1720 self.goto_btn.config(state='disabled')
1721 self.expand()
1722
1723 import threading
1724 if self.scanner:
1725 self.scanner.quit = 1
1726 self.scanner = ModuleScanner()
1727 threading.Thread(target=self.scanner.run,
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +00001728 args=(self.update, key, self.done)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001729
1730 def update(self, path, modname, desc):
1731 if modname[-9:] == '.__init__':
1732 modname = modname[:-9] + ' (package)'
1733 self.result_lst.insert('end',
1734 modname + ' - ' + (desc or '(no description)'))
1735
1736 def stop(self, event=None):
1737 if self.scanner:
1738 self.scanner.quit = 1
1739 self.scanner = None
1740
1741 def done(self):
1742 self.scanner = None
1743 self.search_lbl.config(text='Search for')
1744 self.search_lbl.pack(side='left')
1745 self.search_ent.pack(side='right', fill='x', expand=1)
1746 if sys.platform != 'win32': self.stop_btn.forget()
1747 self.stop_btn.config(state='disabled')
1748
1749 def select(self, event=None):
1750 self.goto_btn.config(state='normal')
1751
1752 def goto(self, event=None):
1753 selection = self.result_lst.curselection()
1754 if selection:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001755 modname = split(self.result_lst.get(selection[0]))[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001756 self.open(url=self.server.url + modname + '.html')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001757
1758 def collapse(self):
1759 if not self.expanded: return
1760 self.result_frm.forget()
1761 self.result_scr.forget()
1762 self.result_lst.forget()
1763 self.bigwidth = self.window.winfo_width()
1764 self.bigheight = self.window.winfo_height()
1765 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1766 self.window.wm_minsize(self.minwidth, self.minheight)
1767 self.expanded = 0
1768
1769 def expand(self):
1770 if self.expanded: return
1771 self.result_frm.pack(side='bottom', fill='x')
1772 self.result_scr.pack(side='right', fill='y')
1773 self.result_lst.pack(side='top', fill='both', expand=1)
1774 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
1775 self.window.wm_minsize(self.minwidth, self.bigminheight)
1776 self.expanded = 1
1777
1778 def hide(self, event=None):
1779 self.stop()
1780 self.collapse()
1781
1782 import Tkinter
1783 try:
1784 gui = GUI(Tkinter.Tk())
1785 Tkinter.mainloop()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001786 except KeyboardInterrupt:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001787 pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001788
1789# -------------------------------------------------- command-line interface
1790
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001791def ispath(x):
1792 return type(x) is types.StringType and find(x, os.sep) >= 0
1793
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001794def cli():
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001795 """Command-line interface (looks at sys.argv to decide what to do)."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001796 import getopt
1797 class BadUsage: pass
1798
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001799 # Scripts don't get the current directory in their path by default.
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00001800 scriptdir = os.path.dirname(sys.argv[0])
1801 if scriptdir in sys.path:
1802 sys.path.remove(scriptdir)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001803 sys.path.insert(0, '.')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001804
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001805 try:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001806 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001807 writing = 0
1808
1809 for opt, val in opts:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001810 if opt == '-g':
1811 gui()
1812 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001813 if opt == '-k':
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001814 apropos(val)
1815 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001816 if opt == '-p':
1817 try:
1818 port = int(val)
1819 except ValueError:
1820 raise BadUsage
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001821 def ready(server):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001822 print 'pydoc server ready at %s' % server.url
1823 def stopped():
1824 print 'pydoc server stopped'
1825 serve(port, ready, stopped)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001826 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001827 if opt == '-w':
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001828 writing = 1
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001829
1830 if not args: raise BadUsage
1831 for arg in args:
1832 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001833 if ispath(arg) and os.path.isfile(arg):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001834 arg = importfile(arg)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001835 if writing:
1836 if ispath(arg) and os.path.isdir(arg):
1837 writedocs(arg)
1838 else:
1839 writedoc(arg)
1840 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001841 doc(arg)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001842 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001843 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001844
1845 except (getopt.error, BadUsage):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001846 cmd = sys.argv[0]
1847 print """pydoc - the Python documentation tool
1848
1849%s <name> ...
1850 Show text documentation on something. <name> may be the name of a
1851 function, module, or package, or a dotted reference to a class or
1852 function within a module or module in a package. If <name> contains
1853 a '%s', it is used as the path to a Python source file to document.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001854
1855%s -k <keyword>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001856 Search for a keyword in the synopsis lines of all available modules.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001857
1858%s -p <port>
1859 Start an HTTP server on the given port on the local machine.
1860
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001861%s -g
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001862 Pop up a graphical interface for finding and serving documentation.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001863
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001864%s -w <name> ...
1865 Write out the HTML documentation for a module to a file in the current
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001866 directory. If <name> contains a '%s', it is treated as a filename; if
1867 it names a directory, documentation is written for all the contents.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001868""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001869
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001870if __name__ == '__main__': cli()