source: python/trunk/Lib/pydoc.py@ 20

Last change on this file since 20 was 2, checked in by Yuri Dario, 15 years ago

Initial import for vendor code.

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