blob: ecee79b67d33f764c49764cd10cfda322a04fef2 [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
Raymond Hettinger32200ae2002-06-01 19:51:15 +000047import sys, imp, os, re, types, inspect
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:
102 pre = max(0, (maxlen-3)/2)
103 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 Yeedec96e92001-04-13 09:55:49 +0000145# ----------------------------------------------------- module manipulation
146
147def ispackage(path):
148 """Guess whether a path refers to a package directory."""
149 if os.path.isdir(path):
150 for ext in ['.py', '.pyc', '.pyo']:
151 if os.path.isfile(os.path.join(path, '__init__' + ext)):
Tim Petersbc0e9102002-04-04 22:55:58 +0000152 return True
153 return False
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000154
155def synopsis(filename, cache={}):
156 """Get the one-line summary out of a module file."""
Raymond Hettinger32200ae2002-06-01 19:51:15 +0000157 mtime = os.stat(filename).st_mtime
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000158 lastupdate, result = cache.get(filename, (0, None))
159 if lastupdate < mtime:
160 info = inspect.getmoduleinfo(filename)
161 file = open(filename)
162 if info and 'b' in info[2]: # binary modules have to be imported
163 try: module = imp.load_module('__temp__', file, filename, info[1:])
164 except: return None
165 result = split(module.__doc__ or '', '\n')[0]
166 del sys.modules['__temp__']
167 else: # text modules can be directly examined
168 line = file.readline()
169 while line[:1] == '#' or not strip(line):
170 line = file.readline()
171 if not line: break
172 line = strip(line)
173 if line[:4] == 'r"""': line = line[1:]
174 if line[:3] == '"""':
175 line = line[3:]
176 if line[-1:] == '\\': line = line[:-1]
177 while not strip(line):
178 line = file.readline()
179 if not line: break
180 result = strip(split(line, '"""')[0])
181 else: result = None
182 file.close()
183 cache[filename] = (mtime, result)
184 return result
185
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000186class ErrorDuringImport(Exception):
187 """Errors that occurred while trying to import something to document it."""
188 def __init__(self, filename, (exc, value, tb)):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000189 self.filename = filename
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000190 self.exc = exc
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000191 self.value = value
192 self.tb = tb
193
194 def __str__(self):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000195 exc = self.exc
196 if type(exc) is types.ClassType:
197 exc = exc.__name__
198 return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000199
200def importfile(path):
201 """Import a Python source file or compiled file given its path."""
202 magic = imp.get_magic()
203 file = open(path, 'r')
204 if file.read(len(magic)) == magic:
205 kind = imp.PY_COMPILED
206 else:
207 kind = imp.PY_SOURCE
208 file.close()
209 filename = os.path.basename(path)
210 name, ext = os.path.splitext(filename)
211 file = open(path, 'r')
212 try:
213 module = imp.load_module(name, file, path, (ext, 'r', kind))
214 except:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000215 raise ErrorDuringImport(path, sys.exc_info())
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000216 file.close()
217 return module
218
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000219def safeimport(path, forceload=0, cache={}):
220 """Import a module; handle errors; return None if the module isn't found.
221
222 If the module *is* found but an exception occurs, it's wrapped in an
223 ErrorDuringImport exception and reraised. Unlike __import__, if a
224 package path is specified, the module at the end of the path is returned,
225 not the package at the beginning. If the optional 'forceload' argument
226 is 1, we reload the module from disk (unless it's a dynamic extension)."""
Raymond Hettinger54f02222002-06-01 14:18:47 +0000227 if forceload and path in sys.modules:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000228 # This is the only way to be sure. Checking the mtime of the file
229 # isn't good enough (e.g. what if the module contains a class that
230 # inherits from another module that has changed?).
231 if path not in sys.builtin_module_names:
232 # Python never loads a dynamic extension a second time from the
233 # same path, even if the file is changed or missing. Deleting
234 # the entry in sys.modules doesn't help for dynamic extensions,
235 # so we're not even going to try to keep them up to date.
236 info = inspect.getmoduleinfo(sys.modules[path].__file__)
237 if info[3] != imp.C_EXTENSION:
238 cache[path] = sys.modules[path] # prevent module from clearing
239 del sys.modules[path]
240 try:
241 module = __import__(path)
242 except:
243 # Did the error occur before or after the module was found?
244 (exc, value, tb) = info = sys.exc_info()
Raymond Hettinger54f02222002-06-01 14:18:47 +0000245 if path in sys.modules:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000246 # An error occured while executing the imported module.
247 raise ErrorDuringImport(sys.modules[path].__file__, info)
248 elif exc is SyntaxError:
249 # A SyntaxError occurred before we could execute the module.
250 raise ErrorDuringImport(value.filename, info)
251 elif exc is ImportError and \
252 split(lower(str(value)))[:2] == ['no', 'module']:
253 # The module was not found.
254 return None
255 else:
256 # Some other error occurred during the importing process.
257 raise ErrorDuringImport(path, sys.exc_info())
258 for part in split(path, '.')[1:]:
259 try: module = getattr(module, part)
260 except AttributeError: return None
261 return module
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000262
263# ---------------------------------------------------- formatter base class
264
265class Doc:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000266 def document(self, object, name=None, *args):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000267 """Generate documentation for an object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000268 args = (object, name) + args
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000269 if inspect.ismodule(object): return apply(self.docmodule, args)
270 if inspect.isclass(object): return apply(self.docclass, args)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000271 if inspect.isroutine(object): return apply(self.docroutine, args)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000272 return apply(self.docother, args)
273
274 def fail(self, object, name=None, *args):
275 """Raise an exception for unimplemented types."""
276 message = "don't know how to document object%s of type %s" % (
277 name and ' ' + repr(name), type(object).__name__)
278 raise TypeError, message
279
280 docmodule = docclass = docroutine = docother = fail
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000281
282# -------------------------------------------- HTML documentation generator
283
284class HTMLRepr(Repr):
285 """Class for safely making an HTML representation of a Python object."""
286 def __init__(self):
287 Repr.__init__(self)
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000288 self.maxlist = self.maxtuple = 20
289 self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000290 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000291
292 def escape(self, text):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000293 return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000294
295 def repr(self, object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000296 return Repr.repr(self, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000297
298 def repr1(self, x, level):
299 methodname = 'repr_' + join(split(type(x).__name__), '_')
300 if hasattr(self, methodname):
301 return getattr(self, methodname)(x, level)
302 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000303 return self.escape(cram(stripid(repr(x)), self.maxother))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000304
305 def repr_string(self, x, level):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000306 test = cram(x, self.maxstring)
307 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000308 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000309 # Backslashes are only literal in the string and are never
310 # needed to make any special characters, so show a raw string.
311 return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000312 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000313 r'<font color="#c040c0">\1</font>',
314 self.escape(testrepr))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000315
Skip Montanarodf708782002-03-07 22:58:02 +0000316 repr_str = repr_string
317
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000318 def repr_instance(self, x, level):
319 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000320 return self.escape(cram(stripid(repr(x)), self.maxstring))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000321 except:
322 return self.escape('<%s instance>' % x.__class__.__name__)
323
324 repr_unicode = repr_string
325
326class HTMLDoc(Doc):
327 """Formatter class for HTML documentation."""
328
329 # ------------------------------------------- HTML formatting utilities
330
331 _repr_instance = HTMLRepr()
332 repr = _repr_instance.repr
333 escape = _repr_instance.escape
334
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000335 def page(self, title, contents):
336 """Format an HTML page."""
337 return '''
Tim Peters59ed4482001-10-31 04:20:26 +0000338<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000339<html><head><title>Python: %s</title>
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000340<style type="text/css"><!--
Ka-Ping Yeed03f8fe2001-04-13 15:04:32 +0000341TT { font-family: lucidatypewriter, lucida console, courier }
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000342--></style></head><body bgcolor="#f0f0f8">
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000343%s
344</body></html>''' % (title, contents)
345
346 def heading(self, title, fgcol, bgcol, extras=''):
347 """Format a page heading."""
348 return '''
Tim Peters59ed4482001-10-31 04:20:26 +0000349<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading">
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000350<tr bgcolor="%s">
Tim Peters2306d242001-09-25 03:18:32 +0000351<td valign=bottom>&nbsp;<br>
352<font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000353><td align=right valign=bottom
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000354><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000355 ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
356
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000357 def section(self, title, fgcol, bgcol, contents, width=10,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000358 prelude='', marginalia=None, gap='&nbsp;&nbsp;'):
359 """Format a section with a heading."""
360 if marginalia is None:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000361 marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000362 result = '''
Tim Peters59ed4482001-10-31 04:20:26 +0000363<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000364<tr bgcolor="%s">
Tim Peters2306d242001-09-25 03:18:32 +0000365<td colspan=3 valign=bottom>&nbsp;<br>
366<font color="%s" face="helvetica, arial">%s</font></td></tr>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000367 ''' % (bgcol, fgcol, title)
368 if prelude:
369 result = result + '''
Ka-Ping Yee987ec902001-03-23 13:35:45 +0000370<tr bgcolor="%s"><td rowspan=2>%s</td>
371<td colspan=2>%s</td></tr>
372<tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
373 else:
374 result = result + '''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000375<tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000376
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000377 return result + '\n<td width="100%%">%s</td></tr></table>' % contents
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000378
379 def bigsection(self, title, *args):
380 """Format a section with a big heading."""
381 title = '<big><strong>%s</strong></big>' % title
382 return apply(self.section, (title,) + args)
383
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000384 def preformat(self, text):
385 """Format literal preformatted text."""
386 text = self.escape(expandtabs(text))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000387 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
388 ' ', '&nbsp;', '\n', '<br>\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000389
390 def multicolumn(self, list, format, cols=4):
391 """Format a list of items into a multi-column list."""
392 result = ''
393 rows = (len(list)+cols-1)/cols
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000394 for col in range(cols):
395 result = result + '<td width="%d%%" valign=top>' % (100/cols)
396 for i in range(rows*col, rows*col+rows):
397 if i < len(list):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000398 result = result + format(list[i]) + '<br>\n'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000399 result = result + '</td>'
Tim Peters59ed4482001-10-31 04:20:26 +0000400 return '<table width="100%%" summary="list"><tr>%s</tr></table>' % result
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000401
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000402 def grey(self, text): return '<font color="#909090">%s</font>' % text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000403
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000404 def namelink(self, name, *dicts):
405 """Make a link for an identifier, given name-to-URL mappings."""
406 for dict in dicts:
Raymond Hettinger54f02222002-06-01 14:18:47 +0000407 if name in dict:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000408 return '<a href="%s">%s</a>' % (dict[name], name)
409 return name
410
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000411 def classlink(self, object, modname):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000412 """Make a link for a class."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000413 name, module = object.__name__, sys.modules.get(object.__module__)
414 if hasattr(module, name) and getattr(module, name) is object:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000415 return '<a href="%s.html#%s">%s</a>' % (
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000416 module.__name__, name, classname(object, modname))
417 return classname(object, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000418
419 def modulelink(self, object):
420 """Make a link for a module."""
421 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
422
423 def modpkglink(self, (name, path, ispackage, shadowed)):
424 """Make a link for a module or package to display in an index."""
425 if shadowed:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000426 return self.grey(name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000427 if path:
428 url = '%s.%s.html' % (path, name)
429 else:
430 url = '%s.html' % name
431 if ispackage:
432 text = '<strong>%s</strong>&nbsp;(package)' % name
433 else:
434 text = name
435 return '<a href="%s">%s</a>' % (url, text)
436
437 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
438 """Mark up some plain text, given a context of symbols to look for.
439 Each context dictionary maps object names to anchor names."""
440 escape = escape or self.escape
441 results = []
442 here = 0
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000443 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
444 r'RFC[- ]?(\d+)|'
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000445 r'PEP[- ]?(\d+)|'
Neil Schemenauerd69711c2002-03-24 23:02:07 +0000446 r'(self\.)?(\w+))')
Guido van Rossum8ca162f2002-04-07 06:36:23 +0000447 while True:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000448 match = pattern.search(text, here)
449 if not match: break
450 start, end = match.span()
451 results.append(escape(text[here:start]))
452
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000453 all, scheme, rfc, pep, selfdot, name = match.groups()
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000454 if scheme:
Neil Schemenauercddc1a02002-03-24 23:11:21 +0000455 url = escape(all).replace('"', '&quot;')
456 results.append('<a href="%s">%s</a>' % (url, url))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000457 elif rfc:
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +0000458 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
459 results.append('<a href="%s">%s</a>' % (url, escape(all)))
460 elif pep:
461 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000462 results.append('<a href="%s">%s</a>' % (url, escape(all)))
463 elif text[end:end+1] == '(':
464 results.append(self.namelink(name, methods, funcs, classes))
465 elif selfdot:
466 results.append('self.<strong>%s</strong>' % name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000467 else:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000468 results.append(self.namelink(name, classes))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000469 here = end
470 results.append(escape(text[here:]))
471 return join(results, '')
472
473 # ---------------------------------------------- type-specific routines
474
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000475 def formattree(self, tree, modname, parent=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000476 """Produce HTML for a class tree as given by inspect.getclasstree()."""
477 result = ''
478 for entry in tree:
479 if type(entry) is type(()):
480 c, bases = entry
Tim Peters2306d242001-09-25 03:18:32 +0000481 result = result + '<dt><font face="helvetica, arial">'
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000482 result = result + self.classlink(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000483 if bases and bases != (parent,):
484 parents = []
485 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000486 parents.append(self.classlink(base, modname))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000487 result = result + '(' + join(parents, ', ') + ')'
Tim Peters2306d242001-09-25 03:18:32 +0000488 result = result + '\n</font></dt>'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000489 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000490 result = result + '<dd>\n%s</dd>\n' % self.formattree(
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000491 entry, modname, c)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000492 return '<dl>\n%s</dl>\n' % result
493
Tim Peters8dd7ade2001-10-18 19:56:17 +0000494 def docmodule(self, object, name=None, mod=None, *ignored):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000495 """Produce HTML documentation for a module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000496 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000497 parts = split(name, '.')
498 links = []
499 for i in range(len(parts)-1):
500 links.append(
501 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
502 (join(parts[:i+1], '.'), parts[i]))
503 linkedname = join(links + parts[-1:], '.')
504 head = '<big><big><strong>%s</strong></big></big>' % linkedname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000505 try:
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000506 path = inspect.getabsfile(object)
Ka-Ping Yee6191a232001-04-13 15:00:27 +0000507 url = path
508 if sys.platform == 'win32':
509 import nturl2path
510 url = nturl2path.pathname2url(path)
511 filelink = '<a href="file:%s">%s</a>' % (url, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000512 except TypeError:
513 filelink = '(built-in)'
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000514 info = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000515 if hasattr(object, '__version__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000516 version = str(object.__version__)
Ka-Ping Yee40c49912001-02-27 22:46:01 +0000517 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
518 version = strip(version[11:-1])
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000519 info.append('version %s' % self.escape(version))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000520 if hasattr(object, '__date__'):
521 info.append(self.escape(str(object.__date__)))
522 if info:
523 head = head + ' (%s)' % join(info, ', ')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000524 result = self.heading(
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000525 head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)
526
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000527 modules = inspect.getmembers(object, inspect.ismodule)
528
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000529 classes, cdict = [], {}
530 for key, value in inspect.getmembers(object, inspect.isclass):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000531 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000532 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000533 cdict[key] = cdict[value] = '#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000534 for key, value in classes:
535 for base in value.__bases__:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000536 key, modname = base.__name__, base.__module__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000537 module = sys.modules.get(modname)
538 if modname != name and module and hasattr(module, key):
539 if getattr(module, key) is base:
Raymond Hettinger54f02222002-06-01 14:18:47 +0000540 if not key in cdict:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000541 cdict[key] = cdict[base] = modname + '.html#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000542 funcs, fdict = [], {}
543 for key, value in inspect.getmembers(object, inspect.isroutine):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000544 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000545 funcs.append((key, value))
546 fdict[key] = '#-' + key
547 if inspect.isfunction(value): fdict[value] = fdict[key]
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000548 data = []
549 for key, value in inspect.getmembers(object, isdata):
550 if key not in ['__builtins__', '__doc__']:
551 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000552
553 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
554 doc = doc and '<tt>%s</tt>' % doc
Tim Peters2306d242001-09-25 03:18:32 +0000555 result = result + '<p>%s</p>\n' % doc
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000556
557 if hasattr(object, '__path__'):
558 modpkgs = []
559 modnames = []
560 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000561 path = os.path.join(object.__path__[0], file)
562 modname = inspect.getmodulename(file)
563 if modname and modname not in modnames:
564 modpkgs.append((modname, name, 0, 0))
565 modnames.append(modname)
566 elif ispackage(path):
567 modpkgs.append((file, name, 1, 0))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000568 modpkgs.sort()
569 contents = self.multicolumn(modpkgs, self.modpkglink)
570 result = result + self.bigsection(
571 'Package Contents', '#ffffff', '#aa55cc', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000572 elif modules:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000573 contents = self.multicolumn(
574 modules, lambda (key, value), s=self: s.modulelink(value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000575 result = result + self.bigsection(
576 'Modules', '#fffff', '#aa55cc', contents)
577
578 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000579 classlist = map(lambda (key, value): value, classes)
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000580 contents = [
581 self.formattree(inspect.getclasstree(classlist, 1), name)]
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000582 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000583 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000584 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000585 'Classes', '#ffffff', '#ee77aa', join(contents))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000586 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000587 contents = []
588 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000589 contents.append(self.document(value, key, name, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000590 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000591 'Functions', '#ffffff', '#eeaa77', join(contents))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000592 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000593 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000594 for key, value in data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000595 contents.append(self.document(value, key))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000596 result = result + self.bigsection(
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000597 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000598 if hasattr(object, '__author__'):
599 contents = self.markup(str(object.__author__), self.preformat)
600 result = result + self.bigsection(
601 'Author', '#ffffff', '#7799ee', contents)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000602 if hasattr(object, '__credits__'):
603 contents = self.markup(str(object.__credits__), self.preformat)
604 result = result + self.bigsection(
605 'Credits', '#ffffff', '#7799ee', contents)
606
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000607 return result
608
Tim Peters8dd7ade2001-10-18 19:56:17 +0000609 def docclass(self, object, name=None, mod=None, funcs={}, classes={},
610 *ignored):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000611 """Produce HTML documentation for a class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000612 realname = object.__name__
613 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000614 bases = object.__bases__
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000615
Tim Petersb47879b2001-09-24 04:47:19 +0000616 contents = []
617 push = contents.append
618
Tim Petersfa26f7c2001-09-24 08:05:11 +0000619 # Cute little class to pump out a horizontal rule between sections.
620 class HorizontalRule:
621 def __init__(self):
622 self.needone = 0
623 def maybe(self):
624 if self.needone:
625 push('<hr>\n')
626 self.needone = 1
627 hr = HorizontalRule()
628
Tim Petersc86f6ca2001-09-26 21:31:51 +0000629 # List the mro, if non-trivial.
Tim Peters351e3622001-09-27 03:29:51 +0000630 mro = list(inspect.getmro(object))
Tim Petersc86f6ca2001-09-26 21:31:51 +0000631 if len(mro) > 2:
632 hr.maybe()
633 push('<dl><dt>Method resolution order:</dt>\n')
634 for base in mro:
635 push('<dd>%s</dd>\n' % self.classlink(base,
636 object.__module__))
637 push('</dl>\n')
638
Tim Petersb47879b2001-09-24 04:47:19 +0000639 def spill(msg, attrs, predicate):
Tim Petersfa26f7c2001-09-24 08:05:11 +0000640 ok, attrs = _split_list(attrs, predicate)
Tim Petersb47879b2001-09-24 04:47:19 +0000641 if ok:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000642 hr.maybe()
Tim Petersb47879b2001-09-24 04:47:19 +0000643 push(msg)
644 for name, kind, homecls, value in ok:
645 push(self.document(getattr(object, name), name, mod,
646 funcs, classes, mdict, object))
647 push('\n')
648 return attrs
649
Tim Petersfa26f7c2001-09-24 08:05:11 +0000650 def spillproperties(msg, attrs, predicate):
651 ok, attrs = _split_list(attrs, predicate)
Tim Petersb47879b2001-09-24 04:47:19 +0000652 if ok:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000653 hr.maybe()
Tim Petersb47879b2001-09-24 04:47:19 +0000654 push(msg)
655 for name, kind, homecls, value in ok:
Tim Peters3e767d12001-09-25 00:01:06 +0000656 push('<dl><dt><strong>%s</strong></dt>\n' % name)
657 if value.__doc__ is not None:
658 doc = self.markup(value.__doc__, self.preformat,
659 funcs, classes, mdict)
Tim Peters2306d242001-09-25 03:18:32 +0000660 push('<dd><tt>%s</tt></dd>\n' % doc)
Tim Petersf33532c2001-09-25 06:30:51 +0000661 for attr, tag in [("fget", " getter"),
662 ("fset", " setter"),
Tim Peters3e767d12001-09-25 00:01:06 +0000663 ("fdel", " deleter")]:
664 func = getattr(value, attr)
665 if func is not None:
666 base = self.document(func, name + tag, mod,
667 funcs, classes, mdict, object)
668 push('<dd>%s</dd>\n' % base)
669 push('</dl>\n')
Tim Petersb47879b2001-09-24 04:47:19 +0000670 return attrs
671
Tim Petersfa26f7c2001-09-24 08:05:11 +0000672 def spilldata(msg, attrs, predicate):
673 ok, attrs = _split_list(attrs, predicate)
Tim Petersb47879b2001-09-24 04:47:19 +0000674 if ok:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000675 hr.maybe()
Tim Petersb47879b2001-09-24 04:47:19 +0000676 push(msg)
677 for name, kind, homecls, value in ok:
678 base = self.docother(getattr(object, name), name, mod)
Guido van Rossum5e355b22002-05-21 20:56:15 +0000679 if callable(value):
680 doc = getattr(value, "__doc__", None)
681 else:
682 doc = None
Tim Petersb47879b2001-09-24 04:47:19 +0000683 if doc is None:
684 push('<dl><dt>%s</dl>\n' % base)
685 else:
686 doc = self.markup(getdoc(value), self.preformat,
687 funcs, classes, mdict)
Tim Peters2306d242001-09-25 03:18:32 +0000688 doc = '<dd><tt>%s</tt>' % doc
Tim Petersb47879b2001-09-24 04:47:19 +0000689 push('<dl><dt>%s%s</dl>\n' % (base, doc))
690 push('\n')
691 return attrs
692
693 attrs = inspect.classify_class_attrs(object)
694 mdict = {}
695 for key, kind, homecls, value in attrs:
696 mdict[key] = anchor = '#' + name + '-' + key
697 value = getattr(object, key)
698 try:
699 # The value may not be hashable (e.g., a data attr with
700 # a dict or list value).
701 mdict[value] = anchor
702 except TypeError:
703 pass
704
Tim Petersfa26f7c2001-09-24 08:05:11 +0000705 while attrs:
Tim Peters351e3622001-09-27 03:29:51 +0000706 if mro:
707 thisclass = mro.pop(0)
708 else:
709 thisclass = attrs[0][2]
Tim Petersfa26f7c2001-09-24 08:05:11 +0000710 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
711
Tim Petersb47879b2001-09-24 04:47:19 +0000712 if thisclass is object:
713 tag = "defined here"
714 else:
Tim Petersfa26f7c2001-09-24 08:05:11 +0000715 tag = "inherited from %s" % self.classlink(thisclass,
Tim Petersb47879b2001-09-24 04:47:19 +0000716 object.__module__)
717 tag += ':<br>\n'
718
719 # Sort attrs by name.
720 attrs.sort(lambda t1, t2: cmp(t1[0], t2[0]))
721
722 # Pump out the attrs, segregated by kind.
723 attrs = spill("Methods %s" % tag, attrs,
724 lambda t: t[1] == 'method')
725 attrs = spill("Class methods %s" % tag, attrs,
726 lambda t: t[1] == 'class method')
727 attrs = spill("Static methods %s" % tag, attrs,
728 lambda t: t[1] == 'static method')
Tim Petersfa26f7c2001-09-24 08:05:11 +0000729 attrs = spillproperties("Properties %s" % tag, attrs,
730 lambda t: t[1] == 'property')
Tim Petersf33532c2001-09-25 06:30:51 +0000731 attrs = spilldata("Data and non-method functions %s" % tag, attrs,
Tim Petersfa26f7c2001-09-24 08:05:11 +0000732 lambda t: t[1] == 'data')
Tim Petersb47879b2001-09-24 04:47:19 +0000733 assert attrs == []
Tim Peters351e3622001-09-27 03:29:51 +0000734 attrs = inherited
Tim Petersb47879b2001-09-24 04:47:19 +0000735
736 contents = ''.join(contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000737
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000738 if name == realname:
739 title = '<a name="%s">class <strong>%s</strong></a>' % (
740 name, realname)
741 else:
742 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
743 name, name, realname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000744 if bases:
745 parents = []
746 for base in bases:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000747 parents.append(self.classlink(base, object.__module__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000748 title = title + '(%s)' % join(parents, ', ')
Tim Peters2306d242001-09-25 03:18:32 +0000749 doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict)
750 doc = doc and '<tt>%s<br>&nbsp;</tt>' % doc or '&nbsp;'
Tim Petersc86f6ca2001-09-26 21:31:51 +0000751
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000752 return self.section(title, '#000000', '#ffc8d8', contents, 5, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000753
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000754 def formatvalue(self, object):
755 """Format an argument default value as text."""
Tim Peters2306d242001-09-25 03:18:32 +0000756 return self.grey('=' + self.repr(object))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000757
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000758 def docroutine(self, object, name=None, mod=None,
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000759 funcs={}, classes={}, methods={}, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000760 """Produce HTML documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000761 realname = object.__name__
762 name = name or realname
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000763 anchor = (cl and cl.__name__ or '') + '-' + name
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000764 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000765 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000766 if inspect.ismethod(object):
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +0000767 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000768 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000769 if imclass is not cl:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000770 note = ' from ' + self.classlink(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000771 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +0000772 if object.im_self:
773 note = ' method of %s instance' % self.classlink(
774 object.im_self.__class__, mod)
775 else:
776 note = ' unbound %s method' % self.classlink(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000777 object = object.im_func
778
779 if name == realname:
780 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
781 else:
Raymond Hettinger54f02222002-06-01 14:18:47 +0000782 if (cl and realname in cl.__dict__ and
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000783 cl.__dict__[realname] is object):
Ka-Ping Yeee280c062001-03-23 14:05:53 +0000784 reallink = '<a href="#%s">%s</a>' % (
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000785 cl.__name__ + '-' + realname, realname)
786 skipdocs = 1
787 else:
788 reallink = realname
789 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
790 anchor, name, reallink)
Tim Peters4bcfa312001-09-20 06:08:24 +0000791 if inspect.isfunction(object):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000792 args, varargs, varkw, defaults = inspect.getargspec(object)
793 argspec = inspect.formatargspec(
794 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000795 if realname == '<lambda>':
Tim Peters59ed4482001-10-31 04:20:26 +0000796 title = '<strong>%s</strong> <em>lambda</em> ' % name
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000797 argspec = argspec[1:-1] # remove parentheses
Tim Peters4bcfa312001-09-20 06:08:24 +0000798 else:
799 argspec = '(...)'
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000800
Tim Peters2306d242001-09-25 03:18:32 +0000801 decl = title + argspec + (note and self.grey(
802 '<font face="helvetica, arial">%s</font>' % note))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000803
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000804 if skipdocs:
Tim Peters2306d242001-09-25 03:18:32 +0000805 return '<dl><dt>%s</dt></dl>\n' % decl
Ka-Ping Yee3bda8792001-03-23 13:17:50 +0000806 else:
807 doc = self.markup(
808 getdoc(object), self.preformat, funcs, classes, methods)
Tim Peters2306d242001-09-25 03:18:32 +0000809 doc = doc and '<dd><tt>%s</tt></dd>' % doc
810 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000811
Tim Peters8dd7ade2001-10-18 19:56:17 +0000812 def docother(self, object, name=None, mod=None, *ignored):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000813 """Produce HTML documentation for a data object."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000814 lhs = name and '<strong>%s</strong> = ' % name or ''
815 return lhs + self.repr(object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000816
817 def index(self, dir, shadowed=None):
818 """Generate an HTML index for a directory of modules."""
819 modpkgs = []
820 if shadowed is None: shadowed = {}
821 seen = {}
822 files = os.listdir(dir)
823
824 def found(name, ispackage,
825 modpkgs=modpkgs, shadowed=shadowed, seen=seen):
Raymond Hettinger54f02222002-06-01 14:18:47 +0000826 if not name in seen:
827 modpkgs.append((name, '', ispackage, name)) in shadowed
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000828 seen[name] = 1
829 shadowed[name] = 1
830
831 # Package spam/__init__.py takes precedence over module spam.py.
832 for file in files:
833 path = os.path.join(dir, file)
834 if ispackage(path): found(file, 1)
835 for file in files:
836 path = os.path.join(dir, file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000837 if os.path.isfile(path):
838 modname = inspect.getmodulename(file)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000839 if modname: found(modname, 0)
840
841 modpkgs.sort()
842 contents = self.multicolumn(modpkgs, self.modpkglink)
843 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
844
845# -------------------------------------------- text documentation generator
846
847class TextRepr(Repr):
848 """Class for safely making a text representation of a Python object."""
849 def __init__(self):
850 Repr.__init__(self)
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000851 self.maxlist = self.maxtuple = 20
852 self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000853 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000854
855 def repr1(self, x, level):
856 methodname = 'repr_' + join(split(type(x).__name__), '_')
857 if hasattr(self, methodname):
858 return getattr(self, methodname)(x, level)
859 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000860 return cram(stripid(repr(x)), self.maxother)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000861
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000862 def repr_string(self, x, level):
863 test = cram(x, self.maxstring)
864 testrepr = repr(test)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000865 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000866 # Backslashes are only literal in the string and are never
867 # needed to make any special characters, so show a raw string.
868 return 'r' + testrepr[0] + test + testrepr[0]
869 return testrepr
870
Skip Montanarodf708782002-03-07 22:58:02 +0000871 repr_str = repr_string
872
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000873 def repr_instance(self, x, level):
874 try:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000875 return cram(stripid(repr(x)), self.maxstring)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000876 except:
877 return '<%s instance>' % x.__class__.__name__
878
879class TextDoc(Doc):
880 """Formatter class for text documentation."""
881
882 # ------------------------------------------- text formatting utilities
883
884 _repr_instance = TextRepr()
885 repr = _repr_instance.repr
886
887 def bold(self, text):
888 """Format a string in bold by overstriking."""
889 return join(map(lambda ch: ch + '\b' + ch, text), '')
890
891 def indent(self, text, prefix=' '):
892 """Indent text by prepending a given prefix to each line."""
893 if not text: return ''
894 lines = split(text, '\n')
895 lines = map(lambda line, prefix=prefix: prefix + line, lines)
896 if lines: lines[-1] = rstrip(lines[-1])
897 return join(lines, '\n')
898
899 def section(self, title, contents):
900 """Format a section with a given heading."""
901 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
902
903 # ---------------------------------------------- type-specific routines
904
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000905 def formattree(self, tree, modname, parent=None, prefix=''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000906 """Render in text a class tree as returned by inspect.getclasstree()."""
907 result = ''
908 for entry in tree:
909 if type(entry) is type(()):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000910 c, bases = entry
911 result = result + prefix + classname(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000912 if bases and bases != (parent,):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000913 parents = map(lambda c, m=modname: classname(c, m), bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000914 result = result + '(%s)' % join(parents, ', ')
915 result = result + '\n'
916 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000917 result = result + self.formattree(
918 entry, modname, c, prefix + ' ')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000919 return result
920
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000921 def docmodule(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000922 """Produce text documentation for a given module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000923 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000924 synop, desc = splitdoc(getdoc(object))
925 result = self.section('NAME', name + (synop and ' - ' + synop))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000926
927 try:
928 file = inspect.getabsfile(object)
929 except TypeError:
930 file = '(built-in)'
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000931 result = result + self.section('FILE', file)
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000932 if desc:
933 result = result + self.section('DESCRIPTION', desc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000934
935 classes = []
936 for key, value in inspect.getmembers(object, inspect.isclass):
937 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000938 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000939 funcs = []
940 for key, value in inspect.getmembers(object, inspect.isroutine):
941 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000942 funcs.append((key, value))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000943 data = []
944 for key, value in inspect.getmembers(object, isdata):
945 if key not in ['__builtins__', '__doc__']:
946 data.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000947
948 if hasattr(object, '__path__'):
949 modpkgs = []
950 for file in os.listdir(object.__path__[0]):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +0000951 path = os.path.join(object.__path__[0], file)
952 modname = inspect.getmodulename(file)
953 if modname and modname not in modpkgs:
954 modpkgs.append(modname)
955 elif ispackage(path):
956 modpkgs.append(file + ' (package)')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000957 modpkgs.sort()
958 result = result + self.section(
959 'PACKAGE CONTENTS', join(modpkgs, '\n'))
960
961 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000962 classlist = map(lambda (key, value): value, classes)
963 contents = [self.formattree(
964 inspect.getclasstree(classlist, 1), name)]
965 for key, value in classes:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000966 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000967 result = result + self.section('CLASSES', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000968
969 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000970 contents = []
971 for key, value in funcs:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000972 contents.append(self.document(value, key, name))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000973 result = result + self.section('FUNCTIONS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000974
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000975 if data:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000976 contents = []
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000977 for key, value in data:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000978 contents.append(self.docother(value, key, name, 70))
Ka-Ping Yeedec96e92001-04-13 09:55:49 +0000979 result = result + self.section('DATA', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000980
981 if hasattr(object, '__version__'):
982 version = str(object.__version__)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000983 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
984 version = strip(version[11:-1])
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000985 result = result + self.section('VERSION', version)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000986 if hasattr(object, '__date__'):
987 result = result + self.section('DATE', str(object.__date__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000988 if hasattr(object, '__author__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000989 result = result + self.section('AUTHOR', str(object.__author__))
990 if hasattr(object, '__credits__'):
991 result = result + self.section('CREDITS', str(object.__credits__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000992 return result
993
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +0000994 def docclass(self, object, name=None, mod=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000995 """Produce text documentation for a given class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000996 realname = object.__name__
997 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000998 bases = object.__bases__
999
Tim Petersc86f6ca2001-09-26 21:31:51 +00001000 def makename(c, m=object.__module__):
1001 return classname(c, m)
1002
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001003 if name == realname:
1004 title = 'class ' + self.bold(realname)
1005 else:
1006 title = self.bold(name) + ' = class ' + realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001007 if bases:
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001008 parents = map(makename, bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001009 title = title + '(%s)' % join(parents, ', ')
1010
1011 doc = getdoc(object)
Tim Peters28355492001-09-23 21:29:55 +00001012 contents = doc and [doc + '\n'] or []
1013 push = contents.append
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001014
Tim Petersc86f6ca2001-09-26 21:31:51 +00001015 # List the mro, if non-trivial.
Tim Peters351e3622001-09-27 03:29:51 +00001016 mro = list(inspect.getmro(object))
Tim Petersc86f6ca2001-09-26 21:31:51 +00001017 if len(mro) > 2:
1018 push("Method resolution order:")
1019 for base in mro:
1020 push(' ' + makename(base))
1021 push('')
1022
Tim Petersf4aad8e2001-09-24 22:40:47 +00001023 # Cute little class to pump out a horizontal rule between sections.
1024 class HorizontalRule:
1025 def __init__(self):
1026 self.needone = 0
1027 def maybe(self):
1028 if self.needone:
1029 push('-' * 70)
1030 self.needone = 1
1031 hr = HorizontalRule()
1032
Tim Peters28355492001-09-23 21:29:55 +00001033 def spill(msg, attrs, predicate):
Tim Petersfa26f7c2001-09-24 08:05:11 +00001034 ok, attrs = _split_list(attrs, predicate)
Tim Peters28355492001-09-23 21:29:55 +00001035 if ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001036 hr.maybe()
Tim Peters28355492001-09-23 21:29:55 +00001037 push(msg)
1038 for name, kind, homecls, value in ok:
1039 push(self.document(getattr(object, name),
1040 name, mod, object))
1041 return attrs
1042
Tim Petersfa26f7c2001-09-24 08:05:11 +00001043 def spillproperties(msg, attrs, predicate):
1044 ok, attrs = _split_list(attrs, predicate)
Tim Peters28355492001-09-23 21:29:55 +00001045 if ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001046 hr.maybe()
Tim Peters28355492001-09-23 21:29:55 +00001047 push(msg)
1048 for name, kind, homecls, value in ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001049 push(name)
1050 need_blank_after_doc = 0
1051 doc = getdoc(value) or ''
1052 if doc:
1053 push(self.indent(doc))
1054 need_blank_after_doc = 1
Tim Petersf33532c2001-09-25 06:30:51 +00001055 for attr, tag in [("fget", " getter"),
1056 ("fset", " setter"),
Tim Petersf4aad8e2001-09-24 22:40:47 +00001057 ("fdel", " deleter")]:
1058 func = getattr(value, attr)
1059 if func is not None:
1060 if need_blank_after_doc:
1061 push('')
1062 need_blank_after_doc = 0
1063 base = self.docother(func, name + tag, mod, 70)
1064 push(self.indent(base))
1065 push('')
Tim Peters28355492001-09-23 21:29:55 +00001066 return attrs
Tim Petersb47879b2001-09-24 04:47:19 +00001067
Tim Petersfa26f7c2001-09-24 08:05:11 +00001068 def spilldata(msg, attrs, predicate):
1069 ok, attrs = _split_list(attrs, predicate)
Tim Peters28355492001-09-23 21:29:55 +00001070 if ok:
Tim Petersf4aad8e2001-09-24 22:40:47 +00001071 hr.maybe()
Tim Peters28355492001-09-23 21:29:55 +00001072 push(msg)
1073 for name, kind, homecls, value in ok:
Guido van Rossum5e355b22002-05-21 20:56:15 +00001074 if callable(value):
1075 doc = getattr(value, "__doc__", None)
1076 else:
1077 doc = None
Tim Peters28355492001-09-23 21:29:55 +00001078 push(self.docother(getattr(object, name),
1079 name, mod, 70, doc) + '\n')
1080 return attrs
1081
1082 attrs = inspect.classify_class_attrs(object)
Tim Petersfa26f7c2001-09-24 08:05:11 +00001083 while attrs:
Tim Peters351e3622001-09-27 03:29:51 +00001084 if mro:
1085 thisclass = mro.pop(0)
1086 else:
1087 thisclass = attrs[0][2]
Tim Petersfa26f7c2001-09-24 08:05:11 +00001088 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
1089
Tim Peters28355492001-09-23 21:29:55 +00001090 if thisclass is object:
1091 tag = "defined here"
1092 else:
Tim Petersfa26f7c2001-09-24 08:05:11 +00001093 tag = "inherited from %s" % classname(thisclass,
1094 object.__module__)
Tim Peters28355492001-09-23 21:29:55 +00001095
1096 # Sort attrs by name.
1097 attrs.sort(lambda t1, t2: cmp(t1[0], t2[0]))
1098
1099 # Pump out the attrs, segregated by kind.
Tim Petersf4aad8e2001-09-24 22:40:47 +00001100 attrs = spill("Methods %s:\n" % tag, attrs,
Tim Peters28355492001-09-23 21:29:55 +00001101 lambda t: t[1] == 'method')
Tim Petersf4aad8e2001-09-24 22:40:47 +00001102 attrs = spill("Class methods %s:\n" % tag, attrs,
Tim Peters28355492001-09-23 21:29:55 +00001103 lambda t: t[1] == 'class method')
Tim Petersf4aad8e2001-09-24 22:40:47 +00001104 attrs = spill("Static methods %s:\n" % tag, attrs,
Tim Peters28355492001-09-23 21:29:55 +00001105 lambda t: t[1] == 'static method')
Tim Petersf4aad8e2001-09-24 22:40:47 +00001106 attrs = spillproperties("Properties %s:\n" % tag, attrs,
Tim Petersfa26f7c2001-09-24 08:05:11 +00001107 lambda t: t[1] == 'property')
Tim Petersf33532c2001-09-25 06:30:51 +00001108 attrs = spilldata("Data and non-method functions %s:\n" % tag,
1109 attrs, lambda t: t[1] == 'data')
Tim Peters28355492001-09-23 21:29:55 +00001110 assert attrs == []
Tim Peters351e3622001-09-27 03:29:51 +00001111 attrs = inherited
Tim Peters28355492001-09-23 21:29:55 +00001112
1113 contents = '\n'.join(contents)
1114 if not contents:
1115 return title + '\n'
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001116 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
1117
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001118 def formatvalue(self, object):
1119 """Format an argument default value as text."""
1120 return '=' + self.repr(object)
1121
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001122 def docroutine(self, object, name=None, mod=None, cl=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001123 """Produce text documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001124 realname = object.__name__
1125 name = name or realname
1126 note = ''
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001127 skipdocs = 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001128 if inspect.ismethod(object):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001129 imclass = object.im_class
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001130 if cl:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001131 if imclass is not cl:
1132 note = ' from ' + classname(imclass, mod)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001133 else:
Ka-Ping Yeeb7a48302001-04-12 20:39:14 +00001134 if object.im_self:
1135 note = ' method of %s instance' % classname(
1136 object.im_self.__class__, mod)
1137 else:
1138 note = ' unbound %s method' % classname(imclass,mod)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001139 object = object.im_func
1140
1141 if name == realname:
1142 title = self.bold(realname)
1143 else:
Raymond Hettinger54f02222002-06-01 14:18:47 +00001144 if (cl and realname in cl.__dict__ and
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001145 cl.__dict__[realname] is object):
1146 skipdocs = 1
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001147 title = self.bold(name) + ' = ' + realname
Tim Peters4bcfa312001-09-20 06:08:24 +00001148 if inspect.isfunction(object):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001149 args, varargs, varkw, defaults = inspect.getargspec(object)
1150 argspec = inspect.formatargspec(
1151 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001152 if realname == '<lambda>':
1153 title = 'lambda'
1154 argspec = argspec[1:-1] # remove parentheses
Tim Peters4bcfa312001-09-20 06:08:24 +00001155 else:
1156 argspec = '(...)'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001157 decl = title + argspec + note
1158
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001159 if skipdocs:
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001160 return decl + '\n'
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001161 else:
1162 doc = getdoc(object) or ''
1163 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001164
Tim Peters28355492001-09-23 21:29:55 +00001165 def docother(self, object, name=None, mod=None, maxlen=None, doc=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001166 """Produce text documentation for a data object."""
1167 repr = self.repr(object)
1168 if maxlen:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001169 line = (name and name + ' = ' or '') + repr
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001170 chop = maxlen - len(line)
1171 if chop < 0: repr = repr[:chop] + '...'
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001172 line = (name and self.bold(name) + ' = ' or '') + repr
Tim Peters28355492001-09-23 21:29:55 +00001173 if doc is not None:
1174 line += '\n' + self.indent(str(doc))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001175 return line
1176
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001177# --------------------------------------------------------- user interfaces
1178
1179def pager(text):
1180 """The first time this is called, determine what kind of pager to use."""
1181 global pager
1182 pager = getpager()
1183 pager(text)
1184
1185def getpager():
1186 """Decide what method to use for paging through text."""
1187 if type(sys.stdout) is not types.FileType:
1188 return plainpager
1189 if not sys.stdin.isatty() or not sys.stdout.isatty():
1190 return plainpager
Fred Drake0a66fcb2001-07-23 19:44:30 +00001191 if os.environ.get('TERM') in ['dumb', 'emacs']:
Fred Drake5e9eb982001-07-23 19:48:10 +00001192 return plainpager
Raymond Hettinger54f02222002-06-01 14:18:47 +00001193 if 'PAGER' in os.environ:
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001194 if sys.platform == 'win32': # pipes completely broken in Windows
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001195 return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
1196 elif os.environ.get('TERM') in ['dumb', 'emacs']:
1197 return lambda text: pipepager(plain(text), os.environ['PAGER'])
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001198 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001199 return lambda text: pipepager(text, os.environ['PAGER'])
Andrew MacIntyre54e0eab2002-03-03 03:12:30 +00001200 if sys.platform == 'win32' or sys.platform.startswith('os2'):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001201 return lambda text: tempfilepager(plain(text), 'more <')
Skip Montanarod404bee2002-09-26 21:44:57 +00001202 if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001203 return lambda text: pipepager(text, 'less')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001204
1205 import tempfile
Guido van Rossum3b0a3292002-08-09 16:38:32 +00001206 (fd, filename) = tempfile.mkstemp()
1207 os.close(fd)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001208 try:
1209 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
1210 return lambda text: pipepager(text, 'more')
1211 else:
1212 return ttypager
1213 finally:
1214 os.unlink(filename)
1215
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001216def plain(text):
1217 """Remove boldface formatting from text."""
1218 return re.sub('.\b', '', text)
1219
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001220def pipepager(text, cmd):
1221 """Page through text by feeding it to another program."""
1222 pipe = os.popen(cmd, 'w')
1223 try:
1224 pipe.write(text)
1225 pipe.close()
1226 except IOError:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001227 pass # Ignore broken pipes caused by quitting the pager program.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001228
1229def tempfilepager(text, cmd):
1230 """Page through text by invoking a program on a temporary file."""
1231 import tempfile
Guido van Rossum3b0a3292002-08-09 16:38:32 +00001232 (fd, filename) = tempfile.mkstemp()
1233 file = os.fdopen(fd, 'w')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001234 file.write(text)
1235 file.close()
1236 try:
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001237 os.system(cmd + ' ' + filename)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001238 finally:
1239 os.unlink(filename)
1240
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001241def ttypager(text):
1242 """Page through text on a text terminal."""
1243 lines = split(plain(text), '\n')
1244 try:
1245 import tty
1246 fd = sys.stdin.fileno()
1247 old = tty.tcgetattr(fd)
1248 tty.setcbreak(fd)
1249 getchar = lambda: sys.stdin.read(1)
Ka-Ping Yee457aab22001-02-27 23:36:29 +00001250 except (ImportError, AttributeError):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001251 tty = None
1252 getchar = lambda: sys.stdin.readline()[:-1][:1]
1253
1254 try:
1255 r = inc = os.environ.get('LINES', 25) - 1
1256 sys.stdout.write(join(lines[:inc], '\n') + '\n')
1257 while lines[r:]:
1258 sys.stdout.write('-- more --')
1259 sys.stdout.flush()
1260 c = getchar()
1261
1262 if c in ['q', 'Q']:
1263 sys.stdout.write('\r \r')
1264 break
1265 elif c in ['\r', '\n']:
1266 sys.stdout.write('\r \r' + lines[r] + '\n')
1267 r = r + 1
1268 continue
1269 if c in ['b', 'B', '\x1b']:
1270 r = r - inc - inc
1271 if r < 0: r = 0
1272 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1273 r = r + inc
1274
1275 finally:
1276 if tty:
1277 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1278
1279def plainpager(text):
1280 """Simply print unformatted text. This is the ultimate fallback."""
1281 sys.stdout.write(plain(text))
1282
1283def describe(thing):
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001284 """Produce a short description of the given thing."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001285 if inspect.ismodule(thing):
1286 if thing.__name__ in sys.builtin_module_names:
1287 return 'built-in module ' + thing.__name__
1288 if hasattr(thing, '__path__'):
1289 return 'package ' + thing.__name__
1290 else:
1291 return 'module ' + thing.__name__
1292 if inspect.isbuiltin(thing):
1293 return 'built-in function ' + thing.__name__
1294 if inspect.isclass(thing):
1295 return 'class ' + thing.__name__
1296 if inspect.isfunction(thing):
1297 return 'function ' + thing.__name__
1298 if inspect.ismethod(thing):
1299 return 'method ' + thing.__name__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001300 if type(thing) is types.InstanceType:
1301 return 'instance of ' + thing.__class__.__name__
1302 return type(thing).__name__
1303
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001304def locate(path, forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001305 """Locate an object by name or dotted path, importing as necessary."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001306 parts = split(path, '.')
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001307 module, n = None, 0
1308 while n < len(parts):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001309 nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001310 if nextmodule: module, n = nextmodule, n + 1
1311 else: break
1312 if module:
1313 object = module
1314 for part in parts[n:]:
1315 try: object = getattr(object, part)
1316 except AttributeError: return None
1317 return object
1318 else:
1319 import __builtin__
1320 if hasattr(__builtin__, path):
1321 return getattr(__builtin__, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001322
1323# --------------------------------------- interactive interpreter interface
1324
1325text = TextDoc()
1326html = HTMLDoc()
1327
Ka-Ping Yee45daeb02002-08-11 15:11:33 +00001328def resolve(thing, forceload=0):
1329 """Given an object or a path to an object, get the object and its name."""
1330 if isinstance(thing, str):
1331 object = locate(thing, forceload)
1332 if not object:
1333 raise ImportError, 'no Python documentation found for %r' % thing
1334 return object, thing
1335 else:
1336 return thing, getattr(thing, '__name__', None)
1337
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001338def doc(thing, title='Python Library Documentation: %s', forceload=0):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001339 """Display text documentation, given an object or a path to an object."""
Ka-Ping Yee45daeb02002-08-11 15:11:33 +00001340 try:
1341 object, name = resolve(thing, forceload)
1342 desc = describe(object)
1343 module = inspect.getmodule(object)
1344 if name and '.' in name:
1345 desc += ' in ' + name[:name.rfind('.')]
1346 elif module and module is not object:
1347 desc += ' in module ' + module.__name__
1348 pager(title % desc + '\n\n' + text.document(object, name))
1349 except (ImportError, ErrorDuringImport), value:
1350 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001351
Ka-Ping Yee45daeb02002-08-11 15:11:33 +00001352def writedoc(thing, forceload=0):
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001353 """Write HTML documentation to a file in the current directory."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001354 try:
Ka-Ping Yee45daeb02002-08-11 15:11:33 +00001355 object, name = resolve(thing, forceload)
1356 page = html.page(describe(object), html.document(object, name))
1357 file = open(name + '.html', 'w')
1358 file.write(page)
1359 file.close()
1360 print 'wrote', name + '.html'
1361 except (ImportError, ErrorDuringImport), value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001362 print value
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001363
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001364def writedocs(dir, pkgpath='', done=None):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001365 """Write out HTML documentation for all modules in a directory tree."""
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001366 if done is None: done = {}
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001367 for file in os.listdir(dir):
1368 path = os.path.join(dir, file)
1369 if ispackage(path):
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001370 writedocs(path, pkgpath + file + '.', done)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001371 elif os.path.isfile(path):
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001372 modname = inspect.getmodulename(path)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001373 if modname:
1374 modname = pkgpath + modname
Raymond Hettinger54f02222002-06-01 14:18:47 +00001375 if not modname in done:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001376 done[modname] = 1
1377 writedoc(modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001378
1379class Helper:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001380 keywords = {
1381 'and': 'BOOLEAN',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001382 'assert': ('ref/assert', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001383 'break': ('ref/break', 'while for'),
1384 'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1385 'continue': ('ref/continue', 'while for'),
1386 'def': ('ref/function', ''),
1387 'del': ('ref/del', 'BASICMETHODS'),
1388 'elif': 'if',
1389 'else': ('ref/if', 'while for'),
1390 'except': 'try',
1391 'exec': ('ref/exec', ''),
1392 'finally': 'try',
1393 'for': ('ref/for', 'break continue while'),
1394 'from': 'import',
1395 'global': ('ref/global', 'NAMESPACES'),
1396 'if': ('ref/if', 'TRUTHVALUE'),
1397 'import': ('ref/import', 'MODULES'),
1398 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1399 'is': 'COMPARISON',
1400 'lambda': ('ref/lambda', 'FUNCTIONS'),
1401 'not': 'BOOLEAN',
1402 'or': 'BOOLEAN',
1403 'pass': 'PASS',
1404 'print': ('ref/print', ''),
1405 'raise': ('ref/raise', 'EXCEPTIONS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001406 'return': ('ref/return', 'FUNCTIONS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001407 'try': ('ref/try', 'EXCEPTIONS'),
1408 'while': ('ref/while', 'break continue if TRUTHVALUE'),
1409 }
1410
1411 topics = {
1412 'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001413 'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001414 'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'),
1415 'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001416 'UNICODE': ('ref/unicode', 'encodings unicode TYPES STRING'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001417 'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1418 'INTEGER': ('ref/integers', 'int range'),
1419 'FLOAT': ('ref/floating', 'float math'),
1420 'COMPLEX': ('ref/imaginary', 'complex cmath'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001421 'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001422 'MAPPINGS': 'DICTIONARIES',
1423 'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'),
1424 'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'),
1425 'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001426 'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001427 'FRAMEOBJECTS': 'TYPES',
1428 'TRACEBACKS': 'TYPES',
1429 'NONE': ('lib/bltin-null-object', ''),
1430 'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'),
1431 'FILES': ('lib/bltin-file-objects', ''),
1432 'SPECIALATTRIBUTES': ('lib/specialattrs', ''),
1433 'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'),
1434 'MODULES': ('lib/typesmodules', 'import'),
1435 'PACKAGES': 'import',
1436 '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'),
1437 'OPERATORS': 'EXPRESSIONS',
1438 'PRECEDENCE': 'EXPRESSIONS',
1439 'OBJECTS': ('ref/objects', 'TYPES'),
1440 'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001441 'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'),
1442 'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1443 'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'),
1444 'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'),
1445 'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'),
1446 'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'),
1447 'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001448 'EXECUTION': ('ref/execframes', ''),
1449 'NAMESPACES': ('ref/execframes', 'global ASSIGNMENT DELETION'),
1450 'SCOPING': 'NAMESPACES',
1451 'FRAMES': 'NAMESPACES',
1452 'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'),
1453 'COERCIONS': 'CONVERSIONS',
1454 'CONVERSIONS': ('ref/conversions', ''),
1455 'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'),
1456 'SPECIALIDENTIFIERS': ('ref/id-classes', ''),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001457 'PRIVATENAMES': ('ref/atom-identifiers', ''),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001458 'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1459 'TUPLES': 'SEQUENCES',
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001460 'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001461 'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001462 'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001463 'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001464 'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'),
1465 'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001466 'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1467 'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'),
1468 'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'),
1469 'CALLS': ('ref/calls', 'EXPRESSIONS'),
1470 'POWER': ('ref/power', 'EXPRESSIONS'),
1471 'UNARY': ('ref/unary', 'EXPRESSIONS'),
1472 'BINARY': ('ref/binary', 'EXPRESSIONS'),
1473 'SHIFTING': ('ref/shifting', 'EXPRESSIONS'),
1474 'BITWISE': ('ref/bitwise', 'EXPRESSIONS'),
1475 'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001476 'BOOLEAN': ('ref/lambda', 'EXPRESSIONS TRUTHVALUE'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001477 'ASSERTION': 'assert',
1478 'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001479 'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001480 'DELETION': 'del',
1481 'PRINTING': 'print',
1482 'RETURNING': 'return',
1483 'IMPORTING': 'import',
1484 'CONDITIONAL': 'if',
1485 'LOOPING': ('ref/compound', 'for while break continue'),
1486 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'),
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001487 'DEBUGGING': ('lib/module-pdb', 'pdb'),
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001488 }
1489
1490 def __init__(self, input, output):
1491 self.input = input
1492 self.output = output
1493 self.docdir = None
1494 execdir = os.path.dirname(sys.executable)
1495 homedir = os.environ.get('PYTHONHOME')
1496 for dir in [os.environ.get('PYTHONDOCS'),
1497 homedir and os.path.join(homedir, 'doc'),
1498 os.path.join(execdir, 'doc'),
1499 '/usr/doc/python-docs-' + split(sys.version)[0],
1500 '/usr/doc/python-' + split(sys.version)[0],
1501 '/usr/doc/python-docs-' + sys.version[:3],
Jack Jansenb2628b02002-08-23 08:40:42 +00001502 '/usr/doc/python-' + sys.version[:3],
1503 os.path.join(sys.prefix, 'Resources/English.lproj/Documentation')]:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001504 if dir and os.path.isdir(os.path.join(dir, 'lib')):
1505 self.docdir = dir
1506
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001507 def __repr__(self):
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001508 if inspect.stack()[1][3] == '?':
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001509 self()
1510 return ''
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001511 return '<pydoc.Helper instance>'
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001512
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001513 def __call__(self, request=None):
1514 if request is not None:
1515 self.help(request)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001516 else:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001517 self.intro()
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001518 self.interact()
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001519 self.output.write('''
Fred Drakee61967f2001-05-10 18:41:02 +00001520You are now leaving help and returning to the Python interpreter.
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001521If you want to ask for help on a particular object directly from the
1522interpreter, you can type "help(object)". Executing "help('string')"
1523has the same effect as typing a particular string at the help> prompt.
1524''')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001525
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001526 def interact(self):
1527 self.output.write('\n')
Guido van Rossum8ca162f2002-04-07 06:36:23 +00001528 while True:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001529 self.output.write('help> ')
1530 self.output.flush()
1531 try:
1532 request = self.input.readline()
1533 if not request: break
1534 except KeyboardInterrupt: break
1535 request = strip(replace(request, '"', '', "'", ''))
1536 if lower(request) in ['q', 'quit']: break
1537 self.help(request)
1538
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001539 def help(self, request):
1540 if type(request) is type(''):
1541 if request == 'help': self.intro()
1542 elif request == 'keywords': self.listkeywords()
1543 elif request == 'topics': self.listtopics()
1544 elif request == 'modules': self.listmodules()
1545 elif request[:8] == 'modules ':
1546 self.listmodules(split(request)[1])
Raymond Hettinger54f02222002-06-01 14:18:47 +00001547 elif request in self.keywords: self.showtopic(request)
1548 elif request in self.topics: self.showtopic(request)
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001549 elif request: doc(request, 'Help on %s:')
1550 elif isinstance(request, Helper): self()
1551 else: doc(request, 'Help on %s:')
1552 self.output.write('\n')
1553
1554 def intro(self):
1555 self.output.write('''
1556Welcome to Python %s! This is the online help utility.
1557
1558If this is your first time using Python, you should definitely check out
1559the tutorial on the Internet at http://www.python.org/doc/tut/.
1560
1561Enter the name of any module, keyword, or topic to get help on writing
1562Python programs and using Python modules. To quit this help utility and
1563return to the interpreter, just type "quit".
1564
1565To get a list of available modules, keywords, or topics, type "modules",
1566"keywords", or "topics". Each module also comes with a one-line summary
1567of what it does; to list the modules whose summaries contain a given word
1568such as "spam", type "modules spam".
1569''' % sys.version[:3])
1570
1571 def list(self, items, columns=4, width=80):
1572 items = items[:]
1573 items.sort()
1574 colw = width / columns
1575 rows = (len(items) + columns - 1) / columns
1576 for row in range(rows):
1577 for col in range(columns):
1578 i = col * rows + row
1579 if i < len(items):
1580 self.output.write(items[i])
1581 if col < columns - 1:
1582 self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1583 self.output.write('\n')
1584
1585 def listkeywords(self):
1586 self.output.write('''
1587Here is a list of the Python keywords. Enter any keyword to get more help.
1588
1589''')
1590 self.list(self.keywords.keys())
1591
1592 def listtopics(self):
1593 self.output.write('''
1594Here is a list of available topics. Enter any topic name to get more help.
1595
1596''')
1597 self.list(self.topics.keys())
1598
1599 def showtopic(self, topic):
1600 if not self.docdir:
1601 self.output.write('''
1602Sorry, topic and keyword documentation is not available because the Python
1603HTML documentation files could not be found. If you have installed them,
1604please set the environment variable PYTHONDOCS to indicate their location.
1605''')
1606 return
1607 target = self.topics.get(topic, self.keywords.get(topic))
1608 if not target:
1609 self.output.write('no documentation found for %s\n' % repr(topic))
1610 return
1611 if type(target) is type(''):
1612 return self.showtopic(target)
1613
1614 filename, xrefs = target
1615 filename = self.docdir + '/' + filename + '.html'
1616 try:
1617 file = open(filename)
1618 except:
1619 self.output.write('could not read docs from %s\n' % filename)
1620 return
1621
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001622 divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
1623 addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001624 document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1625 file.close()
1626
1627 import htmllib, formatter, StringIO
1628 buffer = StringIO.StringIO()
1629 parser = htmllib.HTMLParser(
1630 formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1631 parser.start_table = parser.do_p
1632 parser.end_table = lambda parser=parser: parser.do_p({})
1633 parser.start_tr = parser.do_br
1634 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1635 parser.feed(document)
1636 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
1637 pager(' ' + strip(buffer) + '\n')
Ka-Ping Yeeda793892001-04-13 11:02:51 +00001638 if xrefs:
1639 buffer = StringIO.StringIO()
1640 formatter.DumbWriter(buffer).send_flowing_data(
1641 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1642 self.output.write('\n%s\n' % buffer.getvalue())
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001643
1644 def listmodules(self, key=''):
1645 if key:
1646 self.output.write('''
1647Here is a list of matching modules. Enter any module name to get more help.
1648
1649''')
1650 apropos(key)
1651 else:
1652 self.output.write('''
1653Please wait a moment while I gather a list of all available modules...
1654
1655''')
1656 modules = {}
1657 def callback(path, modname, desc, modules=modules):
1658 if modname and modname[-9:] == '.__init__':
1659 modname = modname[:-9] + ' (package)'
1660 if find(modname, '.') < 0:
1661 modules[modname] = 1
1662 ModuleScanner().run(callback)
1663 self.list(modules.keys())
1664 self.output.write('''
1665Enter any module name to get more help. Or, type "modules spam" to search
1666for modules whose descriptions contain the word "spam".
1667''')
1668
1669help = Helper(sys.stdin, sys.stdout)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001670
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001671class Scanner:
1672 """A generic tree iterator."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001673 def __init__(self, roots, children, descendp):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001674 self.roots = roots[:]
1675 self.state = []
1676 self.children = children
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001677 self.descendp = descendp
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001678
1679 def next(self):
1680 if not self.state:
1681 if not self.roots:
1682 return None
1683 root = self.roots.pop(0)
1684 self.state = [(root, self.children(root))]
1685 node, children = self.state[-1]
1686 if not children:
1687 self.state.pop()
1688 return self.next()
1689 child = children.pop(0)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001690 if self.descendp(child):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001691 self.state.append((child, self.children(child)))
1692 return child
1693
1694class ModuleScanner(Scanner):
1695 """An interruptible scanner that searches module synopses."""
1696 def __init__(self):
1697 roots = map(lambda dir: (dir, ''), pathdirs())
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001698 Scanner.__init__(self, roots, self.submodules, self.isnewpackage)
Raymond Hettinger32200ae2002-06-01 19:51:15 +00001699 self.inodes = map(lambda (dir, pkg): os.stat(dir).st_ino, roots)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001700
1701 def submodules(self, (dir, package)):
1702 children = []
1703 for file in os.listdir(dir):
1704 path = os.path.join(dir, file)
Tim Peters30edd232001-03-16 08:29:48 +00001705 if ispackage(path):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001706 children.append((path, package + (package and '.') + file))
1707 else:
1708 children.append((path, package))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001709 children.sort() # so that spam.py comes before spam.pyc or spam.pyo
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001710 return children
1711
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001712 def isnewpackage(self, (dir, package)):
Raymond Hettinger32200ae2002-06-01 19:51:15 +00001713 inode = os.path.exists(dir) and os.stat(dir).st_ino
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001714 if not (os.path.islink(dir) and inode in self.inodes):
Ka-Ping Yee6191a232001-04-13 15:00:27 +00001715 self.inodes.append(inode) # detect circular symbolic links
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001716 return ispackage(dir)
Guido van Rossum8ca162f2002-04-07 06:36:23 +00001717 return False
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001718
Ka-Ping Yee66246962001-04-12 11:59:50 +00001719 def run(self, callback, key=None, completer=None):
1720 if key: key = lower(key)
Guido van Rossum8ca162f2002-04-07 06:36:23 +00001721 self.quit = False
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001722 seen = {}
1723
1724 for modname in sys.builtin_module_names:
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001725 if modname != '__main__':
1726 seen[modname] = 1
Ka-Ping Yee66246962001-04-12 11:59:50 +00001727 if key is None:
1728 callback(None, modname, '')
1729 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001730 desc = split(__import__(modname).__doc__ or '', '\n')[0]
Ka-Ping Yee66246962001-04-12 11:59:50 +00001731 if find(lower(modname + ' - ' + desc), key) >= 0:
1732 callback(None, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001733
1734 while not self.quit:
1735 node = self.next()
1736 if not node: break
1737 path, package = node
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001738 modname = inspect.getmodulename(path)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001739 if os.path.isfile(path) and modname:
1740 modname = package + (package and '.') + modname
Raymond Hettinger54f02222002-06-01 14:18:47 +00001741 if not modname in seen:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001742 seen[modname] = 1 # if we see spam.py, skip spam.pyc
Ka-Ping Yee66246962001-04-12 11:59:50 +00001743 if key is None:
1744 callback(path, modname, '')
1745 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001746 desc = synopsis(path) or ''
1747 if find(lower(modname + ' - ' + desc), key) >= 0:
1748 callback(path, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001749 if completer: completer()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001750
1751def apropos(key):
1752 """Print all the one-line module summaries that contain a substring."""
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001753 def callback(path, modname, desc):
1754 if modname[-9:] == '.__init__':
1755 modname = modname[:-9] + ' (package)'
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001756 print modname, desc and '- ' + desc
1757 try: import warnings
1758 except ImportError: pass
1759 else: warnings.filterwarnings('ignore') # ignore problems during import
Ka-Ping Yee66246962001-04-12 11:59:50 +00001760 ModuleScanner().run(callback, key)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001761
1762# --------------------------------------------------- web browser interface
1763
Ka-Ping Yee66246962001-04-12 11:59:50 +00001764def serve(port, callback=None, completer=None):
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001765 import BaseHTTPServer, mimetools, select
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001766
1767 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1768 class Message(mimetools.Message):
1769 def __init__(self, fp, seekable=1):
1770 Message = self.__class__
1771 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1772 self.encodingheader = self.getheader('content-transfer-encoding')
1773 self.typeheader = self.getheader('content-type')
1774 self.parsetype()
1775 self.parseplist()
1776
1777 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1778 def send_document(self, title, contents):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001779 try:
1780 self.send_response(200)
1781 self.send_header('Content-Type', 'text/html')
1782 self.end_headers()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001783 self.wfile.write(html.page(title, contents))
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001784 except IOError: pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001785
1786 def do_GET(self):
1787 path = self.path
1788 if path[-5:] == '.html': path = path[:-5]
1789 if path[:1] == '/': path = path[1:]
1790 if path and path != '.':
1791 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001792 obj = locate(path, forceload=1)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001793 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001794 self.send_document(path, html.escape(str(value)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001795 return
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001796 if obj:
1797 self.send_document(describe(obj), html.document(obj, path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001798 else:
1799 self.send_document(path,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001800'no Python documentation found for %s' % repr(path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001801 else:
1802 heading = html.heading(
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001803'<big><big><strong>Python: Index of Modules</strong></big></big>',
1804'#ffffff', '#7799ee')
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001805 def bltinlink(name):
1806 return '<a href="%s.html">%s</a>' % (name, name)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001807 names = filter(lambda x: x != '__main__',
1808 sys.builtin_module_names)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001809 contents = html.multicolumn(names, bltinlink)
1810 indices = ['<p>' + html.bigsection(
1811 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1812
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001813 seen = {}
1814 for dir in pathdirs():
1815 indices.append(html.index(dir, seen))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001816 contents = heading + join(indices) + '''<p align=right>
Tim Peters2306d242001-09-25 03:18:32 +00001817<font color="#909090" face="helvetica, arial"><strong>
1818pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font>'''
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001819 self.send_document('Index of Modules', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001820
1821 def log_message(self, *args): pass
1822
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001823 class DocServer(BaseHTTPServer.HTTPServer):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001824 def __init__(self, port, callback):
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001825 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001826 self.address = ('', port)
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001827 self.url = 'http://%s:%d/' % (host, port)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001828 self.callback = callback
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001829 self.base.__init__(self, self.address, self.handler)
1830
1831 def serve_until_quit(self):
1832 import select
Guido van Rossum8ca162f2002-04-07 06:36:23 +00001833 self.quit = False
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001834 while not self.quit:
1835 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1836 if rd: self.handle_request()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001837
1838 def server_activate(self):
1839 self.base.server_activate(self)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001840 if self.callback: self.callback(self)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001841
1842 DocServer.base = BaseHTTPServer.HTTPServer
1843 DocServer.handler = DocHandler
1844 DocHandler.MessageClass = Message
1845 try:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001846 try:
1847 DocServer(port, callback).serve_until_quit()
1848 except (KeyboardInterrupt, select.error):
1849 pass
1850 finally:
Ka-Ping Yee66246962001-04-12 11:59:50 +00001851 if completer: completer()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001852
1853# ----------------------------------------------------- graphical interface
1854
1855def gui():
1856 """Graphical interface (starts web server and pops up a control window)."""
1857 class GUI:
1858 def __init__(self, window, port=7464):
1859 self.window = window
1860 self.server = None
1861 self.scanner = None
1862
1863 import Tkinter
1864 self.server_frm = Tkinter.Frame(window)
1865 self.title_lbl = Tkinter.Label(self.server_frm,
1866 text='Starting server...\n ')
1867 self.open_btn = Tkinter.Button(self.server_frm,
1868 text='open browser', command=self.open, state='disabled')
1869 self.quit_btn = Tkinter.Button(self.server_frm,
1870 text='quit serving', command=self.quit, state='disabled')
1871
1872 self.search_frm = Tkinter.Frame(window)
1873 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
1874 self.search_ent = Tkinter.Entry(self.search_frm)
1875 self.search_ent.bind('<Return>', self.search)
1876 self.stop_btn = Tkinter.Button(self.search_frm,
1877 text='stop', pady=0, command=self.stop, state='disabled')
1878 if sys.platform == 'win32':
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001879 # Trying to hide and show this button crashes under Windows.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001880 self.stop_btn.pack(side='right')
1881
1882 self.window.title('pydoc')
1883 self.window.protocol('WM_DELETE_WINDOW', self.quit)
1884 self.title_lbl.pack(side='top', fill='x')
1885 self.open_btn.pack(side='left', fill='x', expand=1)
1886 self.quit_btn.pack(side='right', fill='x', expand=1)
1887 self.server_frm.pack(side='top', fill='x')
1888
1889 self.search_lbl.pack(side='left')
1890 self.search_ent.pack(side='right', fill='x', expand=1)
1891 self.search_frm.pack(side='top', fill='x')
1892 self.search_ent.focus_set()
1893
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001894 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001895 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001896 self.result_lst.bind('<Button-1>', self.select)
1897 self.result_lst.bind('<Double-Button-1>', self.goto)
1898 self.result_scr = Tkinter.Scrollbar(window,
1899 orient='vertical', command=self.result_lst.yview)
1900 self.result_lst.config(yscrollcommand=self.result_scr.set)
1901
1902 self.result_frm = Tkinter.Frame(window)
1903 self.goto_btn = Tkinter.Button(self.result_frm,
1904 text='go to selected', command=self.goto)
1905 self.hide_btn = Tkinter.Button(self.result_frm,
1906 text='hide results', command=self.hide)
1907 self.goto_btn.pack(side='left', fill='x', expand=1)
1908 self.hide_btn.pack(side='right', fill='x', expand=1)
1909
1910 self.window.update()
1911 self.minwidth = self.window.winfo_width()
1912 self.minheight = self.window.winfo_height()
1913 self.bigminheight = (self.server_frm.winfo_reqheight() +
1914 self.search_frm.winfo_reqheight() +
1915 self.result_lst.winfo_reqheight() +
1916 self.result_frm.winfo_reqheight())
1917 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
1918 self.expanded = 0
1919 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1920 self.window.wm_minsize(self.minwidth, self.minheight)
1921
1922 import threading
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001923 threading.Thread(
1924 target=serve, args=(port, self.ready, self.quit)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001925
1926 def ready(self, server):
1927 self.server = server
1928 self.title_lbl.config(
1929 text='Python documentation server at\n' + server.url)
1930 self.open_btn.config(state='normal')
1931 self.quit_btn.config(state='normal')
1932
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001933 def open(self, event=None, url=None):
1934 url = url or self.server.url
1935 try:
1936 import webbrowser
1937 webbrowser.open(url)
1938 except ImportError: # pre-webbrowser.py compatibility
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001939 if sys.platform == 'win32':
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001940 os.system('start "%s"' % url)
1941 elif sys.platform == 'mac':
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001942 try: import ic
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001943 except ImportError: pass
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001944 else: ic.launchurl(url)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001945 else:
1946 rc = os.system('netscape -remote "openURL(%s)" &' % url)
1947 if rc: os.system('netscape "%s" &' % url)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001948
1949 def quit(self, event=None):
1950 if self.server:
1951 self.server.quit = 1
1952 self.window.quit()
1953
1954 def search(self, event=None):
1955 key = self.search_ent.get()
1956 self.stop_btn.pack(side='right')
1957 self.stop_btn.config(state='normal')
1958 self.search_lbl.config(text='Searching for "%s"...' % key)
1959 self.search_ent.forget()
1960 self.search_lbl.pack(side='left')
1961 self.result_lst.delete(0, 'end')
1962 self.goto_btn.config(state='disabled')
1963 self.expand()
1964
1965 import threading
1966 if self.scanner:
1967 self.scanner.quit = 1
1968 self.scanner = ModuleScanner()
1969 threading.Thread(target=self.scanner.run,
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +00001970 args=(self.update, key, self.done)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001971
1972 def update(self, path, modname, desc):
1973 if modname[-9:] == '.__init__':
1974 modname = modname[:-9] + ' (package)'
1975 self.result_lst.insert('end',
1976 modname + ' - ' + (desc or '(no description)'))
1977
1978 def stop(self, event=None):
1979 if self.scanner:
1980 self.scanner.quit = 1
1981 self.scanner = None
1982
1983 def done(self):
1984 self.scanner = None
1985 self.search_lbl.config(text='Search for')
1986 self.search_lbl.pack(side='left')
1987 self.search_ent.pack(side='right', fill='x', expand=1)
1988 if sys.platform != 'win32': self.stop_btn.forget()
1989 self.stop_btn.config(state='disabled')
1990
1991 def select(self, event=None):
1992 self.goto_btn.config(state='normal')
1993
1994 def goto(self, event=None):
1995 selection = self.result_lst.curselection()
1996 if selection:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001997 modname = split(self.result_lst.get(selection[0]))[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001998 self.open(url=self.server.url + modname + '.html')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001999
2000 def collapse(self):
2001 if not self.expanded: return
2002 self.result_frm.forget()
2003 self.result_scr.forget()
2004 self.result_lst.forget()
2005 self.bigwidth = self.window.winfo_width()
2006 self.bigheight = self.window.winfo_height()
2007 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
2008 self.window.wm_minsize(self.minwidth, self.minheight)
2009 self.expanded = 0
2010
2011 def expand(self):
2012 if self.expanded: return
2013 self.result_frm.pack(side='bottom', fill='x')
2014 self.result_scr.pack(side='right', fill='y')
2015 self.result_lst.pack(side='top', fill='both', expand=1)
2016 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
2017 self.window.wm_minsize(self.minwidth, self.bigminheight)
2018 self.expanded = 1
2019
2020 def hide(self, event=None):
2021 self.stop()
2022 self.collapse()
2023
2024 import Tkinter
2025 try:
2026 gui = GUI(Tkinter.Tk())
2027 Tkinter.mainloop()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002028 except KeyboardInterrupt:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002029 pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002030
2031# -------------------------------------------------- command-line interface
2032
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002033def ispath(x):
Ka-Ping Yee45daeb02002-08-11 15:11:33 +00002034 return isinstance(x, str) and find(x, os.sep) >= 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002035
Ka-Ping Yee1d384632001-03-01 00:24:32 +00002036def cli():
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002037 """Command-line interface (looks at sys.argv to decide what to do)."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002038 import getopt
2039 class BadUsage: pass
2040
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002041 # Scripts don't get the current directory in their path by default.
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00002042 scriptdir = os.path.dirname(sys.argv[0])
2043 if scriptdir in sys.path:
2044 sys.path.remove(scriptdir)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002045 sys.path.insert(0, '.')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002046
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002047 try:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002048 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002049 writing = 0
2050
2051 for opt, val in opts:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002052 if opt == '-g':
2053 gui()
2054 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002055 if opt == '-k':
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002056 apropos(val)
2057 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002058 if opt == '-p':
2059 try:
2060 port = int(val)
2061 except ValueError:
2062 raise BadUsage
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002063 def ready(server):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00002064 print 'pydoc server ready at %s' % server.url
2065 def stopped():
2066 print 'pydoc server stopped'
2067 serve(port, ready, stopped)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002068 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002069 if opt == '-w':
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002070 writing = 1
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002071
2072 if not args: raise BadUsage
2073 for arg in args:
Ka-Ping Yee45daeb02002-08-11 15:11:33 +00002074 if ispath(arg) and not os.path.exists(arg):
2075 print 'file %r does not exist' % arg
2076 break
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002077 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002078 if ispath(arg) and os.path.isfile(arg):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002079 arg = importfile(arg)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002080 if writing:
2081 if ispath(arg) and os.path.isdir(arg):
2082 writedocs(arg)
2083 else:
2084 writedoc(arg)
2085 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00002086 doc(arg)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002087 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002088 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002089
2090 except (getopt.error, BadUsage):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002091 cmd = sys.argv[0]
2092 print """pydoc - the Python documentation tool
2093
2094%s <name> ...
2095 Show text documentation on something. <name> may be the name of a
2096 function, module, or package, or a dotted reference to a class or
2097 function within a module or module in a package. If <name> contains
2098 a '%s', it is used as the path to a Python source file to document.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002099
2100%s -k <keyword>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002101 Search for a keyword in the synopsis lines of all available modules.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002102
2103%s -p <port>
2104 Start an HTTP server on the given port on the local machine.
2105
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002106%s -g
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00002107 Pop up a graphical interface for finding and serving documentation.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002108
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002109%s -w <name> ...
2110 Write out the HTML documentation for a module to a file in the current
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00002111 directory. If <name> contains a '%s', it is treated as a filename; if
2112 it names a directory, documentation is written for all the contents.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002113""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
Ka-Ping Yee1d384632001-03-01 00:24:32 +00002114
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002115if __name__ == '__main__': cli()