1 #!/usr/bin/env python 2 # -*- coding: latin-1 -*- 3 """Generate Python documentation in HTML or text for interactive use. 4 5 In the Python interpreter, do "from pydoc import help" to provide online 6 help. Calling help(thing) on a Python object documents the object. 7 8 Or, at the shell command line outside of Python: 9 10 Run "pydoc <name>" to show documentation on something. <name> may be 11 the name of a function, module, package, or a dotted reference to a 12 class or function within a module or module in a package. If the 13 argument contains a path segment delimiter (e.g. slash on Unix, 14 backslash on Windows) it is treated as the path to a Python source file. 15 16 Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines 17 of all available modules. 18 19 Run "pydoc -p <port>" to start an HTTP server on a given port on the 20 local machine to generate documentation web pages. 21 22 For platforms without a command line, "pydoc -g" starts the HTTP server 23 and also pops up a little window for controlling it. 24 25 Run "pydoc -w <name>" to write out the HTML documentation for a module 26 to a file named "<name>.html". 27 28 Module docs for core modules are assumed to be in 29 30 http://docs.python.org/library/ 31 32 This can be overridden by setting the PYTHONDOCS environment variable 33 to a different URL or to a local directory containing the Library 34 Reference Manual pages. 35 """ 36 37 __author__ = "Ka-Ping Yee <ping (at] lfw.org>" 38 __date__ = "26 February 2001" 39 40 __version__ = "$Revision$" 41 __credits__ = """Guido van Rossum, for an excellent programming language. 42 Tommy Burnette, the original creator of manpy. 43 Paul Prescod, for all his work on onlinehelp. 44 Richard Chamberlain, for the first implementation of textdoc. 45 """ 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 55 import sys, imp, os, re, types, inspect, __builtin__, pkgutil 56 from repr import Repr 57 from string import expandtabs, find, join, lower, split, strip, rfind, rstrip 58 from traceback import extract_tb 59 try: 60 from collections import deque 61 except ImportError: 62 # Python 2.3 compatibility 63 class deque(list): 64 def popleft(self): 65 return self.pop(0) 66 67 # --------------------------------------------------------- common routines 68 69 def 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 81 def 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 86 def 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 95 def 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 102 def 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 108 def 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 115 def 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) 124 def 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 129 def _is_some_method(obj): 130 return inspect.ismethod(obj) or inspect.ismethoddescriptor(obj) 131 132 def 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 142 def _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 159 def 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 176 def 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 187 def 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 195 def 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 212 def 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 234 class 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 249 def 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 268 def 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 318 class 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 374 class 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 416 class 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 945 class 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 977 class 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 1314 def 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 1320 def 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 sys.platform == 'uefi': 1330 return lambda text: tempfilepager(plain(text), os.environ['PAGER']) 1331 elif os.environ.get('TERM') in ('dumb', 'emacs'): 1332 return lambda text: pipepager(plain(text), os.environ['PAGER']) 1333 else: 1334 return lambda text: pipepager(text, os.environ['PAGER']) 1335 if os.environ.get('TERM') in ('dumb', 'emacs'): 1336 return plainpager 1337 if sys.platform == 'uefi': 1338 return plainpager 1339 if sys.platform == 'win32' or sys.platform.startswith('os2'): 1340 return lambda text: tempfilepager(plain(text), 'more <') 1341 if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0: 1342 return lambda text: pipepager(text, 'less') 1343 1344 import tempfile 1345 (fd, filename) = tempfile.mkstemp() 1346 os.close(fd) 1347 try: 1348 if hasattr(os, 'system') and os.system('more "%s"' % filename) == 0: 1349 return lambda text: pipepager(text, 'more') 1350 else: 1351 return ttypager 1352 finally: 1353 os.unlink(filename) 1354 1355 def plain(text): 1356 """Remove boldface formatting from text.""" 1357 return re.sub('.\b', '', text) 1358 1359 def pipepager(text, cmd): 1360 """Page through text by feeding it to another program.""" 1361 pipe = os.popen(cmd, 'w') 1362 try: 1363 pipe.write(text) 1364 pipe.close() 1365 except IOError: 1366 pass # Ignore broken pipes caused by quitting the pager program. 1367 1368 def tempfilepager(text, cmd): 1369 """Page through text by invoking a program on a temporary file.""" 1370 import tempfile 1371 filename = tempfile.mktemp() 1372 file = open(filename, 'w') 1373 file.write(text) 1374 file.close() 1375 try: 1376 os.system(cmd + ' "' + filename + '"') 1377 finally: 1378 os.unlink(filename) 1379 1380 def ttypager(text): 1381 """Page through text on a text terminal.""" 1382 lines = split(plain(text), '\n') 1383 try: 1384 import tty 1385 fd = sys.stdin.fileno() 1386 old = tty.tcgetattr(fd) 1387 tty.setcbreak(fd) 1388 getchar = lambda: sys.stdin.read(1) 1389 except (ImportError, AttributeError): 1390 tty = None 1391 getchar = lambda: sys.stdin.readline()[:-1][:1] 1392 1393 try: 1394 r = inc = os.environ.get('LINES', 25) - 1 1395 sys.stdout.write(join(lines[:inc], '\n') + '\n') 1396 while lines[r:]: 1397 sys.stdout.write('-- more --') 1398 sys.stdout.flush() 1399 c = getchar() 1400 1401 if c in ('q', 'Q'): 1402 sys.stdout.write('\r \r') 1403 break 1404 elif c in ('\r', '\n'): 1405 sys.stdout.write('\r \r' + lines[r] + '\n') 1406 r = r + 1 1407 continue 1408 if c in ('b', 'B', '\x1b'): 1409 r = r - inc - inc 1410 if r < 0: r = 0 1411 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n') 1412 r = r + inc 1413 1414 finally: 1415 if tty: 1416 tty.tcsetattr(fd, tty.TCSAFLUSH, old) 1417 1418 def plainpager(text): 1419 """Simply print unformatted text. This is the ultimate fallback.""" 1420 sys.stdout.write(plain(text)) 1421 1422 def describe(thing): 1423 """Produce a short description of the given thing.""" 1424 if inspect.ismodule(thing): 1425 if thing.__name__ in sys.builtin_module_names: 1426 return 'built-in module ' + thing.__name__ 1427 if hasattr(thing, '__path__'): 1428 return 'package ' + thing.__name__ 1429 else: 1430 return 'module ' + thing.__name__ 1431 if inspect.isbuiltin(thing): 1432 return 'built-in function ' + thing.__name__ 1433 if inspect.isgetsetdescriptor(thing): 1434 return 'getset descriptor %s.%s.%s' % ( 1435 thing.__objclass__.__module__, thing.__objclass__.__name__, 1436 thing.__name__) 1437 if inspect.ismemberdescriptor(thing): 1438 return 'member descriptor %s.%s.%s' % ( 1439 thing.__objclass__.__module__, thing.__objclass__.__name__, 1440 thing.__name__) 1441 if inspect.isclass(thing): 1442 return 'class ' + thing.__name__ 1443 if inspect.isfunction(thing): 1444 return 'function ' + thing.__name__ 1445 if inspect.ismethod(thing): 1446 return 'method ' + thing.__name__ 1447 if type(thing) is types.InstanceType: 1448 return 'instance of ' + thing.__class__.__name__ 1449 return type(thing).__name__ 1450 1451 def locate(path, forceload=0): 1452 """Locate an object by name or dotted path, importing as necessary.""" 1453 parts = [part for part in split(path, '.') if part] 1454 module, n = None, 0 1455 while n < len(parts): 1456 nextmodule = safeimport(join(parts[:n+1], '.'), forceload) 1457 if nextmodule: module, n = nextmodule, n + 1 1458 else: break 1459 if module: 1460 object = module 1461 for part in parts[n:]: 1462 try: object = getattr(object, part) 1463 except AttributeError: return None 1464 return object 1465 else: 1466 if hasattr(__builtin__, path): 1467 return getattr(__builtin__, path) 1468 1469 # --------------------------------------- interactive interpreter interface 1470 1471 text = TextDoc() 1472 html = HTMLDoc() 1473 1474 class _OldStyleClass: pass 1475 _OLD_INSTANCE_TYPE = type(_OldStyleClass()) 1476 1477 def resolve(thing, forceload=0): 1478 """Given an object or a path to an object, get the object and its name.""" 1479 if isinstance(thing, str): 1480 object = locate(thing, forceload) 1481 if not object: 1482 raise ImportError, 'no Python documentation found for %r' % thing 1483 return object, thing 1484 else: 1485 return thing, getattr(thing, '__name__', None) 1486 1487 def render_doc(thing, title='Python Library Documentation: %s', forceload=0): 1488 """Render text documentation, given an object or a path to an object.""" 1489 object, name = resolve(thing, forceload) 1490 desc = describe(object) 1491 module = inspect.getmodule(object) 1492 if name and '.' in name: 1493 desc += ' in ' + name[:name.rfind('.')] 1494 elif module and module is not object: 1495 desc += ' in module ' + module.__name__ 1496 if type(object) is _OLD_INSTANCE_TYPE: 1497 # If the passed object is an instance of an old-style class, 1498 # document its available methods instead of its value. 1499 object = object.__class__ 1500 elif not (inspect.ismodule(object) or 1501 inspect.isclass(object) or 1502 inspect.isroutine(object) or 1503 inspect.isgetsetdescriptor(object) or 1504 inspect.ismemberdescriptor(object) or 1505 isinstance(object, property)): 1506 # If the passed object is a piece of data or an instance, 1507 # document its available methods instead of its value. 1508 object = type(object) 1509 desc += ' object' 1510 return title % desc + '\n\n' + text.document(object, name) 1511 1512 def doc(thing, title='Python Library Documentation: %s', forceload=0): 1513 """Display text documentation, given an object or a path to an object.""" 1514 try: 1515 pager(render_doc(thing, title, forceload)) 1516 except (ImportError, ErrorDuringImport), value: 1517 print value 1518 1519 def writedoc(thing, forceload=0): 1520 """Write HTML documentation to a file in the current directory.""" 1521 try: 1522 object, name = resolve(thing, forceload) 1523 page = html.page(describe(object), html.document(object, name)) 1524 file = open(name + '.html', 'w') 1525 file.write(page) 1526 file.close() 1527 print 'wrote', name + '.html' 1528 except (ImportError, ErrorDuringImport), value: 1529 print value 1530 1531 def writedocs(dir, pkgpath='', done=None): 1532 """Write out HTML documentation for all modules in a directory tree.""" 1533 if done is None: done = {} 1534 for importer, modname, ispkg in pkgutil.walk_packages([dir], pkgpath): 1535 writedoc(modname) 1536 return 1537 1538 class Helper: 1539 1540 # These dictionaries map a topic name to either an alias, or a tuple 1541 # (label, seealso-items). The "label" is the label of the corresponding 1542 # section in the .rst file under Doc/ and an index into the dictionary 1543 # in pydoc_data/topics.py. 1544 # 1545 # CAUTION: if you change one of these dictionaries, be sure to adapt the 1546 # list of needed labels in Doc/tools/sphinxext/pyspecific.py and 1547 # regenerate the pydoc_data/topics.py file by running 1548 # make pydoc-topics 1549 # in Doc/ and copying the output file into the Lib/ directory. 1550 1551 keywords = { 1552 'and': 'BOOLEAN', 1553 'as': 'with', 1554 'assert': ('assert', ''), 1555 'break': ('break', 'while for'), 1556 'class': ('class', 'CLASSES SPECIALMETHODS'), 1557 'continue': ('continue', 'while for'), 1558 'def': ('function', ''), 1559 'del': ('del', 'BASICMETHODS'), 1560 'elif': 'if', 1561 'else': ('else', 'while for'), 1562 'except': 'try', 1563 'exec': ('exec', ''), 1564 'finally': 'try', 1565 'for': ('for', 'break continue while'), 1566 'from': 'import', 1567 'global': ('global', 'NAMESPACES'), 1568 'if': ('if', 'TRUTHVALUE'), 1569 'import': ('import', 'MODULES'), 1570 'in': ('in', 'SEQUENCEMETHODS2'), 1571 'is': 'COMPARISON', 1572 'lambda': ('lambda', 'FUNCTIONS'), 1573 'not': 'BOOLEAN', 1574 'or': 'BOOLEAN', 1575 'pass': ('pass', ''), 1576 'print': ('print', ''), 1577 'raise': ('raise', 'EXCEPTIONS'), 1578 'return': ('return', 'FUNCTIONS'), 1579 'try': ('try', 'EXCEPTIONS'), 1580 'while': ('while', 'break continue if TRUTHVALUE'), 1581 'with': ('with', 'CONTEXTMANAGERS EXCEPTIONS yield'), 1582 'yield': ('yield', ''), 1583 } 1584 # Either add symbols to this dictionary or to the symbols dictionary 1585 # directly: Whichever is easier. They are merged later. 1586 _symbols_inverse = { 1587 'STRINGS' : ("'", "'''", "r'", "u'", '"""', '"', 'r"', 'u"'), 1588 'OPERATORS' : ('+', '-', '*', '**', '/', '//', '%', '<<', '>>', '&', 1589 '|', '^', '~', '<', '>', '<=', '>=', '==', '!=', '<>'), 1590 'COMPARISON' : ('<', '>', '<=', '>=', '==', '!=', '<>'), 1591 'UNARY' : ('-', '~'), 1592 'AUGMENTEDASSIGNMENT' : ('+=', '-=', '*=', '/=', '%=', '&=', '|=', 1593 '^=', '<<=', '>>=', '**=', '//='), 1594 'BITWISE' : ('<<', '>>', '&', '|', '^', '~'), 1595 'COMPLEX' : ('j', 'J') 1596 } 1597 symbols = { 1598 '%': 'OPERATORS FORMATTING', 1599 '**': 'POWER', 1600 ',': 'TUPLES LISTS FUNCTIONS', 1601 '.': 'ATTRIBUTES FLOAT MODULES OBJECTS', 1602 '...': 'ELLIPSIS', 1603 ':': 'SLICINGS DICTIONARYLITERALS', 1604 '@': 'def class', 1605 '\\': 'STRINGS', 1606 '_': 'PRIVATENAMES', 1607 '__': 'PRIVATENAMES SPECIALMETHODS', 1608 '`': 'BACKQUOTES', 1609 '(': 'TUPLES FUNCTIONS CALLS', 1610 ')': 'TUPLES FUNCTIONS CALLS', 1611 '[': 'LISTS SUBSCRIPTS SLICINGS', 1612 ']': 'LISTS SUBSCRIPTS SLICINGS' 1613 } 1614 for topic, symbols_ in _symbols_inverse.iteritems(): 1615 for symbol in symbols_: 1616 topics = symbols.get(symbol, topic) 1617 if topic not in topics: 1618 topics = topics + ' ' + topic 1619 symbols[symbol] = topics 1620 1621 topics = { 1622 'TYPES': ('types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS ' 1623 'FUNCTIONS CLASSES MODULES FILES inspect'), 1624 'STRINGS': ('strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING ' 1625 'TYPES'), 1626 'STRINGMETHODS': ('string-methods', 'STRINGS FORMATTING'), 1627 'FORMATTING': ('formatstrings', 'OPERATORS'), 1628 'UNICODE': ('strings', 'encodings unicode SEQUENCES STRINGMETHODS ' 1629 'FORMATTING TYPES'), 1630 'NUMBERS': ('numbers', 'INTEGER FLOAT COMPLEX TYPES'), 1631 'INTEGER': ('integers', 'int range'), 1632 'FLOAT': ('floating', 'float math'), 1633 'COMPLEX': ('imaginary', 'complex cmath'), 1634 'SEQUENCES': ('typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'), 1635 'MAPPINGS': 'DICTIONARIES', 1636 'FUNCTIONS': ('typesfunctions', 'def TYPES'), 1637 'METHODS': ('typesmethods', 'class def CLASSES TYPES'), 1638 'CODEOBJECTS': ('bltin-code-objects', 'compile FUNCTIONS TYPES'), 1639 'TYPEOBJECTS': ('bltin-type-objects', 'types TYPES'), 1640 'FRAMEOBJECTS': 'TYPES', 1641 'TRACEBACKS': 'TYPES', 1642 'NONE': ('bltin-null-object', ''), 1643 'ELLIPSIS': ('bltin-ellipsis-object', 'SLICINGS'), 1644 'FILES': ('bltin-file-objects', ''), 1645 'SPECIALATTRIBUTES': ('specialattrs', ''), 1646 'CLASSES': ('types', 'class SPECIALMETHODS PRIVATENAMES'), 1647 'MODULES': ('typesmodules', 'import'), 1648 'PACKAGES': 'import', 1649 'EXPRESSIONS': ('operator-summary', 'lambda or and not in is BOOLEAN ' 1650 'COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER ' 1651 'UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES ' 1652 'LISTS DICTIONARIES BACKQUOTES'), 1653 'OPERATORS': 'EXPRESSIONS', 1654 'PRECEDENCE': 'EXPRESSIONS', 1655 'OBJECTS': ('objects', 'TYPES'), 1656 'SPECIALMETHODS': ('specialnames', 'BASICMETHODS ATTRIBUTEMETHODS ' 1657 'CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS ' 1658 'SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'), 1659 'BASICMETHODS': ('customization', 'cmp hash repr str SPECIALMETHODS'), 1660 'ATTRIBUTEMETHODS': ('attribute-access', 'ATTRIBUTES SPECIALMETHODS'), 1661 'CALLABLEMETHODS': ('callable-types', 'CALLS SPECIALMETHODS'), 1662 'SEQUENCEMETHODS1': ('sequence-types', 'SEQUENCES SEQUENCEMETHODS2 ' 1663 'SPECIALMETHODS'), 1664 'SEQUENCEMETHODS2': ('sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 ' 1665 'SPECIALMETHODS'), 1666 'MAPPINGMETHODS': ('sequence-types', 'MAPPINGS SPECIALMETHODS'), 1667 'NUMBERMETHODS': ('numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT ' 1668 'SPECIALMETHODS'), 1669 'EXECUTION': ('execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'), 1670 'NAMESPACES': ('naming', 'global ASSIGNMENT DELETION DYNAMICFEATURES'), 1671 'DYNAMICFEATURES': ('dynamic-features', ''), 1672 'SCOPING': 'NAMESPACES', 1673 'FRAMES': 'NAMESPACES', 1674 'EXCEPTIONS': ('exceptions', 'try except finally raise'), 1675 'COERCIONS': ('coercion-rules','CONVERSIONS'), 1676 'CONVERSIONS': ('conversions', 'COERCIONS'), 1677 'IDENTIFIERS': ('identifiers', 'keywords SPECIALIDENTIFIERS'), 1678 'SPECIALIDENTIFIERS': ('id-classes', ''), 1679 'PRIVATENAMES': ('atom-identifiers', ''), 1680 'LITERALS': ('atom-literals', 'STRINGS BACKQUOTES NUMBERS ' 1681 'TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'), 1682 'TUPLES': 'SEQUENCES', 1683 'TUPLELITERALS': ('exprlists', 'TUPLES LITERALS'), 1684 'LISTS': ('typesseq-mutable', 'LISTLITERALS'), 1685 'LISTLITERALS': ('lists', 'LISTS LITERALS'), 1686 'DICTIONARIES': ('typesmapping', 'DICTIONARYLITERALS'), 1687 'DICTIONARYLITERALS': ('dict', 'DICTIONARIES LITERALS'), 1688 'BACKQUOTES': ('string-conversions', 'repr str STRINGS LITERALS'), 1689 'ATTRIBUTES': ('attribute-references', 'getattr hasattr setattr ' 1690 'ATTRIBUTEMETHODS'), 1691 'SUBSCRIPTS': ('subscriptions', 'SEQUENCEMETHODS1'), 1692 'SLICINGS': ('slicings', 'SEQUENCEMETHODS2'), 1693 'CALLS': ('calls', 'EXPRESSIONS'), 1694 'POWER': ('power', 'EXPRESSIONS'), 1695 'UNARY': ('unary', 'EXPRESSIONS'), 1696 'BINARY': ('binary', 'EXPRESSIONS'), 1697 'SHIFTING': ('shifting', 'EXPRESSIONS'), 1698 'BITWISE': ('bitwise', 'EXPRESSIONS'), 1699 'COMPARISON': ('comparisons', 'EXPRESSIONS BASICMETHODS'), 1700 'BOOLEAN': ('booleans', 'EXPRESSIONS TRUTHVALUE'), 1701 'ASSERTION': 'assert', 1702 'ASSIGNMENT': ('assignment', 'AUGMENTEDASSIGNMENT'), 1703 'AUGMENTEDASSIGNMENT': ('augassign', 'NUMBERMETHODS'), 1704 'DELETION': 'del', 1705 'PRINTING': 'print', 1706 'RETURNING': 'return', 1707 'IMPORTING': 'import', 1708 'CONDITIONAL': 'if', 1709 'LOOPING': ('compound', 'for while break continue'), 1710 'TRUTHVALUE': ('truth', 'if while and or not BASICMETHODS'), 1711 'DEBUGGING': ('debugger', 'pdb'), 1712 'CONTEXTMANAGERS': ('context-managers', 'with'), 1713 } 1714 1715 def __init__(self, input=None, output=None): 1716 self._input = input 1717 self._output = output 1718 1719 input = property(lambda self: self._input or sys.stdin) 1720 output = property(lambda self: self._output or sys.stdout) 1721 1722 def __repr__(self): 1723 if inspect.stack()[1][3] == '?': 1724 self() 1725 return '' 1726 return '<pydoc.Helper instance>' 1727 1728 _GoInteractive = object() 1729 def __call__(self, request=_GoInteractive): 1730 if request is not self._GoInteractive: 1731 self.help(request) 1732 else: 1733 self.intro() 1734 self.interact() 1735 self.output.write(''' 1736 You are now leaving help and returning to the Python interpreter. 1737 If you want to ask for help on a particular object directly from the 1738 interpreter, you can type "help(object)". Executing "help('string')" 1739 has the same effect as typing a particular string at the help> prompt. 1740 ''') 1741 1742 def interact(self): 1743 self.output.write('\n') 1744 while True: 1745 try: 1746 request = self.getline('help> ') 1747 if not request: break 1748 except (KeyboardInterrupt, EOFError): 1749 break 1750 request = strip(replace(request, '"', '', "'", '')) 1751 if lower(request) in ('q', 'quit'): break 1752 self.help(request) 1753 1754 def getline(self, prompt): 1755 """Read one line, using raw_input when available.""" 1756 if self.input is sys.stdin: 1757 return raw_input(prompt) 1758 else: 1759 self.output.write(prompt) 1760 self.output.flush() 1761 return self.input.readline() 1762 1763 def help(self, request): 1764 if type(request) is type(''): 1765 request = request.strip() 1766 if request == 'help': self.intro() 1767 elif request == 'keywords': self.listkeywords() 1768 elif request == 'symbols': self.listsymbols() 1769 elif request == 'topics': self.listtopics() 1770 elif request == 'modules': self.listmodules() 1771 elif request[:8] == 'modules ': 1772 self.listmodules(split(request)[1]) 1773 elif request in self.symbols: self.showsymbol(request) 1774 elif request in self.keywords: self.showtopic(request) 1775 elif request in self.topics: self.showtopic(request) 1776 elif request: doc(request, 'Help on %s:') 1777 elif isinstance(request, Helper): self() 1778 else: doc(request, 'Help on %s:') 1779 self.output.write('\n') 1780 1781 def intro(self): 1782 self.output.write(''' 1783 Welcome to Python %s! This is the online help utility. 1784 1785 If this is your first time using Python, you should definitely check out 1786 the tutorial on the Internet at http://docs.python.org/tutorial/. 1787 1788 Enter the name of any module, keyword, or topic to get help on writing 1789 Python programs and using Python modules. To quit this help utility and 1790 return to the interpreter, just type "quit". 1791 1792 To get a list of available modules, keywords, or topics, type "modules", 1793 "keywords", or "topics". Each module also comes with a one-line summary 1794 of what it does; to list the modules whose summaries contain a given word 1795 such as "spam", type "modules spam". 1796 ''' % sys.version[:3]) 1797 1798 def list(self, items, columns=4, width=80): 1799 items = items[:] 1800 items.sort() 1801 colw = width / columns 1802 rows = (len(items) + columns - 1) / columns 1803 for row in range(rows): 1804 for col in range(columns): 1805 i = col * rows + row 1806 if i < len(items): 1807 self.output.write(items[i]) 1808 if col < columns - 1: 1809 self.output.write(' ' + ' ' * (colw-1 - len(items[i]))) 1810 self.output.write('\n') 1811 1812 def listkeywords(self): 1813 self.output.write(''' 1814 Here is a list of the Python keywords. Enter any keyword to get more help. 1815 1816 ''') 1817 self.list(self.keywords.keys()) 1818 1819 def listsymbols(self): 1820 self.output.write(''' 1821 Here is a list of the punctuation symbols which Python assigns special meaning 1822 to. Enter any symbol to get more help. 1823 1824 ''') 1825 self.list(self.symbols.keys()) 1826 1827 def listtopics(self): 1828 self.output.write(''' 1829 Here is a list of available topics. Enter any topic name to get more help. 1830 1831 ''') 1832 self.list(self.topics.keys()) 1833 1834 def showtopic(self, topic, more_xrefs=''): 1835 try: 1836 import pydoc_data.topics 1837 except ImportError: 1838 self.output.write(''' 1839 Sorry, topic and keyword documentation is not available because the 1840 module "pydoc_data.topics" could not be found. 1841 ''') 1842 return 1843 target = self.topics.get(topic, self.keywords.get(topic)) 1844 if not target: 1845 self.output.write('no documentation found for %s\n' % repr(topic)) 1846 return 1847 if type(target) is type(''): 1848 return self.showtopic(target, more_xrefs) 1849 1850 label, xrefs = target 1851 try: 1852 doc = pydoc_data.topics.topics[label] 1853 except KeyError: 1854 self.output.write('no documentation found for %s\n' % repr(topic)) 1855 return 1856 pager(strip(doc) + '\n') 1857 if more_xrefs: 1858 xrefs = (xrefs or '') + ' ' + more_xrefs 1859 if xrefs: 1860 import StringIO, formatter 1861 buffer = StringIO.StringIO() 1862 formatter.DumbWriter(buffer).send_flowing_data( 1863 'Related help topics: ' + join(split(xrefs), ', ') + '\n') 1864 self.output.write('\n%s\n' % buffer.getvalue()) 1865 1866 def showsymbol(self, symbol): 1867 target = self.symbols[symbol] 1868 topic, _, xrefs = target.partition(' ') 1869 self.showtopic(topic, xrefs) 1870 1871 def listmodules(self, key=''): 1872 if key: 1873 self.output.write(''' 1874 Here is a list of matching modules. Enter any module name to get more help. 1875 1876 ''') 1877 apropos(key) 1878 else: 1879 self.output.write(''' 1880 Please wait a moment while I gather a list of all available modules... 1881 1882 ''') 1883 modules = {} 1884 def callback(path, modname, desc, modules=modules): 1885 if modname and modname[-9:] == '.__init__': 1886 modname = modname[:-9] + ' (package)' 1887 if find(modname, '.') < 0: 1888 modules[modname] = 1 1889 def onerror(modname): 1890 callback(None, modname, None) 1891 ModuleScanner().run(callback, onerror=onerror) 1892 self.list(modules.keys()) 1893 self.output.write(''' 1894 Enter any module name to get more help. Or, type "modules spam" to search 1895 for modules whose descriptions contain the word "spam". 1896 ''') 1897 1898 help = Helper() 1899 1900 class Scanner: 1901 """A generic tree iterator.""" 1902 def __init__(self, roots, children, descendp): 1903 self.roots = roots[:] 1904 self.state = [] 1905 self.children = children 1906 self.descendp = descendp 1907 1908 def next(self): 1909 if not self.state: 1910 if not self.roots: 1911 return None 1912 root = self.roots.pop(0) 1913 self.state = [(root, self.children(root))] 1914 node, children = self.state[-1] 1915 if not children: 1916 self.state.pop() 1917 return self.next() 1918 child = children.pop(0) 1919 if self.descendp(child): 1920 self.state.append((child, self.children(child))) 1921 return child 1922 1923 1924 class ModuleScanner: 1925 """An interruptible scanner that searches module synopses.""" 1926 1927 def run(self, callback, key=None, completer=None, onerror=None): 1928 if key: key = lower(key) 1929 self.quit = False 1930 seen = {} 1931 1932 for modname in sys.builtin_module_names: 1933 if modname != '__main__': 1934 seen[modname] = 1 1935 if key is None: 1936 callback(None, modname, '') 1937 else: 1938 desc = split(__import__(modname).__doc__ or '', '\n')[0] 1939 if find(lower(modname + ' - ' + desc), key) >= 0: 1940 callback(None, modname, desc) 1941 1942 for importer, modname, ispkg in pkgutil.walk_packages(onerror=onerror): 1943 if self.quit: 1944 break 1945 if key is None: 1946 callback(None, modname, '') 1947 else: 1948 loader = importer.find_module(modname) 1949 if hasattr(loader,'get_source'): 1950 import StringIO 1951 desc = source_synopsis( 1952 StringIO.StringIO(loader.get_source(modname)) 1953 ) or '' 1954 if hasattr(loader,'get_filename'): 1955 path = loader.get_filename(modname) 1956 else: 1957 path = None 1958 else: 1959 module = loader.load_module(modname) 1960 desc = (module.__doc__ or '').splitlines()[0] 1961 path = getattr(module,'__file__',None) 1962 if find(lower(modname + ' - ' + desc), key) >= 0: 1963 callback(path, modname, desc) 1964 1965 if completer: 1966 completer() 1967 1968 def apropos(key): 1969 """Print all the one-line module summaries that contain a substring.""" 1970 def callback(path, modname, desc): 1971 if modname[-9:] == '.__init__': 1972 modname = modname[:-9] + ' (package)' 1973 print modname, desc and '- ' + desc 1974 try: import warnings 1975 except ImportError: pass 1976 else: warnings.filterwarnings('ignore') # ignore problems during import 1977 ModuleScanner().run(callback, key) 1978 1979 # --------------------------------------------------- web browser interface 1980 1981 def serve(port, callback=None, completer=None): 1982 import BaseHTTPServer, mimetools, select 1983 1984 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded. 1985 class Message(mimetools.Message): 1986 def __init__(self, fp, seekable=1): 1987 Message = self.__class__ 1988 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable) 1989 self.encodingheader = self.getheader('content-transfer-encoding') 1990 self.typeheader = self.getheader('content-type') 1991 self.parsetype() 1992 self.parseplist() 1993 1994 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler): 1995 def send_document(self, title, contents): 1996 try: 1997 self.send_response(200) 1998 self.send_header('Content-Type', 'text/html') 1999 self.end_headers() 2000 self.wfile.write(html.page(title, contents)) 2001 except IOError: pass 2002 2003 def do_GET(self): 2004 path = self.path 2005 if path[-5:] == '.html': path = path[:-5] 2006 if path[:1] == '/': path = path[1:] 2007 if path and path != '.': 2008 try: 2009 obj = locate(path, forceload=1) 2010 except ErrorDuringImport, value: 2011 self.send_document(path, html.escape(str(value))) 2012 return 2013 if obj: 2014 self.send_document(describe(obj), html.document(obj, path)) 2015 else: 2016 self.send_document(path, 2017 'no Python documentation found for %s' % repr(path)) 2018 else: 2019 heading = html.heading( 2020 '<big><big><strong>Python: Index of Modules</strong></big></big>', 2021 '#ffffff', '#7799ee') 2022 def bltinlink(name): 2023 return '<a href="%s.html">%s</a>' % (name, name) 2024 names = filter(lambda x: x != '__main__', 2025 sys.builtin_module_names) 2026 contents = html.multicolumn(names, bltinlink) 2027 indices = ['<p>' + html.bigsection( 2028 'Built-in Modules', '#ffffff', '#ee77aa', contents)] 2029 2030 seen = {} 2031 for dir in sys.path: 2032 indices.append(html.index(dir, seen)) 2033 contents = heading + join(indices) + '''<p align=right> 2034 <font color="#909090" face="helvetica, arial"><strong> 2035 pydoc</strong> by Ka-Ping Yee <ping (at] lfw.org></font>''' 2036 self.send_document('Index of Modules', contents) 2037 2038 def log_message(self, *args): pass 2039 2040 class DocServer(BaseHTTPServer.HTTPServer): 2041 def __init__(self, port, callback): 2042 host = 'localhost' 2043 self.address = (host, port) 2044 self.url = 'http://%s:%d/' % (host, port) 2045 self.callback = callback 2046 self.base.__init__(self, self.address, self.handler) 2047 2048 def serve_until_quit(self): 2049 import select 2050 self.quit = False 2051 while not self.quit: 2052 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1) 2053 if rd: self.handle_request() 2054 2055 def server_activate(self): 2056 self.base.server_activate(self) 2057 if self.callback: self.callback(self) 2058 2059 DocServer.base = BaseHTTPServer.HTTPServer 2060 DocServer.handler = DocHandler 2061 DocHandler.MessageClass = Message 2062 try: 2063 try: 2064 DocServer(port, callback).serve_until_quit() 2065 except (KeyboardInterrupt, select.error): 2066 pass 2067 finally: 2068 if completer: completer() 2069 2070 # ----------------------------------------------------- graphical interface 2071 2072 def gui(): 2073 """Graphical interface (starts web server and pops up a control window).""" 2074 class GUI: 2075 def __init__(self, window, port=7464): 2076 self.window = window 2077 self.server = None 2078 self.scanner = None 2079 2080 import Tkinter 2081 self.server_frm = Tkinter.Frame(window) 2082 self.title_lbl = Tkinter.Label(self.server_frm, 2083 text='Starting server...\n ') 2084 self.open_btn = Tkinter.Button(self.server_frm, 2085 text='open browser', command=self.open, state='disabled') 2086 self.quit_btn = Tkinter.Button(self.server_frm, 2087 text='quit serving', command=self.quit, state='disabled') 2088 2089 self.search_frm = Tkinter.Frame(window) 2090 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for') 2091 self.search_ent = Tkinter.Entry(self.search_frm) 2092 self.search_ent.bind('<Return>', self.search) 2093 self.stop_btn = Tkinter.Button(self.search_frm, 2094 text='stop', pady=0, command=self.stop, state='disabled') 2095 if sys.platform == 'win32': 2096 # Trying to hide and show this button crashes under Windows. 2097 self.stop_btn.pack(side='right') 2098 2099 self.window.title('pydoc') 2100 self.window.protocol('WM_DELETE_WINDOW', self.quit) 2101 self.title_lbl.pack(side='top', fill='x') 2102 self.open_btn.pack(side='left', fill='x', expand=1) 2103 self.quit_btn.pack(side='right', fill='x', expand=1) 2104 self.server_frm.pack(side='top', fill='x') 2105 2106 self.search_lbl.pack(side='left') 2107 self.search_ent.pack(side='right', fill='x', expand=1) 2108 self.search_frm.pack(side='top', fill='x') 2109 self.search_ent.focus_set() 2110 2111 font = ('helvetica', sys.platform == 'win32' and 8 or 10) 2112 self.result_lst = Tkinter.Listbox(window, font=font, height=6) 2113 self.result_lst.bind('<Button-1>', self.select) 2114 self.result_lst.bind('<Double-Button-1>', self.goto) 2115 self.result_scr = Tkinter.Scrollbar(window, 2116 orient='vertical', command=self.result_lst.yview) 2117 self.result_lst.config(yscrollcommand=self.result_scr.set) 2118 2119 self.result_frm = Tkinter.Frame(window) 2120 self.goto_btn = Tkinter.Button(self.result_frm, 2121 text='go to selected', command=self.goto) 2122 self.hide_btn = Tkinter.Button(self.result_frm, 2123 text='hide results', command=self.hide) 2124 self.goto_btn.pack(side='left', fill='x', expand=1) 2125 self.hide_btn.pack(side='right', fill='x', expand=1) 2126 2127 self.window.update() 2128 self.minwidth = self.window.winfo_width() 2129 self.minheight = self.window.winfo_height() 2130 self.bigminheight = (self.server_frm.winfo_reqheight() + 2131 self.search_frm.winfo_reqheight() + 2132 self.result_lst.winfo_reqheight() + 2133 self.result_frm.winfo_reqheight()) 2134 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight 2135 self.expanded = 0 2136 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight)) 2137 self.window.wm_minsize(self.minwidth, self.minheight) 2138 self.window.tk.willdispatch() 2139 2140 import threading 2141 threading.Thread( 2142 target=serve, args=(port, self.ready, self.quit)).start() 2143 2144 def ready(self, server): 2145 self.server = server 2146 self.title_lbl.config( 2147 text='Python documentation server at\n' + server.url) 2148 self.open_btn.config(state='normal') 2149 self.quit_btn.config(state='normal') 2150 2151 def open(self, event=None, url=None): 2152 url = url or self.server.url 2153 try: 2154 import webbrowser 2155 webbrowser.open(url) 2156 except ImportError: # pre-webbrowser.py compatibility 2157 if sys.platform == 'win32': 2158 os.system('start "%s"' % url) 2159 else: 2160 rc = os.system('netscape -remote "openURL(%s)" &' % url) 2161 if rc: os.system('netscape "%s" &' % url) 2162 2163 def quit(self, event=None): 2164 if self.server: 2165 self.server.quit = 1 2166 self.window.quit() 2167 2168 def search(self, event=None): 2169 key = self.search_ent.get() 2170 self.stop_btn.pack(side='right') 2171 self.stop_btn.config(state='normal') 2172 self.search_lbl.config(text='Searching for "%s"...' % key) 2173 self.search_ent.forget() 2174 self.search_lbl.pack(side='left') 2175 self.result_lst.delete(0, 'end') 2176 self.goto_btn.config(state='disabled') 2177 self.expand() 2178 2179 import threading 2180 if self.scanner: 2181 self.scanner.quit = 1 2182 self.scanner = ModuleScanner() 2183 threading.Thread(target=self.scanner.run, 2184 args=(self.update, key, self.done)).start() 2185 2186 def update(self, path, modname, desc): 2187 if modname[-9:] == '.__init__': 2188 modname = modname[:-9] + ' (package)' 2189 self.result_lst.insert('end', 2190 modname + ' - ' + (desc or '(no description)')) 2191 2192 def stop(self, event=None): 2193 if self.scanner: 2194 self.scanner.quit = 1 2195 self.scanner = None 2196 2197 def done(self): 2198 self.scanner = None 2199 self.search_lbl.config(text='Search for') 2200 self.search_lbl.pack(side='left') 2201 self.search_ent.pack(side='right', fill='x', expand=1) 2202 if sys.platform != 'win32': self.stop_btn.forget() 2203 self.stop_btn.config(state='disabled') 2204 2205 def select(self, event=None): 2206 self.goto_btn.config(state='normal') 2207 2208 def goto(self, event=None): 2209 selection = self.result_lst.curselection() 2210 if selection: 2211 modname = split(self.result_lst.get(selection[0]))[0] 2212 self.open(url=self.server.url + modname + '.html') 2213 2214 def collapse(self): 2215 if not self.expanded: return 2216 self.result_frm.forget() 2217 self.result_scr.forget() 2218 self.result_lst.forget() 2219 self.bigwidth = self.window.winfo_width() 2220 self.bigheight = self.window.winfo_height() 2221 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight)) 2222 self.window.wm_minsize(self.minwidth, self.minheight) 2223 self.expanded = 0 2224 2225 def expand(self): 2226 if self.expanded: return 2227 self.result_frm.pack(side='bottom', fill='x') 2228 self.result_scr.pack(side='right', fill='y') 2229 self.result_lst.pack(side='top', fill='both', expand=1) 2230 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight)) 2231 self.window.wm_minsize(self.minwidth, self.bigminheight) 2232 self.expanded = 1 2233 2234 def hide(self, event=None): 2235 self.stop() 2236 self.collapse() 2237 2238 import Tkinter 2239 try: 2240 root = Tkinter.Tk() 2241 # Tk will crash if pythonw.exe has an XP .manifest 2242 # file and the root has is not destroyed explicitly. 2243 # If the problem is ever fixed in Tk, the explicit 2244 # destroy can go. 2245 try: 2246 gui = GUI(root) 2247 root.mainloop() 2248 finally: 2249 root.destroy() 2250 except KeyboardInterrupt: 2251 pass 2252 2253 # -------------------------------------------------- command-line interface 2254 2255 def ispath(x): 2256 return isinstance(x, str) and find(x, os.sep) >= 0 2257 2258 def cli(): 2259 """Command-line interface (looks at sys.argv to decide what to do).""" 2260 import getopt 2261 class BadUsage: pass 2262 2263 # Scripts don't get the current directory in their path by default 2264 # unless they are run with the '-m' switch 2265 if '' not in sys.path: 2266 scriptdir = os.path.dirname(sys.argv[0]) 2267 if scriptdir in sys.path: 2268 sys.path.remove(scriptdir) 2269 sys.path.insert(0, '.') 2270 2271 try: 2272 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w') 2273 writing = 0 2274 2275 for opt, val in opts: 2276 if opt == '-g': 2277 gui() 2278 return 2279 if opt == '-k': 2280 apropos(val) 2281 return 2282 if opt == '-p': 2283 try: 2284 port = int(val) 2285 except ValueError: 2286 raise BadUsage 2287 def ready(server): 2288 print 'pydoc server ready at %s' % server.url 2289 def stopped(): 2290 print 'pydoc server stopped' 2291 serve(port, ready, stopped) 2292 return 2293 if opt == '-w': 2294 writing = 1 2295 2296 if not args: raise BadUsage 2297 for arg in args: 2298 if ispath(arg) and not os.path.exists(arg): 2299 print 'file %r does not exist' % arg 2300 break 2301 try: 2302 if ispath(arg) and os.path.isfile(arg): 2303 arg = importfile(arg) 2304 if writing: 2305 if ispath(arg) and os.path.isdir(arg): 2306 writedocs(arg) 2307 else: 2308 writedoc(arg) 2309 else: 2310 help.help(arg) 2311 except ErrorDuringImport, value: 2312 print value 2313 2314 except (getopt.error, BadUsage): 2315 cmd = os.path.basename(sys.argv[0]) 2316 print """pydoc - the Python documentation tool 2317 2318 %s <name> ... 2319 Show text documentation on something. <name> may be the name of a 2320 Python keyword, topic, function, module, or package, or a dotted 2321 reference to a class or function within a module or module in a 2322 package. If <name> contains a '%s', it is used as the path to a 2323 Python source file to document. If name is 'keywords', 'topics', 2324 or 'modules', a listing of these things is displayed. 2325 2326 %s -k <keyword> 2327 Search for a keyword in the synopsis lines of all available modules. 2328 2329 %s -p <port> 2330 Start an HTTP server on the given port on the local machine. 2331 2332 %s -g 2333 Pop up a graphical interface for finding and serving documentation. 2334 2335 %s -w <name> ... 2336 Write out the HTML documentation for a module to a file in the current 2337 directory. If <name> contains a '%s', it is treated as a filename; if 2338 it names a directory, documentation is written for all the contents. 2339 """ % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep) 2340 2341 if __name__ == '__main__': cli() 2342