blob: 1e333398b8819ed0f39b8e6e128ac65cd43cc792 [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
Ka-Ping Yeedd175342001-02-27 14:43:46 +000046import sys, imp, os, stat, re, types, inspect
47from 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)):
151 return 1
152
153def synopsis(filename, cache={}):
154 """Get the one-line summary out of a module file."""
155 mtime = os.stat(filename)[stat.ST_MTIME]
156 lastupdate, result = cache.get(filename, (0, None))
157 if lastupdate < mtime:
158 info = inspect.getmoduleinfo(filename)
159 file = open(filename)
160 if info and 'b' in info[2]: # binary modules have to be imported
161 try: module = imp.load_module('__temp__', file, filename, info[1:])
162 except: return None
163 result = split(module.__doc__ or '', '\n')[0]
164 del sys.modules['__temp__']
165 else: # text modules can be directly examined
166 line = file.readline()
167 while line[:1] == '#' or not strip(line):
168 line = file.readline()
169 if not line: break
170 line = strip(line)
171 if line[:4] == 'r"""': line = line[1:]
172 if line[:3] == '"""':
173 line = line[3:]
174 if line[-1:] == '\\': line = line[:-1]
175 while not strip(line):
176 line = file.readline()
177 if not line: break
178 result = strip(split(line, '"""')[0])
179 else: result = None
180 file.close()
181 cache[filename] = (mtime, result)
182 return result
183
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000184class ErrorDuringImport(Exception):
185 """Errors that occurred while trying to import something to document it."""
186 def __init__(self, filename, (exc, value, tb)):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000187 self.filename = filename
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000188 self.exc = exc
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000189 self.value = value
190 self.tb = tb
191
192 def __str__(self):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000193 exc = self.exc
194 if type(exc) is types.ClassType:
195 exc = exc.__name__
196 return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000197
198def importfile(path):
199 """Import a Python source file or compiled file given its path."""
200 magic = imp.get_magic()
201 file = open(path, 'r')
202 if file.read(len(magic)) == magic:
203 kind = imp.PY_COMPILED
204 else:
205 kind = imp.PY_SOURCE
206 file.close()
207 filename = os.path.basename(path)
208 name, ext = os.path.splitext(filename)
209 file = open(path, 'r')
210 try:
211 module = imp.load_module(name, file, path, (ext, 'r', kind))
212 except:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000213 raise ErrorDuringImport(path, sys.exc_info())
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000214 file.close()
215 return module
216
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000217def safeimport(path, forceload=0, cache={}):
218 """Import a module; handle errors; return None if the module isn't found.
219
220 If the module *is* found but an exception occurs, it's wrapped in an
221 ErrorDuringImport exception and reraised. Unlike __import__, if a
222 package path is specified, the module at the end of the path is returned,
223 not the package at the beginning. If the optional 'forceload' argument
224 is 1, we reload the module from disk (unless it's a dynamic extension)."""
225 if forceload and sys.modules.has_key(path):
226 # This is the only way to be sure. Checking the mtime of the file
227 # isn't good enough (e.g. what if the module contains a class that
228 # inherits from another module that has changed?).
229 if path not in sys.builtin_module_names:
230 # Python never loads a dynamic extension a second time from the
231 # same path, even if the file is changed or missing. Deleting
232 # the entry in sys.modules doesn't help for dynamic extensions,
233 # so we're not even going to try to keep them up to date.
234 info = inspect.getmoduleinfo(sys.modules[path].__file__)
235 if info[3] != imp.C_EXTENSION:
236 cache[path] = sys.modules[path] # prevent module from clearing
237 del sys.modules[path]
238 try:
239 module = __import__(path)
240 except:
241 # Did the error occur before or after the module was found?
242 (exc, value, tb) = info = sys.exc_info()
243 if sys.modules.has_key(path):
244 # An error occured while executing the imported module.
245 raise ErrorDuringImport(sys.modules[path].__file__, info)
246 elif exc is SyntaxError:
247 # A SyntaxError occurred before we could execute the module.
248 raise ErrorDuringImport(value.filename, info)
249 elif exc is ImportError and \
250 split(lower(str(value)))[:2] == ['no', 'module']:
251 # The module was not found.
252 return None
253 else:
254 # Some other error occurred during the importing process.
255 raise ErrorDuringImport(path, sys.exc_info())
256 for part in split(path, '.')[1:]:
257 try: module = getattr(module, part)
258 except AttributeError: return None
259 return module
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000260
261# ---------------------------------------------------- formatter base class
262
263class Doc:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000264 def document(self, object, name=None, *args):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000265 """Generate documentation for an object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000266 args = (object, name) + args
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000267 if inspect.ismodule(object): return apply(self.docmodule, args)
268 if inspect.isclass(object): return apply(self.docclass, args)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000269 if inspect.isroutine(object): return apply(self.docroutine, args)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000270 return apply(self.docother, args)
271
272 def fail(self, object, name=None, *args):
273 """Raise an exception for unimplemented types."""
274 message = "don't know how to document object%s of type %s" % (
275 name and ' ' + repr(name), type(object).__name__)
276 raise TypeError, message
277
278 docmodule = docclass = docroutine = docother = fail
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000279
280# -------------------------------------------- HTML documentation generator
281
282class HTMLRepr(Repr):
283 """Class for safely making an HTML representation of a Python object."""
284 def __init__(self):
285 Repr.__init__(self)
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000286 self.maxlist = self.maxtuple = 20
287 self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000288 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000289
290 def escape(self, text):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000291 return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000292
293 def repr(self, object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000294 return Repr.repr(self, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000295
296 def repr1(self, x, level):
297 methodname = 'repr_' + join(split(type(x).__name__), '_')
298 if hasattr(self, methodname):
299 return getattr(self, methodname)(x, level)
300 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000301 return self.escape(cram(stripid(repr(x)), self.maxother))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000302
303 def repr_string(self, x, level):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000304 test = cram(x, self.maxstring)
305 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000306 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000307 # Backslashes are only literal in the string and are never
308 # needed to make any special characters, so show a raw string.
309 return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000310 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000311 r'<font color="#c040c0">\1</font>',
312 self.escape(testrepr))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000313
314 def repr_instance(self, x, level):
315 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000316 return self.escape(cram(stripid(repr(x)), self.maxstring))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000317 except:
318 return self.escape('<%s instance>' % x.__class__.__name__)
319
320 repr_unicode = repr_string
321
322class HTMLDoc(Doc):
323 """Formatter class for HTML documentation."""
324
325 # ------------------------------------------- HTML formatting utilities
326
327 _repr_instance = HTMLRepr()
328 repr = _repr_instance.repr
329 escape = _repr_instance.escape
330
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000331 def page(self, title, contents):
332 """Format an HTML page."""
333 return '''
334<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000335<html><head><title>Python: %s</title>
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000336<style type="text/css"><!--
Ka-Ping Yeed03f8fe2001-04-13 15:04:32 +0000337TT { font-family: lucidatypewriter, lucida console, courier }
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000338--></style></head><body bgcolor="#f0f0f8">
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000339%s
340</body></html>''' % (title, contents)
341
342 def heading(self, title, fgcol, bgcol, extras=''):
343 """Format a page heading."""
344 return '''
345<table width="100%%" cellspacing=0 cellpadding=2 border=0>
346<tr bgcolor="%s">
Tim Peters2306d242001-09-25 03:18:32 +0000347<td valign=bottom>&nbsp;<br>
348<font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000349><td align=right valign=bottom
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000350><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000351 ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
352
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000353 def section(self, title, fgcol, bgcol, contents, width=10,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000354 prelude='', marginalia=None, gap='&nbsp;&nbsp;'):
355 """Format a section with a heading."""
356 if marginalia is None:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000357 marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000358 result = '''
359<p><table width="100%%" cellspacing=0 cellpadding=2 border=0>
360<tr bgcolor="%s">
Tim Peters2306d242001-09-25 03:18:32 +0000361<td colspan=3 valign=bottom>&nbsp;<br>
362<font color="%s" face="helvetica, arial">%s</font></td></tr>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000363 ''' % (bgcol, fgcol, title)
364 if prelude:
365 result = result + '''
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000366<tr bgcolor="%s"><td rowspan=2>%s</td>
367<td colspan=2>%s</td></tr>
368<tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
369 else:
370 result = result + '''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000371<tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000372
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000373 return result + '\n<td width="100%%">%s</td></tr></table>' % contents
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000374
375 def bigsection(self, title, *args):
376 """Format a section with a big heading."""
377 title = '<big><strong>%s</strong></big>' % title
378 return apply(self.section, (title,) + args)
379
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000380 def preformat(self, text):
381 """Format literal preformatted text."""
382 text = self.escape(expandtabs(text))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000383 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
384 ' ', '&nbsp;', '\n', '<br>\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000385
386 def multicolumn(self, list, format, cols=4):
387 """Format a list of items into a multi-column list."""
388 result = ''
389 rows = (len(list)+cols-1)/cols
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000390 for col in range(cols):
391 result = result + '<td width="%d%%" valign=top>' % (100/cols)
392 for i in range(rows*col, rows*col+rows):
393 if i < len(list):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000394 result = result + format(list[i]) + '<br>\n'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000395 result = result + '</td>'
396 return '<table width="100%%"><tr>%s</tr></table>' % result
397
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000398 def grey(self, text): return '<font color="#909090">%s</font>' % text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000399
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000400 def namelink(self, name, *dicts):
401 """Make a link for an identifier, given name-to-URL mappings."""
402 for dict in dicts:
403 if dict.has_key(name):
404 return '<a href="%s">%s</a>' % (dict[name], name)
405 return name
406
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000407 def classlink(self, object, modname):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000408 """Make a link for a class."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000409 name, module = object.__name__, sys.modules.get(object.__module__)
410 if hasattr(module, name) and getattr(module, name) is object:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000411 return '<a href="%s.html#%s">%s</a>' % (
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000412 module.__name__, name, classname(object, modname))
413 return classname(object, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000414
415 def modulelink(self, object):
416 """Make a link for a module."""
417 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
418
419 def modpkglink(self, (name, path, ispackage, shadowed)):
420 """Make a link for a module or package to display in an index."""
421 if shadowed:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000422 return self.grey(name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000423 if path:
424 url = '%s.%s.html' % (path, name)
425 else:
426 url = '%s.html' % name
427 if ispackage:
428 text = '<strong>%s</strong>&nbsp;(package)' % name
429 else:
430 text = name
431 return '<a href="%s">%s</a>' % (url, text)
432
433 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
434 """Mark up some plain text, given a context of symbols to look for.
435 Each context dictionary maps object names to anchor names."""
436 escape = escape or self.escape
437 results = []
438 here = 0
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000439 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
440 r'RFC[- ]?(\d+)|'
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000441 r'PEP[- ]?(\d+)|'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000442 r'(self\.)?(\w+))\b')
443 while 1:
444 match = pattern.search(text, here)
445 if not match: break
446 start, end = match.span()
447 results.append(escape(text[here:start]))
448
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000449 all, scheme, rfc, pep, selfdot, name = match.groups()
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000450 if scheme:
451 results.append('<a href="%s">%s</a>' % (all, escape(all)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000452 elif rfc:
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000453 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
454 results.append('<a href="%s">%s</a>' % (url, escape(all)))
455 elif pep:
456 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000457 results.append('<a href="%s">%s</a>' % (url, escape(all)))
458 elif text[end:end+1] == '(':
459 results.append(self.namelink(name, methods, funcs, classes))
460 elif selfdot:
461 results.append('self.<strong>%s</strong>' % name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000462 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000463 results.append(self.namelink(name, classes))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000464 here = end
465 results.append(escape(text[here:]))
466 return join(results, '')
467
468 # ---------------------------------------------- type-specific routines
469
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000470 def formattree(self, tree, modname, parent=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000471 """Produce HTML for a class tree as given by inspect.getclasstree()."""
472 result = ''
473 for entry in tree:
474 if type(entry) is type(()):
475 c, bases = entry
Tim Peters2306d242001-09-25 03:18:32 +0000476 result = result + '<dt><font face="helvetica, arial">'
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000477 result = result + self.classlink(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000478 if bases and bases != (parent,):
479 parents = []
480 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000481 parents.append(self.classlink(base, modname))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000482 result = result + '(' + join(parents, ', ') + ')'
Tim Peters2306d242001-09-25 03:18:32 +0000483 result = result + '\n</font></dt>'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000484 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000485 result = result + '<dd>\n%s</dd>\n' % self.formattree(
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000486 entry, modname, c)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000487 return '<dl>\n%s</dl>\n' % result
488
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000489 def docmodule(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000490 """Produce HTML documentation for a module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000491 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000492 parts = split(name, '.')
493 links = []
494 for i in range(len(parts)-1):
495 links.append(
496 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
497 (join(parts[:i+1], '.'), parts[i]))
498 linkedname = join(links + parts[-1:], '.')
499 head = '<big><big><strong>%s</strong></big></big>' % linkedname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000500 try:
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000501 path = inspect.getabsfile(object)
Ka-Ping Yee6191a232001-04-13 15:00:27 +0000502 url = path
503 if sys.platform == 'win32':
504 import nturl2path
505 url = nturl2path.pathname2url(path)
506 filelink = '<a href="file:%s">%s</a>' % (url, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000507 except TypeError:
508 filelink = '(built-in)'
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000509 info = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000510 if hasattr(object, '__version__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000511 version = str(object.__version__)
Ka-Ping Yee40c49912001-02-27 22:46:01 +0000512 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
513 version = strip(version[11:-1])
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000514 info.append('version %s' % self.escape(version))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000515 if hasattr(object, '__date__'):
516 info.append(self.escape(str(object.__date__)))
517 if info:
518 head = head + ' (%s)' % join(info, ', ')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000519 result = self.heading(
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000520 head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)
521
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000522 modules = inspect.getmembers(object, inspect.ismodule)
523
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000524 classes, cdict = [], {}
525 for key, value in inspect.getmembers(object, inspect.isclass):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000526 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000527 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000528 cdict[key] = cdict[value] = '#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000529 for key, value in classes:
530 for base in value.__bases__:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000531 key, modname = base.__name__, base.__module__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000532 module = sys.modules.get(modname)
533 if modname != name and module and hasattr(module, key):
534 if getattr(module, key) is base:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000535 if not cdict.has_key(key):
536 cdict[key] = cdict[base] = modname + '.html#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000537 funcs, fdict = [], {}
538 for key, value in inspect.getmembers(object, inspect.isroutine):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000539 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000540 funcs.append((key, value))
541 fdict[key] = '#-' + key
542 if inspect.isfunction(value): fdict[value] = fdict[key]
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000543 data = []
544 for key, value in inspect.getmembers(object, isdata):
545 if key not in ['__builtins__', '__doc__']:
546 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000547
548 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
549 doc = doc and '<tt>%s</tt>' % doc
Tim Peters2306d242001-09-25 03:18:32 +0000550 result = result + '<p>%s</p>\n' % doc
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000551
552 if hasattr(object, '__path__'):
553 modpkgs = []
554 modnames = []
555 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000556 path = os.path.join(object.__path__[0], file)
557 modname = inspect.getmodulename(file)
558 if modname and modname not in modnames:
559 modpkgs.append((modname, name, 0, 0))
560 modnames.append(modname)
561 elif ispackage(path):
562 modpkgs.append((file, name, 1, 0))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000563 modpkgs.sort()
564 contents = self.multicolumn(modpkgs, self.modpkglink)
565 result = result + self.bigsection(
566 'Package Contents', '#ffffff', '#aa55cc', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000567 elif modules:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000568 contents = self.multicolumn(
569 modules, lambda (key, value), s=self: s.modulelink(value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000570 result = result + self.bigsection(
571 'Modules', '#fffff', '#aa55cc', contents)
572
573 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000574 classlist = map(lambda (key, value): value, classes)
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000575 contents = [
576 self.formattree(inspect.getclasstree(classlist, 1), name)]
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000577 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000578 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000579 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000580 'Classes', '#ffffff', '#ee77aa', join(contents))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000581 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000582 contents = []
583 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000584 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000585 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000586 'Functions', '#ffffff', '#eeaa77', join(contents))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000587 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000588 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000589 for key, value in data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000590 contents.append(self.document(value, key))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000591 result = result + self.bigsection(
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000592 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000593 if hasattr(object, '__author__'):
594 contents = self.markup(str(object.__author__), self.preformat)
595 result = result + self.bigsection(
596 'Author', '#ffffff', '#7799ee', contents)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000597 if hasattr(object, '__credits__'):
598 contents = self.markup(str(object.__credits__), self.preformat)
599 result = result + self.bigsection(
600 'Credits', '#ffffff', '#7799ee', contents)
601
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000602 return result
603
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000604 def docclass(self, object, name=None, mod=None, funcs={}, classes={}):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000605 """Produce HTML documentation for a class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000606 realname = object.__name__
607 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000608 bases = object.__bases__
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000609
Tim Petersb47879b2001-09-24 04:47:19 +0000610 contents = []
611 push = contents.append
612
Tim Petersfa26f7c2001-09-24 08:05:11 +0000613 # Cute little class to pump out a horizontal rule between sections.
614 class HorizontalRule:
615 def __init__(self):
616 self.needone = 0
617 def maybe(self):
618 if self.needone:
619 push('<hr>\n')
620 self.needone = 1
621 hr = HorizontalRule()
622
Tim Petersc86f6ca2001-09-26 21:31:51 +0000623 # List the mro, if non-trivial.
Tim Peters351e3622001-09-27 03:29:51 +0000624 mro = list(inspect.getmro(object))
Tim Petersc86f6ca2001-09-26 21:31:51 +0000625 if len(mro) > 2:
626 hr.maybe()
627 push('<dl><dt>Method resolution order:</dt>\n')
628 for base in mro:
629 push('<dd>%s</dd>\n' % self.classlink(base,
630 object.__module__))
631 push('</dl>\n')
632
Tim Petersb47879b2001-09-24 04:47:19 +0000633 def spill(msg, attrs, predicate):
Tim Petersfa26f7c2001-09-24 08:05:11 +0000634 ok, attrs = _split_list(attrs, predicate)
Tim Petersb47879b2001-09-24 04:47:19 +0000635 if ok:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000636 hr.maybe()
Tim Petersb47879b2001-09-24 04:47:19 +0000637 push(msg)
638 for name, kind, homecls, value in ok:
639 push(self.document(getattr(object, name), name, mod,
640 funcs, classes, mdict, object))
641 push('\n')
642 return attrs
643
Tim Petersfa26f7c2001-09-24 08:05:11 +0000644 def spillproperties(msg, attrs, predicate):
645 ok, attrs = _split_list(attrs, predicate)
Tim Petersb47879b2001-09-24 04:47:19 +0000646 if ok:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000647 hr.maybe()
Tim Petersb47879b2001-09-24 04:47:19 +0000648 push(msg)
649 for name, kind, homecls, value in ok:
Tim Peters3e767d12001-09-25 00:01:06 +0000650 push('<dl><dt><strong>%s</strong></dt>\n' % name)
651 if value.__doc__ is not None:
652 doc = self.markup(value.__doc__, self.preformat,
653 funcs, classes, mdict)
Tim Peters2306d242001-09-25 03:18:32 +0000654 push('<dd><tt>%s</tt></dd>\n' % doc)
Tim Petersf33532c2001-09-25 06:30:51 +0000655 for attr, tag in [("fget", " getter"),
656 ("fset", " setter"),
Tim Peters3e767d12001-09-25 00:01:06 +0000657 ("fdel", " deleter")]:
658 func = getattr(value, attr)
659 if func is not None:
660 base = self.document(func, name + tag, mod,
661 funcs, classes, mdict, object)
662 push('<dd>%s</dd>\n' % base)
663 push('</dl>\n')
Tim Petersb47879b2001-09-24 04:47:19 +0000664 return attrs
665
Tim Petersfa26f7c2001-09-24 08:05:11 +0000666 def spilldata(msg, attrs, predicate):
667 ok, attrs = _split_list(attrs, predicate)
Tim Petersb47879b2001-09-24 04:47:19 +0000668 if ok:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000669 hr.maybe()
Tim Petersb47879b2001-09-24 04:47:19 +0000670 push(msg)
671 for name, kind, homecls, value in ok:
672 base = self.docother(getattr(object, name), name, mod)
673 doc = getattr(value, "__doc__", None)
674 if doc is None:
675 push('<dl><dt>%s</dl>\n' % base)
676 else:
677 doc = self.markup(getdoc(value), self.preformat,
678 funcs, classes, mdict)
Tim Peters2306d242001-09-25 03:18:32 +0000679 doc = '<dd><tt>%s</tt>' % doc
Tim Petersb47879b2001-09-24 04:47:19 +0000680 push('<dl><dt>%s%s</dl>\n' % (base, doc))
681 push('\n')
682 return attrs
683
684 attrs = inspect.classify_class_attrs(object)
685 mdict = {}
686 for key, kind, homecls, value in attrs:
687 mdict[key] = anchor = '#' + name + '-' + key
688 value = getattr(object, key)
689 try:
690 # The value may not be hashable (e.g., a data attr with
691 # a dict or list value).
692 mdict[value] = anchor
693 except TypeError:
694 pass
695
Tim Petersfa26f7c2001-09-24 08:05:11 +0000696 while attrs:
Tim Peters351e3622001-09-27 03:29:51 +0000697 if mro:
698 thisclass = mro.pop(0)
699 else:
700 thisclass = attrs[0][2]
Tim Petersfa26f7c2001-09-24 08:05:11 +0000701 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
702
Tim Petersb47879b2001-09-24 04:47:19 +0000703 if thisclass is object:
704 tag = "defined here"
705 else:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000706 tag = "inherited from %s" % self.classlink(thisclass,
Tim Petersb47879b2001-09-24 04:47:19 +0000707 object.__module__)
708 tag += ':<br>\n'
709
710 # Sort attrs by name.
711 attrs.sort(lambda t1, t2: cmp(t1[0], t2[0]))
712
713 # Pump out the attrs, segregated by kind.
714 attrs = spill("Methods %s" % tag, attrs,
715 lambda t: t[1] == 'method')
716 attrs = spill("Class methods %s" % tag, attrs,
717 lambda t: t[1] == 'class method')
718 attrs = spill("Static methods %s" % tag, attrs,
719 lambda t: t[1] == 'static method')
Tim Petersfa26f7c2001-09-24 08:05:11 +0000720 attrs = spillproperties("Properties %s" % tag, attrs,
721 lambda t: t[1] == 'property')
Tim Petersf33532c2001-09-25 06:30:51 +0000722 attrs = spilldata("Data and non-method functions %s" % tag, attrs,
Tim Petersfa26f7c2001-09-24 08:05:11 +0000723 lambda t: t[1] == 'data')
Tim Petersb47879b2001-09-24 04:47:19 +0000724 assert attrs == []
Tim Peters351e3622001-09-27 03:29:51 +0000725 attrs = inherited
Tim Petersb47879b2001-09-24 04:47:19 +0000726
727 contents = ''.join(contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000728
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000729 if name == realname:
730 title = '<a name="%s">class <strong>%s</strong></a>' % (
731 name, realname)
732 else:
733 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
734 name, name, realname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000735 if bases:
736 parents = []
737 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000738 parents.append(self.classlink(base, object.__module__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000739 title = title + '(%s)' % join(parents, ', ')
Tim Peters2306d242001-09-25 03:18:32 +0000740 doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict)
741 doc = doc and '<tt>%s<br>&nbsp;</tt>' % doc or '&nbsp;'
Tim Petersc86f6ca2001-09-26 21:31:51 +0000742
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000743 return self.section(title, '#000000', '#ffc8d8', contents, 5, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000744
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000745 def formatvalue(self, object):
746 """Format an argument default value as text."""
Tim Peters2306d242001-09-25 03:18:32 +0000747 return self.grey('=' + self.repr(object))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000748
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000749 def docroutine(self, object, name=None, mod=None,
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000750 funcs={}, classes={}, methods={}, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000751 """Produce HTML documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000752 realname = object.__name__
753 name = name or realname
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000754 anchor = (cl and cl.__name__ or '') + '-' + name
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000755 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000756 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000757 if inspect.ismethod(object):
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +0000758 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000759 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000760 if imclass is not cl:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000761 note = ' from ' + self.classlink(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000762 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000763 if object.im_self:
764 note = ' method of %s instance' % self.classlink(
765 object.im_self.__class__, mod)
766 else:
767 note = ' unbound %s method' % self.classlink(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000768 object = object.im_func
769
770 if name == realname:
771 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
772 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000773 if (cl and cl.__dict__.has_key(realname) and
774 cl.__dict__[realname] is object):
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000775 reallink = '<a href="#%s">%s</a>' % (
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000776 cl.__name__ + '-' + realname, realname)
777 skipdocs = 1
778 else:
779 reallink = realname
780 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
781 anchor, name, reallink)
Tim Peters4bcfa312001-09-20 06:08:24 +0000782 if inspect.isfunction(object):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000783 args, varargs, varkw, defaults = inspect.getargspec(object)
784 argspec = inspect.formatargspec(
785 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000786 if realname == '<lambda>':
787 decl = '<em>lambda</em>'
788 argspec = argspec[1:-1] # remove parentheses
Tim Peters4bcfa312001-09-20 06:08:24 +0000789 else:
790 argspec = '(...)'
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000791
Tim Peters2306d242001-09-25 03:18:32 +0000792 decl = title + argspec + (note and self.grey(
793 '<font face="helvetica, arial">%s</font>' % note))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000794
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000795 if skipdocs:
Tim Peters2306d242001-09-25 03:18:32 +0000796 return '<dl><dt>%s</dt></dl>\n' % decl
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000797 else:
798 doc = self.markup(
799 getdoc(object), self.preformat, funcs, classes, methods)
Tim Peters2306d242001-09-25 03:18:32 +0000800 doc = doc and '<dd><tt>%s</tt></dd>' % doc
801 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000802
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000803 def docother(self, object, name=None, mod=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000804 """Produce HTML documentation for a data object."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000805 lhs = name and '<strong>%s</strong> = ' % name or ''
806 return lhs + self.repr(object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000807
808 def index(self, dir, shadowed=None):
809 """Generate an HTML index for a directory of modules."""
810 modpkgs = []
811 if shadowed is None: shadowed = {}
812 seen = {}
813 files = os.listdir(dir)
814
815 def found(name, ispackage,
816 modpkgs=modpkgs, shadowed=shadowed, seen=seen):
817 if not seen.has_key(name):
818 modpkgs.append((name, '', ispackage, shadowed.has_key(name)))
819 seen[name] = 1
820 shadowed[name] = 1
821
822 # Package spam/__init__.py takes precedence over module spam.py.
823 for file in files:
824 path = os.path.join(dir, file)
825 if ispackage(path): found(file, 1)
826 for file in files:
827 path = os.path.join(dir, file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000828 if os.path.isfile(path):
829 modname = inspect.getmodulename(file)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000830 if modname: found(modname, 0)
831
832 modpkgs.sort()
833 contents = self.multicolumn(modpkgs, self.modpkglink)
834 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
835
836# -------------------------------------------- text documentation generator
837
838class TextRepr(Repr):
839 """Class for safely making a text representation of a Python object."""
840 def __init__(self):
841 Repr.__init__(self)
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000842 self.maxlist = self.maxtuple = 20
843 self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000844 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000845
846 def repr1(self, x, level):
847 methodname = 'repr_' + join(split(type(x).__name__), '_')
848 if hasattr(self, methodname):
849 return getattr(self, methodname)(x, level)
850 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000851 return cram(stripid(repr(x)), self.maxother)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000852
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000853 def repr_string(self, x, level):
854 test = cram(x, self.maxstring)
855 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000856 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000857 # Backslashes are only literal in the string and are never
858 # needed to make any special characters, so show a raw string.
859 return 'r' + testrepr[0] + test + testrepr[0]
860 return testrepr
861
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000862 def repr_instance(self, x, level):
863 try:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000864 return cram(stripid(repr(x)), self.maxstring)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000865 except:
866 return '<%s instance>' % x.__class__.__name__
867
868class TextDoc(Doc):
869 """Formatter class for text documentation."""
870
871 # ------------------------------------------- text formatting utilities
872
873 _repr_instance = TextRepr()
874 repr = _repr_instance.repr
875
876 def bold(self, text):
877 """Format a string in bold by overstriking."""
878 return join(map(lambda ch: ch + '\b' + ch, text), '')
879
880 def indent(self, text, prefix=' '):
881 """Indent text by prepending a given prefix to each line."""
882 if not text: return ''
883 lines = split(text, '\n')
884 lines = map(lambda line, prefix=prefix: prefix + line, lines)
885 if lines: lines[-1] = rstrip(lines[-1])
886 return join(lines, '\n')
887
888 def section(self, title, contents):
889 """Format a section with a given heading."""
890 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
891
892 # ---------------------------------------------- type-specific routines
893
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000894 def formattree(self, tree, modname, parent=None, prefix=''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000895 """Render in text a class tree as returned by inspect.getclasstree()."""
896 result = ''
897 for entry in tree:
898 if type(entry) is type(()):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000899 c, bases = entry
900 result = result + prefix + classname(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000901 if bases and bases != (parent,):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000902 parents = map(lambda c, m=modname: classname(c, m), bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000903 result = result + '(%s)' % join(parents, ', ')
904 result = result + '\n'
905 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000906 result = result + self.formattree(
907 entry, modname, c, prefix + ' ')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000908 return result
909
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000910 def docmodule(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000911 """Produce text documentation for a given module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000912 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000913 synop, desc = splitdoc(getdoc(object))
914 result = self.section('NAME', name + (synop and ' - ' + synop))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000915
916 try:
917 file = inspect.getabsfile(object)
918 except TypeError:
919 file = '(built-in)'
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000920 result = result + self.section('FILE', file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000921 if desc:
922 result = result + self.section('DESCRIPTION', desc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000923
924 classes = []
925 for key, value in inspect.getmembers(object, inspect.isclass):
926 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000927 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000928 funcs = []
929 for key, value in inspect.getmembers(object, inspect.isroutine):
930 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000931 funcs.append((key, value))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000932 data = []
933 for key, value in inspect.getmembers(object, isdata):
934 if key not in ['__builtins__', '__doc__']:
935 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000936
937 if hasattr(object, '__path__'):
938 modpkgs = []
939 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000940 path = os.path.join(object.__path__[0], file)
941 modname = inspect.getmodulename(file)
942 if modname and modname not in modpkgs:
943 modpkgs.append(modname)
944 elif ispackage(path):
945 modpkgs.append(file + ' (package)')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000946 modpkgs.sort()
947 result = result + self.section(
948 'PACKAGE CONTENTS', join(modpkgs, '\n'))
949
950 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000951 classlist = map(lambda (key, value): value, classes)
952 contents = [self.formattree(
953 inspect.getclasstree(classlist, 1), name)]
954 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000955 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000956 result = result + self.section('CLASSES', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000957
958 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000959 contents = []
960 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000961 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000962 result = result + self.section('FUNCTIONS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000963
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000964 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000965 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000966 for key, value in data:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000967 contents.append(self.docother(value, key, name, 70))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000968 result = result + self.section('DATA', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000969
970 if hasattr(object, '__version__'):
971 version = str(object.__version__)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000972 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
973 version = strip(version[11:-1])
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000974 result = result + self.section('VERSION', version)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000975 if hasattr(object, '__date__'):
976 result = result + self.section('DATE', str(object.__date__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000977 if hasattr(object, '__author__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000978 result = result + self.section('AUTHOR', str(object.__author__))
979 if hasattr(object, '__credits__'):
980 result = result + self.section('CREDITS', str(object.__credits__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000981 return result
982
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000983 def docclass(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000984 """Produce text documentation for a given class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000985 realname = object.__name__
986 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000987 bases = object.__bases__
988
Tim Petersc86f6ca2001-09-26 21:31:51 +0000989 def makename(c, m=object.__module__):
990 return classname(c, m)
991
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000992 if name == realname:
993 title = 'class ' + self.bold(realname)
994 else:
995 title = self.bold(name) + ' = class ' + realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000996 if bases:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000997 parents = map(makename, bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000998 title = title + '(%s)' % join(parents, ', ')
999
1000 doc = getdoc(object)
Tim Peters28355492001-09-23 21:29:55 +00001001 contents = doc and [doc + '\n'] or []
1002 push = contents.append
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001003
Tim Petersc86f6ca2001-09-26 21:31:51 +00001004 # List the mro, if non-trivial.
Tim Peters351e3622001-09-27 03:29:51 +00001005 mro = list(inspect.getmro(object))
Tim Petersc86f6ca2001-09-26 21:31:51 +00001006 if len(mro) > 2:
1007 push("Method resolution order:")
1008 for base in mro:
1009 push(' ' + makename(base))
1010 push('')
1011
Tim Petersf4aad8e2001-09-24 22:40:47 +00001012 # Cute little class to pump out a horizontal rule between sections.
1013 class HorizontalRule:
1014 def __init__(self):
1015 self.needone = 0
1016 def maybe(self):
1017 if self.needone:
1018 push('-' * 70)
1019 self.needone = 1
1020 hr = HorizontalRule()
1021
Tim Peters28355492001-09-23 21:29:55 +00001022 def spill(msg, attrs, predicate):
Tim Petersfa26f7c2001-09-24 08:05:11 +00001023 ok, attrs = _split_list(attrs, predicate)
Tim Peters28355492001-09-23 21:29:55 +00001024 if ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001025 hr.maybe()
Tim Peters28355492001-09-23 21:29:55 +00001026 push(msg)
1027 for name, kind, homecls, value in ok:
1028 push(self.document(getattr(object, name),
1029 name, mod, object))
1030 return attrs
1031
Tim Petersfa26f7c2001-09-24 08:05:11 +00001032 def spillproperties(msg, attrs, predicate):
1033 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:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001038 push(name)
1039 need_blank_after_doc = 0
1040 doc = getdoc(value) or ''
1041 if doc:
1042 push(self.indent(doc))
1043 need_blank_after_doc = 1
Tim Petersf33532c2001-09-25 06:30:51 +00001044 for attr, tag in [("fget", " getter"),
1045 ("fset", " setter"),
Tim Petersf4aad8e2001-09-24 22:40:47 +00001046 ("fdel", " deleter")]:
1047 func = getattr(value, attr)
1048 if func is not None:
1049 if need_blank_after_doc:
1050 push('')
1051 need_blank_after_doc = 0
1052 base = self.docother(func, name + tag, mod, 70)
1053 push(self.indent(base))
1054 push('')
Tim Peters28355492001-09-23 21:29:55 +00001055 return attrs
Tim Petersb47879b2001-09-24 04:47:19 +00001056
Tim Petersfa26f7c2001-09-24 08:05:11 +00001057 def spilldata(msg, attrs, predicate):
1058 ok, attrs = _split_list(attrs, predicate)
Tim Peters28355492001-09-23 21:29:55 +00001059 if ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001060 hr.maybe()
Tim Peters28355492001-09-23 21:29:55 +00001061 push(msg)
1062 for name, kind, homecls, value in ok:
1063 doc = getattr(value, "__doc__", None)
1064 push(self.docother(getattr(object, name),
1065 name, mod, 70, doc) + '\n')
1066 return attrs
1067
1068 attrs = inspect.classify_class_attrs(object)
Tim Petersfa26f7c2001-09-24 08:05:11 +00001069 while attrs:
Tim Peters351e3622001-09-27 03:29:51 +00001070 if mro:
1071 thisclass = mro.pop(0)
1072 else:
1073 thisclass = attrs[0][2]
Tim Petersfa26f7c2001-09-24 08:05:11 +00001074 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
1075
Tim Peters28355492001-09-23 21:29:55 +00001076 if thisclass is object:
1077 tag = "defined here"
1078 else:
Tim Petersfa26f7c2001-09-24 08:05:11 +00001079 tag = "inherited from %s" % classname(thisclass,
1080 object.__module__)
Tim Peters28355492001-09-23 21:29:55 +00001081
1082 # Sort attrs by name.
1083 attrs.sort(lambda t1, t2: cmp(t1[0], t2[0]))
1084
1085 # Pump out the attrs, segregated by kind.
Tim Petersf4aad8e2001-09-24 22:40:47 +00001086 attrs = spill("Methods %s:\n" % tag, attrs,
Tim Peters28355492001-09-23 21:29:55 +00001087 lambda t: t[1] == 'method')
Tim Petersf4aad8e2001-09-24 22:40:47 +00001088 attrs = spill("Class methods %s:\n" % tag, attrs,
Tim Peters28355492001-09-23 21:29:55 +00001089 lambda t: t[1] == 'class method')
Tim Petersf4aad8e2001-09-24 22:40:47 +00001090 attrs = spill("Static methods %s:\n" % tag, attrs,
Tim Peters28355492001-09-23 21:29:55 +00001091 lambda t: t[1] == 'static method')
Tim Petersf4aad8e2001-09-24 22:40:47 +00001092 attrs = spillproperties("Properties %s:\n" % tag, attrs,
Tim Petersfa26f7c2001-09-24 08:05:11 +00001093 lambda t: t[1] == 'property')
Tim Petersf33532c2001-09-25 06:30:51 +00001094 attrs = spilldata("Data and non-method functions %s:\n" % tag,
1095 attrs, lambda t: t[1] == 'data')
Tim Peters28355492001-09-23 21:29:55 +00001096 assert attrs == []
Tim Peters351e3622001-09-27 03:29:51 +00001097 attrs = inherited
Tim Peters28355492001-09-23 21:29:55 +00001098
1099 contents = '\n'.join(contents)
1100 if not contents:
1101 return title + '\n'
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001102 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
1103
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001104 def formatvalue(self, object):
1105 """Format an argument default value as text."""
1106 return '=' + self.repr(object)
1107
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001108 def docroutine(self, object, name=None, mod=None, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001109 """Produce text documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001110 realname = object.__name__
1111 name = name or realname
1112 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001113 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001114 if inspect.ismethod(object):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001115 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001116 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001117 if imclass is not cl:
1118 note = ' from ' + classname(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001119 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +00001120 if object.im_self:
1121 note = ' method of %s instance' % classname(
1122 object.im_self.__class__, mod)
1123 else:
1124 note = ' unbound %s method' % classname(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001125 object = object.im_func
1126
1127 if name == realname:
1128 title = self.bold(realname)
1129 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001130 if (cl and cl.__dict__.has_key(realname) and
1131 cl.__dict__[realname] is object):
1132 skipdocs = 1
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001133 title = self.bold(name) + ' = ' + realname
Tim Peters4bcfa312001-09-20 06:08:24 +00001134 if inspect.isfunction(object):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001135 args, varargs, varkw, defaults = inspect.getargspec(object)
1136 argspec = inspect.formatargspec(
1137 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001138 if realname == '<lambda>':
1139 title = 'lambda'
1140 argspec = argspec[1:-1] # remove parentheses
Tim Peters4bcfa312001-09-20 06:08:24 +00001141 else:
1142 argspec = '(...)'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001143 decl = title + argspec + note
1144
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001145 if skipdocs:
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001146 return decl + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001147 else:
1148 doc = getdoc(object) or ''
1149 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001150
Tim Peters28355492001-09-23 21:29:55 +00001151 def docother(self, object, name=None, mod=None, maxlen=None, doc=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001152 """Produce text documentation for a data object."""
1153 repr = self.repr(object)
1154 if maxlen:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001155 line = (name and name + ' = ' or '') + repr
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001156 chop = maxlen - len(line)
1157 if chop < 0: repr = repr[:chop] + '...'
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001158 line = (name and self.bold(name) + ' = ' or '') + repr
Tim Peters28355492001-09-23 21:29:55 +00001159 if doc is not None:
1160 line += '\n' + self.indent(str(doc))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001161 return line
1162
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001163# --------------------------------------------------------- user interfaces
1164
1165def pager(text):
1166 """The first time this is called, determine what kind of pager to use."""
1167 global pager
1168 pager = getpager()
1169 pager(text)
1170
1171def getpager():
1172 """Decide what method to use for paging through text."""
1173 if type(sys.stdout) is not types.FileType:
1174 return plainpager
1175 if not sys.stdin.isatty() or not sys.stdout.isatty():
1176 return plainpager
Fred Drake0a66fcb2001-07-23 19:44:30 +00001177 if os.environ.get('TERM') in ['dumb', 'emacs']:
Fred Drake5e9eb982001-07-23 19:48:10 +00001178 return plainpager
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001179 if os.environ.has_key('PAGER'):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001180 if sys.platform == 'win32': # pipes completely broken in Windows
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001181 return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
1182 elif os.environ.get('TERM') in ['dumb', 'emacs']:
1183 return lambda text: pipepager(plain(text), os.environ['PAGER'])
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001184 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001185 return lambda text: pipepager(text, os.environ['PAGER'])
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001186 if sys.platform == 'win32':
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001187 return lambda text: tempfilepager(plain(text), 'more <')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001188 if hasattr(os, 'system') and os.system('less 2>/dev/null') == 0:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001189 return lambda text: pipepager(text, 'less')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001190
1191 import tempfile
1192 filename = tempfile.mktemp()
1193 open(filename, 'w').close()
1194 try:
1195 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
1196 return lambda text: pipepager(text, 'more')
1197 else:
1198 return ttypager
1199 finally:
1200 os.unlink(filename)
1201
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001202def plain(text):
1203 """Remove boldface formatting from text."""
1204 return re.sub('.\b', '', text)
1205
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001206def pipepager(text, cmd):
1207 """Page through text by feeding it to another program."""
1208 pipe = os.popen(cmd, 'w')
1209 try:
1210 pipe.write(text)
1211 pipe.close()
1212 except IOError:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001213 pass # Ignore broken pipes caused by quitting the pager program.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001214
1215def tempfilepager(text, cmd):
1216 """Page through text by invoking a program on a temporary file."""
1217 import tempfile
1218 filename = tempfile.mktemp()
1219 file = open(filename, 'w')
1220 file.write(text)
1221 file.close()
1222 try:
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001223 os.system(cmd + ' ' + filename)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001224 finally:
1225 os.unlink(filename)
1226
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001227def ttypager(text):
1228 """Page through text on a text terminal."""
1229 lines = split(plain(text), '\n')
1230 try:
1231 import tty
1232 fd = sys.stdin.fileno()
1233 old = tty.tcgetattr(fd)
1234 tty.setcbreak(fd)
1235 getchar = lambda: sys.stdin.read(1)
Ka-Ping Yee457aab22001-02-27 23:36:29 +00001236 except (ImportError, AttributeError):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001237 tty = None
1238 getchar = lambda: sys.stdin.readline()[:-1][:1]
1239
1240 try:
1241 r = inc = os.environ.get('LINES', 25) - 1
1242 sys.stdout.write(join(lines[:inc], '\n') + '\n')
1243 while lines[r:]:
1244 sys.stdout.write('-- more --')
1245 sys.stdout.flush()
1246 c = getchar()
1247
1248 if c in ['q', 'Q']:
1249 sys.stdout.write('\r \r')
1250 break
1251 elif c in ['\r', '\n']:
1252 sys.stdout.write('\r \r' + lines[r] + '\n')
1253 r = r + 1
1254 continue
1255 if c in ['b', 'B', '\x1b']:
1256 r = r - inc - inc
1257 if r < 0: r = 0
1258 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1259 r = r + inc
1260
1261 finally:
1262 if tty:
1263 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1264
1265def plainpager(text):
1266 """Simply print unformatted text. This is the ultimate fallback."""
1267 sys.stdout.write(plain(text))
1268
1269def describe(thing):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001270 """Produce a short description of the given thing."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001271 if inspect.ismodule(thing):
1272 if thing.__name__ in sys.builtin_module_names:
1273 return 'built-in module ' + thing.__name__
1274 if hasattr(thing, '__path__'):
1275 return 'package ' + thing.__name__
1276 else:
1277 return 'module ' + thing.__name__
1278 if inspect.isbuiltin(thing):
1279 return 'built-in function ' + thing.__name__
1280 if inspect.isclass(thing):
1281 return 'class ' + thing.__name__
1282 if inspect.isfunction(thing):
1283 return 'function ' + thing.__name__
1284 if inspect.ismethod(thing):
1285 return 'method ' + thing.__name__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001286 if type(thing) is types.InstanceType:
1287 return 'instance of ' + thing.__class__.__name__
1288 return type(thing).__name__
1289
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001290def locate(path, forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001291 """Locate an object by name or dotted path, importing as necessary."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001292 parts = split(path, '.')
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001293 module, n = None, 0
1294 while n < len(parts):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001295 nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001296 if nextmodule: module, n = nextmodule, n + 1
1297 else: break
1298 if module:
1299 object = module
1300 for part in parts[n:]:
1301 try: object = getattr(object, part)
1302 except AttributeError: return None
1303 return object
1304 else:
1305 import __builtin__
1306 if hasattr(__builtin__, path):
1307 return getattr(__builtin__, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001308
1309# --------------------------------------- interactive interpreter interface
1310
1311text = TextDoc()
1312html = HTMLDoc()
1313
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001314def doc(thing, title='Python Library Documentation: %s', forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001315 """Display text documentation, given an object or a path to an object."""
1316 suffix, name = '', None
1317 if type(thing) is type(''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001318 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001319 object = locate(thing, forceload)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001320 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001321 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001322 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001323 if not object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001324 print 'no Python documentation found for %s' % repr(thing)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001325 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001326 parts = split(thing, '.')
1327 if len(parts) > 1: suffix = ' in ' + join(parts[:-1], '.')
1328 name = parts[-1]
1329 thing = object
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001330
1331 desc = describe(thing)
1332 module = inspect.getmodule(thing)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001333 if not suffix and module and module is not thing:
1334 suffix = ' in module ' + module.__name__
Ka-Ping Yee66246962001-04-12 11:59:50 +00001335 pager(title % (desc + suffix) + '\n\n' + text.document(thing, name))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001336
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001337def writedoc(key, forceload=0):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001338 """Write HTML documentation to a file in the current directory."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001339 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001340 object = locate(key, forceload)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001341 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001342 print value
1343 else:
1344 if object:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001345 page = html.page(describe(object),
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001346 html.document(object, object.__name__))
1347 file = open(key + '.html', 'w')
1348 file.write(page)
1349 file.close()
1350 print 'wrote', key + '.html'
1351 else:
1352 print 'no Python documentation found for %s' % repr(key)
1353
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001354def writedocs(dir, pkgpath='', done=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001355 """Write out HTML documentation for all modules in a directory tree."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001356 if done is None: done = {}
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001357 for file in os.listdir(dir):
1358 path = os.path.join(dir, file)
1359 if ispackage(path):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001360 writedocs(path, pkgpath + file + '.', done)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001361 elif os.path.isfile(path):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001362 modname = inspect.getmodulename(path)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001363 if modname:
1364 modname = pkgpath + modname
1365 if not done.has_key(modname):
1366 done[modname] = 1
1367 writedoc(modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001368
1369class Helper:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001370 keywords = {
1371 'and': 'BOOLEAN',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001372 'assert': ('ref/assert', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001373 'break': ('ref/break', 'while for'),
1374 'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1375 'continue': ('ref/continue', 'while for'),
1376 'def': ('ref/function', ''),
1377 'del': ('ref/del', 'BASICMETHODS'),
1378 'elif': 'if',
1379 'else': ('ref/if', 'while for'),
1380 'except': 'try',
1381 'exec': ('ref/exec', ''),
1382 'finally': 'try',
1383 'for': ('ref/for', 'break continue while'),
1384 'from': 'import',
1385 'global': ('ref/global', 'NAMESPACES'),
1386 'if': ('ref/if', 'TRUTHVALUE'),
1387 'import': ('ref/import', 'MODULES'),
1388 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1389 'is': 'COMPARISON',
1390 'lambda': ('ref/lambda', 'FUNCTIONS'),
1391 'not': 'BOOLEAN',
1392 'or': 'BOOLEAN',
1393 'pass': 'PASS',
1394 'print': ('ref/print', ''),
1395 'raise': ('ref/raise', 'EXCEPTIONS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001396 'return': ('ref/return', 'FUNCTIONS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001397 'try': ('ref/try', 'EXCEPTIONS'),
1398 'while': ('ref/while', 'break continue if TRUTHVALUE'),
1399 }
1400
1401 topics = {
1402 'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001403 'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001404 'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'),
1405 'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001406 'UNICODE': ('ref/unicode', 'encodings unicode TYPES STRING'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001407 'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1408 'INTEGER': ('ref/integers', 'int range'),
1409 'FLOAT': ('ref/floating', 'float math'),
1410 'COMPLEX': ('ref/imaginary', 'complex cmath'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001411 'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001412 'MAPPINGS': 'DICTIONARIES',
1413 'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'),
1414 'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'),
1415 'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001416 'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001417 'FRAMEOBJECTS': 'TYPES',
1418 'TRACEBACKS': 'TYPES',
1419 'NONE': ('lib/bltin-null-object', ''),
1420 'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'),
1421 'FILES': ('lib/bltin-file-objects', ''),
1422 'SPECIALATTRIBUTES': ('lib/specialattrs', ''),
1423 'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'),
1424 'MODULES': ('lib/typesmodules', 'import'),
1425 'PACKAGES': 'import',
1426 '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'),
1427 'OPERATORS': 'EXPRESSIONS',
1428 'PRECEDENCE': 'EXPRESSIONS',
1429 'OBJECTS': ('ref/objects', 'TYPES'),
1430 'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001431 'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'),
1432 'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1433 'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'),
1434 'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'),
1435 'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'),
1436 'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'),
1437 'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001438 'EXECUTION': ('ref/execframes', ''),
1439 'NAMESPACES': ('ref/execframes', 'global ASSIGNMENT DELETION'),
1440 'SCOPING': 'NAMESPACES',
1441 'FRAMES': 'NAMESPACES',
1442 'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'),
1443 'COERCIONS': 'CONVERSIONS',
1444 'CONVERSIONS': ('ref/conversions', ''),
1445 'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'),
1446 'SPECIALIDENTIFIERS': ('ref/id-classes', ''),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001447 'PRIVATENAMES': ('ref/atom-identifiers', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001448 'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1449 'TUPLES': 'SEQUENCES',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001450 'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001451 'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001452 'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001453 'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001454 'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'),
1455 'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001456 'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1457 'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'),
1458 'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'),
1459 'CALLS': ('ref/calls', 'EXPRESSIONS'),
1460 'POWER': ('ref/power', 'EXPRESSIONS'),
1461 'UNARY': ('ref/unary', 'EXPRESSIONS'),
1462 'BINARY': ('ref/binary', 'EXPRESSIONS'),
1463 'SHIFTING': ('ref/shifting', 'EXPRESSIONS'),
1464 'BITWISE': ('ref/bitwise', 'EXPRESSIONS'),
1465 'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001466 'BOOLEAN': ('ref/lambda', 'EXPRESSIONS TRUTHVALUE'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001467 'ASSERTION': 'assert',
1468 'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001469 'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001470 'DELETION': 'del',
1471 'PRINTING': 'print',
1472 'RETURNING': 'return',
1473 'IMPORTING': 'import',
1474 'CONDITIONAL': 'if',
1475 'LOOPING': ('ref/compound', 'for while break continue'),
1476 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001477 'DEBUGGING': ('lib/module-pdb', 'pdb'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001478 }
1479
1480 def __init__(self, input, output):
1481 self.input = input
1482 self.output = output
1483 self.docdir = None
1484 execdir = os.path.dirname(sys.executable)
1485 homedir = os.environ.get('PYTHONHOME')
1486 for dir in [os.environ.get('PYTHONDOCS'),
1487 homedir and os.path.join(homedir, 'doc'),
1488 os.path.join(execdir, 'doc'),
1489 '/usr/doc/python-docs-' + split(sys.version)[0],
1490 '/usr/doc/python-' + split(sys.version)[0],
1491 '/usr/doc/python-docs-' + sys.version[:3],
1492 '/usr/doc/python-' + sys.version[:3]]:
1493 if dir and os.path.isdir(os.path.join(dir, 'lib')):
1494 self.docdir = dir
1495
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001496 def __repr__(self):
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001497 if inspect.stack()[1][3] == '?':
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001498 self()
1499 return ''
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001500 return '<pydoc.Helper instance>'
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001501
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001502 def __call__(self, request=None):
1503 if request is not None:
1504 self.help(request)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001505 else:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001506 self.intro()
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001507 self.interact()
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001508 self.output.write('''
Fred Drakee61967f2001-05-10 18:41:02 +00001509You are now leaving help and returning to the Python interpreter.
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001510If you want to ask for help on a particular object directly from the
1511interpreter, you can type "help(object)". Executing "help('string')"
1512has the same effect as typing a particular string at the help> prompt.
1513''')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001514
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001515 def interact(self):
1516 self.output.write('\n')
1517 while 1:
1518 self.output.write('help> ')
1519 self.output.flush()
1520 try:
1521 request = self.input.readline()
1522 if not request: break
1523 except KeyboardInterrupt: break
1524 request = strip(replace(request, '"', '', "'", ''))
1525 if lower(request) in ['q', 'quit']: break
1526 self.help(request)
1527
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001528 def help(self, request):
1529 if type(request) is type(''):
1530 if request == 'help': self.intro()
1531 elif request == 'keywords': self.listkeywords()
1532 elif request == 'topics': self.listtopics()
1533 elif request == 'modules': self.listmodules()
1534 elif request[:8] == 'modules ':
1535 self.listmodules(split(request)[1])
1536 elif self.keywords.has_key(request): self.showtopic(request)
1537 elif self.topics.has_key(request): self.showtopic(request)
1538 elif request: doc(request, 'Help on %s:')
1539 elif isinstance(request, Helper): self()
1540 else: doc(request, 'Help on %s:')
1541 self.output.write('\n')
1542
1543 def intro(self):
1544 self.output.write('''
1545Welcome to Python %s! This is the online help utility.
1546
1547If this is your first time using Python, you should definitely check out
1548the tutorial on the Internet at http://www.python.org/doc/tut/.
1549
1550Enter the name of any module, keyword, or topic to get help on writing
1551Python programs and using Python modules. To quit this help utility and
1552return to the interpreter, just type "quit".
1553
1554To get a list of available modules, keywords, or topics, type "modules",
1555"keywords", or "topics". Each module also comes with a one-line summary
1556of what it does; to list the modules whose summaries contain a given word
1557such as "spam", type "modules spam".
1558''' % sys.version[:3])
1559
1560 def list(self, items, columns=4, width=80):
1561 items = items[:]
1562 items.sort()
1563 colw = width / columns
1564 rows = (len(items) + columns - 1) / columns
1565 for row in range(rows):
1566 for col in range(columns):
1567 i = col * rows + row
1568 if i < len(items):
1569 self.output.write(items[i])
1570 if col < columns - 1:
1571 self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1572 self.output.write('\n')
1573
1574 def listkeywords(self):
1575 self.output.write('''
1576Here is a list of the Python keywords. Enter any keyword to get more help.
1577
1578''')
1579 self.list(self.keywords.keys())
1580
1581 def listtopics(self):
1582 self.output.write('''
1583Here is a list of available topics. Enter any topic name to get more help.
1584
1585''')
1586 self.list(self.topics.keys())
1587
1588 def showtopic(self, topic):
1589 if not self.docdir:
1590 self.output.write('''
1591Sorry, topic and keyword documentation is not available because the Python
1592HTML documentation files could not be found. If you have installed them,
1593please set the environment variable PYTHONDOCS to indicate their location.
1594''')
1595 return
1596 target = self.topics.get(topic, self.keywords.get(topic))
1597 if not target:
1598 self.output.write('no documentation found for %s\n' % repr(topic))
1599 return
1600 if type(target) is type(''):
1601 return self.showtopic(target)
1602
1603 filename, xrefs = target
1604 filename = self.docdir + '/' + filename + '.html'
1605 try:
1606 file = open(filename)
1607 except:
1608 self.output.write('could not read docs from %s\n' % filename)
1609 return
1610
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001611 divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
1612 addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001613 document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1614 file.close()
1615
1616 import htmllib, formatter, StringIO
1617 buffer = StringIO.StringIO()
1618 parser = htmllib.HTMLParser(
1619 formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1620 parser.start_table = parser.do_p
1621 parser.end_table = lambda parser=parser: parser.do_p({})
1622 parser.start_tr = parser.do_br
1623 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1624 parser.feed(document)
1625 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
1626 pager(' ' + strip(buffer) + '\n')
Ka-Ping Yeeda793892001-04-13 11:02:51 +00001627 if xrefs:
1628 buffer = StringIO.StringIO()
1629 formatter.DumbWriter(buffer).send_flowing_data(
1630 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1631 self.output.write('\n%s\n' % buffer.getvalue())
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001632
1633 def listmodules(self, key=''):
1634 if key:
1635 self.output.write('''
1636Here is a list of matching modules. Enter any module name to get more help.
1637
1638''')
1639 apropos(key)
1640 else:
1641 self.output.write('''
1642Please wait a moment while I gather a list of all available modules...
1643
1644''')
1645 modules = {}
1646 def callback(path, modname, desc, modules=modules):
1647 if modname and modname[-9:] == '.__init__':
1648 modname = modname[:-9] + ' (package)'
1649 if find(modname, '.') < 0:
1650 modules[modname] = 1
1651 ModuleScanner().run(callback)
1652 self.list(modules.keys())
1653 self.output.write('''
1654Enter any module name to get more help. Or, type "modules spam" to search
1655for modules whose descriptions contain the word "spam".
1656''')
1657
1658help = Helper(sys.stdin, sys.stdout)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001659
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001660class Scanner:
1661 """A generic tree iterator."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001662 def __init__(self, roots, children, descendp):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001663 self.roots = roots[:]
1664 self.state = []
1665 self.children = children
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001666 self.descendp = descendp
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001667
1668 def next(self):
1669 if not self.state:
1670 if not self.roots:
1671 return None
1672 root = self.roots.pop(0)
1673 self.state = [(root, self.children(root))]
1674 node, children = self.state[-1]
1675 if not children:
1676 self.state.pop()
1677 return self.next()
1678 child = children.pop(0)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001679 if self.descendp(child):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001680 self.state.append((child, self.children(child)))
1681 return child
1682
1683class ModuleScanner(Scanner):
1684 """An interruptible scanner that searches module synopses."""
1685 def __init__(self):
1686 roots = map(lambda dir: (dir, ''), pathdirs())
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001687 Scanner.__init__(self, roots, self.submodules, self.isnewpackage)
1688 self.inodes = map(lambda (dir, pkg): os.stat(dir)[1], roots)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001689
1690 def submodules(self, (dir, package)):
1691 children = []
1692 for file in os.listdir(dir):
1693 path = os.path.join(dir, file)
Tim Peters30edd232001-03-16 08:29:48 +00001694 if ispackage(path):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001695 children.append((path, package + (package and '.') + file))
1696 else:
1697 children.append((path, package))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001698 children.sort() # so that spam.py comes before spam.pyc or spam.pyo
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001699 return children
1700
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001701 def isnewpackage(self, (dir, package)):
Ka-Ping Yee6191a232001-04-13 15:00:27 +00001702 inode = os.path.exists(dir) and os.stat(dir)[1]
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001703 if not (os.path.islink(dir) and inode in self.inodes):
Ka-Ping Yee6191a232001-04-13 15:00:27 +00001704 self.inodes.append(inode) # detect circular symbolic links
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001705 return ispackage(dir)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001706
Ka-Ping Yee66246962001-04-12 11:59:50 +00001707 def run(self, callback, key=None, completer=None):
1708 if key: key = lower(key)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001709 self.quit = 0
1710 seen = {}
1711
1712 for modname in sys.builtin_module_names:
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001713 if modname != '__main__':
1714 seen[modname] = 1
Ka-Ping Yee66246962001-04-12 11:59:50 +00001715 if key is None:
1716 callback(None, modname, '')
1717 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001718 desc = split(__import__(modname).__doc__ or '', '\n')[0]
Ka-Ping Yee66246962001-04-12 11:59:50 +00001719 if find(lower(modname + ' - ' + desc), key) >= 0:
1720 callback(None, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001721
1722 while not self.quit:
1723 node = self.next()
1724 if not node: break
1725 path, package = node
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001726 modname = inspect.getmodulename(path)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001727 if os.path.isfile(path) and modname:
1728 modname = package + (package and '.') + modname
1729 if not seen.has_key(modname):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001730 seen[modname] = 1 # if we see spam.py, skip spam.pyc
Ka-Ping Yee66246962001-04-12 11:59:50 +00001731 if key is None:
1732 callback(path, modname, '')
1733 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001734 desc = synopsis(path) or ''
1735 if find(lower(modname + ' - ' + desc), key) >= 0:
1736 callback(path, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001737 if completer: completer()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001738
1739def apropos(key):
1740 """Print all the one-line module summaries that contain a substring."""
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001741 def callback(path, modname, desc):
1742 if modname[-9:] == '.__init__':
1743 modname = modname[:-9] + ' (package)'
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001744 print modname, desc and '- ' + desc
1745 try: import warnings
1746 except ImportError: pass
1747 else: warnings.filterwarnings('ignore') # ignore problems during import
Ka-Ping Yee66246962001-04-12 11:59:50 +00001748 ModuleScanner().run(callback, key)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001749
1750# --------------------------------------------------- web browser interface
1751
Ka-Ping Yee66246962001-04-12 11:59:50 +00001752def serve(port, callback=None, completer=None):
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001753 import BaseHTTPServer, mimetools, select
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001754
1755 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1756 class Message(mimetools.Message):
1757 def __init__(self, fp, seekable=1):
1758 Message = self.__class__
1759 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1760 self.encodingheader = self.getheader('content-transfer-encoding')
1761 self.typeheader = self.getheader('content-type')
1762 self.parsetype()
1763 self.parseplist()
1764
1765 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1766 def send_document(self, title, contents):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001767 try:
1768 self.send_response(200)
1769 self.send_header('Content-Type', 'text/html')
1770 self.end_headers()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001771 self.wfile.write(html.page(title, contents))
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001772 except IOError: pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001773
1774 def do_GET(self):
1775 path = self.path
1776 if path[-5:] == '.html': path = path[:-5]
1777 if path[:1] == '/': path = path[1:]
1778 if path and path != '.':
1779 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001780 obj = locate(path, forceload=1)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001781 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001782 self.send_document(path, html.escape(str(value)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001783 return
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001784 if obj:
1785 self.send_document(describe(obj), html.document(obj, path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001786 else:
1787 self.send_document(path,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001788'no Python documentation found for %s' % repr(path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001789 else:
1790 heading = html.heading(
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001791'<big><big><strong>Python: Index of Modules</strong></big></big>',
1792'#ffffff', '#7799ee')
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001793 def bltinlink(name):
1794 return '<a href="%s.html">%s</a>' % (name, name)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001795 names = filter(lambda x: x != '__main__',
1796 sys.builtin_module_names)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001797 contents = html.multicolumn(names, bltinlink)
1798 indices = ['<p>' + html.bigsection(
1799 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1800
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001801 seen = {}
1802 for dir in pathdirs():
1803 indices.append(html.index(dir, seen))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001804 contents = heading + join(indices) + '''<p align=right>
Tim Peters2306d242001-09-25 03:18:32 +00001805<font color="#909090" face="helvetica, arial"><strong>
1806pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font>'''
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001807 self.send_document('Index of Modules', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001808
1809 def log_message(self, *args): pass
1810
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001811 class DocServer(BaseHTTPServer.HTTPServer):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001812 def __init__(self, port, callback):
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001813 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001814 self.address = ('', port)
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001815 self.url = 'http://%s:%d/' % (host, port)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001816 self.callback = callback
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001817 self.base.__init__(self, self.address, self.handler)
1818
1819 def serve_until_quit(self):
1820 import select
1821 self.quit = 0
1822 while not self.quit:
1823 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1824 if rd: self.handle_request()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001825
1826 def server_activate(self):
1827 self.base.server_activate(self)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001828 if self.callback: self.callback(self)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001829
1830 DocServer.base = BaseHTTPServer.HTTPServer
1831 DocServer.handler = DocHandler
1832 DocHandler.MessageClass = Message
1833 try:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001834 try:
1835 DocServer(port, callback).serve_until_quit()
1836 except (KeyboardInterrupt, select.error):
1837 pass
1838 finally:
Ka-Ping Yee66246962001-04-12 11:59:50 +00001839 if completer: completer()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001840
1841# ----------------------------------------------------- graphical interface
1842
1843def gui():
1844 """Graphical interface (starts web server and pops up a control window)."""
1845 class GUI:
1846 def __init__(self, window, port=7464):
1847 self.window = window
1848 self.server = None
1849 self.scanner = None
1850
1851 import Tkinter
1852 self.server_frm = Tkinter.Frame(window)
1853 self.title_lbl = Tkinter.Label(self.server_frm,
1854 text='Starting server...\n ')
1855 self.open_btn = Tkinter.Button(self.server_frm,
1856 text='open browser', command=self.open, state='disabled')
1857 self.quit_btn = Tkinter.Button(self.server_frm,
1858 text='quit serving', command=self.quit, state='disabled')
1859
1860 self.search_frm = Tkinter.Frame(window)
1861 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
1862 self.search_ent = Tkinter.Entry(self.search_frm)
1863 self.search_ent.bind('<Return>', self.search)
1864 self.stop_btn = Tkinter.Button(self.search_frm,
1865 text='stop', pady=0, command=self.stop, state='disabled')
1866 if sys.platform == 'win32':
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001867 # Trying to hide and show this button crashes under Windows.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001868 self.stop_btn.pack(side='right')
1869
1870 self.window.title('pydoc')
1871 self.window.protocol('WM_DELETE_WINDOW', self.quit)
1872 self.title_lbl.pack(side='top', fill='x')
1873 self.open_btn.pack(side='left', fill='x', expand=1)
1874 self.quit_btn.pack(side='right', fill='x', expand=1)
1875 self.server_frm.pack(side='top', fill='x')
1876
1877 self.search_lbl.pack(side='left')
1878 self.search_ent.pack(side='right', fill='x', expand=1)
1879 self.search_frm.pack(side='top', fill='x')
1880 self.search_ent.focus_set()
1881
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001882 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001883 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001884 self.result_lst.bind('<Button-1>', self.select)
1885 self.result_lst.bind('<Double-Button-1>', self.goto)
1886 self.result_scr = Tkinter.Scrollbar(window,
1887 orient='vertical', command=self.result_lst.yview)
1888 self.result_lst.config(yscrollcommand=self.result_scr.set)
1889
1890 self.result_frm = Tkinter.Frame(window)
1891 self.goto_btn = Tkinter.Button(self.result_frm,
1892 text='go to selected', command=self.goto)
1893 self.hide_btn = Tkinter.Button(self.result_frm,
1894 text='hide results', command=self.hide)
1895 self.goto_btn.pack(side='left', fill='x', expand=1)
1896 self.hide_btn.pack(side='right', fill='x', expand=1)
1897
1898 self.window.update()
1899 self.minwidth = self.window.winfo_width()
1900 self.minheight = self.window.winfo_height()
1901 self.bigminheight = (self.server_frm.winfo_reqheight() +
1902 self.search_frm.winfo_reqheight() +
1903 self.result_lst.winfo_reqheight() +
1904 self.result_frm.winfo_reqheight())
1905 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
1906 self.expanded = 0
1907 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1908 self.window.wm_minsize(self.minwidth, self.minheight)
1909
1910 import threading
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001911 threading.Thread(
1912 target=serve, args=(port, self.ready, self.quit)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001913
1914 def ready(self, server):
1915 self.server = server
1916 self.title_lbl.config(
1917 text='Python documentation server at\n' + server.url)
1918 self.open_btn.config(state='normal')
1919 self.quit_btn.config(state='normal')
1920
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001921 def open(self, event=None, url=None):
1922 url = url or self.server.url
1923 try:
1924 import webbrowser
1925 webbrowser.open(url)
1926 except ImportError: # pre-webbrowser.py compatibility
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001927 if sys.platform == 'win32':
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001928 os.system('start "%s"' % url)
1929 elif sys.platform == 'mac':
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001930 try: import ic
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001931 except ImportError: pass
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001932 else: ic.launchurl(url)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001933 else:
1934 rc = os.system('netscape -remote "openURL(%s)" &' % url)
1935 if rc: os.system('netscape "%s" &' % url)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001936
1937 def quit(self, event=None):
1938 if self.server:
1939 self.server.quit = 1
1940 self.window.quit()
1941
1942 def search(self, event=None):
1943 key = self.search_ent.get()
1944 self.stop_btn.pack(side='right')
1945 self.stop_btn.config(state='normal')
1946 self.search_lbl.config(text='Searching for "%s"...' % key)
1947 self.search_ent.forget()
1948 self.search_lbl.pack(side='left')
1949 self.result_lst.delete(0, 'end')
1950 self.goto_btn.config(state='disabled')
1951 self.expand()
1952
1953 import threading
1954 if self.scanner:
1955 self.scanner.quit = 1
1956 self.scanner = ModuleScanner()
1957 threading.Thread(target=self.scanner.run,
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +00001958 args=(self.update, key, self.done)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001959
1960 def update(self, path, modname, desc):
1961 if modname[-9:] == '.__init__':
1962 modname = modname[:-9] + ' (package)'
1963 self.result_lst.insert('end',
1964 modname + ' - ' + (desc or '(no description)'))
1965
1966 def stop(self, event=None):
1967 if self.scanner:
1968 self.scanner.quit = 1
1969 self.scanner = None
1970
1971 def done(self):
1972 self.scanner = None
1973 self.search_lbl.config(text='Search for')
1974 self.search_lbl.pack(side='left')
1975 self.search_ent.pack(side='right', fill='x', expand=1)
1976 if sys.platform != 'win32': self.stop_btn.forget()
1977 self.stop_btn.config(state='disabled')
1978
1979 def select(self, event=None):
1980 self.goto_btn.config(state='normal')
1981
1982 def goto(self, event=None):
1983 selection = self.result_lst.curselection()
1984 if selection:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001985 modname = split(self.result_lst.get(selection[0]))[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001986 self.open(url=self.server.url + modname + '.html')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001987
1988 def collapse(self):
1989 if not self.expanded: return
1990 self.result_frm.forget()
1991 self.result_scr.forget()
1992 self.result_lst.forget()
1993 self.bigwidth = self.window.winfo_width()
1994 self.bigheight = self.window.winfo_height()
1995 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1996 self.window.wm_minsize(self.minwidth, self.minheight)
1997 self.expanded = 0
1998
1999 def expand(self):
2000 if self.expanded: return
2001 self.result_frm.pack(side='bottom', fill='x')
2002 self.result_scr.pack(side='right', fill='y')
2003 self.result_lst.pack(side='top', fill='both', expand=1)
2004 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
2005 self.window.wm_minsize(self.minwidth, self.bigminheight)
2006 self.expanded = 1
2007
2008 def hide(self, event=None):
2009 self.stop()
2010 self.collapse()
2011
2012 import Tkinter
2013 try:
2014 gui = GUI(Tkinter.Tk())
2015 Tkinter.mainloop()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002016 except KeyboardInterrupt:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002017 pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002018
2019# -------------------------------------------------- command-line interface
2020
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002021def ispath(x):
2022 return type(x) is types.StringType and find(x, os.sep) >= 0
2023
Ka-Ping Yee1d384632001-03-01 00:24:32 +00002024def cli():
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002025 """Command-line interface (looks at sys.argv to decide what to do)."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002026 import getopt
2027 class BadUsage: pass
2028
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002029 # Scripts don't get the current directory in their path by default.
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00002030 scriptdir = os.path.dirname(sys.argv[0])
2031 if scriptdir in sys.path:
2032 sys.path.remove(scriptdir)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002033 sys.path.insert(0, '.')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002034
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002035 try:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002036 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002037 writing = 0
2038
2039 for opt, val in opts:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002040 if opt == '-g':
2041 gui()
2042 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002043 if opt == '-k':
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002044 apropos(val)
2045 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002046 if opt == '-p':
2047 try:
2048 port = int(val)
2049 except ValueError:
2050 raise BadUsage
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002051 def ready(server):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00002052 print 'pydoc server ready at %s' % server.url
2053 def stopped():
2054 print 'pydoc server stopped'
2055 serve(port, ready, stopped)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002056 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002057 if opt == '-w':
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002058 writing = 1
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002059
2060 if not args: raise BadUsage
2061 for arg in args:
2062 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002063 if ispath(arg) and os.path.isfile(arg):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002064 arg = importfile(arg)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002065 if writing:
2066 if ispath(arg) and os.path.isdir(arg):
2067 writedocs(arg)
2068 else:
2069 writedoc(arg)
2070 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00002071 doc(arg)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002072 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002073 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002074
2075 except (getopt.error, BadUsage):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002076 cmd = sys.argv[0]
2077 print """pydoc - the Python documentation tool
2078
2079%s <name> ...
2080 Show text documentation on something. <name> may be the name of a
2081 function, module, or package, or a dotted reference to a class or
2082 function within a module or module in a package. If <name> contains
2083 a '%s', it is used as the path to a Python source file to document.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002084
2085%s -k <keyword>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002086 Search for a keyword in the synopsis lines of all available modules.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002087
2088%s -p <port>
2089 Start an HTTP server on the given port on the local machine.
2090
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002091%s -g
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00002092 Pop up a graphical interface for finding and serving documentation.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002093
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002094%s -w <name> ...
2095 Write out the HTML documentation for a module to a file in the current
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00002096 directory. If <name> contains a '%s', it is treated as a filename; if
2097 it names a directory, documentation is written for all the contents.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002098""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
Ka-Ping Yee1d384632001-03-01 00:24:32 +00002099
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002100if __name__ == '__main__': cli()