blob: 962e32ebb7eec1236b9cde4c2649bd5aeea8df06 [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 Yee5a804ed2001-04-10 11:46:02 +000038# Known bugs that can't be fixed here:
39# - imp.load_module() cannot be prevented from clobbering existing
40# loaded modules, so calling synopsis() on a binary module file
41# changes the contents of any existing module with the same name.
42# - If the __file__ attribute on a module is a relative path and
43# the current directory is changed with os.chdir(), an incorrect
44# path will be displayed.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000045
Raymond Hettinger32200ae2002-06-01 19:51:15 +000046import sys, imp, os, re, types, inspect
Ka-Ping Yeedd175342001-02-27 14:43:46 +000047from repr import Repr
Ka-Ping Yee3bda8792001-03-23 13:17:50 +000048from string import expandtabs, find, join, lower, split, strip, rfind, rstrip
Ka-Ping Yeedd175342001-02-27 14:43:46 +000049
50# --------------------------------------------------------- common routines
51
Ka-Ping Yeedd175342001-02-27 14:43:46 +000052def pathdirs():
53 """Convert sys.path into a list of absolute, existing, unique paths."""
54 dirs = []
Ka-Ping Yee1d384632001-03-01 00:24:32 +000055 normdirs = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +000056 for dir in sys.path:
57 dir = os.path.abspath(dir or '.')
Ka-Ping Yee1d384632001-03-01 00:24:32 +000058 normdir = os.path.normcase(dir)
59 if normdir not in normdirs and os.path.isdir(dir):
Ka-Ping Yeedd175342001-02-27 14:43:46 +000060 dirs.append(dir)
Ka-Ping Yee1d384632001-03-01 00:24:32 +000061 normdirs.append(normdir)
Ka-Ping Yeedd175342001-02-27 14:43:46 +000062 return dirs
63
64def getdoc(object):
65 """Get the doc string or comments for an object."""
Ka-Ping Yee3bda8792001-03-23 13:17:50 +000066 result = inspect.getdoc(object) or inspect.getcomments(object)
Ka-Ping Yee239432a2001-03-02 02:45:08 +000067 return result and re.sub('^ *\n', '', rstrip(result)) or ''
Ka-Ping Yeedd175342001-02-27 14:43:46 +000068
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +000069def splitdoc(doc):
70 """Split a doc string into a synopsis line (if any) and the rest."""
71 lines = split(strip(doc), '\n')
72 if len(lines) == 1:
73 return lines[0], ''
74 elif len(lines) >= 2 and not rstrip(lines[1]):
75 return lines[0], join(lines[2:], '\n')
76 return '', join(lines, '\n')
77
Ka-Ping Yeedd175342001-02-27 14:43:46 +000078def classname(object, modname):
79 """Get a class name and qualify it with a module name if necessary."""
80 name = object.__name__
81 if object.__module__ != modname:
82 name = object.__module__ + '.' + name
83 return name
84
Ka-Ping Yeedec96e92001-04-13 09:55:49 +000085def isdata(object):
86 """Check if an object is of a type that probably means it's data."""
87 return not (inspect.ismodule(object) or inspect.isclass(object) or
88 inspect.isroutine(object) or inspect.isframe(object) or
89 inspect.istraceback(object) or inspect.iscode(object))
Ka-Ping Yeedd175342001-02-27 14:43:46 +000090
91def replace(text, *pairs):
92 """Do a series of global replacements on a string."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +000093 while pairs:
94 text = join(split(text, pairs[0]), pairs[1])
95 pairs = pairs[2:]
Ka-Ping Yeedd175342001-02-27 14:43:46 +000096 return text
97
98def cram(text, maxlen):
99 """Omit part of a string if needed to make it fit in a maximum length."""
100 if len(text) > maxlen:
101 pre = max(0, (maxlen-3)/2)
102 post = max(0, maxlen-3-pre)
103 return text[:pre] + '...' + text[len(text)-post:]
104 return text
105
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000106def stripid(text):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000107 """Remove the hexadecimal id from a Python object representation."""
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000108 # The behaviour of %p is implementation-dependent; we check two cases.
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000109 for pattern in [' at 0x[0-9a-f]{6,}>$', ' at [0-9A-F]{8,}>$']:
110 if re.search(pattern, repr(Exception)):
111 return re.sub(pattern, '>', text)
112 return text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000113
Tim Peters536d2262001-09-20 05:13:38 +0000114def _is_some_method(object):
115 return inspect.ismethod(object) or inspect.ismethoddescriptor(object)
116
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000117def allmethods(cl):
118 methods = {}
Tim Peters536d2262001-09-20 05:13:38 +0000119 for key, value in inspect.getmembers(cl, _is_some_method):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000120 methods[key] = 1
121 for base in cl.__bases__:
122 methods.update(allmethods(base)) # all your base are belong to us
123 for key in methods.keys():
124 methods[key] = getattr(cl, key)
125 return methods
126
Tim Petersfa26f7c2001-09-24 08:05:11 +0000127def _split_list(s, predicate):
128 """Split sequence s via predicate, and return pair ([true], [false]).
129
130 The return value is a 2-tuple of lists,
131 ([x for x in s if predicate(x)],
132 [x for x in s if not predicate(x)])
133 """
134
Tim Peters28355492001-09-23 21:29:55 +0000135 yes = []
136 no = []
Tim Petersfa26f7c2001-09-24 08:05:11 +0000137 for x in s:
138 if predicate(x):
139 yes.append(x)
Tim Peters28355492001-09-23 21:29:55 +0000140 else:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000141 no.append(x)
Tim Peters28355492001-09-23 21:29:55 +0000142 return yes, no
143
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000144# ----------------------------------------------------- module manipulation
145
146def ispackage(path):
147 """Guess whether a path refers to a package directory."""
148 if os.path.isdir(path):
149 for ext in ['.py', '.pyc', '.pyo']:
150 if os.path.isfile(os.path.join(path, '__init__' + ext)):
Tim Petersbc0e9102002-04-04 22:55:58 +0000151 return True
152 return False
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000153
154def synopsis(filename, cache={}):
155 """Get the one-line summary out of a module file."""
Raymond Hettinger32200ae2002-06-01 19:51:15 +0000156 mtime = os.stat(filename).st_mtime
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000157 lastupdate, result = cache.get(filename, (0, None))
158 if lastupdate < mtime:
159 info = inspect.getmoduleinfo(filename)
160 file = open(filename)
161 if info and 'b' in info[2]: # binary modules have to be imported
162 try: module = imp.load_module('__temp__', file, filename, info[1:])
163 except: return None
164 result = split(module.__doc__ or '', '\n')[0]
165 del sys.modules['__temp__']
166 else: # text modules can be directly examined
167 line = file.readline()
168 while line[:1] == '#' or not strip(line):
169 line = file.readline()
170 if not line: break
171 line = strip(line)
172 if line[:4] == 'r"""': line = line[1:]
173 if line[:3] == '"""':
174 line = line[3:]
175 if line[-1:] == '\\': line = line[:-1]
176 while not strip(line):
177 line = file.readline()
178 if not line: break
179 result = strip(split(line, '"""')[0])
180 else: result = None
181 file.close()
182 cache[filename] = (mtime, result)
183 return result
184
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000185class ErrorDuringImport(Exception):
186 """Errors that occurred while trying to import something to document it."""
187 def __init__(self, filename, (exc, value, tb)):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000188 self.filename = filename
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000189 self.exc = exc
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000190 self.value = value
191 self.tb = tb
192
193 def __str__(self):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000194 exc = self.exc
195 if type(exc) is types.ClassType:
196 exc = exc.__name__
197 return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000198
199def importfile(path):
200 """Import a Python source file or compiled file given its path."""
201 magic = imp.get_magic()
202 file = open(path, 'r')
203 if file.read(len(magic)) == magic:
204 kind = imp.PY_COMPILED
205 else:
206 kind = imp.PY_SOURCE
207 file.close()
208 filename = os.path.basename(path)
209 name, ext = os.path.splitext(filename)
210 file = open(path, 'r')
211 try:
212 module = imp.load_module(name, file, path, (ext, 'r', kind))
213 except:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000214 raise ErrorDuringImport(path, sys.exc_info())
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000215 file.close()
216 return module
217
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000218def safeimport(path, forceload=0, cache={}):
219 """Import a module; handle errors; return None if the module isn't found.
220
221 If the module *is* found but an exception occurs, it's wrapped in an
222 ErrorDuringImport exception and reraised. Unlike __import__, if a
223 package path is specified, the module at the end of the path is returned,
224 not the package at the beginning. If the optional 'forceload' argument
225 is 1, we reload the module from disk (unless it's a dynamic extension)."""
Raymond Hettinger54f02222002-06-01 14:18:47 +0000226 if forceload and path in sys.modules:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000227 # This is the only way to be sure. Checking the mtime of the file
228 # isn't good enough (e.g. what if the module contains a class that
229 # inherits from another module that has changed?).
230 if path not in sys.builtin_module_names:
231 # Python never loads a dynamic extension a second time from the
232 # same path, even if the file is changed or missing. Deleting
233 # the entry in sys.modules doesn't help for dynamic extensions,
234 # so we're not even going to try to keep them up to date.
235 info = inspect.getmoduleinfo(sys.modules[path].__file__)
236 if info[3] != imp.C_EXTENSION:
237 cache[path] = sys.modules[path] # prevent module from clearing
238 del sys.modules[path]
239 try:
240 module = __import__(path)
241 except:
242 # Did the error occur before or after the module was found?
243 (exc, value, tb) = info = sys.exc_info()
Raymond Hettinger54f02222002-06-01 14:18:47 +0000244 if path in sys.modules:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000245 # An error occured while executing the imported module.
246 raise ErrorDuringImport(sys.modules[path].__file__, info)
247 elif exc is SyntaxError:
248 # A SyntaxError occurred before we could execute the module.
249 raise ErrorDuringImport(value.filename, info)
250 elif exc is ImportError and \
251 split(lower(str(value)))[:2] == ['no', 'module']:
252 # The module was not found.
253 return None
254 else:
255 # Some other error occurred during the importing process.
256 raise ErrorDuringImport(path, sys.exc_info())
257 for part in split(path, '.')[1:]:
258 try: module = getattr(module, part)
259 except AttributeError: return None
260 return module
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000261
262# ---------------------------------------------------- formatter base class
263
264class Doc:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000265 def document(self, object, name=None, *args):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000266 """Generate documentation for an object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000267 args = (object, name) + args
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000268 if inspect.ismodule(object): return apply(self.docmodule, args)
269 if inspect.isclass(object): return apply(self.docclass, args)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000270 if inspect.isroutine(object): return apply(self.docroutine, args)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000271 return apply(self.docother, args)
272
273 def fail(self, object, name=None, *args):
274 """Raise an exception for unimplemented types."""
275 message = "don't know how to document object%s of type %s" % (
276 name and ' ' + repr(name), type(object).__name__)
277 raise TypeError, message
278
279 docmodule = docclass = docroutine = docother = fail
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000280
281# -------------------------------------------- HTML documentation generator
282
283class HTMLRepr(Repr):
284 """Class for safely making an HTML representation of a Python object."""
285 def __init__(self):
286 Repr.__init__(self)
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000287 self.maxlist = self.maxtuple = 20
288 self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000289 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000290
291 def escape(self, text):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000292 return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000293
294 def repr(self, object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000295 return Repr.repr(self, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000296
297 def repr1(self, x, level):
298 methodname = 'repr_' + join(split(type(x).__name__), '_')
299 if hasattr(self, methodname):
300 return getattr(self, methodname)(x, level)
301 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000302 return self.escape(cram(stripid(repr(x)), self.maxother))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000303
304 def repr_string(self, x, level):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000305 test = cram(x, self.maxstring)
306 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000307 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000308 # Backslashes are only literal in the string and are never
309 # needed to make any special characters, so show a raw string.
310 return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000311 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000312 r'<font color="#c040c0">\1</font>',
313 self.escape(testrepr))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000314
Skip Montanarodf708782002-03-07 22:58:02 +0000315 repr_str = repr_string
316
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000317 def repr_instance(self, x, level):
318 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000319 return self.escape(cram(stripid(repr(x)), self.maxstring))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000320 except:
321 return self.escape('<%s instance>' % x.__class__.__name__)
322
323 repr_unicode = repr_string
324
325class HTMLDoc(Doc):
326 """Formatter class for HTML documentation."""
327
328 # ------------------------------------------- HTML formatting utilities
329
330 _repr_instance = HTMLRepr()
331 repr = _repr_instance.repr
332 escape = _repr_instance.escape
333
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000334 def page(self, title, contents):
335 """Format an HTML page."""
336 return '''
Tim Peters59ed4482001-10-31 04:20:26 +0000337<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000338<html><head><title>Python: %s</title>
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000339<style type="text/css"><!--
Ka-Ping Yeed03f8fe2001-04-13 15:04:32 +0000340TT { font-family: lucidatypewriter, lucida console, courier }
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000341--></style></head><body bgcolor="#f0f0f8">
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000342%s
343</body></html>''' % (title, contents)
344
345 def heading(self, title, fgcol, bgcol, extras=''):
346 """Format a page heading."""
347 return '''
Tim Peters59ed4482001-10-31 04:20:26 +0000348<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading">
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000349<tr bgcolor="%s">
Tim Peters2306d242001-09-25 03:18:32 +0000350<td valign=bottom>&nbsp;<br>
351<font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000352><td align=right valign=bottom
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000353><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000354 ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
355
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000356 def section(self, title, fgcol, bgcol, contents, width=10,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000357 prelude='', marginalia=None, gap='&nbsp;&nbsp;'):
358 """Format a section with a heading."""
359 if marginalia is None:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000360 marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000361 result = '''
Tim Peters59ed4482001-10-31 04:20:26 +0000362<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000363<tr bgcolor="%s">
Tim Peters2306d242001-09-25 03:18:32 +0000364<td colspan=3 valign=bottom>&nbsp;<br>
365<font color="%s" face="helvetica, arial">%s</font></td></tr>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000366 ''' % (bgcol, fgcol, title)
367 if prelude:
368 result = result + '''
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000369<tr bgcolor="%s"><td rowspan=2>%s</td>
370<td colspan=2>%s</td></tr>
371<tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
372 else:
373 result = result + '''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000374<tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000375
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000376 return result + '\n<td width="100%%">%s</td></tr></table>' % contents
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000377
378 def bigsection(self, title, *args):
379 """Format a section with a big heading."""
380 title = '<big><strong>%s</strong></big>' % title
381 return apply(self.section, (title,) + args)
382
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000383 def preformat(self, text):
384 """Format literal preformatted text."""
385 text = self.escape(expandtabs(text))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000386 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
387 ' ', '&nbsp;', '\n', '<br>\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000388
389 def multicolumn(self, list, format, cols=4):
390 """Format a list of items into a multi-column list."""
391 result = ''
392 rows = (len(list)+cols-1)/cols
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000393 for col in range(cols):
394 result = result + '<td width="%d%%" valign=top>' % (100/cols)
395 for i in range(rows*col, rows*col+rows):
396 if i < len(list):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000397 result = result + format(list[i]) + '<br>\n'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000398 result = result + '</td>'
Tim Peters59ed4482001-10-31 04:20:26 +0000399 return '<table width="100%%" summary="list"><tr>%s</tr></table>' % result
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000400
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000401 def grey(self, text): return '<font color="#909090">%s</font>' % text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000402
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000403 def namelink(self, name, *dicts):
404 """Make a link for an identifier, given name-to-URL mappings."""
405 for dict in dicts:
Raymond Hettinger54f02222002-06-01 14:18:47 +0000406 if name in dict:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000407 return '<a href="%s">%s</a>' % (dict[name], name)
408 return name
409
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000410 def classlink(self, object, modname):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000411 """Make a link for a class."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000412 name, module = object.__name__, sys.modules.get(object.__module__)
413 if hasattr(module, name) and getattr(module, name) is object:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000414 return '<a href="%s.html#%s">%s</a>' % (
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000415 module.__name__, name, classname(object, modname))
416 return classname(object, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000417
418 def modulelink(self, object):
419 """Make a link for a module."""
420 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
421
422 def modpkglink(self, (name, path, ispackage, shadowed)):
423 """Make a link for a module or package to display in an index."""
424 if shadowed:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000425 return self.grey(name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000426 if path:
427 url = '%s.%s.html' % (path, name)
428 else:
429 url = '%s.html' % name
430 if ispackage:
431 text = '<strong>%s</strong>&nbsp;(package)' % name
432 else:
433 text = name
434 return '<a href="%s">%s</a>' % (url, text)
435
436 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
437 """Mark up some plain text, given a context of symbols to look for.
438 Each context dictionary maps object names to anchor names."""
439 escape = escape or self.escape
440 results = []
441 here = 0
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000442 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
443 r'RFC[- ]?(\d+)|'
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000444 r'PEP[- ]?(\d+)|'
Neil Schemenauerd69711c2002-03-24 23:02:07 +0000445 r'(self\.)?(\w+))')
Guido van Rossum8ca162f2002-04-07 06:36:23 +0000446 while True:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000447 match = pattern.search(text, here)
448 if not match: break
449 start, end = match.span()
450 results.append(escape(text[here:start]))
451
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000452 all, scheme, rfc, pep, selfdot, name = match.groups()
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000453 if scheme:
Neil Schemenauercddc1a02002-03-24 23:11:21 +0000454 url = escape(all).replace('"', '&quot;')
455 results.append('<a href="%s">%s</a>' % (url, url))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000456 elif rfc:
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000457 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
458 results.append('<a href="%s">%s</a>' % (url, escape(all)))
459 elif pep:
460 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000461 results.append('<a href="%s">%s</a>' % (url, escape(all)))
462 elif text[end:end+1] == '(':
463 results.append(self.namelink(name, methods, funcs, classes))
464 elif selfdot:
465 results.append('self.<strong>%s</strong>' % name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000466 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000467 results.append(self.namelink(name, classes))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000468 here = end
469 results.append(escape(text[here:]))
470 return join(results, '')
471
472 # ---------------------------------------------- type-specific routines
473
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000474 def formattree(self, tree, modname, parent=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000475 """Produce HTML for a class tree as given by inspect.getclasstree()."""
476 result = ''
477 for entry in tree:
478 if type(entry) is type(()):
479 c, bases = entry
Tim Peters2306d242001-09-25 03:18:32 +0000480 result = result + '<dt><font face="helvetica, arial">'
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000481 result = result + self.classlink(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000482 if bases and bases != (parent,):
483 parents = []
484 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000485 parents.append(self.classlink(base, modname))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000486 result = result + '(' + join(parents, ', ') + ')'
Tim Peters2306d242001-09-25 03:18:32 +0000487 result = result + '\n</font></dt>'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000488 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000489 result = result + '<dd>\n%s</dd>\n' % self.formattree(
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000490 entry, modname, c)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000491 return '<dl>\n%s</dl>\n' % result
492
Tim Peters8dd7ade2001-10-18 19:56:17 +0000493 def docmodule(self, object, name=None, mod=None, *ignored):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000494 """Produce HTML documentation for a module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000495 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000496 parts = split(name, '.')
497 links = []
498 for i in range(len(parts)-1):
499 links.append(
500 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
501 (join(parts[:i+1], '.'), parts[i]))
502 linkedname = join(links + parts[-1:], '.')
503 head = '<big><big><strong>%s</strong></big></big>' % linkedname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000504 try:
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000505 path = inspect.getabsfile(object)
Ka-Ping Yee6191a232001-04-13 15:00:27 +0000506 url = path
507 if sys.platform == 'win32':
508 import nturl2path
509 url = nturl2path.pathname2url(path)
510 filelink = '<a href="file:%s">%s</a>' % (url, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000511 except TypeError:
512 filelink = '(built-in)'
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000513 info = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000514 if hasattr(object, '__version__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000515 version = str(object.__version__)
Ka-Ping Yee40c49912001-02-27 22:46:01 +0000516 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
517 version = strip(version[11:-1])
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000518 info.append('version %s' % self.escape(version))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000519 if hasattr(object, '__date__'):
520 info.append(self.escape(str(object.__date__)))
521 if info:
522 head = head + ' (%s)' % join(info, ', ')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000523 result = self.heading(
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000524 head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)
525
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000526 modules = inspect.getmembers(object, inspect.ismodule)
527
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000528 classes, cdict = [], {}
529 for key, value in inspect.getmembers(object, inspect.isclass):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000530 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000531 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000532 cdict[key] = cdict[value] = '#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000533 for key, value in classes:
534 for base in value.__bases__:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000535 key, modname = base.__name__, base.__module__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000536 module = sys.modules.get(modname)
537 if modname != name and module and hasattr(module, key):
538 if getattr(module, key) is base:
Raymond Hettinger54f02222002-06-01 14:18:47 +0000539 if not key in cdict:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000540 cdict[key] = cdict[base] = modname + '.html#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000541 funcs, fdict = [], {}
542 for key, value in inspect.getmembers(object, inspect.isroutine):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000543 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000544 funcs.append((key, value))
545 fdict[key] = '#-' + key
546 if inspect.isfunction(value): fdict[value] = fdict[key]
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000547 data = []
548 for key, value in inspect.getmembers(object, isdata):
549 if key not in ['__builtins__', '__doc__']:
550 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000551
552 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
553 doc = doc and '<tt>%s</tt>' % doc
Tim Peters2306d242001-09-25 03:18:32 +0000554 result = result + '<p>%s</p>\n' % doc
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000555
556 if hasattr(object, '__path__'):
557 modpkgs = []
558 modnames = []
559 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000560 path = os.path.join(object.__path__[0], file)
561 modname = inspect.getmodulename(file)
562 if modname and modname not in modnames:
563 modpkgs.append((modname, name, 0, 0))
564 modnames.append(modname)
565 elif ispackage(path):
566 modpkgs.append((file, name, 1, 0))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000567 modpkgs.sort()
568 contents = self.multicolumn(modpkgs, self.modpkglink)
569 result = result + self.bigsection(
570 'Package Contents', '#ffffff', '#aa55cc', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000571 elif modules:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000572 contents = self.multicolumn(
573 modules, lambda (key, value), s=self: s.modulelink(value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000574 result = result + self.bigsection(
575 'Modules', '#fffff', '#aa55cc', contents)
576
577 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000578 classlist = map(lambda (key, value): value, classes)
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000579 contents = [
580 self.formattree(inspect.getclasstree(classlist, 1), name)]
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000581 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000582 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000583 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000584 'Classes', '#ffffff', '#ee77aa', join(contents))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000585 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000586 contents = []
587 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000588 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000589 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000590 'Functions', '#ffffff', '#eeaa77', join(contents))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000591 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000592 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000593 for key, value in data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000594 contents.append(self.document(value, key))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000595 result = result + self.bigsection(
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000596 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000597 if hasattr(object, '__author__'):
598 contents = self.markup(str(object.__author__), self.preformat)
599 result = result + self.bigsection(
600 'Author', '#ffffff', '#7799ee', contents)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000601 if hasattr(object, '__credits__'):
602 contents = self.markup(str(object.__credits__), self.preformat)
603 result = result + self.bigsection(
604 'Credits', '#ffffff', '#7799ee', contents)
605
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000606 return result
607
Tim Peters8dd7ade2001-10-18 19:56:17 +0000608 def docclass(self, object, name=None, mod=None, funcs={}, classes={},
609 *ignored):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000610 """Produce HTML documentation for a class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000611 realname = object.__name__
612 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000613 bases = object.__bases__
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000614
Tim Petersb47879b2001-09-24 04:47:19 +0000615 contents = []
616 push = contents.append
617
Tim Petersfa26f7c2001-09-24 08:05:11 +0000618 # Cute little class to pump out a horizontal rule between sections.
619 class HorizontalRule:
620 def __init__(self):
621 self.needone = 0
622 def maybe(self):
623 if self.needone:
624 push('<hr>\n')
625 self.needone = 1
626 hr = HorizontalRule()
627
Tim Petersc86f6ca2001-09-26 21:31:51 +0000628 # List the mro, if non-trivial.
Tim Peters351e3622001-09-27 03:29:51 +0000629 mro = list(inspect.getmro(object))
Tim Petersc86f6ca2001-09-26 21:31:51 +0000630 if len(mro) > 2:
631 hr.maybe()
632 push('<dl><dt>Method resolution order:</dt>\n')
633 for base in mro:
634 push('<dd>%s</dd>\n' % self.classlink(base,
635 object.__module__))
636 push('</dl>\n')
637
Tim Petersb47879b2001-09-24 04:47:19 +0000638 def spill(msg, attrs, predicate):
Tim Petersfa26f7c2001-09-24 08:05:11 +0000639 ok, attrs = _split_list(attrs, predicate)
Tim Petersb47879b2001-09-24 04:47:19 +0000640 if ok:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000641 hr.maybe()
Tim Petersb47879b2001-09-24 04:47:19 +0000642 push(msg)
643 for name, kind, homecls, value in ok:
644 push(self.document(getattr(object, name), name, mod,
645 funcs, classes, mdict, object))
646 push('\n')
647 return attrs
648
Tim Petersfa26f7c2001-09-24 08:05:11 +0000649 def spillproperties(msg, attrs, predicate):
650 ok, attrs = _split_list(attrs, predicate)
Tim Petersb47879b2001-09-24 04:47:19 +0000651 if ok:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000652 hr.maybe()
Tim Petersb47879b2001-09-24 04:47:19 +0000653 push(msg)
654 for name, kind, homecls, value in ok:
Tim Peters3e767d12001-09-25 00:01:06 +0000655 push('<dl><dt><strong>%s</strong></dt>\n' % name)
656 if value.__doc__ is not None:
657 doc = self.markup(value.__doc__, self.preformat,
658 funcs, classes, mdict)
Tim Peters2306d242001-09-25 03:18:32 +0000659 push('<dd><tt>%s</tt></dd>\n' % doc)
Tim Petersf33532c2001-09-25 06:30:51 +0000660 for attr, tag in [("fget", " getter"),
661 ("fset", " setter"),
Tim Peters3e767d12001-09-25 00:01:06 +0000662 ("fdel", " deleter")]:
663 func = getattr(value, attr)
664 if func is not None:
665 base = self.document(func, name + tag, mod,
666 funcs, classes, mdict, object)
667 push('<dd>%s</dd>\n' % base)
668 push('</dl>\n')
Tim Petersb47879b2001-09-24 04:47:19 +0000669 return attrs
670
Tim Petersfa26f7c2001-09-24 08:05:11 +0000671 def spilldata(msg, attrs, predicate):
672 ok, attrs = _split_list(attrs, predicate)
Tim Petersb47879b2001-09-24 04:47:19 +0000673 if ok:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000674 hr.maybe()
Tim Petersb47879b2001-09-24 04:47:19 +0000675 push(msg)
676 for name, kind, homecls, value in ok:
677 base = self.docother(getattr(object, name), name, mod)
Guido van Rossum5e355b22002-05-21 20:56:15 +0000678 if callable(value):
679 doc = getattr(value, "__doc__", None)
680 else:
681 doc = None
Tim Petersb47879b2001-09-24 04:47:19 +0000682 if doc is None:
683 push('<dl><dt>%s</dl>\n' % base)
684 else:
685 doc = self.markup(getdoc(value), self.preformat,
686 funcs, classes, mdict)
Tim Peters2306d242001-09-25 03:18:32 +0000687 doc = '<dd><tt>%s</tt>' % doc
Tim Petersb47879b2001-09-24 04:47:19 +0000688 push('<dl><dt>%s%s</dl>\n' % (base, doc))
689 push('\n')
690 return attrs
691
692 attrs = inspect.classify_class_attrs(object)
693 mdict = {}
694 for key, kind, homecls, value in attrs:
695 mdict[key] = anchor = '#' + name + '-' + key
696 value = getattr(object, key)
697 try:
698 # The value may not be hashable (e.g., a data attr with
699 # a dict or list value).
700 mdict[value] = anchor
701 except TypeError:
702 pass
703
Tim Petersfa26f7c2001-09-24 08:05:11 +0000704 while attrs:
Tim Peters351e3622001-09-27 03:29:51 +0000705 if mro:
706 thisclass = mro.pop(0)
707 else:
708 thisclass = attrs[0][2]
Tim Petersfa26f7c2001-09-24 08:05:11 +0000709 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
710
Tim Petersb47879b2001-09-24 04:47:19 +0000711 if thisclass is object:
712 tag = "defined here"
713 else:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000714 tag = "inherited from %s" % self.classlink(thisclass,
Tim Petersb47879b2001-09-24 04:47:19 +0000715 object.__module__)
716 tag += ':<br>\n'
717
718 # Sort attrs by name.
719 attrs.sort(lambda t1, t2: cmp(t1[0], t2[0]))
720
721 # Pump out the attrs, segregated by kind.
722 attrs = spill("Methods %s" % tag, attrs,
723 lambda t: t[1] == 'method')
724 attrs = spill("Class methods %s" % tag, attrs,
725 lambda t: t[1] == 'class method')
726 attrs = spill("Static methods %s" % tag, attrs,
727 lambda t: t[1] == 'static method')
Tim Petersfa26f7c2001-09-24 08:05:11 +0000728 attrs = spillproperties("Properties %s" % tag, attrs,
729 lambda t: t[1] == 'property')
Tim Petersf33532c2001-09-25 06:30:51 +0000730 attrs = spilldata("Data and non-method functions %s" % tag, attrs,
Tim Petersfa26f7c2001-09-24 08:05:11 +0000731 lambda t: t[1] == 'data')
Tim Petersb47879b2001-09-24 04:47:19 +0000732 assert attrs == []
Tim Peters351e3622001-09-27 03:29:51 +0000733 attrs = inherited
Tim Petersb47879b2001-09-24 04:47:19 +0000734
735 contents = ''.join(contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000736
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000737 if name == realname:
738 title = '<a name="%s">class <strong>%s</strong></a>' % (
739 name, realname)
740 else:
741 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
742 name, name, realname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000743 if bases:
744 parents = []
745 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000746 parents.append(self.classlink(base, object.__module__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000747 title = title + '(%s)' % join(parents, ', ')
Tim Peters2306d242001-09-25 03:18:32 +0000748 doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict)
749 doc = doc and '<tt>%s<br>&nbsp;</tt>' % doc or '&nbsp;'
Tim Petersc86f6ca2001-09-26 21:31:51 +0000750
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000751 return self.section(title, '#000000', '#ffc8d8', contents, 5, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000752
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000753 def formatvalue(self, object):
754 """Format an argument default value as text."""
Tim Peters2306d242001-09-25 03:18:32 +0000755 return self.grey('=' + self.repr(object))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000756
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000757 def docroutine(self, object, name=None, mod=None,
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000758 funcs={}, classes={}, methods={}, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000759 """Produce HTML documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000760 realname = object.__name__
761 name = name or realname
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000762 anchor = (cl and cl.__name__ or '') + '-' + name
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000763 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000764 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000765 if inspect.ismethod(object):
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +0000766 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000767 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000768 if imclass is not cl:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000769 note = ' from ' + self.classlink(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000770 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000771 if object.im_self:
772 note = ' method of %s instance' % self.classlink(
773 object.im_self.__class__, mod)
774 else:
775 note = ' unbound %s method' % self.classlink(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000776 object = object.im_func
777
778 if name == realname:
779 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
780 else:
Raymond Hettinger54f02222002-06-01 14:18:47 +0000781 if (cl and realname in cl.__dict__ and
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000782 cl.__dict__[realname] is object):
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000783 reallink = '<a href="#%s">%s</a>' % (
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000784 cl.__name__ + '-' + realname, realname)
785 skipdocs = 1
786 else:
787 reallink = realname
788 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
789 anchor, name, reallink)
Tim Peters4bcfa312001-09-20 06:08:24 +0000790 if inspect.isfunction(object):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000791 args, varargs, varkw, defaults = inspect.getargspec(object)
792 argspec = inspect.formatargspec(
793 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000794 if realname == '<lambda>':
Tim Peters59ed4482001-10-31 04:20:26 +0000795 title = '<strong>%s</strong> <em>lambda</em> ' % name
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000796 argspec = argspec[1:-1] # remove parentheses
Tim Peters4bcfa312001-09-20 06:08:24 +0000797 else:
798 argspec = '(...)'
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000799
Tim Peters2306d242001-09-25 03:18:32 +0000800 decl = title + argspec + (note and self.grey(
801 '<font face="helvetica, arial">%s</font>' % note))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000802
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000803 if skipdocs:
Tim Peters2306d242001-09-25 03:18:32 +0000804 return '<dl><dt>%s</dt></dl>\n' % decl
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000805 else:
806 doc = self.markup(
807 getdoc(object), self.preformat, funcs, classes, methods)
Tim Peters2306d242001-09-25 03:18:32 +0000808 doc = doc and '<dd><tt>%s</tt></dd>' % doc
809 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000810
Tim Peters8dd7ade2001-10-18 19:56:17 +0000811 def docother(self, object, name=None, mod=None, *ignored):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000812 """Produce HTML documentation for a data object."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000813 lhs = name and '<strong>%s</strong> = ' % name or ''
814 return lhs + self.repr(object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000815
816 def index(self, dir, shadowed=None):
817 """Generate an HTML index for a directory of modules."""
818 modpkgs = []
819 if shadowed is None: shadowed = {}
820 seen = {}
821 files = os.listdir(dir)
822
823 def found(name, ispackage,
824 modpkgs=modpkgs, shadowed=shadowed, seen=seen):
Raymond Hettinger54f02222002-06-01 14:18:47 +0000825 if not name in seen:
826 modpkgs.append((name, '', ispackage, name)) in shadowed
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000827 seen[name] = 1
828 shadowed[name] = 1
829
830 # Package spam/__init__.py takes precedence over module spam.py.
831 for file in files:
832 path = os.path.join(dir, file)
833 if ispackage(path): found(file, 1)
834 for file in files:
835 path = os.path.join(dir, file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000836 if os.path.isfile(path):
837 modname = inspect.getmodulename(file)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000838 if modname: found(modname, 0)
839
840 modpkgs.sort()
841 contents = self.multicolumn(modpkgs, self.modpkglink)
842 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
843
844# -------------------------------------------- text documentation generator
845
846class TextRepr(Repr):
847 """Class for safely making a text representation of a Python object."""
848 def __init__(self):
849 Repr.__init__(self)
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000850 self.maxlist = self.maxtuple = 20
851 self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000852 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000853
854 def repr1(self, x, level):
855 methodname = 'repr_' + join(split(type(x).__name__), '_')
856 if hasattr(self, methodname):
857 return getattr(self, methodname)(x, level)
858 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000859 return cram(stripid(repr(x)), self.maxother)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000860
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000861 def repr_string(self, x, level):
862 test = cram(x, self.maxstring)
863 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000864 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000865 # Backslashes are only literal in the string and are never
866 # needed to make any special characters, so show a raw string.
867 return 'r' + testrepr[0] + test + testrepr[0]
868 return testrepr
869
Skip Montanarodf708782002-03-07 22:58:02 +0000870 repr_str = repr_string
871
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000872 def repr_instance(self, x, level):
873 try:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000874 return cram(stripid(repr(x)), self.maxstring)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000875 except:
876 return '<%s instance>' % x.__class__.__name__
877
878class TextDoc(Doc):
879 """Formatter class for text documentation."""
880
881 # ------------------------------------------- text formatting utilities
882
883 _repr_instance = TextRepr()
884 repr = _repr_instance.repr
885
886 def bold(self, text):
887 """Format a string in bold by overstriking."""
888 return join(map(lambda ch: ch + '\b' + ch, text), '')
889
890 def indent(self, text, prefix=' '):
891 """Indent text by prepending a given prefix to each line."""
892 if not text: return ''
893 lines = split(text, '\n')
894 lines = map(lambda line, prefix=prefix: prefix + line, lines)
895 if lines: lines[-1] = rstrip(lines[-1])
896 return join(lines, '\n')
897
898 def section(self, title, contents):
899 """Format a section with a given heading."""
900 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
901
902 # ---------------------------------------------- type-specific routines
903
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000904 def formattree(self, tree, modname, parent=None, prefix=''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000905 """Render in text a class tree as returned by inspect.getclasstree()."""
906 result = ''
907 for entry in tree:
908 if type(entry) is type(()):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000909 c, bases = entry
910 result = result + prefix + classname(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000911 if bases and bases != (parent,):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000912 parents = map(lambda c, m=modname: classname(c, m), bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000913 result = result + '(%s)' % join(parents, ', ')
914 result = result + '\n'
915 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000916 result = result + self.formattree(
917 entry, modname, c, prefix + ' ')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000918 return result
919
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000920 def docmodule(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000921 """Produce text documentation for a given module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000922 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000923 synop, desc = splitdoc(getdoc(object))
924 result = self.section('NAME', name + (synop and ' - ' + synop))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000925
926 try:
927 file = inspect.getabsfile(object)
928 except TypeError:
929 file = '(built-in)'
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000930 result = result + self.section('FILE', file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000931 if desc:
932 result = result + self.section('DESCRIPTION', desc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000933
934 classes = []
935 for key, value in inspect.getmembers(object, inspect.isclass):
936 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000937 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000938 funcs = []
939 for key, value in inspect.getmembers(object, inspect.isroutine):
940 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000941 funcs.append((key, value))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000942 data = []
943 for key, value in inspect.getmembers(object, isdata):
944 if key not in ['__builtins__', '__doc__']:
945 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000946
947 if hasattr(object, '__path__'):
948 modpkgs = []
949 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000950 path = os.path.join(object.__path__[0], file)
951 modname = inspect.getmodulename(file)
952 if modname and modname not in modpkgs:
953 modpkgs.append(modname)
954 elif ispackage(path):
955 modpkgs.append(file + ' (package)')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000956 modpkgs.sort()
957 result = result + self.section(
958 'PACKAGE CONTENTS', join(modpkgs, '\n'))
959
960 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000961 classlist = map(lambda (key, value): value, classes)
962 contents = [self.formattree(
963 inspect.getclasstree(classlist, 1), name)]
964 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000965 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000966 result = result + self.section('CLASSES', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000967
968 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000969 contents = []
970 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000971 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000972 result = result + self.section('FUNCTIONS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000973
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000974 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000975 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000976 for key, value in data:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000977 contents.append(self.docother(value, key, name, 70))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000978 result = result + self.section('DATA', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000979
980 if hasattr(object, '__version__'):
981 version = str(object.__version__)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000982 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
983 version = strip(version[11:-1])
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000984 result = result + self.section('VERSION', version)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000985 if hasattr(object, '__date__'):
986 result = result + self.section('DATE', str(object.__date__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000987 if hasattr(object, '__author__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000988 result = result + self.section('AUTHOR', str(object.__author__))
989 if hasattr(object, '__credits__'):
990 result = result + self.section('CREDITS', str(object.__credits__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000991 return result
992
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000993 def docclass(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000994 """Produce text documentation for a given class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000995 realname = object.__name__
996 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000997 bases = object.__bases__
998
Tim Petersc86f6ca2001-09-26 21:31:51 +0000999 def makename(c, m=object.__module__):
1000 return classname(c, m)
1001
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001002 if name == realname:
1003 title = 'class ' + self.bold(realname)
1004 else:
1005 title = self.bold(name) + ' = class ' + realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001006 if bases:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001007 parents = map(makename, bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001008 title = title + '(%s)' % join(parents, ', ')
1009
1010 doc = getdoc(object)
Tim Peters28355492001-09-23 21:29:55 +00001011 contents = doc and [doc + '\n'] or []
1012 push = contents.append
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001013
Tim Petersc86f6ca2001-09-26 21:31:51 +00001014 # List the mro, if non-trivial.
Tim Peters351e3622001-09-27 03:29:51 +00001015 mro = list(inspect.getmro(object))
Tim Petersc86f6ca2001-09-26 21:31:51 +00001016 if len(mro) > 2:
1017 push("Method resolution order:")
1018 for base in mro:
1019 push(' ' + makename(base))
1020 push('')
1021
Tim Petersf4aad8e2001-09-24 22:40:47 +00001022 # Cute little class to pump out a horizontal rule between sections.
1023 class HorizontalRule:
1024 def __init__(self):
1025 self.needone = 0
1026 def maybe(self):
1027 if self.needone:
1028 push('-' * 70)
1029 self.needone = 1
1030 hr = HorizontalRule()
1031
Tim Peters28355492001-09-23 21:29:55 +00001032 def spill(msg, attrs, predicate):
Tim Petersfa26f7c2001-09-24 08:05:11 +00001033 ok, attrs = _split_list(attrs, predicate)
Tim Peters28355492001-09-23 21:29:55 +00001034 if ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001035 hr.maybe()
Tim Peters28355492001-09-23 21:29:55 +00001036 push(msg)
1037 for name, kind, homecls, value in ok:
1038 push(self.document(getattr(object, name),
1039 name, mod, object))
1040 return attrs
1041
Tim Petersfa26f7c2001-09-24 08:05:11 +00001042 def spillproperties(msg, attrs, predicate):
1043 ok, attrs = _split_list(attrs, predicate)
Tim Peters28355492001-09-23 21:29:55 +00001044 if ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001045 hr.maybe()
Tim Peters28355492001-09-23 21:29:55 +00001046 push(msg)
1047 for name, kind, homecls, value in ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001048 push(name)
1049 need_blank_after_doc = 0
1050 doc = getdoc(value) or ''
1051 if doc:
1052 push(self.indent(doc))
1053 need_blank_after_doc = 1
Tim Petersf33532c2001-09-25 06:30:51 +00001054 for attr, tag in [("fget", " getter"),
1055 ("fset", " setter"),
Tim Petersf4aad8e2001-09-24 22:40:47 +00001056 ("fdel", " deleter")]:
1057 func = getattr(value, attr)
1058 if func is not None:
1059 if need_blank_after_doc:
1060 push('')
1061 need_blank_after_doc = 0
1062 base = self.docother(func, name + tag, mod, 70)
1063 push(self.indent(base))
1064 push('')
Tim Peters28355492001-09-23 21:29:55 +00001065 return attrs
Tim Petersb47879b2001-09-24 04:47:19 +00001066
Tim Petersfa26f7c2001-09-24 08:05:11 +00001067 def spilldata(msg, attrs, predicate):
1068 ok, attrs = _split_list(attrs, predicate)
Tim Peters28355492001-09-23 21:29:55 +00001069 if ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001070 hr.maybe()
Tim Peters28355492001-09-23 21:29:55 +00001071 push(msg)
1072 for name, kind, homecls, value in ok:
Guido van Rossum5e355b22002-05-21 20:56:15 +00001073 if callable(value):
1074 doc = getattr(value, "__doc__", None)
1075 else:
1076 doc = None
Tim Peters28355492001-09-23 21:29:55 +00001077 push(self.docother(getattr(object, name),
1078 name, mod, 70, doc) + '\n')
1079 return attrs
1080
1081 attrs = inspect.classify_class_attrs(object)
Tim Petersfa26f7c2001-09-24 08:05:11 +00001082 while attrs:
Tim Peters351e3622001-09-27 03:29:51 +00001083 if mro:
1084 thisclass = mro.pop(0)
1085 else:
1086 thisclass = attrs[0][2]
Tim Petersfa26f7c2001-09-24 08:05:11 +00001087 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
1088
Tim Peters28355492001-09-23 21:29:55 +00001089 if thisclass is object:
1090 tag = "defined here"
1091 else:
Tim Petersfa26f7c2001-09-24 08:05:11 +00001092 tag = "inherited from %s" % classname(thisclass,
1093 object.__module__)
Tim Peters28355492001-09-23 21:29:55 +00001094
1095 # Sort attrs by name.
1096 attrs.sort(lambda t1, t2: cmp(t1[0], t2[0]))
1097
1098 # Pump out the attrs, segregated by kind.
Tim Petersf4aad8e2001-09-24 22:40:47 +00001099 attrs = spill("Methods %s:\n" % tag, attrs,
Tim Peters28355492001-09-23 21:29:55 +00001100 lambda t: t[1] == 'method')
Tim Petersf4aad8e2001-09-24 22:40:47 +00001101 attrs = spill("Class methods %s:\n" % tag, attrs,
Tim Peters28355492001-09-23 21:29:55 +00001102 lambda t: t[1] == 'class method')
Tim Petersf4aad8e2001-09-24 22:40:47 +00001103 attrs = spill("Static methods %s:\n" % tag, attrs,
Tim Peters28355492001-09-23 21:29:55 +00001104 lambda t: t[1] == 'static method')
Tim Petersf4aad8e2001-09-24 22:40:47 +00001105 attrs = spillproperties("Properties %s:\n" % tag, attrs,
Tim Petersfa26f7c2001-09-24 08:05:11 +00001106 lambda t: t[1] == 'property')
Tim Petersf33532c2001-09-25 06:30:51 +00001107 attrs = spilldata("Data and non-method functions %s:\n" % tag,
1108 attrs, lambda t: t[1] == 'data')
Tim Peters28355492001-09-23 21:29:55 +00001109 assert attrs == []
Tim Peters351e3622001-09-27 03:29:51 +00001110 attrs = inherited
Tim Peters28355492001-09-23 21:29:55 +00001111
1112 contents = '\n'.join(contents)
1113 if not contents:
1114 return title + '\n'
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001115 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
1116
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001117 def formatvalue(self, object):
1118 """Format an argument default value as text."""
1119 return '=' + self.repr(object)
1120
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001121 def docroutine(self, object, name=None, mod=None, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001122 """Produce text documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001123 realname = object.__name__
1124 name = name or realname
1125 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001126 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001127 if inspect.ismethod(object):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001128 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001129 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001130 if imclass is not cl:
1131 note = ' from ' + classname(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001132 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +00001133 if object.im_self:
1134 note = ' method of %s instance' % classname(
1135 object.im_self.__class__, mod)
1136 else:
1137 note = ' unbound %s method' % classname(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001138 object = object.im_func
1139
1140 if name == realname:
1141 title = self.bold(realname)
1142 else:
Raymond Hettinger54f02222002-06-01 14:18:47 +00001143 if (cl and realname in cl.__dict__ and
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001144 cl.__dict__[realname] is object):
1145 skipdocs = 1
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001146 title = self.bold(name) + ' = ' + realname
Tim Peters4bcfa312001-09-20 06:08:24 +00001147 if inspect.isfunction(object):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001148 args, varargs, varkw, defaults = inspect.getargspec(object)
1149 argspec = inspect.formatargspec(
1150 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001151 if realname == '<lambda>':
1152 title = 'lambda'
1153 argspec = argspec[1:-1] # remove parentheses
Tim Peters4bcfa312001-09-20 06:08:24 +00001154 else:
1155 argspec = '(...)'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001156 decl = title + argspec + note
1157
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001158 if skipdocs:
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001159 return decl + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001160 else:
1161 doc = getdoc(object) or ''
1162 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001163
Tim Peters28355492001-09-23 21:29:55 +00001164 def docother(self, object, name=None, mod=None, maxlen=None, doc=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001165 """Produce text documentation for a data object."""
1166 repr = self.repr(object)
1167 if maxlen:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001168 line = (name and name + ' = ' or '') + repr
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001169 chop = maxlen - len(line)
1170 if chop < 0: repr = repr[:chop] + '...'
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001171 line = (name and self.bold(name) + ' = ' or '') + repr
Tim Peters28355492001-09-23 21:29:55 +00001172 if doc is not None:
1173 line += '\n' + self.indent(str(doc))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001174 return line
1175
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001176# --------------------------------------------------------- user interfaces
1177
1178def pager(text):
1179 """The first time this is called, determine what kind of pager to use."""
1180 global pager
1181 pager = getpager()
1182 pager(text)
1183
1184def getpager():
1185 """Decide what method to use for paging through text."""
1186 if type(sys.stdout) is not types.FileType:
1187 return plainpager
1188 if not sys.stdin.isatty() or not sys.stdout.isatty():
1189 return plainpager
Fred Drake0a66fcb2001-07-23 19:44:30 +00001190 if os.environ.get('TERM') in ['dumb', 'emacs']:
Fred Drake5e9eb982001-07-23 19:48:10 +00001191 return plainpager
Raymond Hettinger54f02222002-06-01 14:18:47 +00001192 if 'PAGER' in os.environ:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001193 if sys.platform == 'win32': # pipes completely broken in Windows
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001194 return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
1195 elif os.environ.get('TERM') in ['dumb', 'emacs']:
1196 return lambda text: pipepager(plain(text), os.environ['PAGER'])
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001197 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001198 return lambda text: pipepager(text, os.environ['PAGER'])
Andrew MacIntyre54e0eab2002-03-03 03:12:30 +00001199 if sys.platform == 'win32' or sys.platform.startswith('os2'):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001200 return lambda text: tempfilepager(plain(text), 'more <')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001201 if hasattr(os, 'system') and os.system('less 2>/dev/null') == 0:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001202 return lambda text: pipepager(text, 'less')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001203
1204 import tempfile
1205 filename = tempfile.mktemp()
1206 open(filename, 'w').close()
1207 try:
1208 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
1209 return lambda text: pipepager(text, 'more')
1210 else:
1211 return ttypager
1212 finally:
1213 os.unlink(filename)
1214
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001215def plain(text):
1216 """Remove boldface formatting from text."""
1217 return re.sub('.\b', '', text)
1218
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001219def pipepager(text, cmd):
1220 """Page through text by feeding it to another program."""
1221 pipe = os.popen(cmd, 'w')
1222 try:
1223 pipe.write(text)
1224 pipe.close()
1225 except IOError:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001226 pass # Ignore broken pipes caused by quitting the pager program.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001227
1228def tempfilepager(text, cmd):
1229 """Page through text by invoking a program on a temporary file."""
1230 import tempfile
1231 filename = tempfile.mktemp()
1232 file = open(filename, 'w')
1233 file.write(text)
1234 file.close()
1235 try:
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001236 os.system(cmd + ' ' + filename)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001237 finally:
1238 os.unlink(filename)
1239
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001240def ttypager(text):
1241 """Page through text on a text terminal."""
1242 lines = split(plain(text), '\n')
1243 try:
1244 import tty
1245 fd = sys.stdin.fileno()
1246 old = tty.tcgetattr(fd)
1247 tty.setcbreak(fd)
1248 getchar = lambda: sys.stdin.read(1)
Ka-Ping Yee457aab22001-02-27 23:36:29 +00001249 except (ImportError, AttributeError):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001250 tty = None
1251 getchar = lambda: sys.stdin.readline()[:-1][:1]
1252
1253 try:
1254 r = inc = os.environ.get('LINES', 25) - 1
1255 sys.stdout.write(join(lines[:inc], '\n') + '\n')
1256 while lines[r:]:
1257 sys.stdout.write('-- more --')
1258 sys.stdout.flush()
1259 c = getchar()
1260
1261 if c in ['q', 'Q']:
1262 sys.stdout.write('\r \r')
1263 break
1264 elif c in ['\r', '\n']:
1265 sys.stdout.write('\r \r' + lines[r] + '\n')
1266 r = r + 1
1267 continue
1268 if c in ['b', 'B', '\x1b']:
1269 r = r - inc - inc
1270 if r < 0: r = 0
1271 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1272 r = r + inc
1273
1274 finally:
1275 if tty:
1276 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1277
1278def plainpager(text):
1279 """Simply print unformatted text. This is the ultimate fallback."""
1280 sys.stdout.write(plain(text))
1281
1282def describe(thing):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001283 """Produce a short description of the given thing."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001284 if inspect.ismodule(thing):
1285 if thing.__name__ in sys.builtin_module_names:
1286 return 'built-in module ' + thing.__name__
1287 if hasattr(thing, '__path__'):
1288 return 'package ' + thing.__name__
1289 else:
1290 return 'module ' + thing.__name__
1291 if inspect.isbuiltin(thing):
1292 return 'built-in function ' + thing.__name__
1293 if inspect.isclass(thing):
1294 return 'class ' + thing.__name__
1295 if inspect.isfunction(thing):
1296 return 'function ' + thing.__name__
1297 if inspect.ismethod(thing):
1298 return 'method ' + thing.__name__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001299 if type(thing) is types.InstanceType:
1300 return 'instance of ' + thing.__class__.__name__
1301 return type(thing).__name__
1302
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001303def locate(path, forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001304 """Locate an object by name or dotted path, importing as necessary."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001305 parts = split(path, '.')
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001306 module, n = None, 0
1307 while n < len(parts):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001308 nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001309 if nextmodule: module, n = nextmodule, n + 1
1310 else: break
1311 if module:
1312 object = module
1313 for part in parts[n:]:
1314 try: object = getattr(object, part)
1315 except AttributeError: return None
1316 return object
1317 else:
1318 import __builtin__
1319 if hasattr(__builtin__, path):
1320 return getattr(__builtin__, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001321
1322# --------------------------------------- interactive interpreter interface
1323
1324text = TextDoc()
1325html = HTMLDoc()
1326
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001327def doc(thing, title='Python Library Documentation: %s', forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001328 """Display text documentation, given an object or a path to an object."""
1329 suffix, name = '', None
1330 if type(thing) is type(''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001331 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001332 object = locate(thing, forceload)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001333 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001334 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001335 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001336 if not object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001337 print 'no Python documentation found for %s' % repr(thing)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001338 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001339 parts = split(thing, '.')
1340 if len(parts) > 1: suffix = ' in ' + join(parts[:-1], '.')
1341 name = parts[-1]
1342 thing = object
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001343
1344 desc = describe(thing)
1345 module = inspect.getmodule(thing)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001346 if not suffix and module and module is not thing:
1347 suffix = ' in module ' + module.__name__
Ka-Ping Yee66246962001-04-12 11:59:50 +00001348 pager(title % (desc + suffix) + '\n\n' + text.document(thing, name))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001349
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001350def writedoc(key, forceload=0):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001351 """Write HTML documentation to a file in the current directory."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001352 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001353 object = locate(key, forceload)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001354 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001355 print value
1356 else:
1357 if object:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001358 page = html.page(describe(object),
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001359 html.document(object, object.__name__))
1360 file = open(key + '.html', 'w')
1361 file.write(page)
1362 file.close()
1363 print 'wrote', key + '.html'
1364 else:
1365 print 'no Python documentation found for %s' % repr(key)
1366
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001367def writedocs(dir, pkgpath='', done=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001368 """Write out HTML documentation for all modules in a directory tree."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001369 if done is None: done = {}
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001370 for file in os.listdir(dir):
1371 path = os.path.join(dir, file)
1372 if ispackage(path):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001373 writedocs(path, pkgpath + file + '.', done)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001374 elif os.path.isfile(path):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001375 modname = inspect.getmodulename(path)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001376 if modname:
1377 modname = pkgpath + modname
Raymond Hettinger54f02222002-06-01 14:18:47 +00001378 if not modname in done:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001379 done[modname] = 1
1380 writedoc(modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001381
1382class Helper:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001383 keywords = {
1384 'and': 'BOOLEAN',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001385 'assert': ('ref/assert', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001386 'break': ('ref/break', 'while for'),
1387 'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1388 'continue': ('ref/continue', 'while for'),
1389 'def': ('ref/function', ''),
1390 'del': ('ref/del', 'BASICMETHODS'),
1391 'elif': 'if',
1392 'else': ('ref/if', 'while for'),
1393 'except': 'try',
1394 'exec': ('ref/exec', ''),
1395 'finally': 'try',
1396 'for': ('ref/for', 'break continue while'),
1397 'from': 'import',
1398 'global': ('ref/global', 'NAMESPACES'),
1399 'if': ('ref/if', 'TRUTHVALUE'),
1400 'import': ('ref/import', 'MODULES'),
1401 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1402 'is': 'COMPARISON',
1403 'lambda': ('ref/lambda', 'FUNCTIONS'),
1404 'not': 'BOOLEAN',
1405 'or': 'BOOLEAN',
1406 'pass': 'PASS',
1407 'print': ('ref/print', ''),
1408 'raise': ('ref/raise', 'EXCEPTIONS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001409 'return': ('ref/return', 'FUNCTIONS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001410 'try': ('ref/try', 'EXCEPTIONS'),
1411 'while': ('ref/while', 'break continue if TRUTHVALUE'),
1412 }
1413
1414 topics = {
1415 'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001416 'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001417 'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'),
1418 'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001419 'UNICODE': ('ref/unicode', 'encodings unicode TYPES STRING'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001420 'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1421 'INTEGER': ('ref/integers', 'int range'),
1422 'FLOAT': ('ref/floating', 'float math'),
1423 'COMPLEX': ('ref/imaginary', 'complex cmath'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001424 'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001425 'MAPPINGS': 'DICTIONARIES',
1426 'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'),
1427 'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'),
1428 'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001429 'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001430 'FRAMEOBJECTS': 'TYPES',
1431 'TRACEBACKS': 'TYPES',
1432 'NONE': ('lib/bltin-null-object', ''),
1433 'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'),
1434 'FILES': ('lib/bltin-file-objects', ''),
1435 'SPECIALATTRIBUTES': ('lib/specialattrs', ''),
1436 'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'),
1437 'MODULES': ('lib/typesmodules', 'import'),
1438 'PACKAGES': 'import',
1439 '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'),
1440 'OPERATORS': 'EXPRESSIONS',
1441 'PRECEDENCE': 'EXPRESSIONS',
1442 'OBJECTS': ('ref/objects', 'TYPES'),
1443 'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001444 'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'),
1445 'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1446 'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'),
1447 'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'),
1448 'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'),
1449 'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'),
1450 'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001451 'EXECUTION': ('ref/execframes', ''),
1452 'NAMESPACES': ('ref/execframes', 'global ASSIGNMENT DELETION'),
1453 'SCOPING': 'NAMESPACES',
1454 'FRAMES': 'NAMESPACES',
1455 'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'),
1456 'COERCIONS': 'CONVERSIONS',
1457 'CONVERSIONS': ('ref/conversions', ''),
1458 'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'),
1459 'SPECIALIDENTIFIERS': ('ref/id-classes', ''),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001460 'PRIVATENAMES': ('ref/atom-identifiers', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001461 'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1462 'TUPLES': 'SEQUENCES',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001463 'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001464 'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001465 'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001466 'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001467 'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'),
1468 'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001469 'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1470 'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'),
1471 'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'),
1472 'CALLS': ('ref/calls', 'EXPRESSIONS'),
1473 'POWER': ('ref/power', 'EXPRESSIONS'),
1474 'UNARY': ('ref/unary', 'EXPRESSIONS'),
1475 'BINARY': ('ref/binary', 'EXPRESSIONS'),
1476 'SHIFTING': ('ref/shifting', 'EXPRESSIONS'),
1477 'BITWISE': ('ref/bitwise', 'EXPRESSIONS'),
1478 'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001479 'BOOLEAN': ('ref/lambda', 'EXPRESSIONS TRUTHVALUE'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001480 'ASSERTION': 'assert',
1481 'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001482 'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001483 'DELETION': 'del',
1484 'PRINTING': 'print',
1485 'RETURNING': 'return',
1486 'IMPORTING': 'import',
1487 'CONDITIONAL': 'if',
1488 'LOOPING': ('ref/compound', 'for while break continue'),
1489 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001490 'DEBUGGING': ('lib/module-pdb', 'pdb'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001491 }
1492
1493 def __init__(self, input, output):
1494 self.input = input
1495 self.output = output
1496 self.docdir = None
1497 execdir = os.path.dirname(sys.executable)
1498 homedir = os.environ.get('PYTHONHOME')
1499 for dir in [os.environ.get('PYTHONDOCS'),
1500 homedir and os.path.join(homedir, 'doc'),
1501 os.path.join(execdir, 'doc'),
1502 '/usr/doc/python-docs-' + split(sys.version)[0],
1503 '/usr/doc/python-' + split(sys.version)[0],
1504 '/usr/doc/python-docs-' + sys.version[:3],
1505 '/usr/doc/python-' + sys.version[:3]]:
1506 if dir and os.path.isdir(os.path.join(dir, 'lib')):
1507 self.docdir = dir
1508
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001509 def __repr__(self):
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001510 if inspect.stack()[1][3] == '?':
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001511 self()
1512 return ''
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001513 return '<pydoc.Helper instance>'
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001514
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001515 def __call__(self, request=None):
1516 if request is not None:
1517 self.help(request)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001518 else:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001519 self.intro()
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001520 self.interact()
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001521 self.output.write('''
Fred Drakee61967f2001-05-10 18:41:02 +00001522You are now leaving help and returning to the Python interpreter.
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001523If you want to ask for help on a particular object directly from the
1524interpreter, you can type "help(object)". Executing "help('string')"
1525has the same effect as typing a particular string at the help> prompt.
1526''')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001527
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001528 def interact(self):
1529 self.output.write('\n')
Guido van Rossum8ca162f2002-04-07 06:36:23 +00001530 while True:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001531 self.output.write('help> ')
1532 self.output.flush()
1533 try:
1534 request = self.input.readline()
1535 if not request: break
1536 except KeyboardInterrupt: break
1537 request = strip(replace(request, '"', '', "'", ''))
1538 if lower(request) in ['q', 'quit']: break
1539 self.help(request)
1540
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001541 def help(self, request):
1542 if type(request) is type(''):
1543 if request == 'help': self.intro()
1544 elif request == 'keywords': self.listkeywords()
1545 elif request == 'topics': self.listtopics()
1546 elif request == 'modules': self.listmodules()
1547 elif request[:8] == 'modules ':
1548 self.listmodules(split(request)[1])
Raymond Hettinger54f02222002-06-01 14:18:47 +00001549 elif request in self.keywords: self.showtopic(request)
1550 elif request in self.topics: self.showtopic(request)
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001551 elif request: doc(request, 'Help on %s:')
1552 elif isinstance(request, Helper): self()
1553 else: doc(request, 'Help on %s:')
1554 self.output.write('\n')
1555
1556 def intro(self):
1557 self.output.write('''
1558Welcome to Python %s! This is the online help utility.
1559
1560If this is your first time using Python, you should definitely check out
1561the tutorial on the Internet at http://www.python.org/doc/tut/.
1562
1563Enter the name of any module, keyword, or topic to get help on writing
1564Python programs and using Python modules. To quit this help utility and
1565return to the interpreter, just type "quit".
1566
1567To get a list of available modules, keywords, or topics, type "modules",
1568"keywords", or "topics". Each module also comes with a one-line summary
1569of what it does; to list the modules whose summaries contain a given word
1570such as "spam", type "modules spam".
1571''' % sys.version[:3])
1572
1573 def list(self, items, columns=4, width=80):
1574 items = items[:]
1575 items.sort()
1576 colw = width / columns
1577 rows = (len(items) + columns - 1) / columns
1578 for row in range(rows):
1579 for col in range(columns):
1580 i = col * rows + row
1581 if i < len(items):
1582 self.output.write(items[i])
1583 if col < columns - 1:
1584 self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1585 self.output.write('\n')
1586
1587 def listkeywords(self):
1588 self.output.write('''
1589Here is a list of the Python keywords. Enter any keyword to get more help.
1590
1591''')
1592 self.list(self.keywords.keys())
1593
1594 def listtopics(self):
1595 self.output.write('''
1596Here is a list of available topics. Enter any topic name to get more help.
1597
1598''')
1599 self.list(self.topics.keys())
1600
1601 def showtopic(self, topic):
1602 if not self.docdir:
1603 self.output.write('''
1604Sorry, topic and keyword documentation is not available because the Python
1605HTML documentation files could not be found. If you have installed them,
1606please set the environment variable PYTHONDOCS to indicate their location.
1607''')
1608 return
1609 target = self.topics.get(topic, self.keywords.get(topic))
1610 if not target:
1611 self.output.write('no documentation found for %s\n' % repr(topic))
1612 return
1613 if type(target) is type(''):
1614 return self.showtopic(target)
1615
1616 filename, xrefs = target
1617 filename = self.docdir + '/' + filename + '.html'
1618 try:
1619 file = open(filename)
1620 except:
1621 self.output.write('could not read docs from %s\n' % filename)
1622 return
1623
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001624 divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
1625 addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001626 document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1627 file.close()
1628
1629 import htmllib, formatter, StringIO
1630 buffer = StringIO.StringIO()
1631 parser = htmllib.HTMLParser(
1632 formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1633 parser.start_table = parser.do_p
1634 parser.end_table = lambda parser=parser: parser.do_p({})
1635 parser.start_tr = parser.do_br
1636 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1637 parser.feed(document)
1638 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
1639 pager(' ' + strip(buffer) + '\n')
Ka-Ping Yeeda793892001-04-13 11:02:51 +00001640 if xrefs:
1641 buffer = StringIO.StringIO()
1642 formatter.DumbWriter(buffer).send_flowing_data(
1643 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1644 self.output.write('\n%s\n' % buffer.getvalue())
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001645
1646 def listmodules(self, key=''):
1647 if key:
1648 self.output.write('''
1649Here is a list of matching modules. Enter any module name to get more help.
1650
1651''')
1652 apropos(key)
1653 else:
1654 self.output.write('''
1655Please wait a moment while I gather a list of all available modules...
1656
1657''')
1658 modules = {}
1659 def callback(path, modname, desc, modules=modules):
1660 if modname and modname[-9:] == '.__init__':
1661 modname = modname[:-9] + ' (package)'
1662 if find(modname, '.') < 0:
1663 modules[modname] = 1
1664 ModuleScanner().run(callback)
1665 self.list(modules.keys())
1666 self.output.write('''
1667Enter any module name to get more help. Or, type "modules spam" to search
1668for modules whose descriptions contain the word "spam".
1669''')
1670
1671help = Helper(sys.stdin, sys.stdout)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001672
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001673class Scanner:
1674 """A generic tree iterator."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001675 def __init__(self, roots, children, descendp):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001676 self.roots = roots[:]
1677 self.state = []
1678 self.children = children
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001679 self.descendp = descendp
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001680
1681 def next(self):
1682 if not self.state:
1683 if not self.roots:
1684 return None
1685 root = self.roots.pop(0)
1686 self.state = [(root, self.children(root))]
1687 node, children = self.state[-1]
1688 if not children:
1689 self.state.pop()
1690 return self.next()
1691 child = children.pop(0)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001692 if self.descendp(child):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001693 self.state.append((child, self.children(child)))
1694 return child
1695
1696class ModuleScanner(Scanner):
1697 """An interruptible scanner that searches module synopses."""
1698 def __init__(self):
1699 roots = map(lambda dir: (dir, ''), pathdirs())
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001700 Scanner.__init__(self, roots, self.submodules, self.isnewpackage)
Raymond Hettinger32200ae2002-06-01 19:51:15 +00001701 self.inodes = map(lambda (dir, pkg): os.stat(dir).st_ino, roots)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001702
1703 def submodules(self, (dir, package)):
1704 children = []
1705 for file in os.listdir(dir):
1706 path = os.path.join(dir, file)
Tim Peters30edd232001-03-16 08:29:48 +00001707 if ispackage(path):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001708 children.append((path, package + (package and '.') + file))
1709 else:
1710 children.append((path, package))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001711 children.sort() # so that spam.py comes before spam.pyc or spam.pyo
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001712 return children
1713
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001714 def isnewpackage(self, (dir, package)):
Raymond Hettinger32200ae2002-06-01 19:51:15 +00001715 inode = os.path.exists(dir) and os.stat(dir).st_ino
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001716 if not (os.path.islink(dir) and inode in self.inodes):
Ka-Ping Yee6191a232001-04-13 15:00:27 +00001717 self.inodes.append(inode) # detect circular symbolic links
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001718 return ispackage(dir)
Guido van Rossum8ca162f2002-04-07 06:36:23 +00001719 return False
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001720
Ka-Ping Yee66246962001-04-12 11:59:50 +00001721 def run(self, callback, key=None, completer=None):
1722 if key: key = lower(key)
Guido van Rossum8ca162f2002-04-07 06:36:23 +00001723 self.quit = False
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001724 seen = {}
1725
1726 for modname in sys.builtin_module_names:
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001727 if modname != '__main__':
1728 seen[modname] = 1
Ka-Ping Yee66246962001-04-12 11:59:50 +00001729 if key is None:
1730 callback(None, modname, '')
1731 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001732 desc = split(__import__(modname).__doc__ or '', '\n')[0]
Ka-Ping Yee66246962001-04-12 11:59:50 +00001733 if find(lower(modname + ' - ' + desc), key) >= 0:
1734 callback(None, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001735
1736 while not self.quit:
1737 node = self.next()
1738 if not node: break
1739 path, package = node
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001740 modname = inspect.getmodulename(path)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001741 if os.path.isfile(path) and modname:
1742 modname = package + (package and '.') + modname
Raymond Hettinger54f02222002-06-01 14:18:47 +00001743 if not modname in seen:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001744 seen[modname] = 1 # if we see spam.py, skip spam.pyc
Ka-Ping Yee66246962001-04-12 11:59:50 +00001745 if key is None:
1746 callback(path, modname, '')
1747 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001748 desc = synopsis(path) or ''
1749 if find(lower(modname + ' - ' + desc), key) >= 0:
1750 callback(path, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001751 if completer: completer()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001752
1753def apropos(key):
1754 """Print all the one-line module summaries that contain a substring."""
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001755 def callback(path, modname, desc):
1756 if modname[-9:] == '.__init__':
1757 modname = modname[:-9] + ' (package)'
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001758 print modname, desc and '- ' + desc
1759 try: import warnings
1760 except ImportError: pass
1761 else: warnings.filterwarnings('ignore') # ignore problems during import
Ka-Ping Yee66246962001-04-12 11:59:50 +00001762 ModuleScanner().run(callback, key)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001763
1764# --------------------------------------------------- web browser interface
1765
Ka-Ping Yee66246962001-04-12 11:59:50 +00001766def serve(port, callback=None, completer=None):
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001767 import BaseHTTPServer, mimetools, select
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001768
1769 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1770 class Message(mimetools.Message):
1771 def __init__(self, fp, seekable=1):
1772 Message = self.__class__
1773 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1774 self.encodingheader = self.getheader('content-transfer-encoding')
1775 self.typeheader = self.getheader('content-type')
1776 self.parsetype()
1777 self.parseplist()
1778
1779 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1780 def send_document(self, title, contents):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001781 try:
1782 self.send_response(200)
1783 self.send_header('Content-Type', 'text/html')
1784 self.end_headers()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001785 self.wfile.write(html.page(title, contents))
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001786 except IOError: pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001787
1788 def do_GET(self):
1789 path = self.path
1790 if path[-5:] == '.html': path = path[:-5]
1791 if path[:1] == '/': path = path[1:]
1792 if path and path != '.':
1793 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001794 obj = locate(path, forceload=1)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001795 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001796 self.send_document(path, html.escape(str(value)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001797 return
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001798 if obj:
1799 self.send_document(describe(obj), html.document(obj, path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001800 else:
1801 self.send_document(path,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001802'no Python documentation found for %s' % repr(path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001803 else:
1804 heading = html.heading(
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001805'<big><big><strong>Python: Index of Modules</strong></big></big>',
1806'#ffffff', '#7799ee')
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001807 def bltinlink(name):
1808 return '<a href="%s.html">%s</a>' % (name, name)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001809 names = filter(lambda x: x != '__main__',
1810 sys.builtin_module_names)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001811 contents = html.multicolumn(names, bltinlink)
1812 indices = ['<p>' + html.bigsection(
1813 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1814
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001815 seen = {}
1816 for dir in pathdirs():
1817 indices.append(html.index(dir, seen))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001818 contents = heading + join(indices) + '''<p align=right>
Tim Peters2306d242001-09-25 03:18:32 +00001819<font color="#909090" face="helvetica, arial"><strong>
1820pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font>'''
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001821 self.send_document('Index of Modules', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001822
1823 def log_message(self, *args): pass
1824
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001825 class DocServer(BaseHTTPServer.HTTPServer):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001826 def __init__(self, port, callback):
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001827 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001828 self.address = ('', port)
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001829 self.url = 'http://%s:%d/' % (host, port)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001830 self.callback = callback
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001831 self.base.__init__(self, self.address, self.handler)
1832
1833 def serve_until_quit(self):
1834 import select
Guido van Rossum8ca162f2002-04-07 06:36:23 +00001835 self.quit = False
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001836 while not self.quit:
1837 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1838 if rd: self.handle_request()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001839
1840 def server_activate(self):
1841 self.base.server_activate(self)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001842 if self.callback: self.callback(self)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001843
1844 DocServer.base = BaseHTTPServer.HTTPServer
1845 DocServer.handler = DocHandler
1846 DocHandler.MessageClass = Message
1847 try:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001848 try:
1849 DocServer(port, callback).serve_until_quit()
1850 except (KeyboardInterrupt, select.error):
1851 pass
1852 finally:
Ka-Ping Yee66246962001-04-12 11:59:50 +00001853 if completer: completer()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001854
1855# ----------------------------------------------------- graphical interface
1856
1857def gui():
1858 """Graphical interface (starts web server and pops up a control window)."""
1859 class GUI:
1860 def __init__(self, window, port=7464):
1861 self.window = window
1862 self.server = None
1863 self.scanner = None
1864
1865 import Tkinter
1866 self.server_frm = Tkinter.Frame(window)
1867 self.title_lbl = Tkinter.Label(self.server_frm,
1868 text='Starting server...\n ')
1869 self.open_btn = Tkinter.Button(self.server_frm,
1870 text='open browser', command=self.open, state='disabled')
1871 self.quit_btn = Tkinter.Button(self.server_frm,
1872 text='quit serving', command=self.quit, state='disabled')
1873
1874 self.search_frm = Tkinter.Frame(window)
1875 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
1876 self.search_ent = Tkinter.Entry(self.search_frm)
1877 self.search_ent.bind('<Return>', self.search)
1878 self.stop_btn = Tkinter.Button(self.search_frm,
1879 text='stop', pady=0, command=self.stop, state='disabled')
1880 if sys.platform == 'win32':
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001881 # Trying to hide and show this button crashes under Windows.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001882 self.stop_btn.pack(side='right')
1883
1884 self.window.title('pydoc')
1885 self.window.protocol('WM_DELETE_WINDOW', self.quit)
1886 self.title_lbl.pack(side='top', fill='x')
1887 self.open_btn.pack(side='left', fill='x', expand=1)
1888 self.quit_btn.pack(side='right', fill='x', expand=1)
1889 self.server_frm.pack(side='top', fill='x')
1890
1891 self.search_lbl.pack(side='left')
1892 self.search_ent.pack(side='right', fill='x', expand=1)
1893 self.search_frm.pack(side='top', fill='x')
1894 self.search_ent.focus_set()
1895
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001896 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001897 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001898 self.result_lst.bind('<Button-1>', self.select)
1899 self.result_lst.bind('<Double-Button-1>', self.goto)
1900 self.result_scr = Tkinter.Scrollbar(window,
1901 orient='vertical', command=self.result_lst.yview)
1902 self.result_lst.config(yscrollcommand=self.result_scr.set)
1903
1904 self.result_frm = Tkinter.Frame(window)
1905 self.goto_btn = Tkinter.Button(self.result_frm,
1906 text='go to selected', command=self.goto)
1907 self.hide_btn = Tkinter.Button(self.result_frm,
1908 text='hide results', command=self.hide)
1909 self.goto_btn.pack(side='left', fill='x', expand=1)
1910 self.hide_btn.pack(side='right', fill='x', expand=1)
1911
1912 self.window.update()
1913 self.minwidth = self.window.winfo_width()
1914 self.minheight = self.window.winfo_height()
1915 self.bigminheight = (self.server_frm.winfo_reqheight() +
1916 self.search_frm.winfo_reqheight() +
1917 self.result_lst.winfo_reqheight() +
1918 self.result_frm.winfo_reqheight())
1919 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
1920 self.expanded = 0
1921 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1922 self.window.wm_minsize(self.minwidth, self.minheight)
1923
1924 import threading
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001925 threading.Thread(
1926 target=serve, args=(port, self.ready, self.quit)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001927
1928 def ready(self, server):
1929 self.server = server
1930 self.title_lbl.config(
1931 text='Python documentation server at\n' + server.url)
1932 self.open_btn.config(state='normal')
1933 self.quit_btn.config(state='normal')
1934
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001935 def open(self, event=None, url=None):
1936 url = url or self.server.url
1937 try:
1938 import webbrowser
1939 webbrowser.open(url)
1940 except ImportError: # pre-webbrowser.py compatibility
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001941 if sys.platform == 'win32':
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001942 os.system('start "%s"' % url)
1943 elif sys.platform == 'mac':
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001944 try: import ic
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001945 except ImportError: pass
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001946 else: ic.launchurl(url)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001947 else:
1948 rc = os.system('netscape -remote "openURL(%s)" &' % url)
1949 if rc: os.system('netscape "%s" &' % url)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001950
1951 def quit(self, event=None):
1952 if self.server:
1953 self.server.quit = 1
1954 self.window.quit()
1955
1956 def search(self, event=None):
1957 key = self.search_ent.get()
1958 self.stop_btn.pack(side='right')
1959 self.stop_btn.config(state='normal')
1960 self.search_lbl.config(text='Searching for "%s"...' % key)
1961 self.search_ent.forget()
1962 self.search_lbl.pack(side='left')
1963 self.result_lst.delete(0, 'end')
1964 self.goto_btn.config(state='disabled')
1965 self.expand()
1966
1967 import threading
1968 if self.scanner:
1969 self.scanner.quit = 1
1970 self.scanner = ModuleScanner()
1971 threading.Thread(target=self.scanner.run,
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +00001972 args=(self.update, key, self.done)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001973
1974 def update(self, path, modname, desc):
1975 if modname[-9:] == '.__init__':
1976 modname = modname[:-9] + ' (package)'
1977 self.result_lst.insert('end',
1978 modname + ' - ' + (desc or '(no description)'))
1979
1980 def stop(self, event=None):
1981 if self.scanner:
1982 self.scanner.quit = 1
1983 self.scanner = None
1984
1985 def done(self):
1986 self.scanner = None
1987 self.search_lbl.config(text='Search for')
1988 self.search_lbl.pack(side='left')
1989 self.search_ent.pack(side='right', fill='x', expand=1)
1990 if sys.platform != 'win32': self.stop_btn.forget()
1991 self.stop_btn.config(state='disabled')
1992
1993 def select(self, event=None):
1994 self.goto_btn.config(state='normal')
1995
1996 def goto(self, event=None):
1997 selection = self.result_lst.curselection()
1998 if selection:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001999 modname = split(self.result_lst.get(selection[0]))[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00002000 self.open(url=self.server.url + modname + '.html')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002001
2002 def collapse(self):
2003 if not self.expanded: return
2004 self.result_frm.forget()
2005 self.result_scr.forget()
2006 self.result_lst.forget()
2007 self.bigwidth = self.window.winfo_width()
2008 self.bigheight = self.window.winfo_height()
2009 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
2010 self.window.wm_minsize(self.minwidth, self.minheight)
2011 self.expanded = 0
2012
2013 def expand(self):
2014 if self.expanded: return
2015 self.result_frm.pack(side='bottom', fill='x')
2016 self.result_scr.pack(side='right', fill='y')
2017 self.result_lst.pack(side='top', fill='both', expand=1)
2018 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
2019 self.window.wm_minsize(self.minwidth, self.bigminheight)
2020 self.expanded = 1
2021
2022 def hide(self, event=None):
2023 self.stop()
2024 self.collapse()
2025
2026 import Tkinter
2027 try:
2028 gui = GUI(Tkinter.Tk())
2029 Tkinter.mainloop()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002030 except KeyboardInterrupt:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002031 pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002032
2033# -------------------------------------------------- command-line interface
2034
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002035def ispath(x):
2036 return type(x) is types.StringType and find(x, os.sep) >= 0
2037
Ka-Ping Yee1d384632001-03-01 00:24:32 +00002038def cli():
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002039 """Command-line interface (looks at sys.argv to decide what to do)."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002040 import getopt
2041 class BadUsage: pass
2042
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002043 # Scripts don't get the current directory in their path by default.
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00002044 scriptdir = os.path.dirname(sys.argv[0])
2045 if scriptdir in sys.path:
2046 sys.path.remove(scriptdir)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002047 sys.path.insert(0, '.')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002048
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002049 try:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002050 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002051 writing = 0
2052
2053 for opt, val in opts:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002054 if opt == '-g':
2055 gui()
2056 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002057 if opt == '-k':
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002058 apropos(val)
2059 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002060 if opt == '-p':
2061 try:
2062 port = int(val)
2063 except ValueError:
2064 raise BadUsage
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002065 def ready(server):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00002066 print 'pydoc server ready at %s' % server.url
2067 def stopped():
2068 print 'pydoc server stopped'
2069 serve(port, ready, stopped)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002070 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002071 if opt == '-w':
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002072 writing = 1
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002073
2074 if not args: raise BadUsage
2075 for arg in args:
2076 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002077 if ispath(arg) and os.path.isfile(arg):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002078 arg = importfile(arg)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002079 if writing:
2080 if ispath(arg) and os.path.isdir(arg):
2081 writedocs(arg)
2082 else:
2083 writedoc(arg)
2084 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00002085 doc(arg)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002086 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002087 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002088
2089 except (getopt.error, BadUsage):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002090 cmd = sys.argv[0]
2091 print """pydoc - the Python documentation tool
2092
2093%s <name> ...
2094 Show text documentation on something. <name> may be the name of a
2095 function, module, or package, or a dotted reference to a class or
2096 function within a module or module in a package. If <name> contains
2097 a '%s', it is used as the path to a Python source file to document.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002098
2099%s -k <keyword>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002100 Search for a keyword in the synopsis lines of all available modules.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002101
2102%s -p <port>
2103 Start an HTTP server on the given port on the local machine.
2104
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002105%s -g
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00002106 Pop up a graphical interface for finding and serving documentation.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002107
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002108%s -w <name> ...
2109 Write out the HTML documentation for a module to a file in the current
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00002110 directory. If <name> contains a '%s', it is treated as a filename; if
2111 it names a directory, documentation is written for all the contents.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002112""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
Ka-Ping Yee1d384632001-03-01 00:24:32 +00002113
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002114if __name__ == '__main__': cli()