blob: 4c7471afbc02c10c80b34c8207692e3787e6c70f [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
Fred Drake0a66fcb2001-07-23 19:44:30 +0000957 if os.environ.get('TERM') in ['dumb', 'emacs']:
Fred Drake5e9eb982001-07-23 19:48:10 +0000958 return plainpager
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000959 if os.environ.has_key('PAGER'):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000960 if sys.platform == 'win32': # pipes completely broken in Windows
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000961 return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
962 elif os.environ.get('TERM') in ['dumb', 'emacs']:
963 return lambda text: pipepager(plain(text), os.environ['PAGER'])
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000964 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000965 return lambda text: pipepager(text, os.environ['PAGER'])
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +0000966 if sys.platform == 'win32':
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000967 return lambda text: tempfilepager(plain(text), 'more <')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000968 if hasattr(os, 'system') and os.system('less 2>/dev/null') == 0:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000969 return lambda text: pipepager(text, 'less')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000970
971 import tempfile
972 filename = tempfile.mktemp()
973 open(filename, 'w').close()
974 try:
975 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
976 return lambda text: pipepager(text, 'more')
977 else:
978 return ttypager
979 finally:
980 os.unlink(filename)
981
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000982def plain(text):
983 """Remove boldface formatting from text."""
984 return re.sub('.\b', '', text)
985
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000986def pipepager(text, cmd):
987 """Page through text by feeding it to another program."""
988 pipe = os.popen(cmd, 'w')
989 try:
990 pipe.write(text)
991 pipe.close()
992 except IOError:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +0000993 pass # Ignore broken pipes caused by quitting the pager program.
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000994
995def tempfilepager(text, cmd):
996 """Page through text by invoking a program on a temporary file."""
997 import tempfile
998 filename = tempfile.mktemp()
999 file = open(filename, 'w')
1000 file.write(text)
1001 file.close()
1002 try:
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001003 os.system(cmd + ' ' + filename)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001004 finally:
1005 os.unlink(filename)
1006
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001007def ttypager(text):
1008 """Page through text on a text terminal."""
1009 lines = split(plain(text), '\n')
1010 try:
1011 import tty
1012 fd = sys.stdin.fileno()
1013 old = tty.tcgetattr(fd)
1014 tty.setcbreak(fd)
1015 getchar = lambda: sys.stdin.read(1)
Ka-Ping Yee457aab22001-02-27 23:36:29 +00001016 except (ImportError, AttributeError):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001017 tty = None
1018 getchar = lambda: sys.stdin.readline()[:-1][:1]
1019
1020 try:
1021 r = inc = os.environ.get('LINES', 25) - 1
1022 sys.stdout.write(join(lines[:inc], '\n') + '\n')
1023 while lines[r:]:
1024 sys.stdout.write('-- more --')
1025 sys.stdout.flush()
1026 c = getchar()
1027
1028 if c in ['q', 'Q']:
1029 sys.stdout.write('\r \r')
1030 break
1031 elif c in ['\r', '\n']:
1032 sys.stdout.write('\r \r' + lines[r] + '\n')
1033 r = r + 1
1034 continue
1035 if c in ['b', 'B', '\x1b']:
1036 r = r - inc - inc
1037 if r < 0: r = 0
1038 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1039 r = r + inc
1040
1041 finally:
1042 if tty:
1043 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1044
1045def plainpager(text):
1046 """Simply print unformatted text. This is the ultimate fallback."""
1047 sys.stdout.write(plain(text))
1048
1049def describe(thing):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001050 """Produce a short description of the given thing."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001051 if inspect.ismodule(thing):
1052 if thing.__name__ in sys.builtin_module_names:
1053 return 'built-in module ' + thing.__name__
1054 if hasattr(thing, '__path__'):
1055 return 'package ' + thing.__name__
1056 else:
1057 return 'module ' + thing.__name__
1058 if inspect.isbuiltin(thing):
1059 return 'built-in function ' + thing.__name__
1060 if inspect.isclass(thing):
1061 return 'class ' + thing.__name__
1062 if inspect.isfunction(thing):
1063 return 'function ' + thing.__name__
1064 if inspect.ismethod(thing):
1065 return 'method ' + thing.__name__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001066 if type(thing) is types.InstanceType:
1067 return 'instance of ' + thing.__class__.__name__
1068 return type(thing).__name__
1069
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001070def locate(path, forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001071 """Locate an object by name or dotted path, importing as necessary."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001072 parts = split(path, '.')
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001073 module, n = None, 0
1074 while n < len(parts):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001075 nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001076 if nextmodule: module, n = nextmodule, n + 1
1077 else: break
1078 if module:
1079 object = module
1080 for part in parts[n:]:
1081 try: object = getattr(object, part)
1082 except AttributeError: return None
1083 return object
1084 else:
1085 import __builtin__
1086 if hasattr(__builtin__, path):
1087 return getattr(__builtin__, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001088
1089# --------------------------------------- interactive interpreter interface
1090
1091text = TextDoc()
1092html = HTMLDoc()
1093
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001094def doc(thing, title='Python Library Documentation: %s', forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001095 """Display text documentation, given an object or a path to an object."""
1096 suffix, name = '', None
1097 if type(thing) is type(''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001098 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001099 object = locate(thing, forceload)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001100 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001101 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001102 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001103 if not object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001104 print 'no Python documentation found for %s' % repr(thing)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001105 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001106 parts = split(thing, '.')
1107 if len(parts) > 1: suffix = ' in ' + join(parts[:-1], '.')
1108 name = parts[-1]
1109 thing = object
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001110
1111 desc = describe(thing)
1112 module = inspect.getmodule(thing)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001113 if not suffix and module and module is not thing:
1114 suffix = ' in module ' + module.__name__
Ka-Ping Yee66246962001-04-12 11:59:50 +00001115 pager(title % (desc + suffix) + '\n\n' + text.document(thing, name))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001116
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001117def writedoc(key, forceload=0):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001118 """Write HTML documentation to a file in the current directory."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001119 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001120 object = locate(key, forceload)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001121 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001122 print value
1123 else:
1124 if object:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001125 page = html.page(describe(object),
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001126 html.document(object, object.__name__))
1127 file = open(key + '.html', 'w')
1128 file.write(page)
1129 file.close()
1130 print 'wrote', key + '.html'
1131 else:
1132 print 'no Python documentation found for %s' % repr(key)
1133
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001134def writedocs(dir, pkgpath='', done=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001135 """Write out HTML documentation for all modules in a directory tree."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001136 if done is None: done = {}
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001137 for file in os.listdir(dir):
1138 path = os.path.join(dir, file)
1139 if ispackage(path):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001140 writedocs(path, pkgpath + file + '.', done)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001141 elif os.path.isfile(path):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001142 modname = inspect.getmodulename(path)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001143 if modname:
1144 modname = pkgpath + modname
1145 if not done.has_key(modname):
1146 done[modname] = 1
1147 writedoc(modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001148
1149class Helper:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001150 keywords = {
1151 'and': 'BOOLEAN',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001152 'assert': ('ref/assert', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001153 'break': ('ref/break', 'while for'),
1154 'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1155 'continue': ('ref/continue', 'while for'),
1156 'def': ('ref/function', ''),
1157 'del': ('ref/del', 'BASICMETHODS'),
1158 'elif': 'if',
1159 'else': ('ref/if', 'while for'),
1160 'except': 'try',
1161 'exec': ('ref/exec', ''),
1162 'finally': 'try',
1163 'for': ('ref/for', 'break continue while'),
1164 'from': 'import',
1165 'global': ('ref/global', 'NAMESPACES'),
1166 'if': ('ref/if', 'TRUTHVALUE'),
1167 'import': ('ref/import', 'MODULES'),
1168 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1169 'is': 'COMPARISON',
1170 'lambda': ('ref/lambda', 'FUNCTIONS'),
1171 'not': 'BOOLEAN',
1172 'or': 'BOOLEAN',
1173 'pass': 'PASS',
1174 'print': ('ref/print', ''),
1175 'raise': ('ref/raise', 'EXCEPTIONS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001176 'return': ('ref/return', 'FUNCTIONS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001177 'try': ('ref/try', 'EXCEPTIONS'),
1178 'while': ('ref/while', 'break continue if TRUTHVALUE'),
1179 }
1180
1181 topics = {
1182 'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001183 'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001184 'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'),
1185 'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001186 'UNICODE': ('ref/unicode', 'encodings unicode TYPES STRING'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001187 'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1188 'INTEGER': ('ref/integers', 'int range'),
1189 'FLOAT': ('ref/floating', 'float math'),
1190 'COMPLEX': ('ref/imaginary', 'complex cmath'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001191 'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001192 'MAPPINGS': 'DICTIONARIES',
1193 'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'),
1194 'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'),
1195 'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001196 'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001197 'FRAMEOBJECTS': 'TYPES',
1198 'TRACEBACKS': 'TYPES',
1199 'NONE': ('lib/bltin-null-object', ''),
1200 'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'),
1201 'FILES': ('lib/bltin-file-objects', ''),
1202 'SPECIALATTRIBUTES': ('lib/specialattrs', ''),
1203 'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'),
1204 'MODULES': ('lib/typesmodules', 'import'),
1205 'PACKAGES': 'import',
1206 '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'),
1207 'OPERATORS': 'EXPRESSIONS',
1208 'PRECEDENCE': 'EXPRESSIONS',
1209 'OBJECTS': ('ref/objects', 'TYPES'),
1210 'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001211 'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'),
1212 'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1213 'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'),
1214 'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'),
1215 'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'),
1216 'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'),
1217 'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001218 'EXECUTION': ('ref/execframes', ''),
1219 'NAMESPACES': ('ref/execframes', 'global ASSIGNMENT DELETION'),
1220 'SCOPING': 'NAMESPACES',
1221 'FRAMES': 'NAMESPACES',
1222 'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'),
1223 'COERCIONS': 'CONVERSIONS',
1224 'CONVERSIONS': ('ref/conversions', ''),
1225 'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'),
1226 'SPECIALIDENTIFIERS': ('ref/id-classes', ''),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001227 'PRIVATENAMES': ('ref/atom-identifiers', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001228 'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1229 'TUPLES': 'SEQUENCES',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001230 'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001231 'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001232 'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001233 'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001234 'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'),
1235 'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001236 'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1237 'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'),
1238 'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'),
1239 'CALLS': ('ref/calls', 'EXPRESSIONS'),
1240 'POWER': ('ref/power', 'EXPRESSIONS'),
1241 'UNARY': ('ref/unary', 'EXPRESSIONS'),
1242 'BINARY': ('ref/binary', 'EXPRESSIONS'),
1243 'SHIFTING': ('ref/shifting', 'EXPRESSIONS'),
1244 'BITWISE': ('ref/bitwise', 'EXPRESSIONS'),
1245 'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001246 'BOOLEAN': ('ref/lambda', 'EXPRESSIONS TRUTHVALUE'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001247 'ASSERTION': 'assert',
1248 'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001249 'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001250 'DELETION': 'del',
1251 'PRINTING': 'print',
1252 'RETURNING': 'return',
1253 'IMPORTING': 'import',
1254 'CONDITIONAL': 'if',
1255 'LOOPING': ('ref/compound', 'for while break continue'),
1256 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001257 'DEBUGGING': ('lib/module-pdb', 'pdb'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001258 }
1259
1260 def __init__(self, input, output):
1261 self.input = input
1262 self.output = output
1263 self.docdir = None
1264 execdir = os.path.dirname(sys.executable)
1265 homedir = os.environ.get('PYTHONHOME')
1266 for dir in [os.environ.get('PYTHONDOCS'),
1267 homedir and os.path.join(homedir, 'doc'),
1268 os.path.join(execdir, 'doc'),
1269 '/usr/doc/python-docs-' + split(sys.version)[0],
1270 '/usr/doc/python-' + split(sys.version)[0],
1271 '/usr/doc/python-docs-' + sys.version[:3],
1272 '/usr/doc/python-' + sys.version[:3]]:
1273 if dir and os.path.isdir(os.path.join(dir, 'lib')):
1274 self.docdir = dir
1275
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001276 def __repr__(self):
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001277 if inspect.stack()[1][3] == '?':
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001278 self()
1279 return ''
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001280 return '<pydoc.Helper instance>'
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001281
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001282 def __call__(self, request=None):
1283 if request is not None:
1284 self.help(request)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001285 else:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001286 self.intro()
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001287 self.interact()
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001288 self.output.write('''
Fred Drakee61967f2001-05-10 18:41:02 +00001289You are now leaving help and returning to the Python interpreter.
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001290If you want to ask for help on a particular object directly from the
1291interpreter, you can type "help(object)". Executing "help('string')"
1292has the same effect as typing a particular string at the help> prompt.
1293''')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001294
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001295 def interact(self):
1296 self.output.write('\n')
1297 while 1:
1298 self.output.write('help> ')
1299 self.output.flush()
1300 try:
1301 request = self.input.readline()
1302 if not request: break
1303 except KeyboardInterrupt: break
1304 request = strip(replace(request, '"', '', "'", ''))
1305 if lower(request) in ['q', 'quit']: break
1306 self.help(request)
1307
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001308 def help(self, request):
1309 if type(request) is type(''):
1310 if request == 'help': self.intro()
1311 elif request == 'keywords': self.listkeywords()
1312 elif request == 'topics': self.listtopics()
1313 elif request == 'modules': self.listmodules()
1314 elif request[:8] == 'modules ':
1315 self.listmodules(split(request)[1])
1316 elif self.keywords.has_key(request): self.showtopic(request)
1317 elif self.topics.has_key(request): self.showtopic(request)
1318 elif request: doc(request, 'Help on %s:')
1319 elif isinstance(request, Helper): self()
1320 else: doc(request, 'Help on %s:')
1321 self.output.write('\n')
1322
1323 def intro(self):
1324 self.output.write('''
1325Welcome to Python %s! This is the online help utility.
1326
1327If this is your first time using Python, you should definitely check out
1328the tutorial on the Internet at http://www.python.org/doc/tut/.
1329
1330Enter the name of any module, keyword, or topic to get help on writing
1331Python programs and using Python modules. To quit this help utility and
1332return to the interpreter, just type "quit".
1333
1334To get a list of available modules, keywords, or topics, type "modules",
1335"keywords", or "topics". Each module also comes with a one-line summary
1336of what it does; to list the modules whose summaries contain a given word
1337such as "spam", type "modules spam".
1338''' % sys.version[:3])
1339
1340 def list(self, items, columns=4, width=80):
1341 items = items[:]
1342 items.sort()
1343 colw = width / columns
1344 rows = (len(items) + columns - 1) / columns
1345 for row in range(rows):
1346 for col in range(columns):
1347 i = col * rows + row
1348 if i < len(items):
1349 self.output.write(items[i])
1350 if col < columns - 1:
1351 self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1352 self.output.write('\n')
1353
1354 def listkeywords(self):
1355 self.output.write('''
1356Here is a list of the Python keywords. Enter any keyword to get more help.
1357
1358''')
1359 self.list(self.keywords.keys())
1360
1361 def listtopics(self):
1362 self.output.write('''
1363Here is a list of available topics. Enter any topic name to get more help.
1364
1365''')
1366 self.list(self.topics.keys())
1367
1368 def showtopic(self, topic):
1369 if not self.docdir:
1370 self.output.write('''
1371Sorry, topic and keyword documentation is not available because the Python
1372HTML documentation files could not be found. If you have installed them,
1373please set the environment variable PYTHONDOCS to indicate their location.
1374''')
1375 return
1376 target = self.topics.get(topic, self.keywords.get(topic))
1377 if not target:
1378 self.output.write('no documentation found for %s\n' % repr(topic))
1379 return
1380 if type(target) is type(''):
1381 return self.showtopic(target)
1382
1383 filename, xrefs = target
1384 filename = self.docdir + '/' + filename + '.html'
1385 try:
1386 file = open(filename)
1387 except:
1388 self.output.write('could not read docs from %s\n' % filename)
1389 return
1390
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001391 divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
1392 addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001393 document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1394 file.close()
1395
1396 import htmllib, formatter, StringIO
1397 buffer = StringIO.StringIO()
1398 parser = htmllib.HTMLParser(
1399 formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1400 parser.start_table = parser.do_p
1401 parser.end_table = lambda parser=parser: parser.do_p({})
1402 parser.start_tr = parser.do_br
1403 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1404 parser.feed(document)
1405 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
1406 pager(' ' + strip(buffer) + '\n')
Ka-Ping Yeeda793892001-04-13 11:02:51 +00001407 if xrefs:
1408 buffer = StringIO.StringIO()
1409 formatter.DumbWriter(buffer).send_flowing_data(
1410 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1411 self.output.write('\n%s\n' % buffer.getvalue())
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001412
1413 def listmodules(self, key=''):
1414 if key:
1415 self.output.write('''
1416Here is a list of matching modules. Enter any module name to get more help.
1417
1418''')
1419 apropos(key)
1420 else:
1421 self.output.write('''
1422Please wait a moment while I gather a list of all available modules...
1423
1424''')
1425 modules = {}
1426 def callback(path, modname, desc, modules=modules):
1427 if modname and modname[-9:] == '.__init__':
1428 modname = modname[:-9] + ' (package)'
1429 if find(modname, '.') < 0:
1430 modules[modname] = 1
1431 ModuleScanner().run(callback)
1432 self.list(modules.keys())
1433 self.output.write('''
1434Enter any module name to get more help. Or, type "modules spam" to search
1435for modules whose descriptions contain the word "spam".
1436''')
1437
1438help = Helper(sys.stdin, sys.stdout)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001439
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001440class Scanner:
1441 """A generic tree iterator."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001442 def __init__(self, roots, children, descendp):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001443 self.roots = roots[:]
1444 self.state = []
1445 self.children = children
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001446 self.descendp = descendp
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001447
1448 def next(self):
1449 if not self.state:
1450 if not self.roots:
1451 return None
1452 root = self.roots.pop(0)
1453 self.state = [(root, self.children(root))]
1454 node, children = self.state[-1]
1455 if not children:
1456 self.state.pop()
1457 return self.next()
1458 child = children.pop(0)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001459 if self.descendp(child):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001460 self.state.append((child, self.children(child)))
1461 return child
1462
1463class ModuleScanner(Scanner):
1464 """An interruptible scanner that searches module synopses."""
1465 def __init__(self):
1466 roots = map(lambda dir: (dir, ''), pathdirs())
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001467 Scanner.__init__(self, roots, self.submodules, self.isnewpackage)
1468 self.inodes = map(lambda (dir, pkg): os.stat(dir)[1], roots)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001469
1470 def submodules(self, (dir, package)):
1471 children = []
1472 for file in os.listdir(dir):
1473 path = os.path.join(dir, file)
Tim Peters30edd232001-03-16 08:29:48 +00001474 if ispackage(path):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001475 children.append((path, package + (package and '.') + file))
1476 else:
1477 children.append((path, package))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001478 children.sort() # so that spam.py comes before spam.pyc or spam.pyo
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001479 return children
1480
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001481 def isnewpackage(self, (dir, package)):
Ka-Ping Yee6191a232001-04-13 15:00:27 +00001482 inode = os.path.exists(dir) and os.stat(dir)[1]
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001483 if not (os.path.islink(dir) and inode in self.inodes):
Ka-Ping Yee6191a232001-04-13 15:00:27 +00001484 self.inodes.append(inode) # detect circular symbolic links
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001485 return ispackage(dir)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001486
Ka-Ping Yee66246962001-04-12 11:59:50 +00001487 def run(self, callback, key=None, completer=None):
1488 if key: key = lower(key)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001489 self.quit = 0
1490 seen = {}
1491
1492 for modname in sys.builtin_module_names:
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001493 if modname != '__main__':
1494 seen[modname] = 1
Ka-Ping Yee66246962001-04-12 11:59:50 +00001495 if key is None:
1496 callback(None, modname, '')
1497 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001498 desc = split(__import__(modname).__doc__ or '', '\n')[0]
Ka-Ping Yee66246962001-04-12 11:59:50 +00001499 if find(lower(modname + ' - ' + desc), key) >= 0:
1500 callback(None, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001501
1502 while not self.quit:
1503 node = self.next()
1504 if not node: break
1505 path, package = node
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001506 modname = inspect.getmodulename(path)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001507 if os.path.isfile(path) and modname:
1508 modname = package + (package and '.') + modname
1509 if not seen.has_key(modname):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001510 seen[modname] = 1 # if we see spam.py, skip spam.pyc
Ka-Ping Yee66246962001-04-12 11:59:50 +00001511 if key is None:
1512 callback(path, modname, '')
1513 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001514 desc = synopsis(path) or ''
1515 if find(lower(modname + ' - ' + desc), key) >= 0:
1516 callback(path, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001517 if completer: completer()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001518
1519def apropos(key):
1520 """Print all the one-line module summaries that contain a substring."""
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001521 def callback(path, modname, desc):
1522 if modname[-9:] == '.__init__':
1523 modname = modname[:-9] + ' (package)'
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001524 print modname, desc and '- ' + desc
1525 try: import warnings
1526 except ImportError: pass
1527 else: warnings.filterwarnings('ignore') # ignore problems during import
Ka-Ping Yee66246962001-04-12 11:59:50 +00001528 ModuleScanner().run(callback, key)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001529
1530# --------------------------------------------------- web browser interface
1531
Ka-Ping Yee66246962001-04-12 11:59:50 +00001532def serve(port, callback=None, completer=None):
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001533 import BaseHTTPServer, mimetools, select
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001534
1535 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1536 class Message(mimetools.Message):
1537 def __init__(self, fp, seekable=1):
1538 Message = self.__class__
1539 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1540 self.encodingheader = self.getheader('content-transfer-encoding')
1541 self.typeheader = self.getheader('content-type')
1542 self.parsetype()
1543 self.parseplist()
1544
1545 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1546 def send_document(self, title, contents):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001547 try:
1548 self.send_response(200)
1549 self.send_header('Content-Type', 'text/html')
1550 self.end_headers()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001551 self.wfile.write(html.page(title, contents))
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001552 except IOError: pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001553
1554 def do_GET(self):
1555 path = self.path
1556 if path[-5:] == '.html': path = path[:-5]
1557 if path[:1] == '/': path = path[1:]
1558 if path and path != '.':
1559 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001560 obj = locate(path, forceload=1)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001561 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001562 self.send_document(path, html.escape(str(value)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001563 return
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001564 if obj:
1565 self.send_document(describe(obj), html.document(obj, path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001566 else:
1567 self.send_document(path,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001568'no Python documentation found for %s' % repr(path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001569 else:
1570 heading = html.heading(
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001571'<big><big><strong>Python: Index of Modules</strong></big></big>',
1572'#ffffff', '#7799ee')
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001573 def bltinlink(name):
1574 return '<a href="%s.html">%s</a>' % (name, name)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001575 names = filter(lambda x: x != '__main__',
1576 sys.builtin_module_names)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001577 contents = html.multicolumn(names, bltinlink)
1578 indices = ['<p>' + html.bigsection(
1579 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1580
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001581 seen = {}
1582 for dir in pathdirs():
1583 indices.append(html.index(dir, seen))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001584 contents = heading + join(indices) + '''<p align=right>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001585<small><small><font color="#909090" face="helvetica, arial"><strong>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001586pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font></small></small>'''
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001587 self.send_document('Index of Modules', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001588
1589 def log_message(self, *args): pass
1590
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001591 class DocServer(BaseHTTPServer.HTTPServer):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001592 def __init__(self, port, callback):
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001593 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001594 self.address = ('', port)
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001595 self.url = 'http://%s:%d/' % (host, port)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001596 self.callback = callback
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001597 self.base.__init__(self, self.address, self.handler)
1598
1599 def serve_until_quit(self):
1600 import select
1601 self.quit = 0
1602 while not self.quit:
1603 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1604 if rd: self.handle_request()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001605
1606 def server_activate(self):
1607 self.base.server_activate(self)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001608 if self.callback: self.callback(self)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001609
1610 DocServer.base = BaseHTTPServer.HTTPServer
1611 DocServer.handler = DocHandler
1612 DocHandler.MessageClass = Message
1613 try:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001614 try:
1615 DocServer(port, callback).serve_until_quit()
1616 except (KeyboardInterrupt, select.error):
1617 pass
1618 finally:
Ka-Ping Yee66246962001-04-12 11:59:50 +00001619 if completer: completer()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001620
1621# ----------------------------------------------------- graphical interface
1622
1623def gui():
1624 """Graphical interface (starts web server and pops up a control window)."""
1625 class GUI:
1626 def __init__(self, window, port=7464):
1627 self.window = window
1628 self.server = None
1629 self.scanner = None
1630
1631 import Tkinter
1632 self.server_frm = Tkinter.Frame(window)
1633 self.title_lbl = Tkinter.Label(self.server_frm,
1634 text='Starting server...\n ')
1635 self.open_btn = Tkinter.Button(self.server_frm,
1636 text='open browser', command=self.open, state='disabled')
1637 self.quit_btn = Tkinter.Button(self.server_frm,
1638 text='quit serving', command=self.quit, state='disabled')
1639
1640 self.search_frm = Tkinter.Frame(window)
1641 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
1642 self.search_ent = Tkinter.Entry(self.search_frm)
1643 self.search_ent.bind('<Return>', self.search)
1644 self.stop_btn = Tkinter.Button(self.search_frm,
1645 text='stop', pady=0, command=self.stop, state='disabled')
1646 if sys.platform == 'win32':
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001647 # Trying to hide and show this button crashes under Windows.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001648 self.stop_btn.pack(side='right')
1649
1650 self.window.title('pydoc')
1651 self.window.protocol('WM_DELETE_WINDOW', self.quit)
1652 self.title_lbl.pack(side='top', fill='x')
1653 self.open_btn.pack(side='left', fill='x', expand=1)
1654 self.quit_btn.pack(side='right', fill='x', expand=1)
1655 self.server_frm.pack(side='top', fill='x')
1656
1657 self.search_lbl.pack(side='left')
1658 self.search_ent.pack(side='right', fill='x', expand=1)
1659 self.search_frm.pack(side='top', fill='x')
1660 self.search_ent.focus_set()
1661
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001662 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001663 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001664 self.result_lst.bind('<Button-1>', self.select)
1665 self.result_lst.bind('<Double-Button-1>', self.goto)
1666 self.result_scr = Tkinter.Scrollbar(window,
1667 orient='vertical', command=self.result_lst.yview)
1668 self.result_lst.config(yscrollcommand=self.result_scr.set)
1669
1670 self.result_frm = Tkinter.Frame(window)
1671 self.goto_btn = Tkinter.Button(self.result_frm,
1672 text='go to selected', command=self.goto)
1673 self.hide_btn = Tkinter.Button(self.result_frm,
1674 text='hide results', command=self.hide)
1675 self.goto_btn.pack(side='left', fill='x', expand=1)
1676 self.hide_btn.pack(side='right', fill='x', expand=1)
1677
1678 self.window.update()
1679 self.minwidth = self.window.winfo_width()
1680 self.minheight = self.window.winfo_height()
1681 self.bigminheight = (self.server_frm.winfo_reqheight() +
1682 self.search_frm.winfo_reqheight() +
1683 self.result_lst.winfo_reqheight() +
1684 self.result_frm.winfo_reqheight())
1685 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
1686 self.expanded = 0
1687 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1688 self.window.wm_minsize(self.minwidth, self.minheight)
1689
1690 import threading
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001691 threading.Thread(
1692 target=serve, args=(port, self.ready, self.quit)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001693
1694 def ready(self, server):
1695 self.server = server
1696 self.title_lbl.config(
1697 text='Python documentation server at\n' + server.url)
1698 self.open_btn.config(state='normal')
1699 self.quit_btn.config(state='normal')
1700
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001701 def open(self, event=None, url=None):
1702 url = url or self.server.url
1703 try:
1704 import webbrowser
1705 webbrowser.open(url)
1706 except ImportError: # pre-webbrowser.py compatibility
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001707 if sys.platform == 'win32':
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001708 os.system('start "%s"' % url)
1709 elif sys.platform == 'mac':
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001710 try: import ic
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001711 except ImportError: pass
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001712 else: ic.launchurl(url)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001713 else:
1714 rc = os.system('netscape -remote "openURL(%s)" &' % url)
1715 if rc: os.system('netscape "%s" &' % url)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001716
1717 def quit(self, event=None):
1718 if self.server:
1719 self.server.quit = 1
1720 self.window.quit()
1721
1722 def search(self, event=None):
1723 key = self.search_ent.get()
1724 self.stop_btn.pack(side='right')
1725 self.stop_btn.config(state='normal')
1726 self.search_lbl.config(text='Searching for "%s"...' % key)
1727 self.search_ent.forget()
1728 self.search_lbl.pack(side='left')
1729 self.result_lst.delete(0, 'end')
1730 self.goto_btn.config(state='disabled')
1731 self.expand()
1732
1733 import threading
1734 if self.scanner:
1735 self.scanner.quit = 1
1736 self.scanner = ModuleScanner()
1737 threading.Thread(target=self.scanner.run,
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +00001738 args=(self.update, key, self.done)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001739
1740 def update(self, path, modname, desc):
1741 if modname[-9:] == '.__init__':
1742 modname = modname[:-9] + ' (package)'
1743 self.result_lst.insert('end',
1744 modname + ' - ' + (desc or '(no description)'))
1745
1746 def stop(self, event=None):
1747 if self.scanner:
1748 self.scanner.quit = 1
1749 self.scanner = None
1750
1751 def done(self):
1752 self.scanner = None
1753 self.search_lbl.config(text='Search for')
1754 self.search_lbl.pack(side='left')
1755 self.search_ent.pack(side='right', fill='x', expand=1)
1756 if sys.platform != 'win32': self.stop_btn.forget()
1757 self.stop_btn.config(state='disabled')
1758
1759 def select(self, event=None):
1760 self.goto_btn.config(state='normal')
1761
1762 def goto(self, event=None):
1763 selection = self.result_lst.curselection()
1764 if selection:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001765 modname = split(self.result_lst.get(selection[0]))[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001766 self.open(url=self.server.url + modname + '.html')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001767
1768 def collapse(self):
1769 if not self.expanded: return
1770 self.result_frm.forget()
1771 self.result_scr.forget()
1772 self.result_lst.forget()
1773 self.bigwidth = self.window.winfo_width()
1774 self.bigheight = self.window.winfo_height()
1775 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1776 self.window.wm_minsize(self.minwidth, self.minheight)
1777 self.expanded = 0
1778
1779 def expand(self):
1780 if self.expanded: return
1781 self.result_frm.pack(side='bottom', fill='x')
1782 self.result_scr.pack(side='right', fill='y')
1783 self.result_lst.pack(side='top', fill='both', expand=1)
1784 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
1785 self.window.wm_minsize(self.minwidth, self.bigminheight)
1786 self.expanded = 1
1787
1788 def hide(self, event=None):
1789 self.stop()
1790 self.collapse()
1791
1792 import Tkinter
1793 try:
1794 gui = GUI(Tkinter.Tk())
1795 Tkinter.mainloop()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001796 except KeyboardInterrupt:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001797 pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001798
1799# -------------------------------------------------- command-line interface
1800
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001801def ispath(x):
1802 return type(x) is types.StringType and find(x, os.sep) >= 0
1803
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001804def cli():
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001805 """Command-line interface (looks at sys.argv to decide what to do)."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001806 import getopt
1807 class BadUsage: pass
1808
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001809 # Scripts don't get the current directory in their path by default.
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00001810 scriptdir = os.path.dirname(sys.argv[0])
1811 if scriptdir in sys.path:
1812 sys.path.remove(scriptdir)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001813 sys.path.insert(0, '.')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001814
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001815 try:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001816 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001817 writing = 0
1818
1819 for opt, val in opts:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001820 if opt == '-g':
1821 gui()
1822 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001823 if opt == '-k':
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001824 apropos(val)
1825 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001826 if opt == '-p':
1827 try:
1828 port = int(val)
1829 except ValueError:
1830 raise BadUsage
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001831 def ready(server):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001832 print 'pydoc server ready at %s' % server.url
1833 def stopped():
1834 print 'pydoc server stopped'
1835 serve(port, ready, stopped)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001836 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001837 if opt == '-w':
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001838 writing = 1
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001839
1840 if not args: raise BadUsage
1841 for arg in args:
1842 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001843 if ispath(arg) and os.path.isfile(arg):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001844 arg = importfile(arg)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001845 if writing:
1846 if ispath(arg) and os.path.isdir(arg):
1847 writedocs(arg)
1848 else:
1849 writedoc(arg)
1850 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001851 doc(arg)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001852 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001853 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001854
1855 except (getopt.error, BadUsage):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001856 cmd = sys.argv[0]
1857 print """pydoc - the Python documentation tool
1858
1859%s <name> ...
1860 Show text documentation on something. <name> may be the name of a
1861 function, module, or package, or a dotted reference to a class or
1862 function within a module or module in a package. If <name> contains
1863 a '%s', it is used as the path to a Python source file to document.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001864
1865%s -k <keyword>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001866 Search for a keyword in the synopsis lines of all available modules.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001867
1868%s -p <port>
1869 Start an HTTP server on the given port on the local machine.
1870
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001871%s -g
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001872 Pop up a graphical interface for finding and serving documentation.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001873
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001874%s -w <name> ...
1875 Write out the HTML documentation for a module to a file in the current
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001876 directory. If <name> contains a '%s', it is treated as a filename; if
1877 it names a directory, documentation is written for all the contents.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001878""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001879
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001880if __name__ == '__main__': cli()