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