blob: 410d921209ef2a1fa5d74eb2ce042110db88f109 [file] [log] [blame]
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001#!/usr/bin/env python
Guido van Rossumfce538c2002-08-06 17:29:38 +00002# -*- coding: Latin-1 -*-
Ka-Ping Yee1d384632001-03-01 00:24:32 +00003"""Generate Python documentation in HTML or text for interactive use.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00004
Ka-Ping Yeedd175342001-02-27 14:43:46 +00005In the Python interpreter, do "from pydoc import help" to provide online
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00006help. Calling help(thing) on a Python object documents the object.
7
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00008Or, at the shell command line outside of Python:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00009
Ka-Ping Yee37f7b382001-03-23 00:12:53 +000010Run "pydoc <name>" to show documentation on something. <name> may be
11the name of a function, module, package, or a dotted reference to a
12class or function within a module or module in a package. If the
13argument contains a path segment delimiter (e.g. slash on Unix,
14backslash on Windows) it is treated as the path to a Python source file.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000015
Ka-Ping Yee37f7b382001-03-23 00:12:53 +000016Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines
17of all available modules.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000018
Ka-Ping Yee37f7b382001-03-23 00:12:53 +000019Run "pydoc -p <port>" to start an HTTP server on a given port on the
20local machine to generate documentation web pages.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000021
Ka-Ping Yee37f7b382001-03-23 00:12:53 +000022For platforms without a command line, "pydoc -g" starts the HTTP server
23and also pops up a little window for controlling it.
24
25Run "pydoc -w <name>" to write out the HTML documentation for a module
26to a file named "<name>.html".
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000027"""
Ka-Ping Yeedd175342001-02-27 14:43:46 +000028
29__author__ = "Ka-Ping Yee <ping@lfw.org>"
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +000030__date__ = "26 February 2001"
Ka-Ping Yee09d7d9a2001-02-27 22:43:48 +000031__version__ = "$Revision$"
Ka-Ping Yee5e2b1732001-02-27 23:35:09 +000032__credits__ = """Guido van Rossum, for an excellent programming language.
33Tommy Burnette, the original creator of manpy.
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +000034Paul Prescod, for all his work on onlinehelp.
35Richard Chamberlain, for the first implementation of textdoc.
36
Ka-Ping Yee5e2b1732001-02-27 23:35:09 +000037Mynd you, møøse bites Kan be pretty nasti..."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +000038
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +000039# Known bugs that can't be fixed here:
40# - imp.load_module() cannot be prevented from clobbering existing
41# loaded modules, so calling synopsis() on a binary module file
42# changes the contents of any existing module with the same name.
43# - If the __file__ attribute on a module is a relative path and
44# the current directory is changed with os.chdir(), an incorrect
45# path will be displayed.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000046
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +000047import sys, imp, os, re, types, inspect, __builtin__
Ka-Ping Yeedd175342001-02-27 14:43:46 +000048from repr import Repr
Ka-Ping Yee3bda8792001-03-23 13:17:50 +000049from string import expandtabs, find, join, lower, split, strip, rfind, rstrip
Ka-Ping Yeedd175342001-02-27 14:43:46 +000050
51# --------------------------------------------------------- common routines
52
Ka-Ping Yeedd175342001-02-27 14:43:46 +000053def pathdirs():
54 """Convert sys.path into a list of absolute, existing, unique paths."""
55 dirs = []
Ka-Ping Yee1d384632001-03-01 00:24:32 +000056 normdirs = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +000057 for dir in sys.path:
58 dir = os.path.abspath(dir or '.')
Ka-Ping Yee1d384632001-03-01 00:24:32 +000059 normdir = os.path.normcase(dir)
60 if normdir not in normdirs and os.path.isdir(dir):
Ka-Ping Yeedd175342001-02-27 14:43:46 +000061 dirs.append(dir)
Ka-Ping Yee1d384632001-03-01 00:24:32 +000062 normdirs.append(normdir)
Ka-Ping Yeedd175342001-02-27 14:43:46 +000063 return dirs
64
65def getdoc(object):
66 """Get the doc string or comments for an object."""
Ka-Ping Yee3bda8792001-03-23 13:17:50 +000067 result = inspect.getdoc(object) or inspect.getcomments(object)
Ka-Ping Yee239432a2001-03-02 02:45:08 +000068 return result and re.sub('^ *\n', '', rstrip(result)) or ''
Ka-Ping Yeedd175342001-02-27 14:43:46 +000069
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +000070def splitdoc(doc):
71 """Split a doc string into a synopsis line (if any) and the rest."""
72 lines = split(strip(doc), '\n')
73 if len(lines) == 1:
74 return lines[0], ''
75 elif len(lines) >= 2 and not rstrip(lines[1]):
76 return lines[0], join(lines[2:], '\n')
77 return '', join(lines, '\n')
78
Ka-Ping Yeedd175342001-02-27 14:43:46 +000079def classname(object, modname):
80 """Get a class name and qualify it with a module name if necessary."""
81 name = object.__name__
82 if object.__module__ != modname:
83 name = object.__module__ + '.' + name
84 return name
85
Ka-Ping Yeedec96e92001-04-13 09:55:49 +000086def isdata(object):
87 """Check if an object is of a type that probably means it's data."""
88 return not (inspect.ismodule(object) or inspect.isclass(object) or
89 inspect.isroutine(object) or inspect.isframe(object) or
90 inspect.istraceback(object) or inspect.iscode(object))
Ka-Ping Yeedd175342001-02-27 14:43:46 +000091
92def replace(text, *pairs):
93 """Do a series of global replacements on a string."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +000094 while pairs:
95 text = join(split(text, pairs[0]), pairs[1])
96 pairs = pairs[2:]
Ka-Ping Yeedd175342001-02-27 14:43:46 +000097 return text
98
99def cram(text, maxlen):
100 """Omit part of a string if needed to make it fit in a maximum length."""
101 if len(text) > maxlen:
Raymond Hettingerfca3bb62002-10-21 04:44:11 +0000102 pre = max(0, (maxlen-3)//2)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000103 post = max(0, maxlen-3-pre)
104 return text[:pre] + '...' + text[len(text)-post:]
105 return text
106
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000107def stripid(text):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000108 """Remove the hexadecimal id from a Python object representation."""
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000109 # The behaviour of %p is implementation-dependent; we check two cases.
Ka-Ping Yee45daeb02002-08-11 15:11:33 +0000110 for pattern in [' at 0x[0-9a-f]{6,}(>+)$', ' at [0-9A-F]{8,}(>+)$']:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000111 if re.search(pattern, repr(Exception)):
Ka-Ping Yee45daeb02002-08-11 15:11:33 +0000112 return re.sub(pattern, '\\1', text)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000113 return text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000114
Tim Peters536d2262001-09-20 05:13:38 +0000115def _is_some_method(object):
116 return inspect.ismethod(object) or inspect.ismethoddescriptor(object)
117
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000118def allmethods(cl):
119 methods = {}
Tim Peters536d2262001-09-20 05:13:38 +0000120 for key, value in inspect.getmembers(cl, _is_some_method):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000121 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
Tim Petersfa26f7c2001-09-24 08:05:11 +0000128def _split_list(s, predicate):
129 """Split sequence s via predicate, and return pair ([true], [false]).
130
131 The return value is a 2-tuple of lists,
132 ([x for x in s if predicate(x)],
133 [x for x in s if not predicate(x)])
134 """
135
Tim Peters28355492001-09-23 21:29:55 +0000136 yes = []
137 no = []
Tim Petersfa26f7c2001-09-24 08:05:11 +0000138 for x in s:
139 if predicate(x):
140 yes.append(x)
Tim Peters28355492001-09-23 21:29:55 +0000141 else:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000142 no.append(x)
Tim Peters28355492001-09-23 21:29:55 +0000143 return yes, no
144
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000145def visiblename(name):
146 """Decide whether to show documentation on a variable."""
147 # Certain special names are redundant.
148 if name in ['__builtins__', '__doc__', '__file__', '__path__',
149 '__module__', '__name__']: return 0
150 # Private names are hidden, but special names are displayed.
151 if name.startswith('__') and name.endswith('__'): return 1
152 return not name.startswith('_')
153
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000154# ----------------------------------------------------- module manipulation
155
156def ispackage(path):
157 """Guess whether a path refers to a package directory."""
158 if os.path.isdir(path):
159 for ext in ['.py', '.pyc', '.pyo']:
160 if os.path.isfile(os.path.join(path, '__init__' + ext)):
Tim Petersbc0e9102002-04-04 22:55:58 +0000161 return True
162 return False
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000163
164def synopsis(filename, cache={}):
165 """Get the one-line summary out of a module file."""
Raymond Hettinger32200ae2002-06-01 19:51:15 +0000166 mtime = os.stat(filename).st_mtime
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000167 lastupdate, result = cache.get(filename, (0, None))
168 if lastupdate < mtime:
169 info = inspect.getmoduleinfo(filename)
170 file = open(filename)
171 if info and 'b' in info[2]: # binary modules have to be imported
172 try: module = imp.load_module('__temp__', file, filename, info[1:])
173 except: return None
174 result = split(module.__doc__ or '', '\n')[0]
175 del sys.modules['__temp__']
176 else: # text modules can be directly examined
177 line = file.readline()
178 while line[:1] == '#' or not strip(line):
179 line = file.readline()
180 if not line: break
181 line = strip(line)
182 if line[:4] == 'r"""': line = line[1:]
183 if line[:3] == '"""':
184 line = line[3:]
185 if line[-1:] == '\\': line = line[:-1]
186 while not strip(line):
187 line = file.readline()
188 if not line: break
189 result = strip(split(line, '"""')[0])
190 else: result = None
191 file.close()
192 cache[filename] = (mtime, result)
193 return result
194
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000195class ErrorDuringImport(Exception):
196 """Errors that occurred while trying to import something to document it."""
197 def __init__(self, filename, (exc, value, tb)):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000198 self.filename = filename
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000199 self.exc = exc
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000200 self.value = value
201 self.tb = tb
202
203 def __str__(self):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000204 exc = self.exc
205 if type(exc) is types.ClassType:
206 exc = exc.__name__
207 return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000208
209def importfile(path):
210 """Import a Python source file or compiled file given its path."""
211 magic = imp.get_magic()
212 file = open(path, 'r')
213 if file.read(len(magic)) == magic:
214 kind = imp.PY_COMPILED
215 else:
216 kind = imp.PY_SOURCE
217 file.close()
218 filename = os.path.basename(path)
219 name, ext = os.path.splitext(filename)
220 file = open(path, 'r')
221 try:
222 module = imp.load_module(name, file, path, (ext, 'r', kind))
223 except:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000224 raise ErrorDuringImport(path, sys.exc_info())
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000225 file.close()
226 return module
227
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000228def safeimport(path, forceload=0, cache={}):
229 """Import a module; handle errors; return None if the module isn't found.
230
231 If the module *is* found but an exception occurs, it's wrapped in an
232 ErrorDuringImport exception and reraised. Unlike __import__, if a
233 package path is specified, the module at the end of the path is returned,
234 not the package at the beginning. If the optional 'forceload' argument
235 is 1, we reload the module from disk (unless it's a dynamic extension)."""
Raymond Hettinger54f02222002-06-01 14:18:47 +0000236 if forceload and path in sys.modules:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000237 # This is the only way to be sure. Checking the mtime of the file
238 # isn't good enough (e.g. what if the module contains a class that
239 # inherits from another module that has changed?).
240 if path not in sys.builtin_module_names:
241 # Python never loads a dynamic extension a second time from the
242 # same path, even if the file is changed or missing. Deleting
243 # the entry in sys.modules doesn't help for dynamic extensions,
244 # so we're not even going to try to keep them up to date.
245 info = inspect.getmoduleinfo(sys.modules[path].__file__)
246 if info[3] != imp.C_EXTENSION:
247 cache[path] = sys.modules[path] # prevent module from clearing
248 del sys.modules[path]
249 try:
250 module = __import__(path)
251 except:
252 # Did the error occur before or after the module was found?
253 (exc, value, tb) = info = sys.exc_info()
Raymond Hettinger54f02222002-06-01 14:18:47 +0000254 if path in sys.modules:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000255 # An error occured while executing the imported module.
256 raise ErrorDuringImport(sys.modules[path].__file__, info)
257 elif exc is SyntaxError:
258 # A SyntaxError occurred before we could execute the module.
259 raise ErrorDuringImport(value.filename, info)
260 elif exc is ImportError and \
261 split(lower(str(value)))[:2] == ['no', 'module']:
262 # The module was not found.
263 return None
264 else:
265 # Some other error occurred during the importing process.
266 raise ErrorDuringImport(path, sys.exc_info())
267 for part in split(path, '.')[1:]:
268 try: module = getattr(module, part)
269 except AttributeError: return None
270 return module
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000271
272# ---------------------------------------------------- formatter base class
273
274class Doc:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000275 def document(self, object, name=None, *args):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000276 """Generate documentation for an object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000277 args = (object, name) + args
Brett Cannon28a4f0f2003-06-11 23:38:55 +0000278 # 'try' clause is to attempt to handle the possibility that inspect
279 # identifies something in a way that pydoc itself has issues handling;
280 # think 'super' and how it is a descriptor (which raises the exception
281 # by lacking a __name__ attribute) and an instance.
282 try:
283 if inspect.ismodule(object): return self.docmodule(*args)
284 if inspect.isclass(object): return self.docclass(*args)
285 if inspect.isroutine(object): return self.docroutine(*args)
286 except AttributeError:
287 pass
Guido van Rossum68468eb2003-02-27 20:14:51 +0000288 return self.docother(*args)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000289
290 def fail(self, object, name=None, *args):
291 """Raise an exception for unimplemented types."""
292 message = "don't know how to document object%s of type %s" % (
293 name and ' ' + repr(name), type(object).__name__)
294 raise TypeError, message
295
296 docmodule = docclass = docroutine = docother = fail
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000297
298# -------------------------------------------- HTML documentation generator
299
300class HTMLRepr(Repr):
301 """Class for safely making an HTML representation of a Python object."""
302 def __init__(self):
303 Repr.__init__(self)
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000304 self.maxlist = self.maxtuple = 20
305 self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000306 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000307
308 def escape(self, text):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000309 return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000310
311 def repr(self, object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000312 return Repr.repr(self, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000313
314 def repr1(self, x, level):
Skip Montanaro0fe8fce2003-06-27 15:45:41 +0000315 if hasattr(type(x), '__name__'):
316 methodname = 'repr_' + join(split(type(x).__name__), '_')
317 if hasattr(self, methodname):
318 return getattr(self, methodname)(x, level)
319 return self.escape(cram(stripid(repr(x)), self.maxother))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000320
321 def repr_string(self, x, level):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000322 test = cram(x, self.maxstring)
323 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000324 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000325 # Backslashes are only literal in the string and are never
326 # needed to make any special characters, so show a raw string.
327 return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000328 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000329 r'<font color="#c040c0">\1</font>',
330 self.escape(testrepr))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000331
Skip Montanarodf708782002-03-07 22:58:02 +0000332 repr_str = repr_string
333
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000334 def repr_instance(self, x, level):
335 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000336 return self.escape(cram(stripid(repr(x)), self.maxstring))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000337 except:
338 return self.escape('<%s instance>' % x.__class__.__name__)
339
340 repr_unicode = repr_string
341
342class HTMLDoc(Doc):
343 """Formatter class for HTML documentation."""
344
345 # ------------------------------------------- HTML formatting utilities
346
347 _repr_instance = HTMLRepr()
348 repr = _repr_instance.repr
349 escape = _repr_instance.escape
350
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000351 def page(self, title, contents):
352 """Format an HTML page."""
353 return '''
Tim Peters59ed4482001-10-31 04:20:26 +0000354<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000355<html><head><title>Python: %s</title>
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000356</head><body bgcolor="#f0f0f8">
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000357%s
358</body></html>''' % (title, contents)
359
360 def heading(self, title, fgcol, bgcol, extras=''):
361 """Format a page heading."""
362 return '''
Tim Peters59ed4482001-10-31 04:20:26 +0000363<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading">
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000364<tr bgcolor="%s">
Tim Peters2306d242001-09-25 03:18:32 +0000365<td valign=bottom>&nbsp;<br>
366<font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000367><td align=right valign=bottom
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000368><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000369 ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
370
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000371 def section(self, title, fgcol, bgcol, contents, width=6,
372 prelude='', marginalia=None, gap='&nbsp;'):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000373 """Format a section with a heading."""
374 if marginalia is None:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000375 marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000376 result = '''<p>
Tim Peters59ed4482001-10-31 04:20:26 +0000377<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000378<tr bgcolor="%s">
Tim Peters2306d242001-09-25 03:18:32 +0000379<td colspan=3 valign=bottom>&nbsp;<br>
380<font color="%s" face="helvetica, arial">%s</font></td></tr>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000381 ''' % (bgcol, fgcol, title)
382 if prelude:
383 result = result + '''
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000384<tr bgcolor="%s"><td rowspan=2>%s</td>
385<td colspan=2>%s</td></tr>
386<tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
387 else:
388 result = result + '''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000389<tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000390
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000391 return result + '\n<td width="100%%">%s</td></tr></table>' % contents
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000392
393 def bigsection(self, title, *args):
394 """Format a section with a big heading."""
395 title = '<big><strong>%s</strong></big>' % title
Guido van Rossum68468eb2003-02-27 20:14:51 +0000396 return self.section(title, *args)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000397
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000398 def preformat(self, text):
399 """Format literal preformatted text."""
400 text = self.escape(expandtabs(text))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000401 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
402 ' ', '&nbsp;', '\n', '<br>\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000403
404 def multicolumn(self, list, format, cols=4):
405 """Format a list of items into a multi-column list."""
406 result = ''
407 rows = (len(list)+cols-1)/cols
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000408 for col in range(cols):
409 result = result + '<td width="%d%%" valign=top>' % (100/cols)
410 for i in range(rows*col, rows*col+rows):
411 if i < len(list):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000412 result = result + format(list[i]) + '<br>\n'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000413 result = result + '</td>'
Tim Peters59ed4482001-10-31 04:20:26 +0000414 return '<table width="100%%" summary="list"><tr>%s</tr></table>' % result
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000415
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000416 def grey(self, text): return '<font color="#909090">%s</font>' % text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000417
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000418 def namelink(self, name, *dicts):
419 """Make a link for an identifier, given name-to-URL mappings."""
420 for dict in dicts:
Raymond Hettinger54f02222002-06-01 14:18:47 +0000421 if name in dict:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000422 return '<a href="%s">%s</a>' % (dict[name], name)
423 return name
424
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000425 def classlink(self, object, modname):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000426 """Make a link for a class."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000427 name, module = object.__name__, sys.modules.get(object.__module__)
428 if hasattr(module, name) and getattr(module, name) is object:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000429 return '<a href="%s.html#%s">%s</a>' % (
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000430 module.__name__, name, classname(object, modname))
431 return classname(object, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000432
433 def modulelink(self, object):
434 """Make a link for a module."""
435 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
436
437 def modpkglink(self, (name, path, ispackage, shadowed)):
438 """Make a link for a module or package to display in an index."""
439 if shadowed:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000440 return self.grey(name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000441 if path:
442 url = '%s.%s.html' % (path, name)
443 else:
444 url = '%s.html' % name
445 if ispackage:
446 text = '<strong>%s</strong>&nbsp;(package)' % name
447 else:
448 text = name
449 return '<a href="%s">%s</a>' % (url, text)
450
451 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
452 """Mark up some plain text, given a context of symbols to look for.
453 Each context dictionary maps object names to anchor names."""
454 escape = escape or self.escape
455 results = []
456 here = 0
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000457 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
458 r'RFC[- ]?(\d+)|'
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000459 r'PEP[- ]?(\d+)|'
Neil Schemenauerd69711c2002-03-24 23:02:07 +0000460 r'(self\.)?(\w+))')
Guido van Rossum8ca162f2002-04-07 06:36:23 +0000461 while True:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000462 match = pattern.search(text, here)
463 if not match: break
464 start, end = match.span()
465 results.append(escape(text[here:start]))
466
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000467 all, scheme, rfc, pep, selfdot, name = match.groups()
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000468 if scheme:
Neil Schemenauercddc1a02002-03-24 23:11:21 +0000469 url = escape(all).replace('"', '&quot;')
470 results.append('<a href="%s">%s</a>' % (url, url))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000471 elif rfc:
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000472 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
473 results.append('<a href="%s">%s</a>' % (url, escape(all)))
474 elif pep:
475 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000476 results.append('<a href="%s">%s</a>' % (url, escape(all)))
477 elif text[end:end+1] == '(':
478 results.append(self.namelink(name, methods, funcs, classes))
479 elif selfdot:
480 results.append('self.<strong>%s</strong>' % name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000481 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000482 results.append(self.namelink(name, classes))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000483 here = end
484 results.append(escape(text[here:]))
485 return join(results, '')
486
487 # ---------------------------------------------- type-specific routines
488
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000489 def formattree(self, tree, modname, parent=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000490 """Produce HTML for a class tree as given by inspect.getclasstree()."""
491 result = ''
492 for entry in tree:
493 if type(entry) is type(()):
494 c, bases = entry
Tim Peters2306d242001-09-25 03:18:32 +0000495 result = result + '<dt><font face="helvetica, arial">'
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000496 result = result + self.classlink(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000497 if bases and bases != (parent,):
498 parents = []
499 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000500 parents.append(self.classlink(base, modname))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000501 result = result + '(' + join(parents, ', ') + ')'
Tim Peters2306d242001-09-25 03:18:32 +0000502 result = result + '\n</font></dt>'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000503 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000504 result = result + '<dd>\n%s</dd>\n' % self.formattree(
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000505 entry, modname, c)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000506 return '<dl>\n%s</dl>\n' % result
507
Tim Peters8dd7ade2001-10-18 19:56:17 +0000508 def docmodule(self, object, name=None, mod=None, *ignored):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000509 """Produce HTML documentation for a module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000510 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000511 parts = split(name, '.')
512 links = []
513 for i in range(len(parts)-1):
514 links.append(
515 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
516 (join(parts[:i+1], '.'), parts[i]))
517 linkedname = join(links + parts[-1:], '.')
518 head = '<big><big><strong>%s</strong></big></big>' % linkedname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000519 try:
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000520 path = inspect.getabsfile(object)
Ka-Ping Yee6191a232001-04-13 15:00:27 +0000521 url = path
522 if sys.platform == 'win32':
523 import nturl2path
524 url = nturl2path.pathname2url(path)
525 filelink = '<a href="file:%s">%s</a>' % (url, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000526 except TypeError:
527 filelink = '(built-in)'
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000528 info = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000529 if hasattr(object, '__version__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000530 version = str(object.__version__)
Ka-Ping Yee40c49912001-02-27 22:46:01 +0000531 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
532 version = strip(version[11:-1])
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000533 info.append('version %s' % self.escape(version))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000534 if hasattr(object, '__date__'):
535 info.append(self.escape(str(object.__date__)))
536 if info:
537 head = head + ' (%s)' % join(info, ', ')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000538 result = self.heading(
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000539 head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)
540
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000541 modules = inspect.getmembers(object, inspect.ismodule)
542
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000543 classes, cdict = [], {}
544 for key, value in inspect.getmembers(object, inspect.isclass):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000545 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000546 if visiblename(key):
547 classes.append((key, value))
548 cdict[key] = cdict[value] = '#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000549 for key, value in classes:
550 for base in value.__bases__:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000551 key, modname = base.__name__, base.__module__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000552 module = sys.modules.get(modname)
553 if modname != name and module and hasattr(module, key):
554 if getattr(module, key) is base:
Raymond Hettinger54f02222002-06-01 14:18:47 +0000555 if not key in cdict:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000556 cdict[key] = cdict[base] = modname + '.html#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000557 funcs, fdict = [], {}
558 for key, value in inspect.getmembers(object, inspect.isroutine):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000559 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000560 if visiblename(key):
561 funcs.append((key, value))
562 fdict[key] = '#-' + key
563 if inspect.isfunction(value): fdict[value] = fdict[key]
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000564 data = []
565 for key, value in inspect.getmembers(object, isdata):
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000566 if visiblename(key):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000567 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000568
569 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
570 doc = doc and '<tt>%s</tt>' % doc
Tim Peters2306d242001-09-25 03:18:32 +0000571 result = result + '<p>%s</p>\n' % doc
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000572
573 if hasattr(object, '__path__'):
574 modpkgs = []
575 modnames = []
576 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000577 path = os.path.join(object.__path__[0], file)
578 modname = inspect.getmodulename(file)
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000579 if modname != '__init__':
580 if modname and modname not in modnames:
581 modpkgs.append((modname, name, 0, 0))
582 modnames.append(modname)
583 elif ispackage(path):
584 modpkgs.append((file, name, 1, 0))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000585 modpkgs.sort()
586 contents = self.multicolumn(modpkgs, self.modpkglink)
587 result = result + self.bigsection(
588 'Package Contents', '#ffffff', '#aa55cc', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000589 elif modules:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000590 contents = self.multicolumn(
591 modules, lambda (key, value), s=self: s.modulelink(value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000592 result = result + self.bigsection(
593 'Modules', '#fffff', '#aa55cc', contents)
594
595 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000596 classlist = map(lambda (key, value): value, classes)
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000597 contents = [
598 self.formattree(inspect.getclasstree(classlist, 1), name)]
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000599 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000600 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000601 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000602 'Classes', '#ffffff', '#ee77aa', join(contents))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000603 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000604 contents = []
605 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000606 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000607 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000608 'Functions', '#ffffff', '#eeaa77', join(contents))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000609 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000610 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000611 for key, value in data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000612 contents.append(self.document(value, key))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000613 result = result + self.bigsection(
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000614 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000615 if hasattr(object, '__author__'):
616 contents = self.markup(str(object.__author__), self.preformat)
617 result = result + self.bigsection(
618 'Author', '#ffffff', '#7799ee', contents)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000619 if hasattr(object, '__credits__'):
620 contents = self.markup(str(object.__credits__), self.preformat)
621 result = result + self.bigsection(
622 'Credits', '#ffffff', '#7799ee', contents)
623
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000624 return result
625
Tim Peters8dd7ade2001-10-18 19:56:17 +0000626 def docclass(self, object, name=None, mod=None, funcs={}, classes={},
627 *ignored):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000628 """Produce HTML documentation for a class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000629 realname = object.__name__
630 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000631 bases = object.__bases__
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000632
Tim Petersb47879b2001-09-24 04:47:19 +0000633 contents = []
634 push = contents.append
635
Tim Petersfa26f7c2001-09-24 08:05:11 +0000636 # Cute little class to pump out a horizontal rule between sections.
637 class HorizontalRule:
638 def __init__(self):
639 self.needone = 0
640 def maybe(self):
641 if self.needone:
642 push('<hr>\n')
643 self.needone = 1
644 hr = HorizontalRule()
645
Tim Petersc86f6ca2001-09-26 21:31:51 +0000646 # List the mro, if non-trivial.
Tim Peters351e3622001-09-27 03:29:51 +0000647 mro = list(inspect.getmro(object))
Tim Petersc86f6ca2001-09-26 21:31:51 +0000648 if len(mro) > 2:
649 hr.maybe()
650 push('<dl><dt>Method resolution order:</dt>\n')
651 for base in mro:
652 push('<dd>%s</dd>\n' % self.classlink(base,
653 object.__module__))
654 push('</dl>\n')
655
Tim Petersb47879b2001-09-24 04:47:19 +0000656 def spill(msg, attrs, predicate):
Tim Petersfa26f7c2001-09-24 08:05:11 +0000657 ok, attrs = _split_list(attrs, predicate)
Tim Petersb47879b2001-09-24 04:47:19 +0000658 if ok:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000659 hr.maybe()
Tim Petersb47879b2001-09-24 04:47:19 +0000660 push(msg)
661 for name, kind, homecls, value in ok:
662 push(self.document(getattr(object, name), name, mod,
663 funcs, classes, mdict, object))
664 push('\n')
665 return attrs
666
Tim Petersfa26f7c2001-09-24 08:05:11 +0000667 def spillproperties(msg, attrs, predicate):
668 ok, attrs = _split_list(attrs, predicate)
Tim Petersb47879b2001-09-24 04:47:19 +0000669 if ok:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000670 hr.maybe()
Tim Petersb47879b2001-09-24 04:47:19 +0000671 push(msg)
672 for name, kind, homecls, value in ok:
Tim Peters3e767d12001-09-25 00:01:06 +0000673 push('<dl><dt><strong>%s</strong></dt>\n' % name)
674 if value.__doc__ is not None:
675 doc = self.markup(value.__doc__, self.preformat,
676 funcs, classes, mdict)
Tim Peters2306d242001-09-25 03:18:32 +0000677 push('<dd><tt>%s</tt></dd>\n' % doc)
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000678 for attr, tag in [('fget', '<em>get</em>'),
679 ('fset', '<em>set</em>'),
680 ('fdel', '<em>delete</em>')]:
Tim Peters3e767d12001-09-25 00:01:06 +0000681 func = getattr(value, attr)
682 if func is not None:
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000683 base = self.document(func, tag, mod,
Tim Peters3e767d12001-09-25 00:01:06 +0000684 funcs, classes, mdict, object)
685 push('<dd>%s</dd>\n' % base)
686 push('</dl>\n')
Tim Petersb47879b2001-09-24 04:47:19 +0000687 return attrs
688
Tim Petersfa26f7c2001-09-24 08:05:11 +0000689 def spilldata(msg, attrs, predicate):
690 ok, attrs = _split_list(attrs, predicate)
Tim Petersb47879b2001-09-24 04:47:19 +0000691 if ok:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000692 hr.maybe()
Tim Petersb47879b2001-09-24 04:47:19 +0000693 push(msg)
694 for name, kind, homecls, value in ok:
695 base = self.docother(getattr(object, name), name, mod)
Martin v. Löwise59e2ba2003-05-03 09:09:02 +0000696 if callable(value) or inspect.isdatadescriptor(value):
Guido van Rossum5e355b22002-05-21 20:56:15 +0000697 doc = getattr(value, "__doc__", None)
698 else:
699 doc = None
Tim Petersb47879b2001-09-24 04:47:19 +0000700 if doc is None:
701 push('<dl><dt>%s</dl>\n' % base)
702 else:
703 doc = self.markup(getdoc(value), self.preformat,
704 funcs, classes, mdict)
Tim Peters2306d242001-09-25 03:18:32 +0000705 doc = '<dd><tt>%s</tt>' % doc
Tim Petersb47879b2001-09-24 04:47:19 +0000706 push('<dl><dt>%s%s</dl>\n' % (base, doc))
707 push('\n')
708 return attrs
709
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000710 attrs = filter(lambda (name, kind, cls, value): visiblename(name),
711 inspect.classify_class_attrs(object))
Tim Petersb47879b2001-09-24 04:47:19 +0000712 mdict = {}
713 for key, kind, homecls, value in attrs:
714 mdict[key] = anchor = '#' + name + '-' + key
715 value = getattr(object, key)
716 try:
717 # The value may not be hashable (e.g., a data attr with
718 # a dict or list value).
719 mdict[value] = anchor
720 except TypeError:
721 pass
722
Tim Petersfa26f7c2001-09-24 08:05:11 +0000723 while attrs:
Tim Peters351e3622001-09-27 03:29:51 +0000724 if mro:
725 thisclass = mro.pop(0)
726 else:
727 thisclass = attrs[0][2]
Tim Petersfa26f7c2001-09-24 08:05:11 +0000728 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
729
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000730 if thisclass is __builtin__.object:
731 attrs = inherited
732 continue
733 elif thisclass is object:
734 tag = 'defined here'
Tim Petersb47879b2001-09-24 04:47:19 +0000735 else:
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000736 tag = 'inherited from %s' % self.classlink(thisclass,
737 object.__module__)
Tim Petersb47879b2001-09-24 04:47:19 +0000738 tag += ':<br>\n'
739
740 # Sort attrs by name.
741 attrs.sort(lambda t1, t2: cmp(t1[0], t2[0]))
742
743 # Pump out the attrs, segregated by kind.
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000744 attrs = spill('Methods %s' % tag, attrs,
Tim Petersb47879b2001-09-24 04:47:19 +0000745 lambda t: t[1] == 'method')
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000746 attrs = spill('Class methods %s' % tag, attrs,
Tim Petersb47879b2001-09-24 04:47:19 +0000747 lambda t: t[1] == 'class method')
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000748 attrs = spill('Static methods %s' % tag, attrs,
Tim Petersb47879b2001-09-24 04:47:19 +0000749 lambda t: t[1] == 'static method')
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000750 attrs = spillproperties('Properties %s' % tag, attrs,
Tim Petersfa26f7c2001-09-24 08:05:11 +0000751 lambda t: t[1] == 'property')
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000752 attrs = spilldata('Data and other attributes %s' % tag, attrs,
Tim Petersfa26f7c2001-09-24 08:05:11 +0000753 lambda t: t[1] == 'data')
Tim Petersb47879b2001-09-24 04:47:19 +0000754 assert attrs == []
Tim Peters351e3622001-09-27 03:29:51 +0000755 attrs = inherited
Tim Petersb47879b2001-09-24 04:47:19 +0000756
757 contents = ''.join(contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000758
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000759 if name == realname:
760 title = '<a name="%s">class <strong>%s</strong></a>' % (
761 name, realname)
762 else:
763 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
764 name, name, realname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000765 if bases:
766 parents = []
767 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000768 parents.append(self.classlink(base, object.__module__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000769 title = title + '(%s)' % join(parents, ', ')
Tim Peters2306d242001-09-25 03:18:32 +0000770 doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict)
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000771 doc = doc and '<tt>%s<br>&nbsp;</tt>' % doc
Tim Petersc86f6ca2001-09-26 21:31:51 +0000772
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000773 return self.section(title, '#000000', '#ffc8d8', contents, 3, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000774
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000775 def formatvalue(self, object):
776 """Format an argument default value as text."""
Tim Peters2306d242001-09-25 03:18:32 +0000777 return self.grey('=' + self.repr(object))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000778
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000779 def docroutine(self, object, name=None, mod=None,
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000780 funcs={}, classes={}, methods={}, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000781 """Produce HTML documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000782 realname = object.__name__
783 name = name or realname
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000784 anchor = (cl and cl.__name__ or '') + '-' + name
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000785 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000786 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000787 if inspect.ismethod(object):
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +0000788 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000789 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000790 if imclass is not cl:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000791 note = ' from ' + self.classlink(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000792 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000793 if object.im_self:
794 note = ' method of %s instance' % self.classlink(
795 object.im_self.__class__, mod)
796 else:
797 note = ' unbound %s method' % self.classlink(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000798 object = object.im_func
799
800 if name == realname:
801 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
802 else:
Raymond Hettinger54f02222002-06-01 14:18:47 +0000803 if (cl and realname in cl.__dict__ and
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000804 cl.__dict__[realname] is object):
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000805 reallink = '<a href="#%s">%s</a>' % (
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000806 cl.__name__ + '-' + realname, realname)
807 skipdocs = 1
808 else:
809 reallink = realname
810 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
811 anchor, name, reallink)
Tim Peters4bcfa312001-09-20 06:08:24 +0000812 if inspect.isfunction(object):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000813 args, varargs, varkw, defaults = inspect.getargspec(object)
814 argspec = inspect.formatargspec(
815 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000816 if realname == '<lambda>':
Tim Peters59ed4482001-10-31 04:20:26 +0000817 title = '<strong>%s</strong> <em>lambda</em> ' % name
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000818 argspec = argspec[1:-1] # remove parentheses
Tim Peters4bcfa312001-09-20 06:08:24 +0000819 else:
820 argspec = '(...)'
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000821
Tim Peters2306d242001-09-25 03:18:32 +0000822 decl = title + argspec + (note and self.grey(
823 '<font face="helvetica, arial">%s</font>' % note))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000824
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000825 if skipdocs:
Tim Peters2306d242001-09-25 03:18:32 +0000826 return '<dl><dt>%s</dt></dl>\n' % decl
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000827 else:
828 doc = self.markup(
829 getdoc(object), self.preformat, funcs, classes, methods)
Tim Peters2306d242001-09-25 03:18:32 +0000830 doc = doc and '<dd><tt>%s</tt></dd>' % doc
831 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000832
Tim Peters8dd7ade2001-10-18 19:56:17 +0000833 def docother(self, object, name=None, mod=None, *ignored):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000834 """Produce HTML documentation for a data object."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000835 lhs = name and '<strong>%s</strong> = ' % name or ''
836 return lhs + self.repr(object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000837
838 def index(self, dir, shadowed=None):
839 """Generate an HTML index for a directory of modules."""
840 modpkgs = []
841 if shadowed is None: shadowed = {}
842 seen = {}
843 files = os.listdir(dir)
844
845 def found(name, ispackage,
846 modpkgs=modpkgs, shadowed=shadowed, seen=seen):
Raymond Hettinger4f759d82002-11-02 02:02:46 +0000847 if name not in seen:
848 modpkgs.append((name, '', ispackage, name in shadowed))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000849 seen[name] = 1
850 shadowed[name] = 1
851
852 # Package spam/__init__.py takes precedence over module spam.py.
853 for file in files:
854 path = os.path.join(dir, file)
855 if ispackage(path): found(file, 1)
856 for file in files:
857 path = os.path.join(dir, file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000858 if os.path.isfile(path):
859 modname = inspect.getmodulename(file)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000860 if modname: found(modname, 0)
861
862 modpkgs.sort()
863 contents = self.multicolumn(modpkgs, self.modpkglink)
864 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
865
866# -------------------------------------------- text documentation generator
867
868class TextRepr(Repr):
869 """Class for safely making a text representation of a Python object."""
870 def __init__(self):
871 Repr.__init__(self)
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000872 self.maxlist = self.maxtuple = 20
873 self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000874 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000875
876 def repr1(self, x, level):
Skip Montanaro0fe8fce2003-06-27 15:45:41 +0000877 if hasattr(type(x), '__name__'):
878 methodname = 'repr_' + join(split(type(x).__name__), '_')
879 if hasattr(self, methodname):
880 return getattr(self, methodname)(x, level)
881 return cram(stripid(repr(x)), self.maxother)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000882
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000883 def repr_string(self, x, level):
884 test = cram(x, self.maxstring)
885 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000886 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000887 # Backslashes are only literal in the string and are never
888 # needed to make any special characters, so show a raw string.
889 return 'r' + testrepr[0] + test + testrepr[0]
890 return testrepr
891
Skip Montanarodf708782002-03-07 22:58:02 +0000892 repr_str = repr_string
893
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000894 def repr_instance(self, x, level):
895 try:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000896 return cram(stripid(repr(x)), self.maxstring)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000897 except:
898 return '<%s instance>' % x.__class__.__name__
899
900class TextDoc(Doc):
901 """Formatter class for text documentation."""
902
903 # ------------------------------------------- text formatting utilities
904
905 _repr_instance = TextRepr()
906 repr = _repr_instance.repr
907
908 def bold(self, text):
909 """Format a string in bold by overstriking."""
910 return join(map(lambda ch: ch + '\b' + ch, text), '')
911
912 def indent(self, text, prefix=' '):
913 """Indent text by prepending a given prefix to each line."""
914 if not text: return ''
915 lines = split(text, '\n')
916 lines = map(lambda line, prefix=prefix: prefix + line, lines)
917 if lines: lines[-1] = rstrip(lines[-1])
918 return join(lines, '\n')
919
920 def section(self, title, contents):
921 """Format a section with a given heading."""
922 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
923
924 # ---------------------------------------------- type-specific routines
925
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000926 def formattree(self, tree, modname, parent=None, prefix=''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000927 """Render in text a class tree as returned by inspect.getclasstree()."""
928 result = ''
929 for entry in tree:
930 if type(entry) is type(()):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000931 c, bases = entry
932 result = result + prefix + classname(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000933 if bases and bases != (parent,):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000934 parents = map(lambda c, m=modname: classname(c, m), bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000935 result = result + '(%s)' % join(parents, ', ')
936 result = result + '\n'
937 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000938 result = result + self.formattree(
939 entry, modname, c, prefix + ' ')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000940 return result
941
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000942 def docmodule(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000943 """Produce text documentation for a given module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000944 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000945 synop, desc = splitdoc(getdoc(object))
946 result = self.section('NAME', name + (synop and ' - ' + synop))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000947
948 try:
949 file = inspect.getabsfile(object)
950 except TypeError:
951 file = '(built-in)'
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000952 result = result + self.section('FILE', file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000953 if desc:
954 result = result + self.section('DESCRIPTION', desc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000955
956 classes = []
957 for key, value in inspect.getmembers(object, inspect.isclass):
958 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000959 if visiblename(key):
960 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000961 funcs = []
962 for key, value in inspect.getmembers(object, inspect.isroutine):
963 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000964 if visiblename(key):
965 funcs.append((key, value))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000966 data = []
967 for key, value in inspect.getmembers(object, isdata):
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000968 if visiblename(key):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000969 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000970
971 if hasattr(object, '__path__'):
972 modpkgs = []
973 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000974 path = os.path.join(object.__path__[0], file)
975 modname = inspect.getmodulename(file)
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +0000976 if modname != '__init__':
977 if modname and modname not in modpkgs:
978 modpkgs.append(modname)
979 elif ispackage(path):
980 modpkgs.append(file + ' (package)')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000981 modpkgs.sort()
982 result = result + self.section(
983 'PACKAGE CONTENTS', join(modpkgs, '\n'))
984
985 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000986 classlist = map(lambda (key, value): value, classes)
987 contents = [self.formattree(
988 inspect.getclasstree(classlist, 1), name)]
989 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000990 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000991 result = result + self.section('CLASSES', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000992
993 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000994 contents = []
995 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000996 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000997 result = result + self.section('FUNCTIONS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000998
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000999 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001000 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001001 for key, value in data:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001002 contents.append(self.docother(value, key, name, 70))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001003 result = result + self.section('DATA', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001004
1005 if hasattr(object, '__version__'):
1006 version = str(object.__version__)
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001007 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
1008 version = strip(version[11:-1])
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001009 result = result + self.section('VERSION', version)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +00001010 if hasattr(object, '__date__'):
1011 result = result + self.section('DATE', str(object.__date__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001012 if hasattr(object, '__author__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +00001013 result = result + self.section('AUTHOR', str(object.__author__))
1014 if hasattr(object, '__credits__'):
1015 result = result + self.section('CREDITS', str(object.__credits__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001016 return result
1017
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001018 def docclass(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001019 """Produce text documentation for a given class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001020 realname = object.__name__
1021 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001022 bases = object.__bases__
1023
Tim Petersc86f6ca2001-09-26 21:31:51 +00001024 def makename(c, m=object.__module__):
1025 return classname(c, m)
1026
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001027 if name == realname:
1028 title = 'class ' + self.bold(realname)
1029 else:
1030 title = self.bold(name) + ' = class ' + realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001031 if bases:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001032 parents = map(makename, bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001033 title = title + '(%s)' % join(parents, ', ')
1034
1035 doc = getdoc(object)
Tim Peters28355492001-09-23 21:29:55 +00001036 contents = doc and [doc + '\n'] or []
1037 push = contents.append
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001038
Tim Petersc86f6ca2001-09-26 21:31:51 +00001039 # List the mro, if non-trivial.
Tim Peters351e3622001-09-27 03:29:51 +00001040 mro = list(inspect.getmro(object))
Tim Petersc86f6ca2001-09-26 21:31:51 +00001041 if len(mro) > 2:
1042 push("Method resolution order:")
1043 for base in mro:
1044 push(' ' + makename(base))
1045 push('')
1046
Tim Petersf4aad8e2001-09-24 22:40:47 +00001047 # Cute little class to pump out a horizontal rule between sections.
1048 class HorizontalRule:
1049 def __init__(self):
1050 self.needone = 0
1051 def maybe(self):
1052 if self.needone:
1053 push('-' * 70)
1054 self.needone = 1
1055 hr = HorizontalRule()
1056
Tim Peters28355492001-09-23 21:29:55 +00001057 def spill(msg, attrs, predicate):
Tim Petersfa26f7c2001-09-24 08:05:11 +00001058 ok, attrs = _split_list(attrs, predicate)
Tim Peters28355492001-09-23 21:29:55 +00001059 if ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001060 hr.maybe()
Tim Peters28355492001-09-23 21:29:55 +00001061 push(msg)
1062 for name, kind, homecls, value in ok:
1063 push(self.document(getattr(object, name),
1064 name, mod, object))
1065 return attrs
1066
Tim Petersfa26f7c2001-09-24 08:05:11 +00001067 def spillproperties(msg, attrs, predicate):
1068 ok, attrs = _split_list(attrs, predicate)
Tim Peters28355492001-09-23 21:29:55 +00001069 if ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001070 hr.maybe()
Tim Peters28355492001-09-23 21:29:55 +00001071 push(msg)
1072 for name, kind, homecls, value in ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001073 push(name)
1074 need_blank_after_doc = 0
1075 doc = getdoc(value) or ''
1076 if doc:
1077 push(self.indent(doc))
1078 need_blank_after_doc = 1
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +00001079 for attr, tag in [('fget', '<get>'),
1080 ('fset', '<set>'),
1081 ('fdel', '<delete>')]:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001082 func = getattr(value, attr)
1083 if func is not None:
1084 if need_blank_after_doc:
1085 push('')
1086 need_blank_after_doc = 0
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +00001087 base = self.document(func, tag, mod)
Tim Petersf4aad8e2001-09-24 22:40:47 +00001088 push(self.indent(base))
Tim Peters28355492001-09-23 21:29:55 +00001089 return attrs
Tim Petersb47879b2001-09-24 04:47:19 +00001090
Tim Petersfa26f7c2001-09-24 08:05:11 +00001091 def spilldata(msg, attrs, predicate):
1092 ok, attrs = _split_list(attrs, predicate)
Tim Peters28355492001-09-23 21:29:55 +00001093 if ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001094 hr.maybe()
Tim Peters28355492001-09-23 21:29:55 +00001095 push(msg)
1096 for name, kind, homecls, value in ok:
Martin v. Löwise59e2ba2003-05-03 09:09:02 +00001097 if callable(value) or inspect.isdatadescriptor(value):
Guido van Rossum5e355b22002-05-21 20:56:15 +00001098 doc = getattr(value, "__doc__", None)
1099 else:
1100 doc = None
Tim Peters28355492001-09-23 21:29:55 +00001101 push(self.docother(getattr(object, name),
1102 name, mod, 70, doc) + '\n')
1103 return attrs
1104
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +00001105 attrs = filter(lambda (name, kind, cls, value): visiblename(name),
1106 inspect.classify_class_attrs(object))
Tim Petersfa26f7c2001-09-24 08:05:11 +00001107 while attrs:
Tim Peters351e3622001-09-27 03:29:51 +00001108 if mro:
1109 thisclass = mro.pop(0)
1110 else:
1111 thisclass = attrs[0][2]
Tim Petersfa26f7c2001-09-24 08:05:11 +00001112 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
1113
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +00001114 if thisclass is __builtin__.object:
1115 attrs = inherited
1116 continue
1117 elif thisclass is object:
Tim Peters28355492001-09-23 21:29:55 +00001118 tag = "defined here"
1119 else:
Tim Petersfa26f7c2001-09-24 08:05:11 +00001120 tag = "inherited from %s" % classname(thisclass,
1121 object.__module__)
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +00001122 filter(lambda t: not t[0].startswith('_'), attrs)
Tim Peters28355492001-09-23 21:29:55 +00001123
1124 # Sort attrs by name.
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +00001125 attrs.sort()
Tim Peters28355492001-09-23 21:29:55 +00001126
1127 # Pump out the attrs, segregated by kind.
Tim Petersf4aad8e2001-09-24 22:40:47 +00001128 attrs = spill("Methods %s:\n" % tag, attrs,
Tim Peters28355492001-09-23 21:29:55 +00001129 lambda t: t[1] == 'method')
Tim Petersf4aad8e2001-09-24 22:40:47 +00001130 attrs = spill("Class methods %s:\n" % tag, attrs,
Tim Peters28355492001-09-23 21:29:55 +00001131 lambda t: t[1] == 'class method')
Tim Petersf4aad8e2001-09-24 22:40:47 +00001132 attrs = spill("Static methods %s:\n" % tag, attrs,
Tim Peters28355492001-09-23 21:29:55 +00001133 lambda t: t[1] == 'static method')
Tim Petersf4aad8e2001-09-24 22:40:47 +00001134 attrs = spillproperties("Properties %s:\n" % tag, attrs,
Tim Petersfa26f7c2001-09-24 08:05:11 +00001135 lambda t: t[1] == 'property')
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +00001136 attrs = spilldata("Data and other attributes %s:\n" % tag, attrs,
1137 lambda t: t[1] == 'data')
Tim Peters28355492001-09-23 21:29:55 +00001138 assert attrs == []
Tim Peters351e3622001-09-27 03:29:51 +00001139 attrs = inherited
Tim Peters28355492001-09-23 21:29:55 +00001140
1141 contents = '\n'.join(contents)
1142 if not contents:
1143 return title + '\n'
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001144 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
1145
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001146 def formatvalue(self, object):
1147 """Format an argument default value as text."""
1148 return '=' + self.repr(object)
1149
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001150 def docroutine(self, object, name=None, mod=None, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001151 """Produce text documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001152 realname = object.__name__
1153 name = name or realname
1154 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001155 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001156 if inspect.ismethod(object):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001157 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001158 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001159 if imclass is not cl:
1160 note = ' from ' + classname(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001161 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +00001162 if object.im_self:
1163 note = ' method of %s instance' % classname(
1164 object.im_self.__class__, mod)
1165 else:
1166 note = ' unbound %s method' % classname(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001167 object = object.im_func
1168
1169 if name == realname:
1170 title = self.bold(realname)
1171 else:
Raymond Hettinger54f02222002-06-01 14:18:47 +00001172 if (cl and realname in cl.__dict__ and
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001173 cl.__dict__[realname] is object):
1174 skipdocs = 1
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001175 title = self.bold(name) + ' = ' + realname
Tim Peters4bcfa312001-09-20 06:08:24 +00001176 if inspect.isfunction(object):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001177 args, varargs, varkw, defaults = inspect.getargspec(object)
1178 argspec = inspect.formatargspec(
1179 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001180 if realname == '<lambda>':
1181 title = 'lambda'
1182 argspec = argspec[1:-1] # remove parentheses
Tim Peters4bcfa312001-09-20 06:08:24 +00001183 else:
1184 argspec = '(...)'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001185 decl = title + argspec + note
1186
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001187 if skipdocs:
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001188 return decl + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001189 else:
1190 doc = getdoc(object) or ''
1191 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001192
Tim Peters28355492001-09-23 21:29:55 +00001193 def docother(self, object, name=None, mod=None, maxlen=None, doc=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001194 """Produce text documentation for a data object."""
1195 repr = self.repr(object)
1196 if maxlen:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001197 line = (name and name + ' = ' or '') + repr
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001198 chop = maxlen - len(line)
1199 if chop < 0: repr = repr[:chop] + '...'
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001200 line = (name and self.bold(name) + ' = ' or '') + repr
Tim Peters28355492001-09-23 21:29:55 +00001201 if doc is not None:
1202 line += '\n' + self.indent(str(doc))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001203 return line
1204
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001205# --------------------------------------------------------- user interfaces
1206
1207def pager(text):
1208 """The first time this is called, determine what kind of pager to use."""
1209 global pager
1210 pager = getpager()
1211 pager(text)
1212
1213def getpager():
1214 """Decide what method to use for paging through text."""
1215 if type(sys.stdout) is not types.FileType:
1216 return plainpager
1217 if not sys.stdin.isatty() or not sys.stdout.isatty():
1218 return plainpager
Fred Drake0a66fcb2001-07-23 19:44:30 +00001219 if os.environ.get('TERM') in ['dumb', 'emacs']:
Fred Drake5e9eb982001-07-23 19:48:10 +00001220 return plainpager
Raymond Hettinger54f02222002-06-01 14:18:47 +00001221 if 'PAGER' in os.environ:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001222 if sys.platform == 'win32': # pipes completely broken in Windows
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001223 return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
1224 elif os.environ.get('TERM') in ['dumb', 'emacs']:
1225 return lambda text: pipepager(plain(text), os.environ['PAGER'])
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001226 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001227 return lambda text: pipepager(text, os.environ['PAGER'])
Andrew MacIntyre54e0eab2002-03-03 03:12:30 +00001228 if sys.platform == 'win32' or sys.platform.startswith('os2'):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001229 return lambda text: tempfilepager(plain(text), 'more <')
Skip Montanarod404bee2002-09-26 21:44:57 +00001230 if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001231 return lambda text: pipepager(text, 'less')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001232
1233 import tempfile
Guido van Rossum3b0a3292002-08-09 16:38:32 +00001234 (fd, filename) = tempfile.mkstemp()
1235 os.close(fd)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001236 try:
1237 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
1238 return lambda text: pipepager(text, 'more')
1239 else:
1240 return ttypager
1241 finally:
1242 os.unlink(filename)
1243
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001244def plain(text):
1245 """Remove boldface formatting from text."""
1246 return re.sub('.\b', '', text)
1247
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001248def pipepager(text, cmd):
1249 """Page through text by feeding it to another program."""
1250 pipe = os.popen(cmd, 'w')
1251 try:
1252 pipe.write(text)
1253 pipe.close()
1254 except IOError:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001255 pass # Ignore broken pipes caused by quitting the pager program.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001256
1257def tempfilepager(text, cmd):
1258 """Page through text by invoking a program on a temporary file."""
1259 import tempfile
Tim Peters550e4e52003-02-07 01:53:46 +00001260 filename = tempfile.mktemp()
1261 file = open(filename, 'w')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001262 file.write(text)
1263 file.close()
1264 try:
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001265 os.system(cmd + ' ' + filename)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001266 finally:
1267 os.unlink(filename)
1268
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001269def ttypager(text):
1270 """Page through text on a text terminal."""
1271 lines = split(plain(text), '\n')
1272 try:
1273 import tty
1274 fd = sys.stdin.fileno()
1275 old = tty.tcgetattr(fd)
1276 tty.setcbreak(fd)
1277 getchar = lambda: sys.stdin.read(1)
Ka-Ping Yee457aab22001-02-27 23:36:29 +00001278 except (ImportError, AttributeError):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001279 tty = None
1280 getchar = lambda: sys.stdin.readline()[:-1][:1]
1281
1282 try:
1283 r = inc = os.environ.get('LINES', 25) - 1
1284 sys.stdout.write(join(lines[:inc], '\n') + '\n')
1285 while lines[r:]:
1286 sys.stdout.write('-- more --')
1287 sys.stdout.flush()
1288 c = getchar()
1289
1290 if c in ['q', 'Q']:
1291 sys.stdout.write('\r \r')
1292 break
1293 elif c in ['\r', '\n']:
1294 sys.stdout.write('\r \r' + lines[r] + '\n')
1295 r = r + 1
1296 continue
1297 if c in ['b', 'B', '\x1b']:
1298 r = r - inc - inc
1299 if r < 0: r = 0
1300 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1301 r = r + inc
1302
1303 finally:
1304 if tty:
1305 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1306
1307def plainpager(text):
1308 """Simply print unformatted text. This is the ultimate fallback."""
1309 sys.stdout.write(plain(text))
1310
1311def describe(thing):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001312 """Produce a short description of the given thing."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001313 if inspect.ismodule(thing):
1314 if thing.__name__ in sys.builtin_module_names:
1315 return 'built-in module ' + thing.__name__
1316 if hasattr(thing, '__path__'):
1317 return 'package ' + thing.__name__
1318 else:
1319 return 'module ' + thing.__name__
1320 if inspect.isbuiltin(thing):
1321 return 'built-in function ' + thing.__name__
1322 if inspect.isclass(thing):
1323 return 'class ' + thing.__name__
1324 if inspect.isfunction(thing):
1325 return 'function ' + thing.__name__
1326 if inspect.ismethod(thing):
1327 return 'method ' + thing.__name__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001328 if type(thing) is types.InstanceType:
1329 return 'instance of ' + thing.__class__.__name__
1330 return type(thing).__name__
1331
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001332def locate(path, forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001333 """Locate an object by name or dotted path, importing as necessary."""
Guido van Rossum97dede02003-02-16 01:12:32 +00001334 parts = [part for part in split(path, '.') if part]
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001335 module, n = None, 0
1336 while n < len(parts):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001337 nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001338 if nextmodule: module, n = nextmodule, n + 1
1339 else: break
1340 if module:
1341 object = module
1342 for part in parts[n:]:
1343 try: object = getattr(object, part)
1344 except AttributeError: return None
1345 return object
1346 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001347 if hasattr(__builtin__, path):
1348 return getattr(__builtin__, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001349
1350# --------------------------------------- interactive interpreter interface
1351
1352text = TextDoc()
1353html = HTMLDoc()
1354
Ka-Ping Yee45daeb02002-08-11 15:11:33 +00001355def resolve(thing, forceload=0):
1356 """Given an object or a path to an object, get the object and its name."""
1357 if isinstance(thing, str):
1358 object = locate(thing, forceload)
1359 if not object:
1360 raise ImportError, 'no Python documentation found for %r' % thing
1361 return object, thing
1362 else:
1363 return thing, getattr(thing, '__name__', None)
1364
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001365def doc(thing, title='Python Library Documentation: %s', forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001366 """Display text documentation, given an object or a path to an object."""
Ka-Ping Yee45daeb02002-08-11 15:11:33 +00001367 try:
1368 object, name = resolve(thing, forceload)
1369 desc = describe(object)
1370 module = inspect.getmodule(object)
1371 if name and '.' in name:
1372 desc += ' in ' + name[:name.rfind('.')]
1373 elif module and module is not object:
1374 desc += ' in module ' + module.__name__
1375 pager(title % desc + '\n\n' + text.document(object, name))
1376 except (ImportError, ErrorDuringImport), value:
1377 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001378
Ka-Ping Yee45daeb02002-08-11 15:11:33 +00001379def writedoc(thing, forceload=0):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001380 """Write HTML documentation to a file in the current directory."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001381 try:
Ka-Ping Yee45daeb02002-08-11 15:11:33 +00001382 object, name = resolve(thing, forceload)
1383 page = html.page(describe(object), html.document(object, name))
1384 file = open(name + '.html', 'w')
1385 file.write(page)
1386 file.close()
1387 print 'wrote', name + '.html'
1388 except (ImportError, ErrorDuringImport), value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001389 print value
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001390
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001391def writedocs(dir, pkgpath='', done=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001392 """Write out HTML documentation for all modules in a directory tree."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001393 if done is None: done = {}
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001394 for file in os.listdir(dir):
1395 path = os.path.join(dir, file)
1396 if ispackage(path):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001397 writedocs(path, pkgpath + file + '.', done)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001398 elif os.path.isfile(path):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001399 modname = inspect.getmodulename(path)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001400 if modname:
Ka-Ping Yeed9e213e2003-03-28 16:35:51 +00001401 if modname == '__init__':
1402 modname = pkgpath[:-1] # remove trailing period
1403 else:
1404 modname = pkgpath + modname
1405 if modname not in done:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001406 done[modname] = 1
1407 writedoc(modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001408
1409class Helper:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001410 keywords = {
1411 'and': 'BOOLEAN',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001412 'assert': ('ref/assert', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001413 'break': ('ref/break', 'while for'),
1414 'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1415 'continue': ('ref/continue', 'while for'),
1416 'def': ('ref/function', ''),
1417 'del': ('ref/del', 'BASICMETHODS'),
1418 'elif': 'if',
1419 'else': ('ref/if', 'while for'),
1420 'except': 'try',
1421 'exec': ('ref/exec', ''),
1422 'finally': 'try',
1423 'for': ('ref/for', 'break continue while'),
1424 'from': 'import',
1425 'global': ('ref/global', 'NAMESPACES'),
1426 'if': ('ref/if', 'TRUTHVALUE'),
1427 'import': ('ref/import', 'MODULES'),
1428 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1429 'is': 'COMPARISON',
Neal Norwitz742dde42003-03-30 20:31:34 +00001430 'lambda': ('ref/lambdas', 'FUNCTIONS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001431 'not': 'BOOLEAN',
1432 'or': 'BOOLEAN',
Neal Norwitz742dde42003-03-30 20:31:34 +00001433 'pass': ('ref/pass', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001434 'print': ('ref/print', ''),
1435 'raise': ('ref/raise', 'EXCEPTIONS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001436 'return': ('ref/return', 'FUNCTIONS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001437 'try': ('ref/try', 'EXCEPTIONS'),
1438 'while': ('ref/while', 'break continue if TRUTHVALUE'),
Tim Petersfb05c4e2002-10-30 05:21:00 +00001439 'yield': ('ref/yield', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001440 }
1441
1442 topics = {
1443 'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001444 'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001445 'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'),
1446 'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'),
Neal Norwitz54f871e2003-05-26 13:49:54 +00001447 'UNICODE': ('ref/strings', 'encodings unicode SEQUENCES STRINGMETHODS FORMATTING TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001448 'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1449 'INTEGER': ('ref/integers', 'int range'),
1450 'FLOAT': ('ref/floating', 'float math'),
1451 'COMPLEX': ('ref/imaginary', 'complex cmath'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001452 'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001453 'MAPPINGS': 'DICTIONARIES',
1454 'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'),
1455 'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'),
1456 'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001457 'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001458 'FRAMEOBJECTS': 'TYPES',
1459 'TRACEBACKS': 'TYPES',
1460 'NONE': ('lib/bltin-null-object', ''),
1461 'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'),
1462 'FILES': ('lib/bltin-file-objects', ''),
1463 'SPECIALATTRIBUTES': ('lib/specialattrs', ''),
1464 'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'),
1465 'MODULES': ('lib/typesmodules', 'import'),
1466 'PACKAGES': 'import',
1467 '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'),
1468 'OPERATORS': 'EXPRESSIONS',
1469 'PRECEDENCE': 'EXPRESSIONS',
1470 'OBJECTS': ('ref/objects', 'TYPES'),
1471 'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001472 'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'),
1473 'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1474 'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'),
1475 'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'),
1476 'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'),
1477 'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'),
1478 'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'),
Neal Norwitz54f871e2003-05-26 13:49:54 +00001479 'EXECUTION': ('ref/execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'),
1480 'NAMESPACES': ('ref/naming', 'global ASSIGNMENT DELETION DYNAMICFEATURES'),
1481 'DYNAMICFEATURES': ('ref/dynamic-features', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001482 'SCOPING': 'NAMESPACES',
1483 'FRAMES': 'NAMESPACES',
1484 'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'),
Neal Norwitz54f871e2003-05-26 13:49:54 +00001485 'COERCIONS': ('ref/coercion-rules','CONVERSIONS'),
1486 'CONVERSIONS': ('ref/conversions', 'COERCIONS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001487 'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'),
1488 'SPECIALIDENTIFIERS': ('ref/id-classes', ''),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001489 'PRIVATENAMES': ('ref/atom-identifiers', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001490 'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1491 'TUPLES': 'SEQUENCES',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001492 'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001493 'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001494 'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001495 'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001496 'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'),
1497 'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001498 'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1499 'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'),
1500 'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'),
1501 'CALLS': ('ref/calls', 'EXPRESSIONS'),
1502 'POWER': ('ref/power', 'EXPRESSIONS'),
1503 'UNARY': ('ref/unary', 'EXPRESSIONS'),
1504 'BINARY': ('ref/binary', 'EXPRESSIONS'),
1505 'SHIFTING': ('ref/shifting', 'EXPRESSIONS'),
1506 'BITWISE': ('ref/bitwise', 'EXPRESSIONS'),
1507 'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'),
Neal Norwitzf98159c2003-02-07 20:49:40 +00001508 'BOOLEAN': ('ref/Booleans', 'EXPRESSIONS TRUTHVALUE'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001509 'ASSERTION': 'assert',
1510 'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001511 'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001512 'DELETION': 'del',
1513 'PRINTING': 'print',
1514 'RETURNING': 'return',
1515 'IMPORTING': 'import',
1516 'CONDITIONAL': 'if',
1517 'LOOPING': ('ref/compound', 'for while break continue'),
1518 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001519 'DEBUGGING': ('lib/module-pdb', 'pdb'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001520 }
1521
1522 def __init__(self, input, output):
1523 self.input = input
1524 self.output = output
1525 self.docdir = None
1526 execdir = os.path.dirname(sys.executable)
1527 homedir = os.environ.get('PYTHONHOME')
1528 for dir in [os.environ.get('PYTHONDOCS'),
1529 homedir and os.path.join(homedir, 'doc'),
1530 os.path.join(execdir, 'doc'),
1531 '/usr/doc/python-docs-' + split(sys.version)[0],
1532 '/usr/doc/python-' + split(sys.version)[0],
1533 '/usr/doc/python-docs-' + sys.version[:3],
Jack Jansenb2628b02002-08-23 08:40:42 +00001534 '/usr/doc/python-' + sys.version[:3],
1535 os.path.join(sys.prefix, 'Resources/English.lproj/Documentation')]:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001536 if dir and os.path.isdir(os.path.join(dir, 'lib')):
1537 self.docdir = dir
1538
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001539 def __repr__(self):
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001540 if inspect.stack()[1][3] == '?':
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001541 self()
1542 return ''
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001543 return '<pydoc.Helper instance>'
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001544
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001545 def __call__(self, request=None):
1546 if request is not None:
1547 self.help(request)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001548 else:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001549 self.intro()
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001550 self.interact()
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001551 self.output.write('''
Fred Drakee61967f2001-05-10 18:41:02 +00001552You are now leaving help and returning to the Python interpreter.
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001553If you want to ask for help on a particular object directly from the
1554interpreter, you can type "help(object)". Executing "help('string')"
1555has the same effect as typing a particular string at the help> prompt.
1556''')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001557
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001558 def interact(self):
1559 self.output.write('\n')
Guido van Rossum8ca162f2002-04-07 06:36:23 +00001560 while True:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001561 self.output.write('help> ')
1562 self.output.flush()
1563 try:
1564 request = self.input.readline()
1565 if not request: break
1566 except KeyboardInterrupt: break
1567 request = strip(replace(request, '"', '', "'", ''))
1568 if lower(request) in ['q', 'quit']: break
1569 self.help(request)
1570
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001571 def help(self, request):
1572 if type(request) is type(''):
1573 if request == 'help': self.intro()
1574 elif request == 'keywords': self.listkeywords()
1575 elif request == 'topics': self.listtopics()
1576 elif request == 'modules': self.listmodules()
1577 elif request[:8] == 'modules ':
1578 self.listmodules(split(request)[1])
Raymond Hettinger54f02222002-06-01 14:18:47 +00001579 elif request in self.keywords: self.showtopic(request)
1580 elif request in self.topics: self.showtopic(request)
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001581 elif request: doc(request, 'Help on %s:')
1582 elif isinstance(request, Helper): self()
1583 else: doc(request, 'Help on %s:')
1584 self.output.write('\n')
1585
1586 def intro(self):
1587 self.output.write('''
1588Welcome to Python %s! This is the online help utility.
1589
1590If this is your first time using Python, you should definitely check out
1591the tutorial on the Internet at http://www.python.org/doc/tut/.
1592
1593Enter the name of any module, keyword, or topic to get help on writing
1594Python programs and using Python modules. To quit this help utility and
1595return to the interpreter, just type "quit".
1596
1597To get a list of available modules, keywords, or topics, type "modules",
1598"keywords", or "topics". Each module also comes with a one-line summary
1599of what it does; to list the modules whose summaries contain a given word
1600such as "spam", type "modules spam".
1601''' % sys.version[:3])
1602
1603 def list(self, items, columns=4, width=80):
1604 items = items[:]
1605 items.sort()
1606 colw = width / columns
1607 rows = (len(items) + columns - 1) / columns
1608 for row in range(rows):
1609 for col in range(columns):
1610 i = col * rows + row
1611 if i < len(items):
1612 self.output.write(items[i])
1613 if col < columns - 1:
1614 self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1615 self.output.write('\n')
1616
1617 def listkeywords(self):
1618 self.output.write('''
1619Here is a list of the Python keywords. Enter any keyword to get more help.
1620
1621''')
1622 self.list(self.keywords.keys())
1623
1624 def listtopics(self):
1625 self.output.write('''
1626Here is a list of available topics. Enter any topic name to get more help.
1627
1628''')
1629 self.list(self.topics.keys())
1630
1631 def showtopic(self, topic):
1632 if not self.docdir:
1633 self.output.write('''
1634Sorry, topic and keyword documentation is not available because the Python
1635HTML documentation files could not be found. If you have installed them,
1636please set the environment variable PYTHONDOCS to indicate their location.
1637''')
1638 return
1639 target = self.topics.get(topic, self.keywords.get(topic))
1640 if not target:
1641 self.output.write('no documentation found for %s\n' % repr(topic))
1642 return
1643 if type(target) is type(''):
1644 return self.showtopic(target)
1645
1646 filename, xrefs = target
1647 filename = self.docdir + '/' + filename + '.html'
1648 try:
1649 file = open(filename)
1650 except:
1651 self.output.write('could not read docs from %s\n' % filename)
1652 return
1653
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001654 divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
1655 addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001656 document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1657 file.close()
1658
1659 import htmllib, formatter, StringIO
1660 buffer = StringIO.StringIO()
1661 parser = htmllib.HTMLParser(
1662 formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1663 parser.start_table = parser.do_p
1664 parser.end_table = lambda parser=parser: parser.do_p({})
1665 parser.start_tr = parser.do_br
1666 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1667 parser.feed(document)
1668 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
1669 pager(' ' + strip(buffer) + '\n')
Ka-Ping Yeeda793892001-04-13 11:02:51 +00001670 if xrefs:
1671 buffer = StringIO.StringIO()
1672 formatter.DumbWriter(buffer).send_flowing_data(
1673 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1674 self.output.write('\n%s\n' % buffer.getvalue())
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001675
1676 def listmodules(self, key=''):
1677 if key:
1678 self.output.write('''
1679Here is a list of matching modules. Enter any module name to get more help.
1680
1681''')
1682 apropos(key)
1683 else:
1684 self.output.write('''
1685Please wait a moment while I gather a list of all available modules...
1686
1687''')
1688 modules = {}
1689 def callback(path, modname, desc, modules=modules):
1690 if modname and modname[-9:] == '.__init__':
1691 modname = modname[:-9] + ' (package)'
1692 if find(modname, '.') < 0:
1693 modules[modname] = 1
1694 ModuleScanner().run(callback)
1695 self.list(modules.keys())
1696 self.output.write('''
1697Enter any module name to get more help. Or, type "modules spam" to search
1698for modules whose descriptions contain the word "spam".
1699''')
1700
1701help = Helper(sys.stdin, sys.stdout)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001702
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001703class Scanner:
1704 """A generic tree iterator."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001705 def __init__(self, roots, children, descendp):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001706 self.roots = roots[:]
1707 self.state = []
1708 self.children = children
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001709 self.descendp = descendp
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001710
1711 def next(self):
1712 if not self.state:
1713 if not self.roots:
1714 return None
1715 root = self.roots.pop(0)
1716 self.state = [(root, self.children(root))]
1717 node, children = self.state[-1]
1718 if not children:
1719 self.state.pop()
1720 return self.next()
1721 child = children.pop(0)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001722 if self.descendp(child):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001723 self.state.append((child, self.children(child)))
1724 return child
1725
1726class ModuleScanner(Scanner):
1727 """An interruptible scanner that searches module synopses."""
1728 def __init__(self):
1729 roots = map(lambda dir: (dir, ''), pathdirs())
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001730 Scanner.__init__(self, roots, self.submodules, self.isnewpackage)
Raymond Hettinger32200ae2002-06-01 19:51:15 +00001731 self.inodes = map(lambda (dir, pkg): os.stat(dir).st_ino, roots)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001732
1733 def submodules(self, (dir, package)):
1734 children = []
1735 for file in os.listdir(dir):
1736 path = os.path.join(dir, file)
Tim Peters30edd232001-03-16 08:29:48 +00001737 if ispackage(path):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001738 children.append((path, package + (package and '.') + file))
1739 else:
1740 children.append((path, package))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001741 children.sort() # so that spam.py comes before spam.pyc or spam.pyo
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001742 return children
1743
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001744 def isnewpackage(self, (dir, package)):
Raymond Hettinger32200ae2002-06-01 19:51:15 +00001745 inode = os.path.exists(dir) and os.stat(dir).st_ino
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001746 if not (os.path.islink(dir) and inode in self.inodes):
Ka-Ping Yee6191a232001-04-13 15:00:27 +00001747 self.inodes.append(inode) # detect circular symbolic links
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001748 return ispackage(dir)
Guido van Rossum8ca162f2002-04-07 06:36:23 +00001749 return False
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001750
Ka-Ping Yee66246962001-04-12 11:59:50 +00001751 def run(self, callback, key=None, completer=None):
1752 if key: key = lower(key)
Guido van Rossum8ca162f2002-04-07 06:36:23 +00001753 self.quit = False
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001754 seen = {}
1755
1756 for modname in sys.builtin_module_names:
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001757 if modname != '__main__':
1758 seen[modname] = 1
Ka-Ping Yee66246962001-04-12 11:59:50 +00001759 if key is None:
1760 callback(None, modname, '')
1761 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001762 desc = split(__import__(modname).__doc__ or '', '\n')[0]
Ka-Ping Yee66246962001-04-12 11:59:50 +00001763 if find(lower(modname + ' - ' + desc), key) >= 0:
1764 callback(None, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001765
1766 while not self.quit:
1767 node = self.next()
1768 if not node: break
1769 path, package = node
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001770 modname = inspect.getmodulename(path)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001771 if os.path.isfile(path) and modname:
1772 modname = package + (package and '.') + modname
Raymond Hettinger54f02222002-06-01 14:18:47 +00001773 if not modname in seen:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001774 seen[modname] = 1 # if we see spam.py, skip spam.pyc
Ka-Ping Yee66246962001-04-12 11:59:50 +00001775 if key is None:
1776 callback(path, modname, '')
1777 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001778 desc = synopsis(path) or ''
1779 if find(lower(modname + ' - ' + desc), key) >= 0:
1780 callback(path, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001781 if completer: completer()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001782
1783def apropos(key):
1784 """Print all the one-line module summaries that contain a substring."""
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001785 def callback(path, modname, desc):
1786 if modname[-9:] == '.__init__':
1787 modname = modname[:-9] + ' (package)'
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001788 print modname, desc and '- ' + desc
1789 try: import warnings
1790 except ImportError: pass
1791 else: warnings.filterwarnings('ignore') # ignore problems during import
Ka-Ping Yee66246962001-04-12 11:59:50 +00001792 ModuleScanner().run(callback, key)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001793
1794# --------------------------------------------------- web browser interface
1795
Ka-Ping Yee66246962001-04-12 11:59:50 +00001796def serve(port, callback=None, completer=None):
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001797 import BaseHTTPServer, mimetools, select
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001798
1799 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1800 class Message(mimetools.Message):
1801 def __init__(self, fp, seekable=1):
1802 Message = self.__class__
1803 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1804 self.encodingheader = self.getheader('content-transfer-encoding')
1805 self.typeheader = self.getheader('content-type')
1806 self.parsetype()
1807 self.parseplist()
1808
1809 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1810 def send_document(self, title, contents):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001811 try:
1812 self.send_response(200)
1813 self.send_header('Content-Type', 'text/html')
1814 self.end_headers()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001815 self.wfile.write(html.page(title, contents))
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001816 except IOError: pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001817
1818 def do_GET(self):
1819 path = self.path
1820 if path[-5:] == '.html': path = path[:-5]
1821 if path[:1] == '/': path = path[1:]
1822 if path and path != '.':
1823 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001824 obj = locate(path, forceload=1)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001825 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001826 self.send_document(path, html.escape(str(value)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001827 return
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001828 if obj:
1829 self.send_document(describe(obj), html.document(obj, path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001830 else:
1831 self.send_document(path,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001832'no Python documentation found for %s' % repr(path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001833 else:
1834 heading = html.heading(
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001835'<big><big><strong>Python: Index of Modules</strong></big></big>',
1836'#ffffff', '#7799ee')
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001837 def bltinlink(name):
1838 return '<a href="%s.html">%s</a>' % (name, name)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001839 names = filter(lambda x: x != '__main__',
1840 sys.builtin_module_names)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001841 contents = html.multicolumn(names, bltinlink)
1842 indices = ['<p>' + html.bigsection(
1843 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1844
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001845 seen = {}
1846 for dir in pathdirs():
1847 indices.append(html.index(dir, seen))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001848 contents = heading + join(indices) + '''<p align=right>
Tim Peters2306d242001-09-25 03:18:32 +00001849<font color="#909090" face="helvetica, arial"><strong>
1850pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font>'''
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001851 self.send_document('Index of Modules', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001852
1853 def log_message(self, *args): pass
1854
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001855 class DocServer(BaseHTTPServer.HTTPServer):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001856 def __init__(self, port, callback):
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001857 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001858 self.address = ('', port)
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001859 self.url = 'http://%s:%d/' % (host, port)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001860 self.callback = callback
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001861 self.base.__init__(self, self.address, self.handler)
1862
1863 def serve_until_quit(self):
1864 import select
Guido van Rossum8ca162f2002-04-07 06:36:23 +00001865 self.quit = False
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001866 while not self.quit:
1867 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1868 if rd: self.handle_request()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001869
1870 def server_activate(self):
1871 self.base.server_activate(self)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001872 if self.callback: self.callback(self)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001873
1874 DocServer.base = BaseHTTPServer.HTTPServer
1875 DocServer.handler = DocHandler
1876 DocHandler.MessageClass = Message
1877 try:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001878 try:
1879 DocServer(port, callback).serve_until_quit()
1880 except (KeyboardInterrupt, select.error):
1881 pass
1882 finally:
Ka-Ping Yee66246962001-04-12 11:59:50 +00001883 if completer: completer()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001884
1885# ----------------------------------------------------- graphical interface
1886
1887def gui():
1888 """Graphical interface (starts web server and pops up a control window)."""
1889 class GUI:
1890 def __init__(self, window, port=7464):
1891 self.window = window
1892 self.server = None
1893 self.scanner = None
1894
1895 import Tkinter
1896 self.server_frm = Tkinter.Frame(window)
1897 self.title_lbl = Tkinter.Label(self.server_frm,
1898 text='Starting server...\n ')
1899 self.open_btn = Tkinter.Button(self.server_frm,
1900 text='open browser', command=self.open, state='disabled')
1901 self.quit_btn = Tkinter.Button(self.server_frm,
1902 text='quit serving', command=self.quit, state='disabled')
1903
1904 self.search_frm = Tkinter.Frame(window)
1905 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
1906 self.search_ent = Tkinter.Entry(self.search_frm)
1907 self.search_ent.bind('<Return>', self.search)
1908 self.stop_btn = Tkinter.Button(self.search_frm,
1909 text='stop', pady=0, command=self.stop, state='disabled')
1910 if sys.platform == 'win32':
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001911 # Trying to hide and show this button crashes under Windows.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001912 self.stop_btn.pack(side='right')
1913
1914 self.window.title('pydoc')
1915 self.window.protocol('WM_DELETE_WINDOW', self.quit)
1916 self.title_lbl.pack(side='top', fill='x')
1917 self.open_btn.pack(side='left', fill='x', expand=1)
1918 self.quit_btn.pack(side='right', fill='x', expand=1)
1919 self.server_frm.pack(side='top', fill='x')
1920
1921 self.search_lbl.pack(side='left')
1922 self.search_ent.pack(side='right', fill='x', expand=1)
1923 self.search_frm.pack(side='top', fill='x')
1924 self.search_ent.focus_set()
1925
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001926 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001927 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001928 self.result_lst.bind('<Button-1>', self.select)
1929 self.result_lst.bind('<Double-Button-1>', self.goto)
1930 self.result_scr = Tkinter.Scrollbar(window,
1931 orient='vertical', command=self.result_lst.yview)
1932 self.result_lst.config(yscrollcommand=self.result_scr.set)
1933
1934 self.result_frm = Tkinter.Frame(window)
1935 self.goto_btn = Tkinter.Button(self.result_frm,
1936 text='go to selected', command=self.goto)
1937 self.hide_btn = Tkinter.Button(self.result_frm,
1938 text='hide results', command=self.hide)
1939 self.goto_btn.pack(side='left', fill='x', expand=1)
1940 self.hide_btn.pack(side='right', fill='x', expand=1)
1941
1942 self.window.update()
1943 self.minwidth = self.window.winfo_width()
1944 self.minheight = self.window.winfo_height()
1945 self.bigminheight = (self.server_frm.winfo_reqheight() +
1946 self.search_frm.winfo_reqheight() +
1947 self.result_lst.winfo_reqheight() +
1948 self.result_frm.winfo_reqheight())
1949 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
1950 self.expanded = 0
1951 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1952 self.window.wm_minsize(self.minwidth, self.minheight)
Martin v. Löwis5b26abb2002-12-28 09:23:09 +00001953 self.window.tk.willdispatch()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001954
1955 import threading
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001956 threading.Thread(
1957 target=serve, args=(port, self.ready, self.quit)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001958
1959 def ready(self, server):
1960 self.server = server
1961 self.title_lbl.config(
1962 text='Python documentation server at\n' + server.url)
1963 self.open_btn.config(state='normal')
1964 self.quit_btn.config(state='normal')
1965
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001966 def open(self, event=None, url=None):
1967 url = url or self.server.url
1968 try:
1969 import webbrowser
1970 webbrowser.open(url)
1971 except ImportError: # pre-webbrowser.py compatibility
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001972 if sys.platform == 'win32':
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001973 os.system('start "%s"' % url)
1974 elif sys.platform == 'mac':
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001975 try: import ic
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001976 except ImportError: pass
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001977 else: ic.launchurl(url)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001978 else:
1979 rc = os.system('netscape -remote "openURL(%s)" &' % url)
1980 if rc: os.system('netscape "%s" &' % url)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001981
1982 def quit(self, event=None):
1983 if self.server:
1984 self.server.quit = 1
1985 self.window.quit()
1986
1987 def search(self, event=None):
1988 key = self.search_ent.get()
1989 self.stop_btn.pack(side='right')
1990 self.stop_btn.config(state='normal')
1991 self.search_lbl.config(text='Searching for "%s"...' % key)
1992 self.search_ent.forget()
1993 self.search_lbl.pack(side='left')
1994 self.result_lst.delete(0, 'end')
1995 self.goto_btn.config(state='disabled')
1996 self.expand()
1997
1998 import threading
1999 if self.scanner:
2000 self.scanner.quit = 1
2001 self.scanner = ModuleScanner()
2002 threading.Thread(target=self.scanner.run,
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +00002003 args=(self.update, key, self.done)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002004
2005 def update(self, path, modname, desc):
2006 if modname[-9:] == '.__init__':
2007 modname = modname[:-9] + ' (package)'
2008 self.result_lst.insert('end',
2009 modname + ' - ' + (desc or '(no description)'))
2010
2011 def stop(self, event=None):
2012 if self.scanner:
2013 self.scanner.quit = 1
2014 self.scanner = None
2015
2016 def done(self):
2017 self.scanner = None
2018 self.search_lbl.config(text='Search for')
2019 self.search_lbl.pack(side='left')
2020 self.search_ent.pack(side='right', fill='x', expand=1)
2021 if sys.platform != 'win32': self.stop_btn.forget()
2022 self.stop_btn.config(state='disabled')
2023
2024 def select(self, event=None):
2025 self.goto_btn.config(state='normal')
2026
2027 def goto(self, event=None):
2028 selection = self.result_lst.curselection()
2029 if selection:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002030 modname = split(self.result_lst.get(selection[0]))[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00002031 self.open(url=self.server.url + modname + '.html')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002032
2033 def collapse(self):
2034 if not self.expanded: return
2035 self.result_frm.forget()
2036 self.result_scr.forget()
2037 self.result_lst.forget()
2038 self.bigwidth = self.window.winfo_width()
2039 self.bigheight = self.window.winfo_height()
2040 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
2041 self.window.wm_minsize(self.minwidth, self.minheight)
2042 self.expanded = 0
2043
2044 def expand(self):
2045 if self.expanded: return
2046 self.result_frm.pack(side='bottom', fill='x')
2047 self.result_scr.pack(side='right', fill='y')
2048 self.result_lst.pack(side='top', fill='both', expand=1)
2049 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
2050 self.window.wm_minsize(self.minwidth, self.bigminheight)
2051 self.expanded = 1
2052
2053 def hide(self, event=None):
2054 self.stop()
2055 self.collapse()
2056
2057 import Tkinter
2058 try:
2059 gui = GUI(Tkinter.Tk())
2060 Tkinter.mainloop()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002061 except KeyboardInterrupt:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002062 pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002063
2064# -------------------------------------------------- command-line interface
2065
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002066def ispath(x):
Ka-Ping Yee45daeb02002-08-11 15:11:33 +00002067 return isinstance(x, str) and find(x, os.sep) >= 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002068
Ka-Ping Yee1d384632001-03-01 00:24:32 +00002069def cli():
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002070 """Command-line interface (looks at sys.argv to decide what to do)."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002071 import getopt
2072 class BadUsage: pass
2073
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002074 # Scripts don't get the current directory in their path by default.
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00002075 scriptdir = os.path.dirname(sys.argv[0])
2076 if scriptdir in sys.path:
2077 sys.path.remove(scriptdir)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002078 sys.path.insert(0, '.')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002079
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002080 try:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002081 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002082 writing = 0
2083
2084 for opt, val in opts:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002085 if opt == '-g':
2086 gui()
2087 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002088 if opt == '-k':
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002089 apropos(val)
2090 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002091 if opt == '-p':
2092 try:
2093 port = int(val)
2094 except ValueError:
2095 raise BadUsage
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002096 def ready(server):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00002097 print 'pydoc server ready at %s' % server.url
2098 def stopped():
2099 print 'pydoc server stopped'
2100 serve(port, ready, stopped)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002101 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002102 if opt == '-w':
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002103 writing = 1
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002104
2105 if not args: raise BadUsage
2106 for arg in args:
Ka-Ping Yee45daeb02002-08-11 15:11:33 +00002107 if ispath(arg) and not os.path.exists(arg):
2108 print 'file %r does not exist' % arg
2109 break
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002110 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002111 if ispath(arg) and os.path.isfile(arg):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002112 arg = importfile(arg)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002113 if writing:
2114 if ispath(arg) and os.path.isdir(arg):
2115 writedocs(arg)
2116 else:
2117 writedoc(arg)
2118 else:
Martin v. Löwisb8c084e2003-06-14 09:03:46 +00002119 help.help(arg)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002120 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002121 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002122
2123 except (getopt.error, BadUsage):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002124 cmd = sys.argv[0]
2125 print """pydoc - the Python documentation tool
2126
2127%s <name> ...
2128 Show text documentation on something. <name> may be the name of a
Martin v. Löwisb8c084e2003-06-14 09:03:46 +00002129 Python keyword, topic, function, module, or package, or a dotted
2130 reference to a class or function within a module or module in a
2131 package. If <name> contains a '%s', it is used as the path to a
2132 Python source file to document. If name is 'keywords', 'topics',
2133 or 'modules', a listing of these things is displayed.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002134
2135%s -k <keyword>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002136 Search for a keyword in the synopsis lines of all available modules.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002137
2138%s -p <port>
2139 Start an HTTP server on the given port on the local machine.
2140
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002141%s -g
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00002142 Pop up a graphical interface for finding and serving documentation.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002143
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002144%s -w <name> ...
2145 Write out the HTML documentation for a module to a file in the current
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00002146 directory. If <name> contains a '%s', it is treated as a filename; if
2147 it names a directory, documentation is written for all the contents.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002148""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
Ka-Ping Yee1d384632001-03-01 00:24:32 +00002149
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002150if __name__ == '__main__': cli()