fix typos, mostly in comments
[python.git] / Lib / pydoc.py
blob5bc92494a711fc4df778ea8a612a061d0c5cb41d
1 #!/usr/bin/env python
2 # -*- coding: Latin-1 -*-
3 """Generate Python documentation in HTML or text for interactive use.
5 In the Python interpreter, do "from pydoc import help" to provide online
6 help. Calling help(thing) on a Python object documents the object.
8 Or, at the shell command line outside of Python:
10 Run "pydoc <name>" to show documentation on something. <name> may be
11 the name of a function, module, package, or a dotted reference to a
12 class or function within a module or module in a package. If the
13 argument contains a path segment delimiter (e.g. slash on Unix,
14 backslash on Windows) it is treated as the path to a Python source file.
16 Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines
17 of all available modules.
19 Run "pydoc -p <port>" to start an HTTP server on a given port on the
20 local machine to generate documentation web pages.
22 For platforms without a command line, "pydoc -g" starts the HTTP server
23 and also pops up a little window for controlling it.
25 Run "pydoc -w <name>" to write out the HTML documentation for a module
26 to a file named "<name>.html".
28 Module docs for core modules are assumed to be in
30 http://www.python.org/doc/current/lib/
32 This can be overridden by setting the PYTHONDOCS environment variable
33 to a different URL or to a local directory containing the Library
34 Reference Manual pages.
35 """
37 __author__ = "Ka-Ping Yee <ping@lfw.org>"
38 __date__ = "26 February 2001"
40 __version__ = "$Revision$"
41 __credits__ = """Guido van Rossum, for an excellent programming language.
42 Tommy Burnette, the original creator of manpy.
43 Paul Prescod, for all his work on onlinehelp.
44 Richard Chamberlain, for the first implementation of textdoc.
45 """
47 # Known bugs that can't be fixed here:
48 # - imp.load_module() cannot be prevented from clobbering existing
49 # loaded modules, so calling synopsis() on a binary module file
50 # changes the contents of any existing module with the same name.
51 # - If the __file__ attribute on a module is a relative path and
52 # the current directory is changed with os.chdir(), an incorrect
53 # path will be displayed.
55 import sys, imp, os, re, types, inspect, __builtin__
56 from repr import Repr
57 from string import expandtabs, find, join, lower, split, strip, rfind, rstrip
58 from collections import deque
60 # --------------------------------------------------------- common routines
62 def pathdirs():
63 """Convert sys.path into a list of absolute, existing, unique paths."""
64 dirs = []
65 normdirs = []
66 for dir in sys.path:
67 dir = os.path.abspath(dir or '.')
68 normdir = os.path.normcase(dir)
69 if normdir not in normdirs and os.path.isdir(dir):
70 dirs.append(dir)
71 normdirs.append(normdir)
72 return dirs
74 def getdoc(object):
75 """Get the doc string or comments for an object."""
76 result = inspect.getdoc(object) or inspect.getcomments(object)
77 return result and re.sub('^ *\n', '', rstrip(result)) or ''
79 def splitdoc(doc):
80 """Split a doc string into a synopsis line (if any) and the rest."""
81 lines = split(strip(doc), '\n')
82 if len(lines) == 1:
83 return lines[0], ''
84 elif len(lines) >= 2 and not rstrip(lines[1]):
85 return lines[0], join(lines[2:], '\n')
86 return '', join(lines, '\n')
88 def classname(object, modname):
89 """Get a class name and qualify it with a module name if necessary."""
90 name = object.__name__
91 if object.__module__ != modname:
92 name = object.__module__ + '.' + name
93 return name
95 def isdata(object):
96 """Check if an object is of a type that probably means it's data."""
97 return not (inspect.ismodule(object) or inspect.isclass(object) or
98 inspect.isroutine(object) or inspect.isframe(object) or
99 inspect.istraceback(object) or inspect.iscode(object))
101 def replace(text, *pairs):
102 """Do a series of global replacements on a string."""
103 while pairs:
104 text = join(split(text, pairs[0]), pairs[1])
105 pairs = pairs[2:]
106 return text
108 def cram(text, maxlen):
109 """Omit part of a string if needed to make it fit in a maximum length."""
110 if len(text) > maxlen:
111 pre = max(0, (maxlen-3)//2)
112 post = max(0, maxlen-3-pre)
113 return text[:pre] + '...' + text[len(text)-post:]
114 return text
116 _re_stripid = re.compile(r' at 0x[0-9a-f]{6,16}(>+)$', re.IGNORECASE)
117 def stripid(text):
118 """Remove the hexadecimal id from a Python object representation."""
119 # The behaviour of %p is implementation-dependent in terms of case.
120 if _re_stripid.search(repr(Exception)):
121 return _re_stripid.sub(r'\1', text)
122 return text
124 def _is_some_method(obj):
125 return inspect.ismethod(obj) or inspect.ismethoddescriptor(obj)
127 def allmethods(cl):
128 methods = {}
129 for key, value in inspect.getmembers(cl, _is_some_method):
130 methods[key] = 1
131 for base in cl.__bases__:
132 methods.update(allmethods(base)) # all your base are belong to us
133 for key in methods.keys():
134 methods[key] = getattr(cl, key)
135 return methods
137 def _split_list(s, predicate):
138 """Split sequence s via predicate, and return pair ([true], [false]).
140 The return value is a 2-tuple of lists,
141 ([x for x in s if predicate(x)],
142 [x for x in s if not predicate(x)])
145 yes = []
146 no = []
147 for x in s:
148 if predicate(x):
149 yes.append(x)
150 else:
151 no.append(x)
152 return yes, no
154 def visiblename(name, all=None):
155 """Decide whether to show documentation on a variable."""
156 # Certain special names are redundant.
157 if name in ('__builtins__', '__doc__', '__file__', '__path__',
158 '__module__', '__name__', '__slots__'): return 0
159 # Private names are hidden, but special names are displayed.
160 if name.startswith('__') and name.endswith('__'): return 1
161 if all is not None:
162 # only document that which the programmer exported in __all__
163 return name in all
164 else:
165 return not name.startswith('_')
167 def classify_class_attrs(object):
168 """Wrap inspect.classify_class_attrs, with fixup for data descriptors."""
169 def fixup((name, kind, cls, value)):
170 if inspect.isdatadescriptor(value):
171 kind = 'data descriptor'
172 return name, kind, cls, value
173 return map(fixup, inspect.classify_class_attrs(object))
175 # ----------------------------------------------------- module manipulation
177 def ispackage(path):
178 """Guess whether a path refers to a package directory."""
179 if os.path.isdir(path):
180 for ext in ('.py', '.pyc', '.pyo'):
181 if os.path.isfile(os.path.join(path, '__init__' + ext)):
182 return True
183 return False
185 def synopsis(filename, cache={}):
186 """Get the one-line summary out of a module file."""
187 mtime = os.stat(filename).st_mtime
188 lastupdate, result = cache.get(filename, (0, None))
189 if lastupdate < mtime:
190 info = inspect.getmoduleinfo(filename)
191 file = open(filename)
192 if info and 'b' in info[2]: # binary modules have to be imported
193 try: module = imp.load_module('__temp__', file, filename, info[1:])
194 except: return None
195 result = split(module.__doc__ or '', '\n')[0]
196 del sys.modules['__temp__']
197 else: # text modules can be directly examined
198 line = file.readline()
199 while line[:1] == '#' or not strip(line):
200 line = file.readline()
201 if not line: break
202 line = strip(line)
203 if line[:4] == 'r"""': line = line[1:]
204 if line[:3] == '"""':
205 line = line[3:]
206 if line[-1:] == '\\': line = line[:-1]
207 while not strip(line):
208 line = file.readline()
209 if not line: break
210 result = strip(split(line, '"""')[0])
211 else: result = None
212 file.close()
213 cache[filename] = (mtime, result)
214 return result
216 class ErrorDuringImport(Exception):
217 """Errors that occurred while trying to import something to document it."""
218 def __init__(self, filename, (exc, value, tb)):
219 self.filename = filename
220 self.exc = exc
221 self.value = value
222 self.tb = tb
224 def __str__(self):
225 exc = self.exc
226 if type(exc) is types.ClassType:
227 exc = exc.__name__
228 return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
230 def importfile(path):
231 """Import a Python source file or compiled file given its path."""
232 magic = imp.get_magic()
233 file = open(path, 'r')
234 if file.read(len(magic)) == magic:
235 kind = imp.PY_COMPILED
236 else:
237 kind = imp.PY_SOURCE
238 file.close()
239 filename = os.path.basename(path)
240 name, ext = os.path.splitext(filename)
241 file = open(path, 'r')
242 try:
243 module = imp.load_module(name, file, path, (ext, 'r', kind))
244 except:
245 raise ErrorDuringImport(path, sys.exc_info())
246 file.close()
247 return module
249 def safeimport(path, forceload=0, cache={}):
250 """Import a module; handle errors; return None if the module isn't found.
252 If the module *is* found but an exception occurs, it's wrapped in an
253 ErrorDuringImport exception and reraised. Unlike __import__, if a
254 package path is specified, the module at the end of the path is returned,
255 not the package at the beginning. If the optional 'forceload' argument
256 is 1, we reload the module from disk (unless it's a dynamic extension)."""
257 if forceload and path in sys.modules:
258 # This is the only way to be sure. Checking the mtime of the file
259 # isn't good enough (e.g. what if the module contains a class that
260 # inherits from another module that has changed?).
261 if path not in sys.builtin_module_names:
262 # Python never loads a dynamic extension a second time from the
263 # same path, even if the file is changed or missing. Deleting
264 # the entry in sys.modules doesn't help for dynamic extensions,
265 # so we're not even going to try to keep them up to date.
266 info = inspect.getmoduleinfo(sys.modules[path].__file__)
267 if info[3] != imp.C_EXTENSION:
268 cache[path] = sys.modules[path] # prevent module from clearing
269 del sys.modules[path]
270 try:
271 module = __import__(path)
272 except:
273 # Did the error occur before or after the module was found?
274 (exc, value, tb) = info = sys.exc_info()
275 if path in sys.modules:
276 # An error occurred while executing the imported module.
277 raise ErrorDuringImport(sys.modules[path].__file__, info)
278 elif exc is SyntaxError:
279 # A SyntaxError occurred before we could execute the module.
280 raise ErrorDuringImport(value.filename, info)
281 elif exc is ImportError and \
282 split(lower(str(value)))[:2] == ['no', 'module']:
283 # The module was not found.
284 return None
285 else:
286 # Some other error occurred during the importing process.
287 raise ErrorDuringImport(path, sys.exc_info())
288 for part in split(path, '.')[1:]:
289 try: module = getattr(module, part)
290 except AttributeError: return None
291 return module
293 # ---------------------------------------------------- formatter base class
295 class Doc:
296 def document(self, object, name=None, *args):
297 """Generate documentation for an object."""
298 args = (object, name) + args
299 # 'try' clause is to attempt to handle the possibility that inspect
300 # identifies something in a way that pydoc itself has issues handling;
301 # think 'super' and how it is a descriptor (which raises the exception
302 # by lacking a __name__ attribute) and an instance.
303 try:
304 if inspect.ismodule(object): return self.docmodule(*args)
305 if inspect.isclass(object): return self.docclass(*args)
306 if inspect.isroutine(object): return self.docroutine(*args)
307 except AttributeError:
308 pass
309 if isinstance(object, property): return self.docproperty(*args)
310 return self.docother(*args)
312 def fail(self, object, name=None, *args):
313 """Raise an exception for unimplemented types."""
314 message = "don't know how to document object%s of type %s" % (
315 name and ' ' + repr(name), type(object).__name__)
316 raise TypeError, message
318 docmodule = docclass = docroutine = docother = fail
320 def getdocloc(self, object):
321 """Return the location of module docs or None"""
323 try:
324 file = inspect.getabsfile(object)
325 except TypeError:
326 file = '(built-in)'
328 docloc = os.environ.get("PYTHONDOCS",
329 "http://www.python.org/doc/current/lib")
330 basedir = os.path.join(sys.exec_prefix, "lib",
331 "python"+sys.version[0:3])
332 if (isinstance(object, type(os)) and
333 (object.__name__ in ('errno', 'exceptions', 'gc', 'imp',
334 'marshal', 'posix', 'signal', 'sys',
335 'thread', 'zipimport') or
336 (file.startswith(basedir) and
337 not file.startswith(os.path.join(basedir, 'site-packages'))))):
338 htmlfile = "module-%s.html" % object.__name__
339 if docloc.startswith("http://"):
340 docloc = "%s/%s" % (docloc.rstrip("/"), htmlfile)
341 else:
342 docloc = os.path.join(docloc, htmlfile)
343 else:
344 docloc = None
345 return docloc
347 # -------------------------------------------- HTML documentation generator
349 class HTMLRepr(Repr):
350 """Class for safely making an HTML representation of a Python object."""
351 def __init__(self):
352 Repr.__init__(self)
353 self.maxlist = self.maxtuple = 20
354 self.maxdict = 10
355 self.maxstring = self.maxother = 100
357 def escape(self, text):
358 return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')
360 def repr(self, object):
361 return Repr.repr(self, object)
363 def repr1(self, x, level):
364 if hasattr(type(x), '__name__'):
365 methodname = 'repr_' + join(split(type(x).__name__), '_')
366 if hasattr(self, methodname):
367 return getattr(self, methodname)(x, level)
368 return self.escape(cram(stripid(repr(x)), self.maxother))
370 def repr_string(self, x, level):
371 test = cram(x, self.maxstring)
372 testrepr = repr(test)
373 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
374 # Backslashes are only literal in the string and are never
375 # needed to make any special characters, so show a raw string.
376 return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
377 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
378 r'<font color="#c040c0">\1</font>',
379 self.escape(testrepr))
381 repr_str = repr_string
383 def repr_instance(self, x, level):
384 try:
385 return self.escape(cram(stripid(repr(x)), self.maxstring))
386 except:
387 return self.escape('<%s instance>' % x.__class__.__name__)
389 repr_unicode = repr_string
391 class HTMLDoc(Doc):
392 """Formatter class for HTML documentation."""
394 # ------------------------------------------- HTML formatting utilities
396 _repr_instance = HTMLRepr()
397 repr = _repr_instance.repr
398 escape = _repr_instance.escape
400 def page(self, title, contents):
401 """Format an HTML page."""
402 return '''
403 <!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
404 <html><head><title>Python: %s</title>
405 </head><body bgcolor="#f0f0f8">
407 </body></html>''' % (title, contents)
409 def heading(self, title, fgcol, bgcol, extras=''):
410 """Format a page heading."""
411 return '''
412 <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading">
413 <tr bgcolor="%s">
414 <td valign=bottom>&nbsp;<br>
415 <font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
416 ><td align=right valign=bottom
417 ><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
418 ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
420 def section(self, title, fgcol, bgcol, contents, width=6,
421 prelude='', marginalia=None, gap='&nbsp;'):
422 """Format a section with a heading."""
423 if marginalia is None:
424 marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
425 result = '''<p>
426 <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
427 <tr bgcolor="%s">
428 <td colspan=3 valign=bottom>&nbsp;<br>
429 <font color="%s" face="helvetica, arial">%s</font></td></tr>
430 ''' % (bgcol, fgcol, title)
431 if prelude:
432 result = result + '''
433 <tr bgcolor="%s"><td rowspan=2>%s</td>
434 <td colspan=2>%s</td></tr>
435 <tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
436 else:
437 result = result + '''
438 <tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
440 return result + '\n<td width="100%%">%s</td></tr></table>' % contents
442 def bigsection(self, title, *args):
443 """Format a section with a big heading."""
444 title = '<big><strong>%s</strong></big>' % title
445 return self.section(title, *args)
447 def preformat(self, text):
448 """Format literal preformatted text."""
449 text = self.escape(expandtabs(text))
450 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
451 ' ', '&nbsp;', '\n', '<br>\n')
453 def multicolumn(self, list, format, cols=4):
454 """Format a list of items into a multi-column list."""
455 result = ''
456 rows = (len(list)+cols-1)/cols
457 for col in range(cols):
458 result = result + '<td width="%d%%" valign=top>' % (100/cols)
459 for i in range(rows*col, rows*col+rows):
460 if i < len(list):
461 result = result + format(list[i]) + '<br>\n'
462 result = result + '</td>'
463 return '<table width="100%%" summary="list"><tr>%s</tr></table>' % result
465 def grey(self, text): return '<font color="#909090">%s</font>' % text
467 def namelink(self, name, *dicts):
468 """Make a link for an identifier, given name-to-URL mappings."""
469 for dict in dicts:
470 if name in dict:
471 return '<a href="%s">%s</a>' % (dict[name], name)
472 return name
474 def classlink(self, object, modname):
475 """Make a link for a class."""
476 name, module = object.__name__, sys.modules.get(object.__module__)
477 if hasattr(module, name) and getattr(module, name) is object:
478 return '<a href="%s.html#%s">%s</a>' % (
479 module.__name__, name, classname(object, modname))
480 return classname(object, modname)
482 def modulelink(self, object):
483 """Make a link for a module."""
484 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
486 def modpkglink(self, (name, path, ispackage, shadowed)):
487 """Make a link for a module or package to display in an index."""
488 if shadowed:
489 return self.grey(name)
490 if path:
491 url = '%s.%s.html' % (path, name)
492 else:
493 url = '%s.html' % name
494 if ispackage:
495 text = '<strong>%s</strong>&nbsp;(package)' % name
496 else:
497 text = name
498 return '<a href="%s">%s</a>' % (url, text)
500 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
501 """Mark up some plain text, given a context of symbols to look for.
502 Each context dictionary maps object names to anchor names."""
503 escape = escape or self.escape
504 results = []
505 here = 0
506 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
507 r'RFC[- ]?(\d+)|'
508 r'PEP[- ]?(\d+)|'
509 r'(self\.)?(\w+))')
510 while True:
511 match = pattern.search(text, here)
512 if not match: break
513 start, end = match.span()
514 results.append(escape(text[here:start]))
516 all, scheme, rfc, pep, selfdot, name = match.groups()
517 if scheme:
518 url = escape(all).replace('"', '&quot;')
519 results.append('<a href="%s">%s</a>' % (url, url))
520 elif rfc:
521 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
522 results.append('<a href="%s">%s</a>' % (url, escape(all)))
523 elif pep:
524 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
525 results.append('<a href="%s">%s</a>' % (url, escape(all)))
526 elif text[end:end+1] == '(':
527 results.append(self.namelink(name, methods, funcs, classes))
528 elif selfdot:
529 results.append('self.<strong>%s</strong>' % name)
530 else:
531 results.append(self.namelink(name, classes))
532 here = end
533 results.append(escape(text[here:]))
534 return join(results, '')
536 # ---------------------------------------------- type-specific routines
538 def formattree(self, tree, modname, parent=None):
539 """Produce HTML for a class tree as given by inspect.getclasstree()."""
540 result = ''
541 for entry in tree:
542 if type(entry) is type(()):
543 c, bases = entry
544 result = result + '<dt><font face="helvetica, arial">'
545 result = result + self.classlink(c, modname)
546 if bases and bases != (parent,):
547 parents = []
548 for base in bases:
549 parents.append(self.classlink(base, modname))
550 result = result + '(' + join(parents, ', ') + ')'
551 result = result + '\n</font></dt>'
552 elif type(entry) is type([]):
553 result = result + '<dd>\n%s</dd>\n' % self.formattree(
554 entry, modname, c)
555 return '<dl>\n%s</dl>\n' % result
557 def docmodule(self, object, name=None, mod=None, *ignored):
558 """Produce HTML documentation for a module object."""
559 name = object.__name__ # ignore the passed-in name
560 try:
561 all = object.__all__
562 except AttributeError:
563 all = None
564 parts = split(name, '.')
565 links = []
566 for i in range(len(parts)-1):
567 links.append(
568 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
569 (join(parts[:i+1], '.'), parts[i]))
570 linkedname = join(links + parts[-1:], '.')
571 head = '<big><big><strong>%s</strong></big></big>' % linkedname
572 try:
573 path = inspect.getabsfile(object)
574 url = path
575 if sys.platform == 'win32':
576 import nturl2path
577 url = nturl2path.pathname2url(path)
578 filelink = '<a href="file:%s">%s</a>' % (url, path)
579 except TypeError:
580 filelink = '(built-in)'
581 info = []
582 if hasattr(object, '__version__'):
583 version = str(object.__version__)
584 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
585 version = strip(version[11:-1])
586 info.append('version %s' % self.escape(version))
587 if hasattr(object, '__date__'):
588 info.append(self.escape(str(object.__date__)))
589 if info:
590 head = head + ' (%s)' % join(info, ', ')
591 docloc = self.getdocloc(object)
592 if docloc is not None:
593 docloc = '<br><a href="%(docloc)s">Module Docs</a>' % locals()
594 else:
595 docloc = ''
596 result = self.heading(
597 head, '#ffffff', '#7799ee',
598 '<a href=".">index</a><br>' + filelink + docloc)
600 modules = inspect.getmembers(object, inspect.ismodule)
602 classes, cdict = [], {}
603 for key, value in inspect.getmembers(object, inspect.isclass):
604 # if __all__ exists, believe it. Otherwise use old heuristic.
605 if (all is not None or
606 (inspect.getmodule(value) or object) is object):
607 if visiblename(key, all):
608 classes.append((key, value))
609 cdict[key] = cdict[value] = '#' + key
610 for key, value in classes:
611 for base in value.__bases__:
612 key, modname = base.__name__, base.__module__
613 module = sys.modules.get(modname)
614 if modname != name and module and hasattr(module, key):
615 if getattr(module, key) is base:
616 if not key in cdict:
617 cdict[key] = cdict[base] = modname + '.html#' + key
618 funcs, fdict = [], {}
619 for key, value in inspect.getmembers(object, inspect.isroutine):
620 # if __all__ exists, believe it. Otherwise use old heuristic.
621 if (all is not None or
622 inspect.isbuiltin(value) or inspect.getmodule(value) is object):
623 if visiblename(key, all):
624 funcs.append((key, value))
625 fdict[key] = '#-' + key
626 if inspect.isfunction(value): fdict[value] = fdict[key]
627 data = []
628 for key, value in inspect.getmembers(object, isdata):
629 if visiblename(key, all):
630 data.append((key, value))
632 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
633 doc = doc and '<tt>%s</tt>' % doc
634 result = result + '<p>%s</p>\n' % doc
636 if hasattr(object, '__path__'):
637 modpkgs = []
638 modnames = []
639 for file in os.listdir(object.__path__[0]):
640 path = os.path.join(object.__path__[0], file)
641 modname = inspect.getmodulename(file)
642 if modname != '__init__':
643 if modname and modname not in modnames:
644 modpkgs.append((modname, name, 0, 0))
645 modnames.append(modname)
646 elif ispackage(path):
647 modpkgs.append((file, name, 1, 0))
648 modpkgs.sort()
649 contents = self.multicolumn(modpkgs, self.modpkglink)
650 result = result + self.bigsection(
651 'Package Contents', '#ffffff', '#aa55cc', contents)
652 elif modules:
653 contents = self.multicolumn(
654 modules, lambda (key, value), s=self: s.modulelink(value))
655 result = result + self.bigsection(
656 'Modules', '#fffff', '#aa55cc', contents)
658 if classes:
659 classlist = map(lambda (key, value): value, classes)
660 contents = [
661 self.formattree(inspect.getclasstree(classlist, 1), name)]
662 for key, value in classes:
663 contents.append(self.document(value, key, name, fdict, cdict))
664 result = result + self.bigsection(
665 'Classes', '#ffffff', '#ee77aa', join(contents))
666 if funcs:
667 contents = []
668 for key, value in funcs:
669 contents.append(self.document(value, key, name, fdict, cdict))
670 result = result + self.bigsection(
671 'Functions', '#ffffff', '#eeaa77', join(contents))
672 if data:
673 contents = []
674 for key, value in data:
675 contents.append(self.document(value, key))
676 result = result + self.bigsection(
677 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))
678 if hasattr(object, '__author__'):
679 contents = self.markup(str(object.__author__), self.preformat)
680 result = result + self.bigsection(
681 'Author', '#ffffff', '#7799ee', contents)
682 if hasattr(object, '__credits__'):
683 contents = self.markup(str(object.__credits__), self.preformat)
684 result = result + self.bigsection(
685 'Credits', '#ffffff', '#7799ee', contents)
687 return result
689 def docclass(self, object, name=None, mod=None, funcs={}, classes={},
690 *ignored):
691 """Produce HTML documentation for a class object."""
692 realname = object.__name__
693 name = name or realname
694 bases = object.__bases__
696 contents = []
697 push = contents.append
699 # Cute little class to pump out a horizontal rule between sections.
700 class HorizontalRule:
701 def __init__(self):
702 self.needone = 0
703 def maybe(self):
704 if self.needone:
705 push('<hr>\n')
706 self.needone = 1
707 hr = HorizontalRule()
709 # List the mro, if non-trivial.
710 mro = deque(inspect.getmro(object))
711 if len(mro) > 2:
712 hr.maybe()
713 push('<dl><dt>Method resolution order:</dt>\n')
714 for base in mro:
715 push('<dd>%s</dd>\n' % self.classlink(base,
716 object.__module__))
717 push('</dl>\n')
719 def spill(msg, attrs, predicate):
720 ok, attrs = _split_list(attrs, predicate)
721 if ok:
722 hr.maybe()
723 push(msg)
724 for name, kind, homecls, value in ok:
725 push(self.document(getattr(object, name), name, mod,
726 funcs, classes, mdict, object))
727 push('\n')
728 return attrs
730 def spilldescriptors(msg, attrs, predicate):
731 ok, attrs = _split_list(attrs, predicate)
732 if ok:
733 hr.maybe()
734 push(msg)
735 for name, kind, homecls, value in ok:
736 push(self._docdescriptor(name, value, mod))
737 return attrs
739 def spilldata(msg, attrs, predicate):
740 ok, attrs = _split_list(attrs, predicate)
741 if ok:
742 hr.maybe()
743 push(msg)
744 for name, kind, homecls, value in ok:
745 base = self.docother(getattr(object, name), name, mod)
746 if callable(value) or inspect.isdatadescriptor(value):
747 doc = getattr(value, "__doc__", None)
748 else:
749 doc = None
750 if doc is None:
751 push('<dl><dt>%s</dl>\n' % base)
752 else:
753 doc = self.markup(getdoc(value), self.preformat,
754 funcs, classes, mdict)
755 doc = '<dd><tt>%s</tt>' % doc
756 push('<dl><dt>%s%s</dl>\n' % (base, doc))
757 push('\n')
758 return attrs
760 attrs = filter(lambda (name, kind, cls, value): visiblename(name),
761 classify_class_attrs(object))
762 mdict = {}
763 for key, kind, homecls, value in attrs:
764 mdict[key] = anchor = '#' + name + '-' + key
765 value = getattr(object, key)
766 try:
767 # The value may not be hashable (e.g., a data attr with
768 # a dict or list value).
769 mdict[value] = anchor
770 except TypeError:
771 pass
773 while attrs:
774 if mro:
775 thisclass = mro.popleft()
776 else:
777 thisclass = attrs[0][2]
778 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
780 if thisclass is __builtin__.object:
781 attrs = inherited
782 continue
783 elif thisclass is object:
784 tag = 'defined here'
785 else:
786 tag = 'inherited from %s' % self.classlink(thisclass,
787 object.__module__)
788 tag += ':<br>\n'
790 # Sort attrs by name.
791 attrs.sort(key=lambda t: t[0])
793 # Pump out the attrs, segregated by kind.
794 attrs = spill('Methods %s' % tag, attrs,
795 lambda t: t[1] == 'method')
796 attrs = spill('Class methods %s' % tag, attrs,
797 lambda t: t[1] == 'class method')
798 attrs = spill('Static methods %s' % tag, attrs,
799 lambda t: t[1] == 'static method')
800 attrs = spilldescriptors('Data descriptors %s' % tag, attrs,
801 lambda t: t[1] == 'data descriptor')
802 attrs = spilldata('Data and other attributes %s' % tag, attrs,
803 lambda t: t[1] == 'data')
804 assert attrs == []
805 attrs = inherited
807 contents = ''.join(contents)
809 if name == realname:
810 title = '<a name="%s">class <strong>%s</strong></a>' % (
811 name, realname)
812 else:
813 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
814 name, name, realname)
815 if bases:
816 parents = []
817 for base in bases:
818 parents.append(self.classlink(base, object.__module__))
819 title = title + '(%s)' % join(parents, ', ')
820 doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict)
821 doc = doc and '<tt>%s<br>&nbsp;</tt>' % doc
823 return self.section(title, '#000000', '#ffc8d8', contents, 3, doc)
825 def formatvalue(self, object):
826 """Format an argument default value as text."""
827 return self.grey('=' + self.repr(object))
829 def docroutine(self, object, name=None, mod=None,
830 funcs={}, classes={}, methods={}, cl=None):
831 """Produce HTML documentation for a function or method object."""
832 realname = object.__name__
833 name = name or realname
834 anchor = (cl and cl.__name__ or '') + '-' + name
835 note = ''
836 skipdocs = 0
837 if inspect.ismethod(object):
838 imclass = object.im_class
839 if cl:
840 if imclass is not cl:
841 note = ' from ' + self.classlink(imclass, mod)
842 else:
843 if object.im_self:
844 note = ' method of %s instance' % self.classlink(
845 object.im_self.__class__, mod)
846 else:
847 note = ' unbound %s method' % self.classlink(imclass,mod)
848 object = object.im_func
850 if name == realname:
851 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
852 else:
853 if (cl and realname in cl.__dict__ and
854 cl.__dict__[realname] is object):
855 reallink = '<a href="#%s">%s</a>' % (
856 cl.__name__ + '-' + realname, realname)
857 skipdocs = 1
858 else:
859 reallink = realname
860 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
861 anchor, name, reallink)
862 if inspect.isfunction(object):
863 args, varargs, varkw, defaults = inspect.getargspec(object)
864 argspec = inspect.formatargspec(
865 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
866 if realname == '<lambda>':
867 title = '<strong>%s</strong> <em>lambda</em> ' % name
868 argspec = argspec[1:-1] # remove parentheses
869 else:
870 argspec = '(...)'
872 decl = title + argspec + (note and self.grey(
873 '<font face="helvetica, arial">%s</font>' % note))
875 if skipdocs:
876 return '<dl><dt>%s</dt></dl>\n' % decl
877 else:
878 doc = self.markup(
879 getdoc(object), self.preformat, funcs, classes, methods)
880 doc = doc and '<dd><tt>%s</tt></dd>' % doc
881 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
883 def _docdescriptor(self, name, value, mod):
884 results = []
885 push = results.append
887 if name:
888 push('<dl><dt><strong>%s</strong></dt>\n' % name)
889 if value.__doc__ is not None:
890 doc = self.markup(getdoc(value), self.preformat)
891 push('<dd><tt>%s</tt></dd>\n' % doc)
892 push('</dl>\n')
894 return ''.join(results)
896 def docproperty(self, object, name=None, mod=None, cl=None):
897 """Produce html documentation for a property."""
898 return self._docdescriptor(name, object, mod)
900 def docother(self, object, name=None, mod=None, *ignored):
901 """Produce HTML documentation for a data object."""
902 lhs = name and '<strong>%s</strong> = ' % name or ''
903 return lhs + self.repr(object)
905 def index(self, dir, shadowed=None):
906 """Generate an HTML index for a directory of modules."""
907 modpkgs = []
908 if shadowed is None: shadowed = {}
909 seen = {}
910 files = os.listdir(dir)
912 def found(name, ispackage,
913 modpkgs=modpkgs, shadowed=shadowed, seen=seen):
914 if name not in seen:
915 modpkgs.append((name, '', ispackage, name in shadowed))
916 seen[name] = 1
917 shadowed[name] = 1
919 # Package spam/__init__.py takes precedence over module spam.py.
920 for file in files:
921 path = os.path.join(dir, file)
922 if ispackage(path): found(file, 1)
923 for file in files:
924 path = os.path.join(dir, file)
925 if os.path.isfile(path):
926 modname = inspect.getmodulename(file)
927 if modname: found(modname, 0)
929 modpkgs.sort()
930 contents = self.multicolumn(modpkgs, self.modpkglink)
931 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
933 # -------------------------------------------- text documentation generator
935 class TextRepr(Repr):
936 """Class for safely making a text representation of a Python object."""
937 def __init__(self):
938 Repr.__init__(self)
939 self.maxlist = self.maxtuple = 20
940 self.maxdict = 10
941 self.maxstring = self.maxother = 100
943 def repr1(self, x, level):
944 if hasattr(type(x), '__name__'):
945 methodname = 'repr_' + join(split(type(x).__name__), '_')
946 if hasattr(self, methodname):
947 return getattr(self, methodname)(x, level)
948 return cram(stripid(repr(x)), self.maxother)
950 def repr_string(self, x, level):
951 test = cram(x, self.maxstring)
952 testrepr = repr(test)
953 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
954 # Backslashes are only literal in the string and are never
955 # needed to make any special characters, so show a raw string.
956 return 'r' + testrepr[0] + test + testrepr[0]
957 return testrepr
959 repr_str = repr_string
961 def repr_instance(self, x, level):
962 try:
963 return cram(stripid(repr(x)), self.maxstring)
964 except:
965 return '<%s instance>' % x.__class__.__name__
967 class TextDoc(Doc):
968 """Formatter class for text documentation."""
970 # ------------------------------------------- text formatting utilities
972 _repr_instance = TextRepr()
973 repr = _repr_instance.repr
975 def bold(self, text):
976 """Format a string in bold by overstriking."""
977 return join(map(lambda ch: ch + '\b' + ch, text), '')
979 def indent(self, text, prefix=' '):
980 """Indent text by prepending a given prefix to each line."""
981 if not text: return ''
982 lines = split(text, '\n')
983 lines = map(lambda line, prefix=prefix: prefix + line, lines)
984 if lines: lines[-1] = rstrip(lines[-1])
985 return join(lines, '\n')
987 def section(self, title, contents):
988 """Format a section with a given heading."""
989 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
991 # ---------------------------------------------- type-specific routines
993 def formattree(self, tree, modname, parent=None, prefix=''):
994 """Render in text a class tree as returned by inspect.getclasstree()."""
995 result = ''
996 for entry in tree:
997 if type(entry) is type(()):
998 c, bases = entry
999 result = result + prefix + classname(c, modname)
1000 if bases and bases != (parent,):
1001 parents = map(lambda c, m=modname: classname(c, m), bases)
1002 result = result + '(%s)' % join(parents, ', ')
1003 result = result + '\n'
1004 elif type(entry) is type([]):
1005 result = result + self.formattree(
1006 entry, modname, c, prefix + ' ')
1007 return result
1009 def docmodule(self, object, name=None, mod=None):
1010 """Produce text documentation for a given module object."""
1011 name = object.__name__ # ignore the passed-in name
1012 synop, desc = splitdoc(getdoc(object))
1013 result = self.section('NAME', name + (synop and ' - ' + synop))
1015 try:
1016 all = object.__all__
1017 except AttributeError:
1018 all = None
1020 try:
1021 file = inspect.getabsfile(object)
1022 except TypeError:
1023 file = '(built-in)'
1024 result = result + self.section('FILE', file)
1026 docloc = self.getdocloc(object)
1027 if docloc is not None:
1028 result = result + self.section('MODULE DOCS', docloc)
1030 if desc:
1031 result = result + self.section('DESCRIPTION', desc)
1033 classes = []
1034 for key, value in inspect.getmembers(object, inspect.isclass):
1035 # if __all__ exists, believe it. Otherwise use old heuristic.
1036 if (all is not None
1037 or (inspect.getmodule(value) or object) is object):
1038 if visiblename(key, all):
1039 classes.append((key, value))
1040 funcs = []
1041 for key, value in inspect.getmembers(object, inspect.isroutine):
1042 # if __all__ exists, believe it. Otherwise use old heuristic.
1043 if (all is not None or
1044 inspect.isbuiltin(value) or inspect.getmodule(value) is object):
1045 if visiblename(key, all):
1046 funcs.append((key, value))
1047 data = []
1048 for key, value in inspect.getmembers(object, isdata):
1049 if visiblename(key, all):
1050 data.append((key, value))
1052 if hasattr(object, '__path__'):
1053 modpkgs = []
1054 for file in os.listdir(object.__path__[0]):
1055 path = os.path.join(object.__path__[0], file)
1056 modname = inspect.getmodulename(file)
1057 if modname != '__init__':
1058 if modname and modname not in modpkgs:
1059 modpkgs.append(modname)
1060 elif ispackage(path):
1061 modpkgs.append(file + ' (package)')
1062 modpkgs.sort()
1063 result = result + self.section(
1064 'PACKAGE CONTENTS', join(modpkgs, '\n'))
1066 if classes:
1067 classlist = map(lambda (key, value): value, classes)
1068 contents = [self.formattree(
1069 inspect.getclasstree(classlist, 1), name)]
1070 for key, value in classes:
1071 contents.append(self.document(value, key, name))
1072 result = result + self.section('CLASSES', join(contents, '\n'))
1074 if funcs:
1075 contents = []
1076 for key, value in funcs:
1077 contents.append(self.document(value, key, name))
1078 result = result + self.section('FUNCTIONS', join(contents, '\n'))
1080 if data:
1081 contents = []
1082 for key, value in data:
1083 contents.append(self.docother(value, key, name, maxlen=70))
1084 result = result + self.section('DATA', join(contents, '\n'))
1086 if hasattr(object, '__version__'):
1087 version = str(object.__version__)
1088 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
1089 version = strip(version[11:-1])
1090 result = result + self.section('VERSION', version)
1091 if hasattr(object, '__date__'):
1092 result = result + self.section('DATE', str(object.__date__))
1093 if hasattr(object, '__author__'):
1094 result = result + self.section('AUTHOR', str(object.__author__))
1095 if hasattr(object, '__credits__'):
1096 result = result + self.section('CREDITS', str(object.__credits__))
1097 return result
1099 def docclass(self, object, name=None, mod=None):
1100 """Produce text documentation for a given class object."""
1101 realname = object.__name__
1102 name = name or realname
1103 bases = object.__bases__
1105 def makename(c, m=object.__module__):
1106 return classname(c, m)
1108 if name == realname:
1109 title = 'class ' + self.bold(realname)
1110 else:
1111 title = self.bold(name) + ' = class ' + realname
1112 if bases:
1113 parents = map(makename, bases)
1114 title = title + '(%s)' % join(parents, ', ')
1116 doc = getdoc(object)
1117 contents = doc and [doc + '\n'] or []
1118 push = contents.append
1120 # List the mro, if non-trivial.
1121 mro = deque(inspect.getmro(object))
1122 if len(mro) > 2:
1123 push("Method resolution order:")
1124 for base in mro:
1125 push(' ' + makename(base))
1126 push('')
1128 # Cute little class to pump out a horizontal rule between sections.
1129 class HorizontalRule:
1130 def __init__(self):
1131 self.needone = 0
1132 def maybe(self):
1133 if self.needone:
1134 push('-' * 70)
1135 self.needone = 1
1136 hr = HorizontalRule()
1138 def spill(msg, attrs, predicate):
1139 ok, attrs = _split_list(attrs, predicate)
1140 if ok:
1141 hr.maybe()
1142 push(msg)
1143 for name, kind, homecls, value in ok:
1144 push(self.document(getattr(object, name),
1145 name, mod, object))
1146 return attrs
1148 def spilldescriptors(msg, attrs, predicate):
1149 ok, attrs = _split_list(attrs, predicate)
1150 if ok:
1151 hr.maybe()
1152 push(msg)
1153 for name, kind, homecls, value in ok:
1154 push(self._docdescriptor(name, value, mod))
1155 return attrs
1157 def spilldata(msg, attrs, predicate):
1158 ok, attrs = _split_list(attrs, predicate)
1159 if ok:
1160 hr.maybe()
1161 push(msg)
1162 for name, kind, homecls, value in ok:
1163 if callable(value) or inspect.isdatadescriptor(value):
1164 doc = getdoc(value)
1165 else:
1166 doc = None
1167 push(self.docother(getattr(object, name),
1168 name, mod, maxlen=70, doc=doc) + '\n')
1169 return attrs
1171 attrs = filter(lambda (name, kind, cls, value): visiblename(name),
1172 classify_class_attrs(object))
1173 while attrs:
1174 if mro:
1175 thisclass = mro.popleft()
1176 else:
1177 thisclass = attrs[0][2]
1178 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
1180 if thisclass is __builtin__.object:
1181 attrs = inherited
1182 continue
1183 elif thisclass is object:
1184 tag = "defined here"
1185 else:
1186 tag = "inherited from %s" % classname(thisclass,
1187 object.__module__)
1188 filter(lambda t: not t[0].startswith('_'), attrs)
1190 # Sort attrs by name.
1191 attrs.sort()
1193 # Pump out the attrs, segregated by kind.
1194 attrs = spill("Methods %s:\n" % tag, attrs,
1195 lambda t: t[1] == 'method')
1196 attrs = spill("Class methods %s:\n" % tag, attrs,
1197 lambda t: t[1] == 'class method')
1198 attrs = spill("Static methods %s:\n" % tag, attrs,
1199 lambda t: t[1] == 'static method')
1200 attrs = spilldescriptors("Data descriptors %s:\n" % tag, attrs,
1201 lambda t: t[1] == 'data descriptor')
1202 attrs = spilldata("Data and other attributes %s:\n" % tag, attrs,
1203 lambda t: t[1] == 'data')
1204 assert attrs == []
1205 attrs = inherited
1207 contents = '\n'.join(contents)
1208 if not contents:
1209 return title + '\n'
1210 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
1212 def formatvalue(self, object):
1213 """Format an argument default value as text."""
1214 return '=' + self.repr(object)
1216 def docroutine(self, object, name=None, mod=None, cl=None):
1217 """Produce text documentation for a function or method object."""
1218 realname = object.__name__
1219 name = name or realname
1220 note = ''
1221 skipdocs = 0
1222 if inspect.ismethod(object):
1223 imclass = object.im_class
1224 if cl:
1225 if imclass is not cl:
1226 note = ' from ' + classname(imclass, mod)
1227 else:
1228 if object.im_self:
1229 note = ' method of %s instance' % classname(
1230 object.im_self.__class__, mod)
1231 else:
1232 note = ' unbound %s method' % classname(imclass,mod)
1233 object = object.im_func
1235 if name == realname:
1236 title = self.bold(realname)
1237 else:
1238 if (cl and realname in cl.__dict__ and
1239 cl.__dict__[realname] is object):
1240 skipdocs = 1
1241 title = self.bold(name) + ' = ' + realname
1242 if inspect.isfunction(object):
1243 args, varargs, varkw, defaults = inspect.getargspec(object)
1244 argspec = inspect.formatargspec(
1245 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
1246 if realname == '<lambda>':
1247 title = 'lambda'
1248 argspec = argspec[1:-1] # remove parentheses
1249 else:
1250 argspec = '(...)'
1251 decl = title + argspec + note
1253 if skipdocs:
1254 return decl + '\n'
1255 else:
1256 doc = getdoc(object) or ''
1257 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
1259 def _docdescriptor(self, name, value, mod):
1260 results = []
1261 push = results.append
1263 if name:
1264 push(self.bold(name))
1265 push('\n')
1266 doc = getdoc(value) or ''
1267 if doc:
1268 push(self.indent(doc))
1269 push('\n')
1270 return ''.join(results)
1272 def docproperty(self, object, name=None, mod=None, cl=None):
1273 """Produce text documentation for a property."""
1274 return self._docdescriptor(name, object, mod)
1276 def docother(self, object, name=None, mod=None, parent=None, maxlen=None, doc=None):
1277 """Produce text documentation for a data object."""
1278 repr = self.repr(object)
1279 if maxlen:
1280 line = (name and name + ' = ' or '') + repr
1281 chop = maxlen - len(line)
1282 if chop < 0: repr = repr[:chop] + '...'
1283 line = (name and self.bold(name) + ' = ' or '') + repr
1284 if doc is not None:
1285 line += '\n' + self.indent(str(doc))
1286 return line
1288 # --------------------------------------------------------- user interfaces
1290 def pager(text):
1291 """The first time this is called, determine what kind of pager to use."""
1292 global pager
1293 pager = getpager()
1294 pager(text)
1296 def getpager():
1297 """Decide what method to use for paging through text."""
1298 if type(sys.stdout) is not types.FileType:
1299 return plainpager
1300 if not sys.stdin.isatty() or not sys.stdout.isatty():
1301 return plainpager
1302 if os.environ.get('TERM') in ('dumb', 'emacs'):
1303 return plainpager
1304 if 'PAGER' in os.environ:
1305 if sys.platform == 'win32': # pipes completely broken in Windows
1306 return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
1307 elif os.environ.get('TERM') in ('dumb', 'emacs'):
1308 return lambda text: pipepager(plain(text), os.environ['PAGER'])
1309 else:
1310 return lambda text: pipepager(text, os.environ['PAGER'])
1311 if sys.platform == 'win32' or sys.platform.startswith('os2'):
1312 return lambda text: tempfilepager(plain(text), 'more <')
1313 if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0:
1314 return lambda text: pipepager(text, 'less')
1316 import tempfile
1317 (fd, filename) = tempfile.mkstemp()
1318 os.close(fd)
1319 try:
1320 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
1321 return lambda text: pipepager(text, 'more')
1322 else:
1323 return ttypager
1324 finally:
1325 os.unlink(filename)
1327 def plain(text):
1328 """Remove boldface formatting from text."""
1329 return re.sub('.\b', '', text)
1331 def pipepager(text, cmd):
1332 """Page through text by feeding it to another program."""
1333 pipe = os.popen(cmd, 'w')
1334 try:
1335 pipe.write(text)
1336 pipe.close()
1337 except IOError:
1338 pass # Ignore broken pipes caused by quitting the pager program.
1340 def tempfilepager(text, cmd):
1341 """Page through text by invoking a program on a temporary file."""
1342 import tempfile
1343 filename = tempfile.mktemp()
1344 file = open(filename, 'w')
1345 file.write(text)
1346 file.close()
1347 try:
1348 os.system(cmd + ' ' + filename)
1349 finally:
1350 os.unlink(filename)
1352 def ttypager(text):
1353 """Page through text on a text terminal."""
1354 lines = split(plain(text), '\n')
1355 try:
1356 import tty
1357 fd = sys.stdin.fileno()
1358 old = tty.tcgetattr(fd)
1359 tty.setcbreak(fd)
1360 getchar = lambda: sys.stdin.read(1)
1361 except (ImportError, AttributeError):
1362 tty = None
1363 getchar = lambda: sys.stdin.readline()[:-1][:1]
1365 try:
1366 r = inc = os.environ.get('LINES', 25) - 1
1367 sys.stdout.write(join(lines[:inc], '\n') + '\n')
1368 while lines[r:]:
1369 sys.stdout.write('-- more --')
1370 sys.stdout.flush()
1371 c = getchar()
1373 if c in ('q', 'Q'):
1374 sys.stdout.write('\r \r')
1375 break
1376 elif c in ('\r', '\n'):
1377 sys.stdout.write('\r \r' + lines[r] + '\n')
1378 r = r + 1
1379 continue
1380 if c in ('b', 'B', '\x1b'):
1381 r = r - inc - inc
1382 if r < 0: r = 0
1383 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1384 r = r + inc
1386 finally:
1387 if tty:
1388 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1390 def plainpager(text):
1391 """Simply print unformatted text. This is the ultimate fallback."""
1392 sys.stdout.write(plain(text))
1394 def describe(thing):
1395 """Produce a short description of the given thing."""
1396 if inspect.ismodule(thing):
1397 if thing.__name__ in sys.builtin_module_names:
1398 return 'built-in module ' + thing.__name__
1399 if hasattr(thing, '__path__'):
1400 return 'package ' + thing.__name__
1401 else:
1402 return 'module ' + thing.__name__
1403 if inspect.isbuiltin(thing):
1404 return 'built-in function ' + thing.__name__
1405 if inspect.isclass(thing):
1406 return 'class ' + thing.__name__
1407 if inspect.isfunction(thing):
1408 return 'function ' + thing.__name__
1409 if inspect.ismethod(thing):
1410 return 'method ' + thing.__name__
1411 if type(thing) is types.InstanceType:
1412 return 'instance of ' + thing.__class__.__name__
1413 return type(thing).__name__
1415 def locate(path, forceload=0):
1416 """Locate an object by name or dotted path, importing as necessary."""
1417 parts = [part for part in split(path, '.') if part]
1418 module, n = None, 0
1419 while n < len(parts):
1420 nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
1421 if nextmodule: module, n = nextmodule, n + 1
1422 else: break
1423 if module:
1424 object = module
1425 for part in parts[n:]:
1426 try: object = getattr(object, part)
1427 except AttributeError: return None
1428 return object
1429 else:
1430 if hasattr(__builtin__, path):
1431 return getattr(__builtin__, path)
1433 # --------------------------------------- interactive interpreter interface
1435 text = TextDoc()
1436 html = HTMLDoc()
1438 def resolve(thing, forceload=0):
1439 """Given an object or a path to an object, get the object and its name."""
1440 if isinstance(thing, str):
1441 object = locate(thing, forceload)
1442 if not object:
1443 raise ImportError, 'no Python documentation found for %r' % thing
1444 return object, thing
1445 else:
1446 return thing, getattr(thing, '__name__', None)
1448 def doc(thing, title='Python Library Documentation: %s', forceload=0):
1449 """Display text documentation, given an object or a path to an object."""
1450 try:
1451 object, name = resolve(thing, forceload)
1452 desc = describe(object)
1453 module = inspect.getmodule(object)
1454 if name and '.' in name:
1455 desc += ' in ' + name[:name.rfind('.')]
1456 elif module and module is not object:
1457 desc += ' in module ' + module.__name__
1458 if not (inspect.ismodule(object) or
1459 inspect.isclass(object) or
1460 inspect.isroutine(object) or
1461 isinstance(object, property)):
1462 # If the passed object is a piece of data or an instance,
1463 # document its available methods instead of its value.
1464 object = type(object)
1465 desc += ' object'
1466 pager(title % desc + '\n\n' + text.document(object, name))
1467 except (ImportError, ErrorDuringImport), value:
1468 print value
1470 def writedoc(thing, forceload=0):
1471 """Write HTML documentation to a file in the current directory."""
1472 try:
1473 object, name = resolve(thing, forceload)
1474 page = html.page(describe(object), html.document(object, name))
1475 file = open(name + '.html', 'w')
1476 file.write(page)
1477 file.close()
1478 print 'wrote', name + '.html'
1479 except (ImportError, ErrorDuringImport), value:
1480 print value
1482 def writedocs(dir, pkgpath='', done=None):
1483 """Write out HTML documentation for all modules in a directory tree."""
1484 if done is None: done = {}
1485 for file in os.listdir(dir):
1486 path = os.path.join(dir, file)
1487 if ispackage(path):
1488 writedocs(path, pkgpath + file + '.', done)
1489 elif os.path.isfile(path):
1490 modname = inspect.getmodulename(path)
1491 if modname:
1492 if modname == '__init__':
1493 modname = pkgpath[:-1] # remove trailing period
1494 else:
1495 modname = pkgpath + modname
1496 if modname not in done:
1497 done[modname] = 1
1498 writedoc(modname)
1500 class Helper:
1501 keywords = {
1502 'and': 'BOOLEAN',
1503 'assert': ('ref/assert', ''),
1504 'break': ('ref/break', 'while for'),
1505 'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1506 'continue': ('ref/continue', 'while for'),
1507 'def': ('ref/function', ''),
1508 'del': ('ref/del', 'BASICMETHODS'),
1509 'elif': 'if',
1510 'else': ('ref/if', 'while for'),
1511 'except': 'try',
1512 'exec': ('ref/exec', ''),
1513 'finally': 'try',
1514 'for': ('ref/for', 'break continue while'),
1515 'from': 'import',
1516 'global': ('ref/global', 'NAMESPACES'),
1517 'if': ('ref/if', 'TRUTHVALUE'),
1518 'import': ('ref/import', 'MODULES'),
1519 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1520 'is': 'COMPARISON',
1521 'lambda': ('ref/lambdas', 'FUNCTIONS'),
1522 'not': 'BOOLEAN',
1523 'or': 'BOOLEAN',
1524 'pass': ('ref/pass', ''),
1525 'print': ('ref/print', ''),
1526 'raise': ('ref/raise', 'EXCEPTIONS'),
1527 'return': ('ref/return', 'FUNCTIONS'),
1528 'try': ('ref/try', 'EXCEPTIONS'),
1529 'while': ('ref/while', 'break continue if TRUTHVALUE'),
1530 'yield': ('ref/yield', ''),
1533 topics = {
1534 'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'),
1535 'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'),
1536 'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'),
1537 'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'),
1538 'UNICODE': ('ref/strings', 'encodings unicode SEQUENCES STRINGMETHODS FORMATTING TYPES'),
1539 'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1540 'INTEGER': ('ref/integers', 'int range'),
1541 'FLOAT': ('ref/floating', 'float math'),
1542 'COMPLEX': ('ref/imaginary', 'complex cmath'),
1543 'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
1544 'MAPPINGS': 'DICTIONARIES',
1545 'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'),
1546 'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'),
1547 'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'),
1548 'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'),
1549 'FRAMEOBJECTS': 'TYPES',
1550 'TRACEBACKS': 'TYPES',
1551 'NONE': ('lib/bltin-null-object', ''),
1552 'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'),
1553 'FILES': ('lib/bltin-file-objects', ''),
1554 'SPECIALATTRIBUTES': ('lib/specialattrs', ''),
1555 'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'),
1556 'MODULES': ('lib/typesmodules', 'import'),
1557 'PACKAGES': 'import',
1558 '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'),
1559 'OPERATORS': 'EXPRESSIONS',
1560 'PRECEDENCE': 'EXPRESSIONS',
1561 'OBJECTS': ('ref/objects', 'TYPES'),
1562 'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
1563 'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'),
1564 'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1565 'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'),
1566 'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'),
1567 'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'),
1568 'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'),
1569 'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'),
1570 'EXECUTION': ('ref/execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'),
1571 'NAMESPACES': ('ref/naming', 'global ASSIGNMENT DELETION DYNAMICFEATURES'),
1572 'DYNAMICFEATURES': ('ref/dynamic-features', ''),
1573 'SCOPING': 'NAMESPACES',
1574 'FRAMES': 'NAMESPACES',
1575 'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'),
1576 'COERCIONS': ('ref/coercion-rules','CONVERSIONS'),
1577 'CONVERSIONS': ('ref/conversions', 'COERCIONS'),
1578 'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'),
1579 'SPECIALIDENTIFIERS': ('ref/id-classes', ''),
1580 'PRIVATENAMES': ('ref/atom-identifiers', ''),
1581 'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1582 'TUPLES': 'SEQUENCES',
1583 'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'),
1584 'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'),
1585 'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'),
1586 'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'),
1587 'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'),
1588 'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'),
1589 'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1590 'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'),
1591 'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'),
1592 'CALLS': ('ref/calls', 'EXPRESSIONS'),
1593 'POWER': ('ref/power', 'EXPRESSIONS'),
1594 'UNARY': ('ref/unary', 'EXPRESSIONS'),
1595 'BINARY': ('ref/binary', 'EXPRESSIONS'),
1596 'SHIFTING': ('ref/shifting', 'EXPRESSIONS'),
1597 'BITWISE': ('ref/bitwise', 'EXPRESSIONS'),
1598 'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'),
1599 'BOOLEAN': ('ref/Booleans', 'EXPRESSIONS TRUTHVALUE'),
1600 'ASSERTION': 'assert',
1601 'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'),
1602 'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'),
1603 'DELETION': 'del',
1604 'PRINTING': 'print',
1605 'RETURNING': 'return',
1606 'IMPORTING': 'import',
1607 'CONDITIONAL': 'if',
1608 'LOOPING': ('ref/compound', 'for while break continue'),
1609 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'),
1610 'DEBUGGING': ('lib/module-pdb', 'pdb'),
1613 def __init__(self, input, output):
1614 self.input = input
1615 self.output = output
1616 self.docdir = None
1617 execdir = os.path.dirname(sys.executable)
1618 homedir = os.environ.get('PYTHONHOME')
1619 for dir in [os.environ.get('PYTHONDOCS'),
1620 homedir and os.path.join(homedir, 'doc'),
1621 os.path.join(execdir, 'doc'),
1622 '/usr/doc/python-docs-' + split(sys.version)[0],
1623 '/usr/doc/python-' + split(sys.version)[0],
1624 '/usr/doc/python-docs-' + sys.version[:3],
1625 '/usr/doc/python-' + sys.version[:3],
1626 os.path.join(sys.prefix, 'Resources/English.lproj/Documentation')]:
1627 if dir and os.path.isdir(os.path.join(dir, 'lib')):
1628 self.docdir = dir
1630 def __repr__(self):
1631 if inspect.stack()[1][3] == '?':
1632 self()
1633 return ''
1634 return '<pydoc.Helper instance>'
1636 def __call__(self, request=None):
1637 if request is not None:
1638 self.help(request)
1639 else:
1640 self.intro()
1641 self.interact()
1642 self.output.write('''
1643 You are now leaving help and returning to the Python interpreter.
1644 If you want to ask for help on a particular object directly from the
1645 interpreter, you can type "help(object)". Executing "help('string')"
1646 has the same effect as typing a particular string at the help> prompt.
1647 ''')
1649 def interact(self):
1650 self.output.write('\n')
1651 while True:
1652 try:
1653 request = self.getline('help> ')
1654 if not request: break
1655 except (KeyboardInterrupt, EOFError):
1656 break
1657 request = strip(replace(request, '"', '', "'", ''))
1658 if lower(request) in ('q', 'quit'): break
1659 self.help(request)
1661 def getline(self, prompt):
1662 """Read one line, using raw_input when available."""
1663 if self.input is sys.stdin:
1664 return raw_input(prompt)
1665 else:
1666 self.output.write(prompt)
1667 self.output.flush()
1668 return self.input.readline()
1670 def help(self, request):
1671 if type(request) is type(''):
1672 if request == 'help': self.intro()
1673 elif request == 'keywords': self.listkeywords()
1674 elif request == 'topics': self.listtopics()
1675 elif request == 'modules': self.listmodules()
1676 elif request[:8] == 'modules ':
1677 self.listmodules(split(request)[1])
1678 elif request in self.keywords: self.showtopic(request)
1679 elif request in self.topics: self.showtopic(request)
1680 elif request: doc(request, 'Help on %s:')
1681 elif isinstance(request, Helper): self()
1682 else: doc(request, 'Help on %s:')
1683 self.output.write('\n')
1685 def intro(self):
1686 self.output.write('''
1687 Welcome to Python %s! This is the online help utility.
1689 If this is your first time using Python, you should definitely check out
1690 the tutorial on the Internet at http://www.python.org/doc/tut/.
1692 Enter the name of any module, keyword, or topic to get help on writing
1693 Python programs and using Python modules. To quit this help utility and
1694 return to the interpreter, just type "quit".
1696 To get a list of available modules, keywords, or topics, type "modules",
1697 "keywords", or "topics". Each module also comes with a one-line summary
1698 of what it does; to list the modules whose summaries contain a given word
1699 such as "spam", type "modules spam".
1700 ''' % sys.version[:3])
1702 def list(self, items, columns=4, width=80):
1703 items = items[:]
1704 items.sort()
1705 colw = width / columns
1706 rows = (len(items) + columns - 1) / columns
1707 for row in range(rows):
1708 for col in range(columns):
1709 i = col * rows + row
1710 if i < len(items):
1711 self.output.write(items[i])
1712 if col < columns - 1:
1713 self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1714 self.output.write('\n')
1716 def listkeywords(self):
1717 self.output.write('''
1718 Here is a list of the Python keywords. Enter any keyword to get more help.
1720 ''')
1721 self.list(self.keywords.keys())
1723 def listtopics(self):
1724 self.output.write('''
1725 Here is a list of available topics. Enter any topic name to get more help.
1727 ''')
1728 self.list(self.topics.keys())
1730 def showtopic(self, topic):
1731 if not self.docdir:
1732 self.output.write('''
1733 Sorry, topic and keyword documentation is not available because the Python
1734 HTML documentation files could not be found. If you have installed them,
1735 please set the environment variable PYTHONDOCS to indicate their location.
1736 ''')
1737 return
1738 target = self.topics.get(topic, self.keywords.get(topic))
1739 if not target:
1740 self.output.write('no documentation found for %s\n' % repr(topic))
1741 return
1742 if type(target) is type(''):
1743 return self.showtopic(target)
1745 filename, xrefs = target
1746 filename = self.docdir + '/' + filename + '.html'
1747 try:
1748 file = open(filename)
1749 except:
1750 self.output.write('could not read docs from %s\n' % filename)
1751 return
1753 divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
1754 addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
1755 document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1756 file.close()
1758 import htmllib, formatter, StringIO
1759 buffer = StringIO.StringIO()
1760 parser = htmllib.HTMLParser(
1761 formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1762 parser.start_table = parser.do_p
1763 parser.end_table = lambda parser=parser: parser.do_p({})
1764 parser.start_tr = parser.do_br
1765 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1766 parser.feed(document)
1767 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
1768 pager(' ' + strip(buffer) + '\n')
1769 if xrefs:
1770 buffer = StringIO.StringIO()
1771 formatter.DumbWriter(buffer).send_flowing_data(
1772 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1773 self.output.write('\n%s\n' % buffer.getvalue())
1775 def listmodules(self, key=''):
1776 if key:
1777 self.output.write('''
1778 Here is a list of matching modules. Enter any module name to get more help.
1780 ''')
1781 apropos(key)
1782 else:
1783 self.output.write('''
1784 Please wait a moment while I gather a list of all available modules...
1786 ''')
1787 modules = {}
1788 def callback(path, modname, desc, modules=modules):
1789 if modname and modname[-9:] == '.__init__':
1790 modname = modname[:-9] + ' (package)'
1791 if find(modname, '.') < 0:
1792 modules[modname] = 1
1793 ModuleScanner().run(callback)
1794 self.list(modules.keys())
1795 self.output.write('''
1796 Enter any module name to get more help. Or, type "modules spam" to search
1797 for modules whose descriptions contain the word "spam".
1798 ''')
1800 help = Helper(sys.stdin, sys.stdout)
1802 class Scanner:
1803 """A generic tree iterator."""
1804 def __init__(self, roots, children, descendp):
1805 self.roots = roots[:]
1806 self.state = []
1807 self.children = children
1808 self.descendp = descendp
1810 def next(self):
1811 if not self.state:
1812 if not self.roots:
1813 return None
1814 root = self.roots.pop(0)
1815 self.state = [(root, self.children(root))]
1816 node, children = self.state[-1]
1817 if not children:
1818 self.state.pop()
1819 return self.next()
1820 child = children.pop(0)
1821 if self.descendp(child):
1822 self.state.append((child, self.children(child)))
1823 return child
1825 class ModuleScanner(Scanner):
1826 """An interruptible scanner that searches module synopses."""
1827 def __init__(self):
1828 roots = map(lambda dir: (dir, ''), pathdirs())
1829 Scanner.__init__(self, roots, self.submodules, self.isnewpackage)
1830 self.inodes = map(lambda (dir, pkg): os.stat(dir).st_ino, roots)
1832 def submodules(self, (dir, package)):
1833 children = []
1834 for file in os.listdir(dir):
1835 path = os.path.join(dir, file)
1836 if ispackage(path):
1837 children.append((path, package + (package and '.') + file))
1838 else:
1839 children.append((path, package))
1840 children.sort() # so that spam.py comes before spam.pyc or spam.pyo
1841 return children
1843 def isnewpackage(self, (dir, package)):
1844 inode = os.path.exists(dir) and os.stat(dir).st_ino
1845 if not (os.path.islink(dir) and inode in self.inodes):
1846 self.inodes.append(inode) # detect circular symbolic links
1847 return ispackage(dir)
1848 return False
1850 def run(self, callback, key=None, completer=None):
1851 if key: key = lower(key)
1852 self.quit = False
1853 seen = {}
1855 for modname in sys.builtin_module_names:
1856 if modname != '__main__':
1857 seen[modname] = 1
1858 if key is None:
1859 callback(None, modname, '')
1860 else:
1861 desc = split(__import__(modname).__doc__ or '', '\n')[0]
1862 if find(lower(modname + ' - ' + desc), key) >= 0:
1863 callback(None, modname, desc)
1865 while not self.quit:
1866 node = self.next()
1867 if not node: break
1868 path, package = node
1869 modname = inspect.getmodulename(path)
1870 if os.path.isfile(path) and modname:
1871 modname = package + (package and '.') + modname
1872 if not modname in seen:
1873 seen[modname] = 1 # if we see spam.py, skip spam.pyc
1874 if key is None:
1875 callback(path, modname, '')
1876 else:
1877 desc = synopsis(path) or ''
1878 if find(lower(modname + ' - ' + desc), key) >= 0:
1879 callback(path, modname, desc)
1880 if completer: completer()
1882 def apropos(key):
1883 """Print all the one-line module summaries that contain a substring."""
1884 def callback(path, modname, desc):
1885 if modname[-9:] == '.__init__':
1886 modname = modname[:-9] + ' (package)'
1887 print modname, desc and '- ' + desc
1888 try: import warnings
1889 except ImportError: pass
1890 else: warnings.filterwarnings('ignore') # ignore problems during import
1891 ModuleScanner().run(callback, key)
1893 # --------------------------------------------------- web browser interface
1895 def serve(port, callback=None, completer=None):
1896 import BaseHTTPServer, mimetools, select
1898 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1899 class Message(mimetools.Message):
1900 def __init__(self, fp, seekable=1):
1901 Message = self.__class__
1902 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1903 self.encodingheader = self.getheader('content-transfer-encoding')
1904 self.typeheader = self.getheader('content-type')
1905 self.parsetype()
1906 self.parseplist()
1908 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1909 def send_document(self, title, contents):
1910 try:
1911 self.send_response(200)
1912 self.send_header('Content-Type', 'text/html')
1913 self.end_headers()
1914 self.wfile.write(html.page(title, contents))
1915 except IOError: pass
1917 def do_GET(self):
1918 path = self.path
1919 if path[-5:] == '.html': path = path[:-5]
1920 if path[:1] == '/': path = path[1:]
1921 if path and path != '.':
1922 try:
1923 obj = locate(path, forceload=1)
1924 except ErrorDuringImport, value:
1925 self.send_document(path, html.escape(str(value)))
1926 return
1927 if obj:
1928 self.send_document(describe(obj), html.document(obj, path))
1929 else:
1930 self.send_document(path,
1931 'no Python documentation found for %s' % repr(path))
1932 else:
1933 heading = html.heading(
1934 '<big><big><strong>Python: Index of Modules</strong></big></big>',
1935 '#ffffff', '#7799ee')
1936 def bltinlink(name):
1937 return '<a href="%s.html">%s</a>' % (name, name)
1938 names = filter(lambda x: x != '__main__',
1939 sys.builtin_module_names)
1940 contents = html.multicolumn(names, bltinlink)
1941 indices = ['<p>' + html.bigsection(
1942 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1944 seen = {}
1945 for dir in pathdirs():
1946 indices.append(html.index(dir, seen))
1947 contents = heading + join(indices) + '''<p align=right>
1948 <font color="#909090" face="helvetica, arial"><strong>
1949 pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font>'''
1950 self.send_document('Index of Modules', contents)
1952 def log_message(self, *args): pass
1954 class DocServer(BaseHTTPServer.HTTPServer):
1955 def __init__(self, port, callback):
1956 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
1957 self.address = ('', port)
1958 self.url = 'http://%s:%d/' % (host, port)
1959 self.callback = callback
1960 self.base.__init__(self, self.address, self.handler)
1962 def serve_until_quit(self):
1963 import select
1964 self.quit = False
1965 while not self.quit:
1966 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1967 if rd: self.handle_request()
1969 def server_activate(self):
1970 self.base.server_activate(self)
1971 if self.callback: self.callback(self)
1973 DocServer.base = BaseHTTPServer.HTTPServer
1974 DocServer.handler = DocHandler
1975 DocHandler.MessageClass = Message
1976 try:
1977 try:
1978 DocServer(port, callback).serve_until_quit()
1979 except (KeyboardInterrupt, select.error):
1980 pass
1981 finally:
1982 if completer: completer()
1984 # ----------------------------------------------------- graphical interface
1986 def gui():
1987 """Graphical interface (starts web server and pops up a control window)."""
1988 class GUI:
1989 def __init__(self, window, port=7464):
1990 self.window = window
1991 self.server = None
1992 self.scanner = None
1994 import Tkinter
1995 self.server_frm = Tkinter.Frame(window)
1996 self.title_lbl = Tkinter.Label(self.server_frm,
1997 text='Starting server...\n ')
1998 self.open_btn = Tkinter.Button(self.server_frm,
1999 text='open browser', command=self.open, state='disabled')
2000 self.quit_btn = Tkinter.Button(self.server_frm,
2001 text='quit serving', command=self.quit, state='disabled')
2003 self.search_frm = Tkinter.Frame(window)
2004 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
2005 self.search_ent = Tkinter.Entry(self.search_frm)
2006 self.search_ent.bind('<Return>', self.search)
2007 self.stop_btn = Tkinter.Button(self.search_frm,
2008 text='stop', pady=0, command=self.stop, state='disabled')
2009 if sys.platform == 'win32':
2010 # Trying to hide and show this button crashes under Windows.
2011 self.stop_btn.pack(side='right')
2013 self.window.title('pydoc')
2014 self.window.protocol('WM_DELETE_WINDOW', self.quit)
2015 self.title_lbl.pack(side='top', fill='x')
2016 self.open_btn.pack(side='left', fill='x', expand=1)
2017 self.quit_btn.pack(side='right', fill='x', expand=1)
2018 self.server_frm.pack(side='top', fill='x')
2020 self.search_lbl.pack(side='left')
2021 self.search_ent.pack(side='right', fill='x', expand=1)
2022 self.search_frm.pack(side='top', fill='x')
2023 self.search_ent.focus_set()
2025 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
2026 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
2027 self.result_lst.bind('<Button-1>', self.select)
2028 self.result_lst.bind('<Double-Button-1>', self.goto)
2029 self.result_scr = Tkinter.Scrollbar(window,
2030 orient='vertical', command=self.result_lst.yview)
2031 self.result_lst.config(yscrollcommand=self.result_scr.set)
2033 self.result_frm = Tkinter.Frame(window)
2034 self.goto_btn = Tkinter.Button(self.result_frm,
2035 text='go to selected', command=self.goto)
2036 self.hide_btn = Tkinter.Button(self.result_frm,
2037 text='hide results', command=self.hide)
2038 self.goto_btn.pack(side='left', fill='x', expand=1)
2039 self.hide_btn.pack(side='right', fill='x', expand=1)
2041 self.window.update()
2042 self.minwidth = self.window.winfo_width()
2043 self.minheight = self.window.winfo_height()
2044 self.bigminheight = (self.server_frm.winfo_reqheight() +
2045 self.search_frm.winfo_reqheight() +
2046 self.result_lst.winfo_reqheight() +
2047 self.result_frm.winfo_reqheight())
2048 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
2049 self.expanded = 0
2050 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
2051 self.window.wm_minsize(self.minwidth, self.minheight)
2052 self.window.tk.willdispatch()
2054 import threading
2055 threading.Thread(
2056 target=serve, args=(port, self.ready, self.quit)).start()
2058 def ready(self, server):
2059 self.server = server
2060 self.title_lbl.config(
2061 text='Python documentation server at\n' + server.url)
2062 self.open_btn.config(state='normal')
2063 self.quit_btn.config(state='normal')
2065 def open(self, event=None, url=None):
2066 url = url or self.server.url
2067 try:
2068 import webbrowser
2069 webbrowser.open(url)
2070 except ImportError: # pre-webbrowser.py compatibility
2071 if sys.platform == 'win32':
2072 os.system('start "%s"' % url)
2073 elif sys.platform == 'mac':
2074 try: import ic
2075 except ImportError: pass
2076 else: ic.launchurl(url)
2077 else:
2078 rc = os.system('netscape -remote "openURL(%s)" &' % url)
2079 if rc: os.system('netscape "%s" &' % url)
2081 def quit(self, event=None):
2082 if self.server:
2083 self.server.quit = 1
2084 self.window.quit()
2086 def search(self, event=None):
2087 key = self.search_ent.get()
2088 self.stop_btn.pack(side='right')
2089 self.stop_btn.config(state='normal')
2090 self.search_lbl.config(text='Searching for "%s"...' % key)
2091 self.search_ent.forget()
2092 self.search_lbl.pack(side='left')
2093 self.result_lst.delete(0, 'end')
2094 self.goto_btn.config(state='disabled')
2095 self.expand()
2097 import threading
2098 if self.scanner:
2099 self.scanner.quit = 1
2100 self.scanner = ModuleScanner()
2101 threading.Thread(target=self.scanner.run,
2102 args=(self.update, key, self.done)).start()
2104 def update(self, path, modname, desc):
2105 if modname[-9:] == '.__init__':
2106 modname = modname[:-9] + ' (package)'
2107 self.result_lst.insert('end',
2108 modname + ' - ' + (desc or '(no description)'))
2110 def stop(self, event=None):
2111 if self.scanner:
2112 self.scanner.quit = 1
2113 self.scanner = None
2115 def done(self):
2116 self.scanner = None
2117 self.search_lbl.config(text='Search for')
2118 self.search_lbl.pack(side='left')
2119 self.search_ent.pack(side='right', fill='x', expand=1)
2120 if sys.platform != 'win32': self.stop_btn.forget()
2121 self.stop_btn.config(state='disabled')
2123 def select(self, event=None):
2124 self.goto_btn.config(state='normal')
2126 def goto(self, event=None):
2127 selection = self.result_lst.curselection()
2128 if selection:
2129 modname = split(self.result_lst.get(selection[0]))[0]
2130 self.open(url=self.server.url + modname + '.html')
2132 def collapse(self):
2133 if not self.expanded: return
2134 self.result_frm.forget()
2135 self.result_scr.forget()
2136 self.result_lst.forget()
2137 self.bigwidth = self.window.winfo_width()
2138 self.bigheight = self.window.winfo_height()
2139 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
2140 self.window.wm_minsize(self.minwidth, self.minheight)
2141 self.expanded = 0
2143 def expand(self):
2144 if self.expanded: return
2145 self.result_frm.pack(side='bottom', fill='x')
2146 self.result_scr.pack(side='right', fill='y')
2147 self.result_lst.pack(side='top', fill='both', expand=1)
2148 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
2149 self.window.wm_minsize(self.minwidth, self.bigminheight)
2150 self.expanded = 1
2152 def hide(self, event=None):
2153 self.stop()
2154 self.collapse()
2156 import Tkinter
2157 try:
2158 root = Tkinter.Tk()
2159 # Tk will crash if pythonw.exe has an XP .manifest
2160 # file and the root has is not destroyed explicitly.
2161 # If the problem is ever fixed in Tk, the explicit
2162 # destroy can go.
2163 try:
2164 gui = GUI(root)
2165 root.mainloop()
2166 finally:
2167 root.destroy()
2168 except KeyboardInterrupt:
2169 pass
2171 # -------------------------------------------------- command-line interface
2173 def ispath(x):
2174 return isinstance(x, str) and find(x, os.sep) >= 0
2176 def cli():
2177 """Command-line interface (looks at sys.argv to decide what to do)."""
2178 import getopt
2179 class BadUsage: pass
2181 # Scripts don't get the current directory in their path by default.
2182 scriptdir = os.path.dirname(sys.argv[0])
2183 if scriptdir in sys.path:
2184 sys.path.remove(scriptdir)
2185 sys.path.insert(0, '.')
2187 try:
2188 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
2189 writing = 0
2191 for opt, val in opts:
2192 if opt == '-g':
2193 gui()
2194 return
2195 if opt == '-k':
2196 apropos(val)
2197 return
2198 if opt == '-p':
2199 try:
2200 port = int(val)
2201 except ValueError:
2202 raise BadUsage
2203 def ready(server):
2204 print 'pydoc server ready at %s' % server.url
2205 def stopped():
2206 print 'pydoc server stopped'
2207 serve(port, ready, stopped)
2208 return
2209 if opt == '-w':
2210 writing = 1
2212 if not args: raise BadUsage
2213 for arg in args:
2214 if ispath(arg) and not os.path.exists(arg):
2215 print 'file %r does not exist' % arg
2216 break
2217 try:
2218 if ispath(arg) and os.path.isfile(arg):
2219 arg = importfile(arg)
2220 if writing:
2221 if ispath(arg) and os.path.isdir(arg):
2222 writedocs(arg)
2223 else:
2224 writedoc(arg)
2225 else:
2226 help.help(arg)
2227 except ErrorDuringImport, value:
2228 print value
2230 except (getopt.error, BadUsage):
2231 cmd = os.path.basename(sys.argv[0])
2232 print """pydoc - the Python documentation tool
2234 %s <name> ...
2235 Show text documentation on something. <name> may be the name of a
2236 Python keyword, topic, function, module, or package, or a dotted
2237 reference to a class or function within a module or module in a
2238 package. If <name> contains a '%s', it is used as the path to a
2239 Python source file to document. If name is 'keywords', 'topics',
2240 or 'modules', a listing of these things is displayed.
2242 %s -k <keyword>
2243 Search for a keyword in the synopsis lines of all available modules.
2245 %s -p <port>
2246 Start an HTTP server on the given port on the local machine.
2248 %s -g
2249 Pop up a graphical interface for finding and serving documentation.
2251 %s -w <name> ...
2252 Write out the HTML documentation for a module to a file in the current
2253 directory. If <name> contains a '%s', it is treated as a filename; if
2254 it names a directory, documentation is written for all the contents.
2255 """ % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
2257 if __name__ == '__main__': cli()