Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / devtools / v9 / lib / python2.4 / pydoc.py
CommitLineData
920dae64
AT
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://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__author__ = "Ka-Ping Yee <ping@lfw.org>"
38__date__ = "26 February 2001"
39__version__ = "$Revision: 1.100.2.4 $"
40__credits__ = """Guido van Rossum, for an excellent programming language.
41Tommy Burnette, the original creator of manpy.
42Paul Prescod, for all his work on onlinehelp.
43Richard Chamberlain, for the first implementation of textdoc.
44"""
45
46# Known bugs that can't be fixed here:
47# - imp.load_module() cannot be prevented from clobbering existing
48# loaded modules, so calling synopsis() on a binary module file
49# changes the contents of any existing module with the same name.
50# - If the __file__ attribute on a module is a relative path and
51# the current directory is changed with os.chdir(), an incorrect
52# path will be displayed.
53
54import sys, imp, os, re, types, inspect, __builtin__
55from repr import Repr
56from string import expandtabs, find, join, lower, split, strip, rfind, rstrip
57from collections import deque
58
59# --------------------------------------------------------- common routines
60
61def pathdirs():
62 """Convert sys.path into a list of absolute, existing, unique paths."""
63 dirs = []
64 normdirs = []
65 for dir in sys.path:
66 dir = os.path.abspath(dir or '.')
67 normdir = os.path.normcase(dir)
68 if normdir not in normdirs and os.path.isdir(dir):
69 dirs.append(dir)
70 normdirs.append(normdir)
71 return dirs
72
73def getdoc(object):
74 """Get the doc string or comments for an object."""
75 result = inspect.getdoc(object) or inspect.getcomments(object)
76 return result and re.sub('^ *\n', '', rstrip(result)) or ''
77
78def splitdoc(doc):
79 """Split a doc string into a synopsis line (if any) and the rest."""
80 lines = split(strip(doc), '\n')
81 if len(lines) == 1:
82 return lines[0], ''
83 elif len(lines) >= 2 and not rstrip(lines[1]):
84 return lines[0], join(lines[2:], '\n')
85 return '', join(lines, '\n')
86
87def classname(object, modname):
88 """Get a class name and qualify it with a module name if necessary."""
89 name = object.__name__
90 if object.__module__ != modname:
91 name = object.__module__ + '.' + name
92 return name
93
94def isdata(object):
95 """Check if an object is of a type that probably means it's data."""
96 return not (inspect.ismodule(object) or inspect.isclass(object) or
97 inspect.isroutine(object) or inspect.isframe(object) or
98 inspect.istraceback(object) or inspect.iscode(object))
99
100def replace(text, *pairs):
101 """Do a series of global replacements on a string."""
102 while pairs:
103 text = join(split(text, pairs[0]), pairs[1])
104 pairs = pairs[2:]
105 return text
106
107def cram(text, maxlen):
108 """Omit part of a string if needed to make it fit in a maximum length."""
109 if len(text) > maxlen:
110 pre = max(0, (maxlen-3)//2)
111 post = max(0, maxlen-3-pre)
112 return text[:pre] + '...' + text[len(text)-post:]
113 return text
114
115_re_stripid = re.compile(r' at 0x[0-9a-f]{6,16}(>+)$', re.IGNORECASE)
116def stripid(text):
117 """Remove the hexadecimal id from a Python object representation."""
118 # The behaviour of %p is implementation-dependent in terms of case.
119 if _re_stripid.search(repr(Exception)):
120 return _re_stripid.sub(r'\1', text)
121 return text
122
123def _is_some_method(obj):
124 return inspect.ismethod(obj) or inspect.ismethoddescriptor(obj)
125
126def allmethods(cl):
127 methods = {}
128 for key, value in inspect.getmembers(cl, _is_some_method):
129 methods[key] = 1
130 for base in cl.__bases__:
131 methods.update(allmethods(base)) # all your base are belong to us
132 for key in methods.keys():
133 methods[key] = getattr(cl, key)
134 return methods
135
136def _split_list(s, predicate):
137 """Split sequence s via predicate, and return pair ([true], [false]).
138
139 The return value is a 2-tuple of lists,
140 ([x for x in s if predicate(x)],
141 [x for x in s if not predicate(x)])
142 """
143
144 yes = []
145 no = []
146 for x in s:
147 if predicate(x):
148 yes.append(x)
149 else:
150 no.append(x)
151 return yes, no
152
153def visiblename(name, all=None):
154 """Decide whether to show documentation on a variable."""
155 # Certain special names are redundant.
156 if name in ['__builtins__', '__doc__', '__file__', '__path__',
157 '__module__', '__name__']: return 0
158 # Private names are hidden, but special names are displayed.
159 if name.startswith('__') and name.endswith('__'): return 1
160 if all is not None:
161 # only document that which the programmer exported in __all__
162 return name in all
163 else:
164 return not name.startswith('_')
165
166# ----------------------------------------------------- module manipulation
167
168def ispackage(path):
169 """Guess whether a path refers to a package directory."""
170 if os.path.isdir(path):
171 for ext in ['.py', '.pyc', '.pyo']:
172 if os.path.isfile(os.path.join(path, '__init__' + ext)):
173 return True
174 return False
175
176def synopsis(filename, cache={}):
177 """Get the one-line summary out of a module file."""
178 mtime = os.stat(filename).st_mtime
179 lastupdate, result = cache.get(filename, (0, None))
180 if lastupdate < mtime:
181 info = inspect.getmoduleinfo(filename)
182 file = open(filename)
183 if info and 'b' in info[2]: # binary modules have to be imported
184 try: module = imp.load_module('__temp__', file, filename, info[1:])
185 except: return None
186 result = split(module.__doc__ or '', '\n')[0]
187 del sys.modules['__temp__']
188 else: # text modules can be directly examined
189 line = file.readline()
190 while line[:1] == '#' or not strip(line):
191 line = file.readline()
192 if not line: break
193 line = strip(line)
194 if line[:4] == 'r"""': line = line[1:]
195 if line[:3] == '"""':
196 line = line[3:]
197 if line[-1:] == '\\': line = line[:-1]
198 while not strip(line):
199 line = file.readline()
200 if not line: break
201 result = strip(split(line, '"""')[0])
202 else: result = None
203 file.close()
204 cache[filename] = (mtime, result)
205 return result
206
207class ErrorDuringImport(Exception):
208 """Errors that occurred while trying to import something to document it."""
209 def __init__(self, filename, (exc, value, tb)):
210 self.filename = filename
211 self.exc = exc
212 self.value = value
213 self.tb = tb
214
215 def __str__(self):
216 exc = self.exc
217 if type(exc) is types.ClassType:
218 exc = exc.__name__
219 return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
220
221def importfile(path):
222 """Import a Python source file or compiled file given its path."""
223 magic = imp.get_magic()
224 file = open(path, 'r')
225 if file.read(len(magic)) == magic:
226 kind = imp.PY_COMPILED
227 else:
228 kind = imp.PY_SOURCE
229 file.close()
230 filename = os.path.basename(path)
231 name, ext = os.path.splitext(filename)
232 file = open(path, 'r')
233 try:
234 module = imp.load_module(name, file, path, (ext, 'r', kind))
235 except:
236 raise ErrorDuringImport(path, sys.exc_info())
237 file.close()
238 return module
239
240def safeimport(path, forceload=0, cache={}):
241 """Import a module; handle errors; return None if the module isn't found.
242
243 If the module *is* found but an exception occurs, it's wrapped in an
244 ErrorDuringImport exception and reraised. Unlike __import__, if a
245 package path is specified, the module at the end of the path is returned,
246 not the package at the beginning. If the optional 'forceload' argument
247 is 1, we reload the module from disk (unless it's a dynamic extension)."""
248 if forceload and path in sys.modules:
249 # This is the only way to be sure. Checking the mtime of the file
250 # isn't good enough (e.g. what if the module contains a class that
251 # inherits from another module that has changed?).
252 if path not in sys.builtin_module_names:
253 # Python never loads a dynamic extension a second time from the
254 # same path, even if the file is changed or missing. Deleting
255 # the entry in sys.modules doesn't help for dynamic extensions,
256 # so we're not even going to try to keep them up to date.
257 info = inspect.getmoduleinfo(sys.modules[path].__file__)
258 if info[3] != imp.C_EXTENSION:
259 cache[path] = sys.modules[path] # prevent module from clearing
260 del sys.modules[path]
261 try:
262 module = __import__(path)
263 except:
264 # Did the error occur before or after the module was found?
265 (exc, value, tb) = info = sys.exc_info()
266 if path in sys.modules:
267 # An error occured while executing the imported module.
268 raise ErrorDuringImport(sys.modules[path].__file__, info)
269 elif exc is SyntaxError:
270 # A SyntaxError occurred before we could execute the module.
271 raise ErrorDuringImport(value.filename, info)
272 elif exc is ImportError and \
273 split(lower(str(value)))[:2] == ['no', 'module']:
274 # The module was not found.
275 return None
276 else:
277 # Some other error occurred during the importing process.
278 raise ErrorDuringImport(path, sys.exc_info())
279 for part in split(path, '.')[1:]:
280 try: module = getattr(module, part)
281 except AttributeError: return None
282 return module
283
284# ---------------------------------------------------- formatter base class
285
286class Doc:
287 def document(self, object, name=None, *args):
288 """Generate documentation for an object."""
289 args = (object, name) + args
290 # 'try' clause is to attempt to handle the possibility that inspect
291 # identifies something in a way that pydoc itself has issues handling;
292 # think 'super' and how it is a descriptor (which raises the exception
293 # by lacking a __name__ attribute) and an instance.
294 try:
295 if inspect.ismodule(object): return self.docmodule(*args)
296 if inspect.isclass(object): return self.docclass(*args)
297 if inspect.isroutine(object): return self.docroutine(*args)
298 except AttributeError:
299 pass
300 if isinstance(object, property): return self.docproperty(*args)
301 return self.docother(*args)
302
303 def fail(self, object, name=None, *args):
304 """Raise an exception for unimplemented types."""
305 message = "don't know how to document object%s of type %s" % (
306 name and ' ' + repr(name), type(object).__name__)
307 raise TypeError, message
308
309 docmodule = docclass = docroutine = docother = fail
310
311 def getdocloc(self, object):
312 """Return the location of module docs or None"""
313
314 try:
315 file = inspect.getabsfile(object)
316 except TypeError:
317 file = '(built-in)'
318
319 docloc = os.environ.get("PYTHONDOCS",
320 "http://www.python.org/doc/current/lib")
321 basedir = os.path.join(sys.exec_prefix, "lib",
322 "python"+sys.version[0:3])
323 if (isinstance(object, type(os)) and
324 (object.__name__ in ('errno', 'exceptions', 'gc', 'imp',
325 'marshal', 'posix', 'signal', 'sys',
326 'thread', 'zipimport') or
327 (file.startswith(basedir) and
328 not file.startswith(os.path.join(basedir, 'site-packages'))))):
329 htmlfile = "module-%s.html" % object.__name__
330 if docloc.startswith("http://"):
331 docloc = "%s/%s" % (docloc.rstrip("/"), htmlfile)
332 else:
333 docloc = os.path.join(docloc, htmlfile)
334 else:
335 docloc = None
336 return docloc
337
338# -------------------------------------------- HTML documentation generator
339
340class HTMLRepr(Repr):
341 """Class for safely making an HTML representation of a Python object."""
342 def __init__(self):
343 Repr.__init__(self)
344 self.maxlist = self.maxtuple = 20
345 self.maxdict = 10
346 self.maxstring = self.maxother = 100
347
348 def escape(self, text):
349 return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')
350
351 def repr(self, object):
352 return Repr.repr(self, object)
353
354 def repr1(self, x, level):
355 if hasattr(type(x), '__name__'):
356 methodname = 'repr_' + join(split(type(x).__name__), '_')
357 if hasattr(self, methodname):
358 return getattr(self, methodname)(x, level)
359 return self.escape(cram(stripid(repr(x)), self.maxother))
360
361 def repr_string(self, x, level):
362 test = cram(x, self.maxstring)
363 testrepr = repr(test)
364 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
365 # Backslashes are only literal in the string and are never
366 # needed to make any special characters, so show a raw string.
367 return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
368 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
369 r'<font color="#c040c0">\1</font>',
370 self.escape(testrepr))
371
372 repr_str = repr_string
373
374 def repr_instance(self, x, level):
375 try:
376 return self.escape(cram(stripid(repr(x)), self.maxstring))
377 except:
378 return self.escape('<%s instance>' % x.__class__.__name__)
379
380 repr_unicode = repr_string
381
382class HTMLDoc(Doc):
383 """Formatter class for HTML documentation."""
384
385 # ------------------------------------------- HTML formatting utilities
386
387 _repr_instance = HTMLRepr()
388 repr = _repr_instance.repr
389 escape = _repr_instance.escape
390
391 def page(self, title, contents):
392 """Format an HTML page."""
393 return '''
394<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
395<html><head><title>Python: %s</title>
396</head><body bgcolor="#f0f0f8">
397%s
398</body></html>''' % (title, contents)
399
400 def heading(self, title, fgcol, bgcol, extras=''):
401 """Format a page heading."""
402 return '''
403<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading">
404<tr bgcolor="%s">
405<td valign=bottom>&nbsp;<br>
406<font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
407><td align=right valign=bottom
408><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
409 ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
410
411 def section(self, title, fgcol, bgcol, contents, width=6,
412 prelude='', marginalia=None, gap='&nbsp;'):
413 """Format a section with a heading."""
414 if marginalia is None:
415 marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
416 result = '''<p>
417<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
418<tr bgcolor="%s">
419<td colspan=3 valign=bottom>&nbsp;<br>
420<font color="%s" face="helvetica, arial">%s</font></td></tr>
421 ''' % (bgcol, fgcol, title)
422 if prelude:
423 result = result + '''
424<tr bgcolor="%s"><td rowspan=2>%s</td>
425<td colspan=2>%s</td></tr>
426<tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
427 else:
428 result = result + '''
429<tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
430
431 return result + '\n<td width="100%%">%s</td></tr></table>' % contents
432
433 def bigsection(self, title, *args):
434 """Format a section with a big heading."""
435 title = '<big><strong>%s</strong></big>' % title
436 return self.section(title, *args)
437
438 def preformat(self, text):
439 """Format literal preformatted text."""
440 text = self.escape(expandtabs(text))
441 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
442 ' ', '&nbsp;', '\n', '<br>\n')
443
444 def multicolumn(self, list, format, cols=4):
445 """Format a list of items into a multi-column list."""
446 result = ''
447 rows = (len(list)+cols-1)/cols
448 for col in range(cols):
449 result = result + '<td width="%d%%" valign=top>' % (100/cols)
450 for i in range(rows*col, rows*col+rows):
451 if i < len(list):
452 result = result + format(list[i]) + '<br>\n'
453 result = result + '</td>'
454 return '<table width="100%%" summary="list"><tr>%s</tr></table>' % result
455
456 def grey(self, text): return '<font color="#909090">%s</font>' % text
457
458 def namelink(self, name, *dicts):
459 """Make a link for an identifier, given name-to-URL mappings."""
460 for dict in dicts:
461 if name in dict:
462 return '<a href="%s">%s</a>' % (dict[name], name)
463 return name
464
465 def classlink(self, object, modname):
466 """Make a link for a class."""
467 name, module = object.__name__, sys.modules.get(object.__module__)
468 if hasattr(module, name) and getattr(module, name) is object:
469 return '<a href="%s.html#%s">%s</a>' % (
470 module.__name__, name, classname(object, modname))
471 return classname(object, modname)
472
473 def modulelink(self, object):
474 """Make a link for a module."""
475 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
476
477 def modpkglink(self, (name, path, ispackage, shadowed)):
478 """Make a link for a module or package to display in an index."""
479 if shadowed:
480 return self.grey(name)
481 if path:
482 url = '%s.%s.html' % (path, name)
483 else:
484 url = '%s.html' % name
485 if ispackage:
486 text = '<strong>%s</strong>&nbsp;(package)' % name
487 else:
488 text = name
489 return '<a href="%s">%s</a>' % (url, text)
490
491 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
492 """Mark up some plain text, given a context of symbols to look for.
493 Each context dictionary maps object names to anchor names."""
494 escape = escape or self.escape
495 results = []
496 here = 0
497 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
498 r'RFC[- ]?(\d+)|'
499 r'PEP[- ]?(\d+)|'
500 r'(self\.)?(\w+))')
501 while True:
502 match = pattern.search(text, here)
503 if not match: break
504 start, end = match.span()
505 results.append(escape(text[here:start]))
506
507 all, scheme, rfc, pep, selfdot, name = match.groups()
508 if scheme:
509 url = escape(all).replace('"', '&quot;')
510 results.append('<a href="%s">%s</a>' % (url, url))
511 elif rfc:
512 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
513 results.append('<a href="%s">%s</a>' % (url, escape(all)))
514 elif pep:
515 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
516 results.append('<a href="%s">%s</a>' % (url, escape(all)))
517 elif text[end:end+1] == '(':
518 results.append(self.namelink(name, methods, funcs, classes))
519 elif selfdot:
520 results.append('self.<strong>%s</strong>' % name)
521 else:
522 results.append(self.namelink(name, classes))
523 here = end
524 results.append(escape(text[here:]))
525 return join(results, '')
526
527 # ---------------------------------------------- type-specific routines
528
529 def formattree(self, tree, modname, parent=None):
530 """Produce HTML for a class tree as given by inspect.getclasstree()."""
531 result = ''
532 for entry in tree:
533 if type(entry) is type(()):
534 c, bases = entry
535 result = result + '<dt><font face="helvetica, arial">'
536 result = result + self.classlink(c, modname)
537 if bases and bases != (parent,):
538 parents = []
539 for base in bases:
540 parents.append(self.classlink(base, modname))
541 result = result + '(' + join(parents, ', ') + ')'
542 result = result + '\n</font></dt>'
543 elif type(entry) is type([]):
544 result = result + '<dd>\n%s</dd>\n' % self.formattree(
545 entry, modname, c)
546 return '<dl>\n%s</dl>\n' % result
547
548 def docmodule(self, object, name=None, mod=None, *ignored):
549 """Produce HTML documentation for a module object."""
550 name = object.__name__ # ignore the passed-in name
551 try:
552 all = object.__all__
553 except AttributeError:
554 all = None
555 parts = split(name, '.')
556 links = []
557 for i in range(len(parts)-1):
558 links.append(
559 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
560 (join(parts[:i+1], '.'), parts[i]))
561 linkedname = join(links + parts[-1:], '.')
562 head = '<big><big><strong>%s</strong></big></big>' % linkedname
563 try:
564 path = inspect.getabsfile(object)
565 url = path
566 if sys.platform == 'win32':
567 import nturl2path
568 url = nturl2path.pathname2url(path)
569 filelink = '<a href="file:%s">%s</a>' % (url, path)
570 except TypeError:
571 filelink = '(built-in)'
572 info = []
573 if hasattr(object, '__version__'):
574 version = str(object.__version__)
575 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
576 version = strip(version[11:-1])
577 info.append('version %s' % self.escape(version))
578 if hasattr(object, '__date__'):
579 info.append(self.escape(str(object.__date__)))
580 if info:
581 head = head + ' (%s)' % join(info, ', ')
582 docloc = self.getdocloc(object)
583 if docloc is not None:
584 docloc = '<br><a href="%(docloc)s">Module Docs</a>' % locals()
585 else:
586 docloc = ''
587 result = self.heading(
588 head, '#ffffff', '#7799ee',
589 '<a href=".">index</a><br>' + filelink + docloc)
590
591 modules = inspect.getmembers(object, inspect.ismodule)
592
593 classes, cdict = [], {}
594 for key, value in inspect.getmembers(object, inspect.isclass):
595 # if __all__ exists, believe it. Otherwise use old heuristic.
596 if (all is not None or
597 (inspect.getmodule(value) or object) is object):
598 if visiblename(key, all):
599 classes.append((key, value))
600 cdict[key] = cdict[value] = '#' + key
601 for key, value in classes:
602 for base in value.__bases__:
603 key, modname = base.__name__, base.__module__
604 module = sys.modules.get(modname)
605 if modname != name and module and hasattr(module, key):
606 if getattr(module, key) is base:
607 if not key in cdict:
608 cdict[key] = cdict[base] = modname + '.html#' + key
609 funcs, fdict = [], {}
610 for key, value in inspect.getmembers(object, inspect.isroutine):
611 # if __all__ exists, believe it. Otherwise use old heuristic.
612 if (all is not None or
613 inspect.isbuiltin(value) or inspect.getmodule(value) is object):
614 if visiblename(key, all):
615 funcs.append((key, value))
616 fdict[key] = '#-' + key
617 if inspect.isfunction(value): fdict[value] = fdict[key]
618 data = []
619 for key, value in inspect.getmembers(object, isdata):
620 if visiblename(key, all):
621 data.append((key, value))
622
623 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
624 doc = doc and '<tt>%s</tt>' % doc
625 result = result + '<p>%s</p>\n' % doc
626
627 if hasattr(object, '__path__'):
628 modpkgs = []
629 modnames = []
630 for file in os.listdir(object.__path__[0]):
631 path = os.path.join(object.__path__[0], file)
632 modname = inspect.getmodulename(file)
633 if modname != '__init__':
634 if modname and modname not in modnames:
635 modpkgs.append((modname, name, 0, 0))
636 modnames.append(modname)
637 elif ispackage(path):
638 modpkgs.append((file, name, 1, 0))
639 modpkgs.sort()
640 contents = self.multicolumn(modpkgs, self.modpkglink)
641 result = result + self.bigsection(
642 'Package Contents', '#ffffff', '#aa55cc', contents)
643 elif modules:
644 contents = self.multicolumn(
645 modules, lambda (key, value), s=self: s.modulelink(value))
646 result = result + self.bigsection(
647 'Modules', '#fffff', '#aa55cc', contents)
648
649 if classes:
650 classlist = map(lambda (key, value): value, classes)
651 contents = [
652 self.formattree(inspect.getclasstree(classlist, 1), name)]
653 for key, value in classes:
654 contents.append(self.document(value, key, name, fdict, cdict))
655 result = result + self.bigsection(
656 'Classes', '#ffffff', '#ee77aa', join(contents))
657 if funcs:
658 contents = []
659 for key, value in funcs:
660 contents.append(self.document(value, key, name, fdict, cdict))
661 result = result + self.bigsection(
662 'Functions', '#ffffff', '#eeaa77', join(contents))
663 if data:
664 contents = []
665 for key, value in data:
666 contents.append(self.document(value, key))
667 result = result + self.bigsection(
668 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))
669 if hasattr(object, '__author__'):
670 contents = self.markup(str(object.__author__), self.preformat)
671 result = result + self.bigsection(
672 'Author', '#ffffff', '#7799ee', contents)
673 if hasattr(object, '__credits__'):
674 contents = self.markup(str(object.__credits__), self.preformat)
675 result = result + self.bigsection(
676 'Credits', '#ffffff', '#7799ee', contents)
677
678 return result
679
680 def docclass(self, object, name=None, mod=None, funcs={}, classes={},
681 *ignored):
682 """Produce HTML documentation for a class object."""
683 realname = object.__name__
684 name = name or realname
685 bases = object.__bases__
686
687 contents = []
688 push = contents.append
689
690 # Cute little class to pump out a horizontal rule between sections.
691 class HorizontalRule:
692 def __init__(self):
693 self.needone = 0
694 def maybe(self):
695 if self.needone:
696 push('<hr>\n')
697 self.needone = 1
698 hr = HorizontalRule()
699
700 # List the mro, if non-trivial.
701 mro = deque(inspect.getmro(object))
702 if len(mro) > 2:
703 hr.maybe()
704 push('<dl><dt>Method resolution order:</dt>\n')
705 for base in mro:
706 push('<dd>%s</dd>\n' % self.classlink(base,
707 object.__module__))
708 push('</dl>\n')
709
710 def spill(msg, attrs, predicate):
711 ok, attrs = _split_list(attrs, predicate)
712 if ok:
713 hr.maybe()
714 push(msg)
715 for name, kind, homecls, value in ok:
716 push(self.document(getattr(object, name), name, mod,
717 funcs, classes, mdict, object))
718 push('\n')
719 return attrs
720
721 def spillproperties(msg, attrs, predicate):
722 ok, attrs = _split_list(attrs, predicate)
723 if ok:
724 hr.maybe()
725 push(msg)
726 for name, kind, homecls, value in ok:
727 push(self._docproperty(name, value, mod))
728 return attrs
729
730 def spilldata(msg, attrs, predicate):
731 ok, attrs = _split_list(attrs, predicate)
732 if ok:
733 hr.maybe()
734 push(msg)
735 for name, kind, homecls, value in ok:
736 base = self.docother(getattr(object, name), name, mod)
737 if callable(value) or inspect.isdatadescriptor(value):
738 doc = getattr(value, "__doc__", None)
739 else:
740 doc = None
741 if doc is None:
742 push('<dl><dt>%s</dl>\n' % base)
743 else:
744 doc = self.markup(getdoc(value), self.preformat,
745 funcs, classes, mdict)
746 doc = '<dd><tt>%s</tt>' % doc
747 push('<dl><dt>%s%s</dl>\n' % (base, doc))
748 push('\n')
749 return attrs
750
751 attrs = filter(lambda (name, kind, cls, value): visiblename(name),
752 inspect.classify_class_attrs(object))
753 mdict = {}
754 for key, kind, homecls, value in attrs:
755 mdict[key] = anchor = '#' + name + '-' + key
756 value = getattr(object, key)
757 try:
758 # The value may not be hashable (e.g., a data attr with
759 # a dict or list value).
760 mdict[value] = anchor
761 except TypeError:
762 pass
763
764 while attrs:
765 if mro:
766 thisclass = mro.popleft()
767 else:
768 thisclass = attrs[0][2]
769 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
770
771 if thisclass is __builtin__.object:
772 attrs = inherited
773 continue
774 elif thisclass is object:
775 tag = 'defined here'
776 else:
777 tag = 'inherited from %s' % self.classlink(thisclass,
778 object.__module__)
779 tag += ':<br>\n'
780
781 # Sort attrs by name.
782 attrs.sort(key=lambda t: t[0])
783
784 # Pump out the attrs, segregated by kind.
785 attrs = spill('Methods %s' % tag, attrs,
786 lambda t: t[1] == 'method')
787 attrs = spill('Class methods %s' % tag, attrs,
788 lambda t: t[1] == 'class method')
789 attrs = spill('Static methods %s' % tag, attrs,
790 lambda t: t[1] == 'static method')
791 attrs = spillproperties('Properties %s' % tag, attrs,
792 lambda t: t[1] == 'property')
793 attrs = spilldata('Data and other attributes %s' % tag, attrs,
794 lambda t: t[1] == 'data')
795 assert attrs == []
796 attrs = inherited
797
798 contents = ''.join(contents)
799
800 if name == realname:
801 title = '<a name="%s">class <strong>%s</strong></a>' % (
802 name, realname)
803 else:
804 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
805 name, name, realname)
806 if bases:
807 parents = []
808 for base in bases:
809 parents.append(self.classlink(base, object.__module__))
810 title = title + '(%s)' % join(parents, ', ')
811 doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict)
812 doc = doc and '<tt>%s<br>&nbsp;</tt>' % doc
813
814 return self.section(title, '#000000', '#ffc8d8', contents, 3, doc)
815
816 def formatvalue(self, object):
817 """Format an argument default value as text."""
818 return self.grey('=' + self.repr(object))
819
820 def docroutine(self, object, name=None, mod=None,
821 funcs={}, classes={}, methods={}, cl=None):
822 """Produce HTML documentation for a function or method object."""
823 realname = object.__name__
824 name = name or realname
825 anchor = (cl and cl.__name__ or '') + '-' + name
826 note = ''
827 skipdocs = 0
828 if inspect.ismethod(object):
829 imclass = object.im_class
830 if cl:
831 if imclass is not cl:
832 note = ' from ' + self.classlink(imclass, mod)
833 else:
834 if object.im_self:
835 note = ' method of %s instance' % self.classlink(
836 object.im_self.__class__, mod)
837 else:
838 note = ' unbound %s method' % self.classlink(imclass,mod)
839 object = object.im_func
840
841 if name == realname:
842 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
843 else:
844 if (cl and realname in cl.__dict__ and
845 cl.__dict__[realname] is object):
846 reallink = '<a href="#%s">%s</a>' % (
847 cl.__name__ + '-' + realname, realname)
848 skipdocs = 1
849 else:
850 reallink = realname
851 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
852 anchor, name, reallink)
853 if inspect.isfunction(object):
854 args, varargs, varkw, defaults = inspect.getargspec(object)
855 argspec = inspect.formatargspec(
856 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
857 if realname == '<lambda>':
858 title = '<strong>%s</strong> <em>lambda</em> ' % name
859 argspec = argspec[1:-1] # remove parentheses
860 else:
861 argspec = '(...)'
862
863 decl = title + argspec + (note and self.grey(
864 '<font face="helvetica, arial">%s</font>' % note))
865
866 if skipdocs:
867 return '<dl><dt>%s</dt></dl>\n' % decl
868 else:
869 doc = self.markup(
870 getdoc(object), self.preformat, funcs, classes, methods)
871 doc = doc and '<dd><tt>%s</tt></dd>' % doc
872 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
873
874 def _docproperty(self, name, value, mod):
875 results = []
876 push = results.append
877
878 if name:
879 push('<dl><dt><strong>%s</strong></dt>\n' % name)
880 if value.__doc__ is not None:
881 doc = self.markup(getdoc(value), self.preformat)
882 push('<dd><tt>%s</tt></dd>\n' % doc)
883 for attr, tag in [('fget', '<em>get</em>'),
884 ('fset', '<em>set</em>'),
885 ('fdel', '<em>delete</em>')]:
886 func = getattr(value, attr)
887 if func is not None:
888 base = self.document(func, tag, mod)
889 push('<dd>%s</dd>\n' % base)
890 push('</dl>\n')
891
892 return ''.join(results)
893
894 def docproperty(self, object, name=None, mod=None, cl=None):
895 """Produce html documentation for a property."""
896 return self._docproperty(name, object, mod)
897
898 def docother(self, object, name=None, mod=None, *ignored):
899 """Produce HTML documentation for a data object."""
900 lhs = name and '<strong>%s</strong> = ' % name or ''
901 return lhs + self.repr(object)
902
903 def index(self, dir, shadowed=None):
904 """Generate an HTML index for a directory of modules."""
905 modpkgs = []
906 if shadowed is None: shadowed = {}
907 seen = {}
908 files = os.listdir(dir)
909
910 def found(name, ispackage,
911 modpkgs=modpkgs, shadowed=shadowed, seen=seen):
912 if name not in seen:
913 modpkgs.append((name, '', ispackage, name in shadowed))
914 seen[name] = 1
915 shadowed[name] = 1
916
917 # Package spam/__init__.py takes precedence over module spam.py.
918 for file in files:
919 path = os.path.join(dir, file)
920 if ispackage(path): found(file, 1)
921 for file in files:
922 path = os.path.join(dir, file)
923 if os.path.isfile(path):
924 modname = inspect.getmodulename(file)
925 if modname: found(modname, 0)
926
927 modpkgs.sort()
928 contents = self.multicolumn(modpkgs, self.modpkglink)
929 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
930
931# -------------------------------------------- text documentation generator
932
933class TextRepr(Repr):
934 """Class for safely making a text representation of a Python object."""
935 def __init__(self):
936 Repr.__init__(self)
937 self.maxlist = self.maxtuple = 20
938 self.maxdict = 10
939 self.maxstring = self.maxother = 100
940
941 def repr1(self, x, level):
942 if hasattr(type(x), '__name__'):
943 methodname = 'repr_' + join(split(type(x).__name__), '_')
944 if hasattr(self, methodname):
945 return getattr(self, methodname)(x, level)
946 return cram(stripid(repr(x)), self.maxother)
947
948 def repr_string(self, x, level):
949 test = cram(x, self.maxstring)
950 testrepr = repr(test)
951 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
952 # Backslashes are only literal in the string and are never
953 # needed to make any special characters, so show a raw string.
954 return 'r' + testrepr[0] + test + testrepr[0]
955 return testrepr
956
957 repr_str = repr_string
958
959 def repr_instance(self, x, level):
960 try:
961 return cram(stripid(repr(x)), self.maxstring)
962 except:
963 return '<%s instance>' % x.__class__.__name__
964
965class TextDoc(Doc):
966 """Formatter class for text documentation."""
967
968 # ------------------------------------------- text formatting utilities
969
970 _repr_instance = TextRepr()
971 repr = _repr_instance.repr
972
973 def bold(self, text):
974 """Format a string in bold by overstriking."""
975 return join(map(lambda ch: ch + '\b' + ch, text), '')
976
977 def indent(self, text, prefix=' '):
978 """Indent text by prepending a given prefix to each line."""
979 if not text: return ''
980 lines = split(text, '\n')
981 lines = map(lambda line, prefix=prefix: prefix + line, lines)
982 if lines: lines[-1] = rstrip(lines[-1])
983 return join(lines, '\n')
984
985 def section(self, title, contents):
986 """Format a section with a given heading."""
987 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
988
989 # ---------------------------------------------- type-specific routines
990
991 def formattree(self, tree, modname, parent=None, prefix=''):
992 """Render in text a class tree as returned by inspect.getclasstree()."""
993 result = ''
994 for entry in tree:
995 if type(entry) is type(()):
996 c, bases = entry
997 result = result + prefix + classname(c, modname)
998 if bases and bases != (parent,):
999 parents = map(lambda c, m=modname: classname(c, m), bases)
1000 result = result + '(%s)' % join(parents, ', ')
1001 result = result + '\n'
1002 elif type(entry) is type([]):
1003 result = result + self.formattree(
1004 entry, modname, c, prefix + ' ')
1005 return result
1006
1007 def docmodule(self, object, name=None, mod=None):
1008 """Produce text documentation for a given module object."""
1009 name = object.__name__ # ignore the passed-in name
1010 synop, desc = splitdoc(getdoc(object))
1011 result = self.section('NAME', name + (synop and ' - ' + synop))
1012
1013 try:
1014 all = object.__all__
1015 except AttributeError:
1016 all = None
1017
1018 try:
1019 file = inspect.getabsfile(object)
1020 except TypeError:
1021 file = '(built-in)'
1022 result = result + self.section('FILE', file)
1023
1024 docloc = self.getdocloc(object)
1025 if docloc is not None:
1026 result = result + self.section('MODULE DOCS', docloc)
1027
1028 if desc:
1029 result = result + self.section('DESCRIPTION', desc)
1030
1031 classes = []
1032 for key, value in inspect.getmembers(object, inspect.isclass):
1033 # if __all__ exists, believe it. Otherwise use old heuristic.
1034 if (all is not None
1035 or (inspect.getmodule(value) or object) is object):
1036 if visiblename(key, all):
1037 classes.append((key, value))
1038 funcs = []
1039 for key, value in inspect.getmembers(object, inspect.isroutine):
1040 # if __all__ exists, believe it. Otherwise use old heuristic.
1041 if (all is not None or
1042 inspect.isbuiltin(value) or inspect.getmodule(value) is object):
1043 if visiblename(key, all):
1044 funcs.append((key, value))
1045 data = []
1046 for key, value in inspect.getmembers(object, isdata):
1047 if visiblename(key, all):
1048 data.append((key, value))
1049
1050 if hasattr(object, '__path__'):
1051 modpkgs = []
1052 for file in os.listdir(object.__path__[0]):
1053 path = os.path.join(object.__path__[0], file)
1054 modname = inspect.getmodulename(file)
1055 if modname != '__init__':
1056 if modname and modname not in modpkgs:
1057 modpkgs.append(modname)
1058 elif ispackage(path):
1059 modpkgs.append(file + ' (package)')
1060 modpkgs.sort()
1061 result = result + self.section(
1062 'PACKAGE CONTENTS', join(modpkgs, '\n'))
1063
1064 if classes:
1065 classlist = map(lambda (key, value): value, classes)
1066 contents = [self.formattree(
1067 inspect.getclasstree(classlist, 1), name)]
1068 for key, value in classes:
1069 contents.append(self.document(value, key, name))
1070 result = result + self.section('CLASSES', join(contents, '\n'))
1071
1072 if funcs:
1073 contents = []
1074 for key, value in funcs:
1075 contents.append(self.document(value, key, name))
1076 result = result + self.section('FUNCTIONS', join(contents, '\n'))
1077
1078 if data:
1079 contents = []
1080 for key, value in data:
1081 contents.append(self.docother(value, key, name, 70))
1082 result = result + self.section('DATA', join(contents, '\n'))
1083
1084 if hasattr(object, '__version__'):
1085 version = str(object.__version__)
1086 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
1087 version = strip(version[11:-1])
1088 result = result + self.section('VERSION', version)
1089 if hasattr(object, '__date__'):
1090 result = result + self.section('DATE', str(object.__date__))
1091 if hasattr(object, '__author__'):
1092 result = result + self.section('AUTHOR', str(object.__author__))
1093 if hasattr(object, '__credits__'):
1094 result = result + self.section('CREDITS', str(object.__credits__))
1095 return result
1096
1097 def docclass(self, object, name=None, mod=None):
1098 """Produce text documentation for a given class object."""
1099 realname = object.__name__
1100 name = name or realname
1101 bases = object.__bases__
1102
1103 def makename(c, m=object.__module__):
1104 return classname(c, m)
1105
1106 if name == realname:
1107 title = 'class ' + self.bold(realname)
1108 else:
1109 title = self.bold(name) + ' = class ' + realname
1110 if bases:
1111 parents = map(makename, bases)
1112 title = title + '(%s)' % join(parents, ', ')
1113
1114 doc = getdoc(object)
1115 contents = doc and [doc + '\n'] or []
1116 push = contents.append
1117
1118 # List the mro, if non-trivial.
1119 mro = deque(inspect.getmro(object))
1120 if len(mro) > 2:
1121 push("Method resolution order:")
1122 for base in mro:
1123 push(' ' + makename(base))
1124 push('')
1125
1126 # Cute little class to pump out a horizontal rule between sections.
1127 class HorizontalRule:
1128 def __init__(self):
1129 self.needone = 0
1130 def maybe(self):
1131 if self.needone:
1132 push('-' * 70)
1133 self.needone = 1
1134 hr = HorizontalRule()
1135
1136 def spill(msg, attrs, predicate):
1137 ok, attrs = _split_list(attrs, predicate)
1138 if ok:
1139 hr.maybe()
1140 push(msg)
1141 for name, kind, homecls, value in ok:
1142 push(self.document(getattr(object, name),
1143 name, mod, object))
1144 return attrs
1145
1146 def spillproperties(msg, attrs, predicate):
1147 ok, attrs = _split_list(attrs, predicate)
1148 if ok:
1149 hr.maybe()
1150 push(msg)
1151 for name, kind, homecls, value in ok:
1152 push(self._docproperty(name, value, mod))
1153 return attrs
1154
1155 def spilldata(msg, attrs, predicate):
1156 ok, attrs = _split_list(attrs, predicate)
1157 if ok:
1158 hr.maybe()
1159 push(msg)
1160 for name, kind, homecls, value in ok:
1161 if callable(value) or inspect.isdatadescriptor(value):
1162 doc = getdoc(value)
1163 else:
1164 doc = None
1165 push(self.docother(getattr(object, name),
1166 name, mod, 70, doc) + '\n')
1167 return attrs
1168
1169 attrs = filter(lambda (name, kind, cls, value): visiblename(name),
1170 inspect.classify_class_attrs(object))
1171 while attrs:
1172 if mro:
1173 thisclass = mro.popleft()
1174 else:
1175 thisclass = attrs[0][2]
1176 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
1177
1178 if thisclass is __builtin__.object:
1179 attrs = inherited
1180 continue
1181 elif thisclass is object:
1182 tag = "defined here"
1183 else:
1184 tag = "inherited from %s" % classname(thisclass,
1185 object.__module__)
1186 filter(lambda t: not t[0].startswith('_'), attrs)
1187
1188 # Sort attrs by name.
1189 attrs.sort()
1190
1191 # Pump out the attrs, segregated by kind.
1192 attrs = spill("Methods %s:\n" % tag, attrs,
1193 lambda t: t[1] == 'method')
1194 attrs = spill("Class methods %s:\n" % tag, attrs,
1195 lambda t: t[1] == 'class method')
1196 attrs = spill("Static methods %s:\n" % tag, attrs,
1197 lambda t: t[1] == 'static method')
1198 attrs = spillproperties("Properties %s:\n" % tag, attrs,
1199 lambda t: t[1] == 'property')
1200 attrs = spilldata("Data and other attributes %s:\n" % tag, attrs,
1201 lambda t: t[1] == 'data')
1202 assert attrs == []
1203 attrs = inherited
1204
1205 contents = '\n'.join(contents)
1206 if not contents:
1207 return title + '\n'
1208 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
1209
1210 def formatvalue(self, object):
1211 """Format an argument default value as text."""
1212 return '=' + self.repr(object)
1213
1214 def docroutine(self, object, name=None, mod=None, cl=None):
1215 """Produce text documentation for a function or method object."""
1216 realname = object.__name__
1217 name = name or realname
1218 note = ''
1219 skipdocs = 0
1220 if inspect.ismethod(object):
1221 imclass = object.im_class
1222 if cl:
1223 if imclass is not cl:
1224 note = ' from ' + classname(imclass, mod)
1225 else:
1226 if object.im_self:
1227 note = ' method of %s instance' % classname(
1228 object.im_self.__class__, mod)
1229 else:
1230 note = ' unbound %s method' % classname(imclass,mod)
1231 object = object.im_func
1232
1233 if name == realname:
1234 title = self.bold(realname)
1235 else:
1236 if (cl and realname in cl.__dict__ and
1237 cl.__dict__[realname] is object):
1238 skipdocs = 1
1239 title = self.bold(name) + ' = ' + realname
1240 if inspect.isfunction(object):
1241 args, varargs, varkw, defaults = inspect.getargspec(object)
1242 argspec = inspect.formatargspec(
1243 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
1244 if realname == '<lambda>':
1245 title = 'lambda'
1246 argspec = argspec[1:-1] # remove parentheses
1247 else:
1248 argspec = '(...)'
1249 decl = title + argspec + note
1250
1251 if skipdocs:
1252 return decl + '\n'
1253 else:
1254 doc = getdoc(object) or ''
1255 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
1256
1257 def _docproperty(self, name, value, mod):
1258 results = []
1259 push = results.append
1260
1261 if name:
1262 push(name)
1263 need_blank_after_doc = 0
1264 doc = getdoc(value) or ''
1265 if doc:
1266 push(self.indent(doc))
1267 need_blank_after_doc = 1
1268 for attr, tag in [('fget', '<get>'),
1269 ('fset', '<set>'),
1270 ('fdel', '<delete>')]:
1271 func = getattr(value, attr)
1272 if func is not None:
1273 if need_blank_after_doc:
1274 push('')
1275 need_blank_after_doc = 0
1276 base = self.document(func, tag, mod)
1277 push(self.indent(base))
1278
1279 return '\n'.join(results)
1280
1281 def docproperty(self, object, name=None, mod=None, cl=None):
1282 """Produce text documentation for a property."""
1283 return self._docproperty(name, object, mod)
1284
1285 def docother(self, object, name=None, mod=None, maxlen=None, doc=None):
1286 """Produce text documentation for a data object."""
1287 repr = self.repr(object)
1288 if maxlen:
1289 line = (name and name + ' = ' or '') + repr
1290 chop = maxlen - len(line)
1291 if chop < 0: repr = repr[:chop] + '...'
1292 line = (name and self.bold(name) + ' = ' or '') + repr
1293 if doc is not None:
1294 line += '\n' + self.indent(str(doc))
1295 return line
1296
1297# --------------------------------------------------------- user interfaces
1298
1299def pager(text):
1300 """The first time this is called, determine what kind of pager to use."""
1301 global pager
1302 pager = getpager()
1303 pager(text)
1304
1305def getpager():
1306 """Decide what method to use for paging through text."""
1307 if type(sys.stdout) is not types.FileType:
1308 return plainpager
1309 if not sys.stdin.isatty() or not sys.stdout.isatty():
1310 return plainpager
1311 if os.environ.get('TERM') in ['dumb', 'emacs']:
1312 return plainpager
1313 if 'PAGER' in os.environ:
1314 if sys.platform == 'win32': # pipes completely broken in Windows
1315 return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
1316 elif os.environ.get('TERM') in ['dumb', 'emacs']:
1317 return lambda text: pipepager(plain(text), os.environ['PAGER'])
1318 else:
1319 return lambda text: pipepager(text, os.environ['PAGER'])
1320 if sys.platform == 'win32' or sys.platform.startswith('os2'):
1321 return lambda text: tempfilepager(plain(text), 'more <')
1322 if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0:
1323 return lambda text: pipepager(text, 'less')
1324
1325 import tempfile
1326 (fd, filename) = tempfile.mkstemp()
1327 os.close(fd)
1328 try:
1329 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
1330 return lambda text: pipepager(text, 'more')
1331 else:
1332 return ttypager
1333 finally:
1334 os.unlink(filename)
1335
1336def plain(text):
1337 """Remove boldface formatting from text."""
1338 return re.sub('.\b', '', text)
1339
1340def pipepager(text, cmd):
1341 """Page through text by feeding it to another program."""
1342 pipe = os.popen(cmd, 'w')
1343 try:
1344 pipe.write(text)
1345 pipe.close()
1346 except IOError:
1347 pass # Ignore broken pipes caused by quitting the pager program.
1348
1349def tempfilepager(text, cmd):
1350 """Page through text by invoking a program on a temporary file."""
1351 import tempfile
1352 filename = tempfile.mktemp()
1353 file = open(filename, 'w')
1354 file.write(text)
1355 file.close()
1356 try:
1357 os.system(cmd + ' ' + filename)
1358 finally:
1359 os.unlink(filename)
1360
1361def ttypager(text):
1362 """Page through text on a text terminal."""
1363 lines = split(plain(text), '\n')
1364 try:
1365 import tty
1366 fd = sys.stdin.fileno()
1367 old = tty.tcgetattr(fd)
1368 tty.setcbreak(fd)
1369 getchar = lambda: sys.stdin.read(1)
1370 except (ImportError, AttributeError):
1371 tty = None
1372 getchar = lambda: sys.stdin.readline()[:-1][:1]
1373
1374 try:
1375 r = inc = os.environ.get('LINES', 25) - 1
1376 sys.stdout.write(join(lines[:inc], '\n') + '\n')
1377 while lines[r:]:
1378 sys.stdout.write('-- more --')
1379 sys.stdout.flush()
1380 c = getchar()
1381
1382 if c in ['q', 'Q']:
1383 sys.stdout.write('\r \r')
1384 break
1385 elif c in ['\r', '\n']:
1386 sys.stdout.write('\r \r' + lines[r] + '\n')
1387 r = r + 1
1388 continue
1389 if c in ['b', 'B', '\x1b']:
1390 r = r - inc - inc
1391 if r < 0: r = 0
1392 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1393 r = r + inc
1394
1395 finally:
1396 if tty:
1397 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1398
1399def plainpager(text):
1400 """Simply print unformatted text. This is the ultimate fallback."""
1401 sys.stdout.write(plain(text))
1402
1403def describe(thing):
1404 """Produce a short description of the given thing."""
1405 if inspect.ismodule(thing):
1406 if thing.__name__ in sys.builtin_module_names:
1407 return 'built-in module ' + thing.__name__
1408 if hasattr(thing, '__path__'):
1409 return 'package ' + thing.__name__
1410 else:
1411 return 'module ' + thing.__name__
1412 if inspect.isbuiltin(thing):
1413 return 'built-in function ' + thing.__name__
1414 if inspect.isclass(thing):
1415 return 'class ' + thing.__name__
1416 if inspect.isfunction(thing):
1417 return 'function ' + thing.__name__
1418 if inspect.ismethod(thing):
1419 return 'method ' + thing.__name__
1420 if type(thing) is types.InstanceType:
1421 return 'instance of ' + thing.__class__.__name__
1422 return type(thing).__name__
1423
1424def locate(path, forceload=0):
1425 """Locate an object by name or dotted path, importing as necessary."""
1426 parts = [part for part in split(path, '.') if part]
1427 module, n = None, 0
1428 while n < len(parts):
1429 nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
1430 if nextmodule: module, n = nextmodule, n + 1
1431 else: break
1432 if module:
1433 object = module
1434 for part in parts[n:]:
1435 try: object = getattr(object, part)
1436 except AttributeError: return None
1437 return object
1438 else:
1439 if hasattr(__builtin__, path):
1440 return getattr(__builtin__, path)
1441
1442# --------------------------------------- interactive interpreter interface
1443
1444text = TextDoc()
1445html = HTMLDoc()
1446
1447def resolve(thing, forceload=0):
1448 """Given an object or a path to an object, get the object and its name."""
1449 if isinstance(thing, str):
1450 object = locate(thing, forceload)
1451 if not object:
1452 raise ImportError, 'no Python documentation found for %r' % thing
1453 return object, thing
1454 else:
1455 return thing, getattr(thing, '__name__', None)
1456
1457def doc(thing, title='Python Library Documentation: %s', forceload=0):
1458 """Display text documentation, given an object or a path to an object."""
1459 try:
1460 object, name = resolve(thing, forceload)
1461 desc = describe(object)
1462 module = inspect.getmodule(object)
1463 if name and '.' in name:
1464 desc += ' in ' + name[:name.rfind('.')]
1465 elif module and module is not object:
1466 desc += ' in module ' + module.__name__
1467 if not (inspect.ismodule(object) or
1468 inspect.isclass(object) or
1469 inspect.isroutine(object) or
1470 isinstance(object, property)):
1471 # If the passed object is a piece of data or an instance,
1472 # document its available methods instead of its value.
1473 object = type(object)
1474 desc += ' object'
1475 pager(title % desc + '\n\n' + text.document(object, name))
1476 except (ImportError, ErrorDuringImport), value:
1477 print value
1478
1479def writedoc(thing, forceload=0):
1480 """Write HTML documentation to a file in the current directory."""
1481 try:
1482 object, name = resolve(thing, forceload)
1483 page = html.page(describe(object), html.document(object, name))
1484 file = open(name + '.html', 'w')
1485 file.write(page)
1486 file.close()
1487 print 'wrote', name + '.html'
1488 except (ImportError, ErrorDuringImport), value:
1489 print value
1490
1491def writedocs(dir, pkgpath='', done=None):
1492 """Write out HTML documentation for all modules in a directory tree."""
1493 if done is None: done = {}
1494 for file in os.listdir(dir):
1495 path = os.path.join(dir, file)
1496 if ispackage(path):
1497 writedocs(path, pkgpath + file + '.', done)
1498 elif os.path.isfile(path):
1499 modname = inspect.getmodulename(path)
1500 if modname:
1501 if modname == '__init__':
1502 modname = pkgpath[:-1] # remove trailing period
1503 else:
1504 modname = pkgpath + modname
1505 if modname not in done:
1506 done[modname] = 1
1507 writedoc(modname)
1508
1509class Helper:
1510 keywords = {
1511 'and': 'BOOLEAN',
1512 'assert': ('ref/assert', ''),
1513 'break': ('ref/break', 'while for'),
1514 'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1515 'continue': ('ref/continue', 'while for'),
1516 'def': ('ref/function', ''),
1517 'del': ('ref/del', 'BASICMETHODS'),
1518 'elif': 'if',
1519 'else': ('ref/if', 'while for'),
1520 'except': 'try',
1521 'exec': ('ref/exec', ''),
1522 'finally': 'try',
1523 'for': ('ref/for', 'break continue while'),
1524 'from': 'import',
1525 'global': ('ref/global', 'NAMESPACES'),
1526 'if': ('ref/if', 'TRUTHVALUE'),
1527 'import': ('ref/import', 'MODULES'),
1528 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1529 'is': 'COMPARISON',
1530 'lambda': ('ref/lambdas', 'FUNCTIONS'),
1531 'not': 'BOOLEAN',
1532 'or': 'BOOLEAN',
1533 'pass': ('ref/pass', ''),
1534 'print': ('ref/print', ''),
1535 'raise': ('ref/raise', 'EXCEPTIONS'),
1536 'return': ('ref/return', 'FUNCTIONS'),
1537 'try': ('ref/try', 'EXCEPTIONS'),
1538 'while': ('ref/while', 'break continue if TRUTHVALUE'),
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 }
1621
1622 def __init__(self, input, output):
1623 self.input = input
1624 self.output = output
1625 self.docdir = None
1626 execdir = os.path.dirname(sys.executable)
1627 homedir = os.environ.get('PYTHONHOME')
1628 for dir in [os.environ.get('PYTHONDOCS'),
1629 homedir and os.path.join(homedir, 'doc'),
1630 os.path.join(execdir, 'doc'),
1631 '/usr/doc/python-docs-' + split(sys.version)[0],
1632 '/usr/doc/python-' + split(sys.version)[0],
1633 '/usr/doc/python-docs-' + sys.version[:3],
1634 '/usr/doc/python-' + sys.version[:3],
1635 os.path.join(sys.prefix, 'Resources/English.lproj/Documentation')]:
1636 if dir and os.path.isdir(os.path.join(dir, 'lib')):
1637 self.docdir = dir
1638
1639 def __repr__(self):
1640 if inspect.stack()[1][3] == '?':
1641 self()
1642 return ''
1643 return '<pydoc.Helper instance>'
1644
1645 def __call__(self, request=None):
1646 if request is not None:
1647 self.help(request)
1648 else:
1649 self.intro()
1650 self.interact()
1651 self.output.write('''
1652You are now leaving help and returning to the Python interpreter.
1653If you want to ask for help on a particular object directly from the
1654interpreter, you can type "help(object)". Executing "help('string')"
1655has the same effect as typing a particular string at the help> prompt.
1656''')
1657
1658 def interact(self):
1659 self.output.write('\n')
1660 while True:
1661 try:
1662 request = self.getline('help> ')
1663 if not request: break
1664 except (KeyboardInterrupt, EOFError):
1665 break
1666 request = strip(replace(request, '"', '', "'", ''))
1667 if lower(request) in ['q', 'quit']: break
1668 self.help(request)
1669
1670 def getline(self, prompt):
1671 """Read one line, using raw_input when available."""
1672 if self.input is sys.stdin:
1673 return raw_input(prompt)
1674 else:
1675 self.output.write(prompt)
1676 self.output.flush()
1677 return self.input.readline()
1678
1679 def help(self, request):
1680 if type(request) is type(''):
1681 if request == 'help': self.intro()
1682 elif request == 'keywords': self.listkeywords()
1683 elif request == 'topics': self.listtopics()
1684 elif request == 'modules': self.listmodules()
1685 elif request[:8] == 'modules ':
1686 self.listmodules(split(request)[1])
1687 elif request in self.keywords: self.showtopic(request)
1688 elif request in self.topics: self.showtopic(request)
1689 elif request: doc(request, 'Help on %s:')
1690 elif isinstance(request, Helper): self()
1691 else: doc(request, 'Help on %s:')
1692 self.output.write('\n')
1693
1694 def intro(self):
1695 self.output.write('''
1696Welcome to Python %s! This is the online help utility.
1697
1698If this is your first time using Python, you should definitely check out
1699the tutorial on the Internet at http://www.python.org/doc/tut/.
1700
1701Enter the name of any module, keyword, or topic to get help on writing
1702Python programs and using Python modules. To quit this help utility and
1703return to the interpreter, just type "quit".
1704
1705To get a list of available modules, keywords, or topics, type "modules",
1706"keywords", or "topics". Each module also comes with a one-line summary
1707of what it does; to list the modules whose summaries contain a given word
1708such as "spam", type "modules spam".
1709''' % sys.version[:3])
1710
1711 def list(self, items, columns=4, width=80):
1712 items = items[:]
1713 items.sort()
1714 colw = width / columns
1715 rows = (len(items) + columns - 1) / columns
1716 for row in range(rows):
1717 for col in range(columns):
1718 i = col * rows + row
1719 if i < len(items):
1720 self.output.write(items[i])
1721 if col < columns - 1:
1722 self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1723 self.output.write('\n')
1724
1725 def listkeywords(self):
1726 self.output.write('''
1727Here is a list of the Python keywords. Enter any keyword to get more help.
1728
1729''')
1730 self.list(self.keywords.keys())
1731
1732 def listtopics(self):
1733 self.output.write('''
1734Here is a list of available topics. Enter any topic name to get more help.
1735
1736''')
1737 self.list(self.topics.keys())
1738
1739 def showtopic(self, topic):
1740 if not self.docdir:
1741 self.output.write('''
1742Sorry, topic and keyword documentation is not available because the Python
1743HTML documentation files could not be found. If you have installed them,
1744please set the environment variable PYTHONDOCS to indicate their location.
1745''')
1746 return
1747 target = self.topics.get(topic, self.keywords.get(topic))
1748 if not target:
1749 self.output.write('no documentation found for %s\n' % repr(topic))
1750 return
1751 if type(target) is type(''):
1752 return self.showtopic(target)
1753
1754 filename, xrefs = target
1755 filename = self.docdir + '/' + filename + '.html'
1756 try:
1757 file = open(filename)
1758 except:
1759 self.output.write('could not read docs from %s\n' % filename)
1760 return
1761
1762 divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
1763 addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
1764 document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1765 file.close()
1766
1767 import htmllib, formatter, StringIO
1768 buffer = StringIO.StringIO()
1769 parser = htmllib.HTMLParser(
1770 formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1771 parser.start_table = parser.do_p
1772 parser.end_table = lambda parser=parser: parser.do_p({})
1773 parser.start_tr = parser.do_br
1774 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1775 parser.feed(document)
1776 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
1777 pager(' ' + strip(buffer) + '\n')
1778 if xrefs:
1779 buffer = StringIO.StringIO()
1780 formatter.DumbWriter(buffer).send_flowing_data(
1781 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1782 self.output.write('\n%s\n' % buffer.getvalue())
1783
1784 def listmodules(self, key=''):
1785 if key:
1786 self.output.write('''
1787Here is a list of matching modules. Enter any module name to get more help.
1788
1789''')
1790 apropos(key)
1791 else:
1792 self.output.write('''
1793Please wait a moment while I gather a list of all available modules...
1794
1795''')
1796 modules = {}
1797 def callback(path, modname, desc, modules=modules):
1798 if modname and modname[-9:] == '.__init__':
1799 modname = modname[:-9] + ' (package)'
1800 if find(modname, '.') < 0:
1801 modules[modname] = 1
1802 ModuleScanner().run(callback)
1803 self.list(modules.keys())
1804 self.output.write('''
1805Enter any module name to get more help. Or, type "modules spam" to search
1806for modules whose descriptions contain the word "spam".
1807''')
1808
1809help = Helper(sys.stdin, sys.stdout)
1810
1811class Scanner:
1812 """A generic tree iterator."""
1813 def __init__(self, roots, children, descendp):
1814 self.roots = roots[:]
1815 self.state = []
1816 self.children = children
1817 self.descendp = descendp
1818
1819 def next(self):
1820 if not self.state:
1821 if not self.roots:
1822 return None
1823 root = self.roots.pop(0)
1824 self.state = [(root, self.children(root))]
1825 node, children = self.state[-1]
1826 if not children:
1827 self.state.pop()
1828 return self.next()
1829 child = children.pop(0)
1830 if self.descendp(child):
1831 self.state.append((child, self.children(child)))
1832 return child
1833
1834class ModuleScanner(Scanner):
1835 """An interruptible scanner that searches module synopses."""
1836 def __init__(self):
1837 roots = map(lambda dir: (dir, ''), pathdirs())
1838 Scanner.__init__(self, roots, self.submodules, self.isnewpackage)
1839 self.inodes = map(lambda (dir, pkg): os.stat(dir).st_ino, roots)
1840
1841 def submodules(self, (dir, package)):
1842 children = []
1843 for file in os.listdir(dir):
1844 path = os.path.join(dir, file)
1845 if ispackage(path):
1846 children.append((path, package + (package and '.') + file))
1847 else:
1848 children.append((path, package))
1849 children.sort() # so that spam.py comes before spam.pyc or spam.pyo
1850 return children
1851
1852 def isnewpackage(self, (dir, package)):
1853 inode = os.path.exists(dir) and os.stat(dir).st_ino
1854 if not (os.path.islink(dir) and inode in self.inodes):
1855 self.inodes.append(inode) # detect circular symbolic links
1856 return ispackage(dir)
1857 return False
1858
1859 def run(self, callback, key=None, completer=None):
1860 if key: key = lower(key)
1861 self.quit = False
1862 seen = {}
1863
1864 for modname in sys.builtin_module_names:
1865 if modname != '__main__':
1866 seen[modname] = 1
1867 if key is None:
1868 callback(None, modname, '')
1869 else:
1870 desc = split(__import__(modname).__doc__ or '', '\n')[0]
1871 if find(lower(modname + ' - ' + desc), key) >= 0:
1872 callback(None, modname, desc)
1873
1874 while not self.quit:
1875 node = self.next()
1876 if not node: break
1877 path, package = node
1878 modname = inspect.getmodulename(path)
1879 if os.path.isfile(path) and modname:
1880 modname = package + (package and '.') + modname
1881 if not modname in seen:
1882 seen[modname] = 1 # if we see spam.py, skip spam.pyc
1883 if key is None:
1884 callback(path, modname, '')
1885 else:
1886 desc = synopsis(path) or ''
1887 if find(lower(modname + ' - ' + desc), key) >= 0:
1888 callback(path, modname, desc)
1889 if completer: completer()
1890
1891def apropos(key):
1892 """Print all the one-line module summaries that contain a substring."""
1893 def callback(path, modname, desc):
1894 if modname[-9:] == '.__init__':
1895 modname = modname[:-9] + ' (package)'
1896 print modname, desc and '- ' + desc
1897 try: import warnings
1898 except ImportError: pass
1899 else: warnings.filterwarnings('ignore') # ignore problems during import
1900 ModuleScanner().run(callback, key)
1901
1902# --------------------------------------------------- web browser interface
1903
1904def serve(port, callback=None, completer=None):
1905 import BaseHTTPServer, mimetools, select
1906
1907 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1908 class Message(mimetools.Message):
1909 def __init__(self, fp, seekable=1):
1910 Message = self.__class__
1911 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1912 self.encodingheader = self.getheader('content-transfer-encoding')
1913 self.typeheader = self.getheader('content-type')
1914 self.parsetype()
1915 self.parseplist()
1916
1917 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1918 def send_document(self, title, contents):
1919 try:
1920 self.send_response(200)
1921 self.send_header('Content-Type', 'text/html')
1922 self.end_headers()
1923 self.wfile.write(html.page(title, contents))
1924 except IOError: pass
1925
1926 def do_GET(self):
1927 path = self.path
1928 if path[-5:] == '.html': path = path[:-5]
1929 if path[:1] == '/': path = path[1:]
1930 if path and path != '.':
1931 try:
1932 obj = locate(path, forceload=1)
1933 except ErrorDuringImport, value:
1934 self.send_document(path, html.escape(str(value)))
1935 return
1936 if obj:
1937 self.send_document(describe(obj), html.document(obj, path))
1938 else:
1939 self.send_document(path,
1940'no Python documentation found for %s' % repr(path))
1941 else:
1942 heading = html.heading(
1943'<big><big><strong>Python: Index of Modules</strong></big></big>',
1944'#ffffff', '#7799ee')
1945 def bltinlink(name):
1946 return '<a href="%s.html">%s</a>' % (name, name)
1947 names = filter(lambda x: x != '__main__',
1948 sys.builtin_module_names)
1949 contents = html.multicolumn(names, bltinlink)
1950 indices = ['<p>' + html.bigsection(
1951 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1952
1953 seen = {}
1954 for dir in pathdirs():
1955 indices.append(html.index(dir, seen))
1956 contents = heading + join(indices) + '''<p align=right>
1957<font color="#909090" face="helvetica, arial"><strong>
1958pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font>'''
1959 self.send_document('Index of Modules', contents)
1960
1961 def log_message(self, *args): pass
1962
1963 class DocServer(BaseHTTPServer.HTTPServer):
1964 def __init__(self, port, callback):
1965 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
1966 self.address = ('', port)
1967 self.url = 'http://%s:%d/' % (host, port)
1968 self.callback = callback
1969 self.base.__init__(self, self.address, self.handler)
1970
1971 def serve_until_quit(self):
1972 import select
1973 self.quit = False
1974 while not self.quit:
1975 rd, wr, ex = select.select([self.socket.fileno()], [], [], 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()