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 _strprefixes = tuple(p + q for p in ('b', 'r', 'u') for q in ("'", '"')) 1651 _symbols_inverse = { 1652 'STRINGS' : ("'", "'''", '"""', '"') + _strprefixes, 1653 'OPERATORS' : ('+', '-', '*', '**', '/', '//', '%', '<<', '>>', '&', 1654 '|', '^', '~', '<', '>', '<=', '>=', '==', '!=', '<>'), 1655 'COMPARISON' : ('<', '>', '<=', '>=', '==', '!=', '<>'), 1656 'UNARY' : ('-', '~'), 1657 'AUGMENTEDASSIGNMENT' : ('+=', '-=', '*=', '/=', '%=', '&=', '|=', 1658 '^=', '<<=', '>>=', '**=', '//='), 1659 'BITWISE' : ('<<', '>>', '&', '|', '^', '~'), 1660 'COMPLEX' : ('j', 'J') 1661 } 1662 symbols = { 1663 '%': 'OPERATORS FORMATTING', 1664 '**': 'POWER', 1665 ',': 'TUPLES LISTS FUNCTIONS', 1666 '.': 'ATTRIBUTES FLOAT MODULES OBJECTS', 1667 '...': 'ELLIPSIS', 1668 ':': 'SLICINGS DICTIONARYLITERALS', 1669 '@': 'def class', 1670 '\\': 'STRINGS', 1671 '_': 'PRIVATENAMES', 1672 '__': 'PRIVATENAMES SPECIALMETHODS', 1673 '`': 'BACKQUOTES', 1674 '(': 'TUPLES FUNCTIONS CALLS', 1675 ')': 'TUPLES FUNCTIONS CALLS', 1676 '[': 'LISTS SUBSCRIPTS SLICINGS', 1677 ']': 'LISTS SUBSCRIPTS SLICINGS' 1678 } 1679 for topic, symbols_ in _symbols_inverse.iteritems(): 1680 for symbol in symbols_: 1681 topics = symbols.get(symbol, topic) 1682 if topic not in topics: 1683 topics = topics + ' ' + topic 1684 symbols[symbol] = topics 1685 1686 topics = { 1687 'TYPES': ('types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS ' 1688 'FUNCTIONS CLASSES MODULES FILES inspect'), 1689 'STRINGS': ('strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING ' 1690 'TYPES'), 1691 'STRINGMETHODS': ('string-methods', 'STRINGS FORMATTING'), 1692 'FORMATTING': ('formatstrings', 'OPERATORS'), 1693 'UNICODE': ('strings', 'encodings unicode SEQUENCES STRINGMETHODS ' 1694 'FORMATTING TYPES'), 1695 'NUMBERS': ('numbers', 'INTEGER FLOAT COMPLEX TYPES'), 1696 'INTEGER': ('integers', 'int range'), 1697 'FLOAT': ('floating', 'float math'), 1698 'COMPLEX': ('imaginary', 'complex cmath'), 1699 'SEQUENCES': ('typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'), 1700 'MAPPINGS': 'DICTIONARIES', 1701 'FUNCTIONS': ('typesfunctions', 'def TYPES'), 1702 'METHODS': ('typesmethods', 'class def CLASSES TYPES'), 1703 'CODEOBJECTS': ('bltin-code-objects', 'compile FUNCTIONS TYPES'), 1704 'TYPEOBJECTS': ('bltin-type-objects', 'types TYPES'), 1705 'FRAMEOBJECTS': 'TYPES', 1706 'TRACEBACKS': 'TYPES', 1707 'NONE': ('bltin-null-object', ''), 1708 'ELLIPSIS': ('bltin-ellipsis-object', 'SLICINGS'), 1709 'FILES': ('bltin-file-objects', ''), 1710 'SPECIALATTRIBUTES': ('specialattrs', ''), 1711 'CLASSES': ('types', 'class SPECIALMETHODS PRIVATENAMES'), 1712 'MODULES': ('typesmodules', 'import'), 1713 'PACKAGES': 'import', 1714 'EXPRESSIONS': ('operator-summary', 'lambda or and not in is BOOLEAN ' 1715 'COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER ' 1716 'UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES ' 1717 'LISTS DICTIONARIES BACKQUOTES'), 1718 'OPERATORS': 'EXPRESSIONS', 1719 'PRECEDENCE': 'EXPRESSIONS', 1720 'OBJECTS': ('objects', 'TYPES'), 1721 'SPECIALMETHODS': ('specialnames', 'BASICMETHODS ATTRIBUTEMETHODS ' 1722 'CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS ' 1723 'SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'), 1724 'BASICMETHODS': ('customization', 'cmp hash repr str SPECIALMETHODS'), 1725 'ATTRIBUTEMETHODS': ('attribute-access', 'ATTRIBUTES SPECIALMETHODS'), 1726 'CALLABLEMETHODS': ('callable-types', 'CALLS SPECIALMETHODS'), 1727 'SEQUENCEMETHODS1': ('sequence-types', 'SEQUENCES SEQUENCEMETHODS2 ' 1728 'SPECIALMETHODS'), 1729 'SEQUENCEMETHODS2': ('sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 ' 1730 'SPECIALMETHODS'), 1731 'MAPPINGMETHODS': ('sequence-types', 'MAPPINGS SPECIALMETHODS'), 1732 'NUMBERMETHODS': ('numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT ' 1733 'SPECIALMETHODS'), 1734 'EXECUTION': ('execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'), 1735 'NAMESPACES': ('naming', 'global ASSIGNMENT DELETION DYNAMICFEATURES'), 1736 'DYNAMICFEATURES': ('dynamic-features', ''), 1737 'SCOPING': 'NAMESPACES', 1738 'FRAMES': 'NAMESPACES', 1739 'EXCEPTIONS': ('exceptions', 'try except finally raise'), 1740 'COERCIONS': ('coercion-rules','CONVERSIONS'), 1741 'CONVERSIONS': ('conversions', 'COERCIONS'), 1742 'IDENTIFIERS': ('identifiers', 'keywords SPECIALIDENTIFIERS'), 1743 'SPECIALIDENTIFIERS': ('id-classes', ''), 1744 'PRIVATENAMES': ('atom-identifiers', ''), 1745 'LITERALS': ('atom-literals', 'STRINGS BACKQUOTES NUMBERS ' 1746 'TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'), 1747 'TUPLES': 'SEQUENCES', 1748 'TUPLELITERALS': ('exprlists', 'TUPLES LITERALS'), 1749 'LISTS': ('typesseq-mutable', 'LISTLITERALS'), 1750 'LISTLITERALS': ('lists', 'LISTS LITERALS'), 1751 'DICTIONARIES': ('typesmapping', 'DICTIONARYLITERALS'), 1752 'DICTIONARYLITERALS': ('dict', 'DICTIONARIES LITERALS'), 1753 'BACKQUOTES': ('string-conversions', 'repr str STRINGS LITERALS'), 1754 'ATTRIBUTES': ('attribute-references', 'getattr hasattr setattr ' 1755 'ATTRIBUTEMETHODS'), 1756 'SUBSCRIPTS': ('subscriptions', 'SEQUENCEMETHODS1'), 1757 'SLICINGS': ('slicings', 'SEQUENCEMETHODS2'), 1758 'CALLS': ('calls', 'EXPRESSIONS'), 1759 'POWER': ('power', 'EXPRESSIONS'), 1760 'UNARY': ('unary', 'EXPRESSIONS'), 1761 'BINARY': ('binary', 'EXPRESSIONS'), 1762 'SHIFTING': ('shifting', 'EXPRESSIONS'), 1763 'BITWISE': ('bitwise', 'EXPRESSIONS'), 1764 'COMPARISON': ('comparisons', 'EXPRESSIONS BASICMETHODS'), 1765 'BOOLEAN': ('booleans', 'EXPRESSIONS TRUTHVALUE'), 1766 'ASSERTION': 'assert', 1767 'ASSIGNMENT': ('assignment', 'AUGMENTEDASSIGNMENT'), 1768 'AUGMENTEDASSIGNMENT': ('augassign', 'NUMBERMETHODS'), 1769 'DELETION': 'del', 1770 'PRINTING': 'print', 1771 'RETURNING': 'return', 1772 'IMPORTING': 'import', 1773 'CONDITIONAL': 'if', 1774 'LOOPING': ('compound', 'for while break continue'), 1775 'TRUTHVALUE': ('truth', 'if while and or not BASICMETHODS'), 1776 'DEBUGGING': ('debugger', 'pdb'), 1777 'CONTEXTMANAGERS': ('context-managers', 'with'), 1778 } 1779 1780 def __init__(self, input=None, output=None): 1781 self._input = input 1782 self._output = output 1783 1784 input = property(lambda self: self._input or sys.stdin) 1785 output = property(lambda self: self._output or sys.stdout) 1786 1787 def __repr__(self): 1788 if inspect.stack()[1][3] == '?': 1789 self() 1790 return '' 1791 return '<pydoc.Helper instance>' 1792 1793 _GoInteractive = object() 1794 def __call__(self, request=_GoInteractive): 1795 if request is not self._GoInteractive: 1796 self.help(request) 1797 else: 1798 self.intro() 1799 self.interact() 1800 self.output.write(''' 1801You are now leaving help and returning to the Python interpreter. 1802If you want to ask for help on a particular object directly from the 1803interpreter, you can type "help(object)". Executing "help('string')" 1804has the same effect as typing a particular string at the help> prompt. 1805''') 1806 1807 def interact(self): 1808 self.output.write('\n') 1809 while True: 1810 try: 1811 request = self.getline('help> ') 1812 if not request: break 1813 except (KeyboardInterrupt, EOFError): 1814 break 1815 request = strip(request) 1816 # Make sure significant trailing quotation marks of literals don't 1817 # get deleted while cleaning input 1818 if (len(request) > 2 and request[0] == request[-1] in ("'", '"') 1819 and request[0] not in request[1:-1]): 1820 request = request[1:-1] 1821 if lower(request) in ('q', 'quit'): break 1822 self.help(request) 1823 1824 def getline(self, prompt): 1825 """Read one line, using raw_input when available.""" 1826 if self.input is sys.stdin: 1827 return raw_input(prompt) 1828 else: 1829 self.output.write(prompt) 1830 self.output.flush() 1831 return self.input.readline() 1832 1833 def help(self, request): 1834 if type(request) is type(''): 1835 request = request.strip() 1836 if request == 'help': self.intro() 1837 elif request == 'keywords': self.listkeywords() 1838 elif request == 'symbols': self.listsymbols() 1839 elif request == 'topics': self.listtopics() 1840 elif request == 'modules': self.listmodules() 1841 elif request[:8] == 'modules ': 1842 self.listmodules(split(request)[1]) 1843 elif request in self.symbols: self.showsymbol(request) 1844 elif request in self.keywords: self.showtopic(request) 1845 elif request in self.topics: self.showtopic(request) 1846 elif request: doc(request, 'Help on %s:') 1847 elif isinstance(request, Helper): self() 1848 else: doc(request, 'Help on %s:') 1849 self.output.write('\n') 1850 1851 def intro(self): 1852 self.output.write(''' 1853Welcome to Python %s! This is the online help utility. 1854 1855If this is your first time using Python, you should definitely check out 1856the tutorial on the Internet at http://docs.python.org/%s/tutorial/. 1857 1858Enter the name of any module, keyword, or topic to get help on writing 1859Python programs and using Python modules. To quit this help utility and 1860return to the interpreter, just type "quit". 1861 1862To get a list of available modules, keywords, or topics, type "modules", 1863"keywords", or "topics". Each module also comes with a one-line summary 1864of what it does; to list the modules whose summaries contain a given word 1865such as "spam", type "modules spam". 1866''' % tuple([sys.version[:3]]*2)) 1867 1868 def list(self, items, columns=4, width=80): 1869 items = items[:] 1870 items.sort() 1871 colw = width / columns 1872 rows = (len(items) + columns - 1) / columns 1873 for row in range(rows): 1874 for col in range(columns): 1875 i = col * rows + row 1876 if i < len(items): 1877 self.output.write(items[i]) 1878 if col < columns - 1: 1879 self.output.write(' ' + ' ' * (colw-1 - len(items[i]))) 1880 self.output.write('\n') 1881 1882 def listkeywords(self): 1883 self.output.write(''' 1884Here is a list of the Python keywords. Enter any keyword to get more help. 1885 1886''') 1887 self.list(self.keywords.keys()) 1888 1889 def listsymbols(self): 1890 self.output.write(''' 1891Here is a list of the punctuation symbols which Python assigns special meaning 1892to. Enter any symbol to get more help. 1893 1894''') 1895 self.list(self.symbols.keys()) 1896 1897 def listtopics(self): 1898 self.output.write(''' 1899Here is a list of available topics. Enter any topic name to get more help. 1900 1901''') 1902 self.list(self.topics.keys()) 1903 1904 def showtopic(self, topic, more_xrefs=''): 1905 try: 1906 import pydoc_data.topics 1907 except ImportError: 1908 self.output.write(''' 1909Sorry, topic and keyword documentation is not available because the 1910module "pydoc_data.topics" could not be found. 1911''') 1912 return 1913 target = self.topics.get(topic, self.keywords.get(topic)) 1914 if not target: 1915 self.output.write('no documentation found for %s\n' % repr(topic)) 1916 return 1917 if type(target) is type(''): 1918 return self.showtopic(target, more_xrefs) 1919 1920 label, xrefs = target 1921 try: 1922 doc = pydoc_data.topics.topics[label] 1923 except KeyError: 1924 self.output.write('no documentation found for %s\n' % repr(topic)) 1925 return 1926 pager(strip(doc) + '\n') 1927 if more_xrefs: 1928 xrefs = (xrefs or '') + ' ' + more_xrefs 1929 if xrefs: 1930 import StringIO, formatter 1931 buffer = StringIO.StringIO() 1932 formatter.DumbWriter(buffer).send_flowing_data( 1933 'Related help topics: ' + join(split(xrefs), ', ') + '\n') 1934 self.output.write('\n%s\n' % buffer.getvalue()) 1935 1936 def showsymbol(self, symbol): 1937 target = self.symbols[symbol] 1938 topic, _, xrefs = target.partition(' ') 1939 self.showtopic(topic, xrefs) 1940 1941 def listmodules(self, key=''): 1942 if key: 1943 self.output.write(''' 1944Here is a list of matching modules. Enter any module name to get more help. 1945 1946''') 1947 apropos(key) 1948 else: 1949 self.output.write(''' 1950Please wait a moment while I gather a list of all available modules... 1951 1952''') 1953 modules = {} 1954 def callback(path, modname, desc, modules=modules): 1955 if modname and modname[-9:] == '.__init__': 1956 modname = modname[:-9] + ' (package)' 1957 if find(modname, '.') < 0: 1958 modules[modname] = 1 1959 def onerror(modname): 1960 callback(None, modname, None) 1961 ModuleScanner().run(callback, onerror=onerror) 1962 self.list(modules.keys()) 1963 self.output.write(''' 1964Enter any module name to get more help. Or, type "modules spam" to search 1965for modules whose descriptions contain the word "spam". 1966''') 1967 1968help = Helper() 1969 1970class Scanner: 1971 """A generic tree iterator.""" 1972 def __init__(self, roots, children, descendp): 1973 self.roots = roots[:] 1974 self.state = [] 1975 self.children = children 1976 self.descendp = descendp 1977 1978 def next(self): 1979 if not self.state: 1980 if not self.roots: 1981 return None 1982 root = self.roots.pop(0) 1983 self.state = [(root, self.children(root))] 1984 node, children = self.state[-1] 1985 if not children: 1986 self.state.pop() 1987 return self.next() 1988 child = children.pop(0) 1989 if self.descendp(child): 1990 self.state.append((child, self.children(child))) 1991 return child 1992 1993 1994class ModuleScanner: 1995 """An interruptible scanner that searches module synopses.""" 1996 1997 def run(self, callback, key=None, completer=None, onerror=None): 1998 if key: key = lower(key) 1999 self.quit = False 2000 seen = {} 2001 2002 for modname in sys.builtin_module_names: 2003 if modname != '__main__': 2004 seen[modname] = 1 2005 if key is None: 2006 callback(None, modname, '') 2007 else: 2008 desc = split(__import__(modname).__doc__ or '', '\n')[0] 2009 if find(lower(modname + ' - ' + desc), key) >= 0: 2010 callback(None, modname, desc) 2011 2012 for importer, modname, ispkg in pkgutil.walk_packages(onerror=onerror): 2013 if self.quit: 2014 break 2015 if key is None: 2016 callback(None, modname, '') 2017 else: 2018 loader = importer.find_module(modname) 2019 if hasattr(loader,'get_source'): 2020 import StringIO 2021 desc = source_synopsis( 2022 StringIO.StringIO(loader.get_source(modname)) 2023 ) or '' 2024 if hasattr(loader,'get_filename'): 2025 path = loader.get_filename(modname) 2026 else: 2027 path = None 2028 else: 2029 module = loader.load_module(modname) 2030 desc = module.__doc__.splitlines()[0] if module.__doc__ else '' 2031 path = getattr(module,'__file__',None) 2032 if find(lower(modname + ' - ' + desc), key) >= 0: 2033 callback(path, modname, desc) 2034 2035 if completer: 2036 completer() 2037 2038def apropos(key): 2039 """Print all the one-line module summaries that contain a substring.""" 2040 def callback(path, modname, desc): 2041 if modname[-9:] == '.__init__': 2042 modname = modname[:-9] + ' (package)' 2043 print modname, desc and '- ' + desc 2044 def onerror(modname): 2045 pass 2046 with warnings.catch_warnings(): 2047 warnings.filterwarnings('ignore') # ignore problems during import 2048 ModuleScanner().run(callback, key, onerror=onerror) 2049 2050# --------------------------------------------------- web browser interface 2051 2052def serve(port, callback=None, completer=None): 2053 import BaseHTTPServer, mimetools, select 2054 2055 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded. 2056 class Message(mimetools.Message): 2057 def __init__(self, fp, seekable=1): 2058 Message = self.__class__ 2059 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable) 2060 self.encodingheader = self.getheader('content-transfer-encoding') 2061 self.typeheader = self.getheader('content-type') 2062 self.parsetype() 2063 self.parseplist() 2064 2065 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler): 2066 def send_document(self, title, contents): 2067 try: 2068 self.send_response(200) 2069 self.send_header('Content-Type', 'text/html') 2070 self.end_headers() 2071 self.wfile.write(html.page(title, contents)) 2072 except IOError: pass 2073 2074 def do_GET(self): 2075 path = self.path 2076 if path[-5:] == '.html': path = path[:-5] 2077 if path[:1] == '/': path = path[1:] 2078 if path and path != '.': 2079 try: 2080 obj = locate(path, forceload=1) 2081 except ErrorDuringImport, value: 2082 self.send_document(path, html.escape(str(value))) 2083 return 2084 if obj: 2085 self.send_document(describe(obj), html.document(obj, path)) 2086 else: 2087 self.send_document(path, 2088'no Python documentation found for %s' % repr(path)) 2089 else: 2090 heading = html.heading( 2091'<big><big><strong>Python: Index of Modules</strong></big></big>', 2092'#ffffff', '#7799ee') 2093 def bltinlink(name): 2094 return '<a href="%s.html">%s</a>' % (name, name) 2095 names = filter(lambda x: x != '__main__', 2096 sys.builtin_module_names) 2097 contents = html.multicolumn(names, bltinlink) 2098 indices = ['<p>' + html.bigsection( 2099 'Built-in Modules', '#ffffff', '#ee77aa', contents)] 2100 2101 seen = {} 2102 for dir in sys.path: 2103 indices.append(html.index(dir, seen)) 2104 contents = heading + join(indices) + '''<p align=right> 2105<font color="#909090" face="helvetica, arial"><strong> 2106pydoc</strong> by Ka-Ping Yee <ping@lfw.org></font>''' 2107 self.send_document('Index of Modules', contents) 2108 2109 def log_message(self, *args): pass 2110 2111 class DocServer(BaseHTTPServer.HTTPServer): 2112 def __init__(self, port, callback): 2113 host = 'localhost' 2114 self.address = (host, port) 2115 self.callback = callback 2116 self.base.__init__(self, self.address, self.handler) 2117 2118 def serve_until_quit(self): 2119 import select 2120 self.quit = False 2121 while not self.quit: 2122 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1) 2123 if rd: self.handle_request() 2124 2125 def server_activate(self): 2126 self.base.server_activate(self) 2127 self.url = 'http://%s:%d/' % (self.address[0], self.server_port) 2128 if self.callback: self.callback(self) 2129 2130 DocServer.base = BaseHTTPServer.HTTPServer 2131 DocServer.handler = DocHandler 2132 DocHandler.MessageClass = Message 2133 try: 2134 try: 2135 DocServer(port, callback).serve_until_quit() 2136 except (KeyboardInterrupt, select.error): 2137 pass 2138 finally: 2139 if completer: completer() 2140 2141# ----------------------------------------------------- graphical interface 2142 2143def gui(): 2144 """Graphical interface (starts web server and pops up a control window).""" 2145 class GUI: 2146 def __init__(self, window, port=7464): 2147 self.window = window 2148 self.server = None 2149 self.scanner = None 2150 2151 import Tkinter 2152 self.server_frm = Tkinter.Frame(window) 2153 self.title_lbl = Tkinter.Label(self.server_frm, 2154 text='Starting server...\n ') 2155 self.open_btn = Tkinter.Button(self.server_frm, 2156 text='open browser', command=self.open, state='disabled') 2157 self.quit_btn = Tkinter.Button(self.server_frm, 2158 text='quit serving', command=self.quit, state='disabled') 2159 2160 self.search_frm = Tkinter.Frame(window) 2161 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for') 2162 self.search_ent = Tkinter.Entry(self.search_frm) 2163 self.search_ent.bind('<Return>', self.search) 2164 self.stop_btn = Tkinter.Button(self.search_frm, 2165 text='stop', pady=0, command=self.stop, state='disabled') 2166 if sys.platform == 'win32': 2167 # Trying to hide and show this button crashes under Windows. 2168 self.stop_btn.pack(side='right') 2169 2170 self.window.title('pydoc') 2171 self.window.protocol('WM_DELETE_WINDOW', self.quit) 2172 self.title_lbl.pack(side='top', fill='x') 2173 self.open_btn.pack(side='left', fill='x', expand=1) 2174 self.quit_btn.pack(side='right', fill='x', expand=1) 2175 self.server_frm.pack(side='top', fill='x') 2176 2177 self.search_lbl.pack(side='left') 2178 self.search_ent.pack(side='right', fill='x', expand=1) 2179 self.search_frm.pack(side='top', fill='x') 2180 self.search_ent.focus_set() 2181 2182 font = ('helvetica', sys.platform == 'win32' and 8 or 10) 2183 self.result_lst = Tkinter.Listbox(window, font=font, height=6) 2184 self.result_lst.bind('<Button-1>', self.select) 2185 self.result_lst.bind('<Double-Button-1>', self.goto) 2186 self.result_scr = Tkinter.Scrollbar(window, 2187 orient='vertical', command=self.result_lst.yview) 2188 self.result_lst.config(yscrollcommand=self.result_scr.set) 2189 2190 self.result_frm = Tkinter.Frame(window) 2191 self.goto_btn = Tkinter.Button(self.result_frm, 2192 text='go to selected', command=self.goto) 2193 self.hide_btn = Tkinter.Button(self.result_frm, 2194 text='hide results', command=self.hide) 2195 self.goto_btn.pack(side='left', fill='x', expand=1) 2196 self.hide_btn.pack(side='right', fill='x', expand=1) 2197 2198 self.window.update() 2199 self.minwidth = self.window.winfo_width() 2200 self.minheight = self.window.winfo_height() 2201 self.bigminheight = (self.server_frm.winfo_reqheight() + 2202 self.search_frm.winfo_reqheight() + 2203 self.result_lst.winfo_reqheight() + 2204 self.result_frm.winfo_reqheight()) 2205 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight 2206 self.expanded = 0 2207 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight)) 2208 self.window.wm_minsize(self.minwidth, self.minheight) 2209 self.window.tk.willdispatch() 2210 2211 import threading 2212 threading.Thread( 2213 target=serve, args=(port, self.ready, self.quit)).start() 2214 2215 def ready(self, server): 2216 self.server = server 2217 self.title_lbl.config( 2218 text='Python documentation server at\n' + server.url) 2219 self.open_btn.config(state='normal') 2220 self.quit_btn.config(state='normal') 2221 2222 def open(self, event=None, url=None): 2223 url = url or self.server.url 2224 try: 2225 import webbrowser 2226 webbrowser.open(url) 2227 except ImportError: # pre-webbrowser.py compatibility 2228 if sys.platform == 'win32': 2229 os.system('start "%s"' % url) 2230 else: 2231 rc = os.system('netscape -remote "openURL(%s)" &' % url) 2232 if rc: os.system('netscape "%s" &' % url) 2233 2234 def quit(self, event=None): 2235 if self.server: 2236 self.server.quit = 1 2237 self.window.quit() 2238 2239 def search(self, event=None): 2240 key = self.search_ent.get() 2241 self.stop_btn.pack(side='right') 2242 self.stop_btn.config(state='normal') 2243 self.search_lbl.config(text='Searching for "%s"...' % key) 2244 self.search_ent.forget() 2245 self.search_lbl.pack(side='left') 2246 self.result_lst.delete(0, 'end') 2247 self.goto_btn.config(state='disabled') 2248 self.expand() 2249 2250 import threading 2251 if self.scanner: 2252 self.scanner.quit = 1 2253 self.scanner = ModuleScanner() 2254 def onerror(modname): 2255 pass 2256 threading.Thread(target=self.scanner.run, 2257 args=(self.update, key, self.done), 2258 kwargs=dict(onerror=onerror)).start() 2259 2260 def update(self, path, modname, desc): 2261 if modname[-9:] == '.__init__': 2262 modname = modname[:-9] + ' (package)' 2263 self.result_lst.insert('end', 2264 modname + ' - ' + (desc or '(no description)')) 2265 2266 def stop(self, event=None): 2267 if self.scanner: 2268 self.scanner.quit = 1 2269 self.scanner = None 2270 2271 def done(self): 2272 self.scanner = None 2273 self.search_lbl.config(text='Search for') 2274 self.search_lbl.pack(side='left') 2275 self.search_ent.pack(side='right', fill='x', expand=1) 2276 if sys.platform != 'win32': self.stop_btn.forget() 2277 self.stop_btn.config(state='disabled') 2278 2279 def select(self, event=None): 2280 self.goto_btn.config(state='normal') 2281 2282 def goto(self, event=None): 2283 selection = self.result_lst.curselection() 2284 if selection: 2285 modname = split(self.result_lst.get(selection[0]))[0] 2286 self.open(url=self.server.url + modname + '.html') 2287 2288 def collapse(self): 2289 if not self.expanded: return 2290 self.result_frm.forget() 2291 self.result_scr.forget() 2292 self.result_lst.forget() 2293 self.bigwidth = self.window.winfo_width() 2294 self.bigheight = self.window.winfo_height() 2295 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight)) 2296 self.window.wm_minsize(self.minwidth, self.minheight) 2297 self.expanded = 0 2298 2299 def expand(self): 2300 if self.expanded: return 2301 self.result_frm.pack(side='bottom', fill='x') 2302 self.result_scr.pack(side='right', fill='y') 2303 self.result_lst.pack(side='top', fill='both', expand=1) 2304 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight)) 2305 self.window.wm_minsize(self.minwidth, self.bigminheight) 2306 self.expanded = 1 2307 2308 def hide(self, event=None): 2309 self.stop() 2310 self.collapse() 2311 2312 import Tkinter 2313 try: 2314 root = Tkinter.Tk() 2315 # Tk will crash if pythonw.exe has an XP .manifest 2316 # file and the root has is not destroyed explicitly. 2317 # If the problem is ever fixed in Tk, the explicit 2318 # destroy can go. 2319 try: 2320 gui = GUI(root) 2321 root.mainloop() 2322 finally: 2323 root.destroy() 2324 except KeyboardInterrupt: 2325 pass 2326 2327# -------------------------------------------------- command-line interface 2328 2329def ispath(x): 2330 return isinstance(x, str) and find(x, os.sep) >= 0 2331 2332def cli(): 2333 """Command-line interface (looks at sys.argv to decide what to do).""" 2334 import getopt 2335 class BadUsage: pass 2336 2337 # Scripts don't get the current directory in their path by default 2338 # unless they are run with the '-m' switch 2339 if '' not in sys.path: 2340 scriptdir = os.path.dirname(sys.argv[0]) 2341 if scriptdir in sys.path: 2342 sys.path.remove(scriptdir) 2343 sys.path.insert(0, '.') 2344 2345 try: 2346 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w') 2347 writing = 0 2348 2349 for opt, val in opts: 2350 if opt == '-g': 2351 gui() 2352 return 2353 if opt == '-k': 2354 apropos(val) 2355 return 2356 if opt == '-p': 2357 try: 2358 port = int(val) 2359 except ValueError: 2360 raise BadUsage 2361 def ready(server): 2362 print 'pydoc server ready at %s' % server.url 2363 def stopped(): 2364 print 'pydoc server stopped' 2365 serve(port, ready, stopped) 2366 return 2367 if opt == '-w': 2368 writing = 1 2369 2370 if not args: raise BadUsage 2371 for arg in args: 2372 if ispath(arg) and not os.path.exists(arg): 2373 print 'file %r does not exist' % arg 2374 break 2375 try: 2376 if ispath(arg) and os.path.isfile(arg): 2377 arg = importfile(arg) 2378 if writing: 2379 if ispath(arg) and os.path.isdir(arg): 2380 writedocs(arg) 2381 else: 2382 writedoc(arg) 2383 else: 2384 help.help(arg) 2385 except ErrorDuringImport, value: 2386 print value 2387 2388 except (getopt.error, BadUsage): 2389 cmd = os.path.basename(sys.argv[0]) 2390 print """pydoc - the Python documentation tool 2391 2392%s <name> ... 2393 Show text documentation on something. <name> may be the name of a 2394 Python keyword, topic, function, module, or package, or a dotted 2395 reference to a class or function within a module or module in a 2396 package. If <name> contains a '%s', it is used as the path to a 2397 Python source file to document. If name is 'keywords', 'topics', 2398 or 'modules', a listing of these things is displayed. 2399 2400%s -k <keyword> 2401 Search for a keyword in the synopsis lines of all available modules. 2402 2403%s -p <port> 2404 Start an HTTP server on the given port on the local machine. Port 2405 number 0 can be used to get an arbitrary unused port. 2406 2407%s -g 2408 Pop up a graphical interface for finding and serving documentation. 2409 2410%s -w <name> ... 2411 Write out the HTML documentation for a module to a file in the current 2412 directory. If <name> contains a '%s', it is treated as a filename; if 2413 it names a directory, documentation is written for all the contents. 2414""" % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep) 2415 2416if __name__ == '__main__': cli() 2417