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: 88564 $" 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, warnings 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, (None, None)) 216 if lastupdate is None or 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 try: 744 value = getattr(object, name) 745 except Exception: 746 # Some descriptors may meet a failure in their __get__. 747 # (bug #1785) 748 push(self._docdescriptor(name, value, mod)) 749 else: 750 push(self.document(value, name, mod, 751 funcs, classes, mdict, object)) 752 push('\n') 753 return attrs 754 755 def spilldescriptors(msg, attrs, predicate): 756 ok, attrs = _split_list(attrs, predicate) 757 if ok: 758 hr.maybe() 759 push(msg) 760 for name, kind, homecls, value in ok: 761 push(self._docdescriptor(name, value, mod)) 762 return attrs 763 764 def spilldata(msg, attrs, predicate): 765 ok, attrs = _split_list(attrs, predicate) 766 if ok: 767 hr.maybe() 768 push(msg) 769 for name, kind, homecls, value in ok: 770 base = self.docother(getattr(object, name), name, mod) 771 if (hasattr(value, '__call__') or 772 inspect.isdatadescriptor(value)): 773 doc = getattr(value, "__doc__", None) 774 else: 775 doc = None 776 if doc is None: 777 push('<dl><dt>%s</dl>\n' % base) 778 else: 779 doc = self.markup(getdoc(value), self.preformat, 780 funcs, classes, mdict) 781 doc = '<dd><tt>%s</tt>' % doc 782 push('<dl><dt>%s%s</dl>\n' % (base, doc)) 783 push('\n') 784 return attrs 785 786 attrs = filter(lambda data: visiblename(data[0], obj=object), 787 classify_class_attrs(object)) 788 mdict = {} 789 for key, kind, homecls, value in attrs: 790 mdict[key] = anchor = '#' + name + '-' + key 791 try: 792 value = getattr(object, name) 793 except Exception: 794 # Some descriptors may meet a failure in their __get__. 795 # (bug #1785) 796 pass 797 try: 798 # The value may not be hashable (e.g., a data attr with 799 # a dict or list value). 800 mdict[value] = anchor 801 except TypeError: 802 pass 803 804 while attrs: 805 if mro: 806 thisclass = mro.popleft() 807 else: 808 thisclass = attrs[0][2] 809 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass) 810 811 if thisclass is __builtin__.object: 812 attrs = inherited 813 continue 814 elif thisclass is object: 815 tag = 'defined here' 816 else: 817 tag = 'inherited from %s' % self.classlink(thisclass, 818 object.__module__) 819 tag += ':<br>\n' 820 821 # Sort attrs by name. 822 try: 823 attrs.sort(key=lambda t: t[0]) 824 except TypeError: 825 attrs.sort(lambda t1, t2: cmp(t1[0], t2[0])) # 2.3 compat 826 827 # Pump out the attrs, segregated by kind. 828 attrs = spill('Methods %s' % tag, attrs, 829 lambda t: t[1] == 'method') 830 attrs = spill('Class methods %s' % tag, attrs, 831 lambda t: t[1] == 'class method') 832 attrs = spill('Static methods %s' % tag, attrs, 833 lambda t: t[1] == 'static method') 834 attrs = spilldescriptors('Data descriptors %s' % tag, attrs, 835 lambda t: t[1] == 'data descriptor') 836 attrs = spilldata('Data and other attributes %s' % tag, attrs, 837 lambda t: t[1] == 'data') 838 assert attrs == [] 839 attrs = inherited 840 841 contents = ''.join(contents) 842 843 if name == realname: 844 title = '<a name="%s">class <strong>%s</strong></a>' % ( 845 name, realname) 846 else: 847 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % ( 848 name, name, realname) 849 if bases: 850 parents = [] 851 for base in bases: 852 parents.append(self.classlink(base, object.__module__)) 853 title = title + '(%s)' % join(parents, ', ') 854 doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict) 855 doc = doc and '<tt>%s<br> </tt>' % doc 856 857 return self.section(title, '#000000', '#ffc8d8', contents, 3, doc) 858 859 def formatvalue(self, object): 860 """Format an argument default value as text.""" 861 return self.grey('=' + self.repr(object)) 862 863 def docroutine(self, object, name=None, mod=None, 864 funcs={}, classes={}, methods={}, cl=None): 865 """Produce HTML documentation for a function or method object.""" 866 realname = object.__name__ 867 name = name or realname 868 anchor = (cl and cl.__name__ or '') + '-' + name 869 note = '' 870 skipdocs = 0 871 if inspect.ismethod(object): 872 imclass = object.im_class 873 if cl: 874 if imclass is not cl: 875 note = ' from ' + self.classlink(imclass, mod) 876 else: 877 if object.im_self is not None: 878 note = ' method of %s instance' % self.classlink( 879 object.im_self.__class__, mod) 880 else: 881 note = ' unbound %s method' % self.classlink(imclass,mod) 882 object = object.im_func 883 884 if name == realname: 885 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname) 886 else: 887 if (cl and realname in cl.__dict__ and 888 cl.__dict__[realname] is object): 889 reallink = '<a href="#%s">%s</a>' % ( 890 cl.__name__ + '-' + realname, realname) 891 skipdocs = 1 892 else: 893 reallink = realname 894 title = '<a name="%s"><strong>%s</strong></a> = %s' % ( 895 anchor, name, reallink) 896 if inspect.isfunction(object): 897 args, varargs, varkw, defaults = inspect.getargspec(object) 898 argspec = inspect.formatargspec( 899 args, varargs, varkw, defaults, formatvalue=self.formatvalue) 900 if realname == '<lambda>': 901 title = '<strong>%s</strong> <em>lambda</em> ' % name 902 argspec = argspec[1:-1] # remove parentheses 903 else: 904 argspec = '(...)' 905 906 decl = title + argspec + (note and self.grey( 907 '<font face="helvetica, arial">%s</font>' % note)) 908 909 if skipdocs: 910 return '<dl><dt>%s</dt></dl>\n' % decl 911 else: 912 doc = self.markup( 913 getdoc(object), self.preformat, funcs, classes, methods) 914 doc = doc and '<dd><tt>%s</tt></dd>' % doc 915 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc) 916 917 def _docdescriptor(self, name, value, mod): 918 results = [] 919 push = results.append 920 921 if name: 922 push('<dl><dt><strong>%s</strong></dt>\n' % name) 923 if value.__doc__ is not None: 924 doc = self.markup(getdoc(value), self.preformat) 925 push('<dd><tt>%s</tt></dd>\n' % doc) 926 push('</dl>\n') 927 928 return ''.join(results) 929 930 def docproperty(self, object, name=None, mod=None, cl=None): 931 """Produce html documentation for a property.""" 932 return self._docdescriptor(name, object, mod) 933 934 def docother(self, object, name=None, mod=None, *ignored): 935 """Produce HTML documentation for a data object.""" 936 lhs = name and '<strong>%s</strong> = ' % name or '' 937 return lhs + self.repr(object) 938 939 def docdata(self, object, name=None, mod=None, cl=None): 940 """Produce html documentation for a data descriptor.""" 941 return self._docdescriptor(name, object, mod) 942 943 def index(self, dir, shadowed=None): 944 """Generate an HTML index for a directory of modules.""" 945 modpkgs = [] 946 if shadowed is None: shadowed = {} 947 for importer, name, ispkg in pkgutil.iter_modules([dir]): 948 modpkgs.append((name, '', ispkg, name in shadowed)) 949 shadowed[name] = 1 950 951 modpkgs.sort() 952 contents = self.multicolumn(modpkgs, self.modpkglink) 953 return self.bigsection(dir, '#ffffff', '#ee77aa', contents) 954 955 # -------------------------------------------- text documentation generator 956 957 class TextRepr(Repr): 958 """Class for safely making a text representation of a Python object.""" 959 def __init__(self): 960 Repr.__init__(self) 961 self.maxlist = self.maxtuple = 20 962 self.maxdict = 10 963 self.maxstring = self.maxother = 100 964 965 def repr1(self, x, level): 966 if hasattr(type(x), '__name__'): 967 methodname = 'repr_' + join(split(type(x).__name__), '_') 968 if hasattr(self, methodname): 969 return getattr(self, methodname)(x, level) 970 return cram(stripid(repr(x)), self.maxother) 971 972 def repr_string(self, x, level): 973 test = cram(x, self.maxstring) 974 testrepr = repr(test) 975 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''): 976 # Backslashes are only literal in the string and are never 977 # needed to make any special characters, so show a raw string. 978 return 'r' + testrepr[0] + test + testrepr[0] 979 return testrepr 980 981 repr_str = repr_string 982 983 def repr_instance(self, x, level): 984 try: 985 return cram(stripid(repr(x)), self.maxstring) 986 except: 987 return '<%s instance>' % x.__class__.__name__ 988 989 class TextDoc(Doc): 990 """Formatter class for text documentation.""" 991 992 # ------------------------------------------- text formatting utilities 993 994 _repr_instance = TextRepr() 995 repr = _repr_instance.repr 996 997 def bold(self, text): 998 """Format a string in bold by overstriking.""" 999 return join(map(lambda ch: ch + '\b' + ch, text), '') 1000 1001 def indent(self, text, prefix=' '): 1002 """Indent text by prepending a given prefix to each line.""" 1003 if not text: return '' 1004 lines = split(text, '\n') 1005 lines = map(lambda line, prefix=prefix: prefix + line, lines) 1006 if lines: lines[-1] = rstrip(lines[-1]) 1007 return join(lines, '\n') 1008 1009 def section(self, title, contents): 1010 """Format a section with a given heading.""" 1011 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n' 1012 1013 # ---------------------------------------------- type-specific routines 1014 1015 def formattree(self, tree, modname, parent=None, prefix=''): 1016 """Render in text a class tree as returned by inspect.getclasstree().""" 1017 result = '' 1018 for entry in tree: 1019 if type(entry) is type(()): 1020 c, bases = entry 1021 result = result + prefix + classname(c, modname) 1022 if bases and bases != (parent,): 1023 parents = map(lambda c, m=modname: classname(c, m), bases) 1024 result = result + '(%s)' % join(parents, ', ') 1025 result = result + '\n' 1026 elif type(entry) is type([]): 1027 result = result + self.formattree( 1028 entry, modname, c, prefix + ' ') 1029 return result 1030 1031 def docmodule(self, object, name=None, mod=None): 1032 """Produce text documentation for a given module object.""" 1033 name = object.__name__ # ignore the passed-in name 1034 synop, desc = splitdoc(getdoc(object)) 1035 result = self.section('NAME', name + (synop and ' - ' + synop)) 1036 1037 try: 1038 all = object.__all__ 1039 except AttributeError: 1040 all = None 1041 1042 try: 1043 file = inspect.getabsfile(object) 1044 except TypeError: 1045 file = '(built-in)' 1046 result = result + self.section('FILE', file) 1047 1048 docloc = self.getdocloc(object) 1049 if docloc is not None: 1050 result = result + self.section('MODULE DOCS', docloc) 1051 1052 if desc: 1053 result = result + self.section('DESCRIPTION', desc) 1054 1055 classes = [] 1056 for key, value in inspect.getmembers(object, inspect.isclass): 1057 # if __all__ exists, believe it. Otherwise use old heuristic. 1058 if (all is not None 1059 or (inspect.getmodule(value) or object) is object): 1060 if visiblename(key, all, object): 1061 classes.append((key, value)) 1062 funcs = [] 1063 for key, value in inspect.getmembers(object, inspect.isroutine): 1064 # if __all__ exists, believe it. Otherwise use old heuristic. 1065 if (all is not None or 1066 inspect.isbuiltin(value) or inspect.getmodule(value) is object): 1067 if visiblename(key, all, object): 1068 funcs.append((key, value)) 1069 data = [] 1070 for key, value in inspect.getmembers(object, isdata): 1071 if visiblename(key, all, object): 1072 data.append((key, value)) 1073 1074 modpkgs = [] 1075 modpkgs_names = set() 1076 if hasattr(object, '__path__'): 1077 for importer, modname, ispkg in pkgutil.iter_modules(object.__path__): 1078 modpkgs_names.add(modname) 1079 if ispkg: 1080 modpkgs.append(modname + ' (package)') 1081 else: 1082 modpkgs.append(modname) 1083 1084 modpkgs.sort() 1085 result = result + self.section( 1086 'PACKAGE CONTENTS', join(modpkgs, '\n')) 1087 1088 # Detect submodules as sometimes created by C extensions 1089 submodules = [] 1090 for key, value in inspect.getmembers(object, inspect.ismodule): 1091 if value.__name__.startswith(name + '.') and key not in modpkgs_names: 1092 submodules.append(key) 1093 if submodules: 1094 submodules.sort() 1095 result = result + self.section( 1096 'SUBMODULES', join(submodules, '\n')) 1097 1098 if classes: 1099 classlist = map(lambda key_value: key_value[1], classes) 1100 contents = [self.formattree( 1101 inspect.getclasstree(classlist, 1), name)] 1102 for key, value in classes: 1103 contents.append(self.document(value, key, name)) 1104 result = result + self.section('CLASSES', join(contents, '\n')) 1105 1106 if funcs: 1107 contents = [] 1108 for key, value in funcs: 1109 contents.append(self.document(value, key, name)) 1110 result = result + self.section('FUNCTIONS', join(contents, '\n')) 1111 1112 if data: 1113 contents = [] 1114 for key, value in data: 1115 contents.append(self.docother(value, key, name, maxlen=70)) 1116 result = result + self.section('DATA', join(contents, '\n')) 1117 1118 if hasattr(object, '__version__'): 1119 version = str(object.__version__) 1120 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$': 1121 version = strip(version[11:-1]) 1122 result = result + self.section('VERSION', version) 1123 if hasattr(object, '__date__'): 1124 result = result + self.section('DATE', str(object.__date__)) 1125 if hasattr(object, '__author__'): 1126 result = result + self.section('AUTHOR', str(object.__author__)) 1127 if hasattr(object, '__credits__'): 1128 result = result + self.section('CREDITS', str(object.__credits__)) 1129 return result 1130 1131 def docclass(self, object, name=None, mod=None, *ignored): 1132 """Produce text documentation for a given class object.""" 1133 realname = object.__name__ 1134 name = name or realname 1135 bases = object.__bases__ 1136 1137 def makename(c, m=object.__module__): 1138 return classname(c, m) 1139 1140 if name == realname: 1141 title = 'class ' + self.bold(realname) 1142 else: 1143 title = self.bold(name) + ' = class ' + realname 1144 if bases: 1145 parents = map(makename, bases) 1146 title = title + '(%s)' % join(parents, ', ') 1147 1148 doc = getdoc(object) 1149 contents = doc and [doc + '\n'] or [] 1150 push = contents.append 1151 1152 # List the mro, if non-trivial. 1153 mro = deque(inspect.getmro(object)) 1154 if len(mro) > 2: 1155 push("Method resolution order:") 1156 for base in mro: 1157 push(' ' + makename(base)) 1158 push('') 1159 1160 # Cute little class to pump out a horizontal rule between sections. 1161 class HorizontalRule: 1162 def __init__(self): 1163 self.needone = 0 1164 def maybe(self): 1165 if self.needone: 1166 push('-' * 70) 1167 self.needone = 1 1168 hr = HorizontalRule() 1169 1170 def spill(msg, attrs, predicate): 1171 ok, attrs = _split_list(attrs, predicate) 1172 if ok: 1173 hr.maybe() 1174 push(msg) 1175 for name, kind, homecls, value in ok: 1176 try: 1177 value = getattr(object, name) 1178 except Exception: 1179 # Some descriptors may meet a failure in their __get__. 1180 # (bug #1785) 1181 push(self._docdescriptor(name, value, mod)) 1182 else: 1183 push(self.document(value, 1184 name, mod, object)) 1185 return attrs 1186 1187 def spilldescriptors(msg, attrs, predicate): 1188 ok, attrs = _split_list(attrs, predicate) 1189 if ok: 1190 hr.maybe() 1191 push(msg) 1192 for name, kind, homecls, value in ok: 1193 push(self._docdescriptor(name, value, mod)) 1194 return attrs 1195 1196 def spilldata(msg, attrs, predicate): 1197 ok, attrs = _split_list(attrs, predicate) 1198 if ok: 1199 hr.maybe() 1200 push(msg) 1201 for name, kind, homecls, value in ok: 1202 if (hasattr(value, '__call__') or 1203 inspect.isdatadescriptor(value)): 1204 doc = getdoc(value) 1205 else: 1206 doc = None 1207 push(self.docother(getattr(object, name), 1208 name, mod, maxlen=70, doc=doc) + '\n') 1209 return attrs 1210 1211 attrs = filter(lambda data: visiblename(data[0], obj=object), 1212 classify_class_attrs(object)) 1213 while attrs: 1214 if mro: 1215 thisclass = mro.popleft() 1216 else: 1217 thisclass = attrs[0][2] 1218 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass) 1219 1220 if thisclass is __builtin__.object: 1221 attrs = inherited 1222 continue 1223 elif thisclass is object: 1224 tag = "defined here" 1225 else: 1226 tag = "inherited from %s" % classname(thisclass, 1227 object.__module__) 1228 1229 # Sort attrs by name. 1230 attrs.sort() 1231 1232 # Pump out the attrs, segregated by kind. 1233 attrs = spill("Methods %s:\n" % tag, attrs, 1234 lambda t: t[1] == 'method') 1235 attrs = spill("Class methods %s:\n" % tag, attrs, 1236 lambda t: t[1] == 'class method') 1237 attrs = spill("Static methods %s:\n" % tag, attrs, 1238 lambda t: t[1] == 'static method') 1239 attrs = spilldescriptors("Data descriptors %s:\n" % tag, attrs, 1240 lambda t: t[1] == 'data descriptor') 1241 attrs = spilldata("Data and other attributes %s:\n" % tag, attrs, 1242 lambda t: t[1] == 'data') 1243 assert attrs == [] 1244 attrs = inherited 1245 1246 contents = '\n'.join(contents) 1247 if not contents: 1248 return title + '\n' 1249 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n' 1250 1251 def formatvalue(self, object): 1252 """Format an argument default value as text.""" 1253 return '=' + self.repr(object) 1254 1255 def docroutine(self, object, name=None, mod=None, cl=None): 1256 """Produce text documentation for a function or method object.""" 1257 realname = object.__name__ 1258 name = name or realname 1259 note = '' 1260 skipdocs = 0 1261 if inspect.ismethod(object): 1262 imclass = object.im_class 1263 if cl: 1264 if imclass is not cl: 1265 note = ' from ' + classname(imclass, mod) 1266 else: 1267 if object.im_self is not None: 1268 note = ' method of %s instance' % classname( 1269 object.im_self.__class__, mod) 1270 else: 1271 note = ' unbound %s method' % classname(imclass,mod) 1272 object = object.im_func 1273 1274 if name == realname: 1275 title = self.bold(realname) 1276 else: 1277 if (cl and realname in cl.__dict__ and 1278 cl.__dict__[realname] is object): 1279 skipdocs = 1 1280 title = self.bold(name) + ' = ' + realname 1281 if inspect.isfunction(object): 1282 args, varargs, varkw, defaults = inspect.getargspec(object) 1283 argspec = inspect.formatargspec( 1284 args, varargs, varkw, defaults, formatvalue=self.formatvalue) 1285 if realname == '<lambda>': 1286 title = self.bold(name) + ' lambda ' 1287 argspec = argspec[1:-1] # remove parentheses 1288 else: 1289 argspec = '(...)' 1290 decl = title + argspec + note 1291 1292 if skipdocs: 1293 return decl + '\n' 1294 else: 1295 doc = getdoc(object) or '' 1296 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n') 1297 1298 def _docdescriptor(self, name, value, mod): 1299 results = [] 1300 push = results.append 1301 1302 if name: 1303 push(self.bold(name)) 1304 push('\n') 1305 doc = getdoc(value) or '' 1306 if doc: 1307 push(self.indent(doc)) 1308 push('\n') 1309 return ''.join(results) 1310 1311 def docproperty(self, object, name=None, mod=None, cl=None): 1312 """Produce text documentation for a property.""" 1313 return self._docdescriptor(name, object, mod) 1314 1315 def docdata(self, object, name=None, mod=None, cl=None): 1316 """Produce text documentation for a data descriptor.""" 1317 return self._docdescriptor(name, object, mod) 1318 1319 def docother(self, object, name=None, mod=None, parent=None, maxlen=None, doc=None): 1320 """Produce text documentation for a data object.""" 1321 repr = self.repr(object) 1322 if maxlen: 1323 line = (name and name + ' = ' or '') + repr 1324 chop = maxlen - len(line) 1325 if chop < 0: repr = repr[:chop] + '...' 1326 line = (name and self.bold(name) + ' = ' or '') + repr 1327 if doc is not None: 1328 line += '\n' + self.indent(str(doc)) 1329 return line 1330 1331 # --------------------------------------------------------- user interfaces 1332 1333 def pager(text): 1334 """The first time this is called, determine what kind of pager to use.""" 1335 global pager 1336 pager = getpager() 1337 pager(text) 1338 1339 def getpager(): 1340 """Decide what method to use for paging through text.""" 1341 if type(sys.stdout) is not types.FileType: 1342 return plainpager 1343 if not sys.stdin.isatty() or not sys.stdout.isatty(): 1344 return plainpager 1345 if 'PAGER' in os.environ: 1346 if sys.platform == 'win32': # pipes completely broken in Windows 1347 return lambda text: tempfilepager(plain(text), os.environ['PAGER']) 1348 elif os.environ.get('TERM') in ('dumb', 'emacs'): 1349 return lambda text: pipepager(plain(text), os.environ['PAGER']) 1350 else: 1351 return lambda text: pipepager(text, os.environ['PAGER']) 1352 if os.environ.get('TERM') in ('dumb', 'emacs'): 1353 return plainpager 1354 if sys.platform == 'win32' or sys.platform.startswith('os2'): 1355 return lambda text: tempfilepager(plain(text), 'more <') 1356 if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0: 1357 return lambda text: pipepager(text, 'less') 1358 1359 import tempfile 1360 (fd, filename) = tempfile.mkstemp() 1361 os.close(fd) 1362 try: 1363 if hasattr(os, 'system') and os.system('more "%s"' % filename) == 0: 1364 return lambda text: pipepager(text, 'more') 1365 else: 1366 return ttypager 1367 finally: 1368 os.unlink(filename) 1369 1370 def plain(text): 1371 """Remove boldface formatting from text.""" 1372 return re.sub('.\b', '', text) 1373 1374 def pipepager(text, cmd): 1375 """Page through text by feeding it to another program.""" 1376 pipe = os.popen(cmd, 'w') 1377 try: 1378 pipe.write(text) 1379 pipe.close() 1380 except IOError: 1381 pass # Ignore broken pipes caused by quitting the pager program. 1382 1383 def tempfilepager(text, cmd): 1384 """Page through text by invoking a program on a temporary file.""" 1385 import tempfile 1386 filename = tempfile.mktemp() 1387 file = open(filename, 'w') 1388 file.write(text) 1389 file.close() 1390 try: 1391 os.system(cmd + ' "' + filename + '"') 1392 finally: 1393 os.unlink(filename) 1394 1395 def ttypager(text): 1396 """Page through text on a text terminal.""" 1397 lines = split(plain(text), '\n') 1398 try: 1399 import tty 1400 fd = sys.stdin.fileno() 1401 old = tty.tcgetattr(fd) 1402 tty.setcbreak(fd) 1403 getchar = lambda: sys.stdin.read(1) 1404 except (ImportError, AttributeError): 1405 tty = None 1406 getchar = lambda: sys.stdin.readline()[:-1][:1] 1407 1408 try: 1409 r = inc = os.environ.get('LINES', 25) - 1 1410 sys.stdout.write(join(lines[:inc], '\n') + '\n') 1411 while lines[r:]: 1412 sys.stdout.write('-- more --') 1413 sys.stdout.flush() 1414 c = getchar() 1415 1416 if c in ('q', 'Q'): 1417 sys.stdout.write('\r \r') 1418 break 1419 elif c in ('\r', '\n'): 1420 sys.stdout.write('\r \r' + lines[r] + '\n') 1421 r = r + 1 1422 continue 1423 if c in ('b', 'B', '\x1b'): 1424 r = r - inc - inc 1425 if r < 0: r = 0 1426 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n') 1427 r = r + inc 1428 1429 finally: 1430 if tty: 1431 tty.tcsetattr(fd, tty.TCSAFLUSH, old) 1432 1433 def plainpager(text): 1434 """Simply print unformatted text. This is the ultimate fallback.""" 1435 sys.stdout.write(plain(text)) 1436 1437 def describe(thing): 1438 """Produce a short description of the given thing.""" 1439 if inspect.ismodule(thing): 1440 if thing.__name__ in sys.builtin_module_names: 1441 return 'built-in module ' + thing.__name__ 1442 if hasattr(thing, '__path__'): 1443 return 'package ' + thing.__name__ 1444 else: 1445 return 'module ' + thing.__name__ 1446 if inspect.isbuiltin(thing): 1447 return 'built-in function ' + thing.__name__ 1448 if inspect.isgetsetdescriptor(thing): 1449 return 'getset descriptor %s.%s.%s' % ( 1450 thing.__objclass__.__module__, thing.__objclass__.__name__, 1451 thing.__name__) 1452 if inspect.ismemberdescriptor(thing): 1453 return 'member descriptor %s.%s.%s' % ( 1454 thing.__objclass__.__module__, thing.__objclass__.__name__, 1455 thing.__name__) 1456 if inspect.isclass(thing): 1457 return 'class ' + thing.__name__ 1458 if inspect.isfunction(thing): 1459 return 'function ' + thing.__name__ 1460 if inspect.ismethod(thing): 1461 return 'method ' + thing.__name__ 1462 if type(thing) is types.InstanceType: 1463 return 'instance of ' + thing.__class__.__name__ 1464 return type(thing).__name__ 1465 1466 def locate(path, forceload=0): 1467 """Locate an object by name or dotted path, importing as necessary.""" 1468 parts = [part for part in split(path, '.') if part] 1469 module, n = None, 0 1470 while n < len(parts): 1471 nextmodule = safeimport(join(parts[:n+1], '.'), forceload) 1472 if nextmodule: module, n = nextmodule, n + 1 1473 else: break 1474 if module: 1475 object = module 1476 else: 1477 object = __builtin__ 1478 for part in parts[n:]: 1479 try: 1480 object = getattr(object, part) 1481 except AttributeError: 1482 return None 1483 return object 1484 1485 # --------------------------------------- interactive interpreter interface 1486 1487 text = TextDoc() 1488 html = HTMLDoc() 1489 1490 class _OldStyleClass: pass 1491 _OLD_INSTANCE_TYPE = type(_OldStyleClass()) 1492 1493 def resolve(thing, forceload=0): 1494 """Given an object or a path to an object, get the object and its name.""" 1495 if isinstance(thing, str): 1496 object = locate(thing, forceload) 1497 if not object: 1498 raise ImportError, 'no Python documentation found for %r' % thing 1499 return object, thing 1500 else: 1501 name = getattr(thing, '__name__', None) 1502 return thing, name if isinstance(name, str) else None 1503 1504 def render_doc(thing, title='Python Library Documentation: %s', forceload=0): 1505 """Render text documentation, given an object or a path to an object.""" 1506 object, name = resolve(thing, forceload) 1507 desc = describe(object) 1508 module = inspect.getmodule(object) 1509 if name and '.' in name: 1510 desc += ' in ' + name[:name.rfind('.')] 1511 elif module and module is not object: 1512 desc += ' in module ' + module.__name__ 1513 if type(object) is _OLD_INSTANCE_TYPE: 1514 # If the passed object is an instance of an old-style class, 1515 # document its available methods instead of its value. 1516 object = object.__class__ 1517 elif not (inspect.ismodule(object) or 1518 inspect.isclass(object) or 1519 inspect.isroutine(object) or 1520 inspect.isgetsetdescriptor(object) or 1521 inspect.ismemberdescriptor(object) or 1522 isinstance(object, property)): 1523 # If the passed object is a piece of data or an instance, 1524 # document its available methods instead of its value. 1525 object = type(object) 1526 desc += ' object' 1527 return title % desc + '\n\n' + text.document(object, name) 1528 1529 def doc(thing, title='Python Library Documentation: %s', forceload=0): 1530 """Display text documentation, given an object or a path to an object.""" 1531 try: 1532 pager(render_doc(thing, title, forceload)) 1533 except (ImportError, ErrorDuringImport), value: 1534 print value 1535 1536 def writedoc(thing, forceload=0): 1537 """Write HTML documentation to a file in the current directory.""" 1538 try: 1539 object, name = resolve(thing, forceload) 1540 page = html.page(describe(object), html.document(object, name)) 1541 file = open(name + '.html', 'w') 1542 file.write(page) 1543 file.close() 1544 print 'wrote', name + '.html' 1545 except (ImportError, ErrorDuringImport), value: 1546 print value 1547 1548 def writedocs(dir, pkgpath='', done=None): 1549 """Write out HTML documentation for all modules in a directory tree.""" 1550 if done is None: done = {} 1551 for importer, modname, ispkg in pkgutil.walk_packages([dir], pkgpath): 1552 writedoc(modname) 1553 return 1554 1555 class Helper: 1556 1557 # These dictionaries map a topic name to either an alias, or a tuple 1558 # (label, seealso-items). The "label" is the label of the corresponding 1559 # section in the .rst file under Doc/ and an index into the dictionary 1560 # in pydoc_data/topics.py. 1561 # 1562 # CAUTION: if you change one of these dictionaries, be sure to adapt the 1563 # list of needed labels in Doc/tools/sphinxext/pyspecific.py and 1564 # regenerate the pydoc_data/topics.py file by running 1565 # make pydoc-topics 1566 # in Doc/ and copying the output file into the Lib/ directory. 1567 1568 keywords = { 1569 'and': 'BOOLEAN', 1570 'as': 'with', 1571 'assert': ('assert', ''), 1572 'break': ('break', 'while for'), 1573 'class': ('class', 'CLASSES SPECIALMETHODS'), 1574 'continue': ('continue', 'while for'), 1575 'def': ('function', ''), 1576 'del': ('del', 'BASICMETHODS'), 1577 'elif': 'if', 1578 'else': ('else', 'while for'), 1579 'except': 'try', 1580 'exec': ('exec', ''), 1581 'finally': 'try', 1582 'for': ('for', 'break continue while'), 1583 'from': 'import', 1584 'global': ('global', 'NAMESPACES'), 1585 'if': ('if', 'TRUTHVALUE'), 1586 'import': ('import', 'MODULES'), 1587 'in': ('in', 'SEQUENCEMETHODS2'), 1588 'is': 'COMPARISON', 1589 'lambda': ('lambda', 'FUNCTIONS'), 1590 'not': 'BOOLEAN', 1591 'or': 'BOOLEAN', 1592 'pass': ('pass', ''), 1593 'print': ('print', ''), 1594 'raise': ('raise', 'EXCEPTIONS'), 1595 'return': ('return', 'FUNCTIONS'), 1596 'try': ('try', 'EXCEPTIONS'), 1597 'while': ('while', 'break continue if TRUTHVALUE'), 1598 'with': ('with', 'CONTEXTMANAGERS EXCEPTIONS yield'), 1599 'yield': ('yield', ''), 1600 } 1601 # Either add symbols to this dictionary or to the symbols dictionary 1602 # directly: Whichever is easier. They are merged later. 1603 _symbols_inverse = { 1604 'STRINGS' : ("'", "'''", "r'", "u'", '"""', '"', 'r"', 'u"'), 1605 'OPERATORS' : ('+', '-', '*', '**', '/', '//', '%', '<<', '>>', '&', 1606 '|', '^', '~', '<', '>', '<=', '>=', '==', '!=', '<>'), 1607 'COMPARISON' : ('<', '>', '<=', '>=', '==', '!=', '<>'), 1608 'UNARY' : ('-', '~'), 1609 'AUGMENTEDASSIGNMENT' : ('+=', '-=', '*=', '/=', '%=', '&=', '|=', 1610 '^=', '<<=', '>>=', '**=', '//='), 1611 'BITWISE' : ('<<', '>>', '&', '|', '^', '~'), 1612 'COMPLEX' : ('j', 'J') 1613 } 1614 symbols = { 1615 '%': 'OPERATORS FORMATTING', 1616 '**': 'POWER', 1617 ',': 'TUPLES LISTS FUNCTIONS', 1618 '.': 'ATTRIBUTES FLOAT MODULES OBJECTS', 1619 '...': 'ELLIPSIS', 1620 ':': 'SLICINGS DICTIONARYLITERALS', 1621 '@': 'def class', 1622 '\\': 'STRINGS', 1623 '_': 'PRIVATENAMES', 1624 '__': 'PRIVATENAMES SPECIALMETHODS', 1625 '`': 'BACKQUOTES', 1626 '(': 'TUPLES FUNCTIONS CALLS', 1627 ')': 'TUPLES FUNCTIONS CALLS', 1628 '[': 'LISTS SUBSCRIPTS SLICINGS', 1629 ']': 'LISTS SUBSCRIPTS SLICINGS' 1630 } 1631 for topic, symbols_ in _symbols_inverse.iteritems(): 1632 for symbol in symbols_: 1633 topics = symbols.get(symbol, topic) 1634 if topic not in topics: 1635 topics = topics + ' ' + topic 1636 symbols[symbol] = topics 1637 1638 topics = { 1639 'TYPES': ('types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS ' 1640 'FUNCTIONS CLASSES MODULES FILES inspect'), 1641 'STRINGS': ('strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING ' 1642 'TYPES'), 1643 'STRINGMETHODS': ('string-methods', 'STRINGS FORMATTING'), 1644 'FORMATTING': ('formatstrings', 'OPERATORS'), 1645 'UNICODE': ('strings', 'encodings unicode SEQUENCES STRINGMETHODS ' 1646 'FORMATTING TYPES'), 1647 'NUMBERS': ('numbers', 'INTEGER FLOAT COMPLEX TYPES'), 1648 'INTEGER': ('integers', 'int range'), 1649 'FLOAT': ('floating', 'float math'), 1650 'COMPLEX': ('imaginary', 'complex cmath'), 1651 'SEQUENCES': ('typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'), 1652 'MAPPINGS': 'DICTIONARIES', 1653 'FUNCTIONS': ('typesfunctions', 'def TYPES'), 1654 'METHODS': ('typesmethods', 'class def CLASSES TYPES'), 1655 'CODEOBJECTS': ('bltin-code-objects', 'compile FUNCTIONS TYPES'), 1656 'TYPEOBJECTS': ('bltin-type-objects', 'types TYPES'), 1657 'FRAMEOBJECTS': 'TYPES', 1658 'TRACEBACKS': 'TYPES', 1659 'NONE': ('bltin-null-object', ''), 1660 'ELLIPSIS': ('bltin-ellipsis-object', 'SLICINGS'), 1661 'FILES': ('bltin-file-objects', ''), 1662 'SPECIALATTRIBUTES': ('specialattrs', ''), 1663 'CLASSES': ('types', 'class SPECIALMETHODS PRIVATENAMES'), 1664 'MODULES': ('typesmodules', 'import'), 1665 'PACKAGES': 'import', 1666 'EXPRESSIONS': ('operator-summary', 'lambda or and not in is BOOLEAN ' 1667 'COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER ' 1668 'UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES ' 1669 'LISTS DICTIONARIES BACKQUOTES'), 1670 'OPERATORS': 'EXPRESSIONS', 1671 'PRECEDENCE': 'EXPRESSIONS', 1672 'OBJECTS': ('objects', 'TYPES'), 1673 'SPECIALMETHODS': ('specialnames', 'BASICMETHODS ATTRIBUTEMETHODS ' 1674 'CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS ' 1675 'SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'), 1676 'BASICMETHODS': ('customization', 'cmp hash repr str SPECIALMETHODS'), 1677 'ATTRIBUTEMETHODS': ('attribute-access', 'ATTRIBUTES SPECIALMETHODS'), 1678 'CALLABLEMETHODS': ('callable-types', 'CALLS SPECIALMETHODS'), 1679 'SEQUENCEMETHODS1': ('sequence-types', 'SEQUENCES SEQUENCEMETHODS2 ' 1680 'SPECIALMETHODS'), 1681 'SEQUENCEMETHODS2': ('sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 ' 1682 'SPECIALMETHODS'), 1683 'MAPPINGMETHODS': ('sequence-types', 'MAPPINGS SPECIALMETHODS'), 1684 'NUMBERMETHODS': ('numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT ' 1685 'SPECIALMETHODS'), 1686 'EXECUTION': ('execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'), 1687 'NAMESPACES': ('naming', 'global ASSIGNMENT DELETION DYNAMICFEATURES'), 1688 'DYNAMICFEATURES': ('dynamic-features', ''), 1689 'SCOPING': 'NAMESPACES', 1690 'FRAMES': 'NAMESPACES', 1691 'EXCEPTIONS': ('exceptions', 'try except finally raise'), 1692 'COERCIONS': ('coercion-rules','CONVERSIONS'), 1693 'CONVERSIONS': ('conversions', 'COERCIONS'), 1694 'IDENTIFIERS': ('identifiers', 'keywords SPECIALIDENTIFIERS'), 1695 'SPECIALIDENTIFIERS': ('id-classes', ''), 1696 'PRIVATENAMES': ('atom-identifiers', ''), 1697 'LITERALS': ('atom-literals', 'STRINGS BACKQUOTES NUMBERS ' 1698 'TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'), 1699 'TUPLES': 'SEQUENCES', 1700 'TUPLELITERALS': ('exprlists', 'TUPLES LITERALS'), 1701 'LISTS': ('typesseq-mutable', 'LISTLITERALS'), 1702 'LISTLITERALS': ('lists', 'LISTS LITERALS'), 1703 'DICTIONARIES': ('typesmapping', 'DICTIONARYLITERALS'), 1704 'DICTIONARYLITERALS': ('dict', 'DICTIONARIES LITERALS'), 1705 'BACKQUOTES': ('string-conversions', 'repr str STRINGS LITERALS'), 1706 'ATTRIBUTES': ('attribute-references', 'getattr hasattr setattr ' 1707 'ATTRIBUTEMETHODS'), 1708 'SUBSCRIPTS': ('subscriptions', 'SEQUENCEMETHODS1'), 1709 'SLICINGS': ('slicings', 'SEQUENCEMETHODS2'), 1710 'CALLS': ('calls', 'EXPRESSIONS'), 1711 'POWER': ('power', 'EXPRESSIONS'), 1712 'UNARY': ('unary', 'EXPRESSIONS'), 1713 'BINARY': ('binary', 'EXPRESSIONS'), 1714 'SHIFTING': ('shifting', 'EXPRESSIONS'), 1715 'BITWISE': ('bitwise', 'EXPRESSIONS'), 1716 'COMPARISON': ('comparisons', 'EXPRESSIONS BASICMETHODS'), 1717 'BOOLEAN': ('booleans', 'EXPRESSIONS TRUTHVALUE'), 1718 'ASSERTION': 'assert', 1719 'ASSIGNMENT': ('assignment', 'AUGMENTEDASSIGNMENT'), 1720 'AUGMENTEDASSIGNMENT': ('augassign', 'NUMBERMETHODS'), 1721 'DELETION': 'del', 1722 'PRINTING': 'print', 1723 'RETURNING': 'return', 1724 'IMPORTING': 'import', 1725 'CONDITIONAL': 'if', 1726 'LOOPING': ('compound', 'for while break continue'), 1727 'TRUTHVALUE': ('truth', 'if while and or not BASICMETHODS'), 1728 'DEBUGGING': ('debugger', 'pdb'), 1729 'CONTEXTMANAGERS': ('context-managers', 'with'), 1730 } 1731 1732 def __init__(self, input=None, output=None): 1733 self._input = input 1734 self._output = output 1735 1736 input = property(lambda self: self._input or sys.stdin) 1737 output = property(lambda self: self._output or sys.stdout) 1738 1739 def __repr__(self): 1740 if inspect.stack()[1][3] == '?': 1741 self() 1742 return '' 1743 return '<pydoc.Helper instance>' 1744 1745 _GoInteractive = object() 1746 def __call__(self, request=_GoInteractive): 1747 if request is not self._GoInteractive: 1748 self.help(request) 1749 else: 1750 self.intro() 1751 self.interact() 1752 self.output.write(''' 1753 You are now leaving help and returning to the Python interpreter. 1754 If you want to ask for help on a particular object directly from the 1755 interpreter, you can type "help(object)". Executing "help('string')" 1756 has the same effect as typing a particular string at the help> prompt. 1757 ''') 1758 1759 def interact(self): 1760 self.output.write('\n') 1761 while True: 1762 try: 1763 request = self.getline('help> ') 1764 if not request: break 1765 except (KeyboardInterrupt, EOFError): 1766 break 1767 request = strip(replace(request, '"', '', "'", '')) 1768 if lower(request) in ('q', 'quit'): break 1769 self.help(request) 1770 1771 def getline(self, prompt): 1772 """Read one line, using raw_input when available.""" 1773 if self.input is sys.stdin: 1774 return raw_input(prompt) 1775 else: 1776 self.output.write(prompt) 1777 self.output.flush() 1778 return self.input.readline() 1779 1780 def help(self, request): 1781 if type(request) is type(''): 1782 request = request.strip() 1783 if request == 'help': self.intro() 1784 elif request == 'keywords': self.listkeywords() 1785 elif request == 'symbols': self.listsymbols() 1786 elif request == 'topics': self.listtopics() 1787 elif request == 'modules': self.listmodules() 1788 elif request[:8] == 'modules ': 1789 self.listmodules(split(request)[1]) 1790 elif request in self.symbols: self.showsymbol(request) 1791 elif request in self.keywords: self.showtopic(request) 1792 elif request in self.topics: self.showtopic(request) 1793 elif request: doc(request, 'Help on %s:') 1794 elif isinstance(request, Helper): self() 1795 else: doc(request, 'Help on %s:') 1796 self.output.write('\n') 1797 1798 def intro(self): 1799 self.output.write(''' 1800 Welcome to Python %s! This is the online help utility. 1801 1802 If this is your first time using Python, you should definitely check out 1803 the tutorial on the Internet at http://docs.python.org/%s/tutorial/. 1804 1805 Enter the name of any module, keyword, or topic to get help on writing 1806 Python programs and using Python modules. To quit this help utility and 1807 return to the interpreter, just type "quit". 1808 1809 To get a list of available modules, keywords, or topics, type "modules", 1810 "keywords", or "topics". Each module also comes with a one-line summary 1811 of what it does; to list the modules whose summaries contain a given word 1812 such as "spam", type "modules spam". 1813 ''' % tuple([sys.version[:3]]*2)) 1814 1815 def list(self, items, columns=4, width=80): 1816 items = items[:] 1817 items.sort() 1818 colw = width / columns 1819 rows = (len(items) + columns - 1) / columns 1820 for row in range(rows): 1821 for col in range(columns): 1822 i = col * rows + row 1823 if i < len(items): 1824 self.output.write(items[i]) 1825 if col < columns - 1: 1826 self.output.write(' ' + ' ' * (colw-1 - len(items[i]))) 1827 self.output.write('\n') 1828 1829 def listkeywords(self): 1830 self.output.write(''' 1831 Here is a list of the Python keywords. Enter any keyword to get more help. 1832 1833 ''') 1834 self.list(self.keywords.keys()) 1835 1836 def listsymbols(self): 1837 self.output.write(''' 1838 Here is a list of the punctuation symbols which Python assigns special meaning 1839 to. Enter any symbol to get more help. 1840 1841 ''') 1842 self.list(self.symbols.keys()) 1843 1844 def listtopics(self): 1845 self.output.write(''' 1846 Here is a list of available topics. Enter any topic name to get more help. 1847 1848 ''') 1849 self.list(self.topics.keys()) 1850 1851 def showtopic(self, topic, more_xrefs=''): 1852 try: 1853 import pydoc_data.topics 1854 except ImportError: 1855 self.output.write(''' 1856 Sorry, topic and keyword documentation is not available because the 1857 module "pydoc_data.topics" could not be found. 1858 ''') 1859 return 1860 target = self.topics.get(topic, self.keywords.get(topic)) 1861 if not target: 1862 self.output.write('no documentation found for %s\n' % repr(topic)) 1863 return 1864 if type(target) is type(''): 1865 return self.showtopic(target, more_xrefs) 1866 1867 label, xrefs = target 1868 try: 1869 doc = pydoc_data.topics.topics[label] 1870 except KeyError: 1871 self.output.write('no documentation found for %s\n' % repr(topic)) 1872 return 1873 pager(strip(doc) + '\n') 1874 if more_xrefs: 1875 xrefs = (xrefs or '') + ' ' + more_xrefs 1876 if xrefs: 1877 import StringIO, formatter 1878 buffer = StringIO.StringIO() 1879 formatter.DumbWriter(buffer).send_flowing_data( 1880 'Related help topics: ' + join(split(xrefs), ', ') + '\n') 1881 self.output.write('\n%s\n' % buffer.getvalue()) 1882 1883 def showsymbol(self, symbol): 1884 target = self.symbols[symbol] 1885 topic, _, xrefs = target.partition(' ') 1886 self.showtopic(topic, xrefs) 1887 1888 def listmodules(self, key=''): 1889 if key: 1890 self.output.write(''' 1891 Here is a list of matching modules. Enter any module name to get more help. 1892 1893 ''') 1894 apropos(key) 1895 else: 1896 self.output.write(''' 1897 Please wait a moment while I gather a list of all available modules... 1898 1899 ''') 1900 modules = {} 1901 def callback(path, modname, desc, modules=modules): 1902 if modname and modname[-9:] == '.__init__': 1903 modname = modname[:-9] + ' (package)' 1904 if find(modname, '.') < 0: 1905 modules[modname] = 1 1906 def onerror(modname): 1907 callback(None, modname, None) 1908 ModuleScanner().run(callback, onerror=onerror) 1909 self.list(modules.keys()) 1910 self.output.write(''' 1911 Enter any module name to get more help. Or, type "modules spam" to search 1912 for modules whose descriptions contain the word "spam". 1913 ''') 1914 1915 help = Helper() 1916 1917 class Scanner: 1918 """A generic tree iterator.""" 1919 def __init__(self, roots, children, descendp): 1920 self.roots = roots[:] 1921 self.state = [] 1922 self.children = children 1923 self.descendp = descendp 1924 1925 def next(self): 1926 if not self.state: 1927 if not self.roots: 1928 return None 1929 root = self.roots.pop(0) 1930 self.state = [(root, self.children(root))] 1931 node, children = self.state[-1] 1932 if not children: 1933 self.state.pop() 1934 return self.next() 1935 child = children.pop(0) 1936 if self.descendp(child): 1937 self.state.append((child, self.children(child))) 1938 return child 1939 1940 1941 class ModuleScanner: 1942 """An interruptible scanner that searches module synopses.""" 1943 1944 def run(self, callback, key=None, completer=None, onerror=None): 1945 if key: key = lower(key) 1946 self.quit = False 1947 seen = {} 1948 1949 for modname in sys.builtin_module_names: 1950 if modname != '__main__': 1951 seen[modname] = 1 1952 if key is None: 1953 callback(None, modname, '') 1954 else: 1955 desc = split(__import__(modname).__doc__ or '', '\n')[0] 1956 if find(lower(modname + ' - ' + desc), key) >= 0: 1957 callback(None, modname, desc) 1958 1959 for importer, modname, ispkg in pkgutil.walk_packages(onerror=onerror): 1960 if self.quit: 1961 break 1962 if key is None: 1963 callback(None, modname, '') 1964 else: 1965 loader = importer.find_module(modname) 1966 if hasattr(loader,'get_source'): 1967 import StringIO 1968 desc = source_synopsis( 1969 StringIO.StringIO(loader.get_source(modname)) 1970 ) or '' 1971 if hasattr(loader,'get_filename'): 1972 path = loader.get_filename(modname) 1973 else: 1974 path = None 1975 else: 1976 module = loader.load_module(modname) 1977 desc = (module.__doc__ or '').splitlines()[0] 1978 path = getattr(module,'__file__',None) 1979 if find(lower(modname + ' - ' + desc), key) >= 0: 1980 callback(path, modname, desc) 1981 1982 if completer: 1983 completer() 1984 1985 def apropos(key): 1986 """Print all the one-line module summaries that contain a substring.""" 1987 def callback(path, modname, desc): 1988 if modname[-9:] == '.__init__': 1989 modname = modname[:-9] + ' (package)' 1990 print modname, desc and '- ' + desc 1991 def onerror(modname): 1992 pass 1993 with warnings.catch_warnings(): 1994 warnings.filterwarnings('ignore') # ignore problems during import 1995 ModuleScanner().run(callback, key, onerror=onerror) 1996 1997 # --------------------------------------------------- web browser interface 1998 1999 def serve(port, callback=None, completer=None): 2000 import BaseHTTPServer, mimetools, select 2001 2002 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded. 2003 class Message(mimetools.Message): 2004 def __init__(self, fp, seekable=1): 2005 Message = self.__class__ 2006 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable) 2007 self.encodingheader = self.getheader('content-transfer-encoding') 2008 self.typeheader = self.getheader('content-type') 2009 self.parsetype() 2010 self.parseplist() 2011 2012 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler): 2013 def send_document(self, title, contents): 2014 try: 2015 self.send_response(200) 2016 self.send_header('Content-Type', 'text/html') 2017 self.end_headers() 2018 self.wfile.write(html.page(title, contents)) 2019 except IOError: pass 2020 2021 def do_GET(self): 2022 path = self.path 2023 if path[-5:] == '.html': path = path[:-5] 2024 if path[:1] == '/': path = path[1:] 2025 if path and path != '.': 2026 try: 2027 obj = locate(path, forceload=1) 2028 except ErrorDuringImport, value: 2029 self.send_document(path, html.escape(str(value))) 2030 return 2031 if obj: 2032 self.send_document(describe(obj), html.document(obj, path)) 2033 else: 2034 self.send_document(path, 2035 'no Python documentation found for %s' % repr(path)) 2036 else: 2037 heading = html.heading( 2038 '<big><big><strong>Python: Index of Modules</strong></big></big>', 2039 '#ffffff', '#7799ee') 2040 def bltinlink(name): 2041 return '<a href="%s.html">%s</a>' % (name, name) 2042 names = filter(lambda x: x != '__main__', 2043 sys.builtin_module_names) 2044 contents = html.multicolumn(names, bltinlink) 2045 indices = ['<p>' + html.bigsection( 2046 'Built-in Modules', '#ffffff', '#ee77aa', contents)] 2047 2048 seen = {} 2049 for dir in sys.path: 2050 indices.append(html.index(dir, seen)) 2051 contents = heading + join(indices) + '''<p align=right> 2052 <font color="#909090" face="helvetica, arial"><strong> 2053 pydoc</strong> by Ka-Ping Yee <ping (at] lfw.org></font>''' 2054 self.send_document('Index of Modules', contents) 2055 2056 def log_message(self, *args): pass 2057 2058 class DocServer(BaseHTTPServer.HTTPServer): 2059 def __init__(self, port, callback): 2060 host = 'localhost' 2061 self.address = (host, port) 2062 self.url = 'http://%s:%d/' % (host, port) 2063 self.callback = callback 2064 self.base.__init__(self, self.address, self.handler) 2065 2066 def serve_until_quit(self): 2067 import select 2068 self.quit = False 2069 while not self.quit: 2070 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1) 2071 if rd: self.handle_request() 2072 2073 def server_activate(self): 2074 self.base.server_activate(self) 2075 if self.callback: self.callback(self) 2076 2077 DocServer.base = BaseHTTPServer.HTTPServer 2078 DocServer.handler = DocHandler 2079 DocHandler.MessageClass = Message 2080 try: 2081 try: 2082 DocServer(port, callback).serve_until_quit() 2083 except (KeyboardInterrupt, select.error): 2084 pass 2085 finally: 2086 if completer: completer() 2087 2088 # ----------------------------------------------------- graphical interface 2089 2090 def gui(): 2091 """Graphical interface (starts web server and pops up a control window).""" 2092 class GUI: 2093 def __init__(self, window, port=7464): 2094 self.window = window 2095 self.server = None 2096 self.scanner = None 2097 2098 import Tkinter 2099 self.server_frm = Tkinter.Frame(window) 2100 self.title_lbl = Tkinter.Label(self.server_frm, 2101 text='Starting server...\n ') 2102 self.open_btn = Tkinter.Button(self.server_frm, 2103 text='open browser', command=self.open, state='disabled') 2104 self.quit_btn = Tkinter.Button(self.server_frm, 2105 text='quit serving', command=self.quit, state='disabled') 2106 2107 self.search_frm = Tkinter.Frame(window) 2108 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for') 2109 self.search_ent = Tkinter.Entry(self.search_frm) 2110 self.search_ent.bind('<Return>', self.search) 2111 self.stop_btn = Tkinter.Button(self.search_frm, 2112 text='stop', pady=0, command=self.stop, state='disabled') 2113 if sys.platform == 'win32': 2114 # Trying to hide and show this button crashes under Windows. 2115 self.stop_btn.pack(side='right') 2116 2117 self.window.title('pydoc') 2118 self.window.protocol('WM_DELETE_WINDOW', self.quit) 2119 self.title_lbl.pack(side='top', fill='x') 2120 self.open_btn.pack(side='left', fill='x', expand=1) 2121 self.quit_btn.pack(side='right', fill='x', expand=1) 2122 self.server_frm.pack(side='top', fill='x') 2123 2124 self.search_lbl.pack(side='left') 2125 self.search_ent.pack(side='right', fill='x', expand=1) 2126 self.search_frm.pack(side='top', fill='x') 2127 self.search_ent.focus_set() 2128 2129 font = ('helvetica', sys.platform == 'win32' and 8 or 10) 2130 self.result_lst = Tkinter.Listbox(window, font=font, height=6) 2131 self.result_lst.bind('<Button-1>', self.select) 2132 self.result_lst.bind('<Double-Button-1>', self.goto) 2133 self.result_scr = Tkinter.Scrollbar(window, 2134 orient='vertical', command=self.result_lst.yview) 2135 self.result_lst.config(yscrollcommand=self.result_scr.set) 2136 2137 self.result_frm = Tkinter.Frame(window) 2138 self.goto_btn = Tkinter.Button(self.result_frm, 2139 text='go to selected', command=self.goto) 2140 self.hide_btn = Tkinter.Button(self.result_frm, 2141 text='hide results', command=self.hide) 2142 self.goto_btn.pack(side='left', fill='x', expand=1) 2143 self.hide_btn.pack(side='right', fill='x', expand=1) 2144 2145 self.window.update() 2146 self.minwidth = self.window.winfo_width() 2147 self.minheight = self.window.winfo_height() 2148 self.bigminheight = (self.server_frm.winfo_reqheight() + 2149 self.search_frm.winfo_reqheight() + 2150 self.result_lst.winfo_reqheight() + 2151 self.result_frm.winfo_reqheight()) 2152 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight 2153 self.expanded = 0 2154 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight)) 2155 self.window.wm_minsize(self.minwidth, self.minheight) 2156 self.window.tk.willdispatch() 2157 2158 import threading 2159 threading.Thread( 2160 target=serve, args=(port, self.ready, self.quit)).start() 2161 2162 def ready(self, server): 2163 self.server = server 2164 self.title_lbl.config( 2165 text='Python documentation server at\n' + server.url) 2166 self.open_btn.config(state='normal') 2167 self.quit_btn.config(state='normal') 2168 2169 def open(self, event=None, url=None): 2170 url = url or self.server.url 2171 try: 2172 import webbrowser 2173 webbrowser.open(url) 2174 except ImportError: # pre-webbrowser.py compatibility 2175 if sys.platform == 'win32': 2176 os.system('start "%s"' % url) 2177 else: 2178 rc = os.system('netscape -remote "openURL(%s)" &' % url) 2179 if rc: os.system('netscape "%s" &' % url) 2180 2181 def quit(self, event=None): 2182 if self.server: 2183 self.server.quit = 1 2184 self.window.quit() 2185 2186 def search(self, event=None): 2187 key = self.search_ent.get() 2188 self.stop_btn.pack(side='right') 2189 self.stop_btn.config(state='normal') 2190 self.search_lbl.config(text='Searching for "%s"...' % key) 2191 self.search_ent.forget() 2192 self.search_lbl.pack(side='left') 2193 self.result_lst.delete(0, 'end') 2194 self.goto_btn.config(state='disabled') 2195 self.expand() 2196 2197 import threading 2198 if self.scanner: 2199 self.scanner.quit = 1 2200 self.scanner = ModuleScanner() 2201 threading.Thread(target=self.scanner.run, 2202 args=(self.update, key, self.done)).start() 2203 2204 def update(self, path, modname, desc): 2205 if modname[-9:] == '.__init__': 2206 modname = modname[:-9] + ' (package)' 2207 self.result_lst.insert('end', 2208 modname + ' - ' + (desc or '(no description)')) 2209 2210 def stop(self, event=None): 2211 if self.scanner: 2212 self.scanner.quit = 1 2213 self.scanner = None 2214 2215 def done(self): 2216 self.scanner = None 2217 self.search_lbl.config(text='Search for') 2218 self.search_lbl.pack(side='left') 2219 self.search_ent.pack(side='right', fill='x', expand=1) 2220 if sys.platform != 'win32': self.stop_btn.forget() 2221 self.stop_btn.config(state='disabled') 2222 2223 def select(self, event=None): 2224 self.goto_btn.config(state='normal') 2225 2226 def goto(self, event=None): 2227 selection = self.result_lst.curselection() 2228 if selection: 2229 modname = split(self.result_lst.get(selection[0]))[0] 2230 self.open(url=self.server.url + modname + '.html') 2231 2232 def collapse(self): 2233 if not self.expanded: return 2234 self.result_frm.forget() 2235 self.result_scr.forget() 2236 self.result_lst.forget() 2237 self.bigwidth = self.window.winfo_width() 2238 self.bigheight = self.window.winfo_height() 2239 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight)) 2240 self.window.wm_minsize(self.minwidth, self.minheight) 2241 self.expanded = 0 2242 2243 def expand(self): 2244 if self.expanded: return 2245 self.result_frm.pack(side='bottom', fill='x') 2246 self.result_scr.pack(side='right', fill='y') 2247 self.result_lst.pack(side='top', fill='both', expand=1) 2248 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight)) 2249 self.window.wm_minsize(self.minwidth, self.bigminheight) 2250 self.expanded = 1 2251 2252 def hide(self, event=None): 2253 self.stop() 2254 self.collapse() 2255 2256 import Tkinter 2257 try: 2258 root = Tkinter.Tk() 2259 # Tk will crash if pythonw.exe has an XP .manifest 2260 # file and the root has is not destroyed explicitly. 2261 # If the problem is ever fixed in Tk, the explicit 2262 # destroy can go. 2263 try: 2264 gui = GUI(root) 2265 root.mainloop() 2266 finally: 2267 root.destroy() 2268 except KeyboardInterrupt: 2269 pass 2270 2271 # -------------------------------------------------- command-line interface 2272 2273 def ispath(x): 2274 return isinstance(x, str) and find(x, os.sep) >= 0 2275 2276 def cli(): 2277 """Command-line interface (looks at sys.argv to decide what to do).""" 2278 import getopt 2279 class BadUsage: pass 2280 2281 # Scripts don't get the current directory in their path by default 2282 # unless they are run with the '-m' switch 2283 if '' not in sys.path: 2284 scriptdir = os.path.dirname(sys.argv[0]) 2285 if scriptdir in sys.path: 2286 sys.path.remove(scriptdir) 2287 sys.path.insert(0, '.') 2288 2289 try: 2290 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w') 2291 writing = 0 2292 2293 for opt, val in opts: 2294 if opt == '-g': 2295 gui() 2296 return 2297 if opt == '-k': 2298 apropos(val) 2299 return 2300 if opt == '-p': 2301 try: 2302 port = int(val) 2303 except ValueError: 2304 raise BadUsage 2305 def ready(server): 2306 print 'pydoc server ready at %s' % server.url 2307 def stopped(): 2308 print 'pydoc server stopped' 2309 serve(port, ready, stopped) 2310 return 2311 if opt == '-w': 2312 writing = 1 2313 2314 if not args: raise BadUsage 2315 for arg in args: 2316 if ispath(arg) and not os.path.exists(arg): 2317 print 'file %r does not exist' % arg 2318 break 2319 try: 2320 if ispath(arg) and os.path.isfile(arg): 2321 arg = importfile(arg) 2322 if writing: 2323 if ispath(arg) and os.path.isdir(arg): 2324 writedocs(arg) 2325 else: 2326 writedoc(arg) 2327 else: 2328 help.help(arg) 2329 except ErrorDuringImport, value: 2330 print value 2331 2332 except (getopt.error, BadUsage): 2333 cmd = os.path.basename(sys.argv[0]) 2334 print """pydoc - the Python documentation tool 2335 2336 %s <name> ... 2337 Show text documentation on something. <name> may be the name of a 2338 Python keyword, topic, function, module, or package, or a dotted 2339 reference to a class or function within a module or module in a 2340 package. If <name> contains a '%s', it is used as the path to a 2341 Python source file to document. If name is 'keywords', 'topics', 2342 or 'modules', a listing of these things is displayed. 2343 2344 %s -k <keyword> 2345 Search for a keyword in the synopsis lines of all available modules. 2346 2347 %s -p <port> 2348 Start an HTTP server on the given port on the local machine. 2349 2350 %s -g 2351 Pop up a graphical interface for finding and serving documentation. 2352 2353 %s -w <name> ... 2354 Write out the HTML documentation for a module to a file in the current 2355 directory. If <name> contains a '%s', it is treated as a filename; if 2356 it names a directory, documentation is written for all the contents. 2357 """ % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep) 2358 2359 if __name__ == '__main__': cli() 2360