blob: baa19855d6555ab3bab415864553d99a29c063ea [file] [log] [blame]
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001#!/usr/bin/env python
Ka-Ping Yee1d384632001-03-01 00:24:32 +00002"""Generate Python documentation in HTML or text for interactive use.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00003
Ka-Ping Yeedd175342001-02-27 14:43:46 +00004In the Python interpreter, do "from pydoc import help" to provide online
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00005help. Calling help(thing) on a Python object documents the object.
6
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00007Or, at the shell command line outside of Python:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00008
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00009Run "pydoc <name>" to show documentation on something. <name> may be
10the name of a function, module, package, or a dotted reference to a
11class or function within a module or module in a package. If the
12argument contains a path segment delimiter (e.g. slash on Unix,
13backslash on Windows) it is treated as the path to a Python source file.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000014
Ka-Ping Yee37f7b382001-03-23 00:12:53 +000015Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines
16of all available modules.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000017
Ka-Ping Yee37f7b382001-03-23 00:12:53 +000018Run "pydoc -p <port>" to start an HTTP server on a given port on the
19local machine to generate documentation web pages.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000020
Ka-Ping Yee37f7b382001-03-23 00:12:53 +000021For platforms without a command line, "pydoc -g" starts the HTTP server
22and also pops up a little window for controlling it.
23
24Run "pydoc -w <name>" to write out the HTML documentation for a module
25to a file named "<name>.html".
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000026"""
Ka-Ping Yeedd175342001-02-27 14:43:46 +000027
28__author__ = "Ka-Ping Yee <ping@lfw.org>"
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +000029__date__ = "26 February 2001"
Ka-Ping Yee09d7d9a2001-02-27 22:43:48 +000030__version__ = "$Revision$"
Ka-Ping Yee5e2b1732001-02-27 23:35:09 +000031__credits__ = """Guido van Rossum, for an excellent programming language.
32Tommy Burnette, the original creator of manpy.
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +000033Paul Prescod, for all his work on onlinehelp.
34Richard Chamberlain, for the first implementation of textdoc.
35
Ka-Ping Yee5e2b1732001-02-27 23:35:09 +000036Mynd you, møøse bites Kan be pretty nasti..."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +000037
Ka-Ping Yee66efbc72001-03-01 13:55:20 +000038# Note: this module is designed to deploy instantly and run under any
39# version of Python from 1.5 and up. That's why it's a single file and
40# some 2.0 features (like string methods) are conspicuously avoided.
41
Ka-Ping Yeedd175342001-02-27 14:43:46 +000042import sys, imp, os, stat, re, types, inspect
43from repr import Repr
44from string import expandtabs, find, join, lower, split, strip, rstrip
45
46# --------------------------------------------------------- common routines
47
48def synopsis(filename, cache={}):
49 """Get the one-line summary out of a module file."""
50 mtime = os.stat(filename)[stat.ST_MTIME]
51 lastupdate, result = cache.get(filename, (0, None))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +000052 # XXX what if ext is 'rb' type in imp_getsuffixes?
Ka-Ping Yeedd175342001-02-27 14:43:46 +000053 if lastupdate < mtime:
54 file = open(filename)
55 line = file.readline()
56 while line[:1] == '#' or strip(line) == '':
57 line = file.readline()
58 if not line: break
59 if line[-2:] == '\\\n':
60 line = line[:-2] + file.readline()
61 line = strip(line)
62 if line[:3] == '"""':
63 line = line[3:]
64 while strip(line) == '':
65 line = file.readline()
66 if not line: break
Ka-Ping Yee239432a2001-03-02 02:45:08 +000067 result = strip(split(line, '"""')[0])
Ka-Ping Yeedd175342001-02-27 14:43:46 +000068 else: result = None
69 file.close()
70 cache[filename] = (mtime, result)
71 return result
72
Ka-Ping Yeedd175342001-02-27 14:43:46 +000073def pathdirs():
74 """Convert sys.path into a list of absolute, existing, unique paths."""
75 dirs = []
Ka-Ping Yee1d384632001-03-01 00:24:32 +000076 normdirs = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +000077 for dir in sys.path:
78 dir = os.path.abspath(dir or '.')
Ka-Ping Yee1d384632001-03-01 00:24:32 +000079 normdir = os.path.normcase(dir)
80 if normdir not in normdirs and os.path.isdir(dir):
Ka-Ping Yeedd175342001-02-27 14:43:46 +000081 dirs.append(dir)
Ka-Ping Yee1d384632001-03-01 00:24:32 +000082 normdirs.append(normdir)
Ka-Ping Yeedd175342001-02-27 14:43:46 +000083 return dirs
84
85def getdoc(object):
86 """Get the doc string or comments for an object."""
87 result = inspect.getdoc(object)
88 if not result:
89 try: result = inspect.getcomments(object)
90 except: pass
Ka-Ping Yee239432a2001-03-02 02:45:08 +000091 return result and re.sub('^ *\n', '', rstrip(result)) or ''
Ka-Ping Yeedd175342001-02-27 14:43:46 +000092
93def classname(object, modname):
94 """Get a class name and qualify it with a module name if necessary."""
95 name = object.__name__
96 if object.__module__ != modname:
97 name = object.__module__ + '.' + name
98 return name
99
100def isconstant(object):
101 """Check if an object is of a type that probably means it's a constant."""
102 return type(object) in [
103 types.FloatType, types.IntType, types.ListType, types.LongType,
104 types.StringType, types.TupleType, types.TypeType,
105 hasattr(types, 'UnicodeType') and types.UnicodeType or 0]
106
107def replace(text, *pairs):
108 """Do a series of global replacements on a string."""
109 for old, new in pairs:
110 text = join(split(text, old), new)
111 return text
112
113def cram(text, maxlen):
114 """Omit part of a string if needed to make it fit in a maximum length."""
115 if len(text) > maxlen:
116 pre = max(0, (maxlen-3)/2)
117 post = max(0, maxlen-3-pre)
118 return text[:pre] + '...' + text[len(text)-post:]
119 return text
120
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000121def stripid(text):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000122 """Remove the hexadecimal id from a Python object representation."""
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000123 # The behaviour of %p is implementation-dependent, so we need an example.
124 for pattern in [' at 0x[0-9a-f]{6,}>$', ' at [0-9A-F]{8,}>$']:
125 if re.search(pattern, repr(Exception)):
126 return re.sub(pattern, '>', text)
127 return text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000128
129def modulename(path):
130 """Return the Python module name for a given path, or None."""
131 filename = os.path.basename(path)
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000132 suffixes = map(lambda (suffix, mode, kind): (len(suffix), suffix),
133 imp.get_suffixes())
134 suffixes.sort()
135 suffixes.reverse() # try longest suffixes first, in case they overlap
136 for length, suffix in suffixes:
137 if len(filename) > length and filename[-length:] == suffix:
138 return filename[:-length]
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000139
140class DocImportError(Exception):
141 """Class for errors while trying to import something to document it."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000142 def __init__(self, filename, (type, value, tb)):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000143 self.filename = filename
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000144 self.type = type
145 self.value = value
146 self.tb = tb
147
148 def __str__(self):
149 t = self.type
150 if type(t) is types.ClassType:
151 t = t.__name__
152 return 'problem in %s - %s: %s' % (self.filename, t, self.value)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000153
154def importfile(path):
155 """Import a Python source file or compiled file given its path."""
156 magic = imp.get_magic()
157 file = open(path, 'r')
158 if file.read(len(magic)) == magic:
159 kind = imp.PY_COMPILED
160 else:
161 kind = imp.PY_SOURCE
162 file.close()
163 filename = os.path.basename(path)
164 name, ext = os.path.splitext(filename)
165 file = open(path, 'r')
166 try:
167 module = imp.load_module(name, file, path, (ext, 'r', kind))
168 except:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000169 raise DocImportError(path, sys.exc_info())
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000170 file.close()
171 return module
172
173def ispackage(path):
174 """Guess whether a path refers to a package directory."""
175 if os.path.isdir(path):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000176 for ext in ['.py', '.pyc', '.pyo']:
177 if os.path.isfile(os.path.join(path, '__init__' + ext)):
178 return 1
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000179
180# ---------------------------------------------------- formatter base class
181
182class Doc:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000183 def document(self, object, name=None, *args):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000184 """Generate documentation for an object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000185 args = (object, name) + args
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000186 if inspect.ismodule(object): return apply(self.docmodule, args)
187 if inspect.isclass(object): return apply(self.docclass, args)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000188 if inspect.isroutine(object): return apply(self.docroutine, args)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000189 return apply(self.docother, args)
190
191 def fail(self, object, name=None, *args):
192 """Raise an exception for unimplemented types."""
193 message = "don't know how to document object%s of type %s" % (
194 name and ' ' + repr(name), type(object).__name__)
195 raise TypeError, message
196
197 docmodule = docclass = docroutine = docother = fail
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000198
199# -------------------------------------------- HTML documentation generator
200
201class HTMLRepr(Repr):
202 """Class for safely making an HTML representation of a Python object."""
203 def __init__(self):
204 Repr.__init__(self)
205 self.maxlist = self.maxtuple = self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000206 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000207
208 def escape(self, text):
209 return replace(text, ('&', '&amp;'), ('<', '&lt;'), ('>', '&gt;'))
210
211 def repr(self, object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000212 return Repr.repr(self, object)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000213
214 def repr1(self, x, level):
215 methodname = 'repr_' + join(split(type(x).__name__), '_')
216 if hasattr(self, methodname):
217 return getattr(self, methodname)(x, level)
218 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000219 return self.escape(cram(stripid(repr(x)), self.maxother))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000220
221 def repr_string(self, x, level):
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000222 test = cram(x, self.maxstring)
223 testrepr = repr(test)
224 if '\\' in test and '\\' not in replace(testrepr, (r'\\', '')):
225 # Backslashes are only literal in the string and are never
226 # needed to make any special characters, so show a raw string.
227 return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
228 return re.sub(r'((\\[\\abfnrtv\'"]|\\x..|\\u....)+)',
229 r'<font color="#c040c0">\1</font>',
230 self.escape(testrepr))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000231
232 def repr_instance(self, x, level):
233 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000234 return self.escape(cram(stripid(repr(x)), self.maxstring))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000235 except:
236 return self.escape('<%s instance>' % x.__class__.__name__)
237
238 repr_unicode = repr_string
239
240class HTMLDoc(Doc):
241 """Formatter class for HTML documentation."""
242
243 # ------------------------------------------- HTML formatting utilities
244
245 _repr_instance = HTMLRepr()
246 repr = _repr_instance.repr
247 escape = _repr_instance.escape
248
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000249 def page(self, title, contents):
250 """Format an HTML page."""
251 return '''
252<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
253<html><title>Python: %s</title><body bgcolor="#f0f0f8">
254%s
255</body></html>''' % (title, contents)
256
257 def heading(self, title, fgcol, bgcol, extras=''):
258 """Format a page heading."""
259 return '''
260<table width="100%%" cellspacing=0 cellpadding=2 border=0>
261<tr bgcolor="%s">
262<td valign=bottom><small>&nbsp;<br></small
263><font color="%s" face="helvetica">&nbsp;<br>%s</font></td
264><td align=right valign=bottom
265><font color="%s" face="helvetica">%s</font></td></tr></table>
266 ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
267
268 def section(self, title, fgcol, bgcol, contents, width=20,
269 prelude='', marginalia=None, gap='&nbsp;&nbsp;'):
270 """Format a section with a heading."""
271 if marginalia is None:
272 marginalia = '&nbsp;' * width
273 result = '''
274<p><table width="100%%" cellspacing=0 cellpadding=2 border=0>
275<tr bgcolor="%s">
276<td colspan=3 valign=bottom><small><small>&nbsp;<br></small></small
277><font color="%s" face="helvetica, arial">%s</font></td></tr>
278 ''' % (bgcol, fgcol, title)
279 if prelude:
280 result = result + '''
281<tr bgcolor="%s"><td>%s</td>
282<td colspan=2>%s</td></tr>
283 ''' % (bgcol, marginalia, prelude)
284 result = result + '''
285<tr><td bgcolor="%s">%s</td><td>%s</td>
286 ''' % (bgcol, marginalia, gap)
287
288 return result + '<td width="100%%">%s</td></tr></table>' % contents
289
290 def bigsection(self, title, *args):
291 """Format a section with a big heading."""
292 title = '<big><strong>%s</strong></big>' % title
293 return apply(self.section, (title,) + args)
294
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000295 def preformat(self, text):
296 """Format literal preformatted text."""
297 text = self.escape(expandtabs(text))
298 return replace(text, ('\n\n', '\n \n'), ('\n\n', '\n \n'),
299 (' ', '&nbsp;'), ('\n', '<br>\n'))
300
301 def multicolumn(self, list, format, cols=4):
302 """Format a list of items into a multi-column list."""
303 result = ''
304 rows = (len(list)+cols-1)/cols
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000305 for col in range(cols):
306 result = result + '<td width="%d%%" valign=top>' % (100/cols)
307 for i in range(rows*col, rows*col+rows):
308 if i < len(list):
309 result = result + format(list[i]) + '<br>'
310 result = result + '</td>'
311 return '<table width="100%%"><tr>%s</tr></table>' % result
312
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000313 def small(self, text): return '<small>%s</small>' % text
314 def grey(self, text): return '<font color="#909090">%s</font>' % text
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000315
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000316 def namelink(self, name, *dicts):
317 """Make a link for an identifier, given name-to-URL mappings."""
318 for dict in dicts:
319 if dict.has_key(name):
320 return '<a href="%s">%s</a>' % (dict[name], name)
321 return name
322
323 def classlink(self, object, modname, *dicts):
324 """Make a link for a class."""
325 name = object.__name__
326 if object.__module__ != modname:
327 name = object.__module__ + '.' + name
328 for dict in dicts:
329 if dict.has_key(object):
330 return '<a href="%s">%s</a>' % (dict[object], name)
331 return name
332
333 def modulelink(self, object):
334 """Make a link for a module."""
335 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
336
337 def modpkglink(self, (name, path, ispackage, shadowed)):
338 """Make a link for a module or package to display in an index."""
339 if shadowed:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000340 return self.grey(name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000341 if path:
342 url = '%s.%s.html' % (path, name)
343 else:
344 url = '%s.html' % name
345 if ispackage:
346 text = '<strong>%s</strong>&nbsp;(package)' % name
347 else:
348 text = name
349 return '<a href="%s">%s</a>' % (url, text)
350
351 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
352 """Mark up some plain text, given a context of symbols to look for.
353 Each context dictionary maps object names to anchor names."""
354 escape = escape or self.escape
355 results = []
356 here = 0
357 pattern = re.compile(r'\b(((http|ftp)://\S+[\w/])|'
358 r'(RFC[- ]?(\d+))|'
359 r'(self\.)?(\w+))\b')
360 while 1:
361 match = pattern.search(text, here)
362 if not match: break
363 start, end = match.span()
364 results.append(escape(text[here:start]))
365
366 all, url, scheme, rfc, rfcnum, selfdot, name = match.groups()
367 if url:
368 results.append('<a href="%s">%s</a>' % (url, escape(url)))
369 elif rfc:
370 url = 'http://www.rfc-editor.org/rfc/rfc%s.txt' % rfcnum
371 results.append('<a href="%s">%s</a>' % (url, escape(rfc)))
372 else:
373 if text[end:end+1] == '(':
374 results.append(self.namelink(name, methods, funcs, classes))
375 elif selfdot:
376 results.append('self.<strong>%s</strong>' % name)
377 else:
378 results.append(self.namelink(name, classes))
379 here = end
380 results.append(escape(text[here:]))
381 return join(results, '')
382
383 # ---------------------------------------------- type-specific routines
384
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000385 def formattree(self, tree, modname, classes={}, parent=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000386 """Produce HTML for a class tree as given by inspect.getclasstree()."""
387 result = ''
388 for entry in tree:
389 if type(entry) is type(()):
390 c, bases = entry
391 result = result + '<dt><font face="helvetica, arial"><small>'
392 result = result + self.classlink(c, modname, classes)
393 if bases and bases != (parent,):
394 parents = []
395 for base in bases:
396 parents.append(self.classlink(base, modname, classes))
397 result = result + '(' + join(parents, ', ') + ')'
398 result = result + '\n</small></font></dt>'
399 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000400 result = result + '<dd>\n%s</dd>\n' % self.formattree(
401 entry, modname, classes, c)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000402 return '<dl>\n%s</dl>\n' % result
403
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000404 def docmodule(self, object, name=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000405 """Produce HTML documentation for a module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000406 name = object.__name__ # ignore the passed-in name
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000407 parts = split(name, '.')
408 links = []
409 for i in range(len(parts)-1):
410 links.append(
411 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
412 (join(parts[:i+1], '.'), parts[i]))
413 linkedname = join(links + parts[-1:], '.')
414 head = '<big><big><strong>%s</strong></big></big>' % linkedname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000415 try:
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000416 path = inspect.getabsfile(object)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000417 filelink = '<a href="file:%s">%s</a>' % (path, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000418 except TypeError:
419 filelink = '(built-in)'
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000420 info = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000421 if hasattr(object, '__version__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000422 version = str(object.__version__)
Ka-Ping Yee40c49912001-02-27 22:46:01 +0000423 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
424 version = strip(version[11:-1])
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000425 info.append('version %s' % self.escape(version))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000426 if hasattr(object, '__date__'):
427 info.append(self.escape(str(object.__date__)))
428 if info:
429 head = head + ' (%s)' % join(info, ', ')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000430 result = self.heading(
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000431 head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)
432
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000433 modules = inspect.getmembers(object, inspect.ismodule)
434
435 if 0 and hasattr(object, '__all__'):
436 visible = lambda key, all=object.__all__: key in all
437 else:
438 visible = lambda key: key[:1] != '_'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000439
440 classes, cdict = [], {}
441 for key, value in inspect.getmembers(object, inspect.isclass):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000442 if visible(key) and (
443 inspect.getmodule(value) or object) is object:
444 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000445 cdict[key] = cdict[value] = '#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000446 for key, value in classes:
447 for base in value.__bases__:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000448 key, modname = base.__name__, base.__module__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000449 module = sys.modules.get(modname)
450 if modname != name and module and hasattr(module, key):
451 if getattr(module, key) is base:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000452 if not cdict.has_key(key):
453 cdict[key] = cdict[base] = modname + '.html#' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000454 funcs, fdict = [], {}
455 for key, value in inspect.getmembers(object, inspect.isroutine):
456 if visible(key) and (inspect.isbuiltin(value) or
457 inspect.getmodule(value) is object):
458 funcs.append((key, value))
459 fdict[key] = '#-' + key
460 if inspect.isfunction(value): fdict[value] = fdict[key]
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000461 constants = []
462 for key, value in inspect.getmembers(object, isconstant):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000463 if visible(key):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000464 constants.append((key, value))
465
466 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
467 doc = doc and '<tt>%s</tt>' % doc
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000468 result = result + '<p>%s\n' % self.small(doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000469
470 if hasattr(object, '__path__'):
471 modpkgs = []
472 modnames = []
473 for file in os.listdir(object.__path__[0]):
474 if file[:1] != '_':
475 path = os.path.join(object.__path__[0], file)
476 modname = modulename(file)
477 if modname and modname not in modnames:
478 modpkgs.append((modname, name, 0, 0))
479 modnames.append(modname)
480 elif ispackage(path):
Tim Peters85ba6732001-02-28 08:26:44 +0000481 modpkgs.append((file, name, 1, 0))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000482 modpkgs.sort()
483 contents = self.multicolumn(modpkgs, self.modpkglink)
484 result = result + self.bigsection(
485 'Package Contents', '#ffffff', '#aa55cc', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000486 elif modules:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000487 contents = self.multicolumn(
488 modules, lambda (key, value), s=self: s.modulelink(value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000489 result = result + self.bigsection(
490 'Modules', '#fffff', '#aa55cc', contents)
491
492 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000493 classlist = map(lambda (key, value): value, classes)
494 contents = [self.formattree(
495 inspect.getclasstree(classlist, 1), name, cdict)]
496 for key, value in classes:
497 contents.append(self.document(value, key, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000498 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000499 'Classes', '#ffffff', '#ee77aa', join(contents))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000500 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000501 contents = []
502 for key, value in funcs:
503 contents.append(self.document(value, key, fdict, cdict))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000504 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000505 'Functions', '#ffffff', '#eeaa77', join(contents))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000506 if constants:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000507 contents = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000508 for key, value in constants:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000509 contents.append(self.document(value, key))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000510 result = result + self.bigsection(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000511 'Constants', '#ffffff', '#55aa55', join(contents, '<br>'))
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000512 if hasattr(object, '__author__'):
513 contents = self.markup(str(object.__author__), self.preformat)
514 result = result + self.bigsection(
515 'Author', '#ffffff', '#7799ee', contents)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000516 if hasattr(object, '__credits__'):
517 contents = self.markup(str(object.__credits__), self.preformat)
518 result = result + self.bigsection(
519 'Credits', '#ffffff', '#7799ee', contents)
520
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000521 return result
522
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000523 def docclass(self, object, name=None, funcs={}, classes={}):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000524 """Produce HTML documentation for a class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000525 realname = object.__name__
526 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000527 bases = object.__bases__
528 contents = ''
529
530 methods, mdict = [], {}
531 for key, value in inspect.getmembers(object, inspect.ismethod):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000532 methods.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000533 mdict[key] = mdict[value] = '#' + name + '-' + key
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000534 for key, value in methods:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000535 contents = contents + self.document(
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000536 value, key, funcs, classes, mdict, name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000537
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000538 if name == realname:
539 title = '<a name="%s">class <strong>%s</strong></a>' % (
540 name, realname)
541 else:
542 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
543 name, name, realname)
544
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000545 if bases:
546 parents = []
547 for base in bases:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000548 parents.append(
549 self.classlink(base, object.__module__, classes))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000550 title = title + '(%s)' % join(parents, ', ')
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000551 doc = self.markup(
552 getdoc(object), self.preformat, funcs, classes, mdict)
553 if doc: doc = self.small('<tt>%s</tt>' % doc)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000554 return self.section(title, '#000000', '#ffc8d8', contents, 10, doc)
555
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000556 def formatvalue(self, object):
557 """Format an argument default value as text."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000558 return self.small(self.grey('=' + self.repr(object)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000559
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000560 def docroutine(self, object, name=None,
561 funcs={}, classes={}, methods={}, clname=''):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000562 """Produce HTML documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000563 realname = object.__name__
564 name = name or realname
565 anchor = clname + '-' + realname
566 note = ''
567 if inspect.ismethod(object):
568 if not clname:
569 note = self.small(self.grey(
570 object.im_self and
571 'method of ' + self.repr(object.im_self) or
572 ' unbound %s method' % object.im_class.__name__))
573 object = object.im_func
574
575 if name == realname:
576 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
577 else:
578 title = '<strong>%s</strong> = <a name="%s">%s</a>' % (
579 name, anchor, realname)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000580 if inspect.isbuiltin(object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000581 argspec = '(...)'
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000582 else:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000583 args, varargs, varkw, defaults = inspect.getargspec(object)
584 argspec = inspect.formatargspec(
585 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000586 if realname == '<lambda>':
587 decl = '<em>lambda</em>'
588 argspec = argspec[1:-1] # remove parentheses
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000589
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000590 decl = title + argspec + note
591
592 doc = self.markup(
593 getdoc(object), self.preformat, funcs, classes, methods)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000594 doc = replace(doc, ('<br>\n', '</tt></small\n><dd><small><tt>'))
595 doc = doc and '<tt>%s</tt>' % doc
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000596 return '<dl><dt>%s<dd>%s</dl>' % (decl, self.small(doc))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000597
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000598 def docother(self, object, name=None):
599 """Produce HTML documentation for a data object."""
600 return '<strong>%s</strong> = %s' % (name, self.repr(object))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000601
602 def index(self, dir, shadowed=None):
603 """Generate an HTML index for a directory of modules."""
604 modpkgs = []
605 if shadowed is None: shadowed = {}
606 seen = {}
607 files = os.listdir(dir)
608
609 def found(name, ispackage,
610 modpkgs=modpkgs, shadowed=shadowed, seen=seen):
611 if not seen.has_key(name):
612 modpkgs.append((name, '', ispackage, shadowed.has_key(name)))
613 seen[name] = 1
614 shadowed[name] = 1
615
616 # Package spam/__init__.py takes precedence over module spam.py.
617 for file in files:
618 path = os.path.join(dir, file)
619 if ispackage(path): found(file, 1)
620 for file in files:
621 path = os.path.join(dir, file)
Tim Peters85ba6732001-02-28 08:26:44 +0000622 if file[:1] != '_' and os.path.isfile(path):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000623 modname = modulename(file)
624 if modname: found(modname, 0)
625
626 modpkgs.sort()
627 contents = self.multicolumn(modpkgs, self.modpkglink)
628 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
629
630# -------------------------------------------- text documentation generator
631
632class TextRepr(Repr):
633 """Class for safely making a text representation of a Python object."""
634 def __init__(self):
635 Repr.__init__(self)
636 self.maxlist = self.maxtuple = self.maxdict = 10
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000637 self.maxstring = self.maxother = 100
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000638
639 def repr1(self, x, level):
640 methodname = 'repr_' + join(split(type(x).__name__), '_')
641 if hasattr(self, methodname):
642 return getattr(self, methodname)(x, level)
643 else:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000644 return cram(stripid(repr(x)), self.maxother)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000645
Ka-Ping Yeea2fe1032001-03-02 01:19:14 +0000646 def repr_string(self, x, level):
647 test = cram(x, self.maxstring)
648 testrepr = repr(test)
649 if '\\' in test and '\\' not in replace(testrepr, (r'\\', '')):
650 # Backslashes are only literal in the string and are never
651 # needed to make any special characters, so show a raw string.
652 return 'r' + testrepr[0] + test + testrepr[0]
653 return testrepr
654
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000655 def repr_instance(self, x, level):
656 try:
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000657 return cram(stripid(repr(x)), self.maxstring)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000658 except:
659 return '<%s instance>' % x.__class__.__name__
660
661class TextDoc(Doc):
662 """Formatter class for text documentation."""
663
664 # ------------------------------------------- text formatting utilities
665
666 _repr_instance = TextRepr()
667 repr = _repr_instance.repr
668
669 def bold(self, text):
670 """Format a string in bold by overstriking."""
671 return join(map(lambda ch: ch + '\b' + ch, text), '')
672
673 def indent(self, text, prefix=' '):
674 """Indent text by prepending a given prefix to each line."""
675 if not text: return ''
676 lines = split(text, '\n')
677 lines = map(lambda line, prefix=prefix: prefix + line, lines)
678 if lines: lines[-1] = rstrip(lines[-1])
679 return join(lines, '\n')
680
681 def section(self, title, contents):
682 """Format a section with a given heading."""
683 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
684
685 # ---------------------------------------------- type-specific routines
686
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000687 def formattree(self, tree, modname, parent=None, prefix=''):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000688 """Render in text a class tree as returned by inspect.getclasstree()."""
689 result = ''
690 for entry in tree:
691 if type(entry) is type(()):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000692 c, bases = entry
693 result = result + prefix + classname(c, modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000694 if bases and bases != (parent,):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000695 parents = map(lambda c, m=modname: classname(c, m), bases)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000696 result = result + '(%s)' % join(parents, ', ')
697 result = result + '\n'
698 elif type(entry) is type([]):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000699 result = result + self.formattree(
700 entry, modname, c, prefix + ' ')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000701 return result
702
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000703 def docmodule(self, object, name=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000704 """Produce text documentation for a given module object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000705 name = object.__name__ # ignore the passed-in name
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000706 lines = split(strip(getdoc(object)), '\n')
707 if len(lines) == 1:
708 if lines[0]: name = name + ' - ' + lines[0]
709 lines = []
710 elif len(lines) >= 2 and not rstrip(lines[1]):
711 if lines[0]: name = name + ' - ' + lines[0]
712 lines = lines[2:]
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000713 result = self.section('NAME', name)
714
715 try:
716 file = inspect.getabsfile(object)
717 except TypeError:
718 file = '(built-in)'
Ka-Ping Yee239432a2001-03-02 02:45:08 +0000719 result = result + self.section('FILE', file)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000720 if lines:
721 result = result + self.section('DESCRIPTION', join(lines, '\n'))
722
723 classes = []
724 for key, value in inspect.getmembers(object, inspect.isclass):
725 if (inspect.getmodule(value) or object) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000726 classes.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000727 funcs = []
728 for key, value in inspect.getmembers(object, inspect.isroutine):
729 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000730 funcs.append((key, value))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000731 constants = []
732 for key, value in inspect.getmembers(object, isconstant):
733 if key[:1] != '_':
734 constants.append((key, value))
735
736 if hasattr(object, '__path__'):
737 modpkgs = []
738 for file in os.listdir(object.__path__[0]):
739 if file[:1] != '_':
740 path = os.path.join(object.__path__[0], file)
741 modname = modulename(file)
742 if modname and modname not in modpkgs:
743 modpkgs.append(modname)
744 elif ispackage(path):
745 modpkgs.append(file + ' (package)')
746 modpkgs.sort()
747 result = result + self.section(
748 'PACKAGE CONTENTS', join(modpkgs, '\n'))
749
750 if classes:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000751 classlist = map(lambda (key, value): value, classes)
752 contents = [self.formattree(
753 inspect.getclasstree(classlist, 1), name)]
754 for key, value in classes:
755 contents.append(self.document(value, key))
756 result = result + self.section('CLASSES', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000757
758 if funcs:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000759 contents = []
760 for key, value in funcs:
761 contents.append(self.document(value, key))
762 result = result + self.section('FUNCTIONS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000763
764 if constants:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000765 contents = []
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000766 for key, value in constants:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000767 contents.append(self.docother(value, key, 70))
768 result = result + self.section('CONSTANTS', join(contents, '\n'))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000769
770 if hasattr(object, '__version__'):
771 version = str(object.__version__)
Ka-Ping Yee1d384632001-03-01 00:24:32 +0000772 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
773 version = strip(version[11:-1])
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000774 result = result + self.section('VERSION', version)
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000775 if hasattr(object, '__date__'):
776 result = result + self.section('DATE', str(object.__date__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000777 if hasattr(object, '__author__'):
Ka-Ping Yee6f3f9a42001-02-27 22:42:36 +0000778 result = result + self.section('AUTHOR', str(object.__author__))
779 if hasattr(object, '__credits__'):
780 result = result + self.section('CREDITS', str(object.__credits__))
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000781 return result
782
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000783 def docclass(self, object, name=None):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000784 """Produce text documentation for a given class object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000785 realname = object.__name__
786 name = name or realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000787 bases = object.__bases__
788
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000789 if name == realname:
790 title = 'class ' + self.bold(realname)
791 else:
792 title = self.bold(name) + ' = class ' + realname
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000793 if bases:
794 parents = map(lambda c, m=object.__module__: classname(c, m), bases)
795 title = title + '(%s)' % join(parents, ', ')
796
797 doc = getdoc(object)
798 contents = doc and doc + '\n'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000799 for key, value in inspect.getmembers(object, inspect.ismethod):
800 contents = contents + '\n' + self.document(value, key, name)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000801
802 if not contents: return title + '\n'
803 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
804
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000805 def formatvalue(self, object):
806 """Format an argument default value as text."""
807 return '=' + self.repr(object)
808
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000809 def docroutine(self, object, name=None, clname=None):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000810 """Produce text documentation for a function or method object."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000811 realname = object.__name__
812 name = name or realname
813 note = ''
814 if inspect.ismethod(object):
815 if not clname:
816 if object.im_self:
817 note = ' method of %s' % self.repr(object.im_self)
818 else:
819 note = ' unbound %s method' % object.im_class.__name__
820 object = object.im_func
821
822 if name == realname:
823 title = self.bold(realname)
824 else:
825 title = self.bold(name) + ' = ' + realname
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000826 if inspect.isbuiltin(object):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000827 argspec = '(...)'
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000828 else:
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000829 args, varargs, varkw, defaults = inspect.getargspec(object)
830 argspec = inspect.formatargspec(
831 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000832 if realname == '<lambda>':
833 title = 'lambda'
834 argspec = argspec[1:-1] # remove parentheses
835 decl = title + argspec + note
836
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000837 doc = getdoc(object)
838 if doc:
839 return decl + '\n' + rstrip(self.indent(doc)) + '\n'
840 else:
841 return decl + '\n'
842
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000843 def docother(self, object, name=None, maxlen=None):
844 """Produce text documentation for a data object."""
845 repr = self.repr(object)
846 if maxlen:
847 line = name + ' = ' + repr
848 chop = maxlen - len(line)
849 if chop < 0: repr = repr[:chop] + '...'
850 line = self.bold(name) + ' = ' + repr
851 return line
852
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000853# --------------------------------------------------------- user interfaces
854
855def pager(text):
856 """The first time this is called, determine what kind of pager to use."""
857 global pager
858 pager = getpager()
859 pager(text)
860
861def getpager():
862 """Decide what method to use for paging through text."""
863 if type(sys.stdout) is not types.FileType:
864 return plainpager
865 if not sys.stdin.isatty() or not sys.stdout.isatty():
866 return plainpager
867 if os.environ.has_key('PAGER'):
868 return lambda a: pipepager(a, os.environ['PAGER'])
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +0000869 if sys.platform == 'win32':
870 return lambda a: tempfilepager(a, 'more <')
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000871 if hasattr(os, 'system') and os.system('less 2>/dev/null') == 0:
872 return lambda a: pipepager(a, 'less')
873
874 import tempfile
875 filename = tempfile.mktemp()
876 open(filename, 'w').close()
877 try:
878 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
879 return lambda text: pipepager(text, 'more')
880 else:
881 return ttypager
882 finally:
883 os.unlink(filename)
884
885def pipepager(text, cmd):
886 """Page through text by feeding it to another program."""
887 pipe = os.popen(cmd, 'w')
888 try:
889 pipe.write(text)
890 pipe.close()
891 except IOError:
892 # Ignore broken pipes caused by quitting the pager program.
893 pass
894
895def tempfilepager(text, cmd):
896 """Page through text by invoking a program on a temporary file."""
897 import tempfile
898 filename = tempfile.mktemp()
899 file = open(filename, 'w')
900 file.write(text)
901 file.close()
902 try:
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +0000903 os.system(cmd + ' ' + filename)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000904 finally:
905 os.unlink(filename)
906
907def plain(text):
908 """Remove boldface formatting from text."""
909 return re.sub('.\b', '', text)
910
911def ttypager(text):
912 """Page through text on a text terminal."""
913 lines = split(plain(text), '\n')
914 try:
915 import tty
916 fd = sys.stdin.fileno()
917 old = tty.tcgetattr(fd)
918 tty.setcbreak(fd)
919 getchar = lambda: sys.stdin.read(1)
Ka-Ping Yee457aab22001-02-27 23:36:29 +0000920 except (ImportError, AttributeError):
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000921 tty = None
922 getchar = lambda: sys.stdin.readline()[:-1][:1]
923
924 try:
925 r = inc = os.environ.get('LINES', 25) - 1
926 sys.stdout.write(join(lines[:inc], '\n') + '\n')
927 while lines[r:]:
928 sys.stdout.write('-- more --')
929 sys.stdout.flush()
930 c = getchar()
931
932 if c in ['q', 'Q']:
933 sys.stdout.write('\r \r')
934 break
935 elif c in ['\r', '\n']:
936 sys.stdout.write('\r \r' + lines[r] + '\n')
937 r = r + 1
938 continue
939 if c in ['b', 'B', '\x1b']:
940 r = r - inc - inc
941 if r < 0: r = 0
942 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
943 r = r + inc
944
945 finally:
946 if tty:
947 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
948
949def plainpager(text):
950 """Simply print unformatted text. This is the ultimate fallback."""
951 sys.stdout.write(plain(text))
952
953def describe(thing):
954 """Produce a short description of the given kind of thing."""
955 if inspect.ismodule(thing):
956 if thing.__name__ in sys.builtin_module_names:
957 return 'built-in module ' + thing.__name__
958 if hasattr(thing, '__path__'):
959 return 'package ' + thing.__name__
960 else:
961 return 'module ' + thing.__name__
962 if inspect.isbuiltin(thing):
963 return 'built-in function ' + thing.__name__
964 if inspect.isclass(thing):
965 return 'class ' + thing.__name__
966 if inspect.isfunction(thing):
967 return 'function ' + thing.__name__
968 if inspect.ismethod(thing):
969 return 'method ' + thing.__name__
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000970 if type(thing) is types.InstanceType:
971 return 'instance of ' + thing.__class__.__name__
972 return type(thing).__name__
973
974def freshimp(path, cache={}):
975 """Import a module, reloading it if the source file has changed."""
976 module = __import__(path)
977 if hasattr(module, '__file__'):
978 file = module.__file__
979 info = (file, os.path.getmtime(file), os.path.getsize(file))
980 if cache.has_key(path):
981 if cache[path] != info:
982 module = reload(module)
983 cache[path] = info
984 return module
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000985
986def locate(path):
987 """Locate an object by name (or dotted path), importing as necessary."""
988 if not path: # special case: imp.find_module('') strangely succeeds
989 return None, None
990 if type(path) is not types.StringType:
991 return None, path
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000992 parts = split(path, '.')
993 n = 1
994 while n <= len(parts):
995 path = join(parts[:n], '.')
996 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +0000997 module = freshimp(path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +0000998 except:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +0000999 # determine if error occurred before or after module was found
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001000 if sys.modules.has_key(path):
1001 filename = sys.modules[path].__file__
1002 elif sys.exc_type is SyntaxError:
1003 filename = sys.exc_value.filename
1004 else:
1005 # module not found, so stop looking
1006 break
1007 # error occurred in the imported module, so report it
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001008 raise DocImportError(filename, sys.exc_info())
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001009 try:
1010 x = module
1011 for p in parts[1:]:
1012 x = getattr(x, p)
1013 return join(parts[:-1], '.'), x
1014 except AttributeError:
1015 n = n + 1
1016 continue
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001017 if hasattr(__builtins__, path):
1018 return None, getattr(__builtins__, path)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001019 return None, None
1020
1021# --------------------------------------- interactive interpreter interface
1022
1023text = TextDoc()
1024html = HTMLDoc()
1025
1026def doc(thing):
1027 """Display documentation on an object (for interactive use)."""
1028 if type(thing) is type(""):
1029 try:
1030 path, x = locate(thing)
1031 except DocImportError, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001032 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001033 return
1034 if x:
1035 thing = x
1036 else:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001037 print 'no Python documentation found for %s' % repr(thing)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001038 return
1039
1040 desc = describe(thing)
1041 module = inspect.getmodule(thing)
1042 if module and module is not thing:
1043 desc = desc + ' in module ' + module.__name__
1044 pager('Help on %s:\n\n' % desc + text.document(thing))
1045
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001046def writedoc(key):
1047 """Write HTML documentation to a file in the current directory."""
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001048 try:
1049 path, object = locate(key)
1050 except DocImportError, value:
1051 print value
1052 else:
1053 if object:
1054 page = html.page('Python: ' + describe(object),
1055 html.document(object, object.__name__))
1056 file = open(key + '.html', 'w')
1057 file.write(page)
1058 file.close()
1059 print 'wrote', key + '.html'
1060 else:
1061 print 'no Python documentation found for %s' % repr(key)
1062
1063def writedocs(dir, pkgpath='', done={}):
1064 """Write out HTML documentation for all modules in a directory tree."""
1065 for file in os.listdir(dir):
1066 path = os.path.join(dir, file)
1067 if ispackage(path):
1068 writedocs(path, pkgpath + file + '.')
1069 elif os.path.isfile(path):
1070 modname = modulename(path)
1071 if modname:
1072 modname = pkgpath + modname
1073 if not done.has_key(modname):
1074 done[modname] = 1
1075 writedoc(modname)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001076
1077class Helper:
1078 def __repr__(self):
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001079 return '''To get help on a Python object, call help(object).
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001080To get help on a module or package, either import it before calling
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001081help(module) or call help('modulename').'''
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001082
1083 def __call__(self, *args):
1084 if args:
1085 doc(args[0])
1086 else:
1087 print repr(self)
1088
1089help = Helper()
1090
1091def man(key):
1092 """Display documentation on an object in a form similar to man(1)."""
1093 path, object = locate(key)
1094 if object:
1095 title = 'Python Library Documentation: ' + describe(object)
1096 if path: title = title + ' in ' + path
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001097 pager('\n' + title + '\n\n' + text.document(object, key))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001098 found = 1
1099 else:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001100 print 'no Python documentation found for %s' % repr(key)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001101
1102class Scanner:
1103 """A generic tree iterator."""
1104 def __init__(self, roots, children, recurse):
1105 self.roots = roots[:]
1106 self.state = []
1107 self.children = children
1108 self.recurse = recurse
1109
1110 def next(self):
1111 if not self.state:
1112 if not self.roots:
1113 return None
1114 root = self.roots.pop(0)
1115 self.state = [(root, self.children(root))]
1116 node, children = self.state[-1]
1117 if not children:
1118 self.state.pop()
1119 return self.next()
1120 child = children.pop(0)
1121 if self.recurse(child):
1122 self.state.append((child, self.children(child)))
1123 return child
1124
1125class ModuleScanner(Scanner):
1126 """An interruptible scanner that searches module synopses."""
1127 def __init__(self):
1128 roots = map(lambda dir: (dir, ''), pathdirs())
1129 Scanner.__init__(self, roots, self.submodules, self.ispackage)
1130
1131 def submodules(self, (dir, package)):
1132 children = []
1133 for file in os.listdir(dir):
1134 path = os.path.join(dir, file)
Tim Peters30edd232001-03-16 08:29:48 +00001135 if ispackage(path):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001136 children.append((path, package + (package and '.') + file))
1137 else:
1138 children.append((path, package))
1139 children.sort()
1140 return children
1141
1142 def ispackage(self, (dir, package)):
1143 return ispackage(dir)
1144
1145 def run(self, key, callback, completer=None):
1146 self.quit = 0
1147 seen = {}
1148
1149 for modname in sys.builtin_module_names:
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001150 if modname != '__main__':
1151 seen[modname] = 1
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001152 desc = split(freshimp(modname).__doc__ or '', '\n')[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001153 if find(lower(modname + ' - ' + desc), lower(key)) >= 0:
1154 callback(None, modname, desc)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001155
1156 while not self.quit:
1157 node = self.next()
1158 if not node: break
1159 path, package = node
1160 modname = modulename(path)
1161 if os.path.isfile(path) and modname:
1162 modname = package + (package and '.') + modname
1163 if not seen.has_key(modname):
1164 seen[modname] = 1
1165 desc = synopsis(path) or ''
1166 if find(lower(modname + ' - ' + desc), lower(key)) >= 0:
1167 callback(path, modname, desc)
1168 if completer: completer()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001169
1170def apropos(key):
1171 """Print all the one-line module summaries that contain a substring."""
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001172 def callback(path, modname, desc):
1173 if modname[-9:] == '.__init__':
1174 modname = modname[:-9] + ' (package)'
1175 print modname, '-', desc or '(no description)'
1176 ModuleScanner().run(key, callback)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001177
1178# --------------------------------------------------- web browser interface
1179
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001180def serve(port, callback=None):
1181 import BaseHTTPServer, mimetools, select
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001182
1183 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1184 class Message(mimetools.Message):
1185 def __init__(self, fp, seekable=1):
1186 Message = self.__class__
1187 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1188 self.encodingheader = self.getheader('content-transfer-encoding')
1189 self.typeheader = self.getheader('content-type')
1190 self.parsetype()
1191 self.parseplist()
1192
1193 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1194 def send_document(self, title, contents):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001195 try:
1196 self.send_response(200)
1197 self.send_header('Content-Type', 'text/html')
1198 self.end_headers()
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001199 self.wfile.write(html.page(title, contents))
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001200 except IOError: pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001201
1202 def do_GET(self):
1203 path = self.path
1204 if path[-5:] == '.html': path = path[:-5]
1205 if path[:1] == '/': path = path[1:]
1206 if path and path != '.':
1207 try:
1208 p, x = locate(path)
1209 except DocImportError, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001210 self.send_document(path, html.escape(str(value)))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001211 return
1212 if x:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001213 self.send_document(describe(x), html.document(x, path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001214 else:
1215 self.send_document(path,
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001216'no Python documentation found for %s' % repr(path))
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001217 else:
1218 heading = html.heading(
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001219'<big><big><strong>Python: Index of Modules</strong></big></big>',
1220'#ffffff', '#7799ee')
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001221 def bltinlink(name):
1222 return '<a href="%s.html">%s</a>' % (name, name)
1223 names = filter(lambda x: x != '__main__', sys.builtin_module_names)
1224 contents = html.multicolumn(names, bltinlink)
1225 indices = ['<p>' + html.bigsection(
1226 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1227
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001228 # for skip in ['', '.', os.getcwd(), os.path.dirname(sys.argv[0])]:
1229 # if skip in sys.path: sys.path.remove(skip)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001230 seen = {}
1231 for dir in pathdirs():
1232 indices.append(html.index(dir, seen))
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001233 contents = heading + join(indices) + '''<p align=right>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001234<small><small><font color="#909090" face="helvetica, arial"><strong>
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001235pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font></small></small>'''
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001236 self.send_document('Index of Modules', contents)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001237
1238 def log_message(self, *args): pass
1239
1240 class DocServer(BaseHTTPServer.HTTPServer):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001241 def __init__(self, port, callback):
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001242 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001243 self.address = ('', port)
Ka-Ping Yeedb8ed152001-03-02 05:58:17 +00001244 self.url = 'http://%s:%d/' % (host, port)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001245 self.callback = callback
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001246 self.base.__init__(self, self.address, self.handler)
1247
1248 def serve_until_quit(self):
1249 import select
1250 self.quit = 0
1251 while not self.quit:
1252 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1253 if rd: self.handle_request()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001254
1255 def server_activate(self):
1256 self.base.server_activate(self)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001257 if self.callback: self.callback(self)
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001258
1259 DocServer.base = BaseHTTPServer.HTTPServer
1260 DocServer.handler = DocHandler
1261 DocHandler.MessageClass = Message
1262 try:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001263 DocServer(port, callback).serve_until_quit()
1264 except (KeyboardInterrupt, select.error):
1265 pass
1266 print 'server stopped'
1267
1268# ----------------------------------------------------- graphical interface
1269
1270def gui():
1271 """Graphical interface (starts web server and pops up a control window)."""
1272 class GUI:
1273 def __init__(self, window, port=7464):
1274 self.window = window
1275 self.server = None
1276 self.scanner = None
1277
1278 import Tkinter
1279 self.server_frm = Tkinter.Frame(window)
1280 self.title_lbl = Tkinter.Label(self.server_frm,
1281 text='Starting server...\n ')
1282 self.open_btn = Tkinter.Button(self.server_frm,
1283 text='open browser', command=self.open, state='disabled')
1284 self.quit_btn = Tkinter.Button(self.server_frm,
1285 text='quit serving', command=self.quit, state='disabled')
1286
1287 self.search_frm = Tkinter.Frame(window)
1288 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
1289 self.search_ent = Tkinter.Entry(self.search_frm)
1290 self.search_ent.bind('<Return>', self.search)
1291 self.stop_btn = Tkinter.Button(self.search_frm,
1292 text='stop', pady=0, command=self.stop, state='disabled')
1293 if sys.platform == 'win32':
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001294 # Trying to hide and show this button crashes under Windows.
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001295 self.stop_btn.pack(side='right')
1296
1297 self.window.title('pydoc')
1298 self.window.protocol('WM_DELETE_WINDOW', self.quit)
1299 self.title_lbl.pack(side='top', fill='x')
1300 self.open_btn.pack(side='left', fill='x', expand=1)
1301 self.quit_btn.pack(side='right', fill='x', expand=1)
1302 self.server_frm.pack(side='top', fill='x')
1303
1304 self.search_lbl.pack(side='left')
1305 self.search_ent.pack(side='right', fill='x', expand=1)
1306 self.search_frm.pack(side='top', fill='x')
1307 self.search_ent.focus_set()
1308
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001309 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001310 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001311 self.result_lst.bind('<Button-1>', self.select)
1312 self.result_lst.bind('<Double-Button-1>', self.goto)
1313 self.result_scr = Tkinter.Scrollbar(window,
1314 orient='vertical', command=self.result_lst.yview)
1315 self.result_lst.config(yscrollcommand=self.result_scr.set)
1316
1317 self.result_frm = Tkinter.Frame(window)
1318 self.goto_btn = Tkinter.Button(self.result_frm,
1319 text='go to selected', command=self.goto)
1320 self.hide_btn = Tkinter.Button(self.result_frm,
1321 text='hide results', command=self.hide)
1322 self.goto_btn.pack(side='left', fill='x', expand=1)
1323 self.hide_btn.pack(side='right', fill='x', expand=1)
1324
1325 self.window.update()
1326 self.minwidth = self.window.winfo_width()
1327 self.minheight = self.window.winfo_height()
1328 self.bigminheight = (self.server_frm.winfo_reqheight() +
1329 self.search_frm.winfo_reqheight() +
1330 self.result_lst.winfo_reqheight() +
1331 self.result_frm.winfo_reqheight())
1332 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
1333 self.expanded = 0
1334 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1335 self.window.wm_minsize(self.minwidth, self.minheight)
1336
1337 import threading
1338 threading.Thread(target=serve, args=(port, self.ready)).start()
1339
1340 def ready(self, server):
1341 self.server = server
1342 self.title_lbl.config(
1343 text='Python documentation server at\n' + server.url)
1344 self.open_btn.config(state='normal')
1345 self.quit_btn.config(state='normal')
1346
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001347 def open(self, event=None, url=None):
1348 url = url or self.server.url
1349 try:
1350 import webbrowser
1351 webbrowser.open(url)
1352 except ImportError: # pre-webbrowser.py compatibility
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001353 if sys.platform == 'win32':
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001354 os.system('start "%s"' % url)
1355 elif sys.platform == 'mac':
1356 try:
1357 import ic
1358 ic.launchurl(url)
1359 except ImportError: pass
1360 else:
1361 rc = os.system('netscape -remote "openURL(%s)" &' % url)
1362 if rc: os.system('netscape "%s" &' % url)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001363
1364 def quit(self, event=None):
1365 if self.server:
1366 self.server.quit = 1
1367 self.window.quit()
1368
1369 def search(self, event=None):
1370 key = self.search_ent.get()
1371 self.stop_btn.pack(side='right')
1372 self.stop_btn.config(state='normal')
1373 self.search_lbl.config(text='Searching for "%s"...' % key)
1374 self.search_ent.forget()
1375 self.search_lbl.pack(side='left')
1376 self.result_lst.delete(0, 'end')
1377 self.goto_btn.config(state='disabled')
1378 self.expand()
1379
1380 import threading
1381 if self.scanner:
1382 self.scanner.quit = 1
1383 self.scanner = ModuleScanner()
1384 threading.Thread(target=self.scanner.run,
1385 args=(key, self.update, self.done)).start()
1386
1387 def update(self, path, modname, desc):
1388 if modname[-9:] == '.__init__':
1389 modname = modname[:-9] + ' (package)'
1390 self.result_lst.insert('end',
1391 modname + ' - ' + (desc or '(no description)'))
1392
1393 def stop(self, event=None):
1394 if self.scanner:
1395 self.scanner.quit = 1
1396 self.scanner = None
1397
1398 def done(self):
1399 self.scanner = None
1400 self.search_lbl.config(text='Search for')
1401 self.search_lbl.pack(side='left')
1402 self.search_ent.pack(side='right', fill='x', expand=1)
1403 if sys.platform != 'win32': self.stop_btn.forget()
1404 self.stop_btn.config(state='disabled')
1405
1406 def select(self, event=None):
1407 self.goto_btn.config(state='normal')
1408
1409 def goto(self, event=None):
1410 selection = self.result_lst.curselection()
1411 if selection:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001412 modname = split(self.result_lst.get(selection[0]))[0]
Ka-Ping Yee239432a2001-03-02 02:45:08 +00001413 self.open(url=self.server.url + modname + '.html')
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001414
1415 def collapse(self):
1416 if not self.expanded: return
1417 self.result_frm.forget()
1418 self.result_scr.forget()
1419 self.result_lst.forget()
1420 self.bigwidth = self.window.winfo_width()
1421 self.bigheight = self.window.winfo_height()
1422 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1423 self.window.wm_minsize(self.minwidth, self.minheight)
1424 self.expanded = 0
1425
1426 def expand(self):
1427 if self.expanded: return
1428 self.result_frm.pack(side='bottom', fill='x')
1429 self.result_scr.pack(side='right', fill='y')
1430 self.result_lst.pack(side='top', fill='both', expand=1)
1431 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
1432 self.window.wm_minsize(self.minwidth, self.bigminheight)
1433 self.expanded = 1
1434
1435 def hide(self, event=None):
1436 self.stop()
1437 self.collapse()
1438
1439 import Tkinter
1440 try:
1441 gui = GUI(Tkinter.Tk())
1442 Tkinter.mainloop()
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001443 except KeyboardInterrupt:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001444 pass
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001445
1446# -------------------------------------------------- command-line interface
1447
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001448def ispath(x):
1449 return type(x) is types.StringType and find(x, os.sep) >= 0
1450
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001451def cli():
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001452 """Command-line interface (looks at sys.argv to decide what to do)."""
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001453 import getopt
1454 class BadUsage: pass
1455
1456 try:
Ka-Ping Yeec92cdf72001-03-02 05:54:35 +00001457 if sys.platform in ['mac', 'win32'] and not sys.argv[1:]:
1458 # graphical platforms with threading (and no CLI)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001459 gui()
1460 return
1461
1462 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001463 writing = 0
1464
1465 for opt, val in opts:
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001466 if opt == '-g':
1467 gui()
1468 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001469 if opt == '-k':
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001470 apropos(val)
1471 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001472 if opt == '-p':
1473 try:
1474 port = int(val)
1475 except ValueError:
1476 raise BadUsage
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001477 def ready(server):
1478 print 'server ready at %s' % server.url
1479 serve(port, ready)
1480 return
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001481 if opt == '-w':
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001482 writing = 1
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001483
1484 if not args: raise BadUsage
1485 for arg in args:
1486 try:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001487 if ispath(arg) and os.path.isfile(arg):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001488 arg = importfile(arg)
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001489 if writing:
1490 if ispath(arg) and os.path.isdir(arg):
1491 writedocs(arg)
1492 else:
1493 writedoc(arg)
1494 else:
1495 man(arg)
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001496 except DocImportError, value:
Ka-Ping Yee37f7b382001-03-23 00:12:53 +00001497 print value
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001498
1499 except (getopt.error, BadUsage):
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001500 cmd = sys.argv[0]
1501 print """pydoc - the Python documentation tool
1502
1503%s <name> ...
1504 Show text documentation on something. <name> may be the name of a
1505 function, module, or package, or a dotted reference to a class or
1506 function within a module or module in a package. If <name> contains
1507 a '%s', it is used as the path to a Python source file to document.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001508
1509%s -k <keyword>
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001510 Search for a keyword in the synopsis lines of all available modules.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001511
1512%s -p <port>
1513 Start an HTTP server on the given port on the local machine.
1514
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001515%s -g
1516 Pop up a graphical interface for serving and finding documentation.
Ka-Ping Yeedd175342001-02-27 14:43:46 +00001517
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001518%s -w <name> ...
1519 Write out the HTML documentation for a module to a file in the current
1520 directory. If <name> contains a '%s', it is treated as a filename.
1521""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
Ka-Ping Yee1d384632001-03-01 00:24:32 +00001522
Ka-Ping Yee66efbc72001-03-01 13:55:20 +00001523if __name__ == '__main__': cli()