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