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