blob: 84ef4a5bf2835c31b28c038d8a9444a8e360c078 [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.
624 mro = inspect.getmro(object)
625 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 # Sort attrs by name of defining class.
697 attrs.sort(lambda t1, t2: cmp(t1[2].__name__, t2[2].__name__))
Tim Petersb47879b2001-09-24 04:47:19 +0000698
Tim Petersfa26f7c2001-09-24 08:05:11 +0000699 thisclass = object # list attrs defined here first
700 while attrs:
701 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 == []
725
726 # Split off the attributes inherited from the next class (note
727 # that inherited remains sorted by class name).
728 if inherited:
729 attrs = inherited
730 thisclass = attrs[0][2]
Tim Petersb47879b2001-09-24 04:47:19 +0000731
732 contents = ''.join(contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000733
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000734 if name == realname:
735 title = '<a name="%s">class <strong>%s</strong></a>' % (
736 name, realname)
737 else:
738 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
739 name, name, realname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000740 if bases:
741 parents = []
742 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000743 parents.append(self.classlink(base, object.__module__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000744 title = title + '(%s)' % join(parents, ', ')
Tim Peters2306d242001-09-25 03:18:32 +0000745 doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict)
746 doc = doc and '<tt>%s<br>&nbsp;</tt>' % doc or '&nbsp;'
Tim Petersc86f6ca2001-09-26 21:31:51 +0000747
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000748 return self.section(title, '#000000', '#ffc8d8', contents, 5, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000749
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000750 def formatvalue(self, object):
751 """Format an argument default value as text."""
Tim Peters2306d242001-09-25 03:18:32 +0000752 return self.grey('=' + self.repr(object))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000753
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000754 def docroutine(self, object, name=None, mod=None,
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000755 funcs={}, classes={}, methods={}, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000756 """Produce HTML documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000757 realname = object.__name__
758 name = name or realname
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000759 anchor = (cl and cl.__name__ or '') + '-' + name
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000760 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000761 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000762 if inspect.ismethod(object):
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +0000763 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000764 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000765 if imclass is not cl:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000766 note = ' from ' + self.classlink(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000767 skipdocs = 1
768 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000769 if object.im_self:
770 note = ' method of %s instance' % self.classlink(
771 object.im_self.__class__, mod)
772 else:
773 note = ' unbound %s method' % self.classlink(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000774 object = object.im_func
775
776 if name == realname:
777 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
778 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000779 if (cl and cl.__dict__.has_key(realname) and
780 cl.__dict__[realname] is object):
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000781 reallink = '<a href="#%s">%s</a>' % (
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000782 cl.__name__ + '-' + realname, realname)
783 skipdocs = 1
784 else:
785 reallink = realname
786 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
787 anchor, name, reallink)
Tim Peters4bcfa312001-09-20 06:08:24 +0000788 if inspect.isfunction(object):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000789 args, varargs, varkw, defaults = inspect.getargspec(object)
790 argspec = inspect.formatargspec(
791 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000792 if realname == '<lambda>':
793 decl = '<em>lambda</em>'
794 argspec = argspec[1:-1] # remove parentheses
Tim Peters4bcfa312001-09-20 06:08:24 +0000795 else:
796 argspec = '(...)'
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000797
Tim Peters2306d242001-09-25 03:18:32 +0000798 decl = title + argspec + (note and self.grey(
799 '<font face="helvetica, arial">%s</font>' % note))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000800
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000801 if skipdocs:
Tim Peters2306d242001-09-25 03:18:32 +0000802 return '<dl><dt>%s</dt></dl>\n' % decl
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000803 else:
804 doc = self.markup(
805 getdoc(object), self.preformat, funcs, classes, methods)
Tim Peters2306d242001-09-25 03:18:32 +0000806 doc = doc and '<dd><tt>%s</tt></dd>' % doc
807 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000808
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000809 def docother(self, object, name=None, mod=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000810 """Produce HTML documentation for a data object."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000811 lhs = name and '<strong>%s</strong> = ' % name or ''
812 return lhs + self.repr(object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000813
814 def index(self, dir, shadowed=None):
815 """Generate an HTML index for a directory of modules."""
816 modpkgs = []
817 if shadowed is None: shadowed = {}
818 seen = {}
819 files = os.listdir(dir)
820
821 def found(name, ispackage,
822 modpkgs=modpkgs, shadowed=shadowed, seen=seen):
823 if not seen.has_key(name):
824 modpkgs.append((name, '', ispackage, shadowed.has_key(name)))
825 seen[name] = 1
826 shadowed[name] = 1
827
828 # Package spam/__init__.py takes precedence over module spam.py.
829 for file in files:
830 path = os.path.join(dir, file)
831 if ispackage(path): found(file, 1)
832 for file in files:
833 path = os.path.join(dir, file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000834 if os.path.isfile(path):
835 modname = inspect.getmodulename(file)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000836 if modname: found(modname, 0)
837
838 modpkgs.sort()
839 contents = self.multicolumn(modpkgs, self.modpkglink)
840 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
841
842# -------------------------------------------- text documentation generator
843
844class TextRepr(Repr):
845 """Class for safely making a text representation of a Python object."""
846 def __init__(self):
847 Repr.__init__(self)
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000848 self.maxlist = self.maxtuple = 20
849 self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000850 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000851
852 def repr1(self, x, level):
853 methodname = 'repr_' + join(split(type(x).__name__), '_')
854 if hasattr(self, methodname):
855 return getattr(self, methodname)(x, level)
856 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000857 return cram(stripid(repr(x)), self.maxother)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000858
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000859 def repr_string(self, x, level):
860 test = cram(x, self.maxstring)
861 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000862 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000863 # Backslashes are only literal in the string and are never
864 # needed to make any special characters, so show a raw string.
865 return 'r' + testrepr[0] + test + testrepr[0]
866 return testrepr
867
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000868 def repr_instance(self, x, level):
869 try:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000870 return cram(stripid(repr(x)), self.maxstring)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000871 except:
872 return '<%s instance>' % x.__class__.__name__
873
874class TextDoc(Doc):
875 """Formatter class for text documentation."""
876
877 # ------------------------------------------- text formatting utilities
878
879 _repr_instance = TextRepr()
880 repr = _repr_instance.repr
881
882 def bold(self, text):
883 """Format a string in bold by overstriking."""
884 return join(map(lambda ch: ch + '\b' + ch, text), '')
885
886 def indent(self, text, prefix=' '):
887 """Indent text by prepending a given prefix to each line."""
888 if not text: return ''
889 lines = split(text, '\n')
890 lines = map(lambda line, prefix=prefix: prefix + line, lines)
891 if lines: lines[-1] = rstrip(lines[-1])
892 return join(lines, '\n')
893
894 def section(self, title, contents):
895 """Format a section with a given heading."""
896 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
897
898 # ---------------------------------------------- type-specific routines
899
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000900 def formattree(self, tree, modname, parent=None, prefix=''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000901 """Render in text a class tree as returned by inspect.getclasstree()."""
902 result = ''
903 for entry in tree:
904 if type(entry) is type(()):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000905 c, bases = entry
906 result = result + prefix + classname(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000907 if bases and bases != (parent,):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000908 parents = map(lambda c, m=modname: classname(c, m), bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000909 result = result + '(%s)' % join(parents, ', ')
910 result = result + '\n'
911 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000912 result = result + self.formattree(
913 entry, modname, c, prefix + ' ')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000914 return result
915
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000916 def docmodule(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000917 """Produce text documentation for a given module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000918 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000919 synop, desc = splitdoc(getdoc(object))
920 result = self.section('NAME', name + (synop and ' - ' + synop))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000921
922 try:
923 file = inspect.getabsfile(object)
924 except TypeError:
925 file = '(built-in)'
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000926 result = result + self.section('FILE', file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000927 if desc:
928 result = result + self.section('DESCRIPTION', desc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000929
930 classes = []
931 for key, value in inspect.getmembers(object, inspect.isclass):
932 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000933 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000934 funcs = []
935 for key, value in inspect.getmembers(object, inspect.isroutine):
936 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000937 funcs.append((key, value))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000938 data = []
939 for key, value in inspect.getmembers(object, isdata):
940 if key not in ['__builtins__', '__doc__']:
941 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000942
943 if hasattr(object, '__path__'):
944 modpkgs = []
945 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000946 path = os.path.join(object.__path__[0], file)
947 modname = inspect.getmodulename(file)
948 if modname and modname not in modpkgs:
949 modpkgs.append(modname)
950 elif ispackage(path):
951 modpkgs.append(file + ' (package)')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000952 modpkgs.sort()
953 result = result + self.section(
954 'PACKAGE CONTENTS', join(modpkgs, '\n'))
955
956 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000957 classlist = map(lambda (key, value): value, classes)
958 contents = [self.formattree(
959 inspect.getclasstree(classlist, 1), name)]
960 for key, value in classes:
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('CLASSES', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000963
964 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000965 contents = []
966 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000967 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000968 result = result + self.section('FUNCTIONS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000969
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000970 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000971 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000972 for key, value in data:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000973 contents.append(self.docother(value, key, name, 70))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000974 result = result + self.section('DATA', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000975
976 if hasattr(object, '__version__'):
977 version = str(object.__version__)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000978 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
979 version = strip(version[11:-1])
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000980 result = result + self.section('VERSION', version)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000981 if hasattr(object, '__date__'):
982 result = result + self.section('DATE', str(object.__date__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000983 if hasattr(object, '__author__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000984 result = result + self.section('AUTHOR', str(object.__author__))
985 if hasattr(object, '__credits__'):
986 result = result + self.section('CREDITS', str(object.__credits__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000987 return result
988
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000989 def docclass(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000990 """Produce text documentation for a given class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000991 realname = object.__name__
992 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000993 bases = object.__bases__
994
Tim Petersc86f6ca2001-09-26 21:31:51 +0000995 def makename(c, m=object.__module__):
996 return classname(c, m)
997
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000998 if name == realname:
999 title = 'class ' + self.bold(realname)
1000 else:
1001 title = self.bold(name) + ' = class ' + realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001002 if bases:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001003 parents = map(makename, bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001004 title = title + '(%s)' % join(parents, ', ')
1005
1006 doc = getdoc(object)
Tim Peters28355492001-09-23 21:29:55 +00001007 contents = doc and [doc + '\n'] or []
1008 push = contents.append
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001009
Tim Petersc86f6ca2001-09-26 21:31:51 +00001010 # List the mro, if non-trivial.
1011 mro = inspect.getmro(object)
1012 if len(mro) > 2:
1013 push("Method resolution order:")
1014 for base in mro:
1015 push(' ' + makename(base))
1016 push('')
1017
Tim Petersf4aad8e2001-09-24 22:40:47 +00001018 # Cute little class to pump out a horizontal rule between sections.
1019 class HorizontalRule:
1020 def __init__(self):
1021 self.needone = 0
1022 def maybe(self):
1023 if self.needone:
1024 push('-' * 70)
1025 self.needone = 1
1026 hr = HorizontalRule()
1027
Tim Peters28355492001-09-23 21:29:55 +00001028 def spill(msg, attrs, predicate):
Tim Petersfa26f7c2001-09-24 08:05:11 +00001029 ok, attrs = _split_list(attrs, predicate)
Tim Peters28355492001-09-23 21:29:55 +00001030 if ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001031 hr.maybe()
Tim Peters28355492001-09-23 21:29:55 +00001032 push(msg)
1033 for name, kind, homecls, value in ok:
1034 push(self.document(getattr(object, name),
1035 name, mod, object))
1036 return attrs
1037
Tim Petersfa26f7c2001-09-24 08:05:11 +00001038 def spillproperties(msg, attrs, predicate):
1039 ok, attrs = _split_list(attrs, predicate)
Tim Peters28355492001-09-23 21:29:55 +00001040 if ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001041 hr.maybe()
Tim Peters28355492001-09-23 21:29:55 +00001042 push(msg)
1043 for name, kind, homecls, value in ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001044 push(name)
1045 need_blank_after_doc = 0
1046 doc = getdoc(value) or ''
1047 if doc:
1048 push(self.indent(doc))
1049 need_blank_after_doc = 1
Tim Petersf33532c2001-09-25 06:30:51 +00001050 for attr, tag in [("fget", " getter"),
1051 ("fset", " setter"),
Tim Petersf4aad8e2001-09-24 22:40:47 +00001052 ("fdel", " deleter")]:
1053 func = getattr(value, attr)
1054 if func is not None:
1055 if need_blank_after_doc:
1056 push('')
1057 need_blank_after_doc = 0
1058 base = self.docother(func, name + tag, mod, 70)
1059 push(self.indent(base))
1060 push('')
Tim Peters28355492001-09-23 21:29:55 +00001061 return attrs
Tim Petersb47879b2001-09-24 04:47:19 +00001062
Tim Petersfa26f7c2001-09-24 08:05:11 +00001063 def spilldata(msg, attrs, predicate):
1064 ok, attrs = _split_list(attrs, predicate)
Tim Peters28355492001-09-23 21:29:55 +00001065 if ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001066 hr.maybe()
Tim Peters28355492001-09-23 21:29:55 +00001067 push(msg)
1068 for name, kind, homecls, value in ok:
1069 doc = getattr(value, "__doc__", None)
1070 push(self.docother(getattr(object, name),
1071 name, mod, 70, doc) + '\n')
1072 return attrs
1073
1074 attrs = inspect.classify_class_attrs(object)
1075
Tim Petersfa26f7c2001-09-24 08:05:11 +00001076 # Sort attrs by name of defining class.
1077 attrs.sort(lambda t1, t2: cmp(t1[2].__name__, t2[2].__name__))
Tim Peters28355492001-09-23 21:29:55 +00001078
Tim Petersfa26f7c2001-09-24 08:05:11 +00001079 thisclass = object # list attrs defined here first
1080 while attrs:
1081 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
1082
Tim Peters28355492001-09-23 21:29:55 +00001083 if thisclass is object:
1084 tag = "defined here"
1085 else:
Tim Petersfa26f7c2001-09-24 08:05:11 +00001086 tag = "inherited from %s" % classname(thisclass,
1087 object.__module__)
Tim Peters28355492001-09-23 21:29:55 +00001088
1089 # Sort attrs by name.
1090 attrs.sort(lambda t1, t2: cmp(t1[0], t2[0]))
1091
1092 # Pump out the attrs, segregated by kind.
Tim Petersf4aad8e2001-09-24 22:40:47 +00001093 attrs = spill("Methods %s:\n" % tag, attrs,
Tim Peters28355492001-09-23 21:29:55 +00001094 lambda t: t[1] == 'method')
Tim Petersf4aad8e2001-09-24 22:40:47 +00001095 attrs = spill("Class methods %s:\n" % tag, attrs,
Tim Peters28355492001-09-23 21:29:55 +00001096 lambda t: t[1] == 'class method')
Tim Petersf4aad8e2001-09-24 22:40:47 +00001097 attrs = spill("Static methods %s:\n" % tag, attrs,
Tim Peters28355492001-09-23 21:29:55 +00001098 lambda t: t[1] == 'static method')
Tim Petersf4aad8e2001-09-24 22:40:47 +00001099 attrs = spillproperties("Properties %s:\n" % tag, attrs,
Tim Petersfa26f7c2001-09-24 08:05:11 +00001100 lambda t: t[1] == 'property')
Tim Petersf33532c2001-09-25 06:30:51 +00001101 attrs = spilldata("Data and non-method functions %s:\n" % tag,
1102 attrs, lambda t: t[1] == 'data')
Tim Peters28355492001-09-23 21:29:55 +00001103 assert attrs == []
1104
1105 # Split off the attributes inherited from the next class (note
1106 # that inherited remains sorted by class name).
1107 if inherited:
1108 attrs = inherited
1109 thisclass = attrs[0][2]
Tim Peters28355492001-09-23 21:29:55 +00001110
1111 contents = '\n'.join(contents)
1112 if not contents:
1113 return title + '\n'
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001114 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
1115
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001116 def formatvalue(self, object):
1117 """Format an argument default value as text."""
1118 return '=' + self.repr(object)
1119
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001120 def docroutine(self, object, name=None, mod=None, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001121 """Produce text documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001122 realname = object.__name__
1123 name = name or realname
1124 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001125 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001126 if inspect.ismethod(object):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001127 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001128 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001129 if imclass is not cl:
1130 note = ' from ' + classname(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001131 skipdocs = 1
1132 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +00001133 if object.im_self:
1134 note = ' method of %s instance' % classname(
1135 object.im_self.__class__, mod)
1136 else:
1137 note = ' unbound %s method' % classname(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001138 object = object.im_func
1139
1140 if name == realname:
1141 title = self.bold(realname)
1142 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001143 if (cl and cl.__dict__.has_key(realname) and
1144 cl.__dict__[realname] is object):
1145 skipdocs = 1
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001146 title = self.bold(name) + ' = ' + realname
Tim Peters4bcfa312001-09-20 06:08:24 +00001147 if inspect.isfunction(object):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001148 args, varargs, varkw, defaults = inspect.getargspec(object)
1149 argspec = inspect.formatargspec(
1150 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001151 if realname == '<lambda>':
1152 title = 'lambda'
1153 argspec = argspec[1:-1] # remove parentheses
Tim Peters4bcfa312001-09-20 06:08:24 +00001154 else:
1155 argspec = '(...)'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001156 decl = title + argspec + note
1157
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001158 if skipdocs:
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001159 return decl + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001160 else:
1161 doc = getdoc(object) or ''
1162 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001163
Tim Peters28355492001-09-23 21:29:55 +00001164 def docother(self, object, name=None, mod=None, maxlen=None, doc=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001165 """Produce text documentation for a data object."""
1166 repr = self.repr(object)
1167 if maxlen:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001168 line = (name and name + ' = ' or '') + repr
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001169 chop = maxlen - len(line)
1170 if chop < 0: repr = repr[:chop] + '...'
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001171 line = (name and self.bold(name) + ' = ' or '') + repr
Tim Peters28355492001-09-23 21:29:55 +00001172 if doc is not None:
1173 line += '\n' + self.indent(str(doc))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001174 return line
1175
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001176# --------------------------------------------------------- user interfaces
1177
1178def pager(text):
1179 """The first time this is called, determine what kind of pager to use."""
1180 global pager
1181 pager = getpager()
1182 pager(text)
1183
1184def getpager():
1185 """Decide what method to use for paging through text."""
1186 if type(sys.stdout) is not types.FileType:
1187 return plainpager
1188 if not sys.stdin.isatty() or not sys.stdout.isatty():
1189 return plainpager
Fred Drake0a66fcb2001-07-23 19:44:30 +00001190 if os.environ.get('TERM') in ['dumb', 'emacs']:
Fred Drake5e9eb982001-07-23 19:48:10 +00001191 return plainpager
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001192 if os.environ.has_key('PAGER'):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001193 if sys.platform == 'win32': # pipes completely broken in Windows
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001194 return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
1195 elif os.environ.get('TERM') in ['dumb', 'emacs']:
1196 return lambda text: pipepager(plain(text), os.environ['PAGER'])
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001197 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001198 return lambda text: pipepager(text, os.environ['PAGER'])
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001199 if sys.platform == 'win32':
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001200 return lambda text: tempfilepager(plain(text), 'more <')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001201 if hasattr(os, 'system') and os.system('less 2>/dev/null') == 0:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001202 return lambda text: pipepager(text, 'less')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001203
1204 import tempfile
1205 filename = tempfile.mktemp()
1206 open(filename, 'w').close()
1207 try:
1208 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
1209 return lambda text: pipepager(text, 'more')
1210 else:
1211 return ttypager
1212 finally:
1213 os.unlink(filename)
1214
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001215def plain(text):
1216 """Remove boldface formatting from text."""
1217 return re.sub('.\b', '', text)
1218
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001219def pipepager(text, cmd):
1220 """Page through text by feeding it to another program."""
1221 pipe = os.popen(cmd, 'w')
1222 try:
1223 pipe.write(text)
1224 pipe.close()
1225 except IOError:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001226 pass # Ignore broken pipes caused by quitting the pager program.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001227
1228def tempfilepager(text, cmd):
1229 """Page through text by invoking a program on a temporary file."""
1230 import tempfile
1231 filename = tempfile.mktemp()
1232 file = open(filename, 'w')
1233 file.write(text)
1234 file.close()
1235 try:
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001236 os.system(cmd + ' ' + filename)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001237 finally:
1238 os.unlink(filename)
1239
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001240def ttypager(text):
1241 """Page through text on a text terminal."""
1242 lines = split(plain(text), '\n')
1243 try:
1244 import tty
1245 fd = sys.stdin.fileno()
1246 old = tty.tcgetattr(fd)
1247 tty.setcbreak(fd)
1248 getchar = lambda: sys.stdin.read(1)
Ka-Ping Yee457aab22001-02-27 23:36:29 +00001249 except (ImportError, AttributeError):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001250 tty = None
1251 getchar = lambda: sys.stdin.readline()[:-1][:1]
1252
1253 try:
1254 r = inc = os.environ.get('LINES', 25) - 1
1255 sys.stdout.write(join(lines[:inc], '\n') + '\n')
1256 while lines[r:]:
1257 sys.stdout.write('-- more --')
1258 sys.stdout.flush()
1259 c = getchar()
1260
1261 if c in ['q', 'Q']:
1262 sys.stdout.write('\r \r')
1263 break
1264 elif c in ['\r', '\n']:
1265 sys.stdout.write('\r \r' + lines[r] + '\n')
1266 r = r + 1
1267 continue
1268 if c in ['b', 'B', '\x1b']:
1269 r = r - inc - inc
1270 if r < 0: r = 0
1271 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1272 r = r + inc
1273
1274 finally:
1275 if tty:
1276 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1277
1278def plainpager(text):
1279 """Simply print unformatted text. This is the ultimate fallback."""
1280 sys.stdout.write(plain(text))
1281
1282def describe(thing):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001283 """Produce a short description of the given thing."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001284 if inspect.ismodule(thing):
1285 if thing.__name__ in sys.builtin_module_names:
1286 return 'built-in module ' + thing.__name__
1287 if hasattr(thing, '__path__'):
1288 return 'package ' + thing.__name__
1289 else:
1290 return 'module ' + thing.__name__
1291 if inspect.isbuiltin(thing):
1292 return 'built-in function ' + thing.__name__
1293 if inspect.isclass(thing):
1294 return 'class ' + thing.__name__
1295 if inspect.isfunction(thing):
1296 return 'function ' + thing.__name__
1297 if inspect.ismethod(thing):
1298 return 'method ' + thing.__name__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001299 if type(thing) is types.InstanceType:
1300 return 'instance of ' + thing.__class__.__name__
1301 return type(thing).__name__
1302
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001303def locate(path, forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001304 """Locate an object by name or dotted path, importing as necessary."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001305 parts = split(path, '.')
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001306 module, n = None, 0
1307 while n < len(parts):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001308 nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001309 if nextmodule: module, n = nextmodule, n + 1
1310 else: break
1311 if module:
1312 object = module
1313 for part in parts[n:]:
1314 try: object = getattr(object, part)
1315 except AttributeError: return None
1316 return object
1317 else:
1318 import __builtin__
1319 if hasattr(__builtin__, path):
1320 return getattr(__builtin__, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001321
1322# --------------------------------------- interactive interpreter interface
1323
1324text = TextDoc()
1325html = HTMLDoc()
1326
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001327def doc(thing, title='Python Library Documentation: %s', forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001328 """Display text documentation, given an object or a path to an object."""
1329 suffix, name = '', None
1330 if type(thing) is type(''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001331 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001332 object = locate(thing, forceload)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001333 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001334 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001335 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001336 if not object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001337 print 'no Python documentation found for %s' % repr(thing)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001338 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001339 parts = split(thing, '.')
1340 if len(parts) > 1: suffix = ' in ' + join(parts[:-1], '.')
1341 name = parts[-1]
1342 thing = object
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001343
1344 desc = describe(thing)
1345 module = inspect.getmodule(thing)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001346 if not suffix and module and module is not thing:
1347 suffix = ' in module ' + module.__name__
Ka-Ping Yee66246962001-04-12 11:59:50 +00001348 pager(title % (desc + suffix) + '\n\n' + text.document(thing, name))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001349
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001350def writedoc(key, forceload=0):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001351 """Write HTML documentation to a file in the current directory."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001352 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001353 object = locate(key, forceload)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001354 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001355 print value
1356 else:
1357 if object:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001358 page = html.page(describe(object),
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001359 html.document(object, object.__name__))
1360 file = open(key + '.html', 'w')
1361 file.write(page)
1362 file.close()
1363 print 'wrote', key + '.html'
1364 else:
1365 print 'no Python documentation found for %s' % repr(key)
1366
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001367def writedocs(dir, pkgpath='', done=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001368 """Write out HTML documentation for all modules in a directory tree."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001369 if done is None: done = {}
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001370 for file in os.listdir(dir):
1371 path = os.path.join(dir, file)
1372 if ispackage(path):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001373 writedocs(path, pkgpath + file + '.', done)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001374 elif os.path.isfile(path):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001375 modname = inspect.getmodulename(path)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001376 if modname:
1377 modname = pkgpath + modname
1378 if not done.has_key(modname):
1379 done[modname] = 1
1380 writedoc(modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001381
1382class Helper:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001383 keywords = {
1384 'and': 'BOOLEAN',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001385 'assert': ('ref/assert', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001386 'break': ('ref/break', 'while for'),
1387 'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1388 'continue': ('ref/continue', 'while for'),
1389 'def': ('ref/function', ''),
1390 'del': ('ref/del', 'BASICMETHODS'),
1391 'elif': 'if',
1392 'else': ('ref/if', 'while for'),
1393 'except': 'try',
1394 'exec': ('ref/exec', ''),
1395 'finally': 'try',
1396 'for': ('ref/for', 'break continue while'),
1397 'from': 'import',
1398 'global': ('ref/global', 'NAMESPACES'),
1399 'if': ('ref/if', 'TRUTHVALUE'),
1400 'import': ('ref/import', 'MODULES'),
1401 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1402 'is': 'COMPARISON',
1403 'lambda': ('ref/lambda', 'FUNCTIONS'),
1404 'not': 'BOOLEAN',
1405 'or': 'BOOLEAN',
1406 'pass': 'PASS',
1407 'print': ('ref/print', ''),
1408 'raise': ('ref/raise', 'EXCEPTIONS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001409 'return': ('ref/return', 'FUNCTIONS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001410 'try': ('ref/try', 'EXCEPTIONS'),
1411 'while': ('ref/while', 'break continue if TRUTHVALUE'),
1412 }
1413
1414 topics = {
1415 'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001416 'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001417 'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'),
1418 'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001419 'UNICODE': ('ref/unicode', 'encodings unicode TYPES STRING'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001420 'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1421 'INTEGER': ('ref/integers', 'int range'),
1422 'FLOAT': ('ref/floating', 'float math'),
1423 'COMPLEX': ('ref/imaginary', 'complex cmath'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001424 'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001425 'MAPPINGS': 'DICTIONARIES',
1426 'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'),
1427 'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'),
1428 'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001429 'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001430 'FRAMEOBJECTS': 'TYPES',
1431 'TRACEBACKS': 'TYPES',
1432 'NONE': ('lib/bltin-null-object', ''),
1433 'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'),
1434 'FILES': ('lib/bltin-file-objects', ''),
1435 'SPECIALATTRIBUTES': ('lib/specialattrs', ''),
1436 'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'),
1437 'MODULES': ('lib/typesmodules', 'import'),
1438 'PACKAGES': 'import',
1439 'EXPRESSIONS': ('ref/summary', 'lambda or and not in is BOOLEAN COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES LISTS DICTIONARIES BACKQUOTES'),
1440 'OPERATORS': 'EXPRESSIONS',
1441 'PRECEDENCE': 'EXPRESSIONS',
1442 'OBJECTS': ('ref/objects', 'TYPES'),
1443 'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001444 'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'),
1445 'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1446 'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'),
1447 'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'),
1448 'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'),
1449 'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'),
1450 'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001451 'EXECUTION': ('ref/execframes', ''),
1452 'NAMESPACES': ('ref/execframes', 'global ASSIGNMENT DELETION'),
1453 'SCOPING': 'NAMESPACES',
1454 'FRAMES': 'NAMESPACES',
1455 'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'),
1456 'COERCIONS': 'CONVERSIONS',
1457 'CONVERSIONS': ('ref/conversions', ''),
1458 'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'),
1459 'SPECIALIDENTIFIERS': ('ref/id-classes', ''),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001460 'PRIVATENAMES': ('ref/atom-identifiers', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001461 'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1462 'TUPLES': 'SEQUENCES',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001463 'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001464 'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001465 'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001466 'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001467 'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'),
1468 'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001469 'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1470 'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'),
1471 'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'),
1472 'CALLS': ('ref/calls', 'EXPRESSIONS'),
1473 'POWER': ('ref/power', 'EXPRESSIONS'),
1474 'UNARY': ('ref/unary', 'EXPRESSIONS'),
1475 'BINARY': ('ref/binary', 'EXPRESSIONS'),
1476 'SHIFTING': ('ref/shifting', 'EXPRESSIONS'),
1477 'BITWISE': ('ref/bitwise', 'EXPRESSIONS'),
1478 'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001479 'BOOLEAN': ('ref/lambda', 'EXPRESSIONS TRUTHVALUE'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001480 'ASSERTION': 'assert',
1481 'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001482 'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001483 'DELETION': 'del',
1484 'PRINTING': 'print',
1485 'RETURNING': 'return',
1486 'IMPORTING': 'import',
1487 'CONDITIONAL': 'if',
1488 'LOOPING': ('ref/compound', 'for while break continue'),
1489 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001490 'DEBUGGING': ('lib/module-pdb', 'pdb'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001491 }
1492
1493 def __init__(self, input, output):
1494 self.input = input
1495 self.output = output
1496 self.docdir = None
1497 execdir = os.path.dirname(sys.executable)
1498 homedir = os.environ.get('PYTHONHOME')
1499 for dir in [os.environ.get('PYTHONDOCS'),
1500 homedir and os.path.join(homedir, 'doc'),
1501 os.path.join(execdir, 'doc'),
1502 '/usr/doc/python-docs-' + split(sys.version)[0],
1503 '/usr/doc/python-' + split(sys.version)[0],
1504 '/usr/doc/python-docs-' + sys.version[:3],
1505 '/usr/doc/python-' + sys.version[:3]]:
1506 if dir and os.path.isdir(os.path.join(dir, 'lib')):
1507 self.docdir = dir
1508
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001509 def __repr__(self):
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001510 if inspect.stack()[1][3] == '?':
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001511 self()
1512 return ''
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001513 return '<pydoc.Helper instance>'
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001514
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001515 def __call__(self, request=None):
1516 if request is not None:
1517 self.help(request)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001518 else:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001519 self.intro()
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001520 self.interact()
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001521 self.output.write('''
Fred Drakee61967f2001-05-10 18:41:02 +00001522You are now leaving help and returning to the Python interpreter.
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001523If you want to ask for help on a particular object directly from the
1524interpreter, you can type "help(object)". Executing "help('string')"
1525has the same effect as typing a particular string at the help> prompt.
1526''')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001527
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001528 def interact(self):
1529 self.output.write('\n')
1530 while 1:
1531 self.output.write('help> ')
1532 self.output.flush()
1533 try:
1534 request = self.input.readline()
1535 if not request: break
1536 except KeyboardInterrupt: break
1537 request = strip(replace(request, '"', '', "'", ''))
1538 if lower(request) in ['q', 'quit']: break
1539 self.help(request)
1540
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001541 def help(self, request):
1542 if type(request) is type(''):
1543 if request == 'help': self.intro()
1544 elif request == 'keywords': self.listkeywords()
1545 elif request == 'topics': self.listtopics()
1546 elif request == 'modules': self.listmodules()
1547 elif request[:8] == 'modules ':
1548 self.listmodules(split(request)[1])
1549 elif self.keywords.has_key(request): self.showtopic(request)
1550 elif self.topics.has_key(request): self.showtopic(request)
1551 elif request: doc(request, 'Help on %s:')
1552 elif isinstance(request, Helper): self()
1553 else: doc(request, 'Help on %s:')
1554 self.output.write('\n')
1555
1556 def intro(self):
1557 self.output.write('''
1558Welcome to Python %s! This is the online help utility.
1559
1560If this is your first time using Python, you should definitely check out
1561the tutorial on the Internet at http://www.python.org/doc/tut/.
1562
1563Enter the name of any module, keyword, or topic to get help on writing
1564Python programs and using Python modules. To quit this help utility and
1565return to the interpreter, just type "quit".
1566
1567To get a list of available modules, keywords, or topics, type "modules",
1568"keywords", or "topics". Each module also comes with a one-line summary
1569of what it does; to list the modules whose summaries contain a given word
1570such as "spam", type "modules spam".
1571''' % sys.version[:3])
1572
1573 def list(self, items, columns=4, width=80):
1574 items = items[:]
1575 items.sort()
1576 colw = width / columns
1577 rows = (len(items) + columns - 1) / columns
1578 for row in range(rows):
1579 for col in range(columns):
1580 i = col * rows + row
1581 if i < len(items):
1582 self.output.write(items[i])
1583 if col < columns - 1:
1584 self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1585 self.output.write('\n')
1586
1587 def listkeywords(self):
1588 self.output.write('''
1589Here is a list of the Python keywords. Enter any keyword to get more help.
1590
1591''')
1592 self.list(self.keywords.keys())
1593
1594 def listtopics(self):
1595 self.output.write('''
1596Here is a list of available topics. Enter any topic name to get more help.
1597
1598''')
1599 self.list(self.topics.keys())
1600
1601 def showtopic(self, topic):
1602 if not self.docdir:
1603 self.output.write('''
1604Sorry, topic and keyword documentation is not available because the Python
1605HTML documentation files could not be found. If you have installed them,
1606please set the environment variable PYTHONDOCS to indicate their location.
1607''')
1608 return
1609 target = self.topics.get(topic, self.keywords.get(topic))
1610 if not target:
1611 self.output.write('no documentation found for %s\n' % repr(topic))
1612 return
1613 if type(target) is type(''):
1614 return self.showtopic(target)
1615
1616 filename, xrefs = target
1617 filename = self.docdir + '/' + filename + '.html'
1618 try:
1619 file = open(filename)
1620 except:
1621 self.output.write('could not read docs from %s\n' % filename)
1622 return
1623
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001624 divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
1625 addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001626 document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1627 file.close()
1628
1629 import htmllib, formatter, StringIO
1630 buffer = StringIO.StringIO()
1631 parser = htmllib.HTMLParser(
1632 formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1633 parser.start_table = parser.do_p
1634 parser.end_table = lambda parser=parser: parser.do_p({})
1635 parser.start_tr = parser.do_br
1636 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1637 parser.feed(document)
1638 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
1639 pager(' ' + strip(buffer) + '\n')
Ka-Ping Yeeda793892001-04-13 11:02:51 +00001640 if xrefs:
1641 buffer = StringIO.StringIO()
1642 formatter.DumbWriter(buffer).send_flowing_data(
1643 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1644 self.output.write('\n%s\n' % buffer.getvalue())
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001645
1646 def listmodules(self, key=''):
1647 if key:
1648 self.output.write('''
1649Here is a list of matching modules. Enter any module name to get more help.
1650
1651''')
1652 apropos(key)
1653 else:
1654 self.output.write('''
1655Please wait a moment while I gather a list of all available modules...
1656
1657''')
1658 modules = {}
1659 def callback(path, modname, desc, modules=modules):
1660 if modname and modname[-9:] == '.__init__':
1661 modname = modname[:-9] + ' (package)'
1662 if find(modname, '.') < 0:
1663 modules[modname] = 1
1664 ModuleScanner().run(callback)
1665 self.list(modules.keys())
1666 self.output.write('''
1667Enter any module name to get more help. Or, type "modules spam" to search
1668for modules whose descriptions contain the word "spam".
1669''')
1670
1671help = Helper(sys.stdin, sys.stdout)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001672
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001673class Scanner:
1674 """A generic tree iterator."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001675 def __init__(self, roots, children, descendp):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001676 self.roots = roots[:]
1677 self.state = []
1678 self.children = children
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001679 self.descendp = descendp
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001680
1681 def next(self):
1682 if not self.state:
1683 if not self.roots:
1684 return None
1685 root = self.roots.pop(0)
1686 self.state = [(root, self.children(root))]
1687 node, children = self.state[-1]
1688 if not children:
1689 self.state.pop()
1690 return self.next()
1691 child = children.pop(0)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001692 if self.descendp(child):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001693 self.state.append((child, self.children(child)))
1694 return child
1695
1696class ModuleScanner(Scanner):
1697 """An interruptible scanner that searches module synopses."""
1698 def __init__(self):
1699 roots = map(lambda dir: (dir, ''), pathdirs())
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001700 Scanner.__init__(self, roots, self.submodules, self.isnewpackage)
1701 self.inodes = map(lambda (dir, pkg): os.stat(dir)[1], roots)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001702
1703 def submodules(self, (dir, package)):
1704 children = []
1705 for file in os.listdir(dir):
1706 path = os.path.join(dir, file)
Tim Peters30edd232001-03-16 08:29:48 +00001707 if ispackage(path):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001708 children.append((path, package + (package and '.') + file))
1709 else:
1710 children.append((path, package))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001711 children.sort() # so that spam.py comes before spam.pyc or spam.pyo
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001712 return children
1713
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001714 def isnewpackage(self, (dir, package)):
Ka-Ping Yee6191a232001-04-13 15:00:27 +00001715 inode = os.path.exists(dir) and os.stat(dir)[1]
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001716 if not (os.path.islink(dir) and inode in self.inodes):
Ka-Ping Yee6191a232001-04-13 15:00:27 +00001717 self.inodes.append(inode) # detect circular symbolic links
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001718 return ispackage(dir)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001719
Ka-Ping Yee66246962001-04-12 11:59:50 +00001720 def run(self, callback, key=None, completer=None):
1721 if key: key = lower(key)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001722 self.quit = 0
1723 seen = {}
1724
1725 for modname in sys.builtin_module_names:
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001726 if modname != '__main__':
1727 seen[modname] = 1
Ka-Ping Yee66246962001-04-12 11:59:50 +00001728 if key is None:
1729 callback(None, modname, '')
1730 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001731 desc = split(__import__(modname).__doc__ or '', '\n')[0]
Ka-Ping Yee66246962001-04-12 11:59:50 +00001732 if find(lower(modname + ' - ' + desc), key) >= 0:
1733 callback(None, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001734
1735 while not self.quit:
1736 node = self.next()
1737 if not node: break
1738 path, package = node
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001739 modname = inspect.getmodulename(path)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001740 if os.path.isfile(path) and modname:
1741 modname = package + (package and '.') + modname
1742 if not seen.has_key(modname):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001743 seen[modname] = 1 # if we see spam.py, skip spam.pyc
Ka-Ping Yee66246962001-04-12 11:59:50 +00001744 if key is None:
1745 callback(path, modname, '')
1746 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001747 desc = synopsis(path) or ''
1748 if find(lower(modname + ' - ' + desc), key) >= 0:
1749 callback(path, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001750 if completer: completer()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001751
1752def apropos(key):
1753 """Print all the one-line module summaries that contain a substring."""
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001754 def callback(path, modname, desc):
1755 if modname[-9:] == '.__init__':
1756 modname = modname[:-9] + ' (package)'
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001757 print modname, desc and '- ' + desc
1758 try: import warnings
1759 except ImportError: pass
1760 else: warnings.filterwarnings('ignore') # ignore problems during import
Ka-Ping Yee66246962001-04-12 11:59:50 +00001761 ModuleScanner().run(callback, key)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001762
1763# --------------------------------------------------- web browser interface
1764
Ka-Ping Yee66246962001-04-12 11:59:50 +00001765def serve(port, callback=None, completer=None):
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001766 import BaseHTTPServer, mimetools, select
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001767
1768 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1769 class Message(mimetools.Message):
1770 def __init__(self, fp, seekable=1):
1771 Message = self.__class__
1772 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1773 self.encodingheader = self.getheader('content-transfer-encoding')
1774 self.typeheader = self.getheader('content-type')
1775 self.parsetype()
1776 self.parseplist()
1777
1778 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1779 def send_document(self, title, contents):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001780 try:
1781 self.send_response(200)
1782 self.send_header('Content-Type', 'text/html')
1783 self.end_headers()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001784 self.wfile.write(html.page(title, contents))
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001785 except IOError: pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001786
1787 def do_GET(self):
1788 path = self.path
1789 if path[-5:] == '.html': path = path[:-5]
1790 if path[:1] == '/': path = path[1:]
1791 if path and path != '.':
1792 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001793 obj = locate(path, forceload=1)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001794 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001795 self.send_document(path, html.escape(str(value)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001796 return
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001797 if obj:
1798 self.send_document(describe(obj), html.document(obj, path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001799 else:
1800 self.send_document(path,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001801'no Python documentation found for %s' % repr(path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001802 else:
1803 heading = html.heading(
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001804'<big><big><strong>Python: Index of Modules</strong></big></big>',
1805'#ffffff', '#7799ee')
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001806 def bltinlink(name):
1807 return '<a href="%s.html">%s</a>' % (name, name)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001808 names = filter(lambda x: x != '__main__',
1809 sys.builtin_module_names)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001810 contents = html.multicolumn(names, bltinlink)
1811 indices = ['<p>' + html.bigsection(
1812 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1813
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001814 seen = {}
1815 for dir in pathdirs():
1816 indices.append(html.index(dir, seen))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001817 contents = heading + join(indices) + '''<p align=right>
Tim Peters2306d242001-09-25 03:18:32 +00001818<font color="#909090" face="helvetica, arial"><strong>
1819pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font>'''
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001820 self.send_document('Index of Modules', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001821
1822 def log_message(self, *args): pass
1823
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001824 class DocServer(BaseHTTPServer.HTTPServer):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001825 def __init__(self, port, callback):
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001826 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001827 self.address = ('', port)
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001828 self.url = 'http://%s:%d/' % (host, port)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001829 self.callback = callback
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001830 self.base.__init__(self, self.address, self.handler)
1831
1832 def serve_until_quit(self):
1833 import select
1834 self.quit = 0
1835 while not self.quit:
1836 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1837 if rd: self.handle_request()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001838
1839 def server_activate(self):
1840 self.base.server_activate(self)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001841 if self.callback: self.callback(self)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001842
1843 DocServer.base = BaseHTTPServer.HTTPServer
1844 DocServer.handler = DocHandler
1845 DocHandler.MessageClass = Message
1846 try:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001847 try:
1848 DocServer(port, callback).serve_until_quit()
1849 except (KeyboardInterrupt, select.error):
1850 pass
1851 finally:
Ka-Ping Yee66246962001-04-12 11:59:50 +00001852 if completer: completer()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001853
1854# ----------------------------------------------------- graphical interface
1855
1856def gui():
1857 """Graphical interface (starts web server and pops up a control window)."""
1858 class GUI:
1859 def __init__(self, window, port=7464):
1860 self.window = window
1861 self.server = None
1862 self.scanner = None
1863
1864 import Tkinter
1865 self.server_frm = Tkinter.Frame(window)
1866 self.title_lbl = Tkinter.Label(self.server_frm,
1867 text='Starting server...\n ')
1868 self.open_btn = Tkinter.Button(self.server_frm,
1869 text='open browser', command=self.open, state='disabled')
1870 self.quit_btn = Tkinter.Button(self.server_frm,
1871 text='quit serving', command=self.quit, state='disabled')
1872
1873 self.search_frm = Tkinter.Frame(window)
1874 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
1875 self.search_ent = Tkinter.Entry(self.search_frm)
1876 self.search_ent.bind('<Return>', self.search)
1877 self.stop_btn = Tkinter.Button(self.search_frm,
1878 text='stop', pady=0, command=self.stop, state='disabled')
1879 if sys.platform == 'win32':
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001880 # Trying to hide and show this button crashes under Windows.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001881 self.stop_btn.pack(side='right')
1882
1883 self.window.title('pydoc')
1884 self.window.protocol('WM_DELETE_WINDOW', self.quit)
1885 self.title_lbl.pack(side='top', fill='x')
1886 self.open_btn.pack(side='left', fill='x', expand=1)
1887 self.quit_btn.pack(side='right', fill='x', expand=1)
1888 self.server_frm.pack(side='top', fill='x')
1889
1890 self.search_lbl.pack(side='left')
1891 self.search_ent.pack(side='right', fill='x', expand=1)
1892 self.search_frm.pack(side='top', fill='x')
1893 self.search_ent.focus_set()
1894
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001895 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001896 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001897 self.result_lst.bind('<Button-1>', self.select)
1898 self.result_lst.bind('<Double-Button-1>', self.goto)
1899 self.result_scr = Tkinter.Scrollbar(window,
1900 orient='vertical', command=self.result_lst.yview)
1901 self.result_lst.config(yscrollcommand=self.result_scr.set)
1902
1903 self.result_frm = Tkinter.Frame(window)
1904 self.goto_btn = Tkinter.Button(self.result_frm,
1905 text='go to selected', command=self.goto)
1906 self.hide_btn = Tkinter.Button(self.result_frm,
1907 text='hide results', command=self.hide)
1908 self.goto_btn.pack(side='left', fill='x', expand=1)
1909 self.hide_btn.pack(side='right', fill='x', expand=1)
1910
1911 self.window.update()
1912 self.minwidth = self.window.winfo_width()
1913 self.minheight = self.window.winfo_height()
1914 self.bigminheight = (self.server_frm.winfo_reqheight() +
1915 self.search_frm.winfo_reqheight() +
1916 self.result_lst.winfo_reqheight() +
1917 self.result_frm.winfo_reqheight())
1918 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
1919 self.expanded = 0
1920 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1921 self.window.wm_minsize(self.minwidth, self.minheight)
1922
1923 import threading
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001924 threading.Thread(
1925 target=serve, args=(port, self.ready, self.quit)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001926
1927 def ready(self, server):
1928 self.server = server
1929 self.title_lbl.config(
1930 text='Python documentation server at\n' + server.url)
1931 self.open_btn.config(state='normal')
1932 self.quit_btn.config(state='normal')
1933
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001934 def open(self, event=None, url=None):
1935 url = url or self.server.url
1936 try:
1937 import webbrowser
1938 webbrowser.open(url)
1939 except ImportError: # pre-webbrowser.py compatibility
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001940 if sys.platform == 'win32':
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001941 os.system('start "%s"' % url)
1942 elif sys.platform == 'mac':
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001943 try: import ic
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001944 except ImportError: pass
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001945 else: ic.launchurl(url)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001946 else:
1947 rc = os.system('netscape -remote "openURL(%s)" &' % url)
1948 if rc: os.system('netscape "%s" &' % url)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001949
1950 def quit(self, event=None):
1951 if self.server:
1952 self.server.quit = 1
1953 self.window.quit()
1954
1955 def search(self, event=None):
1956 key = self.search_ent.get()
1957 self.stop_btn.pack(side='right')
1958 self.stop_btn.config(state='normal')
1959 self.search_lbl.config(text='Searching for "%s"...' % key)
1960 self.search_ent.forget()
1961 self.search_lbl.pack(side='left')
1962 self.result_lst.delete(0, 'end')
1963 self.goto_btn.config(state='disabled')
1964 self.expand()
1965
1966 import threading
1967 if self.scanner:
1968 self.scanner.quit = 1
1969 self.scanner = ModuleScanner()
1970 threading.Thread(target=self.scanner.run,
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +00001971 args=(self.update, key, self.done)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001972
1973 def update(self, path, modname, desc):
1974 if modname[-9:] == '.__init__':
1975 modname = modname[:-9] + ' (package)'
1976 self.result_lst.insert('end',
1977 modname + ' - ' + (desc or '(no description)'))
1978
1979 def stop(self, event=None):
1980 if self.scanner:
1981 self.scanner.quit = 1
1982 self.scanner = None
1983
1984 def done(self):
1985 self.scanner = None
1986 self.search_lbl.config(text='Search for')
1987 self.search_lbl.pack(side='left')
1988 self.search_ent.pack(side='right', fill='x', expand=1)
1989 if sys.platform != 'win32': self.stop_btn.forget()
1990 self.stop_btn.config(state='disabled')
1991
1992 def select(self, event=None):
1993 self.goto_btn.config(state='normal')
1994
1995 def goto(self, event=None):
1996 selection = self.result_lst.curselection()
1997 if selection:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001998 modname = split(self.result_lst.get(selection[0]))[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001999 self.open(url=self.server.url + modname + '.html')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002000
2001 def collapse(self):
2002 if not self.expanded: return
2003 self.result_frm.forget()
2004 self.result_scr.forget()
2005 self.result_lst.forget()
2006 self.bigwidth = self.window.winfo_width()
2007 self.bigheight = self.window.winfo_height()
2008 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
2009 self.window.wm_minsize(self.minwidth, self.minheight)
2010 self.expanded = 0
2011
2012 def expand(self):
2013 if self.expanded: return
2014 self.result_frm.pack(side='bottom', fill='x')
2015 self.result_scr.pack(side='right', fill='y')
2016 self.result_lst.pack(side='top', fill='both', expand=1)
2017 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
2018 self.window.wm_minsize(self.minwidth, self.bigminheight)
2019 self.expanded = 1
2020
2021 def hide(self, event=None):
2022 self.stop()
2023 self.collapse()
2024
2025 import Tkinter
2026 try:
2027 gui = GUI(Tkinter.Tk())
2028 Tkinter.mainloop()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002029 except KeyboardInterrupt:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002030 pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002031
2032# -------------------------------------------------- command-line interface
2033
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002034def ispath(x):
2035 return type(x) is types.StringType and find(x, os.sep) >= 0
2036
Ka-Ping Yee1d384632001-03-01 00:24:32 +00002037def cli():
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002038 """Command-line interface (looks at sys.argv to decide what to do)."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002039 import getopt
2040 class BadUsage: pass
2041
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002042 # Scripts don't get the current directory in their path by default.
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00002043 scriptdir = os.path.dirname(sys.argv[0])
2044 if scriptdir in sys.path:
2045 sys.path.remove(scriptdir)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002046 sys.path.insert(0, '.')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002047
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002048 try:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002049 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002050 writing = 0
2051
2052 for opt, val in opts:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002053 if opt == '-g':
2054 gui()
2055 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002056 if opt == '-k':
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002057 apropos(val)
2058 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002059 if opt == '-p':
2060 try:
2061 port = int(val)
2062 except ValueError:
2063 raise BadUsage
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002064 def ready(server):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00002065 print 'pydoc server ready at %s' % server.url
2066 def stopped():
2067 print 'pydoc server stopped'
2068 serve(port, ready, stopped)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002069 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002070 if opt == '-w':
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002071 writing = 1
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002072
2073 if not args: raise BadUsage
2074 for arg in args:
2075 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002076 if ispath(arg) and os.path.isfile(arg):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002077 arg = importfile(arg)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002078 if writing:
2079 if ispath(arg) and os.path.isdir(arg):
2080 writedocs(arg)
2081 else:
2082 writedoc(arg)
2083 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00002084 doc(arg)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002085 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002086 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002087
2088 except (getopt.error, BadUsage):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002089 cmd = sys.argv[0]
2090 print """pydoc - the Python documentation tool
2091
2092%s <name> ...
2093 Show text documentation on something. <name> may be the name of a
2094 function, module, or package, or a dotted reference to a class or
2095 function within a module or module in a package. If <name> contains
2096 a '%s', it is used as the path to a Python source file to document.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002097
2098%s -k <keyword>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002099 Search for a keyword in the synopsis lines of all available modules.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002100
2101%s -p <port>
2102 Start an HTTP server on the given port on the local machine.
2103
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002104%s -g
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00002105 Pop up a graphical interface for finding and serving documentation.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002106
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002107%s -w <name> ...
2108 Write out the HTML documentation for a module to a file in the current
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00002109 directory. If <name> contains a '%s', it is treated as a filename; if
2110 it names a directory, documentation is written for all the contents.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002111""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
Ka-Ping Yee1d384632001-03-01 00:24:32 +00002112
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002113if __name__ == '__main__': cli()