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