blob: 9ec57aa38ca0c11d28f4d5b985c16b2ccfc2016a [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 Yeed03f8fe2001-04-13 15:04:32 +0000321TT { font-family: lucidatypewriter, 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 Yee6191a232001-04-13 15:00:27 +0000487 url = path
488 if sys.platform == 'win32':
489 import nturl2path
490 url = nturl2path.pathname2url(path)
491 filelink = '<a href="file:%s">%s</a>' % (url, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000492 except TypeError:
493 filelink = '(built-in)'
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000494 info = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000495 if hasattr(object, '__version__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000496 version = str(object.__version__)
Ka-Ping Yee40c49912001-02-27 22:46:01 +0000497 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
498 version = strip(version[11:-1])
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000499 info.append('version %s' % self.escape(version))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000500 if hasattr(object, '__date__'):
501 info.append(self.escape(str(object.__date__)))
502 if info:
503 head = head + ' (%s)' % join(info, ', ')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000504 result = self.heading(
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000505 head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)
506
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000507 modules = inspect.getmembers(object, inspect.ismodule)
508
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000509 classes, cdict = [], {}
510 for key, value in inspect.getmembers(object, inspect.isclass):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000511 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000512 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000513 cdict[key] = cdict[value] = '#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000514 for key, value in classes:
515 for base in value.__bases__:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000516 key, modname = base.__name__, base.__module__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000517 module = sys.modules.get(modname)
518 if modname != name and module and hasattr(module, key):
519 if getattr(module, key) is base:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000520 if not cdict.has_key(key):
521 cdict[key] = cdict[base] = modname + '.html#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000522 funcs, fdict = [], {}
523 for key, value in inspect.getmembers(object, inspect.isroutine):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000524 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000525 funcs.append((key, value))
526 fdict[key] = '#-' + key
527 if inspect.isfunction(value): fdict[value] = fdict[key]
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000528 data = []
529 for key, value in inspect.getmembers(object, isdata):
530 if key not in ['__builtins__', '__doc__']:
531 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000532
533 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
534 doc = doc and '<tt>%s</tt>' % doc
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000535 result = result + '<p>%s</p>\n' % self.small(doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000536
537 if hasattr(object, '__path__'):
538 modpkgs = []
539 modnames = []
540 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000541 path = os.path.join(object.__path__[0], file)
542 modname = inspect.getmodulename(file)
543 if modname and modname not in modnames:
544 modpkgs.append((modname, name, 0, 0))
545 modnames.append(modname)
546 elif ispackage(path):
547 modpkgs.append((file, name, 1, 0))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000548 modpkgs.sort()
549 contents = self.multicolumn(modpkgs, self.modpkglink)
550 result = result + self.bigsection(
551 'Package Contents', '#ffffff', '#aa55cc', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000552 elif modules:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000553 contents = self.multicolumn(
554 modules, lambda (key, value), s=self: s.modulelink(value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000555 result = result + self.bigsection(
556 'Modules', '#fffff', '#aa55cc', contents)
557
558 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000559 classlist = map(lambda (key, value): value, classes)
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000560 contents = [
561 self.formattree(inspect.getclasstree(classlist, 1), name)]
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000562 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000563 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000564 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000565 'Classes', '#ffffff', '#ee77aa', join(contents))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000566 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000567 contents = []
568 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000569 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000570 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000571 'Functions', '#ffffff', '#eeaa77', join(contents))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000572 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000573 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000574 for key, value in data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000575 contents.append(self.document(value, key))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000576 result = result + self.bigsection(
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000577 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000578 if hasattr(object, '__author__'):
579 contents = self.markup(str(object.__author__), self.preformat)
580 result = result + self.bigsection(
581 'Author', '#ffffff', '#7799ee', contents)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000582 if hasattr(object, '__credits__'):
583 contents = self.markup(str(object.__credits__), self.preformat)
584 result = result + self.bigsection(
585 'Credits', '#ffffff', '#7799ee', contents)
586
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000587 return result
588
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000589 def docclass(self, object, name=None, mod=None, funcs={}, classes={}):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000590 """Produce HTML documentation for a class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000591 realname = object.__name__
592 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000593 bases = object.__bases__
594 contents = ''
595
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000596 methods, mdict = allmethods(object).items(), {}
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000597 methods.sort()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000598 for key, value in methods:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000599 mdict[key] = mdict[value] = '#' + name + '-' + key
600 for key, value in methods:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000601 contents = contents + self.document(
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000602 value, key, mod, funcs, classes, mdict, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000603
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000604 if name == realname:
605 title = '<a name="%s">class <strong>%s</strong></a>' % (
606 name, realname)
607 else:
608 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
609 name, name, realname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000610 if bases:
611 parents = []
612 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000613 parents.append(self.classlink(base, object.__module__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000614 title = title + '(%s)' % join(parents, ', ')
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000615 doc = self.markup(
616 getdoc(object), self.preformat, funcs, classes, mdict)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000617 doc = self.small(doc and '<tt>%s<br>&nbsp;</tt>' % doc or
618 self.small('&nbsp;'))
619 return self.section(title, '#000000', '#ffc8d8', contents, 5, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000620
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000621 def formatvalue(self, object):
622 """Format an argument default value as text."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000623 return self.small(self.grey('=' + self.repr(object)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000624
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000625 def docroutine(self, object, name=None, mod=None,
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000626 funcs={}, classes={}, methods={}, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000627 """Produce HTML documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000628 realname = object.__name__
629 name = name or realname
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000630 anchor = (cl and cl.__name__ or '') + '-' + name
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000631 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000632 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000633 if inspect.ismethod(object):
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +0000634 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000635 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000636 if imclass is not cl:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000637 note = ' from ' + self.classlink(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000638 skipdocs = 1
639 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000640 if object.im_self:
641 note = ' method of %s instance' % self.classlink(
642 object.im_self.__class__, mod)
643 else:
644 note = ' unbound %s method' % self.classlink(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000645 object = object.im_func
646
647 if name == realname:
648 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
649 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000650 if (cl and cl.__dict__.has_key(realname) and
651 cl.__dict__[realname] is object):
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000652 reallink = '<a href="#%s">%s</a>' % (
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000653 cl.__name__ + '-' + realname, realname)
654 skipdocs = 1
655 else:
656 reallink = realname
657 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
658 anchor, name, reallink)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000659 if inspect.isbuiltin(object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000660 argspec = '(...)'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000661 else:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000662 args, varargs, varkw, defaults = inspect.getargspec(object)
663 argspec = inspect.formatargspec(
664 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000665 if realname == '<lambda>':
666 decl = '<em>lambda</em>'
667 argspec = argspec[1:-1] # remove parentheses
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000668
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000669 decl = title + argspec + (note and self.small(self.grey(
670 '<font face="helvetica, arial">%s</font>' % note)))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000671
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000672 if skipdocs:
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000673 return '<dl><dt>%s</dl>\n' % decl
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000674 else:
675 doc = self.markup(
676 getdoc(object), self.preformat, funcs, classes, methods)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000677 doc = doc and '<dd>' + self.small('<tt>%s</tt>' % doc)
678 return '<dl><dt>%s%s</dl>\n' % (decl, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000679
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000680 def docother(self, object, name=None, mod=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000681 """Produce HTML documentation for a data object."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000682 lhs = name and '<strong>%s</strong> = ' % name or ''
683 return lhs + self.repr(object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000684
685 def index(self, dir, shadowed=None):
686 """Generate an HTML index for a directory of modules."""
687 modpkgs = []
688 if shadowed is None: shadowed = {}
689 seen = {}
690 files = os.listdir(dir)
691
692 def found(name, ispackage,
693 modpkgs=modpkgs, shadowed=shadowed, seen=seen):
694 if not seen.has_key(name):
695 modpkgs.append((name, '', ispackage, shadowed.has_key(name)))
696 seen[name] = 1
697 shadowed[name] = 1
698
699 # Package spam/__init__.py takes precedence over module spam.py.
700 for file in files:
701 path = os.path.join(dir, file)
702 if ispackage(path): found(file, 1)
703 for file in files:
704 path = os.path.join(dir, file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000705 if os.path.isfile(path):
706 modname = inspect.getmodulename(file)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000707 if modname: found(modname, 0)
708
709 modpkgs.sort()
710 contents = self.multicolumn(modpkgs, self.modpkglink)
711 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
712
713# -------------------------------------------- text documentation generator
714
715class TextRepr(Repr):
716 """Class for safely making a text representation of a Python object."""
717 def __init__(self):
718 Repr.__init__(self)
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000719 self.maxlist = self.maxtuple = 20
720 self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000721 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000722
723 def repr1(self, x, level):
724 methodname = 'repr_' + join(split(type(x).__name__), '_')
725 if hasattr(self, methodname):
726 return getattr(self, methodname)(x, level)
727 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000728 return cram(stripid(repr(x)), self.maxother)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000729
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000730 def repr_string(self, x, level):
731 test = cram(x, self.maxstring)
732 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000733 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000734 # Backslashes are only literal in the string and are never
735 # needed to make any special characters, so show a raw string.
736 return 'r' + testrepr[0] + test + testrepr[0]
737 return testrepr
738
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000739 def repr_instance(self, x, level):
740 try:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000741 return cram(stripid(repr(x)), self.maxstring)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000742 except:
743 return '<%s instance>' % x.__class__.__name__
744
745class TextDoc(Doc):
746 """Formatter class for text documentation."""
747
748 # ------------------------------------------- text formatting utilities
749
750 _repr_instance = TextRepr()
751 repr = _repr_instance.repr
752
753 def bold(self, text):
754 """Format a string in bold by overstriking."""
755 return join(map(lambda ch: ch + '\b' + ch, text), '')
756
757 def indent(self, text, prefix=' '):
758 """Indent text by prepending a given prefix to each line."""
759 if not text: return ''
760 lines = split(text, '\n')
761 lines = map(lambda line, prefix=prefix: prefix + line, lines)
762 if lines: lines[-1] = rstrip(lines[-1])
763 return join(lines, '\n')
764
765 def section(self, title, contents):
766 """Format a section with a given heading."""
767 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
768
769 # ---------------------------------------------- type-specific routines
770
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000771 def formattree(self, tree, modname, parent=None, prefix=''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000772 """Render in text a class tree as returned by inspect.getclasstree()."""
773 result = ''
774 for entry in tree:
775 if type(entry) is type(()):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000776 c, bases = entry
777 result = result + prefix + classname(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000778 if bases and bases != (parent,):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000779 parents = map(lambda c, m=modname: classname(c, m), bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000780 result = result + '(%s)' % join(parents, ', ')
781 result = result + '\n'
782 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000783 result = result + self.formattree(
784 entry, modname, c, prefix + ' ')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000785 return result
786
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000787 def docmodule(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000788 """Produce text documentation for a given module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000789 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000790 synop, desc = splitdoc(getdoc(object))
791 result = self.section('NAME', name + (synop and ' - ' + synop))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000792
793 try:
794 file = inspect.getabsfile(object)
795 except TypeError:
796 file = '(built-in)'
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000797 result = result + self.section('FILE', file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000798 if desc:
799 result = result + self.section('DESCRIPTION', desc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000800
801 classes = []
802 for key, value in inspect.getmembers(object, inspect.isclass):
803 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000804 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000805 funcs = []
806 for key, value in inspect.getmembers(object, inspect.isroutine):
807 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000808 funcs.append((key, value))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000809 data = []
810 for key, value in inspect.getmembers(object, isdata):
811 if key not in ['__builtins__', '__doc__']:
812 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000813
814 if hasattr(object, '__path__'):
815 modpkgs = []
816 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000817 path = os.path.join(object.__path__[0], file)
818 modname = inspect.getmodulename(file)
819 if modname and modname not in modpkgs:
820 modpkgs.append(modname)
821 elif ispackage(path):
822 modpkgs.append(file + ' (package)')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000823 modpkgs.sort()
824 result = result + self.section(
825 'PACKAGE CONTENTS', join(modpkgs, '\n'))
826
827 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000828 classlist = map(lambda (key, value): value, classes)
829 contents = [self.formattree(
830 inspect.getclasstree(classlist, 1), name)]
831 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000832 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000833 result = result + self.section('CLASSES', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000834
835 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000836 contents = []
837 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000838 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000839 result = result + self.section('FUNCTIONS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000840
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000841 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000842 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000843 for key, value in data:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000844 contents.append(self.docother(value, key, name, 70))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000845 result = result + self.section('DATA', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000846
847 if hasattr(object, '__version__'):
848 version = str(object.__version__)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000849 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
850 version = strip(version[11:-1])
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000851 result = result + self.section('VERSION', version)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000852 if hasattr(object, '__date__'):
853 result = result + self.section('DATE', str(object.__date__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000854 if hasattr(object, '__author__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000855 result = result + self.section('AUTHOR', str(object.__author__))
856 if hasattr(object, '__credits__'):
857 result = result + self.section('CREDITS', str(object.__credits__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000858 return result
859
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000860 def docclass(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000861 """Produce text documentation for a given class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000862 realname = object.__name__
863 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000864 bases = object.__bases__
865
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000866 if name == realname:
867 title = 'class ' + self.bold(realname)
868 else:
869 title = self.bold(name) + ' = class ' + realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000870 if bases:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000871 def makename(c, m=object.__module__): return classname(c, m)
872 parents = map(makename, bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000873 title = title + '(%s)' % join(parents, ', ')
874
875 doc = getdoc(object)
876 contents = doc and doc + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000877 methods = allmethods(object).items()
878 methods.sort()
879 for key, value in methods:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000880 contents = contents + '\n' + self.document(value, key, mod, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000881
882 if not contents: return title + '\n'
883 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
884
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000885 def formatvalue(self, object):
886 """Format an argument default value as text."""
887 return '=' + self.repr(object)
888
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000889 def docroutine(self, object, name=None, mod=None, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000890 """Produce text documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000891 realname = object.__name__
892 name = name or realname
893 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000894 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000895 if inspect.ismethod(object):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000896 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000897 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000898 if imclass is not cl:
899 note = ' from ' + classname(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000900 skipdocs = 1
901 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000902 if object.im_self:
903 note = ' method of %s instance' % classname(
904 object.im_self.__class__, mod)
905 else:
906 note = ' unbound %s method' % classname(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000907 object = object.im_func
908
909 if name == realname:
910 title = self.bold(realname)
911 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000912 if (cl and cl.__dict__.has_key(realname) and
913 cl.__dict__[realname] is object):
914 skipdocs = 1
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000915 title = self.bold(name) + ' = ' + realname
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000916 if inspect.isbuiltin(object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000917 argspec = '(...)'
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000918 else:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000919 args, varargs, varkw, defaults = inspect.getargspec(object)
920 argspec = inspect.formatargspec(
921 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000922 if realname == '<lambda>':
923 title = 'lambda'
924 argspec = argspec[1:-1] # remove parentheses
925 decl = title + argspec + note
926
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000927 if skipdocs:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000928 return decl + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000929 else:
930 doc = getdoc(object) or ''
931 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000932
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000933 def docother(self, object, name=None, mod=None, maxlen=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000934 """Produce text documentation for a data object."""
935 repr = self.repr(object)
936 if maxlen:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000937 line = (name and name + ' = ' or '') + repr
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000938 chop = maxlen - len(line)
939 if chop < 0: repr = repr[:chop] + '...'
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000940 line = (name and self.bold(name) + ' = ' or '') + repr
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000941 return line
942
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000943# --------------------------------------------------------- user interfaces
944
945def pager(text):
946 """The first time this is called, determine what kind of pager to use."""
947 global pager
948 pager = getpager()
949 pager(text)
950
951def getpager():
952 """Decide what method to use for paging through text."""
953 if type(sys.stdout) is not types.FileType:
954 return plainpager
955 if not sys.stdin.isatty() or not sys.stdout.isatty():
956 return plainpager
957 if os.environ.has_key('PAGER'):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000958 if sys.platform == 'win32': # pipes completely broken in Windows
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000959 return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
960 elif os.environ.get('TERM') in ['dumb', 'emacs']:
961 return lambda text: pipepager(plain(text), os.environ['PAGER'])
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000962 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000963 return lambda text: pipepager(text, os.environ['PAGER'])
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +0000964 if sys.platform == 'win32':
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000965 return lambda text: tempfilepager(plain(text), 'more <')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000966 if hasattr(os, 'system') and os.system('less 2>/dev/null') == 0:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000967 return lambda text: pipepager(text, 'less')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000968
969 import tempfile
970 filename = tempfile.mktemp()
971 open(filename, 'w').close()
972 try:
973 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
974 return lambda text: pipepager(text, 'more')
975 else:
976 return ttypager
977 finally:
978 os.unlink(filename)
979
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000980def plain(text):
981 """Remove boldface formatting from text."""
982 return re.sub('.\b', '', text)
983
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000984def pipepager(text, cmd):
985 """Page through text by feeding it to another program."""
986 pipe = os.popen(cmd, 'w')
987 try:
988 pipe.write(text)
989 pipe.close()
990 except IOError:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +0000991 pass # Ignore broken pipes caused by quitting the pager program.
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000992
993def tempfilepager(text, cmd):
994 """Page through text by invoking a program on a temporary file."""
995 import tempfile
996 filename = tempfile.mktemp()
997 file = open(filename, 'w')
998 file.write(text)
999 file.close()
1000 try:
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001001 os.system(cmd + ' ' + filename)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001002 finally:
1003 os.unlink(filename)
1004
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001005def ttypager(text):
1006 """Page through text on a text terminal."""
1007 lines = split(plain(text), '\n')
1008 try:
1009 import tty
1010 fd = sys.stdin.fileno()
1011 old = tty.tcgetattr(fd)
1012 tty.setcbreak(fd)
1013 getchar = lambda: sys.stdin.read(1)
Ka-Ping Yee457aab22001-02-27 23:36:29 +00001014 except (ImportError, AttributeError):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001015 tty = None
1016 getchar = lambda: sys.stdin.readline()[:-1][:1]
1017
1018 try:
1019 r = inc = os.environ.get('LINES', 25) - 1
1020 sys.stdout.write(join(lines[:inc], '\n') + '\n')
1021 while lines[r:]:
1022 sys.stdout.write('-- more --')
1023 sys.stdout.flush()
1024 c = getchar()
1025
1026 if c in ['q', 'Q']:
1027 sys.stdout.write('\r \r')
1028 break
1029 elif c in ['\r', '\n']:
1030 sys.stdout.write('\r \r' + lines[r] + '\n')
1031 r = r + 1
1032 continue
1033 if c in ['b', 'B', '\x1b']:
1034 r = r - inc - inc
1035 if r < 0: r = 0
1036 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1037 r = r + inc
1038
1039 finally:
1040 if tty:
1041 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1042
1043def plainpager(text):
1044 """Simply print unformatted text. This is the ultimate fallback."""
1045 sys.stdout.write(plain(text))
1046
1047def describe(thing):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001048 """Produce a short description of the given thing."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001049 if inspect.ismodule(thing):
1050 if thing.__name__ in sys.builtin_module_names:
1051 return 'built-in module ' + thing.__name__
1052 if hasattr(thing, '__path__'):
1053 return 'package ' + thing.__name__
1054 else:
1055 return 'module ' + thing.__name__
1056 if inspect.isbuiltin(thing):
1057 return 'built-in function ' + thing.__name__
1058 if inspect.isclass(thing):
1059 return 'class ' + thing.__name__
1060 if inspect.isfunction(thing):
1061 return 'function ' + thing.__name__
1062 if inspect.ismethod(thing):
1063 return 'method ' + thing.__name__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001064 if type(thing) is types.InstanceType:
1065 return 'instance of ' + thing.__class__.__name__
1066 return type(thing).__name__
1067
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001068def locate(path, forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001069 """Locate an object by name or dotted path, importing as necessary."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001070 parts = split(path, '.')
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001071 module, n = None, 0
1072 while n < len(parts):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001073 nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001074 if nextmodule: module, n = nextmodule, n + 1
1075 else: break
1076 if module:
1077 object = module
1078 for part in parts[n:]:
1079 try: object = getattr(object, part)
1080 except AttributeError: return None
1081 return object
1082 else:
1083 import __builtin__
1084 if hasattr(__builtin__, path):
1085 return getattr(__builtin__, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001086
1087# --------------------------------------- interactive interpreter interface
1088
1089text = TextDoc()
1090html = HTMLDoc()
1091
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001092def doc(thing, title='Python Library Documentation: %s', forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001093 """Display text documentation, given an object or a path to an object."""
1094 suffix, name = '', None
1095 if type(thing) is type(''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001096 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001097 object = locate(thing, forceload)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001098 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001099 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001100 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001101 if not object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001102 print 'no Python documentation found for %s' % repr(thing)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001103 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001104 parts = split(thing, '.')
1105 if len(parts) > 1: suffix = ' in ' + join(parts[:-1], '.')
1106 name = parts[-1]
1107 thing = object
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001108
1109 desc = describe(thing)
1110 module = inspect.getmodule(thing)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001111 if not suffix and module and module is not thing:
1112 suffix = ' in module ' + module.__name__
Ka-Ping Yee66246962001-04-12 11:59:50 +00001113 pager(title % (desc + suffix) + '\n\n' + text.document(thing, name))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001114
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001115def writedoc(key, forceload=0):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001116 """Write HTML documentation to a file in the current directory."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001117 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001118 object = locate(key, forceload)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001119 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001120 print value
1121 else:
1122 if object:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001123 page = html.page(describe(object),
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001124 html.document(object, object.__name__))
1125 file = open(key + '.html', 'w')
1126 file.write(page)
1127 file.close()
1128 print 'wrote', key + '.html'
1129 else:
1130 print 'no Python documentation found for %s' % repr(key)
1131
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001132def writedocs(dir, pkgpath='', done=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001133 """Write out HTML documentation for all modules in a directory tree."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001134 if done is None: done = {}
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001135 for file in os.listdir(dir):
1136 path = os.path.join(dir, file)
1137 if ispackage(path):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001138 writedocs(path, pkgpath + file + '.', done)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001139 elif os.path.isfile(path):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001140 modname = inspect.getmodulename(path)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001141 if modname:
1142 modname = pkgpath + modname
1143 if not done.has_key(modname):
1144 done[modname] = 1
1145 writedoc(modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001146
1147class Helper:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001148 keywords = {
1149 'and': 'BOOLEAN',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001150 'assert': ('ref/assert', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001151 'break': ('ref/break', 'while for'),
1152 'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1153 'continue': ('ref/continue', 'while for'),
1154 'def': ('ref/function', ''),
1155 'del': ('ref/del', 'BASICMETHODS'),
1156 'elif': 'if',
1157 'else': ('ref/if', 'while for'),
1158 'except': 'try',
1159 'exec': ('ref/exec', ''),
1160 'finally': 'try',
1161 'for': ('ref/for', 'break continue while'),
1162 'from': 'import',
1163 'global': ('ref/global', 'NAMESPACES'),
1164 'if': ('ref/if', 'TRUTHVALUE'),
1165 'import': ('ref/import', 'MODULES'),
1166 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1167 'is': 'COMPARISON',
1168 'lambda': ('ref/lambda', 'FUNCTIONS'),
1169 'not': 'BOOLEAN',
1170 'or': 'BOOLEAN',
1171 'pass': 'PASS',
1172 'print': ('ref/print', ''),
1173 'raise': ('ref/raise', 'EXCEPTIONS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001174 'return': ('ref/return', 'FUNCTIONS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001175 'try': ('ref/try', 'EXCEPTIONS'),
1176 'while': ('ref/while', 'break continue if TRUTHVALUE'),
1177 }
1178
1179 topics = {
1180 'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001181 'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001182 'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'),
1183 'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001184 'UNICODE': ('ref/unicode', 'encodings unicode TYPES STRING'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001185 'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1186 'INTEGER': ('ref/integers', 'int range'),
1187 'FLOAT': ('ref/floating', 'float math'),
1188 'COMPLEX': ('ref/imaginary', 'complex cmath'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001189 'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001190 'MAPPINGS': 'DICTIONARIES',
1191 'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'),
1192 'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'),
1193 'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001194 'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001195 'FRAMEOBJECTS': 'TYPES',
1196 'TRACEBACKS': 'TYPES',
1197 'NONE': ('lib/bltin-null-object', ''),
1198 'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'),
1199 'FILES': ('lib/bltin-file-objects', ''),
1200 'SPECIALATTRIBUTES': ('lib/specialattrs', ''),
1201 'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'),
1202 'MODULES': ('lib/typesmodules', 'import'),
1203 'PACKAGES': 'import',
1204 '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'),
1205 'OPERATORS': 'EXPRESSIONS',
1206 'PRECEDENCE': 'EXPRESSIONS',
1207 'OBJECTS': ('ref/objects', 'TYPES'),
1208 'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001209 'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'),
1210 'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1211 'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'),
1212 'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'),
1213 'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'),
1214 'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'),
1215 'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001216 'EXECUTION': ('ref/execframes', ''),
1217 'NAMESPACES': ('ref/execframes', 'global ASSIGNMENT DELETION'),
1218 'SCOPING': 'NAMESPACES',
1219 'FRAMES': 'NAMESPACES',
1220 'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'),
1221 'COERCIONS': 'CONVERSIONS',
1222 'CONVERSIONS': ('ref/conversions', ''),
1223 'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'),
1224 'SPECIALIDENTIFIERS': ('ref/id-classes', ''),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001225 'PRIVATENAMES': ('ref/atom-identifiers', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001226 'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1227 'TUPLES': 'SEQUENCES',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001228 'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001229 'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001230 'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001231 'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001232 'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'),
1233 'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001234 'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1235 'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'),
1236 'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'),
1237 'CALLS': ('ref/calls', 'EXPRESSIONS'),
1238 'POWER': ('ref/power', 'EXPRESSIONS'),
1239 'UNARY': ('ref/unary', 'EXPRESSIONS'),
1240 'BINARY': ('ref/binary', 'EXPRESSIONS'),
1241 'SHIFTING': ('ref/shifting', 'EXPRESSIONS'),
1242 'BITWISE': ('ref/bitwise', 'EXPRESSIONS'),
1243 'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001244 'BOOLEAN': ('ref/lambda', 'EXPRESSIONS TRUTHVALUE'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001245 'ASSERTION': 'assert',
1246 'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001247 'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001248 'DELETION': 'del',
1249 'PRINTING': 'print',
1250 'RETURNING': 'return',
1251 'IMPORTING': 'import',
1252 'CONDITIONAL': 'if',
1253 'LOOPING': ('ref/compound', 'for while break continue'),
1254 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001255 'DEBUGGING': ('lib/module-pdb', 'pdb'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001256 }
1257
1258 def __init__(self, input, output):
1259 self.input = input
1260 self.output = output
1261 self.docdir = None
1262 execdir = os.path.dirname(sys.executable)
1263 homedir = os.environ.get('PYTHONHOME')
1264 for dir in [os.environ.get('PYTHONDOCS'),
1265 homedir and os.path.join(homedir, 'doc'),
1266 os.path.join(execdir, 'doc'),
1267 '/usr/doc/python-docs-' + split(sys.version)[0],
1268 '/usr/doc/python-' + split(sys.version)[0],
1269 '/usr/doc/python-docs-' + sys.version[:3],
1270 '/usr/doc/python-' + sys.version[:3]]:
1271 if dir and os.path.isdir(os.path.join(dir, 'lib')):
1272 self.docdir = dir
1273
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001274 def __repr__(self):
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001275 if inspect.stack()[1][3] == '?':
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001276 self()
1277 return ''
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001278 return '<pydoc.Helper instance>'
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001279
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001280 def __call__(self, request=None):
1281 if request is not None:
1282 self.help(request)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001283 else:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001284 self.intro()
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001285 self.interact()
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001286 self.output.write('''
Fred Drakee61967f2001-05-10 18:41:02 +00001287You are now leaving help and returning to the Python interpreter.
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001288If you want to ask for help on a particular object directly from the
1289interpreter, you can type "help(object)". Executing "help('string')"
1290has the same effect as typing a particular string at the help> prompt.
1291''')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001292
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001293 def interact(self):
1294 self.output.write('\n')
1295 while 1:
1296 self.output.write('help> ')
1297 self.output.flush()
1298 try:
1299 request = self.input.readline()
1300 if not request: break
1301 except KeyboardInterrupt: break
1302 request = strip(replace(request, '"', '', "'", ''))
1303 if lower(request) in ['q', 'quit']: break
1304 self.help(request)
1305
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001306 def help(self, request):
1307 if type(request) is type(''):
1308 if request == 'help': self.intro()
1309 elif request == 'keywords': self.listkeywords()
1310 elif request == 'topics': self.listtopics()
1311 elif request == 'modules': self.listmodules()
1312 elif request[:8] == 'modules ':
1313 self.listmodules(split(request)[1])
1314 elif self.keywords.has_key(request): self.showtopic(request)
1315 elif self.topics.has_key(request): self.showtopic(request)
1316 elif request: doc(request, 'Help on %s:')
1317 elif isinstance(request, Helper): self()
1318 else: doc(request, 'Help on %s:')
1319 self.output.write('\n')
1320
1321 def intro(self):
1322 self.output.write('''
1323Welcome to Python %s! This is the online help utility.
1324
1325If this is your first time using Python, you should definitely check out
1326the tutorial on the Internet at http://www.python.org/doc/tut/.
1327
1328Enter the name of any module, keyword, or topic to get help on writing
1329Python programs and using Python modules. To quit this help utility and
1330return to the interpreter, just type "quit".
1331
1332To get a list of available modules, keywords, or topics, type "modules",
1333"keywords", or "topics". Each module also comes with a one-line summary
1334of what it does; to list the modules whose summaries contain a given word
1335such as "spam", type "modules spam".
1336''' % sys.version[:3])
1337
1338 def list(self, items, columns=4, width=80):
1339 items = items[:]
1340 items.sort()
1341 colw = width / columns
1342 rows = (len(items) + columns - 1) / columns
1343 for row in range(rows):
1344 for col in range(columns):
1345 i = col * rows + row
1346 if i < len(items):
1347 self.output.write(items[i])
1348 if col < columns - 1:
1349 self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1350 self.output.write('\n')
1351
1352 def listkeywords(self):
1353 self.output.write('''
1354Here is a list of the Python keywords. Enter any keyword to get more help.
1355
1356''')
1357 self.list(self.keywords.keys())
1358
1359 def listtopics(self):
1360 self.output.write('''
1361Here is a list of available topics. Enter any topic name to get more help.
1362
1363''')
1364 self.list(self.topics.keys())
1365
1366 def showtopic(self, topic):
1367 if not self.docdir:
1368 self.output.write('''
1369Sorry, topic and keyword documentation is not available because the Python
1370HTML documentation files could not be found. If you have installed them,
1371please set the environment variable PYTHONDOCS to indicate their location.
1372''')
1373 return
1374 target = self.topics.get(topic, self.keywords.get(topic))
1375 if not target:
1376 self.output.write('no documentation found for %s\n' % repr(topic))
1377 return
1378 if type(target) is type(''):
1379 return self.showtopic(target)
1380
1381 filename, xrefs = target
1382 filename = self.docdir + '/' + filename + '.html'
1383 try:
1384 file = open(filename)
1385 except:
1386 self.output.write('could not read docs from %s\n' % filename)
1387 return
1388
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001389 divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
1390 addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001391 document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1392 file.close()
1393
1394 import htmllib, formatter, StringIO
1395 buffer = StringIO.StringIO()
1396 parser = htmllib.HTMLParser(
1397 formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1398 parser.start_table = parser.do_p
1399 parser.end_table = lambda parser=parser: parser.do_p({})
1400 parser.start_tr = parser.do_br
1401 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1402 parser.feed(document)
1403 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
1404 pager(' ' + strip(buffer) + '\n')
Ka-Ping Yeeda793892001-04-13 11:02:51 +00001405 if xrefs:
1406 buffer = StringIO.StringIO()
1407 formatter.DumbWriter(buffer).send_flowing_data(
1408 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1409 self.output.write('\n%s\n' % buffer.getvalue())
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001410
1411 def listmodules(self, key=''):
1412 if key:
1413 self.output.write('''
1414Here is a list of matching modules. Enter any module name to get more help.
1415
1416''')
1417 apropos(key)
1418 else:
1419 self.output.write('''
1420Please wait a moment while I gather a list of all available modules...
1421
1422''')
1423 modules = {}
1424 def callback(path, modname, desc, modules=modules):
1425 if modname and modname[-9:] == '.__init__':
1426 modname = modname[:-9] + ' (package)'
1427 if find(modname, '.') < 0:
1428 modules[modname] = 1
1429 ModuleScanner().run(callback)
1430 self.list(modules.keys())
1431 self.output.write('''
1432Enter any module name to get more help. Or, type "modules spam" to search
1433for modules whose descriptions contain the word "spam".
1434''')
1435
1436help = Helper(sys.stdin, sys.stdout)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001437
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001438class Scanner:
1439 """A generic tree iterator."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001440 def __init__(self, roots, children, descendp):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001441 self.roots = roots[:]
1442 self.state = []
1443 self.children = children
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001444 self.descendp = descendp
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001445
1446 def next(self):
1447 if not self.state:
1448 if not self.roots:
1449 return None
1450 root = self.roots.pop(0)
1451 self.state = [(root, self.children(root))]
1452 node, children = self.state[-1]
1453 if not children:
1454 self.state.pop()
1455 return self.next()
1456 child = children.pop(0)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001457 if self.descendp(child):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001458 self.state.append((child, self.children(child)))
1459 return child
1460
1461class ModuleScanner(Scanner):
1462 """An interruptible scanner that searches module synopses."""
1463 def __init__(self):
1464 roots = map(lambda dir: (dir, ''), pathdirs())
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001465 Scanner.__init__(self, roots, self.submodules, self.isnewpackage)
1466 self.inodes = map(lambda (dir, pkg): os.stat(dir)[1], roots)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001467
1468 def submodules(self, (dir, package)):
1469 children = []
1470 for file in os.listdir(dir):
1471 path = os.path.join(dir, file)
Tim Peters30edd232001-03-16 08:29:48 +00001472 if ispackage(path):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001473 children.append((path, package + (package and '.') + file))
1474 else:
1475 children.append((path, package))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001476 children.sort() # so that spam.py comes before spam.pyc or spam.pyo
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001477 return children
1478
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001479 def isnewpackage(self, (dir, package)):
Ka-Ping Yee6191a232001-04-13 15:00:27 +00001480 inode = os.path.exists(dir) and os.stat(dir)[1]
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001481 if not (os.path.islink(dir) and inode in self.inodes):
Ka-Ping Yee6191a232001-04-13 15:00:27 +00001482 self.inodes.append(inode) # detect circular symbolic links
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001483 return ispackage(dir)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001484
Ka-Ping Yee66246962001-04-12 11:59:50 +00001485 def run(self, callback, key=None, completer=None):
1486 if key: key = lower(key)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001487 self.quit = 0
1488 seen = {}
1489
1490 for modname in sys.builtin_module_names:
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001491 if modname != '__main__':
1492 seen[modname] = 1
Ka-Ping Yee66246962001-04-12 11:59:50 +00001493 if key is None:
1494 callback(None, modname, '')
1495 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001496 desc = split(__import__(modname).__doc__ or '', '\n')[0]
Ka-Ping Yee66246962001-04-12 11:59:50 +00001497 if find(lower(modname + ' - ' + desc), key) >= 0:
1498 callback(None, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001499
1500 while not self.quit:
1501 node = self.next()
1502 if not node: break
1503 path, package = node
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001504 modname = inspect.getmodulename(path)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001505 if os.path.isfile(path) and modname:
1506 modname = package + (package and '.') + modname
1507 if not seen.has_key(modname):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001508 seen[modname] = 1 # if we see spam.py, skip spam.pyc
Ka-Ping Yee66246962001-04-12 11:59:50 +00001509 if key is None:
1510 callback(path, modname, '')
1511 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001512 desc = synopsis(path) or ''
1513 if find(lower(modname + ' - ' + desc), key) >= 0:
1514 callback(path, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001515 if completer: completer()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001516
1517def apropos(key):
1518 """Print all the one-line module summaries that contain a substring."""
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001519 def callback(path, modname, desc):
1520 if modname[-9:] == '.__init__':
1521 modname = modname[:-9] + ' (package)'
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001522 print modname, desc and '- ' + desc
1523 try: import warnings
1524 except ImportError: pass
1525 else: warnings.filterwarnings('ignore') # ignore problems during import
Ka-Ping Yee66246962001-04-12 11:59:50 +00001526 ModuleScanner().run(callback, key)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001527
1528# --------------------------------------------------- web browser interface
1529
Ka-Ping Yee66246962001-04-12 11:59:50 +00001530def serve(port, callback=None, completer=None):
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001531 import BaseHTTPServer, mimetools, select
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001532
1533 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1534 class Message(mimetools.Message):
1535 def __init__(self, fp, seekable=1):
1536 Message = self.__class__
1537 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1538 self.encodingheader = self.getheader('content-transfer-encoding')
1539 self.typeheader = self.getheader('content-type')
1540 self.parsetype()
1541 self.parseplist()
1542
1543 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1544 def send_document(self, title, contents):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001545 try:
1546 self.send_response(200)
1547 self.send_header('Content-Type', 'text/html')
1548 self.end_headers()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001549 self.wfile.write(html.page(title, contents))
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001550 except IOError: pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001551
1552 def do_GET(self):
1553 path = self.path
1554 if path[-5:] == '.html': path = path[:-5]
1555 if path[:1] == '/': path = path[1:]
1556 if path and path != '.':
1557 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001558 obj = locate(path, forceload=1)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001559 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001560 self.send_document(path, html.escape(str(value)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001561 return
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001562 if obj:
1563 self.send_document(describe(obj), html.document(obj, path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001564 else:
1565 self.send_document(path,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001566'no Python documentation found for %s' % repr(path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001567 else:
1568 heading = html.heading(
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001569'<big><big><strong>Python: Index of Modules</strong></big></big>',
1570'#ffffff', '#7799ee')
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001571 def bltinlink(name):
1572 return '<a href="%s.html">%s</a>' % (name, name)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001573 names = filter(lambda x: x != '__main__',
1574 sys.builtin_module_names)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001575 contents = html.multicolumn(names, bltinlink)
1576 indices = ['<p>' + html.bigsection(
1577 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1578
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001579 seen = {}
1580 for dir in pathdirs():
1581 indices.append(html.index(dir, seen))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001582 contents = heading + join(indices) + '''<p align=right>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001583<small><small><font color="#909090" face="helvetica, arial"><strong>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001584pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font></small></small>'''
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001585 self.send_document('Index of Modules', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001586
1587 def log_message(self, *args): pass
1588
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001589 class DocServer(BaseHTTPServer.HTTPServer):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001590 def __init__(self, port, callback):
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001591 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001592 self.address = ('', port)
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001593 self.url = 'http://%s:%d/' % (host, port)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001594 self.callback = callback
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001595 self.base.__init__(self, self.address, self.handler)
1596
1597 def serve_until_quit(self):
1598 import select
1599 self.quit = 0
1600 while not self.quit:
1601 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1602 if rd: self.handle_request()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001603
1604 def server_activate(self):
1605 self.base.server_activate(self)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001606 if self.callback: self.callback(self)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001607
1608 DocServer.base = BaseHTTPServer.HTTPServer
1609 DocServer.handler = DocHandler
1610 DocHandler.MessageClass = Message
1611 try:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001612 try:
1613 DocServer(port, callback).serve_until_quit()
1614 except (KeyboardInterrupt, select.error):
1615 pass
1616 finally:
Ka-Ping Yee66246962001-04-12 11:59:50 +00001617 if completer: completer()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001618
1619# ----------------------------------------------------- graphical interface
1620
1621def gui():
1622 """Graphical interface (starts web server and pops up a control window)."""
1623 class GUI:
1624 def __init__(self, window, port=7464):
1625 self.window = window
1626 self.server = None
1627 self.scanner = None
1628
1629 import Tkinter
1630 self.server_frm = Tkinter.Frame(window)
1631 self.title_lbl = Tkinter.Label(self.server_frm,
1632 text='Starting server...\n ')
1633 self.open_btn = Tkinter.Button(self.server_frm,
1634 text='open browser', command=self.open, state='disabled')
1635 self.quit_btn = Tkinter.Button(self.server_frm,
1636 text='quit serving', command=self.quit, state='disabled')
1637
1638 self.search_frm = Tkinter.Frame(window)
1639 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
1640 self.search_ent = Tkinter.Entry(self.search_frm)
1641 self.search_ent.bind('<Return>', self.search)
1642 self.stop_btn = Tkinter.Button(self.search_frm,
1643 text='stop', pady=0, command=self.stop, state='disabled')
1644 if sys.platform == 'win32':
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001645 # Trying to hide and show this button crashes under Windows.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001646 self.stop_btn.pack(side='right')
1647
1648 self.window.title('pydoc')
1649 self.window.protocol('WM_DELETE_WINDOW', self.quit)
1650 self.title_lbl.pack(side='top', fill='x')
1651 self.open_btn.pack(side='left', fill='x', expand=1)
1652 self.quit_btn.pack(side='right', fill='x', expand=1)
1653 self.server_frm.pack(side='top', fill='x')
1654
1655 self.search_lbl.pack(side='left')
1656 self.search_ent.pack(side='right', fill='x', expand=1)
1657 self.search_frm.pack(side='top', fill='x')
1658 self.search_ent.focus_set()
1659
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001660 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001661 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001662 self.result_lst.bind('<Button-1>', self.select)
1663 self.result_lst.bind('<Double-Button-1>', self.goto)
1664 self.result_scr = Tkinter.Scrollbar(window,
1665 orient='vertical', command=self.result_lst.yview)
1666 self.result_lst.config(yscrollcommand=self.result_scr.set)
1667
1668 self.result_frm = Tkinter.Frame(window)
1669 self.goto_btn = Tkinter.Button(self.result_frm,
1670 text='go to selected', command=self.goto)
1671 self.hide_btn = Tkinter.Button(self.result_frm,
1672 text='hide results', command=self.hide)
1673 self.goto_btn.pack(side='left', fill='x', expand=1)
1674 self.hide_btn.pack(side='right', fill='x', expand=1)
1675
1676 self.window.update()
1677 self.minwidth = self.window.winfo_width()
1678 self.minheight = self.window.winfo_height()
1679 self.bigminheight = (self.server_frm.winfo_reqheight() +
1680 self.search_frm.winfo_reqheight() +
1681 self.result_lst.winfo_reqheight() +
1682 self.result_frm.winfo_reqheight())
1683 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
1684 self.expanded = 0
1685 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1686 self.window.wm_minsize(self.minwidth, self.minheight)
1687
1688 import threading
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001689 threading.Thread(
1690 target=serve, args=(port, self.ready, self.quit)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001691
1692 def ready(self, server):
1693 self.server = server
1694 self.title_lbl.config(
1695 text='Python documentation server at\n' + server.url)
1696 self.open_btn.config(state='normal')
1697 self.quit_btn.config(state='normal')
1698
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001699 def open(self, event=None, url=None):
1700 url = url or self.server.url
1701 try:
1702 import webbrowser
1703 webbrowser.open(url)
1704 except ImportError: # pre-webbrowser.py compatibility
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001705 if sys.platform == 'win32':
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001706 os.system('start "%s"' % url)
1707 elif sys.platform == 'mac':
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001708 try: import ic
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001709 except ImportError: pass
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001710 else: ic.launchurl(url)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001711 else:
1712 rc = os.system('netscape -remote "openURL(%s)" &' % url)
1713 if rc: os.system('netscape "%s" &' % url)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001714
1715 def quit(self, event=None):
1716 if self.server:
1717 self.server.quit = 1
1718 self.window.quit()
1719
1720 def search(self, event=None):
1721 key = self.search_ent.get()
1722 self.stop_btn.pack(side='right')
1723 self.stop_btn.config(state='normal')
1724 self.search_lbl.config(text='Searching for "%s"...' % key)
1725 self.search_ent.forget()
1726 self.search_lbl.pack(side='left')
1727 self.result_lst.delete(0, 'end')
1728 self.goto_btn.config(state='disabled')
1729 self.expand()
1730
1731 import threading
1732 if self.scanner:
1733 self.scanner.quit = 1
1734 self.scanner = ModuleScanner()
1735 threading.Thread(target=self.scanner.run,
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +00001736 args=(self.update, key, self.done)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001737
1738 def update(self, path, modname, desc):
1739 if modname[-9:] == '.__init__':
1740 modname = modname[:-9] + ' (package)'
1741 self.result_lst.insert('end',
1742 modname + ' - ' + (desc or '(no description)'))
1743
1744 def stop(self, event=None):
1745 if self.scanner:
1746 self.scanner.quit = 1
1747 self.scanner = None
1748
1749 def done(self):
1750 self.scanner = None
1751 self.search_lbl.config(text='Search for')
1752 self.search_lbl.pack(side='left')
1753 self.search_ent.pack(side='right', fill='x', expand=1)
1754 if sys.platform != 'win32': self.stop_btn.forget()
1755 self.stop_btn.config(state='disabled')
1756
1757 def select(self, event=None):
1758 self.goto_btn.config(state='normal')
1759
1760 def goto(self, event=None):
1761 selection = self.result_lst.curselection()
1762 if selection:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001763 modname = split(self.result_lst.get(selection[0]))[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001764 self.open(url=self.server.url + modname + '.html')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001765
1766 def collapse(self):
1767 if not self.expanded: return
1768 self.result_frm.forget()
1769 self.result_scr.forget()
1770 self.result_lst.forget()
1771 self.bigwidth = self.window.winfo_width()
1772 self.bigheight = self.window.winfo_height()
1773 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1774 self.window.wm_minsize(self.minwidth, self.minheight)
1775 self.expanded = 0
1776
1777 def expand(self):
1778 if self.expanded: return
1779 self.result_frm.pack(side='bottom', fill='x')
1780 self.result_scr.pack(side='right', fill='y')
1781 self.result_lst.pack(side='top', fill='both', expand=1)
1782 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
1783 self.window.wm_minsize(self.minwidth, self.bigminheight)
1784 self.expanded = 1
1785
1786 def hide(self, event=None):
1787 self.stop()
1788 self.collapse()
1789
1790 import Tkinter
1791 try:
1792 gui = GUI(Tkinter.Tk())
1793 Tkinter.mainloop()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001794 except KeyboardInterrupt:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001795 pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001796
1797# -------------------------------------------------- command-line interface
1798
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001799def ispath(x):
1800 return type(x) is types.StringType and find(x, os.sep) >= 0
1801
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001802def cli():
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001803 """Command-line interface (looks at sys.argv to decide what to do)."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001804 import getopt
1805 class BadUsage: pass
1806
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001807 # Scripts don't get the current directory in their path by default.
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00001808 scriptdir = os.path.dirname(sys.argv[0])
1809 if scriptdir in sys.path:
1810 sys.path.remove(scriptdir)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001811 sys.path.insert(0, '.')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001812
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001813 try:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001814 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001815 writing = 0
1816
1817 for opt, val in opts:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001818 if opt == '-g':
1819 gui()
1820 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001821 if opt == '-k':
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001822 apropos(val)
1823 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001824 if opt == '-p':
1825 try:
1826 port = int(val)
1827 except ValueError:
1828 raise BadUsage
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001829 def ready(server):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001830 print 'pydoc server ready at %s' % server.url
1831 def stopped():
1832 print 'pydoc server stopped'
1833 serve(port, ready, stopped)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001834 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001835 if opt == '-w':
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001836 writing = 1
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001837
1838 if not args: raise BadUsage
1839 for arg in args:
1840 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001841 if ispath(arg) and os.path.isfile(arg):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001842 arg = importfile(arg)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001843 if writing:
1844 if ispath(arg) and os.path.isdir(arg):
1845 writedocs(arg)
1846 else:
1847 writedoc(arg)
1848 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001849 doc(arg)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001850 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001851 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001852
1853 except (getopt.error, BadUsage):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001854 cmd = sys.argv[0]
1855 print """pydoc - the Python documentation tool
1856
1857%s <name> ...
1858 Show text documentation on something. <name> may be the name of a
1859 function, module, or package, or a dotted reference to a class or
1860 function within a module or module in a package. If <name> contains
1861 a '%s', it is used as the path to a Python source file to document.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001862
1863%s -k <keyword>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001864 Search for a keyword in the synopsis lines of all available modules.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001865
1866%s -p <port>
1867 Start an HTTP server on the given port on the local machine.
1868
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001869%s -g
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001870 Pop up a graphical interface for finding and serving documentation.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001871
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001872%s -w <name> ...
1873 Write out the HTML documentation for a module to a file in the current
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001874 directory. If <name> contains a '%s', it is treated as a filename; if
1875 it names a directory, documentation is written for all the contents.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001876""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001877
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001878if __name__ == '__main__': cli()