blob: a99fdb48ffe8d835d7527614ee35f25fe110ec1c [file] [log] [blame]
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001#!/usr/bin/env python
Ka-Ping Yee1d384632001-03-01 00:24:32 +00002"""Generate Python documentation in HTML or text for interactive use.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00003
Ka-Ping Yeedd175342001-02-27 14:43:46 +00004In the Python interpreter, do "from pydoc import help" to provide online
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00005help. Calling help(thing) on a Python object documents the object.
6
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00007Or, at the shell command line outside of Python:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00008
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00009Run "pydoc <name>" to show documentation on something. <name> may be
10the name of a function, module, package, or a dotted reference to a
11class or function within a module or module in a package. If the
12argument contains a path segment delimiter (e.g. slash on Unix,
13backslash on Windows) it is treated as the path to a Python source file.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000014
Ka-Ping Yee37f7b382001-03-23 00:12:53 +000015Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines
16of all available modules.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000017
Ka-Ping Yee37f7b382001-03-23 00:12:53 +000018Run "pydoc -p <port>" to start an HTTP server on a given port on the
19local machine to generate documentation web pages.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000020
Ka-Ping Yee37f7b382001-03-23 00:12:53 +000021For platforms without a command line, "pydoc -g" starts the HTTP server
22and also pops up a little window for controlling it.
23
24Run "pydoc -w <name>" to write out the HTML documentation for a module
25to a file named "<name>.html".
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000026"""
Ka-Ping Yeedd175342001-02-27 14:43:46 +000027
28__author__ = "Ka-Ping Yee <ping@lfw.org>"
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +000029__date__ = "26 February 2001"
Ka-Ping Yee09d7d9a2001-02-27 22:43:48 +000030__version__ = "$Revision$"
Ka-Ping Yee5e2b1732001-02-27 23:35:09 +000031__credits__ = """Guido van Rossum, for an excellent programming language.
32Tommy Burnette, the original creator of manpy.
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +000033Paul Prescod, for all his work on onlinehelp.
34Richard Chamberlain, for the first implementation of textdoc.
35
Ka-Ping Yee5e2b1732001-02-27 23:35:09 +000036Mynd you, møøse bites Kan be pretty nasti..."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +000037
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000038# Note: this module is designed to deploy instantly and run under any
39# version of Python from 1.5 and up. That's why it's a single file and
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +000040# some 2.0 features (like string methods) are conspicuously absent.
41
42# Known bugs that can't be fixed here:
43# - imp.load_module() cannot be prevented from clobbering existing
44# loaded modules, so calling synopsis() on a binary module file
45# changes the contents of any existing module with the same name.
46# - If the __file__ attribute on a module is a relative path and
47# the current directory is changed with os.chdir(), an incorrect
48# path will be displayed.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000049
Ka-Ping Yeedd175342001-02-27 14:43:46 +000050import sys, imp, os, stat, re, types, inspect
51from repr import Repr
Ka-Ping Yee3bda8792001-03-23 13:17:50 +000052from string import expandtabs, find, join, lower, split, strip, rfind, rstrip
Ka-Ping Yeedd175342001-02-27 14:43:46 +000053
54# --------------------------------------------------------- common routines
55
Ka-Ping Yeedd175342001-02-27 14:43:46 +000056def pathdirs():
57 """Convert sys.path into a list of absolute, existing, unique paths."""
58 dirs = []
Ka-Ping Yee1d384632001-03-01 00:24:32 +000059 normdirs = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +000060 for dir in sys.path:
61 dir = os.path.abspath(dir or '.')
Ka-Ping Yee1d384632001-03-01 00:24:32 +000062 normdir = os.path.normcase(dir)
63 if normdir not in normdirs and os.path.isdir(dir):
Ka-Ping Yeedd175342001-02-27 14:43:46 +000064 dirs.append(dir)
Ka-Ping Yee1d384632001-03-01 00:24:32 +000065 normdirs.append(normdir)
Ka-Ping Yeedd175342001-02-27 14:43:46 +000066 return dirs
67
68def getdoc(object):
69 """Get the doc string or comments for an object."""
Ka-Ping Yee3bda8792001-03-23 13:17:50 +000070 result = inspect.getdoc(object) or inspect.getcomments(object)
Ka-Ping Yee239432a2001-03-02 02:45:08 +000071 return result and re.sub('^ *\n', '', rstrip(result)) or ''
Ka-Ping Yeedd175342001-02-27 14:43:46 +000072
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +000073def splitdoc(doc):
74 """Split a doc string into a synopsis line (if any) and the rest."""
75 lines = split(strip(doc), '\n')
76 if len(lines) == 1:
77 return lines[0], ''
78 elif len(lines) >= 2 and not rstrip(lines[1]):
79 return lines[0], join(lines[2:], '\n')
80 return '', join(lines, '\n')
81
Ka-Ping Yeedd175342001-02-27 14:43:46 +000082def classname(object, modname):
83 """Get a class name and qualify it with a module name if necessary."""
84 name = object.__name__
85 if object.__module__ != modname:
86 name = object.__module__ + '.' + name
87 return name
88
Ka-Ping Yeedec96e92001-04-13 09:55:49 +000089def isdata(object):
90 """Check if an object is of a type that probably means it's data."""
91 return not (inspect.ismodule(object) or inspect.isclass(object) or
92 inspect.isroutine(object) or inspect.isframe(object) or
93 inspect.istraceback(object) or inspect.iscode(object))
Ka-Ping Yeedd175342001-02-27 14:43:46 +000094
95def replace(text, *pairs):
96 """Do a series of global replacements on a string."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +000097 while pairs:
98 text = join(split(text, pairs[0]), pairs[1])
99 pairs = pairs[2:]
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000100 return text
101
102def cram(text, maxlen):
103 """Omit part of a string if needed to make it fit in a maximum length."""
104 if len(text) > maxlen:
105 pre = max(0, (maxlen-3)/2)
106 post = max(0, maxlen-3-pre)
107 return text[:pre] + '...' + text[len(text)-post:]
108 return text
109
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000110def stripid(text):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000111 """Remove the hexadecimal id from a Python object representation."""
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000112 # The behaviour of %p is implementation-dependent; we check two cases.
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000113 for pattern in [' at 0x[0-9a-f]{6,}>$', ' at [0-9A-F]{8,}>$']:
114 if re.search(pattern, repr(Exception)):
115 return re.sub(pattern, '>', text)
116 return text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000117
Tim Peters536d2262001-09-20 05:13:38 +0000118def _is_some_method(object):
119 return inspect.ismethod(object) or inspect.ismethoddescriptor(object)
120
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000121def allmethods(cl):
122 methods = {}
Tim Peters536d2262001-09-20 05:13:38 +0000123 for key, value in inspect.getmembers(cl, _is_some_method):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000124 methods[key] = 1
125 for base in cl.__bases__:
126 methods.update(allmethods(base)) # all your base are belong to us
127 for key in methods.keys():
128 methods[key] = getattr(cl, key)
129 return methods
130
Tim Petersfa26f7c2001-09-24 08:05:11 +0000131def _split_list(s, predicate):
132 """Split sequence s via predicate, and return pair ([true], [false]).
133
134 The return value is a 2-tuple of lists,
135 ([x for x in s if predicate(x)],
136 [x for x in s if not predicate(x)])
137 """
138
Tim Peters28355492001-09-23 21:29:55 +0000139 yes = []
140 no = []
Tim Petersfa26f7c2001-09-24 08:05:11 +0000141 for x in s:
142 if predicate(x):
143 yes.append(x)
Tim Peters28355492001-09-23 21:29:55 +0000144 else:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000145 no.append(x)
Tim Peters28355492001-09-23 21:29:55 +0000146 return yes, no
147
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000148# ----------------------------------------------------- module manipulation
149
150def ispackage(path):
151 """Guess whether a path refers to a package directory."""
152 if os.path.isdir(path):
153 for ext in ['.py', '.pyc', '.pyo']:
154 if os.path.isfile(os.path.join(path, '__init__' + ext)):
155 return 1
156
157def synopsis(filename, cache={}):
158 """Get the one-line summary out of a module file."""
159 mtime = os.stat(filename)[stat.ST_MTIME]
160 lastupdate, result = cache.get(filename, (0, None))
161 if lastupdate < mtime:
162 info = inspect.getmoduleinfo(filename)
163 file = open(filename)
164 if info and 'b' in info[2]: # binary modules have to be imported
165 try: module = imp.load_module('__temp__', file, filename, info[1:])
166 except: return None
167 result = split(module.__doc__ or '', '\n')[0]
168 del sys.modules['__temp__']
169 else: # text modules can be directly examined
170 line = file.readline()
171 while line[:1] == '#' or not strip(line):
172 line = file.readline()
173 if not line: break
174 line = strip(line)
175 if line[:4] == 'r"""': line = line[1:]
176 if line[:3] == '"""':
177 line = line[3:]
178 if line[-1:] == '\\': line = line[:-1]
179 while not strip(line):
180 line = file.readline()
181 if not line: break
182 result = strip(split(line, '"""')[0])
183 else: result = None
184 file.close()
185 cache[filename] = (mtime, result)
186 return result
187
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000188class ErrorDuringImport(Exception):
189 """Errors that occurred while trying to import something to document it."""
190 def __init__(self, filename, (exc, value, tb)):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000191 self.filename = filename
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000192 self.exc = exc
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000193 self.value = value
194 self.tb = tb
195
196 def __str__(self):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000197 exc = self.exc
198 if type(exc) is types.ClassType:
199 exc = exc.__name__
200 return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000201
202def importfile(path):
203 """Import a Python source file or compiled file given its path."""
204 magic = imp.get_magic()
205 file = open(path, 'r')
206 if file.read(len(magic)) == magic:
207 kind = imp.PY_COMPILED
208 else:
209 kind = imp.PY_SOURCE
210 file.close()
211 filename = os.path.basename(path)
212 name, ext = os.path.splitext(filename)
213 file = open(path, 'r')
214 try:
215 module = imp.load_module(name, file, path, (ext, 'r', kind))
216 except:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000217 raise ErrorDuringImport(path, sys.exc_info())
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000218 file.close()
219 return module
220
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000221def safeimport(path, forceload=0, cache={}):
222 """Import a module; handle errors; return None if the module isn't found.
223
224 If the module *is* found but an exception occurs, it's wrapped in an
225 ErrorDuringImport exception and reraised. Unlike __import__, if a
226 package path is specified, the module at the end of the path is returned,
227 not the package at the beginning. If the optional 'forceload' argument
228 is 1, we reload the module from disk (unless it's a dynamic extension)."""
229 if forceload and sys.modules.has_key(path):
230 # This is the only way to be sure. Checking the mtime of the file
231 # isn't good enough (e.g. what if the module contains a class that
232 # inherits from another module that has changed?).
233 if path not in sys.builtin_module_names:
234 # Python never loads a dynamic extension a second time from the
235 # same path, even if the file is changed or missing. Deleting
236 # the entry in sys.modules doesn't help for dynamic extensions,
237 # so we're not even going to try to keep them up to date.
238 info = inspect.getmoduleinfo(sys.modules[path].__file__)
239 if info[3] != imp.C_EXTENSION:
240 cache[path] = sys.modules[path] # prevent module from clearing
241 del sys.modules[path]
242 try:
243 module = __import__(path)
244 except:
245 # Did the error occur before or after the module was found?
246 (exc, value, tb) = info = sys.exc_info()
247 if sys.modules.has_key(path):
248 # An error occured while executing the imported module.
249 raise ErrorDuringImport(sys.modules[path].__file__, info)
250 elif exc is SyntaxError:
251 # A SyntaxError occurred before we could execute the module.
252 raise ErrorDuringImport(value.filename, info)
253 elif exc is ImportError and \
254 split(lower(str(value)))[:2] == ['no', 'module']:
255 # The module was not found.
256 return None
257 else:
258 # Some other error occurred during the importing process.
259 raise ErrorDuringImport(path, sys.exc_info())
260 for part in split(path, '.')[1:]:
261 try: module = getattr(module, part)
262 except AttributeError: return None
263 return module
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000264
265# ---------------------------------------------------- formatter base class
266
267class Doc:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000268 def document(self, object, name=None, *args):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000269 """Generate documentation for an object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000270 args = (object, name) + args
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000271 if inspect.ismodule(object): return apply(self.docmodule, args)
272 if inspect.isclass(object): return apply(self.docclass, args)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000273 if inspect.isroutine(object): return apply(self.docroutine, args)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000274 return apply(self.docother, args)
275
276 def fail(self, object, name=None, *args):
277 """Raise an exception for unimplemented types."""
278 message = "don't know how to document object%s of type %s" % (
279 name and ' ' + repr(name), type(object).__name__)
280 raise TypeError, message
281
282 docmodule = docclass = docroutine = docother = fail
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000283
284# -------------------------------------------- HTML documentation generator
285
286class HTMLRepr(Repr):
287 """Class for safely making an HTML representation of a Python object."""
288 def __init__(self):
289 Repr.__init__(self)
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000290 self.maxlist = self.maxtuple = 20
291 self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000292 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000293
294 def escape(self, text):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000295 return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000296
297 def repr(self, object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000298 return Repr.repr(self, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000299
300 def repr1(self, x, level):
301 methodname = 'repr_' + join(split(type(x).__name__), '_')
302 if hasattr(self, methodname):
303 return getattr(self, methodname)(x, level)
304 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000305 return self.escape(cram(stripid(repr(x)), self.maxother))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000306
307 def repr_string(self, x, level):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000308 test = cram(x, self.maxstring)
309 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000310 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000311 # Backslashes are only literal in the string and are never
312 # needed to make any special characters, so show a raw string.
313 return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000314 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000315 r'<font color="#c040c0">\1</font>',
316 self.escape(testrepr))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000317
318 def repr_instance(self, x, level):
319 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000320 return self.escape(cram(stripid(repr(x)), self.maxstring))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000321 except:
322 return self.escape('<%s instance>' % x.__class__.__name__)
323
324 repr_unicode = repr_string
325
326class HTMLDoc(Doc):
327 """Formatter class for HTML documentation."""
328
329 # ------------------------------------------- HTML formatting utilities
330
331 _repr_instance = HTMLRepr()
332 repr = _repr_instance.repr
333 escape = _repr_instance.escape
334
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000335 def page(self, title, contents):
336 """Format an HTML page."""
337 return '''
338<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000339<html><head><title>Python: %s</title>
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000340<style type="text/css"><!--
Ka-Ping Yeed03f8fe2001-04-13 15:04:32 +0000341TT { font-family: lucidatypewriter, lucida console, courier }
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000342--></style></head><body bgcolor="#f0f0f8">
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000343%s
344</body></html>''' % (title, contents)
345
346 def heading(self, title, fgcol, bgcol, extras=''):
347 """Format a page heading."""
348 return '''
349<table width="100%%" cellspacing=0 cellpadding=2 border=0>
350<tr bgcolor="%s">
Tim Peters2306d242001-09-25 03:18:32 +0000351<td valign=bottom>&nbsp;<br>
352<font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000353><td align=right valign=bottom
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000354><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000355 ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
356
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000357 def section(self, title, fgcol, bgcol, contents, width=10,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000358 prelude='', marginalia=None, gap='&nbsp;&nbsp;'):
359 """Format a section with a heading."""
360 if marginalia is None:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000361 marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000362 result = '''
363<p><table width="100%%" cellspacing=0 cellpadding=2 border=0>
364<tr bgcolor="%s">
Tim Peters2306d242001-09-25 03:18:32 +0000365<td colspan=3 valign=bottom>&nbsp;<br>
366<font color="%s" face="helvetica, arial">%s</font></td></tr>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000367 ''' % (bgcol, fgcol, title)
368 if prelude:
369 result = result + '''
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000370<tr bgcolor="%s"><td rowspan=2>%s</td>
371<td colspan=2>%s</td></tr>
372<tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
373 else:
374 result = result + '''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000375<tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000376
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000377 return result + '\n<td width="100%%">%s</td></tr></table>' % contents
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000378
379 def bigsection(self, title, *args):
380 """Format a section with a big heading."""
381 title = '<big><strong>%s</strong></big>' % title
382 return apply(self.section, (title,) + args)
383
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000384 def preformat(self, text):
385 """Format literal preformatted text."""
386 text = self.escape(expandtabs(text))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000387 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
388 ' ', '&nbsp;', '\n', '<br>\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000389
390 def multicolumn(self, list, format, cols=4):
391 """Format a list of items into a multi-column list."""
392 result = ''
393 rows = (len(list)+cols-1)/cols
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000394 for col in range(cols):
395 result = result + '<td width="%d%%" valign=top>' % (100/cols)
396 for i in range(rows*col, rows*col+rows):
397 if i < len(list):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000398 result = result + format(list[i]) + '<br>\n'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000399 result = result + '</td>'
400 return '<table width="100%%"><tr>%s</tr></table>' % result
401
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000402 def grey(self, text): return '<font color="#909090">%s</font>' % text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000403
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000404 def namelink(self, name, *dicts):
405 """Make a link for an identifier, given name-to-URL mappings."""
406 for dict in dicts:
407 if dict.has_key(name):
408 return '<a href="%s">%s</a>' % (dict[name], name)
409 return name
410
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000411 def classlink(self, object, modname):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000412 """Make a link for a class."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000413 name, module = object.__name__, sys.modules.get(object.__module__)
414 if hasattr(module, name) and getattr(module, name) is object:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000415 return '<a href="%s.html#%s">%s</a>' % (
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000416 module.__name__, name, classname(object, modname))
417 return classname(object, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000418
419 def modulelink(self, object):
420 """Make a link for a module."""
421 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
422
423 def modpkglink(self, (name, path, ispackage, shadowed)):
424 """Make a link for a module or package to display in an index."""
425 if shadowed:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000426 return self.grey(name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000427 if path:
428 url = '%s.%s.html' % (path, name)
429 else:
430 url = '%s.html' % name
431 if ispackage:
432 text = '<strong>%s</strong>&nbsp;(package)' % name
433 else:
434 text = name
435 return '<a href="%s">%s</a>' % (url, text)
436
437 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
438 """Mark up some plain text, given a context of symbols to look for.
439 Each context dictionary maps object names to anchor names."""
440 escape = escape or self.escape
441 results = []
442 here = 0
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000443 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
444 r'RFC[- ]?(\d+)|'
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000445 r'PEP[- ]?(\d+)|'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000446 r'(self\.)?(\w+))\b')
447 while 1:
448 match = pattern.search(text, here)
449 if not match: break
450 start, end = match.span()
451 results.append(escape(text[here:start]))
452
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000453 all, scheme, rfc, pep, selfdot, name = match.groups()
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000454 if scheme:
455 results.append('<a href="%s">%s</a>' % (all, escape(all)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000456 elif rfc:
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000457 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
458 results.append('<a href="%s">%s</a>' % (url, escape(all)))
459 elif pep:
460 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000461 results.append('<a href="%s">%s</a>' % (url, escape(all)))
462 elif text[end:end+1] == '(':
463 results.append(self.namelink(name, methods, funcs, classes))
464 elif selfdot:
465 results.append('self.<strong>%s</strong>' % name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000466 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000467 results.append(self.namelink(name, classes))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000468 here = end
469 results.append(escape(text[here:]))
470 return join(results, '')
471
472 # ---------------------------------------------- type-specific routines
473
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000474 def formattree(self, tree, modname, parent=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000475 """Produce HTML for a class tree as given by inspect.getclasstree()."""
476 result = ''
477 for entry in tree:
478 if type(entry) is type(()):
479 c, bases = entry
Tim Peters2306d242001-09-25 03:18:32 +0000480 result = result + '<dt><font face="helvetica, arial">'
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000481 result = result + self.classlink(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000482 if bases and bases != (parent,):
483 parents = []
484 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000485 parents.append(self.classlink(base, modname))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000486 result = result + '(' + join(parents, ', ') + ')'
Tim Peters2306d242001-09-25 03:18:32 +0000487 result = result + '\n</font></dt>'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000488 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000489 result = result + '<dd>\n%s</dd>\n' % self.formattree(
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000490 entry, modname, c)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000491 return '<dl>\n%s</dl>\n' % result
492
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000493 def docmodule(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000494 """Produce HTML documentation for a module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000495 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000496 parts = split(name, '.')
497 links = []
498 for i in range(len(parts)-1):
499 links.append(
500 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
501 (join(parts[:i+1], '.'), parts[i]))
502 linkedname = join(links + parts[-1:], '.')
503 head = '<big><big><strong>%s</strong></big></big>' % linkedname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000504 try:
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000505 path = inspect.getabsfile(object)
Ka-Ping Yee6191a232001-04-13 15:00:27 +0000506 url = path
507 if sys.platform == 'win32':
508 import nturl2path
509 url = nturl2path.pathname2url(path)
510 filelink = '<a href="file:%s">%s</a>' % (url, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000511 except TypeError:
512 filelink = '(built-in)'
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000513 info = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000514 if hasattr(object, '__version__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000515 version = str(object.__version__)
Ka-Ping Yee40c49912001-02-27 22:46:01 +0000516 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
517 version = strip(version[11:-1])
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000518 info.append('version %s' % self.escape(version))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000519 if hasattr(object, '__date__'):
520 info.append(self.escape(str(object.__date__)))
521 if info:
522 head = head + ' (%s)' % join(info, ', ')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000523 result = self.heading(
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000524 head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)
525
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000526 modules = inspect.getmembers(object, inspect.ismodule)
527
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000528 classes, cdict = [], {}
529 for key, value in inspect.getmembers(object, inspect.isclass):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000530 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000531 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000532 cdict[key] = cdict[value] = '#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000533 for key, value in classes:
534 for base in value.__bases__:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000535 key, modname = base.__name__, base.__module__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000536 module = sys.modules.get(modname)
537 if modname != name and module and hasattr(module, key):
538 if getattr(module, key) is base:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000539 if not cdict.has_key(key):
540 cdict[key] = cdict[base] = modname + '.html#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000541 funcs, fdict = [], {}
542 for key, value in inspect.getmembers(object, inspect.isroutine):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000543 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000544 funcs.append((key, value))
545 fdict[key] = '#-' + key
546 if inspect.isfunction(value): fdict[value] = fdict[key]
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000547 data = []
548 for key, value in inspect.getmembers(object, isdata):
549 if key not in ['__builtins__', '__doc__']:
550 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000551
552 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
553 doc = doc and '<tt>%s</tt>' % doc
Tim Peters2306d242001-09-25 03:18:32 +0000554 result = result + '<p>%s</p>\n' % doc
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000555
556 if hasattr(object, '__path__'):
557 modpkgs = []
558 modnames = []
559 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000560 path = os.path.join(object.__path__[0], file)
561 modname = inspect.getmodulename(file)
562 if modname and modname not in modnames:
563 modpkgs.append((modname, name, 0, 0))
564 modnames.append(modname)
565 elif ispackage(path):
566 modpkgs.append((file, name, 1, 0))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000567 modpkgs.sort()
568 contents = self.multicolumn(modpkgs, self.modpkglink)
569 result = result + self.bigsection(
570 'Package Contents', '#ffffff', '#aa55cc', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000571 elif modules:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000572 contents = self.multicolumn(
573 modules, lambda (key, value), s=self: s.modulelink(value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000574 result = result + self.bigsection(
575 'Modules', '#fffff', '#aa55cc', contents)
576
577 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000578 classlist = map(lambda (key, value): value, classes)
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000579 contents = [
580 self.formattree(inspect.getclasstree(classlist, 1), name)]
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000581 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000582 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000583 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000584 'Classes', '#ffffff', '#ee77aa', join(contents))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000585 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000586 contents = []
587 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000588 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000589 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000590 'Functions', '#ffffff', '#eeaa77', join(contents))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000591 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000592 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000593 for key, value in data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000594 contents.append(self.document(value, key))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000595 result = result + self.bigsection(
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000596 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000597 if hasattr(object, '__author__'):
598 contents = self.markup(str(object.__author__), self.preformat)
599 result = result + self.bigsection(
600 'Author', '#ffffff', '#7799ee', contents)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000601 if hasattr(object, '__credits__'):
602 contents = self.markup(str(object.__credits__), self.preformat)
603 result = result + self.bigsection(
604 'Credits', '#ffffff', '#7799ee', contents)
605
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000606 return result
607
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000608 def docclass(self, object, name=None, mod=None, funcs={}, classes={}):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000609 """Produce HTML documentation for a class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000610 realname = object.__name__
611 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000612 bases = object.__bases__
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000613
Tim Petersb47879b2001-09-24 04:47:19 +0000614 contents = []
615 push = contents.append
616
Tim Petersfa26f7c2001-09-24 08:05:11 +0000617 # Cute little class to pump out a horizontal rule between sections.
618 class HorizontalRule:
619 def __init__(self):
620 self.needone = 0
621 def maybe(self):
622 if self.needone:
623 push('<hr>\n')
624 self.needone = 1
625 hr = HorizontalRule()
626
Tim Petersc86f6ca2001-09-26 21:31:51 +0000627 # List the mro, if non-trivial.
628 mro = inspect.getmro(object)
629 if len(mro) > 2:
630 hr.maybe()
631 push('<dl><dt>Method resolution order:</dt>\n')
632 for base in mro:
633 push('<dd>%s</dd>\n' % self.classlink(base,
634 object.__module__))
635 push('</dl>\n')
636
Tim Petersb47879b2001-09-24 04:47:19 +0000637 def spill(msg, attrs, predicate):
Tim Petersfa26f7c2001-09-24 08:05:11 +0000638 ok, attrs = _split_list(attrs, predicate)
Tim Petersb47879b2001-09-24 04:47:19 +0000639 if ok:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000640 hr.maybe()
Tim Petersb47879b2001-09-24 04:47:19 +0000641 push(msg)
642 for name, kind, homecls, value in ok:
643 push(self.document(getattr(object, name), name, mod,
644 funcs, classes, mdict, object))
645 push('\n')
646 return attrs
647
Tim Petersfa26f7c2001-09-24 08:05:11 +0000648 def spillproperties(msg, attrs, predicate):
649 ok, attrs = _split_list(attrs, predicate)
Tim Petersb47879b2001-09-24 04:47:19 +0000650 if ok:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000651 hr.maybe()
Tim Petersb47879b2001-09-24 04:47:19 +0000652 push(msg)
653 for name, kind, homecls, value in ok:
Tim Peters3e767d12001-09-25 00:01:06 +0000654 push('<dl><dt><strong>%s</strong></dt>\n' % name)
655 if value.__doc__ is not None:
656 doc = self.markup(value.__doc__, self.preformat,
657 funcs, classes, mdict)
Tim Peters2306d242001-09-25 03:18:32 +0000658 push('<dd><tt>%s</tt></dd>\n' % doc)
Tim Petersf33532c2001-09-25 06:30:51 +0000659 for attr, tag in [("fget", " getter"),
660 ("fset", " setter"),
Tim Peters3e767d12001-09-25 00:01:06 +0000661 ("fdel", " deleter")]:
662 func = getattr(value, attr)
663 if func is not None:
664 base = self.document(func, name + tag, mod,
665 funcs, classes, mdict, object)
666 push('<dd>%s</dd>\n' % base)
667 push('</dl>\n')
Tim Petersb47879b2001-09-24 04:47:19 +0000668 return attrs
669
Tim Petersfa26f7c2001-09-24 08:05:11 +0000670 def spilldata(msg, attrs, predicate):
671 ok, attrs = _split_list(attrs, predicate)
Tim Petersb47879b2001-09-24 04:47:19 +0000672 if ok:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000673 hr.maybe()
Tim Petersb47879b2001-09-24 04:47:19 +0000674 push(msg)
675 for name, kind, homecls, value in ok:
676 base = self.docother(getattr(object, name), name, mod)
677 doc = getattr(value, "__doc__", None)
678 if doc is None:
679 push('<dl><dt>%s</dl>\n' % base)
680 else:
681 doc = self.markup(getdoc(value), self.preformat,
682 funcs, classes, mdict)
Tim Peters2306d242001-09-25 03:18:32 +0000683 doc = '<dd><tt>%s</tt>' % doc
Tim Petersb47879b2001-09-24 04:47:19 +0000684 push('<dl><dt>%s%s</dl>\n' % (base, doc))
685 push('\n')
686 return attrs
687
688 attrs = inspect.classify_class_attrs(object)
689 mdict = {}
690 for key, kind, homecls, value in attrs:
691 mdict[key] = anchor = '#' + name + '-' + key
692 value = getattr(object, key)
693 try:
694 # The value may not be hashable (e.g., a data attr with
695 # a dict or list value).
696 mdict[value] = anchor
697 except TypeError:
698 pass
699
Tim Petersfa26f7c2001-09-24 08:05:11 +0000700 # Sort attrs by name of defining class.
701 attrs.sort(lambda t1, t2: cmp(t1[2].__name__, t2[2].__name__))
Tim Petersb47879b2001-09-24 04:47:19 +0000702
Tim Petersfa26f7c2001-09-24 08:05:11 +0000703 thisclass = object # list attrs defined here first
704 while attrs:
705 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
706
Tim Petersb47879b2001-09-24 04:47:19 +0000707 if thisclass is object:
708 tag = "defined here"
709 else:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000710 tag = "inherited from %s" % self.classlink(thisclass,
Tim Petersb47879b2001-09-24 04:47:19 +0000711 object.__module__)
712 tag += ':<br>\n'
713
714 # Sort attrs by name.
715 attrs.sort(lambda t1, t2: cmp(t1[0], t2[0]))
716
717 # Pump out the attrs, segregated by kind.
718 attrs = spill("Methods %s" % tag, attrs,
719 lambda t: t[1] == 'method')
720 attrs = spill("Class methods %s" % tag, attrs,
721 lambda t: t[1] == 'class method')
722 attrs = spill("Static methods %s" % tag, attrs,
723 lambda t: t[1] == 'static method')
Tim Petersfa26f7c2001-09-24 08:05:11 +0000724 attrs = spillproperties("Properties %s" % tag, attrs,
725 lambda t: t[1] == 'property')
Tim Petersf33532c2001-09-25 06:30:51 +0000726 attrs = spilldata("Data and non-method functions %s" % tag, attrs,
Tim Petersfa26f7c2001-09-24 08:05:11 +0000727 lambda t: t[1] == 'data')
Tim Petersb47879b2001-09-24 04:47:19 +0000728 assert attrs == []
729
730 # Split off the attributes inherited from the next class (note
731 # that inherited remains sorted by class name).
732 if inherited:
733 attrs = inherited
734 thisclass = attrs[0][2]
Tim Petersb47879b2001-09-24 04:47:19 +0000735
736 contents = ''.join(contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000737
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000738 if name == realname:
739 title = '<a name="%s">class <strong>%s</strong></a>' % (
740 name, realname)
741 else:
742 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
743 name, name, realname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000744 if bases:
745 parents = []
746 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000747 parents.append(self.classlink(base, object.__module__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000748 title = title + '(%s)' % join(parents, ', ')
Tim Peters2306d242001-09-25 03:18:32 +0000749 doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict)
750 doc = doc and '<tt>%s<br>&nbsp;</tt>' % doc or '&nbsp;'
Tim Petersc86f6ca2001-09-26 21:31:51 +0000751
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000752 return self.section(title, '#000000', '#ffc8d8', contents, 5, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000753
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000754 def formatvalue(self, object):
755 """Format an argument default value as text."""
Tim Peters2306d242001-09-25 03:18:32 +0000756 return self.grey('=' + self.repr(object))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000757
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000758 def docroutine(self, object, name=None, mod=None,
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000759 funcs={}, classes={}, methods={}, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000760 """Produce HTML documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000761 realname = object.__name__
762 name = name or realname
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000763 anchor = (cl and cl.__name__ or '') + '-' + name
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000764 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000765 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000766 if inspect.ismethod(object):
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +0000767 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000768 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000769 if imclass is not cl:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000770 note = ' from ' + self.classlink(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000771 skipdocs = 1
772 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000773 if object.im_self:
774 note = ' method of %s instance' % self.classlink(
775 object.im_self.__class__, mod)
776 else:
777 note = ' unbound %s method' % self.classlink(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000778 object = object.im_func
779
780 if name == realname:
781 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
782 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000783 if (cl and cl.__dict__.has_key(realname) and
784 cl.__dict__[realname] is object):
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000785 reallink = '<a href="#%s">%s</a>' % (
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000786 cl.__name__ + '-' + realname, realname)
787 skipdocs = 1
788 else:
789 reallink = realname
790 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
791 anchor, name, reallink)
Tim Peters4bcfa312001-09-20 06:08:24 +0000792 if inspect.isfunction(object):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000793 args, varargs, varkw, defaults = inspect.getargspec(object)
794 argspec = inspect.formatargspec(
795 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000796 if realname == '<lambda>':
797 decl = '<em>lambda</em>'
798 argspec = argspec[1:-1] # remove parentheses
Tim Peters4bcfa312001-09-20 06:08:24 +0000799 else:
800 argspec = '(...)'
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000801
Tim Peters2306d242001-09-25 03:18:32 +0000802 decl = title + argspec + (note and self.grey(
803 '<font face="helvetica, arial">%s</font>' % note))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000804
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000805 if skipdocs:
Tim Peters2306d242001-09-25 03:18:32 +0000806 return '<dl><dt>%s</dt></dl>\n' % decl
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000807 else:
808 doc = self.markup(
809 getdoc(object), self.preformat, funcs, classes, methods)
Tim Peters2306d242001-09-25 03:18:32 +0000810 doc = doc and '<dd><tt>%s</tt></dd>' % doc
811 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000812
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000813 def docother(self, object, name=None, mod=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000814 """Produce HTML documentation for a data object."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000815 lhs = name and '<strong>%s</strong> = ' % name or ''
816 return lhs + self.repr(object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000817
818 def index(self, dir, shadowed=None):
819 """Generate an HTML index for a directory of modules."""
820 modpkgs = []
821 if shadowed is None: shadowed = {}
822 seen = {}
823 files = os.listdir(dir)
824
825 def found(name, ispackage,
826 modpkgs=modpkgs, shadowed=shadowed, seen=seen):
827 if not seen.has_key(name):
828 modpkgs.append((name, '', ispackage, shadowed.has_key(name)))
829 seen[name] = 1
830 shadowed[name] = 1
831
832 # Package spam/__init__.py takes precedence over module spam.py.
833 for file in files:
834 path = os.path.join(dir, file)
835 if ispackage(path): found(file, 1)
836 for file in files:
837 path = os.path.join(dir, file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000838 if os.path.isfile(path):
839 modname = inspect.getmodulename(file)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000840 if modname: found(modname, 0)
841
842 modpkgs.sort()
843 contents = self.multicolumn(modpkgs, self.modpkglink)
844 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
845
846# -------------------------------------------- text documentation generator
847
848class TextRepr(Repr):
849 """Class for safely making a text representation of a Python object."""
850 def __init__(self):
851 Repr.__init__(self)
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000852 self.maxlist = self.maxtuple = 20
853 self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000854 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000855
856 def repr1(self, x, level):
857 methodname = 'repr_' + join(split(type(x).__name__), '_')
858 if hasattr(self, methodname):
859 return getattr(self, methodname)(x, level)
860 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000861 return cram(stripid(repr(x)), self.maxother)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000862
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000863 def repr_string(self, x, level):
864 test = cram(x, self.maxstring)
865 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000866 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000867 # Backslashes are only literal in the string and are never
868 # needed to make any special characters, so show a raw string.
869 return 'r' + testrepr[0] + test + testrepr[0]
870 return testrepr
871
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000872 def repr_instance(self, x, level):
873 try:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000874 return cram(stripid(repr(x)), self.maxstring)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000875 except:
876 return '<%s instance>' % x.__class__.__name__
877
878class TextDoc(Doc):
879 """Formatter class for text documentation."""
880
881 # ------------------------------------------- text formatting utilities
882
883 _repr_instance = TextRepr()
884 repr = _repr_instance.repr
885
886 def bold(self, text):
887 """Format a string in bold by overstriking."""
888 return join(map(lambda ch: ch + '\b' + ch, text), '')
889
890 def indent(self, text, prefix=' '):
891 """Indent text by prepending a given prefix to each line."""
892 if not text: return ''
893 lines = split(text, '\n')
894 lines = map(lambda line, prefix=prefix: prefix + line, lines)
895 if lines: lines[-1] = rstrip(lines[-1])
896 return join(lines, '\n')
897
898 def section(self, title, contents):
899 """Format a section with a given heading."""
900 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
901
902 # ---------------------------------------------- type-specific routines
903
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000904 def formattree(self, tree, modname, parent=None, prefix=''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000905 """Render in text a class tree as returned by inspect.getclasstree()."""
906 result = ''
907 for entry in tree:
908 if type(entry) is type(()):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000909 c, bases = entry
910 result = result + prefix + classname(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000911 if bases and bases != (parent,):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000912 parents = map(lambda c, m=modname: classname(c, m), bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000913 result = result + '(%s)' % join(parents, ', ')
914 result = result + '\n'
915 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000916 result = result + self.formattree(
917 entry, modname, c, prefix + ' ')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000918 return result
919
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000920 def docmodule(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000921 """Produce text documentation for a given module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000922 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000923 synop, desc = splitdoc(getdoc(object))
924 result = self.section('NAME', name + (synop and ' - ' + synop))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000925
926 try:
927 file = inspect.getabsfile(object)
928 except TypeError:
929 file = '(built-in)'
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000930 result = result + self.section('FILE', file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000931 if desc:
932 result = result + self.section('DESCRIPTION', desc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000933
934 classes = []
935 for key, value in inspect.getmembers(object, inspect.isclass):
936 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000937 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000938 funcs = []
939 for key, value in inspect.getmembers(object, inspect.isroutine):
940 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000941 funcs.append((key, value))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000942 data = []
943 for key, value in inspect.getmembers(object, isdata):
944 if key not in ['__builtins__', '__doc__']:
945 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000946
947 if hasattr(object, '__path__'):
948 modpkgs = []
949 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000950 path = os.path.join(object.__path__[0], file)
951 modname = inspect.getmodulename(file)
952 if modname and modname not in modpkgs:
953 modpkgs.append(modname)
954 elif ispackage(path):
955 modpkgs.append(file + ' (package)')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000956 modpkgs.sort()
957 result = result + self.section(
958 'PACKAGE CONTENTS', join(modpkgs, '\n'))
959
960 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000961 classlist = map(lambda (key, value): value, classes)
962 contents = [self.formattree(
963 inspect.getclasstree(classlist, 1), name)]
964 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000965 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000966 result = result + self.section('CLASSES', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000967
968 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000969 contents = []
970 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000971 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000972 result = result + self.section('FUNCTIONS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000973
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000974 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000975 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000976 for key, value in data:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000977 contents.append(self.docother(value, key, name, 70))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000978 result = result + self.section('DATA', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000979
980 if hasattr(object, '__version__'):
981 version = str(object.__version__)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000982 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
983 version = strip(version[11:-1])
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000984 result = result + self.section('VERSION', version)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000985 if hasattr(object, '__date__'):
986 result = result + self.section('DATE', str(object.__date__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000987 if hasattr(object, '__author__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000988 result = result + self.section('AUTHOR', str(object.__author__))
989 if hasattr(object, '__credits__'):
990 result = result + self.section('CREDITS', str(object.__credits__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000991 return result
992
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000993 def docclass(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000994 """Produce text documentation for a given class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000995 realname = object.__name__
996 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000997 bases = object.__bases__
998
Tim Petersc86f6ca2001-09-26 21:31:51 +0000999 def makename(c, m=object.__module__):
1000 return classname(c, m)
1001
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001002 if name == realname:
1003 title = 'class ' + self.bold(realname)
1004 else:
1005 title = self.bold(name) + ' = class ' + realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001006 if bases:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001007 parents = map(makename, bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001008 title = title + '(%s)' % join(parents, ', ')
1009
1010 doc = getdoc(object)
Tim Peters28355492001-09-23 21:29:55 +00001011 contents = doc and [doc + '\n'] or []
1012 push = contents.append
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001013
Tim Petersc86f6ca2001-09-26 21:31:51 +00001014 # List the mro, if non-trivial.
1015 mro = inspect.getmro(object)
1016 if len(mro) > 2:
1017 push("Method resolution order:")
1018 for base in mro:
1019 push(' ' + makename(base))
1020 push('')
1021
Tim Petersf4aad8e2001-09-24 22:40:47 +00001022 # Cute little class to pump out a horizontal rule between sections.
1023 class HorizontalRule:
1024 def __init__(self):
1025 self.needone = 0
1026 def maybe(self):
1027 if self.needone:
1028 push('-' * 70)
1029 self.needone = 1
1030 hr = HorizontalRule()
1031
Tim Peters28355492001-09-23 21:29:55 +00001032 def spill(msg, attrs, predicate):
Tim Petersfa26f7c2001-09-24 08:05:11 +00001033 ok, attrs = _split_list(attrs, predicate)
Tim Peters28355492001-09-23 21:29:55 +00001034 if ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001035 hr.maybe()
Tim Peters28355492001-09-23 21:29:55 +00001036 push(msg)
1037 for name, kind, homecls, value in ok:
1038 push(self.document(getattr(object, name),
1039 name, mod, object))
1040 return attrs
1041
Tim Petersfa26f7c2001-09-24 08:05:11 +00001042 def spillproperties(msg, attrs, predicate):
1043 ok, attrs = _split_list(attrs, predicate)
Tim Peters28355492001-09-23 21:29:55 +00001044 if ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001045 hr.maybe()
Tim Peters28355492001-09-23 21:29:55 +00001046 push(msg)
1047 for name, kind, homecls, value in ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001048 push(name)
1049 need_blank_after_doc = 0
1050 doc = getdoc(value) or ''
1051 if doc:
1052 push(self.indent(doc))
1053 need_blank_after_doc = 1
Tim Petersf33532c2001-09-25 06:30:51 +00001054 for attr, tag in [("fget", " getter"),
1055 ("fset", " setter"),
Tim Petersf4aad8e2001-09-24 22:40:47 +00001056 ("fdel", " deleter")]:
1057 func = getattr(value, attr)
1058 if func is not None:
1059 if need_blank_after_doc:
1060 push('')
1061 need_blank_after_doc = 0
1062 base = self.docother(func, name + tag, mod, 70)
1063 push(self.indent(base))
1064 push('')
Tim Peters28355492001-09-23 21:29:55 +00001065 return attrs
Tim Petersb47879b2001-09-24 04:47:19 +00001066
Tim Petersfa26f7c2001-09-24 08:05:11 +00001067 def spilldata(msg, attrs, predicate):
1068 ok, attrs = _split_list(attrs, predicate)
Tim Peters28355492001-09-23 21:29:55 +00001069 if ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001070 hr.maybe()
Tim Peters28355492001-09-23 21:29:55 +00001071 push(msg)
1072 for name, kind, homecls, value in ok:
1073 doc = getattr(value, "__doc__", None)
1074 push(self.docother(getattr(object, name),
1075 name, mod, 70, doc) + '\n')
1076 return attrs
1077
1078 attrs = inspect.classify_class_attrs(object)
1079
Tim Petersfa26f7c2001-09-24 08:05:11 +00001080 # Sort attrs by name of defining class.
1081 attrs.sort(lambda t1, t2: cmp(t1[2].__name__, t2[2].__name__))
Tim Peters28355492001-09-23 21:29:55 +00001082
Tim Petersfa26f7c2001-09-24 08:05:11 +00001083 thisclass = object # list attrs defined here first
1084 while attrs:
1085 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
1086
Tim Peters28355492001-09-23 21:29:55 +00001087 if thisclass is object:
1088 tag = "defined here"
1089 else:
Tim Petersfa26f7c2001-09-24 08:05:11 +00001090 tag = "inherited from %s" % classname(thisclass,
1091 object.__module__)
Tim Peters28355492001-09-23 21:29:55 +00001092
1093 # Sort attrs by name.
1094 attrs.sort(lambda t1, t2: cmp(t1[0], t2[0]))
1095
1096 # Pump out the attrs, segregated by kind.
Tim Petersf4aad8e2001-09-24 22:40:47 +00001097 attrs = spill("Methods %s:\n" % tag, attrs,
Tim Peters28355492001-09-23 21:29:55 +00001098 lambda t: t[1] == 'method')
Tim Petersf4aad8e2001-09-24 22:40:47 +00001099 attrs = spill("Class methods %s:\n" % tag, attrs,
Tim Peters28355492001-09-23 21:29:55 +00001100 lambda t: t[1] == 'class method')
Tim Petersf4aad8e2001-09-24 22:40:47 +00001101 attrs = spill("Static methods %s:\n" % tag, attrs,
Tim Peters28355492001-09-23 21:29:55 +00001102 lambda t: t[1] == 'static method')
Tim Petersf4aad8e2001-09-24 22:40:47 +00001103 attrs = spillproperties("Properties %s:\n" % tag, attrs,
Tim Petersfa26f7c2001-09-24 08:05:11 +00001104 lambda t: t[1] == 'property')
Tim Petersf33532c2001-09-25 06:30:51 +00001105 attrs = spilldata("Data and non-method functions %s:\n" % tag,
1106 attrs, lambda t: t[1] == 'data')
Tim Peters28355492001-09-23 21:29:55 +00001107 assert attrs == []
1108
1109 # Split off the attributes inherited from the next class (note
1110 # that inherited remains sorted by class name).
1111 if inherited:
1112 attrs = inherited
1113 thisclass = attrs[0][2]
Tim Peters28355492001-09-23 21:29:55 +00001114
1115 contents = '\n'.join(contents)
1116 if not contents:
1117 return title + '\n'
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001118 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
1119
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001120 def formatvalue(self, object):
1121 """Format an argument default value as text."""
1122 return '=' + self.repr(object)
1123
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001124 def docroutine(self, object, name=None, mod=None, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001125 """Produce text documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001126 realname = object.__name__
1127 name = name or realname
1128 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001129 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001130 if inspect.ismethod(object):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001131 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001132 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001133 if imclass is not cl:
1134 note = ' from ' + classname(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001135 skipdocs = 1
1136 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +00001137 if object.im_self:
1138 note = ' method of %s instance' % classname(
1139 object.im_self.__class__, mod)
1140 else:
1141 note = ' unbound %s method' % classname(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001142 object = object.im_func
1143
1144 if name == realname:
1145 title = self.bold(realname)
1146 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001147 if (cl and cl.__dict__.has_key(realname) and
1148 cl.__dict__[realname] is object):
1149 skipdocs = 1
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001150 title = self.bold(name) + ' = ' + realname
Tim Peters4bcfa312001-09-20 06:08:24 +00001151 if inspect.isfunction(object):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001152 args, varargs, varkw, defaults = inspect.getargspec(object)
1153 argspec = inspect.formatargspec(
1154 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001155 if realname == '<lambda>':
1156 title = 'lambda'
1157 argspec = argspec[1:-1] # remove parentheses
Tim Peters4bcfa312001-09-20 06:08:24 +00001158 else:
1159 argspec = '(...)'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001160 decl = title + argspec + note
1161
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001162 if skipdocs:
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001163 return decl + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001164 else:
1165 doc = getdoc(object) or ''
1166 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001167
Tim Peters28355492001-09-23 21:29:55 +00001168 def docother(self, object, name=None, mod=None, maxlen=None, doc=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001169 """Produce text documentation for a data object."""
1170 repr = self.repr(object)
1171 if maxlen:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001172 line = (name and name + ' = ' or '') + repr
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001173 chop = maxlen - len(line)
1174 if chop < 0: repr = repr[:chop] + '...'
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001175 line = (name and self.bold(name) + ' = ' or '') + repr
Tim Peters28355492001-09-23 21:29:55 +00001176 if doc is not None:
1177 line += '\n' + self.indent(str(doc))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001178 return line
1179
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001180# --------------------------------------------------------- user interfaces
1181
1182def pager(text):
1183 """The first time this is called, determine what kind of pager to use."""
1184 global pager
1185 pager = getpager()
1186 pager(text)
1187
1188def getpager():
1189 """Decide what method to use for paging through text."""
1190 if type(sys.stdout) is not types.FileType:
1191 return plainpager
1192 if not sys.stdin.isatty() or not sys.stdout.isatty():
1193 return plainpager
Fred Drake0a66fcb2001-07-23 19:44:30 +00001194 if os.environ.get('TERM') in ['dumb', 'emacs']:
Fred Drake5e9eb982001-07-23 19:48:10 +00001195 return plainpager
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001196 if os.environ.has_key('PAGER'):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001197 if sys.platform == 'win32': # pipes completely broken in Windows
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001198 return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
1199 elif os.environ.get('TERM') in ['dumb', 'emacs']:
1200 return lambda text: pipepager(plain(text), os.environ['PAGER'])
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001201 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001202 return lambda text: pipepager(text, os.environ['PAGER'])
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001203 if sys.platform == 'win32':
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001204 return lambda text: tempfilepager(plain(text), 'more <')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001205 if hasattr(os, 'system') and os.system('less 2>/dev/null') == 0:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001206 return lambda text: pipepager(text, 'less')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001207
1208 import tempfile
1209 filename = tempfile.mktemp()
1210 open(filename, 'w').close()
1211 try:
1212 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
1213 return lambda text: pipepager(text, 'more')
1214 else:
1215 return ttypager
1216 finally:
1217 os.unlink(filename)
1218
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001219def plain(text):
1220 """Remove boldface formatting from text."""
1221 return re.sub('.\b', '', text)
1222
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001223def pipepager(text, cmd):
1224 """Page through text by feeding it to another program."""
1225 pipe = os.popen(cmd, 'w')
1226 try:
1227 pipe.write(text)
1228 pipe.close()
1229 except IOError:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001230 pass # Ignore broken pipes caused by quitting the pager program.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001231
1232def tempfilepager(text, cmd):
1233 """Page through text by invoking a program on a temporary file."""
1234 import tempfile
1235 filename = tempfile.mktemp()
1236 file = open(filename, 'w')
1237 file.write(text)
1238 file.close()
1239 try:
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001240 os.system(cmd + ' ' + filename)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001241 finally:
1242 os.unlink(filename)
1243
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001244def ttypager(text):
1245 """Page through text on a text terminal."""
1246 lines = split(plain(text), '\n')
1247 try:
1248 import tty
1249 fd = sys.stdin.fileno()
1250 old = tty.tcgetattr(fd)
1251 tty.setcbreak(fd)
1252 getchar = lambda: sys.stdin.read(1)
Ka-Ping Yee457aab22001-02-27 23:36:29 +00001253 except (ImportError, AttributeError):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001254 tty = None
1255 getchar = lambda: sys.stdin.readline()[:-1][:1]
1256
1257 try:
1258 r = inc = os.environ.get('LINES', 25) - 1
1259 sys.stdout.write(join(lines[:inc], '\n') + '\n')
1260 while lines[r:]:
1261 sys.stdout.write('-- more --')
1262 sys.stdout.flush()
1263 c = getchar()
1264
1265 if c in ['q', 'Q']:
1266 sys.stdout.write('\r \r')
1267 break
1268 elif c in ['\r', '\n']:
1269 sys.stdout.write('\r \r' + lines[r] + '\n')
1270 r = r + 1
1271 continue
1272 if c in ['b', 'B', '\x1b']:
1273 r = r - inc - inc
1274 if r < 0: r = 0
1275 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1276 r = r + inc
1277
1278 finally:
1279 if tty:
1280 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1281
1282def plainpager(text):
1283 """Simply print unformatted text. This is the ultimate fallback."""
1284 sys.stdout.write(plain(text))
1285
1286def describe(thing):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001287 """Produce a short description of the given thing."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001288 if inspect.ismodule(thing):
1289 if thing.__name__ in sys.builtin_module_names:
1290 return 'built-in module ' + thing.__name__
1291 if hasattr(thing, '__path__'):
1292 return 'package ' + thing.__name__
1293 else:
1294 return 'module ' + thing.__name__
1295 if inspect.isbuiltin(thing):
1296 return 'built-in function ' + thing.__name__
1297 if inspect.isclass(thing):
1298 return 'class ' + thing.__name__
1299 if inspect.isfunction(thing):
1300 return 'function ' + thing.__name__
1301 if inspect.ismethod(thing):
1302 return 'method ' + thing.__name__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001303 if type(thing) is types.InstanceType:
1304 return 'instance of ' + thing.__class__.__name__
1305 return type(thing).__name__
1306
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001307def locate(path, forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001308 """Locate an object by name or dotted path, importing as necessary."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001309 parts = split(path, '.')
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001310 module, n = None, 0
1311 while n < len(parts):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001312 nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001313 if nextmodule: module, n = nextmodule, n + 1
1314 else: break
1315 if module:
1316 object = module
1317 for part in parts[n:]:
1318 try: object = getattr(object, part)
1319 except AttributeError: return None
1320 return object
1321 else:
1322 import __builtin__
1323 if hasattr(__builtin__, path):
1324 return getattr(__builtin__, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001325
1326# --------------------------------------- interactive interpreter interface
1327
1328text = TextDoc()
1329html = HTMLDoc()
1330
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001331def doc(thing, title='Python Library Documentation: %s', forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001332 """Display text documentation, given an object or a path to an object."""
1333 suffix, name = '', None
1334 if type(thing) is type(''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001335 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001336 object = locate(thing, forceload)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001337 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001338 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001339 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001340 if not object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001341 print 'no Python documentation found for %s' % repr(thing)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001342 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001343 parts = split(thing, '.')
1344 if len(parts) > 1: suffix = ' in ' + join(parts[:-1], '.')
1345 name = parts[-1]
1346 thing = object
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001347
1348 desc = describe(thing)
1349 module = inspect.getmodule(thing)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001350 if not suffix and module and module is not thing:
1351 suffix = ' in module ' + module.__name__
Ka-Ping Yee66246962001-04-12 11:59:50 +00001352 pager(title % (desc + suffix) + '\n\n' + text.document(thing, name))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001353
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001354def writedoc(key, forceload=0):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001355 """Write HTML documentation to a file in the current directory."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001356 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001357 object = locate(key, forceload)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001358 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001359 print value
1360 else:
1361 if object:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001362 page = html.page(describe(object),
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001363 html.document(object, object.__name__))
1364 file = open(key + '.html', 'w')
1365 file.write(page)
1366 file.close()
1367 print 'wrote', key + '.html'
1368 else:
1369 print 'no Python documentation found for %s' % repr(key)
1370
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001371def writedocs(dir, pkgpath='', done=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001372 """Write out HTML documentation for all modules in a directory tree."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001373 if done is None: done = {}
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001374 for file in os.listdir(dir):
1375 path = os.path.join(dir, file)
1376 if ispackage(path):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001377 writedocs(path, pkgpath + file + '.', done)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001378 elif os.path.isfile(path):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001379 modname = inspect.getmodulename(path)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001380 if modname:
1381 modname = pkgpath + modname
1382 if not done.has_key(modname):
1383 done[modname] = 1
1384 writedoc(modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001385
1386class Helper:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001387 keywords = {
1388 'and': 'BOOLEAN',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001389 'assert': ('ref/assert', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001390 'break': ('ref/break', 'while for'),
1391 'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1392 'continue': ('ref/continue', 'while for'),
1393 'def': ('ref/function', ''),
1394 'del': ('ref/del', 'BASICMETHODS'),
1395 'elif': 'if',
1396 'else': ('ref/if', 'while for'),
1397 'except': 'try',
1398 'exec': ('ref/exec', ''),
1399 'finally': 'try',
1400 'for': ('ref/for', 'break continue while'),
1401 'from': 'import',
1402 'global': ('ref/global', 'NAMESPACES'),
1403 'if': ('ref/if', 'TRUTHVALUE'),
1404 'import': ('ref/import', 'MODULES'),
1405 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1406 'is': 'COMPARISON',
1407 'lambda': ('ref/lambda', 'FUNCTIONS'),
1408 'not': 'BOOLEAN',
1409 'or': 'BOOLEAN',
1410 'pass': 'PASS',
1411 'print': ('ref/print', ''),
1412 'raise': ('ref/raise', 'EXCEPTIONS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001413 'return': ('ref/return', 'FUNCTIONS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001414 'try': ('ref/try', 'EXCEPTIONS'),
1415 'while': ('ref/while', 'break continue if TRUTHVALUE'),
1416 }
1417
1418 topics = {
1419 'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001420 'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001421 'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'),
1422 'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001423 'UNICODE': ('ref/unicode', 'encodings unicode TYPES STRING'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001424 'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1425 'INTEGER': ('ref/integers', 'int range'),
1426 'FLOAT': ('ref/floating', 'float math'),
1427 'COMPLEX': ('ref/imaginary', 'complex cmath'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001428 'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001429 'MAPPINGS': 'DICTIONARIES',
1430 'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'),
1431 'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'),
1432 'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001433 'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001434 'FRAMEOBJECTS': 'TYPES',
1435 'TRACEBACKS': 'TYPES',
1436 'NONE': ('lib/bltin-null-object', ''),
1437 'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'),
1438 'FILES': ('lib/bltin-file-objects', ''),
1439 'SPECIALATTRIBUTES': ('lib/specialattrs', ''),
1440 'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'),
1441 'MODULES': ('lib/typesmodules', 'import'),
1442 'PACKAGES': 'import',
1443 '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'),
1444 'OPERATORS': 'EXPRESSIONS',
1445 'PRECEDENCE': 'EXPRESSIONS',
1446 'OBJECTS': ('ref/objects', 'TYPES'),
1447 'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001448 'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'),
1449 'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1450 'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'),
1451 'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'),
1452 'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'),
1453 'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'),
1454 'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001455 'EXECUTION': ('ref/execframes', ''),
1456 'NAMESPACES': ('ref/execframes', 'global ASSIGNMENT DELETION'),
1457 'SCOPING': 'NAMESPACES',
1458 'FRAMES': 'NAMESPACES',
1459 'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'),
1460 'COERCIONS': 'CONVERSIONS',
1461 'CONVERSIONS': ('ref/conversions', ''),
1462 'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'),
1463 'SPECIALIDENTIFIERS': ('ref/id-classes', ''),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001464 'PRIVATENAMES': ('ref/atom-identifiers', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001465 'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1466 'TUPLES': 'SEQUENCES',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001467 'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001468 'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001469 'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001470 'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001471 'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'),
1472 'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001473 'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1474 'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'),
1475 'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'),
1476 'CALLS': ('ref/calls', 'EXPRESSIONS'),
1477 'POWER': ('ref/power', 'EXPRESSIONS'),
1478 'UNARY': ('ref/unary', 'EXPRESSIONS'),
1479 'BINARY': ('ref/binary', 'EXPRESSIONS'),
1480 'SHIFTING': ('ref/shifting', 'EXPRESSIONS'),
1481 'BITWISE': ('ref/bitwise', 'EXPRESSIONS'),
1482 'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001483 'BOOLEAN': ('ref/lambda', 'EXPRESSIONS TRUTHVALUE'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001484 'ASSERTION': 'assert',
1485 'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001486 'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001487 'DELETION': 'del',
1488 'PRINTING': 'print',
1489 'RETURNING': 'return',
1490 'IMPORTING': 'import',
1491 'CONDITIONAL': 'if',
1492 'LOOPING': ('ref/compound', 'for while break continue'),
1493 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001494 'DEBUGGING': ('lib/module-pdb', 'pdb'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001495 }
1496
1497 def __init__(self, input, output):
1498 self.input = input
1499 self.output = output
1500 self.docdir = None
1501 execdir = os.path.dirname(sys.executable)
1502 homedir = os.environ.get('PYTHONHOME')
1503 for dir in [os.environ.get('PYTHONDOCS'),
1504 homedir and os.path.join(homedir, 'doc'),
1505 os.path.join(execdir, 'doc'),
1506 '/usr/doc/python-docs-' + split(sys.version)[0],
1507 '/usr/doc/python-' + split(sys.version)[0],
1508 '/usr/doc/python-docs-' + sys.version[:3],
1509 '/usr/doc/python-' + sys.version[:3]]:
1510 if dir and os.path.isdir(os.path.join(dir, 'lib')):
1511 self.docdir = dir
1512
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001513 def __repr__(self):
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001514 if inspect.stack()[1][3] == '?':
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001515 self()
1516 return ''
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001517 return '<pydoc.Helper instance>'
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001518
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001519 def __call__(self, request=None):
1520 if request is not None:
1521 self.help(request)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001522 else:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001523 self.intro()
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001524 self.interact()
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001525 self.output.write('''
Fred Drakee61967f2001-05-10 18:41:02 +00001526You are now leaving help and returning to the Python interpreter.
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001527If you want to ask for help on a particular object directly from the
1528interpreter, you can type "help(object)". Executing "help('string')"
1529has the same effect as typing a particular string at the help> prompt.
1530''')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001531
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001532 def interact(self):
1533 self.output.write('\n')
1534 while 1:
1535 self.output.write('help> ')
1536 self.output.flush()
1537 try:
1538 request = self.input.readline()
1539 if not request: break
1540 except KeyboardInterrupt: break
1541 request = strip(replace(request, '"', '', "'", ''))
1542 if lower(request) in ['q', 'quit']: break
1543 self.help(request)
1544
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001545 def help(self, request):
1546 if type(request) is type(''):
1547 if request == 'help': self.intro()
1548 elif request == 'keywords': self.listkeywords()
1549 elif request == 'topics': self.listtopics()
1550 elif request == 'modules': self.listmodules()
1551 elif request[:8] == 'modules ':
1552 self.listmodules(split(request)[1])
1553 elif self.keywords.has_key(request): self.showtopic(request)
1554 elif self.topics.has_key(request): self.showtopic(request)
1555 elif request: doc(request, 'Help on %s:')
1556 elif isinstance(request, Helper): self()
1557 else: doc(request, 'Help on %s:')
1558 self.output.write('\n')
1559
1560 def intro(self):
1561 self.output.write('''
1562Welcome to Python %s! This is the online help utility.
1563
1564If this is your first time using Python, you should definitely check out
1565the tutorial on the Internet at http://www.python.org/doc/tut/.
1566
1567Enter the name of any module, keyword, or topic to get help on writing
1568Python programs and using Python modules. To quit this help utility and
1569return to the interpreter, just type "quit".
1570
1571To get a list of available modules, keywords, or topics, type "modules",
1572"keywords", or "topics". Each module also comes with a one-line summary
1573of what it does; to list the modules whose summaries contain a given word
1574such as "spam", type "modules spam".
1575''' % sys.version[:3])
1576
1577 def list(self, items, columns=4, width=80):
1578 items = items[:]
1579 items.sort()
1580 colw = width / columns
1581 rows = (len(items) + columns - 1) / columns
1582 for row in range(rows):
1583 for col in range(columns):
1584 i = col * rows + row
1585 if i < len(items):
1586 self.output.write(items[i])
1587 if col < columns - 1:
1588 self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1589 self.output.write('\n')
1590
1591 def listkeywords(self):
1592 self.output.write('''
1593Here is a list of the Python keywords. Enter any keyword to get more help.
1594
1595''')
1596 self.list(self.keywords.keys())
1597
1598 def listtopics(self):
1599 self.output.write('''
1600Here is a list of available topics. Enter any topic name to get more help.
1601
1602''')
1603 self.list(self.topics.keys())
1604
1605 def showtopic(self, topic):
1606 if not self.docdir:
1607 self.output.write('''
1608Sorry, topic and keyword documentation is not available because the Python
1609HTML documentation files could not be found. If you have installed them,
1610please set the environment variable PYTHONDOCS to indicate their location.
1611''')
1612 return
1613 target = self.topics.get(topic, self.keywords.get(topic))
1614 if not target:
1615 self.output.write('no documentation found for %s\n' % repr(topic))
1616 return
1617 if type(target) is type(''):
1618 return self.showtopic(target)
1619
1620 filename, xrefs = target
1621 filename = self.docdir + '/' + filename + '.html'
1622 try:
1623 file = open(filename)
1624 except:
1625 self.output.write('could not read docs from %s\n' % filename)
1626 return
1627
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001628 divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
1629 addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001630 document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1631 file.close()
1632
1633 import htmllib, formatter, StringIO
1634 buffer = StringIO.StringIO()
1635 parser = htmllib.HTMLParser(
1636 formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1637 parser.start_table = parser.do_p
1638 parser.end_table = lambda parser=parser: parser.do_p({})
1639 parser.start_tr = parser.do_br
1640 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1641 parser.feed(document)
1642 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
1643 pager(' ' + strip(buffer) + '\n')
Ka-Ping Yeeda793892001-04-13 11:02:51 +00001644 if xrefs:
1645 buffer = StringIO.StringIO()
1646 formatter.DumbWriter(buffer).send_flowing_data(
1647 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1648 self.output.write('\n%s\n' % buffer.getvalue())
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001649
1650 def listmodules(self, key=''):
1651 if key:
1652 self.output.write('''
1653Here is a list of matching modules. Enter any module name to get more help.
1654
1655''')
1656 apropos(key)
1657 else:
1658 self.output.write('''
1659Please wait a moment while I gather a list of all available modules...
1660
1661''')
1662 modules = {}
1663 def callback(path, modname, desc, modules=modules):
1664 if modname and modname[-9:] == '.__init__':
1665 modname = modname[:-9] + ' (package)'
1666 if find(modname, '.') < 0:
1667 modules[modname] = 1
1668 ModuleScanner().run(callback)
1669 self.list(modules.keys())
1670 self.output.write('''
1671Enter any module name to get more help. Or, type "modules spam" to search
1672for modules whose descriptions contain the word "spam".
1673''')
1674
1675help = Helper(sys.stdin, sys.stdout)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001676
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001677class Scanner:
1678 """A generic tree iterator."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001679 def __init__(self, roots, children, descendp):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001680 self.roots = roots[:]
1681 self.state = []
1682 self.children = children
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001683 self.descendp = descendp
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001684
1685 def next(self):
1686 if not self.state:
1687 if not self.roots:
1688 return None
1689 root = self.roots.pop(0)
1690 self.state = [(root, self.children(root))]
1691 node, children = self.state[-1]
1692 if not children:
1693 self.state.pop()
1694 return self.next()
1695 child = children.pop(0)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001696 if self.descendp(child):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001697 self.state.append((child, self.children(child)))
1698 return child
1699
1700class ModuleScanner(Scanner):
1701 """An interruptible scanner that searches module synopses."""
1702 def __init__(self):
1703 roots = map(lambda dir: (dir, ''), pathdirs())
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001704 Scanner.__init__(self, roots, self.submodules, self.isnewpackage)
1705 self.inodes = map(lambda (dir, pkg): os.stat(dir)[1], roots)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001706
1707 def submodules(self, (dir, package)):
1708 children = []
1709 for file in os.listdir(dir):
1710 path = os.path.join(dir, file)
Tim Peters30edd232001-03-16 08:29:48 +00001711 if ispackage(path):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001712 children.append((path, package + (package and '.') + file))
1713 else:
1714 children.append((path, package))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001715 children.sort() # so that spam.py comes before spam.pyc or spam.pyo
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001716 return children
1717
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001718 def isnewpackage(self, (dir, package)):
Ka-Ping Yee6191a232001-04-13 15:00:27 +00001719 inode = os.path.exists(dir) and os.stat(dir)[1]
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001720 if not (os.path.islink(dir) and inode in self.inodes):
Ka-Ping Yee6191a232001-04-13 15:00:27 +00001721 self.inodes.append(inode) # detect circular symbolic links
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001722 return ispackage(dir)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001723
Ka-Ping Yee66246962001-04-12 11:59:50 +00001724 def run(self, callback, key=None, completer=None):
1725 if key: key = lower(key)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001726 self.quit = 0
1727 seen = {}
1728
1729 for modname in sys.builtin_module_names:
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001730 if modname != '__main__':
1731 seen[modname] = 1
Ka-Ping Yee66246962001-04-12 11:59:50 +00001732 if key is None:
1733 callback(None, modname, '')
1734 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001735 desc = split(__import__(modname).__doc__ or '', '\n')[0]
Ka-Ping Yee66246962001-04-12 11:59:50 +00001736 if find(lower(modname + ' - ' + desc), key) >= 0:
1737 callback(None, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001738
1739 while not self.quit:
1740 node = self.next()
1741 if not node: break
1742 path, package = node
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001743 modname = inspect.getmodulename(path)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001744 if os.path.isfile(path) and modname:
1745 modname = package + (package and '.') + modname
1746 if not seen.has_key(modname):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001747 seen[modname] = 1 # if we see spam.py, skip spam.pyc
Ka-Ping Yee66246962001-04-12 11:59:50 +00001748 if key is None:
1749 callback(path, modname, '')
1750 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001751 desc = synopsis(path) or ''
1752 if find(lower(modname + ' - ' + desc), key) >= 0:
1753 callback(path, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001754 if completer: completer()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001755
1756def apropos(key):
1757 """Print all the one-line module summaries that contain a substring."""
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001758 def callback(path, modname, desc):
1759 if modname[-9:] == '.__init__':
1760 modname = modname[:-9] + ' (package)'
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001761 print modname, desc and '- ' + desc
1762 try: import warnings
1763 except ImportError: pass
1764 else: warnings.filterwarnings('ignore') # ignore problems during import
Ka-Ping Yee66246962001-04-12 11:59:50 +00001765 ModuleScanner().run(callback, key)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001766
1767# --------------------------------------------------- web browser interface
1768
Ka-Ping Yee66246962001-04-12 11:59:50 +00001769def serve(port, callback=None, completer=None):
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001770 import BaseHTTPServer, mimetools, select
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001771
1772 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1773 class Message(mimetools.Message):
1774 def __init__(self, fp, seekable=1):
1775 Message = self.__class__
1776 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1777 self.encodingheader = self.getheader('content-transfer-encoding')
1778 self.typeheader = self.getheader('content-type')
1779 self.parsetype()
1780 self.parseplist()
1781
1782 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1783 def send_document(self, title, contents):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001784 try:
1785 self.send_response(200)
1786 self.send_header('Content-Type', 'text/html')
1787 self.end_headers()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001788 self.wfile.write(html.page(title, contents))
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001789 except IOError: pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001790
1791 def do_GET(self):
1792 path = self.path
1793 if path[-5:] == '.html': path = path[:-5]
1794 if path[:1] == '/': path = path[1:]
1795 if path and path != '.':
1796 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001797 obj = locate(path, forceload=1)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001798 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001799 self.send_document(path, html.escape(str(value)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001800 return
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001801 if obj:
1802 self.send_document(describe(obj), html.document(obj, path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001803 else:
1804 self.send_document(path,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001805'no Python documentation found for %s' % repr(path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001806 else:
1807 heading = html.heading(
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001808'<big><big><strong>Python: Index of Modules</strong></big></big>',
1809'#ffffff', '#7799ee')
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001810 def bltinlink(name):
1811 return '<a href="%s.html">%s</a>' % (name, name)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001812 names = filter(lambda x: x != '__main__',
1813 sys.builtin_module_names)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001814 contents = html.multicolumn(names, bltinlink)
1815 indices = ['<p>' + html.bigsection(
1816 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1817
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001818 seen = {}
1819 for dir in pathdirs():
1820 indices.append(html.index(dir, seen))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001821 contents = heading + join(indices) + '''<p align=right>
Tim Peters2306d242001-09-25 03:18:32 +00001822<font color="#909090" face="helvetica, arial"><strong>
1823pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font>'''
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001824 self.send_document('Index of Modules', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001825
1826 def log_message(self, *args): pass
1827
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001828 class DocServer(BaseHTTPServer.HTTPServer):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001829 def __init__(self, port, callback):
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001830 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001831 self.address = ('', port)
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001832 self.url = 'http://%s:%d/' % (host, port)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001833 self.callback = callback
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001834 self.base.__init__(self, self.address, self.handler)
1835
1836 def serve_until_quit(self):
1837 import select
1838 self.quit = 0
1839 while not self.quit:
1840 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1841 if rd: self.handle_request()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001842
1843 def server_activate(self):
1844 self.base.server_activate(self)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001845 if self.callback: self.callback(self)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001846
1847 DocServer.base = BaseHTTPServer.HTTPServer
1848 DocServer.handler = DocHandler
1849 DocHandler.MessageClass = Message
1850 try:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001851 try:
1852 DocServer(port, callback).serve_until_quit()
1853 except (KeyboardInterrupt, select.error):
1854 pass
1855 finally:
Ka-Ping Yee66246962001-04-12 11:59:50 +00001856 if completer: completer()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001857
1858# ----------------------------------------------------- graphical interface
1859
1860def gui():
1861 """Graphical interface (starts web server and pops up a control window)."""
1862 class GUI:
1863 def __init__(self, window, port=7464):
1864 self.window = window
1865 self.server = None
1866 self.scanner = None
1867
1868 import Tkinter
1869 self.server_frm = Tkinter.Frame(window)
1870 self.title_lbl = Tkinter.Label(self.server_frm,
1871 text='Starting server...\n ')
1872 self.open_btn = Tkinter.Button(self.server_frm,
1873 text='open browser', command=self.open, state='disabled')
1874 self.quit_btn = Tkinter.Button(self.server_frm,
1875 text='quit serving', command=self.quit, state='disabled')
1876
1877 self.search_frm = Tkinter.Frame(window)
1878 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
1879 self.search_ent = Tkinter.Entry(self.search_frm)
1880 self.search_ent.bind('<Return>', self.search)
1881 self.stop_btn = Tkinter.Button(self.search_frm,
1882 text='stop', pady=0, command=self.stop, state='disabled')
1883 if sys.platform == 'win32':
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001884 # Trying to hide and show this button crashes under Windows.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001885 self.stop_btn.pack(side='right')
1886
1887 self.window.title('pydoc')
1888 self.window.protocol('WM_DELETE_WINDOW', self.quit)
1889 self.title_lbl.pack(side='top', fill='x')
1890 self.open_btn.pack(side='left', fill='x', expand=1)
1891 self.quit_btn.pack(side='right', fill='x', expand=1)
1892 self.server_frm.pack(side='top', fill='x')
1893
1894 self.search_lbl.pack(side='left')
1895 self.search_ent.pack(side='right', fill='x', expand=1)
1896 self.search_frm.pack(side='top', fill='x')
1897 self.search_ent.focus_set()
1898
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001899 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001900 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001901 self.result_lst.bind('<Button-1>', self.select)
1902 self.result_lst.bind('<Double-Button-1>', self.goto)
1903 self.result_scr = Tkinter.Scrollbar(window,
1904 orient='vertical', command=self.result_lst.yview)
1905 self.result_lst.config(yscrollcommand=self.result_scr.set)
1906
1907 self.result_frm = Tkinter.Frame(window)
1908 self.goto_btn = Tkinter.Button(self.result_frm,
1909 text='go to selected', command=self.goto)
1910 self.hide_btn = Tkinter.Button(self.result_frm,
1911 text='hide results', command=self.hide)
1912 self.goto_btn.pack(side='left', fill='x', expand=1)
1913 self.hide_btn.pack(side='right', fill='x', expand=1)
1914
1915 self.window.update()
1916 self.minwidth = self.window.winfo_width()
1917 self.minheight = self.window.winfo_height()
1918 self.bigminheight = (self.server_frm.winfo_reqheight() +
1919 self.search_frm.winfo_reqheight() +
1920 self.result_lst.winfo_reqheight() +
1921 self.result_frm.winfo_reqheight())
1922 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
1923 self.expanded = 0
1924 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1925 self.window.wm_minsize(self.minwidth, self.minheight)
1926
1927 import threading
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001928 threading.Thread(
1929 target=serve, args=(port, self.ready, self.quit)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001930
1931 def ready(self, server):
1932 self.server = server
1933 self.title_lbl.config(
1934 text='Python documentation server at\n' + server.url)
1935 self.open_btn.config(state='normal')
1936 self.quit_btn.config(state='normal')
1937
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001938 def open(self, event=None, url=None):
1939 url = url or self.server.url
1940 try:
1941 import webbrowser
1942 webbrowser.open(url)
1943 except ImportError: # pre-webbrowser.py compatibility
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001944 if sys.platform == 'win32':
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001945 os.system('start "%s"' % url)
1946 elif sys.platform == 'mac':
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001947 try: import ic
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001948 except ImportError: pass
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001949 else: ic.launchurl(url)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001950 else:
1951 rc = os.system('netscape -remote "openURL(%s)" &' % url)
1952 if rc: os.system('netscape "%s" &' % url)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001953
1954 def quit(self, event=None):
1955 if self.server:
1956 self.server.quit = 1
1957 self.window.quit()
1958
1959 def search(self, event=None):
1960 key = self.search_ent.get()
1961 self.stop_btn.pack(side='right')
1962 self.stop_btn.config(state='normal')
1963 self.search_lbl.config(text='Searching for "%s"...' % key)
1964 self.search_ent.forget()
1965 self.search_lbl.pack(side='left')
1966 self.result_lst.delete(0, 'end')
1967 self.goto_btn.config(state='disabled')
1968 self.expand()
1969
1970 import threading
1971 if self.scanner:
1972 self.scanner.quit = 1
1973 self.scanner = ModuleScanner()
1974 threading.Thread(target=self.scanner.run,
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +00001975 args=(self.update, key, self.done)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001976
1977 def update(self, path, modname, desc):
1978 if modname[-9:] == '.__init__':
1979 modname = modname[:-9] + ' (package)'
1980 self.result_lst.insert('end',
1981 modname + ' - ' + (desc or '(no description)'))
1982
1983 def stop(self, event=None):
1984 if self.scanner:
1985 self.scanner.quit = 1
1986 self.scanner = None
1987
1988 def done(self):
1989 self.scanner = None
1990 self.search_lbl.config(text='Search for')
1991 self.search_lbl.pack(side='left')
1992 self.search_ent.pack(side='right', fill='x', expand=1)
1993 if sys.platform != 'win32': self.stop_btn.forget()
1994 self.stop_btn.config(state='disabled')
1995
1996 def select(self, event=None):
1997 self.goto_btn.config(state='normal')
1998
1999 def goto(self, event=None):
2000 selection = self.result_lst.curselection()
2001 if selection:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002002 modname = split(self.result_lst.get(selection[0]))[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00002003 self.open(url=self.server.url + modname + '.html')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002004
2005 def collapse(self):
2006 if not self.expanded: return
2007 self.result_frm.forget()
2008 self.result_scr.forget()
2009 self.result_lst.forget()
2010 self.bigwidth = self.window.winfo_width()
2011 self.bigheight = self.window.winfo_height()
2012 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
2013 self.window.wm_minsize(self.minwidth, self.minheight)
2014 self.expanded = 0
2015
2016 def expand(self):
2017 if self.expanded: return
2018 self.result_frm.pack(side='bottom', fill='x')
2019 self.result_scr.pack(side='right', fill='y')
2020 self.result_lst.pack(side='top', fill='both', expand=1)
2021 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
2022 self.window.wm_minsize(self.minwidth, self.bigminheight)
2023 self.expanded = 1
2024
2025 def hide(self, event=None):
2026 self.stop()
2027 self.collapse()
2028
2029 import Tkinter
2030 try:
2031 gui = GUI(Tkinter.Tk())
2032 Tkinter.mainloop()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002033 except KeyboardInterrupt:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002034 pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002035
2036# -------------------------------------------------- command-line interface
2037
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002038def ispath(x):
2039 return type(x) is types.StringType and find(x, os.sep) >= 0
2040
Ka-Ping Yee1d384632001-03-01 00:24:32 +00002041def cli():
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002042 """Command-line interface (looks at sys.argv to decide what to do)."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002043 import getopt
2044 class BadUsage: pass
2045
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002046 # Scripts don't get the current directory in their path by default.
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00002047 scriptdir = os.path.dirname(sys.argv[0])
2048 if scriptdir in sys.path:
2049 sys.path.remove(scriptdir)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002050 sys.path.insert(0, '.')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002051
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002052 try:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002053 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002054 writing = 0
2055
2056 for opt, val in opts:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002057 if opt == '-g':
2058 gui()
2059 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002060 if opt == '-k':
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002061 apropos(val)
2062 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002063 if opt == '-p':
2064 try:
2065 port = int(val)
2066 except ValueError:
2067 raise BadUsage
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002068 def ready(server):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00002069 print 'pydoc server ready at %s' % server.url
2070 def stopped():
2071 print 'pydoc server stopped'
2072 serve(port, ready, stopped)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002073 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002074 if opt == '-w':
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002075 writing = 1
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002076
2077 if not args: raise BadUsage
2078 for arg in args:
2079 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002080 if ispath(arg) and os.path.isfile(arg):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002081 arg = importfile(arg)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002082 if writing:
2083 if ispath(arg) and os.path.isdir(arg):
2084 writedocs(arg)
2085 else:
2086 writedoc(arg)
2087 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00002088 doc(arg)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002089 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002090 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002091
2092 except (getopt.error, BadUsage):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002093 cmd = sys.argv[0]
2094 print """pydoc - the Python documentation tool
2095
2096%s <name> ...
2097 Show text documentation on something. <name> may be the name of a
2098 function, module, or package, or a dotted reference to a class or
2099 function within a module or module in a package. If <name> contains
2100 a '%s', it is used as the path to a Python source file to document.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002101
2102%s -k <keyword>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002103 Search for a keyword in the synopsis lines of all available modules.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002104
2105%s -p <port>
2106 Start an HTTP server on the given port on the local machine.
2107
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002108%s -g
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00002109 Pop up a graphical interface for finding and serving documentation.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002110
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002111%s -w <name> ...
2112 Write out the HTML documentation for a module to a file in the current
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00002113 directory. If <name> contains a '%s', it is treated as a filename; if
2114 it names a directory, documentation is written for all the contents.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002115""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
Ka-Ping Yee1d384632001-03-01 00:24:32 +00002116
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002117if __name__ == '__main__': cli()