blob: ecc39c596471353df178172629d04f15f3c90745 [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
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000131# ----------------------------------------------------- module manipulation
132
133def ispackage(path):
134 """Guess whether a path refers to a package directory."""
135 if os.path.isdir(path):
136 for ext in ['.py', '.pyc', '.pyo']:
137 if os.path.isfile(os.path.join(path, '__init__' + ext)):
138 return 1
139
140def synopsis(filename, cache={}):
141 """Get the one-line summary out of a module file."""
142 mtime = os.stat(filename)[stat.ST_MTIME]
143 lastupdate, result = cache.get(filename, (0, None))
144 if lastupdate < mtime:
145 info = inspect.getmoduleinfo(filename)
146 file = open(filename)
147 if info and 'b' in info[2]: # binary modules have to be imported
148 try: module = imp.load_module('__temp__', file, filename, info[1:])
149 except: return None
150 result = split(module.__doc__ or '', '\n')[0]
151 del sys.modules['__temp__']
152 else: # text modules can be directly examined
153 line = file.readline()
154 while line[:1] == '#' or not strip(line):
155 line = file.readline()
156 if not line: break
157 line = strip(line)
158 if line[:4] == 'r"""': line = line[1:]
159 if line[:3] == '"""':
160 line = line[3:]
161 if line[-1:] == '\\': line = line[:-1]
162 while not strip(line):
163 line = file.readline()
164 if not line: break
165 result = strip(split(line, '"""')[0])
166 else: result = None
167 file.close()
168 cache[filename] = (mtime, result)
169 return result
170
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000171class ErrorDuringImport(Exception):
172 """Errors that occurred while trying to import something to document it."""
173 def __init__(self, filename, (exc, value, tb)):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000174 self.filename = filename
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000175 self.exc = exc
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000176 self.value = value
177 self.tb = tb
178
179 def __str__(self):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000180 exc = self.exc
181 if type(exc) is types.ClassType:
182 exc = exc.__name__
183 return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000184
185def importfile(path):
186 """Import a Python source file or compiled file given its path."""
187 magic = imp.get_magic()
188 file = open(path, 'r')
189 if file.read(len(magic)) == magic:
190 kind = imp.PY_COMPILED
191 else:
192 kind = imp.PY_SOURCE
193 file.close()
194 filename = os.path.basename(path)
195 name, ext = os.path.splitext(filename)
196 file = open(path, 'r')
197 try:
198 module = imp.load_module(name, file, path, (ext, 'r', kind))
199 except:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000200 raise ErrorDuringImport(path, sys.exc_info())
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000201 file.close()
202 return module
203
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000204def safeimport(path, forceload=0, cache={}):
205 """Import a module; handle errors; return None if the module isn't found.
206
207 If the module *is* found but an exception occurs, it's wrapped in an
208 ErrorDuringImport exception and reraised. Unlike __import__, if a
209 package path is specified, the module at the end of the path is returned,
210 not the package at the beginning. If the optional 'forceload' argument
211 is 1, we reload the module from disk (unless it's a dynamic extension)."""
212 if forceload and sys.modules.has_key(path):
213 # This is the only way to be sure. Checking the mtime of the file
214 # isn't good enough (e.g. what if the module contains a class that
215 # inherits from another module that has changed?).
216 if path not in sys.builtin_module_names:
217 # Python never loads a dynamic extension a second time from the
218 # same path, even if the file is changed or missing. Deleting
219 # the entry in sys.modules doesn't help for dynamic extensions,
220 # so we're not even going to try to keep them up to date.
221 info = inspect.getmoduleinfo(sys.modules[path].__file__)
222 if info[3] != imp.C_EXTENSION:
223 cache[path] = sys.modules[path] # prevent module from clearing
224 del sys.modules[path]
225 try:
226 module = __import__(path)
227 except:
228 # Did the error occur before or after the module was found?
229 (exc, value, tb) = info = sys.exc_info()
230 if sys.modules.has_key(path):
231 # An error occured while executing the imported module.
232 raise ErrorDuringImport(sys.modules[path].__file__, info)
233 elif exc is SyntaxError:
234 # A SyntaxError occurred before we could execute the module.
235 raise ErrorDuringImport(value.filename, info)
236 elif exc is ImportError and \
237 split(lower(str(value)))[:2] == ['no', 'module']:
238 # The module was not found.
239 return None
240 else:
241 # Some other error occurred during the importing process.
242 raise ErrorDuringImport(path, sys.exc_info())
243 for part in split(path, '.')[1:]:
244 try: module = getattr(module, part)
245 except AttributeError: return None
246 return module
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000247
248# ---------------------------------------------------- formatter base class
249
250class Doc:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000251 def document(self, object, name=None, *args):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000252 """Generate documentation for an object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000253 args = (object, name) + args
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000254 if inspect.ismodule(object): return apply(self.docmodule, args)
255 if inspect.isclass(object): return apply(self.docclass, args)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000256 if inspect.isroutine(object): return apply(self.docroutine, args)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000257 return apply(self.docother, args)
258
259 def fail(self, object, name=None, *args):
260 """Raise an exception for unimplemented types."""
261 message = "don't know how to document object%s of type %s" % (
262 name and ' ' + repr(name), type(object).__name__)
263 raise TypeError, message
264
265 docmodule = docclass = docroutine = docother = fail
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000266
267# -------------------------------------------- HTML documentation generator
268
269class HTMLRepr(Repr):
270 """Class for safely making an HTML representation of a Python object."""
271 def __init__(self):
272 Repr.__init__(self)
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000273 self.maxlist = self.maxtuple = 20
274 self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000275 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000276
277 def escape(self, text):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000278 return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000279
280 def repr(self, object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000281 return Repr.repr(self, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000282
283 def repr1(self, x, level):
284 methodname = 'repr_' + join(split(type(x).__name__), '_')
285 if hasattr(self, methodname):
286 return getattr(self, methodname)(x, level)
287 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000288 return self.escape(cram(stripid(repr(x)), self.maxother))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000289
290 def repr_string(self, x, level):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000291 test = cram(x, self.maxstring)
292 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000293 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000294 # Backslashes are only literal in the string and are never
295 # needed to make any special characters, so show a raw string.
296 return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000297 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000298 r'<font color="#c040c0">\1</font>',
299 self.escape(testrepr))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000300
301 def repr_instance(self, x, level):
302 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000303 return self.escape(cram(stripid(repr(x)), self.maxstring))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000304 except:
305 return self.escape('<%s instance>' % x.__class__.__name__)
306
307 repr_unicode = repr_string
308
309class HTMLDoc(Doc):
310 """Formatter class for HTML documentation."""
311
312 # ------------------------------------------- HTML formatting utilities
313
314 _repr_instance = HTMLRepr()
315 repr = _repr_instance.repr
316 escape = _repr_instance.escape
317
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000318 def page(self, title, contents):
319 """Format an HTML page."""
320 return '''
321<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000322<html><head><title>Python: %s</title>
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000323<style type="text/css"><!--
Ka-Ping Yeed03f8fe2001-04-13 15:04:32 +0000324TT { font-family: lucidatypewriter, lucida console, courier }
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000325--></style></head><body bgcolor="#f0f0f8">
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000326%s
327</body></html>''' % (title, contents)
328
329 def heading(self, title, fgcol, bgcol, extras=''):
330 """Format a page heading."""
331 return '''
332<table width="100%%" cellspacing=0 cellpadding=2 border=0>
333<tr bgcolor="%s">
334<td valign=bottom><small>&nbsp;<br></small
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000335><font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000336><td align=right valign=bottom
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000337><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000338 ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
339
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000340 def section(self, title, fgcol, bgcol, contents, width=10,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000341 prelude='', marginalia=None, gap='&nbsp;&nbsp;'):
342 """Format a section with a heading."""
343 if marginalia is None:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000344 marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000345 result = '''
346<p><table width="100%%" cellspacing=0 cellpadding=2 border=0>
347<tr bgcolor="%s">
348<td colspan=3 valign=bottom><small><small>&nbsp;<br></small></small
349><font color="%s" face="helvetica, arial">%s</font></td></tr>
350 ''' % (bgcol, fgcol, title)
351 if prelude:
352 result = result + '''
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000353<tr bgcolor="%s"><td rowspan=2>%s</td>
354<td colspan=2>%s</td></tr>
355<tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
356 else:
357 result = result + '''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000358<tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000359
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000360 return result + '\n<td width="100%%">%s</td></tr></table>' % contents
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000361
362 def bigsection(self, title, *args):
363 """Format a section with a big heading."""
364 title = '<big><strong>%s</strong></big>' % title
365 return apply(self.section, (title,) + args)
366
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000367 def preformat(self, text):
368 """Format literal preformatted text."""
369 text = self.escape(expandtabs(text))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000370 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
371 ' ', '&nbsp;', '\n', '<br>\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000372
373 def multicolumn(self, list, format, cols=4):
374 """Format a list of items into a multi-column list."""
375 result = ''
376 rows = (len(list)+cols-1)/cols
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000377 for col in range(cols):
378 result = result + '<td width="%d%%" valign=top>' % (100/cols)
379 for i in range(rows*col, rows*col+rows):
380 if i < len(list):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000381 result = result + format(list[i]) + '<br>\n'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000382 result = result + '</td>'
383 return '<table width="100%%"><tr>%s</tr></table>' % result
384
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000385 def small(self, text): return '<small>%s</small>' % text
386 def grey(self, text): return '<font color="#909090">%s</font>' % text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000387
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000388 def namelink(self, name, *dicts):
389 """Make a link for an identifier, given name-to-URL mappings."""
390 for dict in dicts:
391 if dict.has_key(name):
392 return '<a href="%s">%s</a>' % (dict[name], name)
393 return name
394
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000395 def classlink(self, object, modname):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000396 """Make a link for a class."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000397 name, module = object.__name__, sys.modules.get(object.__module__)
398 if hasattr(module, name) and getattr(module, name) is object:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000399 return '<a href="%s.html#%s">%s</a>' % (
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000400 module.__name__, name, classname(object, modname))
401 return classname(object, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000402
403 def modulelink(self, object):
404 """Make a link for a module."""
405 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
406
407 def modpkglink(self, (name, path, ispackage, shadowed)):
408 """Make a link for a module or package to display in an index."""
409 if shadowed:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000410 return self.grey(name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000411 if path:
412 url = '%s.%s.html' % (path, name)
413 else:
414 url = '%s.html' % name
415 if ispackage:
416 text = '<strong>%s</strong>&nbsp;(package)' % name
417 else:
418 text = name
419 return '<a href="%s">%s</a>' % (url, text)
420
421 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
422 """Mark up some plain text, given a context of symbols to look for.
423 Each context dictionary maps object names to anchor names."""
424 escape = escape or self.escape
425 results = []
426 here = 0
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000427 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
428 r'RFC[- ]?(\d+)|'
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000429 r'PEP[- ]?(\d+)|'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000430 r'(self\.)?(\w+))\b')
431 while 1:
432 match = pattern.search(text, here)
433 if not match: break
434 start, end = match.span()
435 results.append(escape(text[here:start]))
436
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000437 all, scheme, rfc, pep, selfdot, name = match.groups()
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000438 if scheme:
439 results.append('<a href="%s">%s</a>' % (all, escape(all)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000440 elif rfc:
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000441 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
442 results.append('<a href="%s">%s</a>' % (url, escape(all)))
443 elif pep:
444 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000445 results.append('<a href="%s">%s</a>' % (url, escape(all)))
446 elif text[end:end+1] == '(':
447 results.append(self.namelink(name, methods, funcs, classes))
448 elif selfdot:
449 results.append('self.<strong>%s</strong>' % name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000450 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000451 results.append(self.namelink(name, classes))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000452 here = end
453 results.append(escape(text[here:]))
454 return join(results, '')
455
456 # ---------------------------------------------- type-specific routines
457
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000458 def formattree(self, tree, modname, parent=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000459 """Produce HTML for a class tree as given by inspect.getclasstree()."""
460 result = ''
461 for entry in tree:
462 if type(entry) is type(()):
463 c, bases = entry
464 result = result + '<dt><font face="helvetica, arial"><small>'
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000465 result = result + self.classlink(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000466 if bases and bases != (parent,):
467 parents = []
468 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000469 parents.append(self.classlink(base, modname))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000470 result = result + '(' + join(parents, ', ') + ')'
471 result = result + '\n</small></font></dt>'
472 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000473 result = result + '<dd>\n%s</dd>\n' % self.formattree(
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000474 entry, modname, c)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000475 return '<dl>\n%s</dl>\n' % result
476
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000477 def docmodule(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000478 """Produce HTML documentation for a module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000479 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000480 parts = split(name, '.')
481 links = []
482 for i in range(len(parts)-1):
483 links.append(
484 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
485 (join(parts[:i+1], '.'), parts[i]))
486 linkedname = join(links + parts[-1:], '.')
487 head = '<big><big><strong>%s</strong></big></big>' % linkedname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000488 try:
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000489 path = inspect.getabsfile(object)
Ka-Ping Yee6191a232001-04-13 15:00:27 +0000490 url = path
491 if sys.platform == 'win32':
492 import nturl2path
493 url = nturl2path.pathname2url(path)
494 filelink = '<a href="file:%s">%s</a>' % (url, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000495 except TypeError:
496 filelink = '(built-in)'
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000497 info = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000498 if hasattr(object, '__version__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000499 version = str(object.__version__)
Ka-Ping Yee40c49912001-02-27 22:46:01 +0000500 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
501 version = strip(version[11:-1])
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000502 info.append('version %s' % self.escape(version))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000503 if hasattr(object, '__date__'):
504 info.append(self.escape(str(object.__date__)))
505 if info:
506 head = head + ' (%s)' % join(info, ', ')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000507 result = self.heading(
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000508 head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)
509
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000510 modules = inspect.getmembers(object, inspect.ismodule)
511
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000512 classes, cdict = [], {}
513 for key, value in inspect.getmembers(object, inspect.isclass):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000514 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000515 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000516 cdict[key] = cdict[value] = '#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000517 for key, value in classes:
518 for base in value.__bases__:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000519 key, modname = base.__name__, base.__module__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000520 module = sys.modules.get(modname)
521 if modname != name and module and hasattr(module, key):
522 if getattr(module, key) is base:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000523 if not cdict.has_key(key):
524 cdict[key] = cdict[base] = modname + '.html#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000525 funcs, fdict = [], {}
526 for key, value in inspect.getmembers(object, inspect.isroutine):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000527 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000528 funcs.append((key, value))
529 fdict[key] = '#-' + key
530 if inspect.isfunction(value): fdict[value] = fdict[key]
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000531 data = []
532 for key, value in inspect.getmembers(object, isdata):
533 if key not in ['__builtins__', '__doc__']:
534 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000535
536 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
537 doc = doc and '<tt>%s</tt>' % doc
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000538 result = result + '<p>%s</p>\n' % self.small(doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000539
540 if hasattr(object, '__path__'):
541 modpkgs = []
542 modnames = []
543 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000544 path = os.path.join(object.__path__[0], file)
545 modname = inspect.getmodulename(file)
546 if modname and modname not in modnames:
547 modpkgs.append((modname, name, 0, 0))
548 modnames.append(modname)
549 elif ispackage(path):
550 modpkgs.append((file, name, 1, 0))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000551 modpkgs.sort()
552 contents = self.multicolumn(modpkgs, self.modpkglink)
553 result = result + self.bigsection(
554 'Package Contents', '#ffffff', '#aa55cc', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000555 elif modules:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000556 contents = self.multicolumn(
557 modules, lambda (key, value), s=self: s.modulelink(value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000558 result = result + self.bigsection(
559 'Modules', '#fffff', '#aa55cc', contents)
560
561 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000562 classlist = map(lambda (key, value): value, classes)
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000563 contents = [
564 self.formattree(inspect.getclasstree(classlist, 1), name)]
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000565 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000566 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000567 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000568 'Classes', '#ffffff', '#ee77aa', join(contents))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000569 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000570 contents = []
571 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000572 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000573 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000574 'Functions', '#ffffff', '#eeaa77', join(contents))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000575 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000576 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000577 for key, value in data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000578 contents.append(self.document(value, key))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000579 result = result + self.bigsection(
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000580 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000581 if hasattr(object, '__author__'):
582 contents = self.markup(str(object.__author__), self.preformat)
583 result = result + self.bigsection(
584 'Author', '#ffffff', '#7799ee', contents)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000585 if hasattr(object, '__credits__'):
586 contents = self.markup(str(object.__credits__), self.preformat)
587 result = result + self.bigsection(
588 'Credits', '#ffffff', '#7799ee', contents)
589
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000590 return result
591
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000592 def docclass(self, object, name=None, mod=None, funcs={}, classes={}):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000593 """Produce HTML documentation for a class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000594 realname = object.__name__
595 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000596 bases = object.__bases__
597 contents = ''
598
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000599 methods, mdict = allmethods(object).items(), {}
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000600 methods.sort()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000601 for key, value in methods:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000602 mdict[key] = mdict[value] = '#' + name + '-' + key
603 for key, value in methods:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000604 contents = contents + self.document(
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000605 value, key, mod, funcs, classes, mdict, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000606
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000607 if name == realname:
608 title = '<a name="%s">class <strong>%s</strong></a>' % (
609 name, realname)
610 else:
611 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
612 name, name, realname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000613 if bases:
614 parents = []
615 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000616 parents.append(self.classlink(base, object.__module__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000617 title = title + '(%s)' % join(parents, ', ')
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000618 doc = self.markup(
619 getdoc(object), self.preformat, funcs, classes, mdict)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000620 doc = self.small(doc and '<tt>%s<br>&nbsp;</tt>' % doc or
621 self.small('&nbsp;'))
622 return self.section(title, '#000000', '#ffc8d8', contents, 5, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000623
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000624 def formatvalue(self, object):
625 """Format an argument default value as text."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000626 return self.small(self.grey('=' + self.repr(object)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000627
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000628 def docroutine(self, object, name=None, mod=None,
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000629 funcs={}, classes={}, methods={}, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000630 """Produce HTML documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000631 realname = object.__name__
632 name = name or realname
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000633 anchor = (cl and cl.__name__ or '') + '-' + name
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000634 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000635 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000636 if inspect.ismethod(object):
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +0000637 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000638 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000639 if imclass is not cl:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000640 note = ' from ' + self.classlink(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000641 skipdocs = 1
642 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000643 if object.im_self:
644 note = ' method of %s instance' % self.classlink(
645 object.im_self.__class__, mod)
646 else:
647 note = ' unbound %s method' % self.classlink(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000648 object = object.im_func
649
650 if name == realname:
651 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
652 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000653 if (cl and cl.__dict__.has_key(realname) and
654 cl.__dict__[realname] is object):
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000655 reallink = '<a href="#%s">%s</a>' % (
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000656 cl.__name__ + '-' + realname, realname)
657 skipdocs = 1
658 else:
659 reallink = realname
660 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
661 anchor, name, reallink)
Tim Peters4bcfa312001-09-20 06:08:24 +0000662 if inspect.isfunction(object):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000663 args, varargs, varkw, defaults = inspect.getargspec(object)
664 argspec = inspect.formatargspec(
665 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000666 if realname == '<lambda>':
667 decl = '<em>lambda</em>'
668 argspec = argspec[1:-1] # remove parentheses
Tim Peters4bcfa312001-09-20 06:08:24 +0000669 else:
670 argspec = '(...)'
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000671
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000672 decl = title + argspec + (note and self.small(self.grey(
673 '<font face="helvetica, arial">%s</font>' % note)))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000674
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000675 if skipdocs:
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000676 return '<dl><dt>%s</dl>\n' % decl
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000677 else:
678 doc = self.markup(
679 getdoc(object), self.preformat, funcs, classes, methods)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000680 doc = doc and '<dd>' + self.small('<tt>%s</tt>' % doc)
681 return '<dl><dt>%s%s</dl>\n' % (decl, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000682
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000683 def docother(self, object, name=None, mod=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000684 """Produce HTML documentation for a data object."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000685 lhs = name and '<strong>%s</strong> = ' % name or ''
686 return lhs + self.repr(object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000687
688 def index(self, dir, shadowed=None):
689 """Generate an HTML index for a directory of modules."""
690 modpkgs = []
691 if shadowed is None: shadowed = {}
692 seen = {}
693 files = os.listdir(dir)
694
695 def found(name, ispackage,
696 modpkgs=modpkgs, shadowed=shadowed, seen=seen):
697 if not seen.has_key(name):
698 modpkgs.append((name, '', ispackage, shadowed.has_key(name)))
699 seen[name] = 1
700 shadowed[name] = 1
701
702 # Package spam/__init__.py takes precedence over module spam.py.
703 for file in files:
704 path = os.path.join(dir, file)
705 if ispackage(path): found(file, 1)
706 for file in files:
707 path = os.path.join(dir, file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000708 if os.path.isfile(path):
709 modname = inspect.getmodulename(file)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000710 if modname: found(modname, 0)
711
712 modpkgs.sort()
713 contents = self.multicolumn(modpkgs, self.modpkglink)
714 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
715
716# -------------------------------------------- text documentation generator
717
718class TextRepr(Repr):
719 """Class for safely making a text representation of a Python object."""
720 def __init__(self):
721 Repr.__init__(self)
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000722 self.maxlist = self.maxtuple = 20
723 self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000724 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000725
726 def repr1(self, x, level):
727 methodname = 'repr_' + join(split(type(x).__name__), '_')
728 if hasattr(self, methodname):
729 return getattr(self, methodname)(x, level)
730 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000731 return cram(stripid(repr(x)), self.maxother)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000732
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000733 def repr_string(self, x, level):
734 test = cram(x, self.maxstring)
735 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000736 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000737 # Backslashes are only literal in the string and are never
738 # needed to make any special characters, so show a raw string.
739 return 'r' + testrepr[0] + test + testrepr[0]
740 return testrepr
741
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000742 def repr_instance(self, x, level):
743 try:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000744 return cram(stripid(repr(x)), self.maxstring)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000745 except:
746 return '<%s instance>' % x.__class__.__name__
747
748class TextDoc(Doc):
749 """Formatter class for text documentation."""
750
751 # ------------------------------------------- text formatting utilities
752
753 _repr_instance = TextRepr()
754 repr = _repr_instance.repr
755
756 def bold(self, text):
757 """Format a string in bold by overstriking."""
758 return join(map(lambda ch: ch + '\b' + ch, text), '')
759
760 def indent(self, text, prefix=' '):
761 """Indent text by prepending a given prefix to each line."""
762 if not text: return ''
763 lines = split(text, '\n')
764 lines = map(lambda line, prefix=prefix: prefix + line, lines)
765 if lines: lines[-1] = rstrip(lines[-1])
766 return join(lines, '\n')
767
768 def section(self, title, contents):
769 """Format a section with a given heading."""
770 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
771
772 # ---------------------------------------------- type-specific routines
773
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000774 def formattree(self, tree, modname, parent=None, prefix=''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000775 """Render in text a class tree as returned by inspect.getclasstree()."""
776 result = ''
777 for entry in tree:
778 if type(entry) is type(()):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000779 c, bases = entry
780 result = result + prefix + classname(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000781 if bases and bases != (parent,):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000782 parents = map(lambda c, m=modname: classname(c, m), bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000783 result = result + '(%s)' % join(parents, ', ')
784 result = result + '\n'
785 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000786 result = result + self.formattree(
787 entry, modname, c, prefix + ' ')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000788 return result
789
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000790 def docmodule(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000791 """Produce text documentation for a given module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000792 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000793 synop, desc = splitdoc(getdoc(object))
794 result = self.section('NAME', name + (synop and ' - ' + synop))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000795
796 try:
797 file = inspect.getabsfile(object)
798 except TypeError:
799 file = '(built-in)'
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000800 result = result + self.section('FILE', file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000801 if desc:
802 result = result + self.section('DESCRIPTION', desc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000803
804 classes = []
805 for key, value in inspect.getmembers(object, inspect.isclass):
806 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000807 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000808 funcs = []
809 for key, value in inspect.getmembers(object, inspect.isroutine):
810 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000811 funcs.append((key, value))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000812 data = []
813 for key, value in inspect.getmembers(object, isdata):
814 if key not in ['__builtins__', '__doc__']:
815 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000816
817 if hasattr(object, '__path__'):
818 modpkgs = []
819 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000820 path = os.path.join(object.__path__[0], file)
821 modname = inspect.getmodulename(file)
822 if modname and modname not in modpkgs:
823 modpkgs.append(modname)
824 elif ispackage(path):
825 modpkgs.append(file + ' (package)')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000826 modpkgs.sort()
827 result = result + self.section(
828 'PACKAGE CONTENTS', join(modpkgs, '\n'))
829
830 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000831 classlist = map(lambda (key, value): value, classes)
832 contents = [self.formattree(
833 inspect.getclasstree(classlist, 1), name)]
834 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000835 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000836 result = result + self.section('CLASSES', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000837
838 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000839 contents = []
840 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000841 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000842 result = result + self.section('FUNCTIONS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000843
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000844 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000845 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000846 for key, value in data:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000847 contents.append(self.docother(value, key, name, 70))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000848 result = result + self.section('DATA', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000849
850 if hasattr(object, '__version__'):
851 version = str(object.__version__)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000852 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
853 version = strip(version[11:-1])
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000854 result = result + self.section('VERSION', version)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000855 if hasattr(object, '__date__'):
856 result = result + self.section('DATE', str(object.__date__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000857 if hasattr(object, '__author__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000858 result = result + self.section('AUTHOR', str(object.__author__))
859 if hasattr(object, '__credits__'):
860 result = result + self.section('CREDITS', str(object.__credits__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000861 return result
862
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000863 def docclass(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000864 """Produce text documentation for a given class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000865 realname = object.__name__
866 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000867 bases = object.__bases__
868
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000869 if name == realname:
870 title = 'class ' + self.bold(realname)
871 else:
872 title = self.bold(name) + ' = class ' + realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000873 if bases:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000874 def makename(c, m=object.__module__): return classname(c, m)
875 parents = map(makename, bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000876 title = title + '(%s)' % join(parents, ', ')
877
878 doc = getdoc(object)
879 contents = doc and doc + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000880 methods = allmethods(object).items()
881 methods.sort()
882 for key, value in methods:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000883 contents = contents + '\n' + self.document(value, key, mod, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000884
885 if not contents: return title + '\n'
886 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
887
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000888 def formatvalue(self, object):
889 """Format an argument default value as text."""
890 return '=' + self.repr(object)
891
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000892 def docroutine(self, object, name=None, mod=None, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000893 """Produce text documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000894 realname = object.__name__
895 name = name or realname
896 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000897 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000898 if inspect.ismethod(object):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000899 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000900 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000901 if imclass is not cl:
902 note = ' from ' + classname(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000903 skipdocs = 1
904 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000905 if object.im_self:
906 note = ' method of %s instance' % classname(
907 object.im_self.__class__, mod)
908 else:
909 note = ' unbound %s method' % classname(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000910 object = object.im_func
911
912 if name == realname:
913 title = self.bold(realname)
914 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000915 if (cl and cl.__dict__.has_key(realname) and
916 cl.__dict__[realname] is object):
917 skipdocs = 1
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000918 title = self.bold(name) + ' = ' + realname
Tim Peters4bcfa312001-09-20 06:08:24 +0000919 if inspect.isfunction(object):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000920 args, varargs, varkw, defaults = inspect.getargspec(object)
921 argspec = inspect.formatargspec(
922 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000923 if realname == '<lambda>':
924 title = 'lambda'
925 argspec = argspec[1:-1] # remove parentheses
Tim Peters4bcfa312001-09-20 06:08:24 +0000926 else:
927 argspec = '(...)'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000928 decl = title + argspec + note
929
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000930 if skipdocs:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000931 return decl + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000932 else:
933 doc = getdoc(object) or ''
934 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000935
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000936 def docother(self, object, name=None, mod=None, maxlen=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000937 """Produce text documentation for a data object."""
938 repr = self.repr(object)
939 if maxlen:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000940 line = (name and name + ' = ' or '') + repr
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000941 chop = maxlen - len(line)
942 if chop < 0: repr = repr[:chop] + '...'
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000943 line = (name and self.bold(name) + ' = ' or '') + repr
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000944 return line
945
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000946# --------------------------------------------------------- user interfaces
947
948def pager(text):
949 """The first time this is called, determine what kind of pager to use."""
950 global pager
951 pager = getpager()
952 pager(text)
953
954def getpager():
955 """Decide what method to use for paging through text."""
956 if type(sys.stdout) is not types.FileType:
957 return plainpager
958 if not sys.stdin.isatty() or not sys.stdout.isatty():
959 return plainpager
Fred Drake0a66fcb2001-07-23 19:44:30 +0000960 if os.environ.get('TERM') in ['dumb', 'emacs']:
Fred Drake5e9eb982001-07-23 19:48:10 +0000961 return plainpager
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000962 if os.environ.has_key('PAGER'):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000963 if sys.platform == 'win32': # pipes completely broken in Windows
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000964 return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
965 elif os.environ.get('TERM') in ['dumb', 'emacs']:
966 return lambda text: pipepager(plain(text), os.environ['PAGER'])
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000967 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000968 return lambda text: pipepager(text, os.environ['PAGER'])
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +0000969 if sys.platform == 'win32':
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000970 return lambda text: tempfilepager(plain(text), 'more <')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000971 if hasattr(os, 'system') and os.system('less 2>/dev/null') == 0:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000972 return lambda text: pipepager(text, 'less')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000973
974 import tempfile
975 filename = tempfile.mktemp()
976 open(filename, 'w').close()
977 try:
978 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
979 return lambda text: pipepager(text, 'more')
980 else:
981 return ttypager
982 finally:
983 os.unlink(filename)
984
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000985def plain(text):
986 """Remove boldface formatting from text."""
987 return re.sub('.\b', '', text)
988
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000989def pipepager(text, cmd):
990 """Page through text by feeding it to another program."""
991 pipe = os.popen(cmd, 'w')
992 try:
993 pipe.write(text)
994 pipe.close()
995 except IOError:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +0000996 pass # Ignore broken pipes caused by quitting the pager program.
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000997
998def tempfilepager(text, cmd):
999 """Page through text by invoking a program on a temporary file."""
1000 import tempfile
1001 filename = tempfile.mktemp()
1002 file = open(filename, 'w')
1003 file.write(text)
1004 file.close()
1005 try:
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001006 os.system(cmd + ' ' + filename)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001007 finally:
1008 os.unlink(filename)
1009
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001010def ttypager(text):
1011 """Page through text on a text terminal."""
1012 lines = split(plain(text), '\n')
1013 try:
1014 import tty
1015 fd = sys.stdin.fileno()
1016 old = tty.tcgetattr(fd)
1017 tty.setcbreak(fd)
1018 getchar = lambda: sys.stdin.read(1)
Ka-Ping Yee457aab22001-02-27 23:36:29 +00001019 except (ImportError, AttributeError):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001020 tty = None
1021 getchar = lambda: sys.stdin.readline()[:-1][:1]
1022
1023 try:
1024 r = inc = os.environ.get('LINES', 25) - 1
1025 sys.stdout.write(join(lines[:inc], '\n') + '\n')
1026 while lines[r:]:
1027 sys.stdout.write('-- more --')
1028 sys.stdout.flush()
1029 c = getchar()
1030
1031 if c in ['q', 'Q']:
1032 sys.stdout.write('\r \r')
1033 break
1034 elif c in ['\r', '\n']:
1035 sys.stdout.write('\r \r' + lines[r] + '\n')
1036 r = r + 1
1037 continue
1038 if c in ['b', 'B', '\x1b']:
1039 r = r - inc - inc
1040 if r < 0: r = 0
1041 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1042 r = r + inc
1043
1044 finally:
1045 if tty:
1046 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1047
1048def plainpager(text):
1049 """Simply print unformatted text. This is the ultimate fallback."""
1050 sys.stdout.write(plain(text))
1051
1052def describe(thing):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001053 """Produce a short description of the given thing."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001054 if inspect.ismodule(thing):
1055 if thing.__name__ in sys.builtin_module_names:
1056 return 'built-in module ' + thing.__name__
1057 if hasattr(thing, '__path__'):
1058 return 'package ' + thing.__name__
1059 else:
1060 return 'module ' + thing.__name__
1061 if inspect.isbuiltin(thing):
1062 return 'built-in function ' + thing.__name__
1063 if inspect.isclass(thing):
1064 return 'class ' + thing.__name__
1065 if inspect.isfunction(thing):
1066 return 'function ' + thing.__name__
1067 if inspect.ismethod(thing):
1068 return 'method ' + thing.__name__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001069 if type(thing) is types.InstanceType:
1070 return 'instance of ' + thing.__class__.__name__
1071 return type(thing).__name__
1072
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001073def locate(path, forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001074 """Locate an object by name or dotted path, importing as necessary."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001075 parts = split(path, '.')
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001076 module, n = None, 0
1077 while n < len(parts):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001078 nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001079 if nextmodule: module, n = nextmodule, n + 1
1080 else: break
1081 if module:
1082 object = module
1083 for part in parts[n:]:
1084 try: object = getattr(object, part)
1085 except AttributeError: return None
1086 return object
1087 else:
1088 import __builtin__
1089 if hasattr(__builtin__, path):
1090 return getattr(__builtin__, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001091
1092# --------------------------------------- interactive interpreter interface
1093
1094text = TextDoc()
1095html = HTMLDoc()
1096
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001097def doc(thing, title='Python Library Documentation: %s', forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001098 """Display text documentation, given an object or a path to an object."""
1099 suffix, name = '', None
1100 if type(thing) is type(''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001101 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001102 object = locate(thing, forceload)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001103 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001104 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001105 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001106 if not object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001107 print 'no Python documentation found for %s' % repr(thing)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001108 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001109 parts = split(thing, '.')
1110 if len(parts) > 1: suffix = ' in ' + join(parts[:-1], '.')
1111 name = parts[-1]
1112 thing = object
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001113
1114 desc = describe(thing)
1115 module = inspect.getmodule(thing)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001116 if not suffix and module and module is not thing:
1117 suffix = ' in module ' + module.__name__
Ka-Ping Yee66246962001-04-12 11:59:50 +00001118 pager(title % (desc + suffix) + '\n\n' + text.document(thing, name))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001119
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001120def writedoc(key, forceload=0):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001121 """Write HTML documentation to a file in the current directory."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001122 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001123 object = locate(key, forceload)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001124 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001125 print value
1126 else:
1127 if object:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001128 page = html.page(describe(object),
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001129 html.document(object, object.__name__))
1130 file = open(key + '.html', 'w')
1131 file.write(page)
1132 file.close()
1133 print 'wrote', key + '.html'
1134 else:
1135 print 'no Python documentation found for %s' % repr(key)
1136
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001137def writedocs(dir, pkgpath='', done=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001138 """Write out HTML documentation for all modules in a directory tree."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001139 if done is None: done = {}
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001140 for file in os.listdir(dir):
1141 path = os.path.join(dir, file)
1142 if ispackage(path):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001143 writedocs(path, pkgpath + file + '.', done)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001144 elif os.path.isfile(path):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001145 modname = inspect.getmodulename(path)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001146 if modname:
1147 modname = pkgpath + modname
1148 if not done.has_key(modname):
1149 done[modname] = 1
1150 writedoc(modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001151
1152class Helper:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001153 keywords = {
1154 'and': 'BOOLEAN',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001155 'assert': ('ref/assert', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001156 'break': ('ref/break', 'while for'),
1157 'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1158 'continue': ('ref/continue', 'while for'),
1159 'def': ('ref/function', ''),
1160 'del': ('ref/del', 'BASICMETHODS'),
1161 'elif': 'if',
1162 'else': ('ref/if', 'while for'),
1163 'except': 'try',
1164 'exec': ('ref/exec', ''),
1165 'finally': 'try',
1166 'for': ('ref/for', 'break continue while'),
1167 'from': 'import',
1168 'global': ('ref/global', 'NAMESPACES'),
1169 'if': ('ref/if', 'TRUTHVALUE'),
1170 'import': ('ref/import', 'MODULES'),
1171 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1172 'is': 'COMPARISON',
1173 'lambda': ('ref/lambda', 'FUNCTIONS'),
1174 'not': 'BOOLEAN',
1175 'or': 'BOOLEAN',
1176 'pass': 'PASS',
1177 'print': ('ref/print', ''),
1178 'raise': ('ref/raise', 'EXCEPTIONS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001179 'return': ('ref/return', 'FUNCTIONS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001180 'try': ('ref/try', 'EXCEPTIONS'),
1181 'while': ('ref/while', 'break continue if TRUTHVALUE'),
1182 }
1183
1184 topics = {
1185 'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001186 'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001187 'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'),
1188 'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001189 'UNICODE': ('ref/unicode', 'encodings unicode TYPES STRING'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001190 'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1191 'INTEGER': ('ref/integers', 'int range'),
1192 'FLOAT': ('ref/floating', 'float math'),
1193 'COMPLEX': ('ref/imaginary', 'complex cmath'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001194 'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001195 'MAPPINGS': 'DICTIONARIES',
1196 'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'),
1197 'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'),
1198 'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001199 'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001200 'FRAMEOBJECTS': 'TYPES',
1201 'TRACEBACKS': 'TYPES',
1202 'NONE': ('lib/bltin-null-object', ''),
1203 'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'),
1204 'FILES': ('lib/bltin-file-objects', ''),
1205 'SPECIALATTRIBUTES': ('lib/specialattrs', ''),
1206 'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'),
1207 'MODULES': ('lib/typesmodules', 'import'),
1208 'PACKAGES': 'import',
1209 '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'),
1210 'OPERATORS': 'EXPRESSIONS',
1211 'PRECEDENCE': 'EXPRESSIONS',
1212 'OBJECTS': ('ref/objects', 'TYPES'),
1213 'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001214 'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'),
1215 'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1216 'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'),
1217 'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'),
1218 'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'),
1219 'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'),
1220 'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001221 'EXECUTION': ('ref/execframes', ''),
1222 'NAMESPACES': ('ref/execframes', 'global ASSIGNMENT DELETION'),
1223 'SCOPING': 'NAMESPACES',
1224 'FRAMES': 'NAMESPACES',
1225 'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'),
1226 'COERCIONS': 'CONVERSIONS',
1227 'CONVERSIONS': ('ref/conversions', ''),
1228 'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'),
1229 'SPECIALIDENTIFIERS': ('ref/id-classes', ''),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001230 'PRIVATENAMES': ('ref/atom-identifiers', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001231 'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1232 'TUPLES': 'SEQUENCES',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001233 'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001234 'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001235 'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001236 'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001237 'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'),
1238 'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001239 'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1240 'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'),
1241 'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'),
1242 'CALLS': ('ref/calls', 'EXPRESSIONS'),
1243 'POWER': ('ref/power', 'EXPRESSIONS'),
1244 'UNARY': ('ref/unary', 'EXPRESSIONS'),
1245 'BINARY': ('ref/binary', 'EXPRESSIONS'),
1246 'SHIFTING': ('ref/shifting', 'EXPRESSIONS'),
1247 'BITWISE': ('ref/bitwise', 'EXPRESSIONS'),
1248 'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001249 'BOOLEAN': ('ref/lambda', 'EXPRESSIONS TRUTHVALUE'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001250 'ASSERTION': 'assert',
1251 'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001252 'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001253 'DELETION': 'del',
1254 'PRINTING': 'print',
1255 'RETURNING': 'return',
1256 'IMPORTING': 'import',
1257 'CONDITIONAL': 'if',
1258 'LOOPING': ('ref/compound', 'for while break continue'),
1259 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001260 'DEBUGGING': ('lib/module-pdb', 'pdb'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001261 }
1262
1263 def __init__(self, input, output):
1264 self.input = input
1265 self.output = output
1266 self.docdir = None
1267 execdir = os.path.dirname(sys.executable)
1268 homedir = os.environ.get('PYTHONHOME')
1269 for dir in [os.environ.get('PYTHONDOCS'),
1270 homedir and os.path.join(homedir, 'doc'),
1271 os.path.join(execdir, 'doc'),
1272 '/usr/doc/python-docs-' + split(sys.version)[0],
1273 '/usr/doc/python-' + split(sys.version)[0],
1274 '/usr/doc/python-docs-' + sys.version[:3],
1275 '/usr/doc/python-' + sys.version[:3]]:
1276 if dir and os.path.isdir(os.path.join(dir, 'lib')):
1277 self.docdir = dir
1278
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001279 def __repr__(self):
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001280 if inspect.stack()[1][3] == '?':
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001281 self()
1282 return ''
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001283 return '<pydoc.Helper instance>'
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001284
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001285 def __call__(self, request=None):
1286 if request is not None:
1287 self.help(request)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001288 else:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001289 self.intro()
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001290 self.interact()
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001291 self.output.write('''
Fred Drakee61967f2001-05-10 18:41:02 +00001292You are now leaving help and returning to the Python interpreter.
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001293If you want to ask for help on a particular object directly from the
1294interpreter, you can type "help(object)". Executing "help('string')"
1295has the same effect as typing a particular string at the help> prompt.
1296''')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001297
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001298 def interact(self):
1299 self.output.write('\n')
1300 while 1:
1301 self.output.write('help> ')
1302 self.output.flush()
1303 try:
1304 request = self.input.readline()
1305 if not request: break
1306 except KeyboardInterrupt: break
1307 request = strip(replace(request, '"', '', "'", ''))
1308 if lower(request) in ['q', 'quit']: break
1309 self.help(request)
1310
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001311 def help(self, request):
1312 if type(request) is type(''):
1313 if request == 'help': self.intro()
1314 elif request == 'keywords': self.listkeywords()
1315 elif request == 'topics': self.listtopics()
1316 elif request == 'modules': self.listmodules()
1317 elif request[:8] == 'modules ':
1318 self.listmodules(split(request)[1])
1319 elif self.keywords.has_key(request): self.showtopic(request)
1320 elif self.topics.has_key(request): self.showtopic(request)
1321 elif request: doc(request, 'Help on %s:')
1322 elif isinstance(request, Helper): self()
1323 else: doc(request, 'Help on %s:')
1324 self.output.write('\n')
1325
1326 def intro(self):
1327 self.output.write('''
1328Welcome to Python %s! This is the online help utility.
1329
1330If this is your first time using Python, you should definitely check out
1331the tutorial on the Internet at http://www.python.org/doc/tut/.
1332
1333Enter the name of any module, keyword, or topic to get help on writing
1334Python programs and using Python modules. To quit this help utility and
1335return to the interpreter, just type "quit".
1336
1337To get a list of available modules, keywords, or topics, type "modules",
1338"keywords", or "topics". Each module also comes with a one-line summary
1339of what it does; to list the modules whose summaries contain a given word
1340such as "spam", type "modules spam".
1341''' % sys.version[:3])
1342
1343 def list(self, items, columns=4, width=80):
1344 items = items[:]
1345 items.sort()
1346 colw = width / columns
1347 rows = (len(items) + columns - 1) / columns
1348 for row in range(rows):
1349 for col in range(columns):
1350 i = col * rows + row
1351 if i < len(items):
1352 self.output.write(items[i])
1353 if col < columns - 1:
1354 self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1355 self.output.write('\n')
1356
1357 def listkeywords(self):
1358 self.output.write('''
1359Here is a list of the Python keywords. Enter any keyword to get more help.
1360
1361''')
1362 self.list(self.keywords.keys())
1363
1364 def listtopics(self):
1365 self.output.write('''
1366Here is a list of available topics. Enter any topic name to get more help.
1367
1368''')
1369 self.list(self.topics.keys())
1370
1371 def showtopic(self, topic):
1372 if not self.docdir:
1373 self.output.write('''
1374Sorry, topic and keyword documentation is not available because the Python
1375HTML documentation files could not be found. If you have installed them,
1376please set the environment variable PYTHONDOCS to indicate their location.
1377''')
1378 return
1379 target = self.topics.get(topic, self.keywords.get(topic))
1380 if not target:
1381 self.output.write('no documentation found for %s\n' % repr(topic))
1382 return
1383 if type(target) is type(''):
1384 return self.showtopic(target)
1385
1386 filename, xrefs = target
1387 filename = self.docdir + '/' + filename + '.html'
1388 try:
1389 file = open(filename)
1390 except:
1391 self.output.write('could not read docs from %s\n' % filename)
1392 return
1393
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001394 divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
1395 addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001396 document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1397 file.close()
1398
1399 import htmllib, formatter, StringIO
1400 buffer = StringIO.StringIO()
1401 parser = htmllib.HTMLParser(
1402 formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1403 parser.start_table = parser.do_p
1404 parser.end_table = lambda parser=parser: parser.do_p({})
1405 parser.start_tr = parser.do_br
1406 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1407 parser.feed(document)
1408 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
1409 pager(' ' + strip(buffer) + '\n')
Ka-Ping Yeeda793892001-04-13 11:02:51 +00001410 if xrefs:
1411 buffer = StringIO.StringIO()
1412 formatter.DumbWriter(buffer).send_flowing_data(
1413 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1414 self.output.write('\n%s\n' % buffer.getvalue())
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001415
1416 def listmodules(self, key=''):
1417 if key:
1418 self.output.write('''
1419Here is a list of matching modules. Enter any module name to get more help.
1420
1421''')
1422 apropos(key)
1423 else:
1424 self.output.write('''
1425Please wait a moment while I gather a list of all available modules...
1426
1427''')
1428 modules = {}
1429 def callback(path, modname, desc, modules=modules):
1430 if modname and modname[-9:] == '.__init__':
1431 modname = modname[:-9] + ' (package)'
1432 if find(modname, '.') < 0:
1433 modules[modname] = 1
1434 ModuleScanner().run(callback)
1435 self.list(modules.keys())
1436 self.output.write('''
1437Enter any module name to get more help. Or, type "modules spam" to search
1438for modules whose descriptions contain the word "spam".
1439''')
1440
1441help = Helper(sys.stdin, sys.stdout)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001442
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001443class Scanner:
1444 """A generic tree iterator."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001445 def __init__(self, roots, children, descendp):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001446 self.roots = roots[:]
1447 self.state = []
1448 self.children = children
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001449 self.descendp = descendp
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001450
1451 def next(self):
1452 if not self.state:
1453 if not self.roots:
1454 return None
1455 root = self.roots.pop(0)
1456 self.state = [(root, self.children(root))]
1457 node, children = self.state[-1]
1458 if not children:
1459 self.state.pop()
1460 return self.next()
1461 child = children.pop(0)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001462 if self.descendp(child):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001463 self.state.append((child, self.children(child)))
1464 return child
1465
1466class ModuleScanner(Scanner):
1467 """An interruptible scanner that searches module synopses."""
1468 def __init__(self):
1469 roots = map(lambda dir: (dir, ''), pathdirs())
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001470 Scanner.__init__(self, roots, self.submodules, self.isnewpackage)
1471 self.inodes = map(lambda (dir, pkg): os.stat(dir)[1], roots)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001472
1473 def submodules(self, (dir, package)):
1474 children = []
1475 for file in os.listdir(dir):
1476 path = os.path.join(dir, file)
Tim Peters30edd232001-03-16 08:29:48 +00001477 if ispackage(path):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001478 children.append((path, package + (package and '.') + file))
1479 else:
1480 children.append((path, package))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001481 children.sort() # so that spam.py comes before spam.pyc or spam.pyo
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001482 return children
1483
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001484 def isnewpackage(self, (dir, package)):
Ka-Ping Yee6191a232001-04-13 15:00:27 +00001485 inode = os.path.exists(dir) and os.stat(dir)[1]
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001486 if not (os.path.islink(dir) and inode in self.inodes):
Ka-Ping Yee6191a232001-04-13 15:00:27 +00001487 self.inodes.append(inode) # detect circular symbolic links
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001488 return ispackage(dir)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001489
Ka-Ping Yee66246962001-04-12 11:59:50 +00001490 def run(self, callback, key=None, completer=None):
1491 if key: key = lower(key)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001492 self.quit = 0
1493 seen = {}
1494
1495 for modname in sys.builtin_module_names:
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001496 if modname != '__main__':
1497 seen[modname] = 1
Ka-Ping Yee66246962001-04-12 11:59:50 +00001498 if key is None:
1499 callback(None, modname, '')
1500 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001501 desc = split(__import__(modname).__doc__ or '', '\n')[0]
Ka-Ping Yee66246962001-04-12 11:59:50 +00001502 if find(lower(modname + ' - ' + desc), key) >= 0:
1503 callback(None, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001504
1505 while not self.quit:
1506 node = self.next()
1507 if not node: break
1508 path, package = node
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001509 modname = inspect.getmodulename(path)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001510 if os.path.isfile(path) and modname:
1511 modname = package + (package and '.') + modname
1512 if not seen.has_key(modname):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001513 seen[modname] = 1 # if we see spam.py, skip spam.pyc
Ka-Ping Yee66246962001-04-12 11:59:50 +00001514 if key is None:
1515 callback(path, modname, '')
1516 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001517 desc = synopsis(path) or ''
1518 if find(lower(modname + ' - ' + desc), key) >= 0:
1519 callback(path, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001520 if completer: completer()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001521
1522def apropos(key):
1523 """Print all the one-line module summaries that contain a substring."""
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001524 def callback(path, modname, desc):
1525 if modname[-9:] == '.__init__':
1526 modname = modname[:-9] + ' (package)'
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001527 print modname, desc and '- ' + desc
1528 try: import warnings
1529 except ImportError: pass
1530 else: warnings.filterwarnings('ignore') # ignore problems during import
Ka-Ping Yee66246962001-04-12 11:59:50 +00001531 ModuleScanner().run(callback, key)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001532
1533# --------------------------------------------------- web browser interface
1534
Ka-Ping Yee66246962001-04-12 11:59:50 +00001535def serve(port, callback=None, completer=None):
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001536 import BaseHTTPServer, mimetools, select
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001537
1538 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1539 class Message(mimetools.Message):
1540 def __init__(self, fp, seekable=1):
1541 Message = self.__class__
1542 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1543 self.encodingheader = self.getheader('content-transfer-encoding')
1544 self.typeheader = self.getheader('content-type')
1545 self.parsetype()
1546 self.parseplist()
1547
1548 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1549 def send_document(self, title, contents):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001550 try:
1551 self.send_response(200)
1552 self.send_header('Content-Type', 'text/html')
1553 self.end_headers()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001554 self.wfile.write(html.page(title, contents))
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001555 except IOError: pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001556
1557 def do_GET(self):
1558 path = self.path
1559 if path[-5:] == '.html': path = path[:-5]
1560 if path[:1] == '/': path = path[1:]
1561 if path and path != '.':
1562 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001563 obj = locate(path, forceload=1)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001564 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001565 self.send_document(path, html.escape(str(value)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001566 return
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001567 if obj:
1568 self.send_document(describe(obj), html.document(obj, path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001569 else:
1570 self.send_document(path,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001571'no Python documentation found for %s' % repr(path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001572 else:
1573 heading = html.heading(
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001574'<big><big><strong>Python: Index of Modules</strong></big></big>',
1575'#ffffff', '#7799ee')
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001576 def bltinlink(name):
1577 return '<a href="%s.html">%s</a>' % (name, name)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001578 names = filter(lambda x: x != '__main__',
1579 sys.builtin_module_names)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001580 contents = html.multicolumn(names, bltinlink)
1581 indices = ['<p>' + html.bigsection(
1582 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1583
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001584 seen = {}
1585 for dir in pathdirs():
1586 indices.append(html.index(dir, seen))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001587 contents = heading + join(indices) + '''<p align=right>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001588<small><small><font color="#909090" face="helvetica, arial"><strong>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001589pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font></small></small>'''
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001590 self.send_document('Index of Modules', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001591
1592 def log_message(self, *args): pass
1593
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001594 class DocServer(BaseHTTPServer.HTTPServer):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001595 def __init__(self, port, callback):
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001596 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001597 self.address = ('', port)
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001598 self.url = 'http://%s:%d/' % (host, port)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001599 self.callback = callback
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001600 self.base.__init__(self, self.address, self.handler)
1601
1602 def serve_until_quit(self):
1603 import select
1604 self.quit = 0
1605 while not self.quit:
1606 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1607 if rd: self.handle_request()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001608
1609 def server_activate(self):
1610 self.base.server_activate(self)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001611 if self.callback: self.callback(self)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001612
1613 DocServer.base = BaseHTTPServer.HTTPServer
1614 DocServer.handler = DocHandler
1615 DocHandler.MessageClass = Message
1616 try:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001617 try:
1618 DocServer(port, callback).serve_until_quit()
1619 except (KeyboardInterrupt, select.error):
1620 pass
1621 finally:
Ka-Ping Yee66246962001-04-12 11:59:50 +00001622 if completer: completer()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001623
1624# ----------------------------------------------------- graphical interface
1625
1626def gui():
1627 """Graphical interface (starts web server and pops up a control window)."""
1628 class GUI:
1629 def __init__(self, window, port=7464):
1630 self.window = window
1631 self.server = None
1632 self.scanner = None
1633
1634 import Tkinter
1635 self.server_frm = Tkinter.Frame(window)
1636 self.title_lbl = Tkinter.Label(self.server_frm,
1637 text='Starting server...\n ')
1638 self.open_btn = Tkinter.Button(self.server_frm,
1639 text='open browser', command=self.open, state='disabled')
1640 self.quit_btn = Tkinter.Button(self.server_frm,
1641 text='quit serving', command=self.quit, state='disabled')
1642
1643 self.search_frm = Tkinter.Frame(window)
1644 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
1645 self.search_ent = Tkinter.Entry(self.search_frm)
1646 self.search_ent.bind('<Return>', self.search)
1647 self.stop_btn = Tkinter.Button(self.search_frm,
1648 text='stop', pady=0, command=self.stop, state='disabled')
1649 if sys.platform == 'win32':
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001650 # Trying to hide and show this button crashes under Windows.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001651 self.stop_btn.pack(side='right')
1652
1653 self.window.title('pydoc')
1654 self.window.protocol('WM_DELETE_WINDOW', self.quit)
1655 self.title_lbl.pack(side='top', fill='x')
1656 self.open_btn.pack(side='left', fill='x', expand=1)
1657 self.quit_btn.pack(side='right', fill='x', expand=1)
1658 self.server_frm.pack(side='top', fill='x')
1659
1660 self.search_lbl.pack(side='left')
1661 self.search_ent.pack(side='right', fill='x', expand=1)
1662 self.search_frm.pack(side='top', fill='x')
1663 self.search_ent.focus_set()
1664
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001665 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001666 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001667 self.result_lst.bind('<Button-1>', self.select)
1668 self.result_lst.bind('<Double-Button-1>', self.goto)
1669 self.result_scr = Tkinter.Scrollbar(window,
1670 orient='vertical', command=self.result_lst.yview)
1671 self.result_lst.config(yscrollcommand=self.result_scr.set)
1672
1673 self.result_frm = Tkinter.Frame(window)
1674 self.goto_btn = Tkinter.Button(self.result_frm,
1675 text='go to selected', command=self.goto)
1676 self.hide_btn = Tkinter.Button(self.result_frm,
1677 text='hide results', command=self.hide)
1678 self.goto_btn.pack(side='left', fill='x', expand=1)
1679 self.hide_btn.pack(side='right', fill='x', expand=1)
1680
1681 self.window.update()
1682 self.minwidth = self.window.winfo_width()
1683 self.minheight = self.window.winfo_height()
1684 self.bigminheight = (self.server_frm.winfo_reqheight() +
1685 self.search_frm.winfo_reqheight() +
1686 self.result_lst.winfo_reqheight() +
1687 self.result_frm.winfo_reqheight())
1688 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
1689 self.expanded = 0
1690 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1691 self.window.wm_minsize(self.minwidth, self.minheight)
1692
1693 import threading
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001694 threading.Thread(
1695 target=serve, args=(port, self.ready, self.quit)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001696
1697 def ready(self, server):
1698 self.server = server
1699 self.title_lbl.config(
1700 text='Python documentation server at\n' + server.url)
1701 self.open_btn.config(state='normal')
1702 self.quit_btn.config(state='normal')
1703
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001704 def open(self, event=None, url=None):
1705 url = url or self.server.url
1706 try:
1707 import webbrowser
1708 webbrowser.open(url)
1709 except ImportError: # pre-webbrowser.py compatibility
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001710 if sys.platform == 'win32':
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001711 os.system('start "%s"' % url)
1712 elif sys.platform == 'mac':
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001713 try: import ic
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001714 except ImportError: pass
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001715 else: ic.launchurl(url)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001716 else:
1717 rc = os.system('netscape -remote "openURL(%s)" &' % url)
1718 if rc: os.system('netscape "%s" &' % url)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001719
1720 def quit(self, event=None):
1721 if self.server:
1722 self.server.quit = 1
1723 self.window.quit()
1724
1725 def search(self, event=None):
1726 key = self.search_ent.get()
1727 self.stop_btn.pack(side='right')
1728 self.stop_btn.config(state='normal')
1729 self.search_lbl.config(text='Searching for "%s"...' % key)
1730 self.search_ent.forget()
1731 self.search_lbl.pack(side='left')
1732 self.result_lst.delete(0, 'end')
1733 self.goto_btn.config(state='disabled')
1734 self.expand()
1735
1736 import threading
1737 if self.scanner:
1738 self.scanner.quit = 1
1739 self.scanner = ModuleScanner()
1740 threading.Thread(target=self.scanner.run,
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +00001741 args=(self.update, key, self.done)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001742
1743 def update(self, path, modname, desc):
1744 if modname[-9:] == '.__init__':
1745 modname = modname[:-9] + ' (package)'
1746 self.result_lst.insert('end',
1747 modname + ' - ' + (desc or '(no description)'))
1748
1749 def stop(self, event=None):
1750 if self.scanner:
1751 self.scanner.quit = 1
1752 self.scanner = None
1753
1754 def done(self):
1755 self.scanner = None
1756 self.search_lbl.config(text='Search for')
1757 self.search_lbl.pack(side='left')
1758 self.search_ent.pack(side='right', fill='x', expand=1)
1759 if sys.platform != 'win32': self.stop_btn.forget()
1760 self.stop_btn.config(state='disabled')
1761
1762 def select(self, event=None):
1763 self.goto_btn.config(state='normal')
1764
1765 def goto(self, event=None):
1766 selection = self.result_lst.curselection()
1767 if selection:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001768 modname = split(self.result_lst.get(selection[0]))[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001769 self.open(url=self.server.url + modname + '.html')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001770
1771 def collapse(self):
1772 if not self.expanded: return
1773 self.result_frm.forget()
1774 self.result_scr.forget()
1775 self.result_lst.forget()
1776 self.bigwidth = self.window.winfo_width()
1777 self.bigheight = self.window.winfo_height()
1778 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1779 self.window.wm_minsize(self.minwidth, self.minheight)
1780 self.expanded = 0
1781
1782 def expand(self):
1783 if self.expanded: return
1784 self.result_frm.pack(side='bottom', fill='x')
1785 self.result_scr.pack(side='right', fill='y')
1786 self.result_lst.pack(side='top', fill='both', expand=1)
1787 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
1788 self.window.wm_minsize(self.minwidth, self.bigminheight)
1789 self.expanded = 1
1790
1791 def hide(self, event=None):
1792 self.stop()
1793 self.collapse()
1794
1795 import Tkinter
1796 try:
1797 gui = GUI(Tkinter.Tk())
1798 Tkinter.mainloop()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001799 except KeyboardInterrupt:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001800 pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001801
1802# -------------------------------------------------- command-line interface
1803
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001804def ispath(x):
1805 return type(x) is types.StringType and find(x, os.sep) >= 0
1806
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001807def cli():
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001808 """Command-line interface (looks at sys.argv to decide what to do)."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001809 import getopt
1810 class BadUsage: pass
1811
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001812 # Scripts don't get the current directory in their path by default.
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00001813 scriptdir = os.path.dirname(sys.argv[0])
1814 if scriptdir in sys.path:
1815 sys.path.remove(scriptdir)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001816 sys.path.insert(0, '.')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001817
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001818 try:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001819 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001820 writing = 0
1821
1822 for opt, val in opts:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001823 if opt == '-g':
1824 gui()
1825 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001826 if opt == '-k':
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001827 apropos(val)
1828 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001829 if opt == '-p':
1830 try:
1831 port = int(val)
1832 except ValueError:
1833 raise BadUsage
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001834 def ready(server):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001835 print 'pydoc server ready at %s' % server.url
1836 def stopped():
1837 print 'pydoc server stopped'
1838 serve(port, ready, stopped)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001839 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001840 if opt == '-w':
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001841 writing = 1
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001842
1843 if not args: raise BadUsage
1844 for arg in args:
1845 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001846 if ispath(arg) and os.path.isfile(arg):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001847 arg = importfile(arg)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001848 if writing:
1849 if ispath(arg) and os.path.isdir(arg):
1850 writedocs(arg)
1851 else:
1852 writedoc(arg)
1853 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001854 doc(arg)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001855 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001856 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001857
1858 except (getopt.error, BadUsage):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001859 cmd = sys.argv[0]
1860 print """pydoc - the Python documentation tool
1861
1862%s <name> ...
1863 Show text documentation on something. <name> may be the name of a
1864 function, module, or package, or a dotted reference to a class or
1865 function within a module or module in a package. If <name> contains
1866 a '%s', it is used as the path to a Python source file to document.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001867
1868%s -k <keyword>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001869 Search for a keyword in the synopsis lines of all available modules.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001870
1871%s -p <port>
1872 Start an HTTP server on the given port on the local machine.
1873
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001874%s -g
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001875 Pop up a graphical interface for finding and serving documentation.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001876
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001877%s -w <name> ...
1878 Write out the HTML documentation for a module to a file in the current
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001879 directory. If <name> contains a '%s', it is treated as a filename; if
1880 it names a directory, documentation is written for all the contents.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001881""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001882
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001883if __name__ == '__main__': cli()