Saved and restored logging._handlerList at the same time as saving/restoring logging...
[python.git] / Lib / pydoc.py
blobb82e0e7d7183d0519529590ae2448550752f0726
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 try:
258 # If forceload is 1 and the module has been previously loaded from
259 # disk, we always have to reload the module. Checking the file's
260 # mtime isn't good enough (e.g. the module could contain a class
261 # that inherits from another module that has changed).
262 if forceload and path in sys.modules:
263 if path not in sys.builtin_module_names:
264 # Avoid simply calling reload() because it leaves names in
265 # the currently loaded module lying around if they're not
266 # defined in the new source file. Instead, remove the
267 # module from sys.modules and re-import. Also remove any
268 # submodules because they won't appear in the newly loaded
269 # module's namespace if they're already in sys.modules.
270 subs = [m for m in sys.modules if m.startswith(path + '.')]
271 for key in [path] + subs:
272 # Prevent garbage collection.
273 cache[key] = sys.modules[key]
274 del sys.modules[key]
275 module = __import__(path)
276 except:
277 # Did the error occur before or after the module was found?
278 (exc, value, tb) = info = sys.exc_info()
279 if path in sys.modules:
280 # An error occurred while executing the imported module.
281 raise ErrorDuringImport(sys.modules[path].__file__, info)
282 elif exc is SyntaxError:
283 # A SyntaxError occurred before we could execute the module.
284 raise ErrorDuringImport(value.filename, info)
285 elif exc is ImportError and \
286 split(lower(str(value)))[:2] == ['no', 'module']:
287 # The module was not found.
288 return None
289 else:
290 # Some other error occurred during the importing process.
291 raise ErrorDuringImport(path, sys.exc_info())
292 for part in split(path, '.')[1:]:
293 try: module = getattr(module, part)
294 except AttributeError: return None
295 return module
297 # ---------------------------------------------------- formatter base class
299 class Doc:
300 def document(self, object, name=None, *args):
301 """Generate documentation for an object."""
302 args = (object, name) + args
303 # 'try' clause is to attempt to handle the possibility that inspect
304 # identifies something in a way that pydoc itself has issues handling;
305 # think 'super' and how it is a descriptor (which raises the exception
306 # by lacking a __name__ attribute) and an instance.
307 try:
308 if inspect.ismodule(object): return self.docmodule(*args)
309 if inspect.isclass(object): return self.docclass(*args)
310 if inspect.isroutine(object): return self.docroutine(*args)
311 except AttributeError:
312 pass
313 if isinstance(object, property): return self.docproperty(*args)
314 return self.docother(*args)
316 def fail(self, object, name=None, *args):
317 """Raise an exception for unimplemented types."""
318 message = "don't know how to document object%s of type %s" % (
319 name and ' ' + repr(name), type(object).__name__)
320 raise TypeError, message
322 docmodule = docclass = docroutine = docother = fail
324 def getdocloc(self, object):
325 """Return the location of module docs or None"""
327 try:
328 file = inspect.getabsfile(object)
329 except TypeError:
330 file = '(built-in)'
332 docloc = os.environ.get("PYTHONDOCS",
333 "http://www.python.org/doc/current/lib")
334 basedir = os.path.join(sys.exec_prefix, "lib",
335 "python"+sys.version[0:3])
336 if (isinstance(object, type(os)) and
337 (object.__name__ in ('errno', 'exceptions', 'gc', 'imp',
338 'marshal', 'posix', 'signal', 'sys',
339 'thread', 'zipimport') or
340 (file.startswith(basedir) and
341 not file.startswith(os.path.join(basedir, 'site-packages'))))):
342 htmlfile = "module-%s.html" % object.__name__
343 if docloc.startswith("http://"):
344 docloc = "%s/%s" % (docloc.rstrip("/"), htmlfile)
345 else:
346 docloc = os.path.join(docloc, htmlfile)
347 else:
348 docloc = None
349 return docloc
351 # -------------------------------------------- HTML documentation generator
353 class HTMLRepr(Repr):
354 """Class for safely making an HTML representation of a Python object."""
355 def __init__(self):
356 Repr.__init__(self)
357 self.maxlist = self.maxtuple = 20
358 self.maxdict = 10
359 self.maxstring = self.maxother = 100
361 def escape(self, text):
362 return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')
364 def repr(self, object):
365 return Repr.repr(self, object)
367 def repr1(self, x, level):
368 if hasattr(type(x), '__name__'):
369 methodname = 'repr_' + join(split(type(x).__name__), '_')
370 if hasattr(self, methodname):
371 return getattr(self, methodname)(x, level)
372 return self.escape(cram(stripid(repr(x)), self.maxother))
374 def repr_string(self, x, level):
375 test = cram(x, self.maxstring)
376 testrepr = repr(test)
377 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
378 # Backslashes are only literal in the string and are never
379 # needed to make any special characters, so show a raw string.
380 return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
381 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
382 r'<font color="#c040c0">\1</font>',
383 self.escape(testrepr))
385 repr_str = repr_string
387 def repr_instance(self, x, level):
388 try:
389 return self.escape(cram(stripid(repr(x)), self.maxstring))
390 except:
391 return self.escape('<%s instance>' % x.__class__.__name__)
393 repr_unicode = repr_string
395 class HTMLDoc(Doc):
396 """Formatter class for HTML documentation."""
398 # ------------------------------------------- HTML formatting utilities
400 _repr_instance = HTMLRepr()
401 repr = _repr_instance.repr
402 escape = _repr_instance.escape
404 def page(self, title, contents):
405 """Format an HTML page."""
406 return '''
407 <!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
408 <html><head><title>Python: %s</title>
409 </head><body bgcolor="#f0f0f8">
411 </body></html>''' % (title, contents)
413 def heading(self, title, fgcol, bgcol, extras=''):
414 """Format a page heading."""
415 return '''
416 <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading">
417 <tr bgcolor="%s">
418 <td valign=bottom>&nbsp;<br>
419 <font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
420 ><td align=right valign=bottom
421 ><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
422 ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
424 def section(self, title, fgcol, bgcol, contents, width=6,
425 prelude='', marginalia=None, gap='&nbsp;'):
426 """Format a section with a heading."""
427 if marginalia is None:
428 marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
429 result = '''<p>
430 <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
431 <tr bgcolor="%s">
432 <td colspan=3 valign=bottom>&nbsp;<br>
433 <font color="%s" face="helvetica, arial">%s</font></td></tr>
434 ''' % (bgcol, fgcol, title)
435 if prelude:
436 result = result + '''
437 <tr bgcolor="%s"><td rowspan=2>%s</td>
438 <td colspan=2>%s</td></tr>
439 <tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
440 else:
441 result = result + '''
442 <tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
444 return result + '\n<td width="100%%">%s</td></tr></table>' % contents
446 def bigsection(self, title, *args):
447 """Format a section with a big heading."""
448 title = '<big><strong>%s</strong></big>' % title
449 return self.section(title, *args)
451 def preformat(self, text):
452 """Format literal preformatted text."""
453 text = self.escape(expandtabs(text))
454 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
455 ' ', '&nbsp;', '\n', '<br>\n')
457 def multicolumn(self, list, format, cols=4):
458 """Format a list of items into a multi-column list."""
459 result = ''
460 rows = (len(list)+cols-1)/cols
461 for col in range(cols):
462 result = result + '<td width="%d%%" valign=top>' % (100/cols)
463 for i in range(rows*col, rows*col+rows):
464 if i < len(list):
465 result = result + format(list[i]) + '<br>\n'
466 result = result + '</td>'
467 return '<table width="100%%" summary="list"><tr>%s</tr></table>' % result
469 def grey(self, text): return '<font color="#909090">%s</font>' % text
471 def namelink(self, name, *dicts):
472 """Make a link for an identifier, given name-to-URL mappings."""
473 for dict in dicts:
474 if name in dict:
475 return '<a href="%s">%s</a>' % (dict[name], name)
476 return name
478 def classlink(self, object, modname):
479 """Make a link for a class."""
480 name, module = object.__name__, sys.modules.get(object.__module__)
481 if hasattr(module, name) and getattr(module, name) is object:
482 return '<a href="%s.html#%s">%s</a>' % (
483 module.__name__, name, classname(object, modname))
484 return classname(object, modname)
486 def modulelink(self, object):
487 """Make a link for a module."""
488 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
490 def modpkglink(self, (name, path, ispackage, shadowed)):
491 """Make a link for a module or package to display in an index."""
492 if shadowed:
493 return self.grey(name)
494 if path:
495 url = '%s.%s.html' % (path, name)
496 else:
497 url = '%s.html' % name
498 if ispackage:
499 text = '<strong>%s</strong>&nbsp;(package)' % name
500 else:
501 text = name
502 return '<a href="%s">%s</a>' % (url, text)
504 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
505 """Mark up some plain text, given a context of symbols to look for.
506 Each context dictionary maps object names to anchor names."""
507 escape = escape or self.escape
508 results = []
509 here = 0
510 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
511 r'RFC[- ]?(\d+)|'
512 r'PEP[- ]?(\d+)|'
513 r'(self\.)?(\w+))')
514 while True:
515 match = pattern.search(text, here)
516 if not match: break
517 start, end = match.span()
518 results.append(escape(text[here:start]))
520 all, scheme, rfc, pep, selfdot, name = match.groups()
521 if scheme:
522 url = escape(all).replace('"', '&quot;')
523 results.append('<a href="%s">%s</a>' % (url, url))
524 elif rfc:
525 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
526 results.append('<a href="%s">%s</a>' % (url, escape(all)))
527 elif pep:
528 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
529 results.append('<a href="%s">%s</a>' % (url, escape(all)))
530 elif text[end:end+1] == '(':
531 results.append(self.namelink(name, methods, funcs, classes))
532 elif selfdot:
533 results.append('self.<strong>%s</strong>' % name)
534 else:
535 results.append(self.namelink(name, classes))
536 here = end
537 results.append(escape(text[here:]))
538 return join(results, '')
540 # ---------------------------------------------- type-specific routines
542 def formattree(self, tree, modname, parent=None):
543 """Produce HTML for a class tree as given by inspect.getclasstree()."""
544 result = ''
545 for entry in tree:
546 if type(entry) is type(()):
547 c, bases = entry
548 result = result + '<dt><font face="helvetica, arial">'
549 result = result + self.classlink(c, modname)
550 if bases and bases != (parent,):
551 parents = []
552 for base in bases:
553 parents.append(self.classlink(base, modname))
554 result = result + '(' + join(parents, ', ') + ')'
555 result = result + '\n</font></dt>'
556 elif type(entry) is type([]):
557 result = result + '<dd>\n%s</dd>\n' % self.formattree(
558 entry, modname, c)
559 return '<dl>\n%s</dl>\n' % result
561 def docmodule(self, object, name=None, mod=None, *ignored):
562 """Produce HTML documentation for a module object."""
563 name = object.__name__ # ignore the passed-in name
564 try:
565 all = object.__all__
566 except AttributeError:
567 all = None
568 parts = split(name, '.')
569 links = []
570 for i in range(len(parts)-1):
571 links.append(
572 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
573 (join(parts[:i+1], '.'), parts[i]))
574 linkedname = join(links + parts[-1:], '.')
575 head = '<big><big><strong>%s</strong></big></big>' % linkedname
576 try:
577 path = inspect.getabsfile(object)
578 url = path
579 if sys.platform == 'win32':
580 import nturl2path
581 url = nturl2path.pathname2url(path)
582 filelink = '<a href="file:%s">%s</a>' % (url, path)
583 except TypeError:
584 filelink = '(built-in)'
585 info = []
586 if hasattr(object, '__version__'):
587 version = str(object.__version__)
588 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
589 version = strip(version[11:-1])
590 info.append('version %s' % self.escape(version))
591 if hasattr(object, '__date__'):
592 info.append(self.escape(str(object.__date__)))
593 if info:
594 head = head + ' (%s)' % join(info, ', ')
595 docloc = self.getdocloc(object)
596 if docloc is not None:
597 docloc = '<br><a href="%(docloc)s">Module Docs</a>' % locals()
598 else:
599 docloc = ''
600 result = self.heading(
601 head, '#ffffff', '#7799ee',
602 '<a href=".">index</a><br>' + filelink + docloc)
604 modules = inspect.getmembers(object, inspect.ismodule)
606 classes, cdict = [], {}
607 for key, value in inspect.getmembers(object, inspect.isclass):
608 # if __all__ exists, believe it. Otherwise use old heuristic.
609 if (all is not None or
610 (inspect.getmodule(value) or object) is object):
611 if visiblename(key, all):
612 classes.append((key, value))
613 cdict[key] = cdict[value] = '#' + key
614 for key, value in classes:
615 for base in value.__bases__:
616 key, modname = base.__name__, base.__module__
617 module = sys.modules.get(modname)
618 if modname != name and module and hasattr(module, key):
619 if getattr(module, key) is base:
620 if not key in cdict:
621 cdict[key] = cdict[base] = modname + '.html#' + key
622 funcs, fdict = [], {}
623 for key, value in inspect.getmembers(object, inspect.isroutine):
624 # if __all__ exists, believe it. Otherwise use old heuristic.
625 if (all is not None or
626 inspect.isbuiltin(value) or inspect.getmodule(value) is object):
627 if visiblename(key, all):
628 funcs.append((key, value))
629 fdict[key] = '#-' + key
630 if inspect.isfunction(value): fdict[value] = fdict[key]
631 data = []
632 for key, value in inspect.getmembers(object, isdata):
633 if visiblename(key, all):
634 data.append((key, value))
636 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
637 doc = doc and '<tt>%s</tt>' % doc
638 result = result + '<p>%s</p>\n' % doc
640 if hasattr(object, '__path__'):
641 modpkgs = []
642 modnames = []
643 for file in os.listdir(object.__path__[0]):
644 path = os.path.join(object.__path__[0], file)
645 modname = inspect.getmodulename(file)
646 if modname != '__init__':
647 if modname and modname not in modnames:
648 modpkgs.append((modname, name, 0, 0))
649 modnames.append(modname)
650 elif ispackage(path):
651 modpkgs.append((file, name, 1, 0))
652 modpkgs.sort()
653 contents = self.multicolumn(modpkgs, self.modpkglink)
654 result = result + self.bigsection(
655 'Package Contents', '#ffffff', '#aa55cc', contents)
656 elif modules:
657 contents = self.multicolumn(
658 modules, lambda (key, value), s=self: s.modulelink(value))
659 result = result + self.bigsection(
660 'Modules', '#fffff', '#aa55cc', contents)
662 if classes:
663 classlist = map(lambda (key, value): value, classes)
664 contents = [
665 self.formattree(inspect.getclasstree(classlist, 1), name)]
666 for key, value in classes:
667 contents.append(self.document(value, key, name, fdict, cdict))
668 result = result + self.bigsection(
669 'Classes', '#ffffff', '#ee77aa', join(contents))
670 if funcs:
671 contents = []
672 for key, value in funcs:
673 contents.append(self.document(value, key, name, fdict, cdict))
674 result = result + self.bigsection(
675 'Functions', '#ffffff', '#eeaa77', join(contents))
676 if data:
677 contents = []
678 for key, value in data:
679 contents.append(self.document(value, key))
680 result = result + self.bigsection(
681 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))
682 if hasattr(object, '__author__'):
683 contents = self.markup(str(object.__author__), self.preformat)
684 result = result + self.bigsection(
685 'Author', '#ffffff', '#7799ee', contents)
686 if hasattr(object, '__credits__'):
687 contents = self.markup(str(object.__credits__), self.preformat)
688 result = result + self.bigsection(
689 'Credits', '#ffffff', '#7799ee', contents)
691 return result
693 def docclass(self, object, name=None, mod=None, funcs={}, classes={},
694 *ignored):
695 """Produce HTML documentation for a class object."""
696 realname = object.__name__
697 name = name or realname
698 bases = object.__bases__
700 contents = []
701 push = contents.append
703 # Cute little class to pump out a horizontal rule between sections.
704 class HorizontalRule:
705 def __init__(self):
706 self.needone = 0
707 def maybe(self):
708 if self.needone:
709 push('<hr>\n')
710 self.needone = 1
711 hr = HorizontalRule()
713 # List the mro, if non-trivial.
714 mro = deque(inspect.getmro(object))
715 if len(mro) > 2:
716 hr.maybe()
717 push('<dl><dt>Method resolution order:</dt>\n')
718 for base in mro:
719 push('<dd>%s</dd>\n' % self.classlink(base,
720 object.__module__))
721 push('</dl>\n')
723 def spill(msg, attrs, predicate):
724 ok, attrs = _split_list(attrs, predicate)
725 if ok:
726 hr.maybe()
727 push(msg)
728 for name, kind, homecls, value in ok:
729 push(self.document(getattr(object, name), name, mod,
730 funcs, classes, mdict, object))
731 push('\n')
732 return attrs
734 def spilldescriptors(msg, attrs, predicate):
735 ok, attrs = _split_list(attrs, predicate)
736 if ok:
737 hr.maybe()
738 push(msg)
739 for name, kind, homecls, value in ok:
740 push(self._docdescriptor(name, value, mod))
741 return attrs
743 def spilldata(msg, attrs, predicate):
744 ok, attrs = _split_list(attrs, predicate)
745 if ok:
746 hr.maybe()
747 push(msg)
748 for name, kind, homecls, value in ok:
749 base = self.docother(getattr(object, name), name, mod)
750 if callable(value) or inspect.isdatadescriptor(value):
751 doc = getattr(value, "__doc__", None)
752 else:
753 doc = None
754 if doc is None:
755 push('<dl><dt>%s</dl>\n' % base)
756 else:
757 doc = self.markup(getdoc(value), self.preformat,
758 funcs, classes, mdict)
759 doc = '<dd><tt>%s</tt>' % doc
760 push('<dl><dt>%s%s</dl>\n' % (base, doc))
761 push('\n')
762 return attrs
764 attrs = filter(lambda (name, kind, cls, value): visiblename(name),
765 classify_class_attrs(object))
766 mdict = {}
767 for key, kind, homecls, value in attrs:
768 mdict[key] = anchor = '#' + name + '-' + key
769 value = getattr(object, key)
770 try:
771 # The value may not be hashable (e.g., a data attr with
772 # a dict or list value).
773 mdict[value] = anchor
774 except TypeError:
775 pass
777 while attrs:
778 if mro:
779 thisclass = mro.popleft()
780 else:
781 thisclass = attrs[0][2]
782 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
784 if thisclass is __builtin__.object:
785 attrs = inherited
786 continue
787 elif thisclass is object:
788 tag = 'defined here'
789 else:
790 tag = 'inherited from %s' % self.classlink(thisclass,
791 object.__module__)
792 tag += ':<br>\n'
794 # Sort attrs by name.
795 attrs.sort(key=lambda t: t[0])
797 # Pump out the attrs, segregated by kind.
798 attrs = spill('Methods %s' % tag, attrs,
799 lambda t: t[1] == 'method')
800 attrs = spill('Class methods %s' % tag, attrs,
801 lambda t: t[1] == 'class method')
802 attrs = spill('Static methods %s' % tag, attrs,
803 lambda t: t[1] == 'static method')
804 attrs = spilldescriptors('Data descriptors %s' % tag, attrs,
805 lambda t: t[1] == 'data descriptor')
806 attrs = spilldata('Data and other attributes %s' % tag, attrs,
807 lambda t: t[1] == 'data')
808 assert attrs == []
809 attrs = inherited
811 contents = ''.join(contents)
813 if name == realname:
814 title = '<a name="%s">class <strong>%s</strong></a>' % (
815 name, realname)
816 else:
817 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
818 name, name, realname)
819 if bases:
820 parents = []
821 for base in bases:
822 parents.append(self.classlink(base, object.__module__))
823 title = title + '(%s)' % join(parents, ', ')
824 doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict)
825 doc = doc and '<tt>%s<br>&nbsp;</tt>' % doc
827 return self.section(title, '#000000', '#ffc8d8', contents, 3, doc)
829 def formatvalue(self, object):
830 """Format an argument default value as text."""
831 return self.grey('=' + self.repr(object))
833 def docroutine(self, object, name=None, mod=None,
834 funcs={}, classes={}, methods={}, cl=None):
835 """Produce HTML documentation for a function or method object."""
836 realname = object.__name__
837 name = name or realname
838 anchor = (cl and cl.__name__ or '') + '-' + name
839 note = ''
840 skipdocs = 0
841 if inspect.ismethod(object):
842 imclass = object.im_class
843 if cl:
844 if imclass is not cl:
845 note = ' from ' + self.classlink(imclass, mod)
846 else:
847 if object.im_self:
848 note = ' method of %s instance' % self.classlink(
849 object.im_self.__class__, mod)
850 else:
851 note = ' unbound %s method' % self.classlink(imclass,mod)
852 object = object.im_func
854 if name == realname:
855 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
856 else:
857 if (cl and realname in cl.__dict__ and
858 cl.__dict__[realname] is object):
859 reallink = '<a href="#%s">%s</a>' % (
860 cl.__name__ + '-' + realname, realname)
861 skipdocs = 1
862 else:
863 reallink = realname
864 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
865 anchor, name, reallink)
866 if inspect.isfunction(object):
867 args, varargs, varkw, defaults = inspect.getargspec(object)
868 argspec = inspect.formatargspec(
869 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
870 if realname == '<lambda>':
871 title = '<strong>%s</strong> <em>lambda</em> ' % name
872 argspec = argspec[1:-1] # remove parentheses
873 else:
874 argspec = '(...)'
876 decl = title + argspec + (note and self.grey(
877 '<font face="helvetica, arial">%s</font>' % note))
879 if skipdocs:
880 return '<dl><dt>%s</dt></dl>\n' % decl
881 else:
882 doc = self.markup(
883 getdoc(object), self.preformat, funcs, classes, methods)
884 doc = doc and '<dd><tt>%s</tt></dd>' % doc
885 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
887 def _docdescriptor(self, name, value, mod):
888 results = []
889 push = results.append
891 if name:
892 push('<dl><dt><strong>%s</strong></dt>\n' % name)
893 if value.__doc__ is not None:
894 doc = self.markup(getdoc(value), self.preformat)
895 push('<dd><tt>%s</tt></dd>\n' % doc)
896 push('</dl>\n')
898 return ''.join(results)
900 def docproperty(self, object, name=None, mod=None, cl=None):
901 """Produce html documentation for a property."""
902 return self._docdescriptor(name, object, mod)
904 def docother(self, object, name=None, mod=None, *ignored):
905 """Produce HTML documentation for a data object."""
906 lhs = name and '<strong>%s</strong> = ' % name or ''
907 return lhs + self.repr(object)
909 def index(self, dir, shadowed=None):
910 """Generate an HTML index for a directory of modules."""
911 modpkgs = []
912 if shadowed is None: shadowed = {}
913 seen = {}
914 files = os.listdir(dir)
916 def found(name, ispackage,
917 modpkgs=modpkgs, shadowed=shadowed, seen=seen):
918 if name not in seen:
919 modpkgs.append((name, '', ispackage, name in shadowed))
920 seen[name] = 1
921 shadowed[name] = 1
923 # Package spam/__init__.py takes precedence over module spam.py.
924 for file in files:
925 path = os.path.join(dir, file)
926 if ispackage(path): found(file, 1)
927 for file in files:
928 path = os.path.join(dir, file)
929 if os.path.isfile(path):
930 modname = inspect.getmodulename(file)
931 if modname: found(modname, 0)
933 modpkgs.sort()
934 contents = self.multicolumn(modpkgs, self.modpkglink)
935 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
937 # -------------------------------------------- text documentation generator
939 class TextRepr(Repr):
940 """Class for safely making a text representation of a Python object."""
941 def __init__(self):
942 Repr.__init__(self)
943 self.maxlist = self.maxtuple = 20
944 self.maxdict = 10
945 self.maxstring = self.maxother = 100
947 def repr1(self, x, level):
948 if hasattr(type(x), '__name__'):
949 methodname = 'repr_' + join(split(type(x).__name__), '_')
950 if hasattr(self, methodname):
951 return getattr(self, methodname)(x, level)
952 return cram(stripid(repr(x)), self.maxother)
954 def repr_string(self, x, level):
955 test = cram(x, self.maxstring)
956 testrepr = repr(test)
957 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
958 # Backslashes are only literal in the string and are never
959 # needed to make any special characters, so show a raw string.
960 return 'r' + testrepr[0] + test + testrepr[0]
961 return testrepr
963 repr_str = repr_string
965 def repr_instance(self, x, level):
966 try:
967 return cram(stripid(repr(x)), self.maxstring)
968 except:
969 return '<%s instance>' % x.__class__.__name__
971 class TextDoc(Doc):
972 """Formatter class for text documentation."""
974 # ------------------------------------------- text formatting utilities
976 _repr_instance = TextRepr()
977 repr = _repr_instance.repr
979 def bold(self, text):
980 """Format a string in bold by overstriking."""
981 return join(map(lambda ch: ch + '\b' + ch, text), '')
983 def indent(self, text, prefix=' '):
984 """Indent text by prepending a given prefix to each line."""
985 if not text: return ''
986 lines = split(text, '\n')
987 lines = map(lambda line, prefix=prefix: prefix + line, lines)
988 if lines: lines[-1] = rstrip(lines[-1])
989 return join(lines, '\n')
991 def section(self, title, contents):
992 """Format a section with a given heading."""
993 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
995 # ---------------------------------------------- type-specific routines
997 def formattree(self, tree, modname, parent=None, prefix=''):
998 """Render in text a class tree as returned by inspect.getclasstree()."""
999 result = ''
1000 for entry in tree:
1001 if type(entry) is type(()):
1002 c, bases = entry
1003 result = result + prefix + classname(c, modname)
1004 if bases and bases != (parent,):
1005 parents = map(lambda c, m=modname: classname(c, m), bases)
1006 result = result + '(%s)' % join(parents, ', ')
1007 result = result + '\n'
1008 elif type(entry) is type([]):
1009 result = result + self.formattree(
1010 entry, modname, c, prefix + ' ')
1011 return result
1013 def docmodule(self, object, name=None, mod=None):
1014 """Produce text documentation for a given module object."""
1015 name = object.__name__ # ignore the passed-in name
1016 synop, desc = splitdoc(getdoc(object))
1017 result = self.section('NAME', name + (synop and ' - ' + synop))
1019 try:
1020 all = object.__all__
1021 except AttributeError:
1022 all = None
1024 try:
1025 file = inspect.getabsfile(object)
1026 except TypeError:
1027 file = '(built-in)'
1028 result = result + self.section('FILE', file)
1030 docloc = self.getdocloc(object)
1031 if docloc is not None:
1032 result = result + self.section('MODULE DOCS', docloc)
1034 if desc:
1035 result = result + self.section('DESCRIPTION', desc)
1037 classes = []
1038 for key, value in inspect.getmembers(object, inspect.isclass):
1039 # if __all__ exists, believe it. Otherwise use old heuristic.
1040 if (all is not None
1041 or (inspect.getmodule(value) or object) is object):
1042 if visiblename(key, all):
1043 classes.append((key, value))
1044 funcs = []
1045 for key, value in inspect.getmembers(object, inspect.isroutine):
1046 # if __all__ exists, believe it. Otherwise use old heuristic.
1047 if (all is not None or
1048 inspect.isbuiltin(value) or inspect.getmodule(value) is object):
1049 if visiblename(key, all):
1050 funcs.append((key, value))
1051 data = []
1052 for key, value in inspect.getmembers(object, isdata):
1053 if visiblename(key, all):
1054 data.append((key, value))
1056 if hasattr(object, '__path__'):
1057 modpkgs = []
1058 for file in os.listdir(object.__path__[0]):
1059 path = os.path.join(object.__path__[0], file)
1060 modname = inspect.getmodulename(file)
1061 if modname != '__init__':
1062 if modname and modname not in modpkgs:
1063 modpkgs.append(modname)
1064 elif ispackage(path):
1065 modpkgs.append(file + ' (package)')
1066 modpkgs.sort()
1067 result = result + self.section(
1068 'PACKAGE CONTENTS', join(modpkgs, '\n'))
1070 if classes:
1071 classlist = map(lambda (key, value): value, classes)
1072 contents = [self.formattree(
1073 inspect.getclasstree(classlist, 1), name)]
1074 for key, value in classes:
1075 contents.append(self.document(value, key, name))
1076 result = result + self.section('CLASSES', join(contents, '\n'))
1078 if funcs:
1079 contents = []
1080 for key, value in funcs:
1081 contents.append(self.document(value, key, name))
1082 result = result + self.section('FUNCTIONS', join(contents, '\n'))
1084 if data:
1085 contents = []
1086 for key, value in data:
1087 contents.append(self.docother(value, key, name, maxlen=70))
1088 result = result + self.section('DATA', join(contents, '\n'))
1090 if hasattr(object, '__version__'):
1091 version = str(object.__version__)
1092 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
1093 version = strip(version[11:-1])
1094 result = result + self.section('VERSION', version)
1095 if hasattr(object, '__date__'):
1096 result = result + self.section('DATE', str(object.__date__))
1097 if hasattr(object, '__author__'):
1098 result = result + self.section('AUTHOR', str(object.__author__))
1099 if hasattr(object, '__credits__'):
1100 result = result + self.section('CREDITS', str(object.__credits__))
1101 return result
1103 def docclass(self, object, name=None, mod=None):
1104 """Produce text documentation for a given class object."""
1105 realname = object.__name__
1106 name = name or realname
1107 bases = object.__bases__
1109 def makename(c, m=object.__module__):
1110 return classname(c, m)
1112 if name == realname:
1113 title = 'class ' + self.bold(realname)
1114 else:
1115 title = self.bold(name) + ' = class ' + realname
1116 if bases:
1117 parents = map(makename, bases)
1118 title = title + '(%s)' % join(parents, ', ')
1120 doc = getdoc(object)
1121 contents = doc and [doc + '\n'] or []
1122 push = contents.append
1124 # List the mro, if non-trivial.
1125 mro = deque(inspect.getmro(object))
1126 if len(mro) > 2:
1127 push("Method resolution order:")
1128 for base in mro:
1129 push(' ' + makename(base))
1130 push('')
1132 # Cute little class to pump out a horizontal rule between sections.
1133 class HorizontalRule:
1134 def __init__(self):
1135 self.needone = 0
1136 def maybe(self):
1137 if self.needone:
1138 push('-' * 70)
1139 self.needone = 1
1140 hr = HorizontalRule()
1142 def spill(msg, attrs, predicate):
1143 ok, attrs = _split_list(attrs, predicate)
1144 if ok:
1145 hr.maybe()
1146 push(msg)
1147 for name, kind, homecls, value in ok:
1148 push(self.document(getattr(object, name),
1149 name, mod, object))
1150 return attrs
1152 def spilldescriptors(msg, attrs, predicate):
1153 ok, attrs = _split_list(attrs, predicate)
1154 if ok:
1155 hr.maybe()
1156 push(msg)
1157 for name, kind, homecls, value in ok:
1158 push(self._docdescriptor(name, value, mod))
1159 return attrs
1161 def spilldata(msg, attrs, predicate):
1162 ok, attrs = _split_list(attrs, predicate)
1163 if ok:
1164 hr.maybe()
1165 push(msg)
1166 for name, kind, homecls, value in ok:
1167 if callable(value) or inspect.isdatadescriptor(value):
1168 doc = getdoc(value)
1169 else:
1170 doc = None
1171 push(self.docother(getattr(object, name),
1172 name, mod, maxlen=70, doc=doc) + '\n')
1173 return attrs
1175 attrs = filter(lambda (name, kind, cls, value): visiblename(name),
1176 classify_class_attrs(object))
1177 while attrs:
1178 if mro:
1179 thisclass = mro.popleft()
1180 else:
1181 thisclass = attrs[0][2]
1182 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
1184 if thisclass is __builtin__.object:
1185 attrs = inherited
1186 continue
1187 elif thisclass is object:
1188 tag = "defined here"
1189 else:
1190 tag = "inherited from %s" % classname(thisclass,
1191 object.__module__)
1192 filter(lambda t: not t[0].startswith('_'), attrs)
1194 # Sort attrs by name.
1195 attrs.sort()
1197 # Pump out the attrs, segregated by kind.
1198 attrs = spill("Methods %s:\n" % tag, attrs,
1199 lambda t: t[1] == 'method')
1200 attrs = spill("Class methods %s:\n" % tag, attrs,
1201 lambda t: t[1] == 'class method')
1202 attrs = spill("Static methods %s:\n" % tag, attrs,
1203 lambda t: t[1] == 'static method')
1204 attrs = spilldescriptors("Data descriptors %s:\n" % tag, attrs,
1205 lambda t: t[1] == 'data descriptor')
1206 attrs = spilldata("Data and other attributes %s:\n" % tag, attrs,
1207 lambda t: t[1] == 'data')
1208 assert attrs == []
1209 attrs = inherited
1211 contents = '\n'.join(contents)
1212 if not contents:
1213 return title + '\n'
1214 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
1216 def formatvalue(self, object):
1217 """Format an argument default value as text."""
1218 return '=' + self.repr(object)
1220 def docroutine(self, object, name=None, mod=None, cl=None):
1221 """Produce text documentation for a function or method object."""
1222 realname = object.__name__
1223 name = name or realname
1224 note = ''
1225 skipdocs = 0
1226 if inspect.ismethod(object):
1227 imclass = object.im_class
1228 if cl:
1229 if imclass is not cl:
1230 note = ' from ' + classname(imclass, mod)
1231 else:
1232 if object.im_self:
1233 note = ' method of %s instance' % classname(
1234 object.im_self.__class__, mod)
1235 else:
1236 note = ' unbound %s method' % classname(imclass,mod)
1237 object = object.im_func
1239 if name == realname:
1240 title = self.bold(realname)
1241 else:
1242 if (cl and realname in cl.__dict__ and
1243 cl.__dict__[realname] is object):
1244 skipdocs = 1
1245 title = self.bold(name) + ' = ' + realname
1246 if inspect.isfunction(object):
1247 args, varargs, varkw, defaults = inspect.getargspec(object)
1248 argspec = inspect.formatargspec(
1249 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
1250 if realname == '<lambda>':
1251 title = 'lambda'
1252 argspec = argspec[1:-1] # remove parentheses
1253 else:
1254 argspec = '(...)'
1255 decl = title + argspec + note
1257 if skipdocs:
1258 return decl + '\n'
1259 else:
1260 doc = getdoc(object) or ''
1261 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
1263 def _docdescriptor(self, name, value, mod):
1264 results = []
1265 push = results.append
1267 if name:
1268 push(self.bold(name))
1269 push('\n')
1270 doc = getdoc(value) or ''
1271 if doc:
1272 push(self.indent(doc))
1273 push('\n')
1274 return ''.join(results)
1276 def docproperty(self, object, name=None, mod=None, cl=None):
1277 """Produce text documentation for a property."""
1278 return self._docdescriptor(name, object, mod)
1280 def docother(self, object, name=None, mod=None, parent=None, maxlen=None, doc=None):
1281 """Produce text documentation for a data object."""
1282 repr = self.repr(object)
1283 if maxlen:
1284 line = (name and name + ' = ' or '') + repr
1285 chop = maxlen - len(line)
1286 if chop < 0: repr = repr[:chop] + '...'
1287 line = (name and self.bold(name) + ' = ' or '') + repr
1288 if doc is not None:
1289 line += '\n' + self.indent(str(doc))
1290 return line
1292 # --------------------------------------------------------- user interfaces
1294 def pager(text):
1295 """The first time this is called, determine what kind of pager to use."""
1296 global pager
1297 pager = getpager()
1298 pager(text)
1300 def getpager():
1301 """Decide what method to use for paging through text."""
1302 if type(sys.stdout) is not types.FileType:
1303 return plainpager
1304 if not sys.stdin.isatty() or not sys.stdout.isatty():
1305 return plainpager
1306 if 'PAGER' in os.environ:
1307 if sys.platform == 'win32': # pipes completely broken in Windows
1308 return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
1309 elif os.environ.get('TERM') in ('dumb', 'emacs'):
1310 return lambda text: pipepager(plain(text), os.environ['PAGER'])
1311 else:
1312 return lambda text: pipepager(text, os.environ['PAGER'])
1313 if os.environ.get('TERM') in ('dumb', 'emacs'):
1314 return plainpager
1315 if sys.platform == 'win32' or sys.platform.startswith('os2'):
1316 return lambda text: tempfilepager(plain(text), 'more <')
1317 if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0:
1318 return lambda text: pipepager(text, 'less')
1320 import tempfile
1321 (fd, filename) = tempfile.mkstemp()
1322 os.close(fd)
1323 try:
1324 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
1325 return lambda text: pipepager(text, 'more')
1326 else:
1327 return ttypager
1328 finally:
1329 os.unlink(filename)
1331 def plain(text):
1332 """Remove boldface formatting from text."""
1333 return re.sub('.\b', '', text)
1335 def pipepager(text, cmd):
1336 """Page through text by feeding it to another program."""
1337 pipe = os.popen(cmd, 'w')
1338 try:
1339 pipe.write(text)
1340 pipe.close()
1341 except IOError:
1342 pass # Ignore broken pipes caused by quitting the pager program.
1344 def tempfilepager(text, cmd):
1345 """Page through text by invoking a program on a temporary file."""
1346 import tempfile
1347 filename = tempfile.mktemp()
1348 file = open(filename, 'w')
1349 file.write(text)
1350 file.close()
1351 try:
1352 os.system(cmd + ' ' + filename)
1353 finally:
1354 os.unlink(filename)
1356 def ttypager(text):
1357 """Page through text on a text terminal."""
1358 lines = split(plain(text), '\n')
1359 try:
1360 import tty
1361 fd = sys.stdin.fileno()
1362 old = tty.tcgetattr(fd)
1363 tty.setcbreak(fd)
1364 getchar = lambda: sys.stdin.read(1)
1365 except (ImportError, AttributeError):
1366 tty = None
1367 getchar = lambda: sys.stdin.readline()[:-1][:1]
1369 try:
1370 r = inc = os.environ.get('LINES', 25) - 1
1371 sys.stdout.write(join(lines[:inc], '\n') + '\n')
1372 while lines[r:]:
1373 sys.stdout.write('-- more --')
1374 sys.stdout.flush()
1375 c = getchar()
1377 if c in ('q', 'Q'):
1378 sys.stdout.write('\r \r')
1379 break
1380 elif c in ('\r', '\n'):
1381 sys.stdout.write('\r \r' + lines[r] + '\n')
1382 r = r + 1
1383 continue
1384 if c in ('b', 'B', '\x1b'):
1385 r = r - inc - inc
1386 if r < 0: r = 0
1387 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1388 r = r + inc
1390 finally:
1391 if tty:
1392 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1394 def plainpager(text):
1395 """Simply print unformatted text. This is the ultimate fallback."""
1396 sys.stdout.write(plain(text))
1398 def describe(thing):
1399 """Produce a short description of the given thing."""
1400 if inspect.ismodule(thing):
1401 if thing.__name__ in sys.builtin_module_names:
1402 return 'built-in module ' + thing.__name__
1403 if hasattr(thing, '__path__'):
1404 return 'package ' + thing.__name__
1405 else:
1406 return 'module ' + thing.__name__
1407 if inspect.isbuiltin(thing):
1408 return 'built-in function ' + thing.__name__
1409 if inspect.isclass(thing):
1410 return 'class ' + thing.__name__
1411 if inspect.isfunction(thing):
1412 return 'function ' + thing.__name__
1413 if inspect.ismethod(thing):
1414 return 'method ' + thing.__name__
1415 if type(thing) is types.InstanceType:
1416 return 'instance of ' + thing.__class__.__name__
1417 return type(thing).__name__
1419 def locate(path, forceload=0):
1420 """Locate an object by name or dotted path, importing as necessary."""
1421 parts = [part for part in split(path, '.') if part]
1422 module, n = None, 0
1423 while n < len(parts):
1424 nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
1425 if nextmodule: module, n = nextmodule, n + 1
1426 else: break
1427 if module:
1428 object = module
1429 for part in parts[n:]:
1430 try: object = getattr(object, part)
1431 except AttributeError: return None
1432 return object
1433 else:
1434 if hasattr(__builtin__, path):
1435 return getattr(__builtin__, path)
1437 # --------------------------------------- interactive interpreter interface
1439 text = TextDoc()
1440 html = HTMLDoc()
1442 def resolve(thing, forceload=0):
1443 """Given an object or a path to an object, get the object and its name."""
1444 if isinstance(thing, str):
1445 object = locate(thing, forceload)
1446 if not object:
1447 raise ImportError, 'no Python documentation found for %r' % thing
1448 return object, thing
1449 else:
1450 return thing, getattr(thing, '__name__', None)
1452 def doc(thing, title='Python Library Documentation: %s', forceload=0):
1453 """Display text documentation, given an object or a path to an object."""
1454 try:
1455 object, name = resolve(thing, forceload)
1456 desc = describe(object)
1457 module = inspect.getmodule(object)
1458 if name and '.' in name:
1459 desc += ' in ' + name[:name.rfind('.')]
1460 elif module and module is not object:
1461 desc += ' in module ' + module.__name__
1462 if not (inspect.ismodule(object) or
1463 inspect.isclass(object) or
1464 inspect.isroutine(object) or
1465 isinstance(object, property)):
1466 # If the passed object is a piece of data or an instance,
1467 # document its available methods instead of its value.
1468 object = type(object)
1469 desc += ' object'
1470 pager(title % desc + '\n\n' + text.document(object, name))
1471 except (ImportError, ErrorDuringImport), value:
1472 print value
1474 def writedoc(thing, forceload=0):
1475 """Write HTML documentation to a file in the current directory."""
1476 try:
1477 object, name = resolve(thing, forceload)
1478 page = html.page(describe(object), html.document(object, name))
1479 file = open(name + '.html', 'w')
1480 file.write(page)
1481 file.close()
1482 print 'wrote', name + '.html'
1483 except (ImportError, ErrorDuringImport), value:
1484 print value
1486 def writedocs(dir, pkgpath='', done=None):
1487 """Write out HTML documentation for all modules in a directory tree."""
1488 if done is None: done = {}
1489 for file in os.listdir(dir):
1490 path = os.path.join(dir, file)
1491 if ispackage(path):
1492 writedocs(path, pkgpath + file + '.', done)
1493 elif os.path.isfile(path):
1494 modname = inspect.getmodulename(path)
1495 if modname:
1496 if modname == '__init__':
1497 modname = pkgpath[:-1] # remove trailing period
1498 else:
1499 modname = pkgpath + modname
1500 if modname not in done:
1501 done[modname] = 1
1502 writedoc(modname)
1504 class Helper:
1505 keywords = {
1506 'and': 'BOOLEAN',
1507 'assert': ('ref/assert', ''),
1508 'break': ('ref/break', 'while for'),
1509 'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1510 'continue': ('ref/continue', 'while for'),
1511 'def': ('ref/function', ''),
1512 'del': ('ref/del', 'BASICMETHODS'),
1513 'elif': 'if',
1514 'else': ('ref/if', 'while for'),
1515 'except': 'try',
1516 'exec': ('ref/exec', ''),
1517 'finally': 'try',
1518 'for': ('ref/for', 'break continue while'),
1519 'from': 'import',
1520 'global': ('ref/global', 'NAMESPACES'),
1521 'if': ('ref/if', 'TRUTHVALUE'),
1522 'import': ('ref/import', 'MODULES'),
1523 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1524 'is': 'COMPARISON',
1525 'lambda': ('ref/lambdas', 'FUNCTIONS'),
1526 'not': 'BOOLEAN',
1527 'or': 'BOOLEAN',
1528 'pass': ('ref/pass', ''),
1529 'print': ('ref/print', ''),
1530 'raise': ('ref/raise', 'EXCEPTIONS'),
1531 'return': ('ref/return', 'FUNCTIONS'),
1532 'try': ('ref/try', 'EXCEPTIONS'),
1533 'while': ('ref/while', 'break continue if TRUTHVALUE'),
1534 'yield': ('ref/yield', ''),
1537 topics = {
1538 'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'),
1539 'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'),
1540 'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'),
1541 'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'),
1542 'UNICODE': ('ref/strings', 'encodings unicode SEQUENCES STRINGMETHODS FORMATTING TYPES'),
1543 'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1544 'INTEGER': ('ref/integers', 'int range'),
1545 'FLOAT': ('ref/floating', 'float math'),
1546 'COMPLEX': ('ref/imaginary', 'complex cmath'),
1547 'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
1548 'MAPPINGS': 'DICTIONARIES',
1549 'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'),
1550 'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'),
1551 'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'),
1552 'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'),
1553 'FRAMEOBJECTS': 'TYPES',
1554 'TRACEBACKS': 'TYPES',
1555 'NONE': ('lib/bltin-null-object', ''),
1556 'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'),
1557 'FILES': ('lib/bltin-file-objects', ''),
1558 'SPECIALATTRIBUTES': ('lib/specialattrs', ''),
1559 'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'),
1560 'MODULES': ('lib/typesmodules', 'import'),
1561 'PACKAGES': 'import',
1562 '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'),
1563 'OPERATORS': 'EXPRESSIONS',
1564 'PRECEDENCE': 'EXPRESSIONS',
1565 'OBJECTS': ('ref/objects', 'TYPES'),
1566 'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
1567 'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'),
1568 'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1569 'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'),
1570 'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'),
1571 'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'),
1572 'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'),
1573 'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'),
1574 'EXECUTION': ('ref/execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'),
1575 'NAMESPACES': ('ref/naming', 'global ASSIGNMENT DELETION DYNAMICFEATURES'),
1576 'DYNAMICFEATURES': ('ref/dynamic-features', ''),
1577 'SCOPING': 'NAMESPACES',
1578 'FRAMES': 'NAMESPACES',
1579 'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'),
1580 'COERCIONS': ('ref/coercion-rules','CONVERSIONS'),
1581 'CONVERSIONS': ('ref/conversions', 'COERCIONS'),
1582 'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'),
1583 'SPECIALIDENTIFIERS': ('ref/id-classes', ''),
1584 'PRIVATENAMES': ('ref/atom-identifiers', ''),
1585 'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1586 'TUPLES': 'SEQUENCES',
1587 'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'),
1588 'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'),
1589 'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'),
1590 'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'),
1591 'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'),
1592 'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'),
1593 'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1594 'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'),
1595 'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'),
1596 'CALLS': ('ref/calls', 'EXPRESSIONS'),
1597 'POWER': ('ref/power', 'EXPRESSIONS'),
1598 'UNARY': ('ref/unary', 'EXPRESSIONS'),
1599 'BINARY': ('ref/binary', 'EXPRESSIONS'),
1600 'SHIFTING': ('ref/shifting', 'EXPRESSIONS'),
1601 'BITWISE': ('ref/bitwise', 'EXPRESSIONS'),
1602 'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'),
1603 'BOOLEAN': ('ref/Booleans', 'EXPRESSIONS TRUTHVALUE'),
1604 'ASSERTION': 'assert',
1605 'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'),
1606 'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'),
1607 'DELETION': 'del',
1608 'PRINTING': 'print',
1609 'RETURNING': 'return',
1610 'IMPORTING': 'import',
1611 'CONDITIONAL': 'if',
1612 'LOOPING': ('ref/compound', 'for while break continue'),
1613 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'),
1614 'DEBUGGING': ('lib/module-pdb', 'pdb'),
1617 def __init__(self, input, output):
1618 self.input = input
1619 self.output = output
1620 self.docdir = None
1621 execdir = os.path.dirname(sys.executable)
1622 homedir = os.environ.get('PYTHONHOME')
1623 for dir in [os.environ.get('PYTHONDOCS'),
1624 homedir and os.path.join(homedir, 'doc'),
1625 os.path.join(execdir, 'doc'),
1626 '/usr/doc/python-docs-' + split(sys.version)[0],
1627 '/usr/doc/python-' + split(sys.version)[0],
1628 '/usr/doc/python-docs-' + sys.version[:3],
1629 '/usr/doc/python-' + sys.version[:3],
1630 os.path.join(sys.prefix, 'Resources/English.lproj/Documentation')]:
1631 if dir and os.path.isdir(os.path.join(dir, 'lib')):
1632 self.docdir = dir
1634 def __repr__(self):
1635 if inspect.stack()[1][3] == '?':
1636 self()
1637 return ''
1638 return '<pydoc.Helper instance>'
1640 def __call__(self, request=None):
1641 if request is not None:
1642 self.help(request)
1643 else:
1644 self.intro()
1645 self.interact()
1646 self.output.write('''
1647 You are now leaving help and returning to the Python interpreter.
1648 If you want to ask for help on a particular object directly from the
1649 interpreter, you can type "help(object)". Executing "help('string')"
1650 has the same effect as typing a particular string at the help> prompt.
1651 ''')
1653 def interact(self):
1654 self.output.write('\n')
1655 while True:
1656 try:
1657 request = self.getline('help> ')
1658 if not request: break
1659 except (KeyboardInterrupt, EOFError):
1660 break
1661 request = strip(replace(request, '"', '', "'", ''))
1662 if lower(request) in ('q', 'quit'): break
1663 self.help(request)
1665 def getline(self, prompt):
1666 """Read one line, using raw_input when available."""
1667 if self.input is sys.stdin:
1668 return raw_input(prompt)
1669 else:
1670 self.output.write(prompt)
1671 self.output.flush()
1672 return self.input.readline()
1674 def help(self, request):
1675 if type(request) is type(''):
1676 if request == 'help': self.intro()
1677 elif request == 'keywords': self.listkeywords()
1678 elif request == 'topics': self.listtopics()
1679 elif request == 'modules': self.listmodules()
1680 elif request[:8] == 'modules ':
1681 self.listmodules(split(request)[1])
1682 elif request in self.keywords: self.showtopic(request)
1683 elif request in self.topics: self.showtopic(request)
1684 elif request: doc(request, 'Help on %s:')
1685 elif isinstance(request, Helper): self()
1686 else: doc(request, 'Help on %s:')
1687 self.output.write('\n')
1689 def intro(self):
1690 self.output.write('''
1691 Welcome to Python %s! This is the online help utility.
1693 If this is your first time using Python, you should definitely check out
1694 the tutorial on the Internet at http://www.python.org/doc/tut/.
1696 Enter the name of any module, keyword, or topic to get help on writing
1697 Python programs and using Python modules. To quit this help utility and
1698 return to the interpreter, just type "quit".
1700 To get a list of available modules, keywords, or topics, type "modules",
1701 "keywords", or "topics". Each module also comes with a one-line summary
1702 of what it does; to list the modules whose summaries contain a given word
1703 such as "spam", type "modules spam".
1704 ''' % sys.version[:3])
1706 def list(self, items, columns=4, width=80):
1707 items = items[:]
1708 items.sort()
1709 colw = width / columns
1710 rows = (len(items) + columns - 1) / columns
1711 for row in range(rows):
1712 for col in range(columns):
1713 i = col * rows + row
1714 if i < len(items):
1715 self.output.write(items[i])
1716 if col < columns - 1:
1717 self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1718 self.output.write('\n')
1720 def listkeywords(self):
1721 self.output.write('''
1722 Here is a list of the Python keywords. Enter any keyword to get more help.
1724 ''')
1725 self.list(self.keywords.keys())
1727 def listtopics(self):
1728 self.output.write('''
1729 Here is a list of available topics. Enter any topic name to get more help.
1731 ''')
1732 self.list(self.topics.keys())
1734 def showtopic(self, topic):
1735 if not self.docdir:
1736 self.output.write('''
1737 Sorry, topic and keyword documentation is not available because the Python
1738 HTML documentation files could not be found. If you have installed them,
1739 please set the environment variable PYTHONDOCS to indicate their location.
1740 ''')
1741 return
1742 target = self.topics.get(topic, self.keywords.get(topic))
1743 if not target:
1744 self.output.write('no documentation found for %s\n' % repr(topic))
1745 return
1746 if type(target) is type(''):
1747 return self.showtopic(target)
1749 filename, xrefs = target
1750 filename = self.docdir + '/' + filename + '.html'
1751 try:
1752 file = open(filename)
1753 except:
1754 self.output.write('could not read docs from %s\n' % filename)
1755 return
1757 divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
1758 addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
1759 document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1760 file.close()
1762 import htmllib, formatter, StringIO
1763 buffer = StringIO.StringIO()
1764 parser = htmllib.HTMLParser(
1765 formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1766 parser.start_table = parser.do_p
1767 parser.end_table = lambda parser=parser: parser.do_p({})
1768 parser.start_tr = parser.do_br
1769 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1770 parser.feed(document)
1771 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
1772 pager(' ' + strip(buffer) + '\n')
1773 if xrefs:
1774 buffer = StringIO.StringIO()
1775 formatter.DumbWriter(buffer).send_flowing_data(
1776 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1777 self.output.write('\n%s\n' % buffer.getvalue())
1779 def listmodules(self, key=''):
1780 if key:
1781 self.output.write('''
1782 Here is a list of matching modules. Enter any module name to get more help.
1784 ''')
1785 apropos(key)
1786 else:
1787 self.output.write('''
1788 Please wait a moment while I gather a list of all available modules...
1790 ''')
1791 modules = {}
1792 def callback(path, modname, desc, modules=modules):
1793 if modname and modname[-9:] == '.__init__':
1794 modname = modname[:-9] + ' (package)'
1795 if find(modname, '.') < 0:
1796 modules[modname] = 1
1797 ModuleScanner().run(callback)
1798 self.list(modules.keys())
1799 self.output.write('''
1800 Enter any module name to get more help. Or, type "modules spam" to search
1801 for modules whose descriptions contain the word "spam".
1802 ''')
1804 help = Helper(sys.stdin, sys.stdout)
1806 class Scanner:
1807 """A generic tree iterator."""
1808 def __init__(self, roots, children, descendp):
1809 self.roots = roots[:]
1810 self.state = []
1811 self.children = children
1812 self.descendp = descendp
1814 def next(self):
1815 if not self.state:
1816 if not self.roots:
1817 return None
1818 root = self.roots.pop(0)
1819 self.state = [(root, self.children(root))]
1820 node, children = self.state[-1]
1821 if not children:
1822 self.state.pop()
1823 return self.next()
1824 child = children.pop(0)
1825 if self.descendp(child):
1826 self.state.append((child, self.children(child)))
1827 return child
1829 class ModuleScanner(Scanner):
1830 """An interruptible scanner that searches module synopses."""
1831 def __init__(self):
1832 roots = map(lambda dir: (dir, ''), pathdirs())
1833 Scanner.__init__(self, roots, self.submodules, self.isnewpackage)
1834 self.inodes = map(lambda (dir, pkg): os.stat(dir).st_ino, roots)
1836 def submodules(self, (dir, package)):
1837 children = []
1838 for file in os.listdir(dir):
1839 path = os.path.join(dir, file)
1840 if ispackage(path):
1841 children.append((path, package + (package and '.') + file))
1842 else:
1843 children.append((path, package))
1844 children.sort() # so that spam.py comes before spam.pyc or spam.pyo
1845 return children
1847 def isnewpackage(self, (dir, package)):
1848 inode = os.path.exists(dir) and os.stat(dir).st_ino
1849 if not (os.path.islink(dir) and inode in self.inodes):
1850 self.inodes.append(inode) # detect circular symbolic links
1851 return ispackage(dir)
1852 return False
1854 def run(self, callback, key=None, completer=None):
1855 if key: key = lower(key)
1856 self.quit = False
1857 seen = {}
1859 for modname in sys.builtin_module_names:
1860 if modname != '__main__':
1861 seen[modname] = 1
1862 if key is None:
1863 callback(None, modname, '')
1864 else:
1865 desc = split(__import__(modname).__doc__ or '', '\n')[0]
1866 if find(lower(modname + ' - ' + desc), key) >= 0:
1867 callback(None, modname, desc)
1869 while not self.quit:
1870 node = self.next()
1871 if not node: break
1872 path, package = node
1873 modname = inspect.getmodulename(path)
1874 if os.path.isfile(path) and modname:
1875 modname = package + (package and '.') + modname
1876 if not modname in seen:
1877 seen[modname] = 1 # if we see spam.py, skip spam.pyc
1878 if key is None:
1879 callback(path, modname, '')
1880 else:
1881 desc = synopsis(path) or ''
1882 if find(lower(modname + ' - ' + desc), key) >= 0:
1883 callback(path, modname, desc)
1884 if completer: completer()
1886 def apropos(key):
1887 """Print all the one-line module summaries that contain a substring."""
1888 def callback(path, modname, desc):
1889 if modname[-9:] == '.__init__':
1890 modname = modname[:-9] + ' (package)'
1891 print modname, desc and '- ' + desc
1892 try: import warnings
1893 except ImportError: pass
1894 else: warnings.filterwarnings('ignore') # ignore problems during import
1895 ModuleScanner().run(callback, key)
1897 # --------------------------------------------------- web browser interface
1899 def serve(port, callback=None, completer=None):
1900 import BaseHTTPServer, mimetools, select
1902 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1903 class Message(mimetools.Message):
1904 def __init__(self, fp, seekable=1):
1905 Message = self.__class__
1906 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1907 self.encodingheader = self.getheader('content-transfer-encoding')
1908 self.typeheader = self.getheader('content-type')
1909 self.parsetype()
1910 self.parseplist()
1912 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1913 def send_document(self, title, contents):
1914 try:
1915 self.send_response(200)
1916 self.send_header('Content-Type', 'text/html')
1917 self.end_headers()
1918 self.wfile.write(html.page(title, contents))
1919 except IOError: pass
1921 def do_GET(self):
1922 path = self.path
1923 if path[-5:] == '.html': path = path[:-5]
1924 if path[:1] == '/': path = path[1:]
1925 if path and path != '.':
1926 try:
1927 obj = locate(path, forceload=1)
1928 except ErrorDuringImport, value:
1929 self.send_document(path, html.escape(str(value)))
1930 return
1931 if obj:
1932 self.send_document(describe(obj), html.document(obj, path))
1933 else:
1934 self.send_document(path,
1935 'no Python documentation found for %s' % repr(path))
1936 else:
1937 heading = html.heading(
1938 '<big><big><strong>Python: Index of Modules</strong></big></big>',
1939 '#ffffff', '#7799ee')
1940 def bltinlink(name):
1941 return '<a href="%s.html">%s</a>' % (name, name)
1942 names = filter(lambda x: x != '__main__',
1943 sys.builtin_module_names)
1944 contents = html.multicolumn(names, bltinlink)
1945 indices = ['<p>' + html.bigsection(
1946 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1948 seen = {}
1949 for dir in pathdirs():
1950 indices.append(html.index(dir, seen))
1951 contents = heading + join(indices) + '''<p align=right>
1952 <font color="#909090" face="helvetica, arial"><strong>
1953 pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font>'''
1954 self.send_document('Index of Modules', contents)
1956 def log_message(self, *args): pass
1958 class DocServer(BaseHTTPServer.HTTPServer):
1959 def __init__(self, port, callback):
1960 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
1961 self.address = ('', port)
1962 self.url = 'http://%s:%d/' % (host, port)
1963 self.callback = callback
1964 self.base.__init__(self, self.address, self.handler)
1966 def serve_until_quit(self):
1967 import select
1968 self.quit = False
1969 while not self.quit:
1970 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1971 if rd: self.handle_request()
1973 def server_activate(self):
1974 self.base.server_activate(self)
1975 if self.callback: self.callback(self)
1977 DocServer.base = BaseHTTPServer.HTTPServer
1978 DocServer.handler = DocHandler
1979 DocHandler.MessageClass = Message
1980 try:
1981 try:
1982 DocServer(port, callback).serve_until_quit()
1983 except (KeyboardInterrupt, select.error):
1984 pass
1985 finally:
1986 if completer: completer()
1988 # ----------------------------------------------------- graphical interface
1990 def gui():
1991 """Graphical interface (starts web server and pops up a control window)."""
1992 class GUI:
1993 def __init__(self, window, port=7464):
1994 self.window = window
1995 self.server = None
1996 self.scanner = None
1998 import Tkinter
1999 self.server_frm = Tkinter.Frame(window)
2000 self.title_lbl = Tkinter.Label(self.server_frm,
2001 text='Starting server...\n ')
2002 self.open_btn = Tkinter.Button(self.server_frm,
2003 text='open browser', command=self.open, state='disabled')
2004 self.quit_btn = Tkinter.Button(self.server_frm,
2005 text='quit serving', command=self.quit, state='disabled')
2007 self.search_frm = Tkinter.Frame(window)
2008 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
2009 self.search_ent = Tkinter.Entry(self.search_frm)
2010 self.search_ent.bind('<Return>', self.search)
2011 self.stop_btn = Tkinter.Button(self.search_frm,
2012 text='stop', pady=0, command=self.stop, state='disabled')
2013 if sys.platform == 'win32':
2014 # Trying to hide and show this button crashes under Windows.
2015 self.stop_btn.pack(side='right')
2017 self.window.title('pydoc')
2018 self.window.protocol('WM_DELETE_WINDOW', self.quit)
2019 self.title_lbl.pack(side='top', fill='x')
2020 self.open_btn.pack(side='left', fill='x', expand=1)
2021 self.quit_btn.pack(side='right', fill='x', expand=1)
2022 self.server_frm.pack(side='top', fill='x')
2024 self.search_lbl.pack(side='left')
2025 self.search_ent.pack(side='right', fill='x', expand=1)
2026 self.search_frm.pack(side='top', fill='x')
2027 self.search_ent.focus_set()
2029 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
2030 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
2031 self.result_lst.bind('<Button-1>', self.select)
2032 self.result_lst.bind('<Double-Button-1>', self.goto)
2033 self.result_scr = Tkinter.Scrollbar(window,
2034 orient='vertical', command=self.result_lst.yview)
2035 self.result_lst.config(yscrollcommand=self.result_scr.set)
2037 self.result_frm = Tkinter.Frame(window)
2038 self.goto_btn = Tkinter.Button(self.result_frm,
2039 text='go to selected', command=self.goto)
2040 self.hide_btn = Tkinter.Button(self.result_frm,
2041 text='hide results', command=self.hide)
2042 self.goto_btn.pack(side='left', fill='x', expand=1)
2043 self.hide_btn.pack(side='right', fill='x', expand=1)
2045 self.window.update()
2046 self.minwidth = self.window.winfo_width()
2047 self.minheight = self.window.winfo_height()
2048 self.bigminheight = (self.server_frm.winfo_reqheight() +
2049 self.search_frm.winfo_reqheight() +
2050 self.result_lst.winfo_reqheight() +
2051 self.result_frm.winfo_reqheight())
2052 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
2053 self.expanded = 0
2054 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
2055 self.window.wm_minsize(self.minwidth, self.minheight)
2056 self.window.tk.willdispatch()
2058 import threading
2059 threading.Thread(
2060 target=serve, args=(port, self.ready, self.quit)).start()
2062 def ready(self, server):
2063 self.server = server
2064 self.title_lbl.config(
2065 text='Python documentation server at\n' + server.url)
2066 self.open_btn.config(state='normal')
2067 self.quit_btn.config(state='normal')
2069 def open(self, event=None, url=None):
2070 url = url or self.server.url
2071 try:
2072 import webbrowser
2073 webbrowser.open(url)
2074 except ImportError: # pre-webbrowser.py compatibility
2075 if sys.platform == 'win32':
2076 os.system('start "%s"' % url)
2077 elif sys.platform == 'mac':
2078 try: import ic
2079 except ImportError: pass
2080 else: ic.launchurl(url)
2081 else:
2082 rc = os.system('netscape -remote "openURL(%s)" &' % url)
2083 if rc: os.system('netscape "%s" &' % url)
2085 def quit(self, event=None):
2086 if self.server:
2087 self.server.quit = 1
2088 self.window.quit()
2090 def search(self, event=None):
2091 key = self.search_ent.get()
2092 self.stop_btn.pack(side='right')
2093 self.stop_btn.config(state='normal')
2094 self.search_lbl.config(text='Searching for "%s"...' % key)
2095 self.search_ent.forget()
2096 self.search_lbl.pack(side='left')
2097 self.result_lst.delete(0, 'end')
2098 self.goto_btn.config(state='disabled')
2099 self.expand()
2101 import threading
2102 if self.scanner:
2103 self.scanner.quit = 1
2104 self.scanner = ModuleScanner()
2105 threading.Thread(target=self.scanner.run,
2106 args=(self.update, key, self.done)).start()
2108 def update(self, path, modname, desc):
2109 if modname[-9:] == '.__init__':
2110 modname = modname[:-9] + ' (package)'
2111 self.result_lst.insert('end',
2112 modname + ' - ' + (desc or '(no description)'))
2114 def stop(self, event=None):
2115 if self.scanner:
2116 self.scanner.quit = 1
2117 self.scanner = None
2119 def done(self):
2120 self.scanner = None
2121 self.search_lbl.config(text='Search for')
2122 self.search_lbl.pack(side='left')
2123 self.search_ent.pack(side='right', fill='x', expand=1)
2124 if sys.platform != 'win32': self.stop_btn.forget()
2125 self.stop_btn.config(state='disabled')
2127 def select(self, event=None):
2128 self.goto_btn.config(state='normal')
2130 def goto(self, event=None):
2131 selection = self.result_lst.curselection()
2132 if selection:
2133 modname = split(self.result_lst.get(selection[0]))[0]
2134 self.open(url=self.server.url + modname + '.html')
2136 def collapse(self):
2137 if not self.expanded: return
2138 self.result_frm.forget()
2139 self.result_scr.forget()
2140 self.result_lst.forget()
2141 self.bigwidth = self.window.winfo_width()
2142 self.bigheight = self.window.winfo_height()
2143 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
2144 self.window.wm_minsize(self.minwidth, self.minheight)
2145 self.expanded = 0
2147 def expand(self):
2148 if self.expanded: return
2149 self.result_frm.pack(side='bottom', fill='x')
2150 self.result_scr.pack(side='right', fill='y')
2151 self.result_lst.pack(side='top', fill='both', expand=1)
2152 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
2153 self.window.wm_minsize(self.minwidth, self.bigminheight)
2154 self.expanded = 1
2156 def hide(self, event=None):
2157 self.stop()
2158 self.collapse()
2160 import Tkinter
2161 try:
2162 root = Tkinter.Tk()
2163 # Tk will crash if pythonw.exe has an XP .manifest
2164 # file and the root has is not destroyed explicitly.
2165 # If the problem is ever fixed in Tk, the explicit
2166 # destroy can go.
2167 try:
2168 gui = GUI(root)
2169 root.mainloop()
2170 finally:
2171 root.destroy()
2172 except KeyboardInterrupt:
2173 pass
2175 # -------------------------------------------------- command-line interface
2177 def ispath(x):
2178 return isinstance(x, str) and find(x, os.sep) >= 0
2180 def cli():
2181 """Command-line interface (looks at sys.argv to decide what to do)."""
2182 import getopt
2183 class BadUsage: pass
2185 # Scripts don't get the current directory in their path by default.
2186 scriptdir = os.path.dirname(sys.argv[0])
2187 if scriptdir in sys.path:
2188 sys.path.remove(scriptdir)
2189 sys.path.insert(0, '.')
2191 try:
2192 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
2193 writing = 0
2195 for opt, val in opts:
2196 if opt == '-g':
2197 gui()
2198 return
2199 if opt == '-k':
2200 apropos(val)
2201 return
2202 if opt == '-p':
2203 try:
2204 port = int(val)
2205 except ValueError:
2206 raise BadUsage
2207 def ready(server):
2208 print 'pydoc server ready at %s' % server.url
2209 def stopped():
2210 print 'pydoc server stopped'
2211 serve(port, ready, stopped)
2212 return
2213 if opt == '-w':
2214 writing = 1
2216 if not args: raise BadUsage
2217 for arg in args:
2218 if ispath(arg) and not os.path.exists(arg):
2219 print 'file %r does not exist' % arg
2220 break
2221 try:
2222 if ispath(arg) and os.path.isfile(arg):
2223 arg = importfile(arg)
2224 if writing:
2225 if ispath(arg) and os.path.isdir(arg):
2226 writedocs(arg)
2227 else:
2228 writedoc(arg)
2229 else:
2230 help.help(arg)
2231 except ErrorDuringImport, value:
2232 print value
2234 except (getopt.error, BadUsage):
2235 cmd = os.path.basename(sys.argv[0])
2236 print """pydoc - the Python documentation tool
2238 %s <name> ...
2239 Show text documentation on something. <name> may be the name of a
2240 Python keyword, topic, function, module, or package, or a dotted
2241 reference to a class or function within a module or module in a
2242 package. If <name> contains a '%s', it is used as the path to a
2243 Python source file to document. If name is 'keywords', 'topics',
2244 or 'modules', a listing of these things is displayed.
2246 %s -k <keyword>
2247 Search for a keyword in the synopsis lines of all available modules.
2249 %s -p <port>
2250 Start an HTTP server on the given port on the local machine.
2252 %s -g
2253 Pop up a graphical interface for finding and serving documentation.
2255 %s -w <name> ...
2256 Write out the HTML documentation for a module to a file in the current
2257 directory. If <name> contains a '%s', it is treated as a filename; if
2258 it names a directory, documentation is written for all the contents.
2259 """ % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
2261 if __name__ == '__main__': cli()