Apparently the code to forestall Tk eating events was too aggressive (Tk user input...
[python/dscho.git] / Lib / pydoc.py
blob5de0903f02fe92dde4f6662d254edb9946034a5f
1 #!/usr/bin/env python
2 """Generate Python documentation in HTML or text for interactive use.
4 In the Python interpreter, do "from pydoc import help" to provide online
5 help. Calling help(thing) on a Python object documents the object.
7 Or, at the shell command line outside of Python:
9 Run "pydoc <name>" to show documentation on something. <name> may be
10 the name of a function, module, package, or a dotted reference to a
11 class or function within a module or module in a package. If the
12 argument contains a path segment delimiter (e.g. slash on Unix,
13 backslash on Windows) it is treated as the path to a Python source file.
15 Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines
16 of all available modules.
18 Run "pydoc -p <port>" to start an HTTP server on a given port on the
19 local machine to generate documentation web pages.
21 For platforms without a command line, "pydoc -g" starts the HTTP server
22 and also pops up a little window for controlling it.
24 Run "pydoc -w <name>" to write out the HTML documentation for a module
25 to a file named "<name>.html".
26 """
28 __author__ = "Ka-Ping Yee <ping@lfw.org>"
29 __date__ = "26 February 2001"
30 __version__ = "$Revision$"
31 __credits__ = """Guido van Rossum, for an excellent programming language.
32 Tommy Burnette, the original creator of manpy.
33 Paul Prescod, for all his work on onlinehelp.
34 Richard Chamberlain, for the first implementation of textdoc.
36 Mynd you, møøse bites Kan be pretty nasti..."""
38 # 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 absent.
42 # Known bugs that can't be fixed here:
43 # - imp.load_module() cannot be prevented from clobbering existing
44 # loaded modules, so calling synopsis() on a binary module file
45 # changes the contents of any existing module with the same name.
46 # - If the __file__ attribute on a module is a relative path and
47 # the current directory is changed with os.chdir(), an incorrect
48 # path will be displayed.
50 import sys, imp, os, stat, re, types, inspect
51 from repr import Repr
52 from string import expandtabs, find, join, lower, split, strip, rfind, rstrip
54 # --------------------------------------------------------- common routines
56 def pathdirs():
57 """Convert sys.path into a list of absolute, existing, unique paths."""
58 dirs = []
59 normdirs = []
60 for dir in sys.path:
61 dir = os.path.abspath(dir or '.')
62 normdir = os.path.normcase(dir)
63 if normdir not in normdirs and os.path.isdir(dir):
64 dirs.append(dir)
65 normdirs.append(normdir)
66 return dirs
68 def getdoc(object):
69 """Get the doc string or comments for an object."""
70 result = inspect.getdoc(object) or inspect.getcomments(object)
71 return result and re.sub('^ *\n', '', rstrip(result)) or ''
73 def splitdoc(doc):
74 """Split a doc string into a synopsis line (if any) and the rest."""
75 lines = split(strip(doc), '\n')
76 if len(lines) == 1:
77 return lines[0], ''
78 elif len(lines) >= 2 and not rstrip(lines[1]):
79 return lines[0], join(lines[2:], '\n')
80 return '', join(lines, '\n')
82 def classname(object, modname):
83 """Get a class name and qualify it with a module name if necessary."""
84 name = object.__name__
85 if object.__module__ != modname:
86 name = object.__module__ + '.' + name
87 return name
89 def isdata(object):
90 """Check if an object is of a type that probably means it's data."""
91 return not (inspect.ismodule(object) or inspect.isclass(object) or
92 inspect.isroutine(object) or inspect.isframe(object) or
93 inspect.istraceback(object) or inspect.iscode(object))
95 def replace(text, *pairs):
96 """Do a series of global replacements on a string."""
97 while pairs:
98 text = join(split(text, pairs[0]), pairs[1])
99 pairs = pairs[2:]
100 return text
102 def cram(text, maxlen):
103 """Omit part of a string if needed to make it fit in a maximum length."""
104 if len(text) > maxlen:
105 pre = max(0, (maxlen-3)/2)
106 post = max(0, maxlen-3-pre)
107 return text[:pre] + '...' + text[len(text)-post:]
108 return text
110 def stripid(text):
111 """Remove the hexadecimal id from a Python object representation."""
112 # The behaviour of %p is implementation-dependent; we check two cases.
113 for pattern in [' at 0x[0-9a-f]{6,}>$', ' at [0-9A-F]{8,}>$']:
114 if re.search(pattern, repr(Exception)):
115 return re.sub(pattern, '>', text)
116 return text
118 def allmethods(cl):
119 methods = {}
120 for key, value in inspect.getmembers(cl, inspect.ismethod):
121 methods[key] = 1
122 for base in cl.__bases__:
123 methods.update(allmethods(base)) # all your base are belong to us
124 for key in methods.keys():
125 methods[key] = getattr(cl, key)
126 return methods
128 # ----------------------------------------------------- module manipulation
130 def ispackage(path):
131 """Guess whether a path refers to a package directory."""
132 if os.path.isdir(path):
133 for ext in ['.py', '.pyc', '.pyo']:
134 if os.path.isfile(os.path.join(path, '__init__' + ext)):
135 return 1
137 def synopsis(filename, cache={}):
138 """Get the one-line summary out of a module file."""
139 mtime = os.stat(filename)[stat.ST_MTIME]
140 lastupdate, result = cache.get(filename, (0, None))
141 if lastupdate < mtime:
142 info = inspect.getmoduleinfo(filename)
143 file = open(filename)
144 if info and 'b' in info[2]: # binary modules have to be imported
145 try: module = imp.load_module('__temp__', file, filename, info[1:])
146 except: return None
147 result = split(module.__doc__ or '', '\n')[0]
148 del sys.modules['__temp__']
149 else: # text modules can be directly examined
150 line = file.readline()
151 while line[:1] == '#' or not strip(line):
152 line = file.readline()
153 if not line: break
154 line = strip(line)
155 if line[:4] == 'r"""': line = line[1:]
156 if line[:3] == '"""':
157 line = line[3:]
158 if line[-1:] == '\\': line = line[:-1]
159 while not strip(line):
160 line = file.readline()
161 if not line: break
162 result = strip(split(line, '"""')[0])
163 else: result = None
164 file.close()
165 cache[filename] = (mtime, result)
166 return result
168 class ErrorDuringImport(Exception):
169 """Errors that occurred while trying to import something to document it."""
170 def __init__(self, filename, (exc, value, tb)):
171 self.filename = filename
172 self.exc = exc
173 self.value = value
174 self.tb = tb
176 def __str__(self):
177 exc = self.exc
178 if type(exc) is types.ClassType:
179 exc = exc.__name__
180 return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
182 def importfile(path):
183 """Import a Python source file or compiled file given its path."""
184 magic = imp.get_magic()
185 file = open(path, 'r')
186 if file.read(len(magic)) == magic:
187 kind = imp.PY_COMPILED
188 else:
189 kind = imp.PY_SOURCE
190 file.close()
191 filename = os.path.basename(path)
192 name, ext = os.path.splitext(filename)
193 file = open(path, 'r')
194 try:
195 module = imp.load_module(name, file, path, (ext, 'r', kind))
196 except:
197 raise ErrorDuringImport(path, sys.exc_info())
198 file.close()
199 return module
201 def safeimport(path, forceload=0, cache={}):
202 """Import a module; handle errors; return None if the module isn't found.
204 If the module *is* found but an exception occurs, it's wrapped in an
205 ErrorDuringImport exception and reraised. Unlike __import__, if a
206 package path is specified, the module at the end of the path is returned,
207 not the package at the beginning. If the optional 'forceload' argument
208 is 1, we reload the module from disk (unless it's a dynamic extension)."""
209 if forceload and sys.modules.has_key(path):
210 # This is the only way to be sure. Checking the mtime of the file
211 # isn't good enough (e.g. what if the module contains a class that
212 # inherits from another module that has changed?).
213 if path not in sys.builtin_module_names:
214 # Python never loads a dynamic extension a second time from the
215 # same path, even if the file is changed or missing. Deleting
216 # the entry in sys.modules doesn't help for dynamic extensions,
217 # so we're not even going to try to keep them up to date.
218 info = inspect.getmoduleinfo(sys.modules[path].__file__)
219 if info[3] != imp.C_EXTENSION:
220 cache[path] = sys.modules[path] # prevent module from clearing
221 del sys.modules[path]
222 try:
223 module = __import__(path)
224 except:
225 # Did the error occur before or after the module was found?
226 (exc, value, tb) = info = sys.exc_info()
227 if sys.modules.has_key(path):
228 # An error occured while executing the imported module.
229 raise ErrorDuringImport(sys.modules[path].__file__, info)
230 elif exc is SyntaxError:
231 # A SyntaxError occurred before we could execute the module.
232 raise ErrorDuringImport(value.filename, info)
233 elif exc is ImportError and \
234 split(lower(str(value)))[:2] == ['no', 'module']:
235 # The module was not found.
236 return None
237 else:
238 # Some other error occurred during the importing process.
239 raise ErrorDuringImport(path, sys.exc_info())
240 for part in split(path, '.')[1:]:
241 try: module = getattr(module, part)
242 except AttributeError: return None
243 return module
245 # ---------------------------------------------------- formatter base class
247 class Doc:
248 def document(self, object, name=None, *args):
249 """Generate documentation for an object."""
250 args = (object, name) + args
251 if inspect.ismodule(object): return apply(self.docmodule, args)
252 if inspect.isclass(object): return apply(self.docclass, args)
253 if inspect.isroutine(object): return apply(self.docroutine, args)
254 return apply(self.docother, args)
256 def fail(self, object, name=None, *args):
257 """Raise an exception for unimplemented types."""
258 message = "don't know how to document object%s of type %s" % (
259 name and ' ' + repr(name), type(object).__name__)
260 raise TypeError, message
262 docmodule = docclass = docroutine = docother = fail
264 # -------------------------------------------- HTML documentation generator
266 class HTMLRepr(Repr):
267 """Class for safely making an HTML representation of a Python object."""
268 def __init__(self):
269 Repr.__init__(self)
270 self.maxlist = self.maxtuple = 20
271 self.maxdict = 10
272 self.maxstring = self.maxother = 100
274 def escape(self, text):
275 return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')
277 def repr(self, object):
278 return Repr.repr(self, object)
280 def repr1(self, x, level):
281 methodname = 'repr_' + join(split(type(x).__name__), '_')
282 if hasattr(self, methodname):
283 return getattr(self, methodname)(x, level)
284 else:
285 return self.escape(cram(stripid(repr(x)), self.maxother))
287 def repr_string(self, x, level):
288 test = cram(x, self.maxstring)
289 testrepr = repr(test)
290 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
291 # Backslashes are only literal in the string and are never
292 # needed to make any special characters, so show a raw string.
293 return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
294 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
295 r'<font color="#c040c0">\1</font>',
296 self.escape(testrepr))
298 def repr_instance(self, x, level):
299 try:
300 return self.escape(cram(stripid(repr(x)), self.maxstring))
301 except:
302 return self.escape('<%s instance>' % x.__class__.__name__)
304 repr_unicode = repr_string
306 class HTMLDoc(Doc):
307 """Formatter class for HTML documentation."""
309 # ------------------------------------------- HTML formatting utilities
311 _repr_instance = HTMLRepr()
312 repr = _repr_instance.repr
313 escape = _repr_instance.escape
315 def page(self, title, contents):
316 """Format an HTML page."""
317 return '''
318 <!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
319 <html><head><title>Python: %s</title>
320 <style type="text/css"><!--
321 TT { font-family: lucidatypewriter, lucida console, courier }
322 --></style></head><body bgcolor="#f0f0f8">
324 </body></html>''' % (title, contents)
326 def heading(self, title, fgcol, bgcol, extras=''):
327 """Format a page heading."""
328 return '''
329 <table width="100%%" cellspacing=0 cellpadding=2 border=0>
330 <tr bgcolor="%s">
331 <td valign=bottom><small>&nbsp;<br></small
332 ><font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
333 ><td align=right valign=bottom
334 ><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
335 ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
337 def section(self, title, fgcol, bgcol, contents, width=10,
338 prelude='', marginalia=None, gap='&nbsp;&nbsp;'):
339 """Format a section with a heading."""
340 if marginalia is None:
341 marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
342 result = '''
343 <p><table width="100%%" cellspacing=0 cellpadding=2 border=0>
344 <tr bgcolor="%s">
345 <td colspan=3 valign=bottom><small><small>&nbsp;<br></small></small
346 ><font color="%s" face="helvetica, arial">%s</font></td></tr>
347 ''' % (bgcol, fgcol, title)
348 if prelude:
349 result = result + '''
350 <tr bgcolor="%s"><td rowspan=2>%s</td>
351 <td colspan=2>%s</td></tr>
352 <tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
353 else:
354 result = result + '''
355 <tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
357 return result + '\n<td width="100%%">%s</td></tr></table>' % contents
359 def bigsection(self, title, *args):
360 """Format a section with a big heading."""
361 title = '<big><strong>%s</strong></big>' % title
362 return apply(self.section, (title,) + args)
364 def preformat(self, text):
365 """Format literal preformatted text."""
366 text = self.escape(expandtabs(text))
367 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
368 ' ', '&nbsp;', '\n', '<br>\n')
370 def multicolumn(self, list, format, cols=4):
371 """Format a list of items into a multi-column list."""
372 result = ''
373 rows = (len(list)+cols-1)/cols
374 for col in range(cols):
375 result = result + '<td width="%d%%" valign=top>' % (100/cols)
376 for i in range(rows*col, rows*col+rows):
377 if i < len(list):
378 result = result + format(list[i]) + '<br>\n'
379 result = result + '</td>'
380 return '<table width="100%%"><tr>%s</tr></table>' % result
382 def small(self, text): return '<small>%s</small>' % text
383 def grey(self, text): return '<font color="#909090">%s</font>' % text
385 def namelink(self, name, *dicts):
386 """Make a link for an identifier, given name-to-URL mappings."""
387 for dict in dicts:
388 if dict.has_key(name):
389 return '<a href="%s">%s</a>' % (dict[name], name)
390 return name
392 def classlink(self, object, modname):
393 """Make a link for a class."""
394 name, module = object.__name__, sys.modules.get(object.__module__)
395 if hasattr(module, name) and getattr(module, name) is object:
396 return '<a href="%s.html#%s">%s</a>' % (
397 module.__name__, name, classname(object, modname))
398 return classname(object, modname)
400 def modulelink(self, object):
401 """Make a link for a module."""
402 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
404 def modpkglink(self, (name, path, ispackage, shadowed)):
405 """Make a link for a module or package to display in an index."""
406 if shadowed:
407 return self.grey(name)
408 if path:
409 url = '%s.%s.html' % (path, name)
410 else:
411 url = '%s.html' % name
412 if ispackage:
413 text = '<strong>%s</strong>&nbsp;(package)' % name
414 else:
415 text = name
416 return '<a href="%s">%s</a>' % (url, text)
418 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
419 """Mark up some plain text, given a context of symbols to look for.
420 Each context dictionary maps object names to anchor names."""
421 escape = escape or self.escape
422 results = []
423 here = 0
424 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
425 r'RFC[- ]?(\d+)|'
426 r'PEP[- ]?(\d+)|'
427 r'(self\.)?(\w+))\b')
428 while 1:
429 match = pattern.search(text, here)
430 if not match: break
431 start, end = match.span()
432 results.append(escape(text[here:start]))
434 all, scheme, rfc, pep, selfdot, name = match.groups()
435 if scheme:
436 results.append('<a href="%s">%s</a>' % (all, escape(all)))
437 elif rfc:
438 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
439 results.append('<a href="%s">%s</a>' % (url, escape(all)))
440 elif pep:
441 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
442 results.append('<a href="%s">%s</a>' % (url, escape(all)))
443 elif text[end:end+1] == '(':
444 results.append(self.namelink(name, methods, funcs, classes))
445 elif selfdot:
446 results.append('self.<strong>%s</strong>' % name)
447 else:
448 results.append(self.namelink(name, classes))
449 here = end
450 results.append(escape(text[here:]))
451 return join(results, '')
453 # ---------------------------------------------- type-specific routines
455 def formattree(self, tree, modname, parent=None):
456 """Produce HTML for a class tree as given by inspect.getclasstree()."""
457 result = ''
458 for entry in tree:
459 if type(entry) is type(()):
460 c, bases = entry
461 result = result + '<dt><font face="helvetica, arial"><small>'
462 result = result + self.classlink(c, modname)
463 if bases and bases != (parent,):
464 parents = []
465 for base in bases:
466 parents.append(self.classlink(base, modname))
467 result = result + '(' + join(parents, ', ') + ')'
468 result = result + '\n</small></font></dt>'
469 elif type(entry) is type([]):
470 result = result + '<dd>\n%s</dd>\n' % self.formattree(
471 entry, modname, c)
472 return '<dl>\n%s</dl>\n' % result
474 def docmodule(self, object, name=None, mod=None):
475 """Produce HTML documentation for a module object."""
476 name = object.__name__ # ignore the passed-in name
477 parts = split(name, '.')
478 links = []
479 for i in range(len(parts)-1):
480 links.append(
481 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
482 (join(parts[:i+1], '.'), parts[i]))
483 linkedname = join(links + parts[-1:], '.')
484 head = '<big><big><strong>%s</strong></big></big>' % linkedname
485 try:
486 path = inspect.getabsfile(object)
487 url = path
488 if sys.platform == 'win32':
489 import nturl2path
490 url = nturl2path.pathname2url(path)
491 filelink = '<a href="file:%s">%s</a>' % (url, path)
492 except TypeError:
493 filelink = '(built-in)'
494 info = []
495 if hasattr(object, '__version__'):
496 version = str(object.__version__)
497 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
498 version = strip(version[11:-1])
499 info.append('version %s' % self.escape(version))
500 if hasattr(object, '__date__'):
501 info.append(self.escape(str(object.__date__)))
502 if info:
503 head = head + ' (%s)' % join(info, ', ')
504 result = self.heading(
505 head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)
507 modules = inspect.getmembers(object, inspect.ismodule)
509 classes, cdict = [], {}
510 for key, value in inspect.getmembers(object, inspect.isclass):
511 if (inspect.getmodule(value) or object) is object:
512 classes.append((key, value))
513 cdict[key] = cdict[value] = '#' + key
514 for key, value in classes:
515 for base in value.__bases__:
516 key, modname = base.__name__, base.__module__
517 module = sys.modules.get(modname)
518 if modname != name and module and hasattr(module, key):
519 if getattr(module, key) is base:
520 if not cdict.has_key(key):
521 cdict[key] = cdict[base] = modname + '.html#' + key
522 funcs, fdict = [], {}
523 for key, value in inspect.getmembers(object, inspect.isroutine):
524 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
525 funcs.append((key, value))
526 fdict[key] = '#-' + key
527 if inspect.isfunction(value): fdict[value] = fdict[key]
528 data = []
529 for key, value in inspect.getmembers(object, isdata):
530 if key not in ['__builtins__', '__doc__']:
531 data.append((key, value))
533 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
534 doc = doc and '<tt>%s</tt>' % doc
535 result = result + '<p>%s</p>\n' % self.small(doc)
537 if hasattr(object, '__path__'):
538 modpkgs = []
539 modnames = []
540 for file in os.listdir(object.__path__[0]):
541 path = os.path.join(object.__path__[0], file)
542 modname = inspect.getmodulename(file)
543 if modname and modname not in modnames:
544 modpkgs.append((modname, name, 0, 0))
545 modnames.append(modname)
546 elif ispackage(path):
547 modpkgs.append((file, name, 1, 0))
548 modpkgs.sort()
549 contents = self.multicolumn(modpkgs, self.modpkglink)
550 result = result + self.bigsection(
551 'Package Contents', '#ffffff', '#aa55cc', contents)
552 elif modules:
553 contents = self.multicolumn(
554 modules, lambda (key, value), s=self: s.modulelink(value))
555 result = result + self.bigsection(
556 'Modules', '#fffff', '#aa55cc', contents)
558 if classes:
559 classlist = map(lambda (key, value): value, classes)
560 contents = [
561 self.formattree(inspect.getclasstree(classlist, 1), name)]
562 for key, value in classes:
563 contents.append(self.document(value, key, name, fdict, cdict))
564 result = result + self.bigsection(
565 'Classes', '#ffffff', '#ee77aa', join(contents))
566 if funcs:
567 contents = []
568 for key, value in funcs:
569 contents.append(self.document(value, key, name, fdict, cdict))
570 result = result + self.bigsection(
571 'Functions', '#ffffff', '#eeaa77', join(contents))
572 if data:
573 contents = []
574 for key, value in data:
575 contents.append(self.document(value, key))
576 result = result + self.bigsection(
577 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))
578 if hasattr(object, '__author__'):
579 contents = self.markup(str(object.__author__), self.preformat)
580 result = result + self.bigsection(
581 'Author', '#ffffff', '#7799ee', contents)
582 if hasattr(object, '__credits__'):
583 contents = self.markup(str(object.__credits__), self.preformat)
584 result = result + self.bigsection(
585 'Credits', '#ffffff', '#7799ee', contents)
587 return result
589 def docclass(self, object, name=None, mod=None, funcs={}, classes={}):
590 """Produce HTML documentation for a class object."""
591 realname = object.__name__
592 name = name or realname
593 bases = object.__bases__
594 contents = ''
596 methods, mdict = allmethods(object).items(), {}
597 methods.sort()
598 for key, value in methods:
599 mdict[key] = mdict[value] = '#' + name + '-' + key
600 for key, value in methods:
601 contents = contents + self.document(
602 value, key, mod, funcs, classes, mdict, object)
604 if name == realname:
605 title = '<a name="%s">class <strong>%s</strong></a>' % (
606 name, realname)
607 else:
608 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
609 name, name, realname)
610 if bases:
611 parents = []
612 for base in bases:
613 parents.append(self.classlink(base, object.__module__))
614 title = title + '(%s)' % join(parents, ', ')
615 doc = self.markup(
616 getdoc(object), self.preformat, funcs, classes, mdict)
617 doc = self.small(doc and '<tt>%s<br>&nbsp;</tt>' % doc or
618 self.small('&nbsp;'))
619 return self.section(title, '#000000', '#ffc8d8', contents, 5, doc)
621 def formatvalue(self, object):
622 """Format an argument default value as text."""
623 return self.small(self.grey('=' + self.repr(object)))
625 def docroutine(self, object, name=None, mod=None,
626 funcs={}, classes={}, methods={}, cl=None):
627 """Produce HTML documentation for a function or method object."""
628 realname = object.__name__
629 name = name or realname
630 anchor = (cl and cl.__name__ or '') + '-' + name
631 note = ''
632 skipdocs = 0
633 if inspect.ismethod(object):
634 imclass = object.im_class
635 if cl:
636 if imclass is not cl:
637 note = ' from ' + self.classlink(imclass, mod)
638 skipdocs = 1
639 else:
640 if object.im_self:
641 note = ' method of %s instance' % self.classlink(
642 object.im_self.__class__, mod)
643 else:
644 note = ' unbound %s method' % self.classlink(imclass,mod)
645 object = object.im_func
647 if name == realname:
648 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
649 else:
650 if (cl and cl.__dict__.has_key(realname) and
651 cl.__dict__[realname] is object):
652 reallink = '<a href="#%s">%s</a>' % (
653 cl.__name__ + '-' + realname, realname)
654 skipdocs = 1
655 else:
656 reallink = realname
657 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
658 anchor, name, reallink)
659 if inspect.isbuiltin(object):
660 argspec = '(...)'
661 else:
662 args, varargs, varkw, defaults = inspect.getargspec(object)
663 argspec = inspect.formatargspec(
664 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
665 if realname == '<lambda>':
666 decl = '<em>lambda</em>'
667 argspec = argspec[1:-1] # remove parentheses
669 decl = title + argspec + (note and self.small(self.grey(
670 '<font face="helvetica, arial">%s</font>' % note)))
672 if skipdocs:
673 return '<dl><dt>%s</dl>\n' % decl
674 else:
675 doc = self.markup(
676 getdoc(object), self.preformat, funcs, classes, methods)
677 doc = doc and '<dd>' + self.small('<tt>%s</tt>' % doc)
678 return '<dl><dt>%s%s</dl>\n' % (decl, doc)
680 def docother(self, object, name=None, mod=None):
681 """Produce HTML documentation for a data object."""
682 lhs = name and '<strong>%s</strong> = ' % name or ''
683 return lhs + self.repr(object)
685 def index(self, dir, shadowed=None):
686 """Generate an HTML index for a directory of modules."""
687 modpkgs = []
688 if shadowed is None: shadowed = {}
689 seen = {}
690 files = os.listdir(dir)
692 def found(name, ispackage,
693 modpkgs=modpkgs, shadowed=shadowed, seen=seen):
694 if not seen.has_key(name):
695 modpkgs.append((name, '', ispackage, shadowed.has_key(name)))
696 seen[name] = 1
697 shadowed[name] = 1
699 # Package spam/__init__.py takes precedence over module spam.py.
700 for file in files:
701 path = os.path.join(dir, file)
702 if ispackage(path): found(file, 1)
703 for file in files:
704 path = os.path.join(dir, file)
705 if os.path.isfile(path):
706 modname = inspect.getmodulename(file)
707 if modname: found(modname, 0)
709 modpkgs.sort()
710 contents = self.multicolumn(modpkgs, self.modpkglink)
711 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
713 # -------------------------------------------- text documentation generator
715 class TextRepr(Repr):
716 """Class for safely making a text representation of a Python object."""
717 def __init__(self):
718 Repr.__init__(self)
719 self.maxlist = self.maxtuple = 20
720 self.maxdict = 10
721 self.maxstring = self.maxother = 100
723 def repr1(self, x, level):
724 methodname = 'repr_' + join(split(type(x).__name__), '_')
725 if hasattr(self, methodname):
726 return getattr(self, methodname)(x, level)
727 else:
728 return cram(stripid(repr(x)), self.maxother)
730 def repr_string(self, x, level):
731 test = cram(x, self.maxstring)
732 testrepr = repr(test)
733 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
734 # Backslashes are only literal in the string and are never
735 # needed to make any special characters, so show a raw string.
736 return 'r' + testrepr[0] + test + testrepr[0]
737 return testrepr
739 def repr_instance(self, x, level):
740 try:
741 return cram(stripid(repr(x)), self.maxstring)
742 except:
743 return '<%s instance>' % x.__class__.__name__
745 class TextDoc(Doc):
746 """Formatter class for text documentation."""
748 # ------------------------------------------- text formatting utilities
750 _repr_instance = TextRepr()
751 repr = _repr_instance.repr
753 def bold(self, text):
754 """Format a string in bold by overstriking."""
755 return join(map(lambda ch: ch + '\b' + ch, text), '')
757 def indent(self, text, prefix=' '):
758 """Indent text by prepending a given prefix to each line."""
759 if not text: return ''
760 lines = split(text, '\n')
761 lines = map(lambda line, prefix=prefix: prefix + line, lines)
762 if lines: lines[-1] = rstrip(lines[-1])
763 return join(lines, '\n')
765 def section(self, title, contents):
766 """Format a section with a given heading."""
767 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
769 # ---------------------------------------------- type-specific routines
771 def formattree(self, tree, modname, parent=None, prefix=''):
772 """Render in text a class tree as returned by inspect.getclasstree()."""
773 result = ''
774 for entry in tree:
775 if type(entry) is type(()):
776 c, bases = entry
777 result = result + prefix + classname(c, modname)
778 if bases and bases != (parent,):
779 parents = map(lambda c, m=modname: classname(c, m), bases)
780 result = result + '(%s)' % join(parents, ', ')
781 result = result + '\n'
782 elif type(entry) is type([]):
783 result = result + self.formattree(
784 entry, modname, c, prefix + ' ')
785 return result
787 def docmodule(self, object, name=None, mod=None):
788 """Produce text documentation for a given module object."""
789 name = object.__name__ # ignore the passed-in name
790 synop, desc = splitdoc(getdoc(object))
791 result = self.section('NAME', name + (synop and ' - ' + synop))
793 try:
794 file = inspect.getabsfile(object)
795 except TypeError:
796 file = '(built-in)'
797 result = result + self.section('FILE', file)
798 if desc:
799 result = result + self.section('DESCRIPTION', desc)
801 classes = []
802 for key, value in inspect.getmembers(object, inspect.isclass):
803 if (inspect.getmodule(value) or object) is object:
804 classes.append((key, value))
805 funcs = []
806 for key, value in inspect.getmembers(object, inspect.isroutine):
807 if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
808 funcs.append((key, value))
809 data = []
810 for key, value in inspect.getmembers(object, isdata):
811 if key not in ['__builtins__', '__doc__']:
812 data.append((key, value))
814 if hasattr(object, '__path__'):
815 modpkgs = []
816 for file in os.listdir(object.__path__[0]):
817 path = os.path.join(object.__path__[0], file)
818 modname = inspect.getmodulename(file)
819 if modname and modname not in modpkgs:
820 modpkgs.append(modname)
821 elif ispackage(path):
822 modpkgs.append(file + ' (package)')
823 modpkgs.sort()
824 result = result + self.section(
825 'PACKAGE CONTENTS', join(modpkgs, '\n'))
827 if classes:
828 classlist = map(lambda (key, value): value, classes)
829 contents = [self.formattree(
830 inspect.getclasstree(classlist, 1), name)]
831 for key, value in classes:
832 contents.append(self.document(value, key, name))
833 result = result + self.section('CLASSES', join(contents, '\n'))
835 if funcs:
836 contents = []
837 for key, value in funcs:
838 contents.append(self.document(value, key, name))
839 result = result + self.section('FUNCTIONS', join(contents, '\n'))
841 if data:
842 contents = []
843 for key, value in data:
844 contents.append(self.docother(value, key, name, 70))
845 result = result + self.section('DATA', join(contents, '\n'))
847 if hasattr(object, '__version__'):
848 version = str(object.__version__)
849 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
850 version = strip(version[11:-1])
851 result = result + self.section('VERSION', version)
852 if hasattr(object, '__date__'):
853 result = result + self.section('DATE', str(object.__date__))
854 if hasattr(object, '__author__'):
855 result = result + self.section('AUTHOR', str(object.__author__))
856 if hasattr(object, '__credits__'):
857 result = result + self.section('CREDITS', str(object.__credits__))
858 return result
860 def docclass(self, object, name=None, mod=None):
861 """Produce text documentation for a given class object."""
862 realname = object.__name__
863 name = name or realname
864 bases = object.__bases__
866 if name == realname:
867 title = 'class ' + self.bold(realname)
868 else:
869 title = self.bold(name) + ' = class ' + realname
870 if bases:
871 def makename(c, m=object.__module__): return classname(c, m)
872 parents = map(makename, bases)
873 title = title + '(%s)' % join(parents, ', ')
875 doc = getdoc(object)
876 contents = doc and doc + '\n'
877 methods = allmethods(object).items()
878 methods.sort()
879 for key, value in methods:
880 contents = contents + '\n' + self.document(value, key, mod, object)
882 if not contents: return title + '\n'
883 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
885 def formatvalue(self, object):
886 """Format an argument default value as text."""
887 return '=' + self.repr(object)
889 def docroutine(self, object, name=None, mod=None, cl=None):
890 """Produce text documentation for a function or method object."""
891 realname = object.__name__
892 name = name or realname
893 note = ''
894 skipdocs = 0
895 if inspect.ismethod(object):
896 imclass = object.im_class
897 if cl:
898 if imclass is not cl:
899 note = ' from ' + classname(imclass, mod)
900 skipdocs = 1
901 else:
902 if object.im_self:
903 note = ' method of %s instance' % classname(
904 object.im_self.__class__, mod)
905 else:
906 note = ' unbound %s method' % classname(imclass,mod)
907 object = object.im_func
909 if name == realname:
910 title = self.bold(realname)
911 else:
912 if (cl and cl.__dict__.has_key(realname) and
913 cl.__dict__[realname] is object):
914 skipdocs = 1
915 title = self.bold(name) + ' = ' + realname
916 if inspect.isbuiltin(object):
917 argspec = '(...)'
918 else:
919 args, varargs, varkw, defaults = inspect.getargspec(object)
920 argspec = inspect.formatargspec(
921 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
922 if realname == '<lambda>':
923 title = 'lambda'
924 argspec = argspec[1:-1] # remove parentheses
925 decl = title + argspec + note
927 if skipdocs:
928 return decl + '\n'
929 else:
930 doc = getdoc(object) or ''
931 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
933 def docother(self, object, name=None, mod=None, maxlen=None):
934 """Produce text documentation for a data object."""
935 repr = self.repr(object)
936 if maxlen:
937 line = (name and name + ' = ' or '') + repr
938 chop = maxlen - len(line)
939 if chop < 0: repr = repr[:chop] + '...'
940 line = (name and self.bold(name) + ' = ' or '') + repr
941 return line
943 # --------------------------------------------------------- user interfaces
945 def pager(text):
946 """The first time this is called, determine what kind of pager to use."""
947 global pager
948 pager = getpager()
949 pager(text)
951 def getpager():
952 """Decide what method to use for paging through text."""
953 if type(sys.stdout) is not types.FileType:
954 return plainpager
955 if not sys.stdin.isatty() or not sys.stdout.isatty():
956 return plainpager
957 if os.environ.has_key('PAGER'):
958 if sys.platform == 'win32': # pipes completely broken in Windows
959 return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
960 elif os.environ.get('TERM') in ['dumb', 'emacs']:
961 return lambda text: pipepager(plain(text), os.environ['PAGER'])
962 else:
963 return lambda text: pipepager(text, os.environ['PAGER'])
964 if sys.platform == 'win32':
965 return lambda text: tempfilepager(plain(text), 'more <')
966 if hasattr(os, 'system') and os.system('less 2>/dev/null') == 0:
967 return lambda text: pipepager(text, 'less')
969 import tempfile
970 filename = tempfile.mktemp()
971 open(filename, 'w').close()
972 try:
973 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
974 return lambda text: pipepager(text, 'more')
975 else:
976 return ttypager
977 finally:
978 os.unlink(filename)
980 def plain(text):
981 """Remove boldface formatting from text."""
982 return re.sub('.\b', '', text)
984 def pipepager(text, cmd):
985 """Page through text by feeding it to another program."""
986 pipe = os.popen(cmd, 'w')
987 try:
988 pipe.write(text)
989 pipe.close()
990 except IOError:
991 pass # Ignore broken pipes caused by quitting the pager program.
993 def tempfilepager(text, cmd):
994 """Page through text by invoking a program on a temporary file."""
995 import tempfile
996 filename = tempfile.mktemp()
997 file = open(filename, 'w')
998 file.write(text)
999 file.close()
1000 try:
1001 os.system(cmd + ' ' + filename)
1002 finally:
1003 os.unlink(filename)
1005 def ttypager(text):
1006 """Page through text on a text terminal."""
1007 lines = split(plain(text), '\n')
1008 try:
1009 import tty
1010 fd = sys.stdin.fileno()
1011 old = tty.tcgetattr(fd)
1012 tty.setcbreak(fd)
1013 getchar = lambda: sys.stdin.read(1)
1014 except (ImportError, AttributeError):
1015 tty = None
1016 getchar = lambda: sys.stdin.readline()[:-1][:1]
1018 try:
1019 r = inc = os.environ.get('LINES', 25) - 1
1020 sys.stdout.write(join(lines[:inc], '\n') + '\n')
1021 while lines[r:]:
1022 sys.stdout.write('-- more --')
1023 sys.stdout.flush()
1024 c = getchar()
1026 if c in ['q', 'Q']:
1027 sys.stdout.write('\r \r')
1028 break
1029 elif c in ['\r', '\n']:
1030 sys.stdout.write('\r \r' + lines[r] + '\n')
1031 r = r + 1
1032 continue
1033 if c in ['b', 'B', '\x1b']:
1034 r = r - inc - inc
1035 if r < 0: r = 0
1036 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1037 r = r + inc
1039 finally:
1040 if tty:
1041 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1043 def plainpager(text):
1044 """Simply print unformatted text. This is the ultimate fallback."""
1045 sys.stdout.write(plain(text))
1047 def describe(thing):
1048 """Produce a short description of the given thing."""
1049 if inspect.ismodule(thing):
1050 if thing.__name__ in sys.builtin_module_names:
1051 return 'built-in module ' + thing.__name__
1052 if hasattr(thing, '__path__'):
1053 return 'package ' + thing.__name__
1054 else:
1055 return 'module ' + thing.__name__
1056 if inspect.isbuiltin(thing):
1057 return 'built-in function ' + thing.__name__
1058 if inspect.isclass(thing):
1059 return 'class ' + thing.__name__
1060 if inspect.isfunction(thing):
1061 return 'function ' + thing.__name__
1062 if inspect.ismethod(thing):
1063 return 'method ' + thing.__name__
1064 if type(thing) is types.InstanceType:
1065 return 'instance of ' + thing.__class__.__name__
1066 return type(thing).__name__
1068 def locate(path, forceload=0):
1069 """Locate an object by name or dotted path, importing as necessary."""
1070 parts = split(path, '.')
1071 module, n = None, 0
1072 while n < len(parts):
1073 nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
1074 if nextmodule: module, n = nextmodule, n + 1
1075 else: break
1076 if module:
1077 object = module
1078 for part in parts[n:]:
1079 try: object = getattr(object, part)
1080 except AttributeError: return None
1081 return object
1082 else:
1083 import __builtin__
1084 if hasattr(__builtin__, path):
1085 return getattr(__builtin__, path)
1087 # --------------------------------------- interactive interpreter interface
1089 text = TextDoc()
1090 html = HTMLDoc()
1092 def doc(thing, title='Python Library Documentation: %s', forceload=0):
1093 """Display text documentation, given an object or a path to an object."""
1094 suffix, name = '', None
1095 if type(thing) is type(''):
1096 try:
1097 object = locate(thing, forceload)
1098 except ErrorDuringImport, value:
1099 print value
1100 return
1101 if not object:
1102 print 'no Python documentation found for %s' % repr(thing)
1103 return
1104 parts = split(thing, '.')
1105 if len(parts) > 1: suffix = ' in ' + join(parts[:-1], '.')
1106 name = parts[-1]
1107 thing = object
1109 desc = describe(thing)
1110 module = inspect.getmodule(thing)
1111 if not suffix and module and module is not thing:
1112 suffix = ' in module ' + module.__name__
1113 pager(title % (desc + suffix) + '\n\n' + text.document(thing, name))
1115 def writedoc(key, forceload=0):
1116 """Write HTML documentation to a file in the current directory."""
1117 try:
1118 object = locate(key, forceload)
1119 except ErrorDuringImport, value:
1120 print value
1121 else:
1122 if object:
1123 page = html.page(describe(object),
1124 html.document(object, object.__name__))
1125 file = open(key + '.html', 'w')
1126 file.write(page)
1127 file.close()
1128 print 'wrote', key + '.html'
1129 else:
1130 print 'no Python documentation found for %s' % repr(key)
1132 def writedocs(dir, pkgpath='', done=None):
1133 """Write out HTML documentation for all modules in a directory tree."""
1134 if done is None: done = {}
1135 for file in os.listdir(dir):
1136 path = os.path.join(dir, file)
1137 if ispackage(path):
1138 writedocs(path, pkgpath + file + '.', done)
1139 elif os.path.isfile(path):
1140 modname = inspect.getmodulename(path)
1141 if modname:
1142 modname = pkgpath + modname
1143 if not done.has_key(modname):
1144 done[modname] = 1
1145 writedoc(modname)
1147 class Helper:
1148 keywords = {
1149 'and': 'BOOLEAN',
1150 'assert': ('ref/assert', ''),
1151 'break': ('ref/break', 'while for'),
1152 'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1153 'continue': ('ref/continue', 'while for'),
1154 'def': ('ref/function', ''),
1155 'del': ('ref/del', 'BASICMETHODS'),
1156 'elif': 'if',
1157 'else': ('ref/if', 'while for'),
1158 'except': 'try',
1159 'exec': ('ref/exec', ''),
1160 'finally': 'try',
1161 'for': ('ref/for', 'break continue while'),
1162 'from': 'import',
1163 'global': ('ref/global', 'NAMESPACES'),
1164 'if': ('ref/if', 'TRUTHVALUE'),
1165 'import': ('ref/import', 'MODULES'),
1166 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1167 'is': 'COMPARISON',
1168 'lambda': ('ref/lambda', 'FUNCTIONS'),
1169 'not': 'BOOLEAN',
1170 'or': 'BOOLEAN',
1171 'pass': 'PASS',
1172 'print': ('ref/print', ''),
1173 'raise': ('ref/raise', 'EXCEPTIONS'),
1174 'return': ('ref/return', 'FUNCTIONS'),
1175 'try': ('ref/try', 'EXCEPTIONS'),
1176 'while': ('ref/while', 'break continue if TRUTHVALUE'),
1179 topics = {
1180 'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'),
1181 'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'),
1182 'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'),
1183 'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'),
1184 'UNICODE': ('ref/unicode', 'encodings unicode TYPES STRING'),
1185 'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1186 'INTEGER': ('ref/integers', 'int range'),
1187 'FLOAT': ('ref/floating', 'float math'),
1188 'COMPLEX': ('ref/imaginary', 'complex cmath'),
1189 'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
1190 'MAPPINGS': 'DICTIONARIES',
1191 'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'),
1192 'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'),
1193 'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'),
1194 'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'),
1195 'FRAMEOBJECTS': 'TYPES',
1196 'TRACEBACKS': 'TYPES',
1197 'NONE': ('lib/bltin-null-object', ''),
1198 'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'),
1199 'FILES': ('lib/bltin-file-objects', ''),
1200 'SPECIALATTRIBUTES': ('lib/specialattrs', ''),
1201 'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'),
1202 'MODULES': ('lib/typesmodules', 'import'),
1203 'PACKAGES': 'import',
1204 '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'),
1205 'OPERATORS': 'EXPRESSIONS',
1206 'PRECEDENCE': 'EXPRESSIONS',
1207 'OBJECTS': ('ref/objects', 'TYPES'),
1208 'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
1209 'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'),
1210 'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1211 'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'),
1212 'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'),
1213 'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'),
1214 'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'),
1215 'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'),
1216 'EXECUTION': ('ref/execframes', ''),
1217 'NAMESPACES': ('ref/execframes', 'global ASSIGNMENT DELETION'),
1218 'SCOPING': 'NAMESPACES',
1219 'FRAMES': 'NAMESPACES',
1220 'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'),
1221 'COERCIONS': 'CONVERSIONS',
1222 'CONVERSIONS': ('ref/conversions', ''),
1223 'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'),
1224 'SPECIALIDENTIFIERS': ('ref/id-classes', ''),
1225 'PRIVATENAMES': ('ref/atom-identifiers', ''),
1226 'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1227 'TUPLES': 'SEQUENCES',
1228 'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'),
1229 'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'),
1230 'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'),
1231 'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'),
1232 'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'),
1233 'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'),
1234 'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1235 'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'),
1236 'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'),
1237 'CALLS': ('ref/calls', 'EXPRESSIONS'),
1238 'POWER': ('ref/power', 'EXPRESSIONS'),
1239 'UNARY': ('ref/unary', 'EXPRESSIONS'),
1240 'BINARY': ('ref/binary', 'EXPRESSIONS'),
1241 'SHIFTING': ('ref/shifting', 'EXPRESSIONS'),
1242 'BITWISE': ('ref/bitwise', 'EXPRESSIONS'),
1243 'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'),
1244 'BOOLEAN': ('ref/lambda', 'EXPRESSIONS TRUTHVALUE'),
1245 'ASSERTION': 'assert',
1246 'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'),
1247 'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'),
1248 'DELETION': 'del',
1249 'PRINTING': 'print',
1250 'RETURNING': 'return',
1251 'IMPORTING': 'import',
1252 'CONDITIONAL': 'if',
1253 'LOOPING': ('ref/compound', 'for while break continue'),
1254 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'),
1255 'DEBUGGING': ('lib/module-pdb', 'pdb'),
1258 def __init__(self, input, output):
1259 self.input = input
1260 self.output = output
1261 self.docdir = None
1262 execdir = os.path.dirname(sys.executable)
1263 homedir = os.environ.get('PYTHONHOME')
1264 for dir in [os.environ.get('PYTHONDOCS'),
1265 homedir and os.path.join(homedir, 'doc'),
1266 os.path.join(execdir, 'doc'),
1267 '/usr/doc/python-docs-' + split(sys.version)[0],
1268 '/usr/doc/python-' + split(sys.version)[0],
1269 '/usr/doc/python-docs-' + sys.version[:3],
1270 '/usr/doc/python-' + sys.version[:3]]:
1271 if dir and os.path.isdir(os.path.join(dir, 'lib')):
1272 self.docdir = dir
1274 def __repr__(self):
1275 if inspect.stack()[1][3] == '?':
1276 self()
1277 return ''
1278 return '<pydoc.Helper instance>'
1280 def __call__(self, request=None):
1281 if request is not None:
1282 self.help(request)
1283 else:
1284 self.intro()
1285 self.interact()
1286 self.output.write('''
1287 You're now leaving help and returning to the Python interpreter.
1288 If you want to ask for help on a particular object directly from the
1289 interpreter, you can type "help(object)". Executing "help('string')"
1290 has the same effect as typing a particular string at the help> prompt.
1291 ''')
1293 def interact(self):
1294 self.output.write('\n')
1295 while 1:
1296 self.output.write('help> ')
1297 self.output.flush()
1298 try:
1299 request = self.input.readline()
1300 if not request: break
1301 except KeyboardInterrupt: break
1302 request = strip(replace(request, '"', '', "'", ''))
1303 if lower(request) in ['q', 'quit']: break
1304 self.help(request)
1306 def help(self, request):
1307 if type(request) is type(''):
1308 if request == 'help': self.intro()
1309 elif request == 'keywords': self.listkeywords()
1310 elif request == 'topics': self.listtopics()
1311 elif request == 'modules': self.listmodules()
1312 elif request[:8] == 'modules ':
1313 self.listmodules(split(request)[1])
1314 elif self.keywords.has_key(request): self.showtopic(request)
1315 elif self.topics.has_key(request): self.showtopic(request)
1316 elif request: doc(request, 'Help on %s:')
1317 elif isinstance(request, Helper): self()
1318 else: doc(request, 'Help on %s:')
1319 self.output.write('\n')
1321 def intro(self):
1322 self.output.write('''
1323 Welcome to Python %s! This is the online help utility.
1325 If this is your first time using Python, you should definitely check out
1326 the tutorial on the Internet at http://www.python.org/doc/tut/.
1328 Enter the name of any module, keyword, or topic to get help on writing
1329 Python programs and using Python modules. To quit this help utility and
1330 return to the interpreter, just type "quit".
1332 To get a list of available modules, keywords, or topics, type "modules",
1333 "keywords", or "topics". Each module also comes with a one-line summary
1334 of what it does; to list the modules whose summaries contain a given word
1335 such as "spam", type "modules spam".
1336 ''' % sys.version[:3])
1338 def list(self, items, columns=4, width=80):
1339 items = items[:]
1340 items.sort()
1341 colw = width / columns
1342 rows = (len(items) + columns - 1) / columns
1343 for row in range(rows):
1344 for col in range(columns):
1345 i = col * rows + row
1346 if i < len(items):
1347 self.output.write(items[i])
1348 if col < columns - 1:
1349 self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1350 self.output.write('\n')
1352 def listkeywords(self):
1353 self.output.write('''
1354 Here is a list of the Python keywords. Enter any keyword to get more help.
1356 ''')
1357 self.list(self.keywords.keys())
1359 def listtopics(self):
1360 self.output.write('''
1361 Here is a list of available topics. Enter any topic name to get more help.
1363 ''')
1364 self.list(self.topics.keys())
1366 def showtopic(self, topic):
1367 if not self.docdir:
1368 self.output.write('''
1369 Sorry, topic and keyword documentation is not available because the Python
1370 HTML documentation files could not be found. If you have installed them,
1371 please set the environment variable PYTHONDOCS to indicate their location.
1372 ''')
1373 return
1374 target = self.topics.get(topic, self.keywords.get(topic))
1375 if not target:
1376 self.output.write('no documentation found for %s\n' % repr(topic))
1377 return
1378 if type(target) is type(''):
1379 return self.showtopic(target)
1381 filename, xrefs = target
1382 filename = self.docdir + '/' + filename + '.html'
1383 try:
1384 file = open(filename)
1385 except:
1386 self.output.write('could not read docs from %s\n' % filename)
1387 return
1389 divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
1390 addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
1391 document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1392 file.close()
1394 import htmllib, formatter, StringIO
1395 buffer = StringIO.StringIO()
1396 parser = htmllib.HTMLParser(
1397 formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1398 parser.start_table = parser.do_p
1399 parser.end_table = lambda parser=parser: parser.do_p({})
1400 parser.start_tr = parser.do_br
1401 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1402 parser.feed(document)
1403 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
1404 pager(' ' + strip(buffer) + '\n')
1405 if xrefs:
1406 buffer = StringIO.StringIO()
1407 formatter.DumbWriter(buffer).send_flowing_data(
1408 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1409 self.output.write('\n%s\n' % buffer.getvalue())
1411 def listmodules(self, key=''):
1412 if key:
1413 self.output.write('''
1414 Here is a list of matching modules. Enter any module name to get more help.
1416 ''')
1417 apropos(key)
1418 else:
1419 self.output.write('''
1420 Please wait a moment while I gather a list of all available modules...
1422 ''')
1423 modules = {}
1424 def callback(path, modname, desc, modules=modules):
1425 if modname and modname[-9:] == '.__init__':
1426 modname = modname[:-9] + ' (package)'
1427 if find(modname, '.') < 0:
1428 modules[modname] = 1
1429 ModuleScanner().run(callback)
1430 self.list(modules.keys())
1431 self.output.write('''
1432 Enter any module name to get more help. Or, type "modules spam" to search
1433 for modules whose descriptions contain the word "spam".
1434 ''')
1436 help = Helper(sys.stdin, sys.stdout)
1438 class Scanner:
1439 """A generic tree iterator."""
1440 def __init__(self, roots, children, descendp):
1441 self.roots = roots[:]
1442 self.state = []
1443 self.children = children
1444 self.descendp = descendp
1446 def next(self):
1447 if not self.state:
1448 if not self.roots:
1449 return None
1450 root = self.roots.pop(0)
1451 self.state = [(root, self.children(root))]
1452 node, children = self.state[-1]
1453 if not children:
1454 self.state.pop()
1455 return self.next()
1456 child = children.pop(0)
1457 if self.descendp(child):
1458 self.state.append((child, self.children(child)))
1459 return child
1461 class ModuleScanner(Scanner):
1462 """An interruptible scanner that searches module synopses."""
1463 def __init__(self):
1464 roots = map(lambda dir: (dir, ''), pathdirs())
1465 Scanner.__init__(self, roots, self.submodules, self.isnewpackage)
1466 self.inodes = map(lambda (dir, pkg): os.stat(dir)[1], roots)
1468 def submodules(self, (dir, package)):
1469 children = []
1470 for file in os.listdir(dir):
1471 path = os.path.join(dir, file)
1472 if ispackage(path):
1473 children.append((path, package + (package and '.') + file))
1474 else:
1475 children.append((path, package))
1476 children.sort() # so that spam.py comes before spam.pyc or spam.pyo
1477 return children
1479 def isnewpackage(self, (dir, package)):
1480 inode = os.path.exists(dir) and os.stat(dir)[1]
1481 if not (os.path.islink(dir) and inode in self.inodes):
1482 self.inodes.append(inode) # detect circular symbolic links
1483 return ispackage(dir)
1485 def run(self, callback, key=None, completer=None):
1486 if key: key = lower(key)
1487 self.quit = 0
1488 seen = {}
1490 for modname in sys.builtin_module_names:
1491 if modname != '__main__':
1492 seen[modname] = 1
1493 if key is None:
1494 callback(None, modname, '')
1495 else:
1496 desc = split(__import__(modname).__doc__ or '', '\n')[0]
1497 if find(lower(modname + ' - ' + desc), key) >= 0:
1498 callback(None, modname, desc)
1500 while not self.quit:
1501 node = self.next()
1502 if not node: break
1503 path, package = node
1504 modname = inspect.getmodulename(path)
1505 if os.path.isfile(path) and modname:
1506 modname = package + (package and '.') + modname
1507 if not seen.has_key(modname):
1508 seen[modname] = 1 # if we see spam.py, skip spam.pyc
1509 if key is None:
1510 callback(path, modname, '')
1511 else:
1512 desc = synopsis(path) or ''
1513 if find(lower(modname + ' - ' + desc), key) >= 0:
1514 callback(path, modname, desc)
1515 if completer: completer()
1517 def apropos(key):
1518 """Print all the one-line module summaries that contain a substring."""
1519 def callback(path, modname, desc):
1520 if modname[-9:] == '.__init__':
1521 modname = modname[:-9] + ' (package)'
1522 print modname, desc and '- ' + desc
1523 try: import warnings
1524 except ImportError: pass
1525 else: warnings.filterwarnings('ignore') # ignore problems during import
1526 ModuleScanner().run(callback, key)
1528 # --------------------------------------------------- web browser interface
1530 def serve(port, callback=None, completer=None):
1531 import BaseHTTPServer, mimetools, select
1533 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1534 class Message(mimetools.Message):
1535 def __init__(self, fp, seekable=1):
1536 Message = self.__class__
1537 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1538 self.encodingheader = self.getheader('content-transfer-encoding')
1539 self.typeheader = self.getheader('content-type')
1540 self.parsetype()
1541 self.parseplist()
1543 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1544 def send_document(self, title, contents):
1545 try:
1546 self.send_response(200)
1547 self.send_header('Content-Type', 'text/html')
1548 self.end_headers()
1549 self.wfile.write(html.page(title, contents))
1550 except IOError: pass
1552 def do_GET(self):
1553 path = self.path
1554 if path[-5:] == '.html': path = path[:-5]
1555 if path[:1] == '/': path = path[1:]
1556 if path and path != '.':
1557 try:
1558 obj = locate(path, forceload=1)
1559 except ErrorDuringImport, value:
1560 self.send_document(path, html.escape(str(value)))
1561 return
1562 if obj:
1563 self.send_document(describe(obj), html.document(obj, path))
1564 else:
1565 self.send_document(path,
1566 'no Python documentation found for %s' % repr(path))
1567 else:
1568 heading = html.heading(
1569 '<big><big><strong>Python: Index of Modules</strong></big></big>',
1570 '#ffffff', '#7799ee')
1571 def bltinlink(name):
1572 return '<a href="%s.html">%s</a>' % (name, name)
1573 names = filter(lambda x: x != '__main__',
1574 sys.builtin_module_names)
1575 contents = html.multicolumn(names, bltinlink)
1576 indices = ['<p>' + html.bigsection(
1577 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1579 seen = {}
1580 for dir in pathdirs():
1581 indices.append(html.index(dir, seen))
1582 contents = heading + join(indices) + '''<p align=right>
1583 <small><small><font color="#909090" face="helvetica, arial"><strong>
1584 pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font></small></small>'''
1585 self.send_document('Index of Modules', contents)
1587 def log_message(self, *args): pass
1589 class DocServer(BaseHTTPServer.HTTPServer):
1590 def __init__(self, port, callback):
1591 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
1592 self.address = ('', port)
1593 self.url = 'http://%s:%d/' % (host, port)
1594 self.callback = callback
1595 self.base.__init__(self, self.address, self.handler)
1597 def serve_until_quit(self):
1598 import select
1599 self.quit = 0
1600 while not self.quit:
1601 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1602 if rd: self.handle_request()
1604 def server_activate(self):
1605 self.base.server_activate(self)
1606 if self.callback: self.callback(self)
1608 DocServer.base = BaseHTTPServer.HTTPServer
1609 DocServer.handler = DocHandler
1610 DocHandler.MessageClass = Message
1611 try:
1612 try:
1613 DocServer(port, callback).serve_until_quit()
1614 except (KeyboardInterrupt, select.error):
1615 pass
1616 finally:
1617 if completer: completer()
1619 # ----------------------------------------------------- graphical interface
1621 def gui():
1622 """Graphical interface (starts web server and pops up a control window)."""
1623 class GUI:
1624 def __init__(self, window, port=7464):
1625 self.window = window
1626 self.server = None
1627 self.scanner = None
1629 import Tkinter
1630 self.server_frm = Tkinter.Frame(window)
1631 self.title_lbl = Tkinter.Label(self.server_frm,
1632 text='Starting server...\n ')
1633 self.open_btn = Tkinter.Button(self.server_frm,
1634 text='open browser', command=self.open, state='disabled')
1635 self.quit_btn = Tkinter.Button(self.server_frm,
1636 text='quit serving', command=self.quit, state='disabled')
1638 self.search_frm = Tkinter.Frame(window)
1639 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
1640 self.search_ent = Tkinter.Entry(self.search_frm)
1641 self.search_ent.bind('<Return>', self.search)
1642 self.stop_btn = Tkinter.Button(self.search_frm,
1643 text='stop', pady=0, command=self.stop, state='disabled')
1644 if sys.platform == 'win32':
1645 # Trying to hide and show this button crashes under Windows.
1646 self.stop_btn.pack(side='right')
1648 self.window.title('pydoc')
1649 self.window.protocol('WM_DELETE_WINDOW', self.quit)
1650 self.title_lbl.pack(side='top', fill='x')
1651 self.open_btn.pack(side='left', fill='x', expand=1)
1652 self.quit_btn.pack(side='right', fill='x', expand=1)
1653 self.server_frm.pack(side='top', fill='x')
1655 self.search_lbl.pack(side='left')
1656 self.search_ent.pack(side='right', fill='x', expand=1)
1657 self.search_frm.pack(side='top', fill='x')
1658 self.search_ent.focus_set()
1660 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
1661 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
1662 self.result_lst.bind('<Button-1>', self.select)
1663 self.result_lst.bind('<Double-Button-1>', self.goto)
1664 self.result_scr = Tkinter.Scrollbar(window,
1665 orient='vertical', command=self.result_lst.yview)
1666 self.result_lst.config(yscrollcommand=self.result_scr.set)
1668 self.result_frm = Tkinter.Frame(window)
1669 self.goto_btn = Tkinter.Button(self.result_frm,
1670 text='go to selected', command=self.goto)
1671 self.hide_btn = Tkinter.Button(self.result_frm,
1672 text='hide results', command=self.hide)
1673 self.goto_btn.pack(side='left', fill='x', expand=1)
1674 self.hide_btn.pack(side='right', fill='x', expand=1)
1676 self.window.update()
1677 self.minwidth = self.window.winfo_width()
1678 self.minheight = self.window.winfo_height()
1679 self.bigminheight = (self.server_frm.winfo_reqheight() +
1680 self.search_frm.winfo_reqheight() +
1681 self.result_lst.winfo_reqheight() +
1682 self.result_frm.winfo_reqheight())
1683 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
1684 self.expanded = 0
1685 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1686 self.window.wm_minsize(self.minwidth, self.minheight)
1688 import threading
1689 threading.Thread(
1690 target=serve, args=(port, self.ready, self.quit)).start()
1692 def ready(self, server):
1693 self.server = server
1694 self.title_lbl.config(
1695 text='Python documentation server at\n' + server.url)
1696 self.open_btn.config(state='normal')
1697 self.quit_btn.config(state='normal')
1699 def open(self, event=None, url=None):
1700 url = url or self.server.url
1701 try:
1702 import webbrowser
1703 webbrowser.open(url)
1704 except ImportError: # pre-webbrowser.py compatibility
1705 if sys.platform == 'win32':
1706 os.system('start "%s"' % url)
1707 elif sys.platform == 'mac':
1708 try: import ic
1709 except ImportError: pass
1710 else: ic.launchurl(url)
1711 else:
1712 rc = os.system('netscape -remote "openURL(%s)" &' % url)
1713 if rc: os.system('netscape "%s" &' % url)
1715 def quit(self, event=None):
1716 if self.server:
1717 self.server.quit = 1
1718 self.window.quit()
1720 def search(self, event=None):
1721 key = self.search_ent.get()
1722 self.stop_btn.pack(side='right')
1723 self.stop_btn.config(state='normal')
1724 self.search_lbl.config(text='Searching for "%s"...' % key)
1725 self.search_ent.forget()
1726 self.search_lbl.pack(side='left')
1727 self.result_lst.delete(0, 'end')
1728 self.goto_btn.config(state='disabled')
1729 self.expand()
1731 import threading
1732 if self.scanner:
1733 self.scanner.quit = 1
1734 self.scanner = ModuleScanner()
1735 threading.Thread(target=self.scanner.run,
1736 args=(self.update, key, self.done)).start()
1738 def update(self, path, modname, desc):
1739 if modname[-9:] == '.__init__':
1740 modname = modname[:-9] + ' (package)'
1741 self.result_lst.insert('end',
1742 modname + ' - ' + (desc or '(no description)'))
1744 def stop(self, event=None):
1745 if self.scanner:
1746 self.scanner.quit = 1
1747 self.scanner = None
1749 def done(self):
1750 self.scanner = None
1751 self.search_lbl.config(text='Search for')
1752 self.search_lbl.pack(side='left')
1753 self.search_ent.pack(side='right', fill='x', expand=1)
1754 if sys.platform != 'win32': self.stop_btn.forget()
1755 self.stop_btn.config(state='disabled')
1757 def select(self, event=None):
1758 self.goto_btn.config(state='normal')
1760 def goto(self, event=None):
1761 selection = self.result_lst.curselection()
1762 if selection:
1763 modname = split(self.result_lst.get(selection[0]))[0]
1764 self.open(url=self.server.url + modname + '.html')
1766 def collapse(self):
1767 if not self.expanded: return
1768 self.result_frm.forget()
1769 self.result_scr.forget()
1770 self.result_lst.forget()
1771 self.bigwidth = self.window.winfo_width()
1772 self.bigheight = self.window.winfo_height()
1773 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
1774 self.window.wm_minsize(self.minwidth, self.minheight)
1775 self.expanded = 0
1777 def expand(self):
1778 if self.expanded: return
1779 self.result_frm.pack(side='bottom', fill='x')
1780 self.result_scr.pack(side='right', fill='y')
1781 self.result_lst.pack(side='top', fill='both', expand=1)
1782 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
1783 self.window.wm_minsize(self.minwidth, self.bigminheight)
1784 self.expanded = 1
1786 def hide(self, event=None):
1787 self.stop()
1788 self.collapse()
1790 import Tkinter
1791 try:
1792 gui = GUI(Tkinter.Tk())
1793 Tkinter.mainloop()
1794 except KeyboardInterrupt:
1795 pass
1797 # -------------------------------------------------- command-line interface
1799 def ispath(x):
1800 return type(x) is types.StringType and find(x, os.sep) >= 0
1802 def cli():
1803 """Command-line interface (looks at sys.argv to decide what to do)."""
1804 import getopt
1805 class BadUsage: pass
1807 # Scripts don't get the current directory in their path by default.
1808 scriptdir = os.path.dirname(sys.argv[0])
1809 if scriptdir in sys.path:
1810 sys.path.remove(scriptdir)
1811 sys.path.insert(0, '.')
1813 try:
1814 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
1815 writing = 0
1817 for opt, val in opts:
1818 if opt == '-g':
1819 gui()
1820 return
1821 if opt == '-k':
1822 apropos(val)
1823 return
1824 if opt == '-p':
1825 try:
1826 port = int(val)
1827 except ValueError:
1828 raise BadUsage
1829 def ready(server):
1830 print 'pydoc server ready at %s' % server.url
1831 def stopped():
1832 print 'pydoc server stopped'
1833 serve(port, ready, stopped)
1834 return
1835 if opt == '-w':
1836 writing = 1
1838 if not args: raise BadUsage
1839 for arg in args:
1840 try:
1841 if ispath(arg) and os.path.isfile(arg):
1842 arg = importfile(arg)
1843 if writing:
1844 if ispath(arg) and os.path.isdir(arg):
1845 writedocs(arg)
1846 else:
1847 writedoc(arg)
1848 else:
1849 doc(arg)
1850 except ErrorDuringImport, value:
1851 print value
1853 except (getopt.error, BadUsage):
1854 cmd = sys.argv[0]
1855 print """pydoc - the Python documentation tool
1857 %s <name> ...
1858 Show text documentation on something. <name> may be the name of a
1859 function, module, or package, or a dotted reference to a class or
1860 function within a module or module in a package. If <name> contains
1861 a '%s', it is used as the path to a Python source file to document.
1863 %s -k <keyword>
1864 Search for a keyword in the synopsis lines of all available modules.
1866 %s -p <port>
1867 Start an HTTP server on the given port on the local machine.
1869 %s -g
1870 Pop up a graphical interface for finding and serving documentation.
1872 %s -w <name> ...
1873 Write out the HTML documentation for a module to a file in the current
1874 directory. If <name> contains a '%s', it is treated as a filename; if
1875 it names a directory, documentation is written for all the contents.
1876 """ % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
1878 if __name__ == '__main__': cli()