blob: 95e82b3b90091fb81a080288a0b0b8ccfb946d1b [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
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000118def allmethods(cl):
119 methods = {}
120 for key, value in inspect.getmembers(cl, inspect.ismethod):
121 methods[key] = 1
122 for base in cl.__bases__:
123 methods.update(allmethods(base)) # all your base are belong to us
124 for key in methods.keys():
125 methods[key] = getattr(cl, key)
126 return methods
127
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000128# ----------------------------------------------------- module manipulation
129
130def ispackage(path):
131 """Guess whether a path refers to a package directory."""
132 if os.path.isdir(path):
133 for ext in ['.py', '.pyc', '.pyo']:
134 if os.path.isfile(os.path.join(path, '__init__' + ext)):
135 return 1
136
137def synopsis(filename, cache={}):
138 """Get the one-line summary out of a module file."""
139 mtime = os.stat(filename)[stat.ST_MTIME]
140 lastupdate, result = cache.get(filename, (0, None))
141 if lastupdate < mtime:
142 info = inspect.getmoduleinfo(filename)
143 file = open(filename)
144 if info and 'b' in info[2]: # binary modules have to be imported
145 try: module = imp.load_module('__temp__', file, filename, info[1:])
146 except: return None
147 result = split(module.__doc__ or '', '\n')[0]
148 del sys.modules['__temp__']
149 else: # text modules can be directly examined
150 line = file.readline()
151 while line[:1] == '#' or not strip(line):
152 line = file.readline()
153 if not line: break
154 line = strip(line)
155 if line[:4] == 'r"""': line = line[1:]
156 if line[:3] == '"""':
157 line = line[3:]
158 if line[-1:] == '\\': line = line[:-1]
159 while not strip(line):
160 line = file.readline()
161 if not line: break
162 result = strip(split(line, '"""')[0])
163 else: result = None
164 file.close()
165 cache[filename] = (mtime, result)
166 return result
167
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000168class ErrorDuringImport(Exception):
169 """Errors that occurred while trying to import something to document it."""
170 def __init__(self, filename, (exc, value, tb)):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000171 self.filename = filename
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000172 self.exc = exc
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000173 self.value = value
174 self.tb = tb
175
176 def __str__(self):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000177 exc = self.exc
178 if type(exc) is types.ClassType:
179 exc = exc.__name__
180 return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000181
182def importfile(path):
183 """Import a Python source file or compiled file given its path."""
184 magic = imp.get_magic()
185 file = open(path, 'r')
186 if file.read(len(magic)) == magic:
187 kind = imp.PY_COMPILED
188 else:
189 kind = imp.PY_SOURCE
190 file.close()
191 filename = os.path.basename(path)
192 name, ext = os.path.splitext(filename)
193 file = open(path, 'r')
194 try:
195 module = imp.load_module(name, file, path, (ext, 'r', kind))
196 except:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000197 raise ErrorDuringImport(path, sys.exc_info())
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000198 file.close()
199 return module
200
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000201def safeimport(path, forceload=0, cache={}):
202 """Import a module; handle errors; return None if the module isn't found.
203
204 If the module *is* found but an exception occurs, it's wrapped in an
205 ErrorDuringImport exception and reraised. Unlike __import__, if a
206 package path is specified, the module at the end of the path is returned,
207 not the package at the beginning. If the optional 'forceload' argument
208 is 1, we reload the module from disk (unless it's a dynamic extension)."""
209 if forceload and sys.modules.has_key(path):
210 # This is the only way to be sure. Checking the mtime of the file
211 # isn't good enough (e.g. what if the module contains a class that
212 # inherits from another module that has changed?).
213 if path not in sys.builtin_module_names:
214 # Python never loads a dynamic extension a second time from the
215 # same path, even if the file is changed or missing. Deleting
216 # the entry in sys.modules doesn't help for dynamic extensions,
217 # so we're not even going to try to keep them up to date.
218 info = inspect.getmoduleinfo(sys.modules[path].__file__)
219 if info[3] != imp.C_EXTENSION:
220 cache[path] = sys.modules[path] # prevent module from clearing
221 del sys.modules[path]
222 try:
223 module = __import__(path)
224 except:
225 # Did the error occur before or after the module was found?
226 (exc, value, tb) = info = sys.exc_info()
227 if sys.modules.has_key(path):
228 # An error occured while executing the imported module.
229 raise ErrorDuringImport(sys.modules[path].__file__, info)
230 elif exc is SyntaxError:
231 # A SyntaxError occurred before we could execute the module.
232 raise ErrorDuringImport(value.filename, info)
233 elif exc is ImportError and \
234 split(lower(str(value)))[:2] == ['no', 'module']:
235 # The module was not found.
236 return None
237 else:
238 # Some other error occurred during the importing process.
239 raise ErrorDuringImport(path, sys.exc_info())
240 for part in split(path, '.')[1:]:
241 try: module = getattr(module, part)
242 except AttributeError: return None
243 return module
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000244
245# ---------------------------------------------------- formatter base class
246
247class Doc:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000248 def document(self, object, name=None, *args):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000249 """Generate documentation for an object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000250 args = (object, name) + args
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000251 if inspect.ismodule(object): return apply(self.docmodule, args)
252 if inspect.isclass(object): return apply(self.docclass, args)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000253 if inspect.isroutine(object): return apply(self.docroutine, args)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000254 return apply(self.docother, args)
255
256 def fail(self, object, name=None, *args):
257 """Raise an exception for unimplemented types."""
258 message = "don't know how to document object%s of type %s" % (
259 name and ' ' + repr(name), type(object).__name__)
260 raise TypeError, message
261
262 docmodule = docclass = docroutine = docother = fail
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000263
264# -------------------------------------------- HTML documentation generator
265
266class HTMLRepr(Repr):
267 """Class for safely making an HTML representation of a Python object."""
268 def __init__(self):
269 Repr.__init__(self)
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000270 self.maxlist = self.maxtuple = 20
271 self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000272 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000273
274 def escape(self, text):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000275 return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000276
277 def repr(self, object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000278 return Repr.repr(self, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000279
280 def repr1(self, x, level):
281 methodname = 'repr_' + join(split(type(x).__name__), '_')
282 if hasattr(self, methodname):
283 return getattr(self, methodname)(x, level)
284 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000285 return self.escape(cram(stripid(repr(x)), self.maxother))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000286
287 def repr_string(self, x, level):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000288 test = cram(x, self.maxstring)
289 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000290 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000291 # Backslashes are only literal in the string and are never
292 # needed to make any special characters, so show a raw string.
293 return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000294 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000295 r'<font color="#c040c0">\1</font>',
296 self.escape(testrepr))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000297
298 def repr_instance(self, x, level):
299 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000300 return self.escape(cram(stripid(repr(x)), self.maxstring))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000301 except:
302 return self.escape('<%s instance>' % x.__class__.__name__)
303
304 repr_unicode = repr_string
305
306class HTMLDoc(Doc):
307 """Formatter class for HTML documentation."""
308
309 # ------------------------------------------- HTML formatting utilities
310
311 _repr_instance = HTMLRepr()
312 repr = _repr_instance.repr
313 escape = _repr_instance.escape
314
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000315 def page(self, title, contents):
316 """Format an HTML page."""
317 return '''
318<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000319<html><head><title>Python: %s</title>
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000320<style type="text/css"><!--
321TT { font-family: lucida console, lucida typewriter, courier }
322--></style></head><body bgcolor="#f0f0f8">
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000323%s
324</body></html>''' % (title, contents)
325
326 def heading(self, title, fgcol, bgcol, extras=''):
327 """Format a page heading."""
328 return '''
329<table width="100%%" cellspacing=0 cellpadding=2 border=0>
330<tr bgcolor="%s">
331<td valign=bottom><small>&nbsp;<br></small
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000332><font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000333><td align=right valign=bottom
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000334><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000335 ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
336
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000337 def section(self, title, fgcol, bgcol, contents, width=10,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000338 prelude='', marginalia=None, gap='&nbsp;&nbsp;'):
339 """Format a section with a heading."""
340 if marginalia is None:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000341 marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000342 result = '''
343<p><table width="100%%" cellspacing=0 cellpadding=2 border=0>
344<tr bgcolor="%s">
345<td colspan=3 valign=bottom><small><small>&nbsp;<br></small></small
346><font color="%s" face="helvetica, arial">%s</font></td></tr>
347 ''' % (bgcol, fgcol, title)
348 if prelude:
349 result = result + '''
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000350<tr bgcolor="%s"><td rowspan=2>%s</td>
351<td colspan=2>%s</td></tr>
352<tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
353 else:
354 result = result + '''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000355<tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000356
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000357 return result + '\n<td width="100%%">%s</td></tr></table>' % contents
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000358
359 def bigsection(self, title, *args):
360 """Format a section with a big heading."""
361 title = '<big><strong>%s</strong></big>' % title
362 return apply(self.section, (title,) + args)
363
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000364 def preformat(self, text):
365 """Format literal preformatted text."""
366 text = self.escape(expandtabs(text))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000367 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
368 ' ', '&nbsp;', '\n', '<br>\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000369
370 def multicolumn(self, list, format, cols=4):
371 """Format a list of items into a multi-column list."""
372 result = ''
373 rows = (len(list)+cols-1)/cols
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000374 for col in range(cols):
375 result = result + '<td width="%d%%" valign=top>' % (100/cols)
376 for i in range(rows*col, rows*col+rows):
377 if i < len(list):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000378 result = result + format(list[i]) + '<br>\n'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000379 result = result + '</td>'
380 return '<table width="100%%"><tr>%s</tr></table>' % result
381
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000382 def small(self, text): return '<small>%s</small>' % text
383 def grey(self, text): return '<font color="#909090">%s</font>' % text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000384
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000385 def namelink(self, name, *dicts):
386 """Make a link for an identifier, given name-to-URL mappings."""
387 for dict in dicts:
388 if dict.has_key(name):
389 return '<a href="%s">%s</a>' % (dict[name], name)
390 return name
391
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000392 def classlink(self, object, modname):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000393 """Make a link for a class."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000394 name, module = object.__name__, sys.modules.get(object.__module__)
395 if hasattr(module, name) and getattr(module, name) is object:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000396 return '<a href="%s.html#%s">%s</a>' % (
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000397 module.__name__, name, classname(object, modname))
398 return classname(object, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000399
400 def modulelink(self, object):
401 """Make a link for a module."""
402 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
403
404 def modpkglink(self, (name, path, ispackage, shadowed)):
405 """Make a link for a module or package to display in an index."""
406 if shadowed:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000407 return self.grey(name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000408 if path:
409 url = '%s.%s.html' % (path, name)
410 else:
411 url = '%s.html' % name
412 if ispackage:
413 text = '<strong>%s</strong>&nbsp;(package)' % name
414 else:
415 text = name
416 return '<a href="%s">%s</a>' % (url, text)
417
418 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
419 """Mark up some plain text, given a context of symbols to look for.
420 Each context dictionary maps object names to anchor names."""
421 escape = escape or self.escape
422 results = []
423 here = 0
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000424 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
425 r'RFC[- ]?(\d+)|'
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000426 r'PEP[- ]?(\d+)|'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000427 r'(self\.)?(\w+))\b')
428 while 1:
429 match = pattern.search(text, here)
430 if not match: break
431 start, end = match.span()
432 results.append(escape(text[here:start]))
433
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000434 all, scheme, rfc, pep, selfdot, name = match.groups()
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000435 if scheme:
436 results.append('<a href="%s">%s</a>' % (all, escape(all)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000437 elif rfc:
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000438 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
439 results.append('<a href="%s">%s</a>' % (url, escape(all)))
440 elif pep:
441 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000442 results.append('<a href="%s">%s</a>' % (url, escape(all)))
443 elif text[end:end+1] == '(':
444 results.append(self.namelink(name, methods, funcs, classes))
445 elif selfdot:
446 results.append('self.<strong>%s</strong>' % name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000447 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000448 results.append(self.namelink(name, classes))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000449 here = end
450 results.append(escape(text[here:]))
451 return join(results, '')
452
453 # ---------------------------------------------- type-specific routines
454
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000455 def formattree(self, tree, modname, parent=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000456 """Produce HTML for a class tree as given by inspect.getclasstree()."""
457 result = ''
458 for entry in tree:
459 if type(entry) is type(()):
460 c, bases = entry
461 result = result + '<dt><font face="helvetica, arial"><small>'
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000462 result = result + self.classlink(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000463 if bases and bases != (parent,):
464 parents = []
465 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000466 parents.append(self.classlink(base, modname))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000467 result = result + '(' + join(parents, ', ') + ')'
468 result = result + '\n</small></font></dt>'
469 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000470 result = result + '<dd>\n%s</dd>\n' % self.formattree(
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000471 entry, modname, c)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000472 return '<dl>\n%s</dl>\n' % result
473
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000474 def docmodule(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000475 """Produce HTML documentation for a module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000476 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000477 parts = split(name, '.')
478 links = []
479 for i in range(len(parts)-1):
480 links.append(
481 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
482 (join(parts[:i+1], '.'), parts[i]))
483 linkedname = join(links + parts[-1:], '.')
484 head = '<big><big><strong>%s</strong></big></big>' % linkedname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000485 try:
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000486 path = inspect.getabsfile(object)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000487 filelink = '<a href="file:%s">%s</a>' % (path, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000488 except TypeError:
489 filelink = '(built-in)'
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000490 info = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000491 if hasattr(object, '__version__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000492 version = str(object.__version__)
Ka-Ping Yee40c49912001-02-27 22:46:01 +0000493 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
494 version = strip(version[11:-1])
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000495 info.append('version %s' % self.escape(version))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000496 if hasattr(object, '__date__'):
497 info.append(self.escape(str(object.__date__)))
498 if info:
499 head = head + ' (%s)' % join(info, ', ')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000500 result = self.heading(
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000501 head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)
502
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000503 modules = inspect.getmembers(object, inspect.ismodule)
504
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000505 classes, cdict = [], {}
506 for key, value in inspect.getmembers(object, inspect.isclass):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000507 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000508 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000509 cdict[key] = cdict[value] = '#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000510 for key, value in classes:
511 for base in value.__bases__:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000512 key, modname = base.__name__, base.__module__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000513 module = sys.modules.get(modname)
514 if modname != name and module and hasattr(module, key):
515 if getattr(module, key) is base:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000516 if not cdict.has_key(key):
517 cdict[key] = cdict[base] = modname + '.html#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000518 funcs, fdict = [], {}
519 for key, value in inspect.getmembers(object, inspect.isroutine):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000520 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000521 funcs.append((key, value))
522 fdict[key] = '#-' + key
523 if inspect.isfunction(value): fdict[value] = fdict[key]
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000524 data = []
525 for key, value in inspect.getmembers(object, isdata):
526 if key not in ['__builtins__', '__doc__']:
527 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000528
529 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
530 doc = doc and '<tt>%s</tt>' % doc
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000531 result = result + '<p>%s</p>\n' % self.small(doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000532
533 if hasattr(object, '__path__'):
534 modpkgs = []
535 modnames = []
536 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000537 path = os.path.join(object.__path__[0], file)
538 modname = inspect.getmodulename(file)
539 if modname and modname not in modnames:
540 modpkgs.append((modname, name, 0, 0))
541 modnames.append(modname)
542 elif ispackage(path):
543 modpkgs.append((file, name, 1, 0))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000544 modpkgs.sort()
545 contents = self.multicolumn(modpkgs, self.modpkglink)
546 result = result + self.bigsection(
547 'Package Contents', '#ffffff', '#aa55cc', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000548 elif modules:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000549 contents = self.multicolumn(
550 modules, lambda (key, value), s=self: s.modulelink(value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000551 result = result + self.bigsection(
552 'Modules', '#fffff', '#aa55cc', contents)
553
554 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000555 classlist = map(lambda (key, value): value, classes)
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000556 contents = [
557 self.formattree(inspect.getclasstree(classlist, 1), name)]
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000558 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000559 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000560 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000561 'Classes', '#ffffff', '#ee77aa', join(contents))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000562 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000563 contents = []
564 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000565 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000566 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000567 'Functions', '#ffffff', '#eeaa77', join(contents))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000568 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000569 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000570 for key, value in data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000571 contents.append(self.document(value, key))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000572 result = result + self.bigsection(
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000573 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000574 if hasattr(object, '__author__'):
575 contents = self.markup(str(object.__author__), self.preformat)
576 result = result + self.bigsection(
577 'Author', '#ffffff', '#7799ee', contents)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000578 if hasattr(object, '__credits__'):
579 contents = self.markup(str(object.__credits__), self.preformat)
580 result = result + self.bigsection(
581 'Credits', '#ffffff', '#7799ee', contents)
582
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000583 return result
584
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000585 def docclass(self, object, name=None, mod=None, funcs={}, classes={}):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000586 """Produce HTML documentation for a class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000587 realname = object.__name__
588 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000589 bases = object.__bases__
590 contents = ''
591
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000592 methods, mdict = allmethods(object).items(), {}
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000593 methods.sort()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000594 for key, value in methods:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000595 mdict[key] = mdict[value] = '#' + name + '-' + key
596 for key, value in methods:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000597 contents = contents + self.document(
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000598 value, key, mod, funcs, classes, mdict, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000599
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000600 if name == realname:
601 title = '<a name="%s">class <strong>%s</strong></a>' % (
602 name, realname)
603 else:
604 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
605 name, name, realname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000606 if bases:
607 parents = []
608 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000609 parents.append(self.classlink(base, object.__module__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000610 title = title + '(%s)' % join(parents, ', ')
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000611 doc = self.markup(
612 getdoc(object), self.preformat, funcs, classes, mdict)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000613 doc = self.small(doc and '<tt>%s<br>&nbsp;</tt>' % doc or
614 self.small('&nbsp;'))
615 return self.section(title, '#000000', '#ffc8d8', contents, 5, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000616
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000617 def formatvalue(self, object):
618 """Format an argument default value as text."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000619 return self.small(self.grey('=' + self.repr(object)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000620
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000621 def docroutine(self, object, name=None, mod=None,
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000622 funcs={}, classes={}, methods={}, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000623 """Produce HTML documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000624 realname = object.__name__
625 name = name or realname
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000626 anchor = (cl and cl.__name__ or '') + '-' + name
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000627 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000628 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000629 if inspect.ismethod(object):
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +0000630 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000631 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000632 if imclass is not cl:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000633 note = ' from ' + self.classlink(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000634 skipdocs = 1
635 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000636 if object.im_self:
637 note = ' method of %s instance' % self.classlink(
638 object.im_self.__class__, mod)
639 else:
640 note = ' unbound %s method' % self.classlink(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000641 object = object.im_func
642
643 if name == realname:
644 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
645 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000646 if (cl and cl.__dict__.has_key(realname) and
647 cl.__dict__[realname] is object):
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000648 reallink = '<a href="#%s">%s</a>' % (
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000649 cl.__name__ + '-' + realname, realname)
650 skipdocs = 1
651 else:
652 reallink = realname
653 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
654 anchor, name, reallink)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000655 if inspect.isbuiltin(object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000656 argspec = '(...)'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000657 else:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000658 args, varargs, varkw, defaults = inspect.getargspec(object)
659 argspec = inspect.formatargspec(
660 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000661 if realname == '<lambda>':
662 decl = '<em>lambda</em>'
663 argspec = argspec[1:-1] # remove parentheses
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000664
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000665 decl = title + argspec + (note and self.small(self.grey(
666 '<font face="helvetica, arial">%s</font>' % note)))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000667
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000668 if skipdocs:
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000669 return '<dl><dt>%s</dl>\n' % decl
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000670 else:
671 doc = self.markup(
672 getdoc(object), self.preformat, funcs, classes, methods)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000673 doc = doc and '<dd>' + self.small('<tt>%s</tt>' % doc)
674 return '<dl><dt>%s%s</dl>\n' % (decl, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000675
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000676 def docother(self, object, name=None, mod=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000677 """Produce HTML documentation for a data object."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000678 lhs = name and '<strong>%s</strong> = ' % name or ''
679 return lhs + self.repr(object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000680
681 def index(self, dir, shadowed=None):
682 """Generate an HTML index for a directory of modules."""
683 modpkgs = []
684 if shadowed is None: shadowed = {}
685 seen = {}
686 files = os.listdir(dir)
687
688 def found(name, ispackage,
689 modpkgs=modpkgs, shadowed=shadowed, seen=seen):
690 if not seen.has_key(name):
691 modpkgs.append((name, '', ispackage, shadowed.has_key(name)))
692 seen[name] = 1
693 shadowed[name] = 1
694
695 # Package spam/__init__.py takes precedence over module spam.py.
696 for file in files:
697 path = os.path.join(dir, file)
698 if ispackage(path): found(file, 1)
699 for file in files:
700 path = os.path.join(dir, file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000701 if os.path.isfile(path):
702 modname = inspect.getmodulename(file)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000703 if modname: found(modname, 0)
704
705 modpkgs.sort()
706 contents = self.multicolumn(modpkgs, self.modpkglink)
707 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
708
709# -------------------------------------------- text documentation generator
710
711class TextRepr(Repr):
712 """Class for safely making a text representation of a Python object."""
713 def __init__(self):
714 Repr.__init__(self)
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000715 self.maxlist = self.maxtuple = 20
716 self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000717 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000718
719 def repr1(self, x, level):
720 methodname = 'repr_' + join(split(type(x).__name__), '_')
721 if hasattr(self, methodname):
722 return getattr(self, methodname)(x, level)
723 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000724 return cram(stripid(repr(x)), self.maxother)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000725
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000726 def repr_string(self, x, level):
727 test = cram(x, self.maxstring)
728 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000729 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000730 # Backslashes are only literal in the string and are never
731 # needed to make any special characters, so show a raw string.
732 return 'r' + testrepr[0] + test + testrepr[0]
733 return testrepr
734
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000735 def repr_instance(self, x, level):
736 try:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000737 return cram(stripid(repr(x)), self.maxstring)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000738 except:
739 return '<%s instance>' % x.__class__.__name__
740
741class TextDoc(Doc):
742 """Formatter class for text documentation."""
743
744 # ------------------------------------------- text formatting utilities
745
746 _repr_instance = TextRepr()
747 repr = _repr_instance.repr
748
749 def bold(self, text):
750 """Format a string in bold by overstriking."""
751 return join(map(lambda ch: ch + '\b' + ch, text), '')
752
753 def indent(self, text, prefix=' '):
754 """Indent text by prepending a given prefix to each line."""
755 if not text: return ''
756 lines = split(text, '\n')
757 lines = map(lambda line, prefix=prefix: prefix + line, lines)
758 if lines: lines[-1] = rstrip(lines[-1])
759 return join(lines, '\n')
760
761 def section(self, title, contents):
762 """Format a section with a given heading."""
763 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
764
765 # ---------------------------------------------- type-specific routines
766
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000767 def formattree(self, tree, modname, parent=None, prefix=''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000768 """Render in text a class tree as returned by inspect.getclasstree()."""
769 result = ''
770 for entry in tree:
771 if type(entry) is type(()):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000772 c, bases = entry
773 result = result + prefix + classname(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000774 if bases and bases != (parent,):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000775 parents = map(lambda c, m=modname: classname(c, m), bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000776 result = result + '(%s)' % join(parents, ', ')
777 result = result + '\n'
778 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000779 result = result + self.formattree(
780 entry, modname, c, prefix + ' ')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000781 return result
782
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000783 def docmodule(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000784 """Produce text documentation for a given module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000785 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000786 synop, desc = splitdoc(getdoc(object))
787 result = self.section('NAME', name + (synop and ' - ' + synop))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000788
789 try:
790 file = inspect.getabsfile(object)
791 except TypeError:
792 file = '(built-in)'
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000793 result = result + self.section('FILE', file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000794 if desc:
795 result = result + self.section('DESCRIPTION', desc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000796
797 classes = []
798 for key, value in inspect.getmembers(object, inspect.isclass):
799 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000800 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000801 funcs = []
802 for key, value in inspect.getmembers(object, inspect.isroutine):
803 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000804 funcs.append((key, value))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000805 data = []
806 for key, value in inspect.getmembers(object, isdata):
807 if key not in ['__builtins__', '__doc__']:
808 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000809
810 if hasattr(object, '__path__'):
811 modpkgs = []
812 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000813 path = os.path.join(object.__path__[0], file)
814 modname = inspect.getmodulename(file)
815 if modname and modname not in modpkgs:
816 modpkgs.append(modname)
817 elif ispackage(path):
818 modpkgs.append(file + ' (package)')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000819 modpkgs.sort()
820 result = result + self.section(
821 'PACKAGE CONTENTS', join(modpkgs, '\n'))
822
823 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000824 classlist = map(lambda (key, value): value, classes)
825 contents = [self.formattree(
826 inspect.getclasstree(classlist, 1), name)]
827 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000828 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000829 result = result + self.section('CLASSES', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000830
831 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000832 contents = []
833 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000834 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000835 result = result + self.section('FUNCTIONS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000836
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000837 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000838 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000839 for key, value in data:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000840 contents.append(self.docother(value, key, name, 70))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000841 result = result + self.section('DATA', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000842
843 if hasattr(object, '__version__'):
844 version = str(object.__version__)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000845 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
846 version = strip(version[11:-1])
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000847 result = result + self.section('VERSION', version)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000848 if hasattr(object, '__date__'):
849 result = result + self.section('DATE', str(object.__date__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000850 if hasattr(object, '__author__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000851 result = result + self.section('AUTHOR', str(object.__author__))
852 if hasattr(object, '__credits__'):
853 result = result + self.section('CREDITS', str(object.__credits__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000854 return result
855
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000856 def docclass(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000857 """Produce text documentation for a given class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000858 realname = object.__name__
859 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000860 bases = object.__bases__
861
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000862 if name == realname:
863 title = 'class ' + self.bold(realname)
864 else:
865 title = self.bold(name) + ' = class ' + realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000866 if bases:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000867 def makename(c, m=object.__module__): return classname(c, m)
868 parents = map(makename, bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000869 title = title + '(%s)' % join(parents, ', ')
870
871 doc = getdoc(object)
872 contents = doc and doc + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000873 methods = allmethods(object).items()
874 methods.sort()
875 for key, value in methods:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000876 contents = contents + '\n' + self.document(value, key, mod, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000877
878 if not contents: return title + '\n'
879 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
880
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000881 def formatvalue(self, object):
882 """Format an argument default value as text."""
883 return '=' + self.repr(object)
884
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000885 def docroutine(self, object, name=None, mod=None, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000886 """Produce text documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000887 realname = object.__name__
888 name = name or realname
889 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000890 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000891 if inspect.ismethod(object):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000892 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000893 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000894 if imclass is not cl:
895 note = ' from ' + classname(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000896 skipdocs = 1
897 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000898 if object.im_self:
899 note = ' method of %s instance' % classname(
900 object.im_self.__class__, mod)
901 else:
902 note = ' unbound %s method' % classname(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000903 object = object.im_func
904
905 if name == realname:
906 title = self.bold(realname)
907 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000908 if (cl and cl.__dict__.has_key(realname) and
909 cl.__dict__[realname] is object):
910 skipdocs = 1
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000911 title = self.bold(name) + ' = ' + realname
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000912 if inspect.isbuiltin(object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000913 argspec = '(...)'
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000914 else:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000915 args, varargs, varkw, defaults = inspect.getargspec(object)
916 argspec = inspect.formatargspec(
917 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000918 if realname == '<lambda>':
919 title = 'lambda'
920 argspec = argspec[1:-1] # remove parentheses
921 decl = title + argspec + note
922
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000923 if skipdocs:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000924 return decl + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000925 else:
926 doc = getdoc(object) or ''
927 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000928
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000929 def docother(self, object, name=None, mod=None, maxlen=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000930 """Produce text documentation for a data object."""
931 repr = self.repr(object)
932 if maxlen:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000933 line = (name and name + ' = ' or '') + repr
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000934 chop = maxlen - len(line)
935 if chop < 0: repr = repr[:chop] + '...'
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000936 line = (name and self.bold(name) + ' = ' or '') + repr
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000937 return line
938
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000939# --------------------------------------------------------- user interfaces
940
941def pager(text):
942 """The first time this is called, determine what kind of pager to use."""
943 global pager
944 pager = getpager()
945 pager(text)
946
947def getpager():
948 """Decide what method to use for paging through text."""
949 if type(sys.stdout) is not types.FileType:
950 return plainpager
951 if not sys.stdin.isatty() or not sys.stdout.isatty():
952 return plainpager
953 if os.environ.has_key('PAGER'):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000954 if sys.platform == 'win32': # pipes completely broken in Windows
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000955 return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
956 elif os.environ.get('TERM') in ['dumb', 'emacs']:
957 return lambda text: pipepager(plain(text), os.environ['PAGER'])
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000958 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000959 return lambda text: pipepager(text, os.environ['PAGER'])
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +0000960 if sys.platform == 'win32':
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000961 return lambda text: tempfilepager(plain(text), 'more <')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000962 if hasattr(os, 'system') and os.system('less 2>/dev/null') == 0:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000963 return lambda text: pipepager(text, 'less')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000964
965 import tempfile
966 filename = tempfile.mktemp()
967 open(filename, 'w').close()
968 try:
969 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
970 return lambda text: pipepager(text, 'more')
971 else:
972 return ttypager
973 finally:
974 os.unlink(filename)
975
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000976def plain(text):
977 """Remove boldface formatting from text."""
978 return re.sub('.\b', '', text)
979
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000980def pipepager(text, cmd):
981 """Page through text by feeding it to another program."""
982 pipe = os.popen(cmd, 'w')
983 try:
984 pipe.write(text)
985 pipe.close()
986 except IOError:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +0000987 pass # Ignore broken pipes caused by quitting the pager program.
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000988
989def tempfilepager(text, cmd):
990 """Page through text by invoking a program on a temporary file."""
991 import tempfile
992 filename = tempfile.mktemp()
993 file = open(filename, 'w')
994 file.write(text)
995 file.close()
996 try:
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +0000997 os.system(cmd + ' ' + filename)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000998 finally:
999 os.unlink(filename)
1000
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001001def ttypager(text):
1002 """Page through text on a text terminal."""
1003 lines = split(plain(text), '\n')
1004 try:
1005 import tty
1006 fd = sys.stdin.fileno()
1007 old = tty.tcgetattr(fd)
1008 tty.setcbreak(fd)
1009 getchar = lambda: sys.stdin.read(1)
Ka-Ping Yee457aab22001-02-27 23:36:29 +00001010 except (ImportError, AttributeError):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001011 tty = None
1012 getchar = lambda: sys.stdin.readline()[:-1][:1]
1013
1014 try:
1015 r = inc = os.environ.get('LINES', 25) - 1
1016 sys.stdout.write(join(lines[:inc], '\n') + '\n')
1017 while lines[r:]:
1018 sys.stdout.write('-- more --')
1019 sys.stdout.flush()
1020 c = getchar()
1021
1022 if c in ['q', 'Q']:
1023 sys.stdout.write('\r \r')
1024 break
1025 elif c in ['\r', '\n']:
1026 sys.stdout.write('\r \r' + lines[r] + '\n')
1027 r = r + 1
1028 continue
1029 if c in ['b', 'B', '\x1b']:
1030 r = r - inc - inc
1031 if r < 0: r = 0
1032 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1033 r = r + inc
1034
1035 finally:
1036 if tty:
1037 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1038
1039def plainpager(text):
1040 """Simply print unformatted text. This is the ultimate fallback."""
1041 sys.stdout.write(plain(text))
1042
1043def describe(thing):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001044 """Produce a short description of the given thing."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001045 if inspect.ismodule(thing):
1046 if thing.__name__ in sys.builtin_module_names:
1047 return 'built-in module ' + thing.__name__
1048 if hasattr(thing, '__path__'):
1049 return 'package ' + thing.__name__
1050 else:
1051 return 'module ' + thing.__name__
1052 if inspect.isbuiltin(thing):
1053 return 'built-in function ' + thing.__name__
1054 if inspect.isclass(thing):
1055 return 'class ' + thing.__name__
1056 if inspect.isfunction(thing):
1057 return 'function ' + thing.__name__
1058 if inspect.ismethod(thing):
1059 return 'method ' + thing.__name__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001060 if type(thing) is types.InstanceType:
1061 return 'instance of ' + thing.__class__.__name__
1062 return type(thing).__name__
1063
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001064def locate(path, forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001065 """Locate an object by name or dotted path, importing as necessary."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001066 parts = split(path, '.')
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001067 module, n = None, 0
1068 while n < len(parts):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001069 nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001070 if nextmodule: module, n = nextmodule, n + 1
1071 else: break
1072 if module:
1073 object = module
1074 for part in parts[n:]:
1075 try: object = getattr(object, part)
1076 except AttributeError: return None
1077 return object
1078 else:
1079 import __builtin__
1080 if hasattr(__builtin__, path):
1081 return getattr(__builtin__, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001082
1083# --------------------------------------- interactive interpreter interface
1084
1085text = TextDoc()
1086html = HTMLDoc()
1087
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001088def doc(thing, title='Python Library Documentation: %s', forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001089 """Display text documentation, given an object or a path to an object."""
1090 suffix, name = '', None
1091 if type(thing) is type(''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001092 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001093 object = locate(thing, forceload)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001094 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001095 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001096 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001097 if not object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001098 print 'no Python documentation found for %s' % repr(thing)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001099 return
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001100 parts = split(thing, '.')
1101 if len(parts) > 1: suffix = ' in ' + join(parts[:-1], '.')
1102 name = parts[-1]
1103 thing = object
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001104
1105 desc = describe(thing)
1106 module = inspect.getmodule(thing)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001107 if not suffix and module and module is not thing:
1108 suffix = ' in module ' + module.__name__
Ka-Ping Yee66246962001-04-12 11:59:50 +00001109 pager(title % (desc + suffix) + '\n\n' + text.document(thing, name))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001110
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001111def writedoc(key, forceload=0):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001112 """Write HTML documentation to a file in the current directory."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001113 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001114 object = locate(key, forceload)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001115 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001116 print value
1117 else:
1118 if object:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001119 page = html.page(describe(object),
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001120 html.document(object, object.__name__))
1121 file = open(key + '.html', 'w')
1122 file.write(page)
1123 file.close()
1124 print 'wrote', key + '.html'
1125 else:
1126 print 'no Python documentation found for %s' % repr(key)
1127
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001128def writedocs(dir, pkgpath='', done=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001129 """Write out HTML documentation for all modules in a directory tree."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001130 if done is None: done = {}
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001131 for file in os.listdir(dir):
1132 path = os.path.join(dir, file)
1133 if ispackage(path):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001134 writedocs(path, pkgpath + file + '.', done)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001135 elif os.path.isfile(path):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001136 modname = inspect.getmodulename(path)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001137 if modname:
1138 modname = pkgpath + modname
1139 if not done.has_key(modname):
1140 done[modname] = 1
1141 writedoc(modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001142
1143class Helper:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001144 keywords = {
1145 'and': 'BOOLEAN',
1146 'assert': 'ASSERT',
1147 'break': ('ref/break', 'while for'),
1148 'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1149 'continue': ('ref/continue', 'while for'),
1150 'def': ('ref/function', ''),
1151 'del': ('ref/del', 'BASICMETHODS'),
1152 'elif': 'if',
1153 'else': ('ref/if', 'while for'),
1154 'except': 'try',
1155 'exec': ('ref/exec', ''),
1156 'finally': 'try',
1157 'for': ('ref/for', 'break continue while'),
1158 'from': 'import',
1159 'global': ('ref/global', 'NAMESPACES'),
1160 'if': ('ref/if', 'TRUTHVALUE'),
1161 'import': ('ref/import', 'MODULES'),
1162 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1163 'is': 'COMPARISON',
1164 'lambda': ('ref/lambda', 'FUNCTIONS'),
1165 'not': 'BOOLEAN',
1166 'or': 'BOOLEAN',
1167 'pass': 'PASS',
1168 'print': ('ref/print', ''),
1169 'raise': ('ref/raise', 'EXCEPTIONS'),
1170 'return': ('ref/return', ''),
1171 'try': ('ref/try', 'EXCEPTIONS'),
1172 'while': ('ref/while', 'break continue if TRUTHVALUE'),
1173 }
1174
1175 topics = {
1176 'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'),
1177 'STRINGS': ('ref/strings', 'UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'),
1178 'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'),
1179 'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'),
1180 'UNICODE': ('ref/unicode', 'TYPES STRING'),
1181 'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1182 'INTEGER': ('ref/integers', 'int range'),
1183 'FLOAT': ('ref/floating', 'float math'),
1184 'COMPLEX': ('ref/imaginary', 'complex cmath'),
1185 'SEQUENCES': ('lib/typesseq', 'LISTS'),
1186 'MAPPINGS': 'DICTIONARIES',
1187 'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'),
1188 'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'),
1189 'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'),
1190 'TYPEOBJECTS': ('lib/bltin-type-objects', 'TYPES'),
1191 'FRAMEOBJECTS': 'TYPES',
1192 'TRACEBACKS': 'TYPES',
1193 'NONE': ('lib/bltin-null-object', ''),
1194 'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'),
1195 'FILES': ('lib/bltin-file-objects', ''),
1196 'SPECIALATTRIBUTES': ('lib/specialattrs', ''),
1197 'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'),
1198 'MODULES': ('lib/typesmodules', 'import'),
1199 'PACKAGES': 'import',
1200 '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'),
1201 'OPERATORS': 'EXPRESSIONS',
1202 'PRECEDENCE': 'EXPRESSIONS',
1203 'OBJECTS': ('ref/objects', 'TYPES'),
1204 'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
1205 'BASICMETHODS': ('ref/customization', 'SPECIALMETHODS'),
1206 'ATTRIBUTEMETHODS': ('ref/attribute-access', 'SPECIALMETHODS'),
1207 'CALLABLEMETHODS': ('ref/callable-types', 'SPECIALMETHODS'),
1208 'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCEMETHODS2'),
1209 'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCEMETHODS1'),
1210 'MAPPINGMETHODS': ('ref/sequence-types', 'SPECIALMETHODS'),
1211 'NUMBERMETHODS': ('ref/numeric-types', 'SPECIALMETHODS'),
1212 'EXECUTION': ('ref/execframes', ''),
1213 'NAMESPACES': ('ref/execframes', 'global ASSIGNMENT DELETION'),
1214 'SCOPING': 'NAMESPACES',
1215 'FRAMES': 'NAMESPACES',
1216 'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'),
1217 'COERCIONS': 'CONVERSIONS',
1218 'CONVERSIONS': ('ref/conversions', ''),
1219 'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'),
1220 'SPECIALIDENTIFIERS': ('ref/id-classes', ''),
1221 'PRIVATENAMES': ('ref/identifiers', ''),
1222 'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1223 'TUPLES': 'SEQUENCES',
1224 'TUPLELITERALS': ('ref/exprlists', 'LITERALS'),
1225 'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'),
1226 'LISTLITERALS': ('ref/lists', 'LITERALS'),
1227 'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'),
1228 'DICTIONARYLITERALS': ('ref/dict', 'LITERALS'),
1229 'BACKQUOTES': ('ref/string-conversions', 'LITERALS'),
1230 'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1231 'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'),
1232 'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'),
1233 'CALLS': ('ref/calls', 'EXPRESSIONS'),
1234 'POWER': ('ref/power', 'EXPRESSIONS'),
1235 'UNARY': ('ref/unary', 'EXPRESSIONS'),
1236 'BINARY': ('ref/binary', 'EXPRESSIONS'),
1237 'SHIFTING': ('ref/shifting', 'EXPRESSIONS'),
1238 'BITWISE': ('ref/bitwise', 'EXPRESSIONS'),
1239 'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'),
1240 'BOOLEAN': ('ref/lambda', 'EXPRESSIONS'),
1241 'ASSERTION': 'assert',
1242 'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'),
1243 'AUGMENTEDASSIGNMENT': ('ref/augassign', ''),
1244 'DELETION': 'del',
1245 'PRINTING': 'print',
1246 'RETURNING': 'return',
1247 'IMPORTING': 'import',
1248 'CONDITIONAL': 'if',
1249 'LOOPING': ('ref/compound', 'for while break continue'),
1250 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'),
1251 }
1252
1253 def __init__(self, input, output):
1254 self.input = input
1255 self.output = output
1256 self.docdir = None
1257 execdir = os.path.dirname(sys.executable)
1258 homedir = os.environ.get('PYTHONHOME')
1259 for dir in [os.environ.get('PYTHONDOCS'),
1260 homedir and os.path.join(homedir, 'doc'),
1261 os.path.join(execdir, 'doc'),
1262 '/usr/doc/python-docs-' + split(sys.version)[0],
1263 '/usr/doc/python-' + split(sys.version)[0],
1264 '/usr/doc/python-docs-' + sys.version[:3],
1265 '/usr/doc/python-' + sys.version[:3]]:
1266 if dir and os.path.isdir(os.path.join(dir, 'lib')):
1267 self.docdir = dir
1268
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001269 def __call__(self, request=None):
1270 if request is not None:
1271 self.help(request)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001272 else:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001273 self.intro()
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001274 self.interact()
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001275 self.output.write('''
1276You're now leaving help and returning to the Python interpreter.
1277If you want to ask for help on a particular object directly from the
1278interpreter, you can type "help(object)". Executing "help('string')"
1279has the same effect as typing a particular string at the help> prompt.
1280''')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001281
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001282 def interact(self):
1283 self.output.write('\n')
1284 while 1:
1285 self.output.write('help> ')
1286 self.output.flush()
1287 try:
1288 request = self.input.readline()
1289 if not request: break
1290 except KeyboardInterrupt: break
1291 request = strip(replace(request, '"', '', "'", ''))
1292 if lower(request) in ['q', 'quit']: break
1293 self.help(request)
1294
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001295 def help(self, request):
1296 if type(request) is type(''):
1297 if request == 'help': self.intro()
1298 elif request == 'keywords': self.listkeywords()
1299 elif request == 'topics': self.listtopics()
1300 elif request == 'modules': self.listmodules()
1301 elif request[:8] == 'modules ':
1302 self.listmodules(split(request)[1])
1303 elif self.keywords.has_key(request): self.showtopic(request)
1304 elif self.topics.has_key(request): self.showtopic(request)
1305 elif request: doc(request, 'Help on %s:')
1306 elif isinstance(request, Helper): self()
1307 else: doc(request, 'Help on %s:')
1308 self.output.write('\n')
1309
1310 def intro(self):
1311 self.output.write('''
1312Welcome to Python %s! This is the online help utility.
1313
1314If this is your first time using Python, you should definitely check out
1315the tutorial on the Internet at http://www.python.org/doc/tut/.
1316
1317Enter the name of any module, keyword, or topic to get help on writing
1318Python programs and using Python modules. To quit this help utility and
1319return to the interpreter, just type "quit".
1320
1321To get a list of available modules, keywords, or topics, type "modules",
1322"keywords", or "topics". Each module also comes with a one-line summary
1323of what it does; to list the modules whose summaries contain a given word
1324such as "spam", type "modules spam".
1325''' % sys.version[:3])
1326
1327 def list(self, items, columns=4, width=80):
1328 items = items[:]
1329 items.sort()
1330 colw = width / columns
1331 rows = (len(items) + columns - 1) / columns
1332 for row in range(rows):
1333 for col in range(columns):
1334 i = col * rows + row
1335 if i < len(items):
1336 self.output.write(items[i])
1337 if col < columns - 1:
1338 self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1339 self.output.write('\n')
1340
1341 def listkeywords(self):
1342 self.output.write('''
1343Here is a list of the Python keywords. Enter any keyword to get more help.
1344
1345''')
1346 self.list(self.keywords.keys())
1347
1348 def listtopics(self):
1349 self.output.write('''
1350Here is a list of available topics. Enter any topic name to get more help.
1351
1352''')
1353 self.list(self.topics.keys())
1354
1355 def showtopic(self, topic):
1356 if not self.docdir:
1357 self.output.write('''
1358Sorry, topic and keyword documentation is not available because the Python
1359HTML documentation files could not be found. If you have installed them,
1360please set the environment variable PYTHONDOCS to indicate their location.
1361''')
1362 return
1363 target = self.topics.get(topic, self.keywords.get(topic))
1364 if not target:
1365 self.output.write('no documentation found for %s\n' % repr(topic))
1366 return
1367 if type(target) is type(''):
1368 return self.showtopic(target)
1369
1370 filename, xrefs = target
1371 filename = self.docdir + '/' + filename + '.html'
1372 try:
1373 file = open(filename)
1374 except:
1375 self.output.write('could not read docs from %s\n' % filename)
1376 return
1377
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001378 divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
1379 addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001380 document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1381 file.close()
1382
1383 import htmllib, formatter, StringIO
1384 buffer = StringIO.StringIO()
1385 parser = htmllib.HTMLParser(
1386 formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1387 parser.start_table = parser.do_p
1388 parser.end_table = lambda parser=parser: parser.do_p({})
1389 parser.start_tr = parser.do_br
1390 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1391 parser.feed(document)
1392 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
1393 pager(' ' + strip(buffer) + '\n')
1394 if xrefs: self.output.write('\nRelated help topics: %s\n' % xrefs)
1395
1396 def listmodules(self, key=''):
1397 if key:
1398 self.output.write('''
1399Here is a list of matching modules. Enter any module name to get more help.
1400
1401''')
1402 apropos(key)
1403 else:
1404 self.output.write('''
1405Please wait a moment while I gather a list of all available modules...
1406
1407''')
1408 modules = {}
1409 def callback(path, modname, desc, modules=modules):
1410 if modname and modname[-9:] == '.__init__':
1411 modname = modname[:-9] + ' (package)'
1412 if find(modname, '.') < 0:
1413 modules[modname] = 1
1414 ModuleScanner().run(callback)
1415 self.list(modules.keys())
1416 self.output.write('''
1417Enter any module name to get more help. Or, type "modules spam" to search
1418for modules whose descriptions contain the word "spam".
1419''')
1420
1421help = Helper(sys.stdin, sys.stdout)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001422
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001423class Scanner:
1424 """A generic tree iterator."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001425 def __init__(self, roots, children, descendp):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001426 self.roots = roots[:]
1427 self.state = []
1428 self.children = children
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001429 self.descendp = descendp
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001430
1431 def next(self):
1432 if not self.state:
1433 if not self.roots:
1434 return None
1435 root = self.roots.pop(0)
1436 self.state = [(root, self.children(root))]
1437 node, children = self.state[-1]
1438 if not children:
1439 self.state.pop()
1440 return self.next()
1441 child = children.pop(0)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001442 if self.descendp(child):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001443 self.state.append((child, self.children(child)))
1444 return child
1445
1446class ModuleScanner(Scanner):
1447 """An interruptible scanner that searches module synopses."""
1448 def __init__(self):
1449 roots = map(lambda dir: (dir, ''), pathdirs())
1450 Scanner.__init__(self, roots, self.submodules, self.ispackage)
1451
1452 def submodules(self, (dir, package)):
1453 children = []
1454 for file in os.listdir(dir):
1455 path = os.path.join(dir, file)
Tim Peters30edd232001-03-16 08:29:48 +00001456 if ispackage(path):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001457 children.append((path, package + (package and '.') + file))
1458 else:
1459 children.append((path, package))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001460 children.sort() # so that spam.py comes before spam.pyc or spam.pyo
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001461 return children
1462
1463 def ispackage(self, (dir, package)):
1464 return ispackage(dir)
1465
Ka-Ping Yee66246962001-04-12 11:59:50 +00001466 def run(self, callback, key=None, completer=None):
1467 if key: key = lower(key)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001468 self.quit = 0
1469 seen = {}
1470
1471 for modname in sys.builtin_module_names:
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001472 if modname != '__main__':
1473 seen[modname] = 1
Ka-Ping Yee66246962001-04-12 11:59:50 +00001474 if key is None:
1475 callback(None, modname, '')
1476 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001477 desc = split(__import__(modname).__doc__ or '', '\n')[0]
Ka-Ping Yee66246962001-04-12 11:59:50 +00001478 if find(lower(modname + ' - ' + desc), key) >= 0:
1479 callback(None, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001480
1481 while not self.quit:
1482 node = self.next()
1483 if not node: break
1484 path, package = node
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001485 modname = inspect.getmodulename(path)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001486 if os.path.isfile(path) and modname:
1487 modname = package + (package and '.') + modname
1488 if not seen.has_key(modname):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001489 seen[modname] = 1 # if we see spam.py, skip spam.pyc
Ka-Ping Yee66246962001-04-12 11:59:50 +00001490 if key is None:
1491 callback(path, modname, '')
1492 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001493 desc = synopsis(path) or ''
1494 if find(lower(modname + ' - ' + desc), key) >= 0:
1495 callback(path, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001496 if completer: completer()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001497
1498def apropos(key):
1499 """Print all the one-line module summaries that contain a substring."""
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001500 def callback(path, modname, desc):
1501 if modname[-9:] == '.__init__':
1502 modname = modname[:-9] + ' (package)'
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001503 print modname, desc and '- ' + desc
1504 try: import warnings
1505 except ImportError: pass
1506 else: warnings.filterwarnings('ignore') # ignore problems during import
Ka-Ping Yee66246962001-04-12 11:59:50 +00001507 ModuleScanner().run(callback, key)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001508
1509# --------------------------------------------------- web browser interface
1510
Ka-Ping Yee66246962001-04-12 11:59:50 +00001511def serve(port, callback=None, completer=None):
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001512 import BaseHTTPServer, mimetools, select
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001513
1514 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1515 class Message(mimetools.Message):
1516 def __init__(self, fp, seekable=1):
1517 Message = self.__class__
1518 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1519 self.encodingheader = self.getheader('content-transfer-encoding')
1520 self.typeheader = self.getheader('content-type')
1521 self.parsetype()
1522 self.parseplist()
1523
1524 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1525 def send_document(self, title, contents):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001526 try:
1527 self.send_response(200)
1528 self.send_header('Content-Type', 'text/html')
1529 self.end_headers()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001530 self.wfile.write(html.page(title, contents))
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001531 except IOError: pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001532
1533 def do_GET(self):
1534 path = self.path
1535 if path[-5:] == '.html': path = path[:-5]
1536 if path[:1] == '/': path = path[1:]
1537 if path and path != '.':
1538 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001539 obj = locate(path, forceload=1)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001540 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001541 self.send_document(path, html.escape(str(value)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001542 return
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001543 if obj:
1544 self.send_document(describe(obj), html.document(obj, path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001545 else:
1546 self.send_document(path,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001547'no Python documentation found for %s' % repr(path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001548 else:
1549 heading = html.heading(
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001550'<big><big><strong>Python: Index of Modules</strong></big></big>',
1551'#ffffff', '#7799ee')
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001552 def bltinlink(name):
1553 return '<a href="%s.html">%s</a>' % (name, name)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001554 names = filter(lambda x: x != '__main__',
1555 sys.builtin_module_names)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001556 contents = html.multicolumn(names, bltinlink)
1557 indices = ['<p>' + html.bigsection(
1558 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1559
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001560 seen = {}
1561 for dir in pathdirs():
1562 indices.append(html.index(dir, seen))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001563 contents = heading + join(indices) + '''<p align=right>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001564<small><small><font color="#909090" face="helvetica, arial"><strong>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001565pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font></small></small>'''
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001566 self.send_document('Index of Modules', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001567
1568 def log_message(self, *args): pass
1569
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001570 class DocServer(BaseHTTPServer.HTTPServer):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001571 def __init__(self, port, callback):
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001572 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001573 self.address = ('', port)
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001574 self.url = 'http://%s:%d/' % (host, port)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001575 self.callback = callback
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001576 self.base.__init__(self, self.address, self.handler)
1577
1578 def serve_until_quit(self):
1579 import select
1580 self.quit = 0
1581 while not self.quit:
1582 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1583 if rd: self.handle_request()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001584
1585 def server_activate(self):
1586 self.base.server_activate(self)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001587 if self.callback: self.callback(self)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001588
1589 DocServer.base = BaseHTTPServer.HTTPServer
1590 DocServer.handler = DocHandler
1591 DocHandler.MessageClass = Message
1592 try:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001593 try:
1594 DocServer(port, callback).serve_until_quit()
1595 except (KeyboardInterrupt, select.error):
1596 pass
1597 finally:
Ka-Ping Yee66246962001-04-12 11:59:50 +00001598 if completer: completer()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001599
1600# ----------------------------------------------------- graphical interface
1601
1602def gui():
1603 """Graphical interface (starts web server and pops up a control window)."""
1604 class GUI:
1605 def __init__(self, window, port=7464):
1606 self.window = window
1607 self.server = None
1608 self.scanner = None
1609
1610 import Tkinter
1611 self.server_frm = Tkinter.Frame(window)
1612 self.title_lbl = Tkinter.Label(self.server_frm,
1613 text='Starting server...\n ')
1614 self.open_btn = Tkinter.Button(self.server_frm,
1615 text='open browser', command=self.open, state='disabled')
1616 self.quit_btn = Tkinter.Button(self.server_frm,
1617 text='quit serving', command=self.quit, state='disabled')
1618
1619 self.search_frm = Tkinter.Frame(window)
1620 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
1621 self.search_ent = Tkinter.Entry(self.search_frm)
1622 self.search_ent.bind('<Return>', self.search)
1623 self.stop_btn = Tkinter.Button(self.search_frm,
1624 text='stop', pady=0, command=self.stop, state='disabled')
1625 if sys.platform == 'win32':
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001626 # Trying to hide and show this button crashes under Windows.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001627 self.stop_btn.pack(side='right')
1628
1629 self.window.title('pydoc')
1630 self.window.protocol('WM_DELETE_WINDOW', self.quit)
1631 self.title_lbl.pack(side='top', fill='x')
1632 self.open_btn.pack(side='left', fill='x', expand=1)
1633 self.quit_btn.pack(side='right', fill='x', expand=1)
1634 self.server_frm.pack(side='top', fill='x')
1635
1636 self.search_lbl.pack(side='left')
1637 self.search_ent.pack(side='right', fill='x', expand=1)
1638 self.search_frm.pack(side='top', fill='x')
1639 self.search_ent.focus_set()
1640
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001641 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001642 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001643 self.result_lst.bind('<Button-1>', self.select)
1644 self.result_lst.bind('<Double-Button-1>', self.goto)
1645 self.result_scr = Tkinter.Scrollbar(window,
1646 orient='vertical', command=self.result_lst.yview)
1647 self.result_lst.config(yscrollcommand=self.result_scr.set)
1648
1649 self.result_frm = Tkinter.Frame(window)
1650 self.goto_btn = Tkinter.Button(self.result_frm,
1651 text='go to selected', command=self.goto)
1652 self.hide_btn = Tkinter.Button(self.result_frm,
1653 text='hide results', command=self.hide)
1654 self.goto_btn.pack(side='left', fill='x', expand=1)
1655 self.hide_btn.pack(side='right', fill='x', expand=1)
1656
1657 self.window.update()
1658 self.minwidth = self.window.winfo_width()
1659 self.minheight = self.window.winfo_height()
1660 self.bigminheight = (self.server_frm.winfo_reqheight() +
1661 self.search_frm.winfo_reqheight() +
1662 self.result_lst.winfo_reqheight() +
1663 self.result_frm.winfo_reqheight())
1664 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
1665 self.expanded = 0
1666 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1667 self.window.wm_minsize(self.minwidth, self.minheight)
1668
1669 import threading
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001670 threading.Thread(
1671 target=serve, args=(port, self.ready, self.quit)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001672
1673 def ready(self, server):
1674 self.server = server
1675 self.title_lbl.config(
1676 text='Python documentation server at\n' + server.url)
1677 self.open_btn.config(state='normal')
1678 self.quit_btn.config(state='normal')
1679
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001680 def open(self, event=None, url=None):
1681 url = url or self.server.url
1682 try:
1683 import webbrowser
1684 webbrowser.open(url)
1685 except ImportError: # pre-webbrowser.py compatibility
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001686 if sys.platform == 'win32':
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001687 os.system('start "%s"' % url)
1688 elif sys.platform == 'mac':
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001689 try: import ic
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001690 except ImportError: pass
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001691 else: ic.launchurl(url)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001692 else:
1693 rc = os.system('netscape -remote "openURL(%s)" &' % url)
1694 if rc: os.system('netscape "%s" &' % url)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001695
1696 def quit(self, event=None):
1697 if self.server:
1698 self.server.quit = 1
1699 self.window.quit()
1700
1701 def search(self, event=None):
1702 key = self.search_ent.get()
1703 self.stop_btn.pack(side='right')
1704 self.stop_btn.config(state='normal')
1705 self.search_lbl.config(text='Searching for "%s"...' % key)
1706 self.search_ent.forget()
1707 self.search_lbl.pack(side='left')
1708 self.result_lst.delete(0, 'end')
1709 self.goto_btn.config(state='disabled')
1710 self.expand()
1711
1712 import threading
1713 if self.scanner:
1714 self.scanner.quit = 1
1715 self.scanner = ModuleScanner()
1716 threading.Thread(target=self.scanner.run,
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +00001717 args=(self.update, key, self.done)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001718
1719 def update(self, path, modname, desc):
1720 if modname[-9:] == '.__init__':
1721 modname = modname[:-9] + ' (package)'
1722 self.result_lst.insert('end',
1723 modname + ' - ' + (desc or '(no description)'))
1724
1725 def stop(self, event=None):
1726 if self.scanner:
1727 self.scanner.quit = 1
1728 self.scanner = None
1729
1730 def done(self):
1731 self.scanner = None
1732 self.search_lbl.config(text='Search for')
1733 self.search_lbl.pack(side='left')
1734 self.search_ent.pack(side='right', fill='x', expand=1)
1735 if sys.platform != 'win32': self.stop_btn.forget()
1736 self.stop_btn.config(state='disabled')
1737
1738 def select(self, event=None):
1739 self.goto_btn.config(state='normal')
1740
1741 def goto(self, event=None):
1742 selection = self.result_lst.curselection()
1743 if selection:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001744 modname = split(self.result_lst.get(selection[0]))[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001745 self.open(url=self.server.url + modname + '.html')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001746
1747 def collapse(self):
1748 if not self.expanded: return
1749 self.result_frm.forget()
1750 self.result_scr.forget()
1751 self.result_lst.forget()
1752 self.bigwidth = self.window.winfo_width()
1753 self.bigheight = self.window.winfo_height()
1754 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1755 self.window.wm_minsize(self.minwidth, self.minheight)
1756 self.expanded = 0
1757
1758 def expand(self):
1759 if self.expanded: return
1760 self.result_frm.pack(side='bottom', fill='x')
1761 self.result_scr.pack(side='right', fill='y')
1762 self.result_lst.pack(side='top', fill='both', expand=1)
1763 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
1764 self.window.wm_minsize(self.minwidth, self.bigminheight)
1765 self.expanded = 1
1766
1767 def hide(self, event=None):
1768 self.stop()
1769 self.collapse()
1770
1771 import Tkinter
1772 try:
1773 gui = GUI(Tkinter.Tk())
1774 Tkinter.mainloop()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001775 except KeyboardInterrupt:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001776 pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001777
1778# -------------------------------------------------- command-line interface
1779
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001780def ispath(x):
1781 return type(x) is types.StringType and find(x, os.sep) >= 0
1782
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001783def cli():
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001784 """Command-line interface (looks at sys.argv to decide what to do)."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001785 import getopt
1786 class BadUsage: pass
1787
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001788 # Scripts don't get the current directory in their path by default.
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00001789 scriptdir = os.path.dirname(sys.argv[0])
1790 if scriptdir in sys.path:
1791 sys.path.remove(scriptdir)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001792 sys.path.insert(0, '.')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001793
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001794 try:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001795 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001796 writing = 0
1797
1798 for opt, val in opts:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001799 if opt == '-g':
1800 gui()
1801 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001802 if opt == '-k':
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001803 apropos(val)
1804 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001805 if opt == '-p':
1806 try:
1807 port = int(val)
1808 except ValueError:
1809 raise BadUsage
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001810 def ready(server):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001811 print 'pydoc server ready at %s' % server.url
1812 def stopped():
1813 print 'pydoc server stopped'
1814 serve(port, ready, stopped)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001815 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001816 if opt == '-w':
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001817 writing = 1
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001818
1819 if not args: raise BadUsage
1820 for arg in args:
1821 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001822 if ispath(arg) and os.path.isfile(arg):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001823 arg = importfile(arg)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001824 if writing:
1825 if ispath(arg) and os.path.isdir(arg):
1826 writedocs(arg)
1827 else:
1828 writedoc(arg)
1829 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001830 doc(arg)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001831 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001832 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001833
1834 except (getopt.error, BadUsage):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001835 cmd = sys.argv[0]
1836 print """pydoc - the Python documentation tool
1837
1838%s <name> ...
1839 Show text documentation on something. <name> may be the name of a
1840 function, module, or package, or a dotted reference to a class or
1841 function within a module or module in a package. If <name> contains
1842 a '%s', it is used as the path to a Python source file to document.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001843
1844%s -k <keyword>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001845 Search for a keyword in the synopsis lines of all available modules.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001846
1847%s -p <port>
1848 Start an HTTP server on the given port on the local machine.
1849
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001850%s -g
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001851 Pop up a graphical interface for finding and serving documentation.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001852
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001853%s -w <name> ...
1854 Write out the HTML documentation for a module to a file in the current
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001855 directory. If <name> contains a '%s', it is treated as a filename; if
1856 it names a directory, documentation is written for all the contents.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001857""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001858
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001859if __name__ == '__main__': cli()