blob: f6826b4df8b7d5cb32819fd65cb8c1634b826b63 [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 <')
Ka-Ping Yeedd175342001-02-27 14:43:46 +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],
1502 '/usr/doc/python-' + sys.version[:3]]:
1503 if dir and os.path.isdir(os.path.join(dir, 'lib')):
1504 self.docdir = dir
1505
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001506 def __repr__(self):
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001507 if inspect.stack()[1][3] == '?':
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001508 self()
1509 return ''
Ka-Ping Yee9bc576b2001-04-13 13:57:31 +00001510 return '<pydoc.Helper instance>'
Ka-Ping Yee79c009d2001-04-13 10:53:25 +00001511
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001512 def __call__(self, request=None):
1513 if request is not None:
1514 self.help(request)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001515 else:
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001516 self.intro()
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001517 self.interact()
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001518 self.output.write('''
Fred Drakee61967f2001-05-10 18:41:02 +00001519You are now leaving help and returning to the Python interpreter.
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001520If you want to ask for help on a particular object directly from the
1521interpreter, you can type "help(object)". Executing "help('string')"
1522has the same effect as typing a particular string at the help> prompt.
1523''')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001524
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001525 def interact(self):
1526 self.output.write('\n')
Guido van Rossum8ca162f2002-04-07 06:36:23 +00001527 while True:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001528 self.output.write('help> ')
1529 self.output.flush()
1530 try:
1531 request = self.input.readline()
1532 if not request: break
1533 except KeyboardInterrupt: break
1534 request = strip(replace(request, '"', '', "'", ''))
1535 if lower(request) in ['q', 'quit']: break
1536 self.help(request)
1537
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001538 def help(self, request):
1539 if type(request) is type(''):
1540 if request == 'help': self.intro()
1541 elif request == 'keywords': self.listkeywords()
1542 elif request == 'topics': self.listtopics()
1543 elif request == 'modules': self.listmodules()
1544 elif request[:8] == 'modules ':
1545 self.listmodules(split(request)[1])
Raymond Hettinger54f02222002-06-01 14:18:47 +00001546 elif request in self.keywords: self.showtopic(request)
1547 elif request in self.topics: self.showtopic(request)
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001548 elif request: doc(request, 'Help on %s:')
1549 elif isinstance(request, Helper): self()
1550 else: doc(request, 'Help on %s:')
1551 self.output.write('\n')
1552
1553 def intro(self):
1554 self.output.write('''
1555Welcome to Python %s! This is the online help utility.
1556
1557If this is your first time using Python, you should definitely check out
1558the tutorial on the Internet at http://www.python.org/doc/tut/.
1559
1560Enter the name of any module, keyword, or topic to get help on writing
1561Python programs and using Python modules. To quit this help utility and
1562return to the interpreter, just type "quit".
1563
1564To get a list of available modules, keywords, or topics, type "modules",
1565"keywords", or "topics". Each module also comes with a one-line summary
1566of what it does; to list the modules whose summaries contain a given word
1567such as "spam", type "modules spam".
1568''' % sys.version[:3])
1569
1570 def list(self, items, columns=4, width=80):
1571 items = items[:]
1572 items.sort()
1573 colw = width / columns
1574 rows = (len(items) + columns - 1) / columns
1575 for row in range(rows):
1576 for col in range(columns):
1577 i = col * rows + row
1578 if i < len(items):
1579 self.output.write(items[i])
1580 if col < columns - 1:
1581 self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1582 self.output.write('\n')
1583
1584 def listkeywords(self):
1585 self.output.write('''
1586Here is a list of the Python keywords. Enter any keyword to get more help.
1587
1588''')
1589 self.list(self.keywords.keys())
1590
1591 def listtopics(self):
1592 self.output.write('''
1593Here is a list of available topics. Enter any topic name to get more help.
1594
1595''')
1596 self.list(self.topics.keys())
1597
1598 def showtopic(self, topic):
1599 if not self.docdir:
1600 self.output.write('''
1601Sorry, topic and keyword documentation is not available because the Python
1602HTML documentation files could not be found. If you have installed them,
1603please set the environment variable PYTHONDOCS to indicate their location.
1604''')
1605 return
1606 target = self.topics.get(topic, self.keywords.get(topic))
1607 if not target:
1608 self.output.write('no documentation found for %s\n' % repr(topic))
1609 return
1610 if type(target) is type(''):
1611 return self.showtopic(target)
1612
1613 filename, xrefs = target
1614 filename = self.docdir + '/' + filename + '.html'
1615 try:
1616 file = open(filename)
1617 except:
1618 self.output.write('could not read docs from %s\n' % filename)
1619 return
1620
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001621 divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
1622 addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001623 document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1624 file.close()
1625
1626 import htmllib, formatter, StringIO
1627 buffer = StringIO.StringIO()
1628 parser = htmllib.HTMLParser(
1629 formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1630 parser.start_table = parser.do_p
1631 parser.end_table = lambda parser=parser: parser.do_p({})
1632 parser.start_tr = parser.do_br
1633 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1634 parser.feed(document)
1635 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
1636 pager(' ' + strip(buffer) + '\n')
Ka-Ping Yeeda793892001-04-13 11:02:51 +00001637 if xrefs:
1638 buffer = StringIO.StringIO()
1639 formatter.DumbWriter(buffer).send_flowing_data(
1640 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1641 self.output.write('\n%s\n' % buffer.getvalue())
Ka-Ping Yee35cf0a32001-04-12 19:53:52 +00001642
1643 def listmodules(self, key=''):
1644 if key:
1645 self.output.write('''
1646Here is a list of matching modules. Enter any module name to get more help.
1647
1648''')
1649 apropos(key)
1650 else:
1651 self.output.write('''
1652Please wait a moment while I gather a list of all available modules...
1653
1654''')
1655 modules = {}
1656 def callback(path, modname, desc, modules=modules):
1657 if modname and modname[-9:] == '.__init__':
1658 modname = modname[:-9] + ' (package)'
1659 if find(modname, '.') < 0:
1660 modules[modname] = 1
1661 ModuleScanner().run(callback)
1662 self.list(modules.keys())
1663 self.output.write('''
1664Enter any module name to get more help. Or, type "modules spam" to search
1665for modules whose descriptions contain the word "spam".
1666''')
1667
1668help = Helper(sys.stdin, sys.stdout)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001669
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001670class Scanner:
1671 """A generic tree iterator."""
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001672 def __init__(self, roots, children, descendp):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001673 self.roots = roots[:]
1674 self.state = []
1675 self.children = children
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001676 self.descendp = descendp
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001677
1678 def next(self):
1679 if not self.state:
1680 if not self.roots:
1681 return None
1682 root = self.roots.pop(0)
1683 self.state = [(root, self.children(root))]
1684 node, children = self.state[-1]
1685 if not children:
1686 self.state.pop()
1687 return self.next()
1688 child = children.pop(0)
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001689 if self.descendp(child):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001690 self.state.append((child, self.children(child)))
1691 return child
1692
1693class ModuleScanner(Scanner):
1694 """An interruptible scanner that searches module synopses."""
1695 def __init__(self):
1696 roots = map(lambda dir: (dir, ''), pathdirs())
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001697 Scanner.__init__(self, roots, self.submodules, self.isnewpackage)
Raymond Hettinger32200ae2002-06-01 19:51:15 +00001698 self.inodes = map(lambda (dir, pkg): os.stat(dir).st_ino, roots)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001699
1700 def submodules(self, (dir, package)):
1701 children = []
1702 for file in os.listdir(dir):
1703 path = os.path.join(dir, file)
Tim Peters30edd232001-03-16 08:29:48 +00001704 if ispackage(path):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001705 children.append((path, package + (package and '.') + file))
1706 else:
1707 children.append((path, package))
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001708 children.sort() # so that spam.py comes before spam.pyc or spam.pyo
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001709 return children
1710
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001711 def isnewpackage(self, (dir, package)):
Raymond Hettinger32200ae2002-06-01 19:51:15 +00001712 inode = os.path.exists(dir) and os.stat(dir).st_ino
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001713 if not (os.path.islink(dir) and inode in self.inodes):
Ka-Ping Yee6191a232001-04-13 15:00:27 +00001714 self.inodes.append(inode) # detect circular symbolic links
Ka-Ping Yeeeca15c12001-04-13 13:53:07 +00001715 return ispackage(dir)
Guido van Rossum8ca162f2002-04-07 06:36:23 +00001716 return False
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001717
Ka-Ping Yee66246962001-04-12 11:59:50 +00001718 def run(self, callback, key=None, completer=None):
1719 if key: key = lower(key)
Guido van Rossum8ca162f2002-04-07 06:36:23 +00001720 self.quit = False
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001721 seen = {}
1722
1723 for modname in sys.builtin_module_names:
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001724 if modname != '__main__':
1725 seen[modname] = 1
Ka-Ping Yee66246962001-04-12 11:59:50 +00001726 if key is None:
1727 callback(None, modname, '')
1728 else:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001729 desc = split(__import__(modname).__doc__ or '', '\n')[0]
Ka-Ping Yee66246962001-04-12 11:59:50 +00001730 if find(lower(modname + ' - ' + desc), key) >= 0:
1731 callback(None, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001732
1733 while not self.quit:
1734 node = self.next()
1735 if not node: break
1736 path, package = node
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001737 modname = inspect.getmodulename(path)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001738 if os.path.isfile(path) and modname:
1739 modname = package + (package and '.') + modname
Raymond Hettinger54f02222002-06-01 14:18:47 +00001740 if not modname in seen:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001741 seen[modname] = 1 # if we see spam.py, skip spam.pyc
Ka-Ping Yee66246962001-04-12 11:59:50 +00001742 if key is None:
1743 callback(path, modname, '')
1744 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001745 desc = synopsis(path) or ''
1746 if find(lower(modname + ' - ' + desc), key) >= 0:
1747 callback(path, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001748 if completer: completer()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001749
1750def apropos(key):
1751 """Print all the one-line module summaries that contain a substring."""
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001752 def callback(path, modname, desc):
1753 if modname[-9:] == '.__init__':
1754 modname = modname[:-9] + ' (package)'
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001755 print modname, desc and '- ' + desc
1756 try: import warnings
1757 except ImportError: pass
1758 else: warnings.filterwarnings('ignore') # ignore problems during import
Ka-Ping Yee66246962001-04-12 11:59:50 +00001759 ModuleScanner().run(callback, key)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001760
1761# --------------------------------------------------- web browser interface
1762
Ka-Ping Yee66246962001-04-12 11:59:50 +00001763def serve(port, callback=None, completer=None):
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001764 import BaseHTTPServer, mimetools, select
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001765
1766 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1767 class Message(mimetools.Message):
1768 def __init__(self, fp, seekable=1):
1769 Message = self.__class__
1770 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1771 self.encodingheader = self.getheader('content-transfer-encoding')
1772 self.typeheader = self.getheader('content-type')
1773 self.parsetype()
1774 self.parseplist()
1775
1776 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1777 def send_document(self, title, contents):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001778 try:
1779 self.send_response(200)
1780 self.send_header('Content-Type', 'text/html')
1781 self.end_headers()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001782 self.wfile.write(html.page(title, contents))
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001783 except IOError: pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001784
1785 def do_GET(self):
1786 path = self.path
1787 if path[-5:] == '.html': path = path[:-5]
1788 if path[:1] == '/': path = path[1:]
1789 if path and path != '.':
1790 try:
Ka-Ping Yeedec96e92001-04-13 09:55:49 +00001791 obj = locate(path, forceload=1)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001792 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001793 self.send_document(path, html.escape(str(value)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001794 return
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001795 if obj:
1796 self.send_document(describe(obj), html.document(obj, path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001797 else:
1798 self.send_document(path,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001799'no Python documentation found for %s' % repr(path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001800 else:
1801 heading = html.heading(
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001802'<big><big><strong>Python: Index of Modules</strong></big></big>',
1803'#ffffff', '#7799ee')
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001804 def bltinlink(name):
1805 return '<a href="%s.html">%s</a>' % (name, name)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00001806 names = filter(lambda x: x != '__main__',
1807 sys.builtin_module_names)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001808 contents = html.multicolumn(names, bltinlink)
1809 indices = ['<p>' + html.bigsection(
1810 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1811
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001812 seen = {}
1813 for dir in pathdirs():
1814 indices.append(html.index(dir, seen))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001815 contents = heading + join(indices) + '''<p align=right>
Tim Peters2306d242001-09-25 03:18:32 +00001816<font color="#909090" face="helvetica, arial"><strong>
1817pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font>'''
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001818 self.send_document('Index of Modules', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001819
1820 def log_message(self, *args): pass
1821
Ka-Ping Yeefd540692001-04-12 12:54:36 +00001822 class DocServer(BaseHTTPServer.HTTPServer):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001823 def __init__(self, port, callback):
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001824 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001825 self.address = ('', port)
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001826 self.url = 'http://%s:%d/' % (host, port)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001827 self.callback = callback
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001828 self.base.__init__(self, self.address, self.handler)
1829
1830 def serve_until_quit(self):
1831 import select
Guido van Rossum8ca162f2002-04-07 06:36:23 +00001832 self.quit = False
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001833 while not self.quit:
1834 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1835 if rd: self.handle_request()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001836
1837 def server_activate(self):
1838 self.base.server_activate(self)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001839 if self.callback: self.callback(self)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001840
1841 DocServer.base = BaseHTTPServer.HTTPServer
1842 DocServer.handler = DocHandler
1843 DocHandler.MessageClass = Message
1844 try:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001845 try:
1846 DocServer(port, callback).serve_until_quit()
1847 except (KeyboardInterrupt, select.error):
1848 pass
1849 finally:
Ka-Ping Yee66246962001-04-12 11:59:50 +00001850 if completer: completer()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001851
1852# ----------------------------------------------------- graphical interface
1853
1854def gui():
1855 """Graphical interface (starts web server and pops up a control window)."""
1856 class GUI:
1857 def __init__(self, window, port=7464):
1858 self.window = window
1859 self.server = None
1860 self.scanner = None
1861
1862 import Tkinter
1863 self.server_frm = Tkinter.Frame(window)
1864 self.title_lbl = Tkinter.Label(self.server_frm,
1865 text='Starting server...\n ')
1866 self.open_btn = Tkinter.Button(self.server_frm,
1867 text='open browser', command=self.open, state='disabled')
1868 self.quit_btn = Tkinter.Button(self.server_frm,
1869 text='quit serving', command=self.quit, state='disabled')
1870
1871 self.search_frm = Tkinter.Frame(window)
1872 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
1873 self.search_ent = Tkinter.Entry(self.search_frm)
1874 self.search_ent.bind('<Return>', self.search)
1875 self.stop_btn = Tkinter.Button(self.search_frm,
1876 text='stop', pady=0, command=self.stop, state='disabled')
1877 if sys.platform == 'win32':
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001878 # Trying to hide and show this button crashes under Windows.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001879 self.stop_btn.pack(side='right')
1880
1881 self.window.title('pydoc')
1882 self.window.protocol('WM_DELETE_WINDOW', self.quit)
1883 self.title_lbl.pack(side='top', fill='x')
1884 self.open_btn.pack(side='left', fill='x', expand=1)
1885 self.quit_btn.pack(side='right', fill='x', expand=1)
1886 self.server_frm.pack(side='top', fill='x')
1887
1888 self.search_lbl.pack(side='left')
1889 self.search_ent.pack(side='right', fill='x', expand=1)
1890 self.search_frm.pack(side='top', fill='x')
1891 self.search_ent.focus_set()
1892
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001893 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001894 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001895 self.result_lst.bind('<Button-1>', self.select)
1896 self.result_lst.bind('<Double-Button-1>', self.goto)
1897 self.result_scr = Tkinter.Scrollbar(window,
1898 orient='vertical', command=self.result_lst.yview)
1899 self.result_lst.config(yscrollcommand=self.result_scr.set)
1900
1901 self.result_frm = Tkinter.Frame(window)
1902 self.goto_btn = Tkinter.Button(self.result_frm,
1903 text='go to selected', command=self.goto)
1904 self.hide_btn = Tkinter.Button(self.result_frm,
1905 text='hide results', command=self.hide)
1906 self.goto_btn.pack(side='left', fill='x', expand=1)
1907 self.hide_btn.pack(side='right', fill='x', expand=1)
1908
1909 self.window.update()
1910 self.minwidth = self.window.winfo_width()
1911 self.minheight = self.window.winfo_height()
1912 self.bigminheight = (self.server_frm.winfo_reqheight() +
1913 self.search_frm.winfo_reqheight() +
1914 self.result_lst.winfo_reqheight() +
1915 self.result_frm.winfo_reqheight())
1916 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
1917 self.expanded = 0
1918 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1919 self.window.wm_minsize(self.minwidth, self.minheight)
1920
1921 import threading
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00001922 threading.Thread(
1923 target=serve, args=(port, self.ready, self.quit)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001924
1925 def ready(self, server):
1926 self.server = server
1927 self.title_lbl.config(
1928 text='Python documentation server at\n' + server.url)
1929 self.open_btn.config(state='normal')
1930 self.quit_btn.config(state='normal')
1931
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001932 def open(self, event=None, url=None):
1933 url = url or self.server.url
1934 try:
1935 import webbrowser
1936 webbrowser.open(url)
1937 except ImportError: # pre-webbrowser.py compatibility
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001938 if sys.platform == 'win32':
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001939 os.system('start "%s"' % url)
1940 elif sys.platform == 'mac':
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001941 try: import ic
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001942 except ImportError: pass
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00001943 else: ic.launchurl(url)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001944 else:
1945 rc = os.system('netscape -remote "openURL(%s)" &' % url)
1946 if rc: os.system('netscape "%s" &' % url)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001947
1948 def quit(self, event=None):
1949 if self.server:
1950 self.server.quit = 1
1951 self.window.quit()
1952
1953 def search(self, event=None):
1954 key = self.search_ent.get()
1955 self.stop_btn.pack(side='right')
1956 self.stop_btn.config(state='normal')
1957 self.search_lbl.config(text='Searching for "%s"...' % key)
1958 self.search_ent.forget()
1959 self.search_lbl.pack(side='left')
1960 self.result_lst.delete(0, 'end')
1961 self.goto_btn.config(state='disabled')
1962 self.expand()
1963
1964 import threading
1965 if self.scanner:
1966 self.scanner.quit = 1
1967 self.scanner = ModuleScanner()
1968 threading.Thread(target=self.scanner.run,
Ka-Ping Yee6dcfa382001-04-12 20:27:31 +00001969 args=(self.update, key, self.done)).start()
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001970
1971 def update(self, path, modname, desc):
1972 if modname[-9:] == '.__init__':
1973 modname = modname[:-9] + ' (package)'
1974 self.result_lst.insert('end',
1975 modname + ' - ' + (desc or '(no description)'))
1976
1977 def stop(self, event=None):
1978 if self.scanner:
1979 self.scanner.quit = 1
1980 self.scanner = None
1981
1982 def done(self):
1983 self.scanner = None
1984 self.search_lbl.config(text='Search for')
1985 self.search_lbl.pack(side='left')
1986 self.search_ent.pack(side='right', fill='x', expand=1)
1987 if sys.platform != 'win32': self.stop_btn.forget()
1988 self.stop_btn.config(state='disabled')
1989
1990 def select(self, event=None):
1991 self.goto_btn.config(state='normal')
1992
1993 def goto(self, event=None):
1994 selection = self.result_lst.curselection()
1995 if selection:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001996 modname = split(self.result_lst.get(selection[0]))[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001997 self.open(url=self.server.url + modname + '.html')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001998
1999 def collapse(self):
2000 if not self.expanded: return
2001 self.result_frm.forget()
2002 self.result_scr.forget()
2003 self.result_lst.forget()
2004 self.bigwidth = self.window.winfo_width()
2005 self.bigheight = self.window.winfo_height()
2006 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
2007 self.window.wm_minsize(self.minwidth, self.minheight)
2008 self.expanded = 0
2009
2010 def expand(self):
2011 if self.expanded: return
2012 self.result_frm.pack(side='bottom', fill='x')
2013 self.result_scr.pack(side='right', fill='y')
2014 self.result_lst.pack(side='top', fill='both', expand=1)
2015 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
2016 self.window.wm_minsize(self.minwidth, self.bigminheight)
2017 self.expanded = 1
2018
2019 def hide(self, event=None):
2020 self.stop()
2021 self.collapse()
2022
2023 import Tkinter
2024 try:
2025 gui = GUI(Tkinter.Tk())
2026 Tkinter.mainloop()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002027 except KeyboardInterrupt:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002028 pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002029
2030# -------------------------------------------------- command-line interface
2031
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002032def ispath(x):
Ka-Ping Yee45daeb02002-08-11 15:11:33 +00002033 return isinstance(x, str) and find(x, os.sep) >= 0
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002034
Ka-Ping Yee1d384632001-03-01 00:24:32 +00002035def cli():
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002036 """Command-line interface (looks at sys.argv to decide what to do)."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002037 import getopt
2038 class BadUsage: pass
2039
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002040 # Scripts don't get the current directory in their path by default.
Ka-Ping Yeef78a81b2001-03-27 08:13:42 +00002041 scriptdir = os.path.dirname(sys.argv[0])
2042 if scriptdir in sys.path:
2043 sys.path.remove(scriptdir)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002044 sys.path.insert(0, '.')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002045
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002046 try:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002047 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002048 writing = 0
2049
2050 for opt, val in opts:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002051 if opt == '-g':
2052 gui()
2053 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002054 if opt == '-k':
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002055 apropos(val)
2056 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002057 if opt == '-p':
2058 try:
2059 port = int(val)
2060 except ValueError:
2061 raise BadUsage
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002062 def ready(server):
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00002063 print 'pydoc server ready at %s' % server.url
2064 def stopped():
2065 print 'pydoc server stopped'
2066 serve(port, ready, stopped)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002067 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002068 if opt == '-w':
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002069 writing = 1
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002070
2071 if not args: raise BadUsage
2072 for arg in args:
Ka-Ping Yee45daeb02002-08-11 15:11:33 +00002073 if ispath(arg) and not os.path.exists(arg):
2074 print 'file %r does not exist' % arg
2075 break
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002076 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002077 if ispath(arg) and os.path.isfile(arg):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002078 arg = importfile(arg)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002079 if writing:
2080 if ispath(arg) and os.path.isdir(arg):
2081 writedocs(arg)
2082 else:
2083 writedoc(arg)
2084 else:
Ka-Ping Yee9aa0d902001-04-12 10:50:23 +00002085 doc(arg)
Ka-Ping Yee3bda8792001-03-23 13:17:50 +00002086 except ErrorDuringImport, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00002087 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002088
2089 except (getopt.error, BadUsage):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002090 cmd = sys.argv[0]
2091 print """pydoc - the Python documentation tool
2092
2093%s <name> ...
2094 Show text documentation on something. <name> may be the name of a
2095 function, module, or package, or a dotted reference to a class or
2096 function within a module or module in a package. If <name> contains
2097 a '%s', it is used as the path to a Python source file to document.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002098
2099%s -k <keyword>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002100 Search for a keyword in the synopsis lines of all available modules.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002101
2102%s -p <port>
2103 Start an HTTP server on the given port on the local machine.
2104
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002105%s -g
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00002106 Pop up a graphical interface for finding and serving documentation.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00002107
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002108%s -w <name> ...
2109 Write out the HTML documentation for a module to a file in the current
Ka-Ping Yee5a804ed2001-04-10 11:46:02 +00002110 directory. If <name> contains a '%s', it is treated as a filename; if
2111 it names a directory, documentation is written for all the contents.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002112""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
Ka-Ping Yee1d384632001-03-01 00:24:32 +00002113
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00002114if __name__ == '__main__': cli()