round(0, "ermintrude") succeeded instead of producing a TypeError. Fix this.
[python.git] / Lib / pydoc.py
blob7da75b65bf0fb95f5d6dd659c92a94cc5736ed20
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://docs.python.org/library/
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__, pkgutil
56 from repr import Repr
57 from string import expandtabs, find, join, lower, split, strip, rfind, rstrip
58 from traceback import extract_tb
59 try:
60 from collections import deque
61 except ImportError:
62 # Python 2.3 compatibility
63 class deque(list):
64 def popleft(self):
65 return self.pop(0)
67 # --------------------------------------------------------- common routines
69 def pathdirs():
70 """Convert sys.path into a list of absolute, existing, unique paths."""
71 dirs = []
72 normdirs = []
73 for dir in sys.path:
74 dir = os.path.abspath(dir or '.')
75 normdir = os.path.normcase(dir)
76 if normdir not in normdirs and os.path.isdir(dir):
77 dirs.append(dir)
78 normdirs.append(normdir)
79 return dirs
81 def getdoc(object):
82 """Get the doc string or comments for an object."""
83 result = inspect.getdoc(object) or inspect.getcomments(object)
84 return result and re.sub('^ *\n', '', rstrip(result)) or ''
86 def splitdoc(doc):
87 """Split a doc string into a synopsis line (if any) and the rest."""
88 lines = split(strip(doc), '\n')
89 if len(lines) == 1:
90 return lines[0], ''
91 elif len(lines) >= 2 and not rstrip(lines[1]):
92 return lines[0], join(lines[2:], '\n')
93 return '', join(lines, '\n')
95 def classname(object, modname):
96 """Get a class name and qualify it with a module name if necessary."""
97 name = object.__name__
98 if object.__module__ != modname:
99 name = object.__module__ + '.' + name
100 return name
102 def isdata(object):
103 """Check if an object is of a type that probably means it's data."""
104 return not (inspect.ismodule(object) or inspect.isclass(object) or
105 inspect.isroutine(object) or inspect.isframe(object) or
106 inspect.istraceback(object) or inspect.iscode(object))
108 def replace(text, *pairs):
109 """Do a series of global replacements on a string."""
110 while pairs:
111 text = join(split(text, pairs[0]), pairs[1])
112 pairs = pairs[2:]
113 return text
115 def cram(text, maxlen):
116 """Omit part of a string if needed to make it fit in a maximum length."""
117 if len(text) > maxlen:
118 pre = max(0, (maxlen-3)//2)
119 post = max(0, maxlen-3-pre)
120 return text[:pre] + '...' + text[len(text)-post:]
121 return text
123 _re_stripid = re.compile(r' at 0x[0-9a-f]{6,16}(>+)$', re.IGNORECASE)
124 def stripid(text):
125 """Remove the hexadecimal id from a Python object representation."""
126 # The behaviour of %p is implementation-dependent in terms of case.
127 if _re_stripid.search(repr(Exception)):
128 return _re_stripid.sub(r'\1', text)
129 return text
131 def _is_some_method(obj):
132 return inspect.ismethod(obj) or inspect.ismethoddescriptor(obj)
134 def allmethods(cl):
135 methods = {}
136 for key, value in inspect.getmembers(cl, _is_some_method):
137 methods[key] = 1
138 for base in cl.__bases__:
139 methods.update(allmethods(base)) # all your base are belong to us
140 for key in methods.keys():
141 methods[key] = getattr(cl, key)
142 return methods
144 def _split_list(s, predicate):
145 """Split sequence s via predicate, and return pair ([true], [false]).
147 The return value is a 2-tuple of lists,
148 ([x for x in s if predicate(x)],
149 [x for x in s if not predicate(x)])
152 yes = []
153 no = []
154 for x in s:
155 if predicate(x):
156 yes.append(x)
157 else:
158 no.append(x)
159 return yes, no
161 def visiblename(name, all=None):
162 """Decide whether to show documentation on a variable."""
163 # Certain special names are redundant.
164 _hidden_names = ('__builtins__', '__doc__', '__file__', '__path__',
165 '__module__', '__name__', '__slots__', '__package__')
166 if name in _hidden_names: return 0
167 # Private names are hidden, but special names are displayed.
168 if name.startswith('__') and name.endswith('__'): return 1
169 if all is not None:
170 # only document that which the programmer exported in __all__
171 return name in all
172 else:
173 return not name.startswith('_')
175 def classify_class_attrs(object):
176 """Wrap inspect.classify_class_attrs, with fixup for data descriptors."""
177 def fixup(data):
178 name, kind, cls, value = data
179 if inspect.isdatadescriptor(value):
180 kind = 'data descriptor'
181 return name, kind, cls, value
182 return map(fixup, inspect.classify_class_attrs(object))
184 # ----------------------------------------------------- module manipulation
186 def ispackage(path):
187 """Guess whether a path refers to a package directory."""
188 if os.path.isdir(path):
189 for ext in ('.py', '.pyc', '.pyo'):
190 if os.path.isfile(os.path.join(path, '__init__' + ext)):
191 return True
192 return False
194 def source_synopsis(file):
195 line = file.readline()
196 while line[:1] == '#' or not strip(line):
197 line = file.readline()
198 if not line: break
199 line = strip(line)
200 if line[:4] == 'r"""': line = line[1:]
201 if line[:3] == '"""':
202 line = line[3:]
203 if line[-1:] == '\\': line = line[:-1]
204 while not strip(line):
205 line = file.readline()
206 if not line: break
207 result = strip(split(line, '"""')[0])
208 else: result = None
209 return result
211 def synopsis(filename, cache={}):
212 """Get the one-line summary out of a module file."""
213 mtime = os.stat(filename).st_mtime
214 lastupdate, result = cache.get(filename, (0, None))
215 if lastupdate < mtime:
216 info = inspect.getmoduleinfo(filename)
217 try:
218 file = open(filename)
219 except IOError:
220 # module can't be opened, so skip it
221 return None
222 if info and 'b' in info[2]: # binary modules have to be imported
223 try: module = imp.load_module('__temp__', file, filename, info[1:])
224 except: return None
225 result = (module.__doc__ or '').splitlines()[0]
226 del sys.modules['__temp__']
227 else: # text modules can be directly examined
228 result = source_synopsis(file)
229 file.close()
230 cache[filename] = (mtime, result)
231 return result
233 class ErrorDuringImport(Exception):
234 """Errors that occurred while trying to import something to document it."""
235 def __init__(self, filename, exc_info):
236 exc, value, tb = exc_info
237 self.filename = filename
238 self.exc = exc
239 self.value = value
240 self.tb = tb
242 def __str__(self):
243 exc = self.exc
244 if type(exc) is types.ClassType:
245 exc = exc.__name__
246 return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
248 def importfile(path):
249 """Import a Python source file or compiled file given its path."""
250 magic = imp.get_magic()
251 file = open(path, 'r')
252 if file.read(len(magic)) == magic:
253 kind = imp.PY_COMPILED
254 else:
255 kind = imp.PY_SOURCE
256 file.close()
257 filename = os.path.basename(path)
258 name, ext = os.path.splitext(filename)
259 file = open(path, 'r')
260 try:
261 module = imp.load_module(name, file, path, (ext, 'r', kind))
262 except:
263 raise ErrorDuringImport(path, sys.exc_info())
264 file.close()
265 return module
267 def safeimport(path, forceload=0, cache={}):
268 """Import a module; handle errors; return None if the module isn't found.
270 If the module *is* found but an exception occurs, it's wrapped in an
271 ErrorDuringImport exception and reraised. Unlike __import__, if a
272 package path is specified, the module at the end of the path is returned,
273 not the package at the beginning. If the optional 'forceload' argument
274 is 1, we reload the module from disk (unless it's a dynamic extension)."""
275 try:
276 # If forceload is 1 and the module has been previously loaded from
277 # disk, we always have to reload the module. Checking the file's
278 # mtime isn't good enough (e.g. the module could contain a class
279 # that inherits from another module that has changed).
280 if forceload and path in sys.modules:
281 if path not in sys.builtin_module_names:
282 # Avoid simply calling reload() because it leaves names in
283 # the currently loaded module lying around if they're not
284 # defined in the new source file. Instead, remove the
285 # module from sys.modules and re-import. Also remove any
286 # submodules because they won't appear in the newly loaded
287 # module's namespace if they're already in sys.modules.
288 subs = [m for m in sys.modules if m.startswith(path + '.')]
289 for key in [path] + subs:
290 # Prevent garbage collection.
291 cache[key] = sys.modules[key]
292 del sys.modules[key]
293 module = __import__(path)
294 except:
295 # Did the error occur before or after the module was found?
296 (exc, value, tb) = info = sys.exc_info()
297 if path in sys.modules:
298 # An error occurred while executing the imported module.
299 raise ErrorDuringImport(sys.modules[path].__file__, info)
300 elif exc is SyntaxError:
301 # A SyntaxError occurred before we could execute the module.
302 raise ErrorDuringImport(value.filename, info)
303 elif exc is ImportError and extract_tb(tb)[-1][2]=='safeimport':
304 # The import error occurred directly in this function,
305 # which means there is no such module in the path.
306 return None
307 else:
308 # Some other error occurred during the importing process.
309 raise ErrorDuringImport(path, sys.exc_info())
310 for part in split(path, '.')[1:]:
311 try: module = getattr(module, part)
312 except AttributeError: return None
313 return module
315 # ---------------------------------------------------- formatter base class
317 class Doc:
318 def document(self, object, name=None, *args):
319 """Generate documentation for an object."""
320 args = (object, name) + args
321 # 'try' clause is to attempt to handle the possibility that inspect
322 # identifies something in a way that pydoc itself has issues handling;
323 # think 'super' and how it is a descriptor (which raises the exception
324 # by lacking a __name__ attribute) and an instance.
325 if inspect.isgetsetdescriptor(object): return self.docdata(*args)
326 if inspect.ismemberdescriptor(object): return self.docdata(*args)
327 try:
328 if inspect.ismodule(object): return self.docmodule(*args)
329 if inspect.isclass(object): return self.docclass(*args)
330 if inspect.isroutine(object): return self.docroutine(*args)
331 except AttributeError:
332 pass
333 if isinstance(object, property): return self.docproperty(*args)
334 return self.docother(*args)
336 def fail(self, object, name=None, *args):
337 """Raise an exception for unimplemented types."""
338 message = "don't know how to document object%s of type %s" % (
339 name and ' ' + repr(name), type(object).__name__)
340 raise TypeError, message
342 docmodule = docclass = docroutine = docother = docproperty = docdata = fail
344 def getdocloc(self, object):
345 """Return the location of module docs or None"""
347 try:
348 file = inspect.getabsfile(object)
349 except TypeError:
350 file = '(built-in)'
352 docloc = os.environ.get("PYTHONDOCS",
353 "http://docs.python.org/library")
354 basedir = os.path.join(sys.exec_prefix, "lib",
355 "python"+sys.version[0:3])
356 if (isinstance(object, type(os)) and
357 (object.__name__ in ('errno', 'exceptions', 'gc', 'imp',
358 'marshal', 'posix', 'signal', 'sys',
359 'thread', 'zipimport') or
360 (file.startswith(basedir) and
361 not file.startswith(os.path.join(basedir, 'site-packages'))))):
362 if docloc.startswith("http://"):
363 docloc = "%s/%s" % (docloc.rstrip("/"), object.__name__)
364 else:
365 docloc = os.path.join(docloc, object.__name__ + ".html")
366 else:
367 docloc = None
368 return docloc
370 # -------------------------------------------- HTML documentation generator
372 class HTMLRepr(Repr):
373 """Class for safely making an HTML representation of a Python object."""
374 def __init__(self):
375 Repr.__init__(self)
376 self.maxlist = self.maxtuple = 20
377 self.maxdict = 10
378 self.maxstring = self.maxother = 100
380 def escape(self, text):
381 return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')
383 def repr(self, object):
384 return Repr.repr(self, object)
386 def repr1(self, x, level):
387 if hasattr(type(x), '__name__'):
388 methodname = 'repr_' + join(split(type(x).__name__), '_')
389 if hasattr(self, methodname):
390 return getattr(self, methodname)(x, level)
391 return self.escape(cram(stripid(repr(x)), self.maxother))
393 def repr_string(self, x, level):
394 test = cram(x, self.maxstring)
395 testrepr = repr(test)
396 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
397 # Backslashes are only literal in the string and are never
398 # needed to make any special characters, so show a raw string.
399 return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
400 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
401 r'<font color="#c040c0">\1</font>',
402 self.escape(testrepr))
404 repr_str = repr_string
406 def repr_instance(self, x, level):
407 try:
408 return self.escape(cram(stripid(repr(x)), self.maxstring))
409 except:
410 return self.escape('<%s instance>' % x.__class__.__name__)
412 repr_unicode = repr_string
414 class HTMLDoc(Doc):
415 """Formatter class for HTML documentation."""
417 # ------------------------------------------- HTML formatting utilities
419 _repr_instance = HTMLRepr()
420 repr = _repr_instance.repr
421 escape = _repr_instance.escape
423 def page(self, title, contents):
424 """Format an HTML page."""
425 return '''
426 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
427 <html><head><title>Python: %s</title>
428 </head><body bgcolor="#f0f0f8">
430 </body></html>''' % (title, contents)
432 def heading(self, title, fgcol, bgcol, extras=''):
433 """Format a page heading."""
434 return '''
435 <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading">
436 <tr bgcolor="%s">
437 <td valign=bottom>&nbsp;<br>
438 <font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
439 ><td align=right valign=bottom
440 ><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
441 ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
443 def section(self, title, fgcol, bgcol, contents, width=6,
444 prelude='', marginalia=None, gap='&nbsp;'):
445 """Format a section with a heading."""
446 if marginalia is None:
447 marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
448 result = '''<p>
449 <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
450 <tr bgcolor="%s">
451 <td colspan=3 valign=bottom>&nbsp;<br>
452 <font color="%s" face="helvetica, arial">%s</font></td></tr>
453 ''' % (bgcol, fgcol, title)
454 if prelude:
455 result = result + '''
456 <tr bgcolor="%s"><td rowspan=2>%s</td>
457 <td colspan=2>%s</td></tr>
458 <tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
459 else:
460 result = result + '''
461 <tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
463 return result + '\n<td width="100%%">%s</td></tr></table>' % contents
465 def bigsection(self, title, *args):
466 """Format a section with a big heading."""
467 title = '<big><strong>%s</strong></big>' % title
468 return self.section(title, *args)
470 def preformat(self, text):
471 """Format literal preformatted text."""
472 text = self.escape(expandtabs(text))
473 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
474 ' ', '&nbsp;', '\n', '<br>\n')
476 def multicolumn(self, list, format, cols=4):
477 """Format a list of items into a multi-column list."""
478 result = ''
479 rows = (len(list)+cols-1)/cols
480 for col in range(cols):
481 result = result + '<td width="%d%%" valign=top>' % (100/cols)
482 for i in range(rows*col, rows*col+rows):
483 if i < len(list):
484 result = result + format(list[i]) + '<br>\n'
485 result = result + '</td>'
486 return '<table width="100%%" summary="list"><tr>%s</tr></table>' % result
488 def grey(self, text): return '<font color="#909090">%s</font>' % text
490 def namelink(self, name, *dicts):
491 """Make a link for an identifier, given name-to-URL mappings."""
492 for dict in dicts:
493 if name in dict:
494 return '<a href="%s">%s</a>' % (dict[name], name)
495 return name
497 def classlink(self, object, modname):
498 """Make a link for a class."""
499 name, module = object.__name__, sys.modules.get(object.__module__)
500 if hasattr(module, name) and getattr(module, name) is object:
501 return '<a href="%s.html#%s">%s</a>' % (
502 module.__name__, name, classname(object, modname))
503 return classname(object, modname)
505 def modulelink(self, object):
506 """Make a link for a module."""
507 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
509 def modpkglink(self, data):
510 """Make a link for a module or package to display in an index."""
511 name, path, ispackage, shadowed = data
512 if shadowed:
513 return self.grey(name)
514 if path:
515 url = '%s.%s.html' % (path, name)
516 else:
517 url = '%s.html' % name
518 if ispackage:
519 text = '<strong>%s</strong>&nbsp;(package)' % name
520 else:
521 text = name
522 return '<a href="%s">%s</a>' % (url, text)
524 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
525 """Mark up some plain text, given a context of symbols to look for.
526 Each context dictionary maps object names to anchor names."""
527 escape = escape or self.escape
528 results = []
529 here = 0
530 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
531 r'RFC[- ]?(\d+)|'
532 r'PEP[- ]?(\d+)|'
533 r'(self\.)?(\w+))')
534 while True:
535 match = pattern.search(text, here)
536 if not match: break
537 start, end = match.span()
538 results.append(escape(text[here:start]))
540 all, scheme, rfc, pep, selfdot, name = match.groups()
541 if scheme:
542 url = escape(all).replace('"', '&quot;')
543 results.append('<a href="%s">%s</a>' % (url, url))
544 elif rfc:
545 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
546 results.append('<a href="%s">%s</a>' % (url, escape(all)))
547 elif pep:
548 url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep)
549 results.append('<a href="%s">%s</a>' % (url, escape(all)))
550 elif text[end:end+1] == '(':
551 results.append(self.namelink(name, methods, funcs, classes))
552 elif selfdot:
553 results.append('self.<strong>%s</strong>' % name)
554 else:
555 results.append(self.namelink(name, classes))
556 here = end
557 results.append(escape(text[here:]))
558 return join(results, '')
560 # ---------------------------------------------- type-specific routines
562 def formattree(self, tree, modname, parent=None):
563 """Produce HTML for a class tree as given by inspect.getclasstree()."""
564 result = ''
565 for entry in tree:
566 if type(entry) is type(()):
567 c, bases = entry
568 result = result + '<dt><font face="helvetica, arial">'
569 result = result + self.classlink(c, modname)
570 if bases and bases != (parent,):
571 parents = []
572 for base in bases:
573 parents.append(self.classlink(base, modname))
574 result = result + '(' + join(parents, ', ') + ')'
575 result = result + '\n</font></dt>'
576 elif type(entry) is type([]):
577 result = result + '<dd>\n%s</dd>\n' % self.formattree(
578 entry, modname, c)
579 return '<dl>\n%s</dl>\n' % result
581 def docmodule(self, object, name=None, mod=None, *ignored):
582 """Produce HTML documentation for a module object."""
583 name = object.__name__ # ignore the passed-in name
584 try:
585 all = object.__all__
586 except AttributeError:
587 all = None
588 parts = split(name, '.')
589 links = []
590 for i in range(len(parts)-1):
591 links.append(
592 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
593 (join(parts[:i+1], '.'), parts[i]))
594 linkedname = join(links + parts[-1:], '.')
595 head = '<big><big><strong>%s</strong></big></big>' % linkedname
596 try:
597 path = inspect.getabsfile(object)
598 url = path
599 if sys.platform == 'win32':
600 import nturl2path
601 url = nturl2path.pathname2url(path)
602 filelink = '<a href="file:%s">%s</a>' % (url, path)
603 except TypeError:
604 filelink = '(built-in)'
605 info = []
606 if hasattr(object, '__version__'):
607 version = str(object.__version__)
608 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
609 version = strip(version[11:-1])
610 info.append('version %s' % self.escape(version))
611 if hasattr(object, '__date__'):
612 info.append(self.escape(str(object.__date__)))
613 if info:
614 head = head + ' (%s)' % join(info, ', ')
615 docloc = self.getdocloc(object)
616 if docloc is not None:
617 docloc = '<br><a href="%(docloc)s">Module Docs</a>' % locals()
618 else:
619 docloc = ''
620 result = self.heading(
621 head, '#ffffff', '#7799ee',
622 '<a href=".">index</a><br>' + filelink + docloc)
624 modules = inspect.getmembers(object, inspect.ismodule)
626 classes, cdict = [], {}
627 for key, value in inspect.getmembers(object, inspect.isclass):
628 # if __all__ exists, believe it. Otherwise use old heuristic.
629 if (all is not None or
630 (inspect.getmodule(value) or object) is object):
631 if visiblename(key, all):
632 classes.append((key, value))
633 cdict[key] = cdict[value] = '#' + key
634 for key, value in classes:
635 for base in value.__bases__:
636 key, modname = base.__name__, base.__module__
637 module = sys.modules.get(modname)
638 if modname != name and module and hasattr(module, key):
639 if getattr(module, key) is base:
640 if not key in cdict:
641 cdict[key] = cdict[base] = modname + '.html#' + key
642 funcs, fdict = [], {}
643 for key, value in inspect.getmembers(object, inspect.isroutine):
644 # if __all__ exists, believe it. Otherwise use old heuristic.
645 if (all is not None or
646 inspect.isbuiltin(value) or inspect.getmodule(value) is object):
647 if visiblename(key, all):
648 funcs.append((key, value))
649 fdict[key] = '#-' + key
650 if inspect.isfunction(value): fdict[value] = fdict[key]
651 data = []
652 for key, value in inspect.getmembers(object, isdata):
653 if visiblename(key, all):
654 data.append((key, value))
656 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
657 doc = doc and '<tt>%s</tt>' % doc
658 result = result + '<p>%s</p>\n' % doc
660 if hasattr(object, '__path__'):
661 modpkgs = []
662 for importer, modname, ispkg in pkgutil.iter_modules(object.__path__):
663 modpkgs.append((modname, name, ispkg, 0))
664 modpkgs.sort()
665 contents = self.multicolumn(modpkgs, self.modpkglink)
666 result = result + self.bigsection(
667 'Package Contents', '#ffffff', '#aa55cc', contents)
668 elif modules:
669 contents = self.multicolumn(
670 modules, lambda key_value, s=self: s.modulelink(key_value[1]))
671 result = result + self.bigsection(
672 'Modules', '#ffffff', '#aa55cc', contents)
674 if classes:
675 classlist = map(lambda key_value: key_value[1], classes)
676 contents = [
677 self.formattree(inspect.getclasstree(classlist, 1), name)]
678 for key, value in classes:
679 contents.append(self.document(value, key, name, fdict, cdict))
680 result = result + self.bigsection(
681 'Classes', '#ffffff', '#ee77aa', join(contents))
682 if funcs:
683 contents = []
684 for key, value in funcs:
685 contents.append(self.document(value, key, name, fdict, cdict))
686 result = result + self.bigsection(
687 'Functions', '#ffffff', '#eeaa77', join(contents))
688 if data:
689 contents = []
690 for key, value in data:
691 contents.append(self.document(value, key))
692 result = result + self.bigsection(
693 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))
694 if hasattr(object, '__author__'):
695 contents = self.markup(str(object.__author__), self.preformat)
696 result = result + self.bigsection(
697 'Author', '#ffffff', '#7799ee', contents)
698 if hasattr(object, '__credits__'):
699 contents = self.markup(str(object.__credits__), self.preformat)
700 result = result + self.bigsection(
701 'Credits', '#ffffff', '#7799ee', contents)
703 return result
705 def docclass(self, object, name=None, mod=None, funcs={}, classes={},
706 *ignored):
707 """Produce HTML documentation for a class object."""
708 realname = object.__name__
709 name = name or realname
710 bases = object.__bases__
712 contents = []
713 push = contents.append
715 # Cute little class to pump out a horizontal rule between sections.
716 class HorizontalRule:
717 def __init__(self):
718 self.needone = 0
719 def maybe(self):
720 if self.needone:
721 push('<hr>\n')
722 self.needone = 1
723 hr = HorizontalRule()
725 # List the mro, if non-trivial.
726 mro = deque(inspect.getmro(object))
727 if len(mro) > 2:
728 hr.maybe()
729 push('<dl><dt>Method resolution order:</dt>\n')
730 for base in mro:
731 push('<dd>%s</dd>\n' % self.classlink(base,
732 object.__module__))
733 push('</dl>\n')
735 def spill(msg, attrs, predicate):
736 ok, attrs = _split_list(attrs, predicate)
737 if ok:
738 hr.maybe()
739 push(msg)
740 for name, kind, homecls, value in ok:
741 push(self.document(getattr(object, name), name, mod,
742 funcs, classes, mdict, object))
743 push('\n')
744 return attrs
746 def spilldescriptors(msg, attrs, predicate):
747 ok, attrs = _split_list(attrs, predicate)
748 if ok:
749 hr.maybe()
750 push(msg)
751 for name, kind, homecls, value in ok:
752 push(self._docdescriptor(name, value, mod))
753 return attrs
755 def spilldata(msg, attrs, predicate):
756 ok, attrs = _split_list(attrs, predicate)
757 if ok:
758 hr.maybe()
759 push(msg)
760 for name, kind, homecls, value in ok:
761 base = self.docother(getattr(object, name), name, mod)
762 if (hasattr(value, '__call__') or
763 inspect.isdatadescriptor(value)):
764 doc = getattr(value, "__doc__", None)
765 else:
766 doc = None
767 if doc is None:
768 push('<dl><dt>%s</dl>\n' % base)
769 else:
770 doc = self.markup(getdoc(value), self.preformat,
771 funcs, classes, mdict)
772 doc = '<dd><tt>%s</tt>' % doc
773 push('<dl><dt>%s%s</dl>\n' % (base, doc))
774 push('\n')
775 return attrs
777 attrs = filter(lambda data: visiblename(data[0]),
778 classify_class_attrs(object))
779 mdict = {}
780 for key, kind, homecls, value in attrs:
781 mdict[key] = anchor = '#' + name + '-' + key
782 value = getattr(object, key)
783 try:
784 # The value may not be hashable (e.g., a data attr with
785 # a dict or list value).
786 mdict[value] = anchor
787 except TypeError:
788 pass
790 while attrs:
791 if mro:
792 thisclass = mro.popleft()
793 else:
794 thisclass = attrs[0][2]
795 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
797 if thisclass is __builtin__.object:
798 attrs = inherited
799 continue
800 elif thisclass is object:
801 tag = 'defined here'
802 else:
803 tag = 'inherited from %s' % self.classlink(thisclass,
804 object.__module__)
805 tag += ':<br>\n'
807 # Sort attrs by name.
808 try:
809 attrs.sort(key=lambda t: t[0])
810 except TypeError:
811 attrs.sort(lambda t1, t2: cmp(t1[0], t2[0])) # 2.3 compat
813 # Pump out the attrs, segregated by kind.
814 attrs = spill('Methods %s' % tag, attrs,
815 lambda t: t[1] == 'method')
816 attrs = spill('Class methods %s' % tag, attrs,
817 lambda t: t[1] == 'class method')
818 attrs = spill('Static methods %s' % tag, attrs,
819 lambda t: t[1] == 'static method')
820 attrs = spilldescriptors('Data descriptors %s' % tag, attrs,
821 lambda t: t[1] == 'data descriptor')
822 attrs = spilldata('Data and other attributes %s' % tag, attrs,
823 lambda t: t[1] == 'data')
824 assert attrs == []
825 attrs = inherited
827 contents = ''.join(contents)
829 if name == realname:
830 title = '<a name="%s">class <strong>%s</strong></a>' % (
831 name, realname)
832 else:
833 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
834 name, name, realname)
835 if bases:
836 parents = []
837 for base in bases:
838 parents.append(self.classlink(base, object.__module__))
839 title = title + '(%s)' % join(parents, ', ')
840 doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict)
841 doc = doc and '<tt>%s<br>&nbsp;</tt>' % doc
843 return self.section(title, '#000000', '#ffc8d8', contents, 3, doc)
845 def formatvalue(self, object):
846 """Format an argument default value as text."""
847 return self.grey('=' + self.repr(object))
849 def docroutine(self, object, name=None, mod=None,
850 funcs={}, classes={}, methods={}, cl=None):
851 """Produce HTML documentation for a function or method object."""
852 realname = object.__name__
853 name = name or realname
854 anchor = (cl and cl.__name__ or '') + '-' + name
855 note = ''
856 skipdocs = 0
857 if inspect.ismethod(object):
858 imclass = object.im_class
859 if cl:
860 if imclass is not cl:
861 note = ' from ' + self.classlink(imclass, mod)
862 else:
863 if object.im_self is not None:
864 note = ' method of %s instance' % self.classlink(
865 object.im_self.__class__, mod)
866 else:
867 note = ' unbound %s method' % self.classlink(imclass,mod)
868 object = object.im_func
870 if name == realname:
871 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
872 else:
873 if (cl and realname in cl.__dict__ and
874 cl.__dict__[realname] is object):
875 reallink = '<a href="#%s">%s</a>' % (
876 cl.__name__ + '-' + realname, realname)
877 skipdocs = 1
878 else:
879 reallink = realname
880 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
881 anchor, name, reallink)
882 if inspect.isfunction(object):
883 args, varargs, varkw, defaults = inspect.getargspec(object)
884 argspec = inspect.formatargspec(
885 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
886 if realname == '<lambda>':
887 title = '<strong>%s</strong> <em>lambda</em> ' % name
888 argspec = argspec[1:-1] # remove parentheses
889 else:
890 argspec = '(...)'
892 decl = title + argspec + (note and self.grey(
893 '<font face="helvetica, arial">%s</font>' % note))
895 if skipdocs:
896 return '<dl><dt>%s</dt></dl>\n' % decl
897 else:
898 doc = self.markup(
899 getdoc(object), self.preformat, funcs, classes, methods)
900 doc = doc and '<dd><tt>%s</tt></dd>' % doc
901 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
903 def _docdescriptor(self, name, value, mod):
904 results = []
905 push = results.append
907 if name:
908 push('<dl><dt><strong>%s</strong></dt>\n' % name)
909 if value.__doc__ is not None:
910 doc = self.markup(getdoc(value), self.preformat)
911 push('<dd><tt>%s</tt></dd>\n' % doc)
912 push('</dl>\n')
914 return ''.join(results)
916 def docproperty(self, object, name=None, mod=None, cl=None):
917 """Produce html documentation for a property."""
918 return self._docdescriptor(name, object, mod)
920 def docother(self, object, name=None, mod=None, *ignored):
921 """Produce HTML documentation for a data object."""
922 lhs = name and '<strong>%s</strong> = ' % name or ''
923 return lhs + self.repr(object)
925 def docdata(self, object, name=None, mod=None, cl=None):
926 """Produce html documentation for a data descriptor."""
927 return self._docdescriptor(name, object, mod)
929 def index(self, dir, shadowed=None):
930 """Generate an HTML index for a directory of modules."""
931 modpkgs = []
932 if shadowed is None: shadowed = {}
933 for importer, name, ispkg in pkgutil.iter_modules([dir]):
934 modpkgs.append((name, '', ispkg, name in shadowed))
935 shadowed[name] = 1
937 modpkgs.sort()
938 contents = self.multicolumn(modpkgs, self.modpkglink)
939 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
941 # -------------------------------------------- text documentation generator
943 class TextRepr(Repr):
944 """Class for safely making a text representation of a Python object."""
945 def __init__(self):
946 Repr.__init__(self)
947 self.maxlist = self.maxtuple = 20
948 self.maxdict = 10
949 self.maxstring = self.maxother = 100
951 def repr1(self, x, level):
952 if hasattr(type(x), '__name__'):
953 methodname = 'repr_' + join(split(type(x).__name__), '_')
954 if hasattr(self, methodname):
955 return getattr(self, methodname)(x, level)
956 return cram(stripid(repr(x)), self.maxother)
958 def repr_string(self, x, level):
959 test = cram(x, self.maxstring)
960 testrepr = repr(test)
961 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
962 # Backslashes are only literal in the string and are never
963 # needed to make any special characters, so show a raw string.
964 return 'r' + testrepr[0] + test + testrepr[0]
965 return testrepr
967 repr_str = repr_string
969 def repr_instance(self, x, level):
970 try:
971 return cram(stripid(repr(x)), self.maxstring)
972 except:
973 return '<%s instance>' % x.__class__.__name__
975 class TextDoc(Doc):
976 """Formatter class for text documentation."""
978 # ------------------------------------------- text formatting utilities
980 _repr_instance = TextRepr()
981 repr = _repr_instance.repr
983 def bold(self, text):
984 """Format a string in bold by overstriking."""
985 return join(map(lambda ch: ch + '\b' + ch, text), '')
987 def indent(self, text, prefix=' '):
988 """Indent text by prepending a given prefix to each line."""
989 if not text: return ''
990 lines = split(text, '\n')
991 lines = map(lambda line, prefix=prefix: prefix + line, lines)
992 if lines: lines[-1] = rstrip(lines[-1])
993 return join(lines, '\n')
995 def section(self, title, contents):
996 """Format a section with a given heading."""
997 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
999 # ---------------------------------------------- type-specific routines
1001 def formattree(self, tree, modname, parent=None, prefix=''):
1002 """Render in text a class tree as returned by inspect.getclasstree()."""
1003 result = ''
1004 for entry in tree:
1005 if type(entry) is type(()):
1006 c, bases = entry
1007 result = result + prefix + classname(c, modname)
1008 if bases and bases != (parent,):
1009 parents = map(lambda c, m=modname: classname(c, m), bases)
1010 result = result + '(%s)' % join(parents, ', ')
1011 result = result + '\n'
1012 elif type(entry) is type([]):
1013 result = result + self.formattree(
1014 entry, modname, c, prefix + ' ')
1015 return result
1017 def docmodule(self, object, name=None, mod=None):
1018 """Produce text documentation for a given module object."""
1019 name = object.__name__ # ignore the passed-in name
1020 synop, desc = splitdoc(getdoc(object))
1021 result = self.section('NAME', name + (synop and ' - ' + synop))
1023 try:
1024 all = object.__all__
1025 except AttributeError:
1026 all = None
1028 try:
1029 file = inspect.getabsfile(object)
1030 except TypeError:
1031 file = '(built-in)'
1032 result = result + self.section('FILE', file)
1034 docloc = self.getdocloc(object)
1035 if docloc is not None:
1036 result = result + self.section('MODULE DOCS', docloc)
1038 if desc:
1039 result = result + self.section('DESCRIPTION', desc)
1041 classes = []
1042 for key, value in inspect.getmembers(object, inspect.isclass):
1043 # if __all__ exists, believe it. Otherwise use old heuristic.
1044 if (all is not None
1045 or (inspect.getmodule(value) or object) is object):
1046 if visiblename(key, all):
1047 classes.append((key, value))
1048 funcs = []
1049 for key, value in inspect.getmembers(object, inspect.isroutine):
1050 # if __all__ exists, believe it. Otherwise use old heuristic.
1051 if (all is not None or
1052 inspect.isbuiltin(value) or inspect.getmodule(value) is object):
1053 if visiblename(key, all):
1054 funcs.append((key, value))
1055 data = []
1056 for key, value in inspect.getmembers(object, isdata):
1057 if visiblename(key, all):
1058 data.append((key, value))
1060 modpkgs = []
1061 modpkgs_names = set()
1062 if hasattr(object, '__path__'):
1063 for importer, modname, ispkg in pkgutil.iter_modules(object.__path__):
1064 modpkgs_names.add(modname)
1065 if ispkg:
1066 modpkgs.append(modname + ' (package)')
1067 else:
1068 modpkgs.append(modname)
1070 modpkgs.sort()
1071 result = result + self.section(
1072 'PACKAGE CONTENTS', join(modpkgs, '\n'))
1074 # Detect submodules as sometimes created by C extensions
1075 submodules = []
1076 for key, value in inspect.getmembers(object, inspect.ismodule):
1077 if value.__name__.startswith(name + '.') and key not in modpkgs_names:
1078 submodules.append(key)
1079 if submodules:
1080 submodules.sort()
1081 result = result + self.section(
1082 'SUBMODULES', join(submodules, '\n'))
1084 if classes:
1085 classlist = map(lambda key_value: key_value[1], classes)
1086 contents = [self.formattree(
1087 inspect.getclasstree(classlist, 1), name)]
1088 for key, value in classes:
1089 contents.append(self.document(value, key, name))
1090 result = result + self.section('CLASSES', join(contents, '\n'))
1092 if funcs:
1093 contents = []
1094 for key, value in funcs:
1095 contents.append(self.document(value, key, name))
1096 result = result + self.section('FUNCTIONS', join(contents, '\n'))
1098 if data:
1099 contents = []
1100 for key, value in data:
1101 contents.append(self.docother(value, key, name, maxlen=70))
1102 result = result + self.section('DATA', join(contents, '\n'))
1104 if hasattr(object, '__version__'):
1105 version = str(object.__version__)
1106 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
1107 version = strip(version[11:-1])
1108 result = result + self.section('VERSION', version)
1109 if hasattr(object, '__date__'):
1110 result = result + self.section('DATE', str(object.__date__))
1111 if hasattr(object, '__author__'):
1112 result = result + self.section('AUTHOR', str(object.__author__))
1113 if hasattr(object, '__credits__'):
1114 result = result + self.section('CREDITS', str(object.__credits__))
1115 return result
1117 def docclass(self, object, name=None, mod=None):
1118 """Produce text documentation for a given class object."""
1119 realname = object.__name__
1120 name = name or realname
1121 bases = object.__bases__
1123 def makename(c, m=object.__module__):
1124 return classname(c, m)
1126 if name == realname:
1127 title = 'class ' + self.bold(realname)
1128 else:
1129 title = self.bold(name) + ' = class ' + realname
1130 if bases:
1131 parents = map(makename, bases)
1132 title = title + '(%s)' % join(parents, ', ')
1134 doc = getdoc(object)
1135 contents = doc and [doc + '\n'] or []
1136 push = contents.append
1138 # List the mro, if non-trivial.
1139 mro = deque(inspect.getmro(object))
1140 if len(mro) > 2:
1141 push("Method resolution order:")
1142 for base in mro:
1143 push(' ' + makename(base))
1144 push('')
1146 # Cute little class to pump out a horizontal rule between sections.
1147 class HorizontalRule:
1148 def __init__(self):
1149 self.needone = 0
1150 def maybe(self):
1151 if self.needone:
1152 push('-' * 70)
1153 self.needone = 1
1154 hr = HorizontalRule()
1156 def spill(msg, attrs, predicate):
1157 ok, attrs = _split_list(attrs, predicate)
1158 if ok:
1159 hr.maybe()
1160 push(msg)
1161 for name, kind, homecls, value in ok:
1162 push(self.document(getattr(object, name),
1163 name, mod, object))
1164 return attrs
1166 def spilldescriptors(msg, attrs, predicate):
1167 ok, attrs = _split_list(attrs, predicate)
1168 if ok:
1169 hr.maybe()
1170 push(msg)
1171 for name, kind, homecls, value in ok:
1172 push(self._docdescriptor(name, value, mod))
1173 return attrs
1175 def spilldata(msg, attrs, predicate):
1176 ok, attrs = _split_list(attrs, predicate)
1177 if ok:
1178 hr.maybe()
1179 push(msg)
1180 for name, kind, homecls, value in ok:
1181 if (hasattr(value, '__call__') or
1182 inspect.isdatadescriptor(value)):
1183 doc = getdoc(value)
1184 else:
1185 doc = None
1186 push(self.docother(getattr(object, name),
1187 name, mod, maxlen=70, doc=doc) + '\n')
1188 return attrs
1190 attrs = filter(lambda data: visiblename(data[0]),
1191 classify_class_attrs(object))
1192 while attrs:
1193 if mro:
1194 thisclass = mro.popleft()
1195 else:
1196 thisclass = attrs[0][2]
1197 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
1199 if thisclass is __builtin__.object:
1200 attrs = inherited
1201 continue
1202 elif thisclass is object:
1203 tag = "defined here"
1204 else:
1205 tag = "inherited from %s" % classname(thisclass,
1206 object.__module__)
1208 # Sort attrs by name.
1209 attrs.sort()
1211 # Pump out the attrs, segregated by kind.
1212 attrs = spill("Methods %s:\n" % tag, attrs,
1213 lambda t: t[1] == 'method')
1214 attrs = spill("Class methods %s:\n" % tag, attrs,
1215 lambda t: t[1] == 'class method')
1216 attrs = spill("Static methods %s:\n" % tag, attrs,
1217 lambda t: t[1] == 'static method')
1218 attrs = spilldescriptors("Data descriptors %s:\n" % tag, attrs,
1219 lambda t: t[1] == 'data descriptor')
1220 attrs = spilldata("Data and other attributes %s:\n" % tag, attrs,
1221 lambda t: t[1] == 'data')
1222 assert attrs == []
1223 attrs = inherited
1225 contents = '\n'.join(contents)
1226 if not contents:
1227 return title + '\n'
1228 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
1230 def formatvalue(self, object):
1231 """Format an argument default value as text."""
1232 return '=' + self.repr(object)
1234 def docroutine(self, object, name=None, mod=None, cl=None):
1235 """Produce text documentation for a function or method object."""
1236 realname = object.__name__
1237 name = name or realname
1238 note = ''
1239 skipdocs = 0
1240 if inspect.ismethod(object):
1241 imclass = object.im_class
1242 if cl:
1243 if imclass is not cl:
1244 note = ' from ' + classname(imclass, mod)
1245 else:
1246 if object.im_self is not None:
1247 note = ' method of %s instance' % classname(
1248 object.im_self.__class__, mod)
1249 else:
1250 note = ' unbound %s method' % classname(imclass,mod)
1251 object = object.im_func
1253 if name == realname:
1254 title = self.bold(realname)
1255 else:
1256 if (cl and realname in cl.__dict__ and
1257 cl.__dict__[realname] is object):
1258 skipdocs = 1
1259 title = self.bold(name) + ' = ' + realname
1260 if inspect.isfunction(object):
1261 args, varargs, varkw, defaults = inspect.getargspec(object)
1262 argspec = inspect.formatargspec(
1263 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
1264 if realname == '<lambda>':
1265 title = self.bold(name) + ' lambda '
1266 argspec = argspec[1:-1] # remove parentheses
1267 else:
1268 argspec = '(...)'
1269 decl = title + argspec + note
1271 if skipdocs:
1272 return decl + '\n'
1273 else:
1274 doc = getdoc(object) or ''
1275 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
1277 def _docdescriptor(self, name, value, mod):
1278 results = []
1279 push = results.append
1281 if name:
1282 push(self.bold(name))
1283 push('\n')
1284 doc = getdoc(value) or ''
1285 if doc:
1286 push(self.indent(doc))
1287 push('\n')
1288 return ''.join(results)
1290 def docproperty(self, object, name=None, mod=None, cl=None):
1291 """Produce text documentation for a property."""
1292 return self._docdescriptor(name, object, mod)
1294 def docdata(self, object, name=None, mod=None, cl=None):
1295 """Produce text documentation for a data descriptor."""
1296 return self._docdescriptor(name, object, mod)
1298 def docother(self, object, name=None, mod=None, parent=None, maxlen=None, doc=None):
1299 """Produce text documentation for a data object."""
1300 repr = self.repr(object)
1301 if maxlen:
1302 line = (name and name + ' = ' or '') + repr
1303 chop = maxlen - len(line)
1304 if chop < 0: repr = repr[:chop] + '...'
1305 line = (name and self.bold(name) + ' = ' or '') + repr
1306 if doc is not None:
1307 line += '\n' + self.indent(str(doc))
1308 return line
1310 # --------------------------------------------------------- user interfaces
1312 def pager(text):
1313 """The first time this is called, determine what kind of pager to use."""
1314 global pager
1315 pager = getpager()
1316 pager(text)
1318 def getpager():
1319 """Decide what method to use for paging through text."""
1320 if type(sys.stdout) is not types.FileType:
1321 return plainpager
1322 if not sys.stdin.isatty() or not sys.stdout.isatty():
1323 return plainpager
1324 if 'PAGER' in os.environ:
1325 if sys.platform == 'win32': # pipes completely broken in Windows
1326 return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
1327 elif os.environ.get('TERM') in ('dumb', 'emacs'):
1328 return lambda text: pipepager(plain(text), os.environ['PAGER'])
1329 else:
1330 return lambda text: pipepager(text, os.environ['PAGER'])
1331 if os.environ.get('TERM') in ('dumb', 'emacs'):
1332 return plainpager
1333 if sys.platform == 'win32' or sys.platform.startswith('os2'):
1334 return lambda text: tempfilepager(plain(text), 'more <')
1335 if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0:
1336 return lambda text: pipepager(text, 'less')
1338 import tempfile
1339 (fd, filename) = tempfile.mkstemp()
1340 os.close(fd)
1341 try:
1342 if hasattr(os, 'system') and os.system('more "%s"' % filename) == 0:
1343 return lambda text: pipepager(text, 'more')
1344 else:
1345 return ttypager
1346 finally:
1347 os.unlink(filename)
1349 def plain(text):
1350 """Remove boldface formatting from text."""
1351 return re.sub('.\b', '', text)
1353 def pipepager(text, cmd):
1354 """Page through text by feeding it to another program."""
1355 pipe = os.popen(cmd, 'w')
1356 try:
1357 pipe.write(text)
1358 pipe.close()
1359 except IOError:
1360 pass # Ignore broken pipes caused by quitting the pager program.
1362 def tempfilepager(text, cmd):
1363 """Page through text by invoking a program on a temporary file."""
1364 import tempfile
1365 filename = tempfile.mktemp()
1366 file = open(filename, 'w')
1367 file.write(text)
1368 file.close()
1369 try:
1370 os.system(cmd + ' "' + filename + '"')
1371 finally:
1372 os.unlink(filename)
1374 def ttypager(text):
1375 """Page through text on a text terminal."""
1376 lines = split(plain(text), '\n')
1377 try:
1378 import tty
1379 fd = sys.stdin.fileno()
1380 old = tty.tcgetattr(fd)
1381 tty.setcbreak(fd)
1382 getchar = lambda: sys.stdin.read(1)
1383 except (ImportError, AttributeError):
1384 tty = None
1385 getchar = lambda: sys.stdin.readline()[:-1][:1]
1387 try:
1388 r = inc = os.environ.get('LINES', 25) - 1
1389 sys.stdout.write(join(lines[:inc], '\n') + '\n')
1390 while lines[r:]:
1391 sys.stdout.write('-- more --')
1392 sys.stdout.flush()
1393 c = getchar()
1395 if c in ('q', 'Q'):
1396 sys.stdout.write('\r \r')
1397 break
1398 elif c in ('\r', '\n'):
1399 sys.stdout.write('\r \r' + lines[r] + '\n')
1400 r = r + 1
1401 continue
1402 if c in ('b', 'B', '\x1b'):
1403 r = r - inc - inc
1404 if r < 0: r = 0
1405 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1406 r = r + inc
1408 finally:
1409 if tty:
1410 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1412 def plainpager(text):
1413 """Simply print unformatted text. This is the ultimate fallback."""
1414 sys.stdout.write(plain(text))
1416 def describe(thing):
1417 """Produce a short description of the given thing."""
1418 if inspect.ismodule(thing):
1419 if thing.__name__ in sys.builtin_module_names:
1420 return 'built-in module ' + thing.__name__
1421 if hasattr(thing, '__path__'):
1422 return 'package ' + thing.__name__
1423 else:
1424 return 'module ' + thing.__name__
1425 if inspect.isbuiltin(thing):
1426 return 'built-in function ' + thing.__name__
1427 if inspect.isgetsetdescriptor(thing):
1428 return 'getset descriptor %s.%s.%s' % (
1429 thing.__objclass__.__module__, thing.__objclass__.__name__,
1430 thing.__name__)
1431 if inspect.ismemberdescriptor(thing):
1432 return 'member descriptor %s.%s.%s' % (
1433 thing.__objclass__.__module__, thing.__objclass__.__name__,
1434 thing.__name__)
1435 if inspect.isclass(thing):
1436 return 'class ' + thing.__name__
1437 if inspect.isfunction(thing):
1438 return 'function ' + thing.__name__
1439 if inspect.ismethod(thing):
1440 return 'method ' + thing.__name__
1441 if type(thing) is types.InstanceType:
1442 return 'instance of ' + thing.__class__.__name__
1443 return type(thing).__name__
1445 def locate(path, forceload=0):
1446 """Locate an object by name or dotted path, importing as necessary."""
1447 parts = [part for part in split(path, '.') if part]
1448 module, n = None, 0
1449 while n < len(parts):
1450 nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
1451 if nextmodule: module, n = nextmodule, n + 1
1452 else: break
1453 if module:
1454 object = module
1455 for part in parts[n:]:
1456 try: object = getattr(object, part)
1457 except AttributeError: return None
1458 return object
1459 else:
1460 if hasattr(__builtin__, path):
1461 return getattr(__builtin__, path)
1463 # --------------------------------------- interactive interpreter interface
1465 text = TextDoc()
1466 html = HTMLDoc()
1468 class _OldStyleClass: pass
1469 _OLD_INSTANCE_TYPE = type(_OldStyleClass())
1471 def resolve(thing, forceload=0):
1472 """Given an object or a path to an object, get the object and its name."""
1473 if isinstance(thing, str):
1474 object = locate(thing, forceload)
1475 if not object:
1476 raise ImportError, 'no Python documentation found for %r' % thing
1477 return object, thing
1478 else:
1479 return thing, getattr(thing, '__name__', None)
1481 def render_doc(thing, title='Python Library Documentation: %s', forceload=0):
1482 """Render text documentation, given an object or a path to an object."""
1483 object, name = resolve(thing, forceload)
1484 desc = describe(object)
1485 module = inspect.getmodule(object)
1486 if name and '.' in name:
1487 desc += ' in ' + name[:name.rfind('.')]
1488 elif module and module is not object:
1489 desc += ' in module ' + module.__name__
1490 if type(object) is _OLD_INSTANCE_TYPE:
1491 # If the passed object is an instance of an old-style class,
1492 # document its available methods instead of its value.
1493 object = object.__class__
1494 elif not (inspect.ismodule(object) or
1495 inspect.isclass(object) or
1496 inspect.isroutine(object) or
1497 inspect.isgetsetdescriptor(object) or
1498 inspect.ismemberdescriptor(object) or
1499 isinstance(object, property)):
1500 # If the passed object is a piece of data or an instance,
1501 # document its available methods instead of its value.
1502 object = type(object)
1503 desc += ' object'
1504 return title % desc + '\n\n' + text.document(object, name)
1506 def doc(thing, title='Python Library Documentation: %s', forceload=0):
1507 """Display text documentation, given an object or a path to an object."""
1508 try:
1509 pager(render_doc(thing, title, forceload))
1510 except (ImportError, ErrorDuringImport), value:
1511 print value
1513 def writedoc(thing, forceload=0):
1514 """Write HTML documentation to a file in the current directory."""
1515 try:
1516 object, name = resolve(thing, forceload)
1517 page = html.page(describe(object), html.document(object, name))
1518 file = open(name + '.html', 'w')
1519 file.write(page)
1520 file.close()
1521 print 'wrote', name + '.html'
1522 except (ImportError, ErrorDuringImport), value:
1523 print value
1525 def writedocs(dir, pkgpath='', done=None):
1526 """Write out HTML documentation for all modules in a directory tree."""
1527 if done is None: done = {}
1528 for importer, modname, ispkg in pkgutil.walk_packages([dir], pkgpath):
1529 writedoc(modname)
1530 return
1532 class Helper:
1534 # These dictionaries map a topic name to either an alias, or a tuple
1535 # (label, seealso-items). The "label" is the label of the corresponding
1536 # section in the .rst file under Doc/ and an index into the dictionary
1537 # in pydoc_data/topics.py.
1539 # CAUTION: if you change one of these dictionaries, be sure to adapt the
1540 # list of needed labels in Doc/tools/sphinxext/pyspecific.py and
1541 # regenerate the pydoc_data/topics.py file by running
1542 # make pydoc-topics
1543 # in Doc/ and copying the output file into the Lib/ directory.
1545 keywords = {
1546 'and': 'BOOLEAN',
1547 'as': 'with',
1548 'assert': ('assert', ''),
1549 'break': ('break', 'while for'),
1550 'class': ('class', 'CLASSES SPECIALMETHODS'),
1551 'continue': ('continue', 'while for'),
1552 'def': ('function', ''),
1553 'del': ('del', 'BASICMETHODS'),
1554 'elif': 'if',
1555 'else': ('else', 'while for'),
1556 'except': 'try',
1557 'exec': ('exec', ''),
1558 'finally': 'try',
1559 'for': ('for', 'break continue while'),
1560 'from': 'import',
1561 'global': ('global', 'NAMESPACES'),
1562 'if': ('if', 'TRUTHVALUE'),
1563 'import': ('import', 'MODULES'),
1564 'in': ('in', 'SEQUENCEMETHODS2'),
1565 'is': 'COMPARISON',
1566 'lambda': ('lambda', 'FUNCTIONS'),
1567 'not': 'BOOLEAN',
1568 'or': 'BOOLEAN',
1569 'pass': ('pass', ''),
1570 'print': ('print', ''),
1571 'raise': ('raise', 'EXCEPTIONS'),
1572 'return': ('return', 'FUNCTIONS'),
1573 'try': ('try', 'EXCEPTIONS'),
1574 'while': ('while', 'break continue if TRUTHVALUE'),
1575 'with': ('with', 'CONTEXTMANAGERS EXCEPTIONS yield'),
1576 'yield': ('yield', ''),
1578 # Either add symbols to this dictionary or to the symbols dictionary
1579 # directly: Whichever is easier. They are merged later.
1580 _symbols_inverse = {
1581 'STRINGS' : ("'", "'''", "r'", "u'", '"""', '"', 'r"', 'u"'),
1582 'OPERATORS' : ('+', '-', '*', '**', '/', '//', '%', '<<', '>>', '&',
1583 '|', '^', '~', '<', '>', '<=', '>=', '==', '!=', '<>'),
1584 'COMPARISON' : ('<', '>', '<=', '>=', '==', '!=', '<>'),
1585 'UNARY' : ('-', '~'),
1586 'AUGMENTEDASSIGNMENT' : ('+=', '-=', '*=', '/=', '%=', '&=', '|=',
1587 '^=', '<<=', '>>=', '**=', '//='),
1588 'BITWISE' : ('<<', '>>', '&', '|', '^', '~'),
1589 'COMPLEX' : ('j', 'J')
1591 symbols = {
1592 '%': 'OPERATORS FORMATTING',
1593 '**': 'POWER',
1594 ',': 'TUPLES LISTS FUNCTIONS',
1595 '.': 'ATTRIBUTES FLOAT MODULES OBJECTS',
1596 '...': 'ELLIPSIS',
1597 ':': 'SLICINGS DICTIONARYLITERALS',
1598 '@': 'def class',
1599 '\\': 'STRINGS',
1600 '_': 'PRIVATENAMES',
1601 '__': 'PRIVATENAMES SPECIALMETHODS',
1602 '`': 'BACKQUOTES',
1603 '(': 'TUPLES FUNCTIONS CALLS',
1604 ')': 'TUPLES FUNCTIONS CALLS',
1605 '[': 'LISTS SUBSCRIPTS SLICINGS',
1606 ']': 'LISTS SUBSCRIPTS SLICINGS'
1608 for topic, symbols_ in _symbols_inverse.iteritems():
1609 for symbol in symbols_:
1610 topics = symbols.get(symbol, topic)
1611 if topic not in topics:
1612 topics = topics + ' ' + topic
1613 symbols[symbol] = topics
1615 topics = {
1616 'TYPES': ('types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS '
1617 'FUNCTIONS CLASSES MODULES FILES inspect'),
1618 'STRINGS': ('strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING '
1619 'TYPES'),
1620 'STRINGMETHODS': ('string-methods', 'STRINGS FORMATTING'),
1621 'FORMATTING': ('formatstrings', 'OPERATORS'),
1622 'UNICODE': ('strings', 'encodings unicode SEQUENCES STRINGMETHODS '
1623 'FORMATTING TYPES'),
1624 'NUMBERS': ('numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1625 'INTEGER': ('integers', 'int range'),
1626 'FLOAT': ('floating', 'float math'),
1627 'COMPLEX': ('imaginary', 'complex cmath'),
1628 'SEQUENCES': ('typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
1629 'MAPPINGS': 'DICTIONARIES',
1630 'FUNCTIONS': ('typesfunctions', 'def TYPES'),
1631 'METHODS': ('typesmethods', 'class def CLASSES TYPES'),
1632 'CODEOBJECTS': ('bltin-code-objects', 'compile FUNCTIONS TYPES'),
1633 'TYPEOBJECTS': ('bltin-type-objects', 'types TYPES'),
1634 'FRAMEOBJECTS': 'TYPES',
1635 'TRACEBACKS': 'TYPES',
1636 'NONE': ('bltin-null-object', ''),
1637 'ELLIPSIS': ('bltin-ellipsis-object', 'SLICINGS'),
1638 'FILES': ('bltin-file-objects', ''),
1639 'SPECIALATTRIBUTES': ('specialattrs', ''),
1640 'CLASSES': ('types', 'class SPECIALMETHODS PRIVATENAMES'),
1641 'MODULES': ('typesmodules', 'import'),
1642 'PACKAGES': 'import',
1643 'EXPRESSIONS': ('operator-summary', 'lambda or and not in is BOOLEAN '
1644 'COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER '
1645 'UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES '
1646 'LISTS DICTIONARIES BACKQUOTES'),
1647 'OPERATORS': 'EXPRESSIONS',
1648 'PRECEDENCE': 'EXPRESSIONS',
1649 'OBJECTS': ('objects', 'TYPES'),
1650 'SPECIALMETHODS': ('specialnames', 'BASICMETHODS ATTRIBUTEMETHODS '
1651 'CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS '
1652 'SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
1653 'BASICMETHODS': ('customization', 'cmp hash repr str SPECIALMETHODS'),
1654 'ATTRIBUTEMETHODS': ('attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1655 'CALLABLEMETHODS': ('callable-types', 'CALLS SPECIALMETHODS'),
1656 'SEQUENCEMETHODS1': ('sequence-types', 'SEQUENCES SEQUENCEMETHODS2 '
1657 'SPECIALMETHODS'),
1658 'SEQUENCEMETHODS2': ('sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 '
1659 'SPECIALMETHODS'),
1660 'MAPPINGMETHODS': ('sequence-types', 'MAPPINGS SPECIALMETHODS'),
1661 'NUMBERMETHODS': ('numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT '
1662 'SPECIALMETHODS'),
1663 'EXECUTION': ('execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'),
1664 'NAMESPACES': ('naming', 'global ASSIGNMENT DELETION DYNAMICFEATURES'),
1665 'DYNAMICFEATURES': ('dynamic-features', ''),
1666 'SCOPING': 'NAMESPACES',
1667 'FRAMES': 'NAMESPACES',
1668 'EXCEPTIONS': ('exceptions', 'try except finally raise'),
1669 'COERCIONS': ('coercion-rules','CONVERSIONS'),
1670 'CONVERSIONS': ('conversions', 'COERCIONS'),
1671 'IDENTIFIERS': ('identifiers', 'keywords SPECIALIDENTIFIERS'),
1672 'SPECIALIDENTIFIERS': ('id-classes', ''),
1673 'PRIVATENAMES': ('atom-identifiers', ''),
1674 'LITERALS': ('atom-literals', 'STRINGS BACKQUOTES NUMBERS '
1675 'TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1676 'TUPLES': 'SEQUENCES',
1677 'TUPLELITERALS': ('exprlists', 'TUPLES LITERALS'),
1678 'LISTS': ('typesseq-mutable', 'LISTLITERALS'),
1679 'LISTLITERALS': ('lists', 'LISTS LITERALS'),
1680 'DICTIONARIES': ('typesmapping', 'DICTIONARYLITERALS'),
1681 'DICTIONARYLITERALS': ('dict', 'DICTIONARIES LITERALS'),
1682 'BACKQUOTES': ('string-conversions', 'repr str STRINGS LITERALS'),
1683 'ATTRIBUTES': ('attribute-references', 'getattr hasattr setattr '
1684 'ATTRIBUTEMETHODS'),
1685 'SUBSCRIPTS': ('subscriptions', 'SEQUENCEMETHODS1'),
1686 'SLICINGS': ('slicings', 'SEQUENCEMETHODS2'),
1687 'CALLS': ('calls', 'EXPRESSIONS'),
1688 'POWER': ('power', 'EXPRESSIONS'),
1689 'UNARY': ('unary', 'EXPRESSIONS'),
1690 'BINARY': ('binary', 'EXPRESSIONS'),
1691 'SHIFTING': ('shifting', 'EXPRESSIONS'),
1692 'BITWISE': ('bitwise', 'EXPRESSIONS'),
1693 'COMPARISON': ('comparisons', 'EXPRESSIONS BASICMETHODS'),
1694 'BOOLEAN': ('booleans', 'EXPRESSIONS TRUTHVALUE'),
1695 'ASSERTION': 'assert',
1696 'ASSIGNMENT': ('assignment', 'AUGMENTEDASSIGNMENT'),
1697 'AUGMENTEDASSIGNMENT': ('augassign', 'NUMBERMETHODS'),
1698 'DELETION': 'del',
1699 'PRINTING': 'print',
1700 'RETURNING': 'return',
1701 'IMPORTING': 'import',
1702 'CONDITIONAL': 'if',
1703 'LOOPING': ('compound', 'for while break continue'),
1704 'TRUTHVALUE': ('truth', 'if while and or not BASICMETHODS'),
1705 'DEBUGGING': ('debugger', 'pdb'),
1706 'CONTEXTMANAGERS': ('context-managers', 'with'),
1709 def __init__(self, input, output):
1710 self.input = input
1711 self.output = output
1713 def __repr__(self):
1714 if inspect.stack()[1][3] == '?':
1715 self()
1716 return ''
1717 return '<pydoc.Helper instance>'
1719 def __call__(self, request=None):
1720 if request is not None:
1721 self.help(request)
1722 else:
1723 self.intro()
1724 self.interact()
1725 self.output.write('''
1726 You are now leaving help and returning to the Python interpreter.
1727 If you want to ask for help on a particular object directly from the
1728 interpreter, you can type "help(object)". Executing "help('string')"
1729 has the same effect as typing a particular string at the help> prompt.
1730 ''')
1732 def interact(self):
1733 self.output.write('\n')
1734 while True:
1735 try:
1736 request = self.getline('help> ')
1737 if not request: break
1738 except (KeyboardInterrupt, EOFError):
1739 break
1740 request = strip(replace(request, '"', '', "'", ''))
1741 if lower(request) in ('q', 'quit'): break
1742 self.help(request)
1744 def getline(self, prompt):
1745 """Read one line, using raw_input when available."""
1746 if self.input is sys.stdin:
1747 return raw_input(prompt)
1748 else:
1749 self.output.write(prompt)
1750 self.output.flush()
1751 return self.input.readline()
1753 def help(self, request):
1754 if type(request) is type(''):
1755 request = request.strip()
1756 if request == 'help': self.intro()
1757 elif request == 'keywords': self.listkeywords()
1758 elif request == 'symbols': self.listsymbols()
1759 elif request == 'topics': self.listtopics()
1760 elif request == 'modules': self.listmodules()
1761 elif request[:8] == 'modules ':
1762 self.listmodules(split(request)[1])
1763 elif request in self.symbols: self.showsymbol(request)
1764 elif request in self.keywords: self.showtopic(request)
1765 elif request in self.topics: self.showtopic(request)
1766 elif request: doc(request, 'Help on %s:')
1767 elif isinstance(request, Helper): self()
1768 else: doc(request, 'Help on %s:')
1769 self.output.write('\n')
1771 def intro(self):
1772 self.output.write('''
1773 Welcome to Python %s! This is the online help utility.
1775 If this is your first time using Python, you should definitely check out
1776 the tutorial on the Internet at http://docs.python.org/tutorial/.
1778 Enter the name of any module, keyword, or topic to get help on writing
1779 Python programs and using Python modules. To quit this help utility and
1780 return to the interpreter, just type "quit".
1782 To get a list of available modules, keywords, or topics, type "modules",
1783 "keywords", or "topics". Each module also comes with a one-line summary
1784 of what it does; to list the modules whose summaries contain a given word
1785 such as "spam", type "modules spam".
1786 ''' % sys.version[:3])
1788 def list(self, items, columns=4, width=80):
1789 items = items[:]
1790 items.sort()
1791 colw = width / columns
1792 rows = (len(items) + columns - 1) / columns
1793 for row in range(rows):
1794 for col in range(columns):
1795 i = col * rows + row
1796 if i < len(items):
1797 self.output.write(items[i])
1798 if col < columns - 1:
1799 self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1800 self.output.write('\n')
1802 def listkeywords(self):
1803 self.output.write('''
1804 Here is a list of the Python keywords. Enter any keyword to get more help.
1806 ''')
1807 self.list(self.keywords.keys())
1809 def listsymbols(self):
1810 self.output.write('''
1811 Here is a list of the punctuation symbols which Python assigns special meaning
1812 to. Enter any symbol to get more help.
1814 ''')
1815 self.list(self.symbols.keys())
1817 def listtopics(self):
1818 self.output.write('''
1819 Here is a list of available topics. Enter any topic name to get more help.
1821 ''')
1822 self.list(self.topics.keys())
1824 def showtopic(self, topic, more_xrefs=''):
1825 try:
1826 import pydoc_data.topics
1827 except ImportError:
1828 self.output.write('''
1829 Sorry, topic and keyword documentation is not available because the
1830 module "pydoc_data.topics" could not be found.
1831 ''')
1832 return
1833 target = self.topics.get(topic, self.keywords.get(topic))
1834 if not target:
1835 self.output.write('no documentation found for %s\n' % repr(topic))
1836 return
1837 if type(target) is type(''):
1838 return self.showtopic(target, more_xrefs)
1840 label, xrefs = target
1841 try:
1842 doc = pydoc_data.topics.topics[label]
1843 except KeyError:
1844 self.output.write('no documentation found for %s\n' % repr(topic))
1845 return
1846 pager(strip(doc) + '\n')
1847 if more_xrefs:
1848 xrefs = (xrefs or '') + ' ' + more_xrefs
1849 if xrefs:
1850 import StringIO, formatter
1851 buffer = StringIO.StringIO()
1852 formatter.DumbWriter(buffer).send_flowing_data(
1853 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1854 self.output.write('\n%s\n' % buffer.getvalue())
1856 def showsymbol(self, symbol):
1857 target = self.symbols[symbol]
1858 topic, _, xrefs = target.partition(' ')
1859 self.showtopic(topic, xrefs)
1861 def listmodules(self, key=''):
1862 if key:
1863 self.output.write('''
1864 Here is a list of matching modules. Enter any module name to get more help.
1866 ''')
1867 apropos(key)
1868 else:
1869 self.output.write('''
1870 Please wait a moment while I gather a list of all available modules...
1872 ''')
1873 modules = {}
1874 def callback(path, modname, desc, modules=modules):
1875 if modname and modname[-9:] == '.__init__':
1876 modname = modname[:-9] + ' (package)'
1877 if find(modname, '.') < 0:
1878 modules[modname] = 1
1879 def onerror(modname):
1880 callback(None, modname, None)
1881 ModuleScanner().run(callback, onerror=onerror)
1882 self.list(modules.keys())
1883 self.output.write('''
1884 Enter any module name to get more help. Or, type "modules spam" to search
1885 for modules whose descriptions contain the word "spam".
1886 ''')
1888 help = Helper(sys.stdin, sys.stdout)
1890 class Scanner:
1891 """A generic tree iterator."""
1892 def __init__(self, roots, children, descendp):
1893 self.roots = roots[:]
1894 self.state = []
1895 self.children = children
1896 self.descendp = descendp
1898 def next(self):
1899 if not self.state:
1900 if not self.roots:
1901 return None
1902 root = self.roots.pop(0)
1903 self.state = [(root, self.children(root))]
1904 node, children = self.state[-1]
1905 if not children:
1906 self.state.pop()
1907 return self.next()
1908 child = children.pop(0)
1909 if self.descendp(child):
1910 self.state.append((child, self.children(child)))
1911 return child
1914 class ModuleScanner:
1915 """An interruptible scanner that searches module synopses."""
1917 def run(self, callback, key=None, completer=None, onerror=None):
1918 if key: key = lower(key)
1919 self.quit = False
1920 seen = {}
1922 for modname in sys.builtin_module_names:
1923 if modname != '__main__':
1924 seen[modname] = 1
1925 if key is None:
1926 callback(None, modname, '')
1927 else:
1928 desc = split(__import__(modname).__doc__ or '', '\n')[0]
1929 if find(lower(modname + ' - ' + desc), key) >= 0:
1930 callback(None, modname, desc)
1932 for importer, modname, ispkg in pkgutil.walk_packages(onerror=onerror):
1933 if self.quit:
1934 break
1935 if key is None:
1936 callback(None, modname, '')
1937 else:
1938 loader = importer.find_module(modname)
1939 if hasattr(loader,'get_source'):
1940 import StringIO
1941 desc = source_synopsis(
1942 StringIO.StringIO(loader.get_source(modname))
1943 ) or ''
1944 if hasattr(loader,'get_filename'):
1945 path = loader.get_filename(modname)
1946 else:
1947 path = None
1948 else:
1949 module = loader.load_module(modname)
1950 desc = (module.__doc__ or '').splitlines()[0]
1951 path = getattr(module,'__file__',None)
1952 if find(lower(modname + ' - ' + desc), key) >= 0:
1953 callback(path, modname, desc)
1955 if completer:
1956 completer()
1958 def apropos(key):
1959 """Print all the one-line module summaries that contain a substring."""
1960 def callback(path, modname, desc):
1961 if modname[-9:] == '.__init__':
1962 modname = modname[:-9] + ' (package)'
1963 print modname, desc and '- ' + desc
1964 try: import warnings
1965 except ImportError: pass
1966 else: warnings.filterwarnings('ignore') # ignore problems during import
1967 ModuleScanner().run(callback, key)
1969 # --------------------------------------------------- web browser interface
1971 def serve(port, callback=None, completer=None):
1972 import BaseHTTPServer, mimetools, select
1974 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1975 class Message(mimetools.Message):
1976 def __init__(self, fp, seekable=1):
1977 Message = self.__class__
1978 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1979 self.encodingheader = self.getheader('content-transfer-encoding')
1980 self.typeheader = self.getheader('content-type')
1981 self.parsetype()
1982 self.parseplist()
1984 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1985 def send_document(self, title, contents):
1986 try:
1987 self.send_response(200)
1988 self.send_header('Content-Type', 'text/html')
1989 self.end_headers()
1990 self.wfile.write(html.page(title, contents))
1991 except IOError: pass
1993 def do_GET(self):
1994 path = self.path
1995 if path[-5:] == '.html': path = path[:-5]
1996 if path[:1] == '/': path = path[1:]
1997 if path and path != '.':
1998 try:
1999 obj = locate(path, forceload=1)
2000 except ErrorDuringImport, value:
2001 self.send_document(path, html.escape(str(value)))
2002 return
2003 if obj:
2004 self.send_document(describe(obj), html.document(obj, path))
2005 else:
2006 self.send_document(path,
2007 'no Python documentation found for %s' % repr(path))
2008 else:
2009 heading = html.heading(
2010 '<big><big><strong>Python: Index of Modules</strong></big></big>',
2011 '#ffffff', '#7799ee')
2012 def bltinlink(name):
2013 return '<a href="%s.html">%s</a>' % (name, name)
2014 names = filter(lambda x: x != '__main__',
2015 sys.builtin_module_names)
2016 contents = html.multicolumn(names, bltinlink)
2017 indices = ['<p>' + html.bigsection(
2018 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
2020 seen = {}
2021 for dir in sys.path:
2022 indices.append(html.index(dir, seen))
2023 contents = heading + join(indices) + '''<p align=right>
2024 <font color="#909090" face="helvetica, arial"><strong>
2025 pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font>'''
2026 self.send_document('Index of Modules', contents)
2028 def log_message(self, *args): pass
2030 class DocServer(BaseHTTPServer.HTTPServer):
2031 def __init__(self, port, callback):
2032 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
2033 self.address = ('', port)
2034 self.url = 'http://%s:%d/' % (host, port)
2035 self.callback = callback
2036 self.base.__init__(self, self.address, self.handler)
2038 def serve_until_quit(self):
2039 import select
2040 self.quit = False
2041 while not self.quit:
2042 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
2043 if rd: self.handle_request()
2045 def server_activate(self):
2046 self.base.server_activate(self)
2047 if self.callback: self.callback(self)
2049 DocServer.base = BaseHTTPServer.HTTPServer
2050 DocServer.handler = DocHandler
2051 DocHandler.MessageClass = Message
2052 try:
2053 try:
2054 DocServer(port, callback).serve_until_quit()
2055 except (KeyboardInterrupt, select.error):
2056 pass
2057 finally:
2058 if completer: completer()
2060 # ----------------------------------------------------- graphical interface
2062 def gui():
2063 """Graphical interface (starts web server and pops up a control window)."""
2064 class GUI:
2065 def __init__(self, window, port=7464):
2066 self.window = window
2067 self.server = None
2068 self.scanner = None
2070 import Tkinter
2071 self.server_frm = Tkinter.Frame(window)
2072 self.title_lbl = Tkinter.Label(self.server_frm,
2073 text='Starting server...\n ')
2074 self.open_btn = Tkinter.Button(self.server_frm,
2075 text='open browser', command=self.open, state='disabled')
2076 self.quit_btn = Tkinter.Button(self.server_frm,
2077 text='quit serving', command=self.quit, state='disabled')
2079 self.search_frm = Tkinter.Frame(window)
2080 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
2081 self.search_ent = Tkinter.Entry(self.search_frm)
2082 self.search_ent.bind('<Return>', self.search)
2083 self.stop_btn = Tkinter.Button(self.search_frm,
2084 text='stop', pady=0, command=self.stop, state='disabled')
2085 if sys.platform == 'win32':
2086 # Trying to hide and show this button crashes under Windows.
2087 self.stop_btn.pack(side='right')
2089 self.window.title('pydoc')
2090 self.window.protocol('WM_DELETE_WINDOW', self.quit)
2091 self.title_lbl.pack(side='top', fill='x')
2092 self.open_btn.pack(side='left', fill='x', expand=1)
2093 self.quit_btn.pack(side='right', fill='x', expand=1)
2094 self.server_frm.pack(side='top', fill='x')
2096 self.search_lbl.pack(side='left')
2097 self.search_ent.pack(side='right', fill='x', expand=1)
2098 self.search_frm.pack(side='top', fill='x')
2099 self.search_ent.focus_set()
2101 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
2102 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
2103 self.result_lst.bind('<Button-1>', self.select)
2104 self.result_lst.bind('<Double-Button-1>', self.goto)
2105 self.result_scr = Tkinter.Scrollbar(window,
2106 orient='vertical', command=self.result_lst.yview)
2107 self.result_lst.config(yscrollcommand=self.result_scr.set)
2109 self.result_frm = Tkinter.Frame(window)
2110 self.goto_btn = Tkinter.Button(self.result_frm,
2111 text='go to selected', command=self.goto)
2112 self.hide_btn = Tkinter.Button(self.result_frm,
2113 text='hide results', command=self.hide)
2114 self.goto_btn.pack(side='left', fill='x', expand=1)
2115 self.hide_btn.pack(side='right', fill='x', expand=1)
2117 self.window.update()
2118 self.minwidth = self.window.winfo_width()
2119 self.minheight = self.window.winfo_height()
2120 self.bigminheight = (self.server_frm.winfo_reqheight() +
2121 self.search_frm.winfo_reqheight() +
2122 self.result_lst.winfo_reqheight() +
2123 self.result_frm.winfo_reqheight())
2124 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
2125 self.expanded = 0
2126 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
2127 self.window.wm_minsize(self.minwidth, self.minheight)
2128 self.window.tk.willdispatch()
2130 import threading
2131 threading.Thread(
2132 target=serve, args=(port, self.ready, self.quit)).start()
2134 def ready(self, server):
2135 self.server = server
2136 self.title_lbl.config(
2137 text='Python documentation server at\n' + server.url)
2138 self.open_btn.config(state='normal')
2139 self.quit_btn.config(state='normal')
2141 def open(self, event=None, url=None):
2142 url = url or self.server.url
2143 try:
2144 import webbrowser
2145 webbrowser.open(url)
2146 except ImportError: # pre-webbrowser.py compatibility
2147 if sys.platform == 'win32':
2148 os.system('start "%s"' % url)
2149 elif sys.platform == 'mac':
2150 try: import ic
2151 except ImportError: pass
2152 else: ic.launchurl(url)
2153 else:
2154 rc = os.system('netscape -remote "openURL(%s)" &' % url)
2155 if rc: os.system('netscape "%s" &' % url)
2157 def quit(self, event=None):
2158 if self.server:
2159 self.server.quit = 1
2160 self.window.quit()
2162 def search(self, event=None):
2163 key = self.search_ent.get()
2164 self.stop_btn.pack(side='right')
2165 self.stop_btn.config(state='normal')
2166 self.search_lbl.config(text='Searching for "%s"...' % key)
2167 self.search_ent.forget()
2168 self.search_lbl.pack(side='left')
2169 self.result_lst.delete(0, 'end')
2170 self.goto_btn.config(state='disabled')
2171 self.expand()
2173 import threading
2174 if self.scanner:
2175 self.scanner.quit = 1
2176 self.scanner = ModuleScanner()
2177 threading.Thread(target=self.scanner.run,
2178 args=(self.update, key, self.done)).start()
2180 def update(self, path, modname, desc):
2181 if modname[-9:] == '.__init__':
2182 modname = modname[:-9] + ' (package)'
2183 self.result_lst.insert('end',
2184 modname + ' - ' + (desc or '(no description)'))
2186 def stop(self, event=None):
2187 if self.scanner:
2188 self.scanner.quit = 1
2189 self.scanner = None
2191 def done(self):
2192 self.scanner = None
2193 self.search_lbl.config(text='Search for')
2194 self.search_lbl.pack(side='left')
2195 self.search_ent.pack(side='right', fill='x', expand=1)
2196 if sys.platform != 'win32': self.stop_btn.forget()
2197 self.stop_btn.config(state='disabled')
2199 def select(self, event=None):
2200 self.goto_btn.config(state='normal')
2202 def goto(self, event=None):
2203 selection = self.result_lst.curselection()
2204 if selection:
2205 modname = split(self.result_lst.get(selection[0]))[0]
2206 self.open(url=self.server.url + modname + '.html')
2208 def collapse(self):
2209 if not self.expanded: return
2210 self.result_frm.forget()
2211 self.result_scr.forget()
2212 self.result_lst.forget()
2213 self.bigwidth = self.window.winfo_width()
2214 self.bigheight = self.window.winfo_height()
2215 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
2216 self.window.wm_minsize(self.minwidth, self.minheight)
2217 self.expanded = 0
2219 def expand(self):
2220 if self.expanded: return
2221 self.result_frm.pack(side='bottom', fill='x')
2222 self.result_scr.pack(side='right', fill='y')
2223 self.result_lst.pack(side='top', fill='both', expand=1)
2224 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
2225 self.window.wm_minsize(self.minwidth, self.bigminheight)
2226 self.expanded = 1
2228 def hide(self, event=None):
2229 self.stop()
2230 self.collapse()
2232 import Tkinter
2233 try:
2234 root = Tkinter.Tk()
2235 # Tk will crash if pythonw.exe has an XP .manifest
2236 # file and the root has is not destroyed explicitly.
2237 # If the problem is ever fixed in Tk, the explicit
2238 # destroy can go.
2239 try:
2240 gui = GUI(root)
2241 root.mainloop()
2242 finally:
2243 root.destroy()
2244 except KeyboardInterrupt:
2245 pass
2247 # -------------------------------------------------- command-line interface
2249 def ispath(x):
2250 return isinstance(x, str) and find(x, os.sep) >= 0
2252 def cli():
2253 """Command-line interface (looks at sys.argv to decide what to do)."""
2254 import getopt
2255 class BadUsage: pass
2257 # Scripts don't get the current directory in their path by default
2258 # unless they are run with the '-m' switch
2259 if '' not in sys.path:
2260 scriptdir = os.path.dirname(sys.argv[0])
2261 if scriptdir in sys.path:
2262 sys.path.remove(scriptdir)
2263 sys.path.insert(0, '.')
2265 try:
2266 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
2267 writing = 0
2269 for opt, val in opts:
2270 if opt == '-g':
2271 gui()
2272 return
2273 if opt == '-k':
2274 apropos(val)
2275 return
2276 if opt == '-p':
2277 try:
2278 port = int(val)
2279 except ValueError:
2280 raise BadUsage
2281 def ready(server):
2282 print 'pydoc server ready at %s' % server.url
2283 def stopped():
2284 print 'pydoc server stopped'
2285 serve(port, ready, stopped)
2286 return
2287 if opt == '-w':
2288 writing = 1
2290 if not args: raise BadUsage
2291 for arg in args:
2292 if ispath(arg) and not os.path.exists(arg):
2293 print 'file %r does not exist' % arg
2294 break
2295 try:
2296 if ispath(arg) and os.path.isfile(arg):
2297 arg = importfile(arg)
2298 if writing:
2299 if ispath(arg) and os.path.isdir(arg):
2300 writedocs(arg)
2301 else:
2302 writedoc(arg)
2303 else:
2304 help.help(arg)
2305 except ErrorDuringImport, value:
2306 print value
2308 except (getopt.error, BadUsage):
2309 cmd = os.path.basename(sys.argv[0])
2310 print """pydoc - the Python documentation tool
2312 %s <name> ...
2313 Show text documentation on something. <name> may be the name of a
2314 Python keyword, topic, function, module, or package, or a dotted
2315 reference to a class or function within a module or module in a
2316 package. If <name> contains a '%s', it is used as the path to a
2317 Python source file to document. If name is 'keywords', 'topics',
2318 or 'modules', a listing of these things is displayed.
2320 %s -k <keyword>
2321 Search for a keyword in the synopsis lines of all available modules.
2323 %s -p <port>
2324 Start an HTTP server on the given port on the local machine.
2326 %s -g
2327 Pop up a graphical interface for finding and serving documentation.
2329 %s -w <name> ...
2330 Write out the HTML documentation for a module to a file in the current
2331 directory. If <name> contains a '%s', it is treated as a filename; if
2332 it names a directory, documentation is written for all the contents.
2333 """ % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
2335 if __name__ == '__main__': cli()