0001 #!/usr/bin/env python 0002 # -*- coding: Latin-1 -*- 0003 """Generate Python documentation in HTML or text for interactive use. 0004 0005 In the Python interpreter, do "from pydoc import help" to provide online 0006 help. Calling help(thing) on a Python object documents the object. 0007 0008 Or, at the shell command line outside of Python: 0009 0010 Run "pydoc <name>" to show documentation on something. <name> may be 0011 the name of a function, module, package, or a dotted reference to a 0012 class or function within a module or module in a package. If the 0013 argument contains a path segment delimiter (e.g. slash on Unix, 0014 backslash on Windows) it is treated as the path to a Python source file. 0015 0016 Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines 0017 of all available modules. 0018 0019 Run "pydoc -p <port>" to start an HTTP server on a given port on the 0020 local machine to generate documentation web pages. 0021 0022 For platforms without a command line, "pydoc -g" starts the HTTP server 0023 and also pops up a little window for controlling it. 0024 0025 Run "pydoc -w <name>" to write out the HTML documentation for a module 0026 to a file named "<name>.html". 0027 0028 Module docs for core modules are assumed to be in 0029 0030 http://www.python.org/doc/current/lib/ 0031 0032 This can be overridden by setting the PYTHONDOCS environment variable 0033 to a different URL or to a local directory containing the Library 0034 Reference Manual pages. 0035 """ 0036 0037 __author__ = "Ka-Ping Yee <ping@lfw.org>" 0038 __date__ = "26 February 2001" 0039 __version__ = "$Revision: 1.98 $" 0040 __credits__ = u"""Guido van Rossum, for an excellent programming language. 0041 Tommy Burnette, the original creator of manpy. 0042 Paul Prescod, for all his work on onlinehelp. 0043 Richard Chamberlain, for the first implementation of textdoc. 0044 0045 Mynd you, møøse bites Kan be pretty nasti...""" 0046 0047 # Known bugs that can't be fixed here: 0048 # - imp.load_module() cannot be prevented from clobbering existing 0049 # loaded modules, so calling synopsis() on a binary module file 0050 # changes the contents of any existing module with the same name. 0051 # - If the __file__ attribute on a module is a relative path and 0052 # the current directory is changed with os.chdir(), an incorrect 0053 # path will be displayed. 0054 0055 import sys, imp, os, re, types, inspect, __builtin__ 0056 from repr import Repr 0057 from string import expandtabs, find, join, lower, split, strip, rfind, rstrip 0058 from collections import deque 0059 0060 # --------------------------------------------------------- common routines 0061 0062 def pathdirs(): 0063 """Convert sys.path into a list of absolute, existing, unique paths.""" 0064 dirs = [] 0065 normdirs = [] 0066 for dir in sys.path: 0067 dir = os.path.abspath(dir or '.') 0068 normdir = os.path.normcase(dir) 0069 if normdir not in normdirs and os.path.isdir(dir): 0070 dirs.append(dir) 0071 normdirs.append(normdir) 0072 return dirs 0073 0074 def getdoc(object): 0075 """Get the doc string or comments for an object.""" 0076 result = inspect.getdoc(object) or inspect.getcomments(object) 0077 return result and re.sub('^ *\n', '', rstrip(result)) or '' 0078 0079 def splitdoc(doc): 0080 """Split a doc string into a synopsis line (if any) and the rest.""" 0081 lines = split(strip(doc), '\n') 0082 if len(lines) == 1: 0083 return lines[0], '' 0084 elif len(lines) >= 2 and not rstrip(lines[1]): 0085 return lines[0], join(lines[2:], '\n') 0086 return '', join(lines, '\n') 0087 0088 def classname(object, modname): 0089 """Get a class name and qualify it with a module name if necessary.""" 0090 name = object.__name__ 0091 if object.__module__ != modname: 0092 name = object.__module__ + '.' + name 0093 return name 0094 0095 def isdata(object): 0096 """Check if an object is of a type that probably means it's data.""" 0097 return not (inspect.ismodule(object) or inspect.isclass(object) or 0098 inspect.isroutine(object) or inspect.isframe(object) or 0099 inspect.istraceback(object) or inspect.iscode(object)) 0100 0101 def replace(text, *pairs): 0102 """Do a series of global replacements on a string.""" 0103 while pairs: 0104 text = join(split(text, pairs[0]), pairs[1]) 0105 pairs = pairs[2:] 0106 return text 0107 0108 def cram(text, maxlen): 0109 """Omit part of a string if needed to make it fit in a maximum length.""" 0110 if len(text) > maxlen: 0111 pre = max(0, (maxlen-3)//2) 0112 post = max(0, maxlen-3-pre) 0113 return text[:pre] + '...' + text[len(text)-post:] 0114 return text 0115 0116 _re_stripid = re.compile(r' at 0x[0-9a-f]{6,16}(>+)$', re.IGNORECASE) 0117 def stripid(text): 0118 """Remove the hexadecimal id from a Python object representation.""" 0119 # The behaviour of %p is implementation-dependent in terms of case. 0120 if _re_stripid.search(repr(Exception)): 0121 return _re_stripid.sub(r'\1', text) 0122 return text 0123 0124 def _is_some_method(obj): 0125 return inspect.ismethod(obj) or inspect.ismethoddescriptor(obj) 0126 0127 def allmethods(cl): 0128 methods = {} 0129 for key, value in inspect.getmembers(cl, _is_some_method): 0130 methods[key] = 1 0131 for base in cl.__bases__: 0132 methods.update(allmethods(base)) # all your base are belong to us 0133 for key in methods.keys(): 0134 methods[key] = getattr(cl, key) 0135 return methods 0136 0137 def _split_list(s, predicate): 0138 """Split sequence s via predicate, and return pair ([true], [false]). 0139 0140 The return value is a 2-tuple of lists, 0141 ([x for x in s if predicate(x)], 0142 [x for x in s if not predicate(x)]) 0143 """ 0144 0145 yes = [] 0146 no = [] 0147 for x in s: 0148 if predicate(x): 0149 yes.append(x) 0150 else: 0151 no.append(x) 0152 return yes, no 0153 0154 def visiblename(name, all=None): 0155 """Decide whether to show documentation on a variable.""" 0156 # Certain special names are redundant. 0157 if name in ['__builtins__', '__doc__', '__file__', '__path__', 0158 '__module__', '__name__']: return 0 0159 # Private names are hidden, but special names are displayed. 0160 if name.startswith('__') and name.endswith('__'): return 1 0161 if all is not None: 0162 # only document that which the programmer exported in __all__ 0163 return name in all 0164 else: 0165 return not name.startswith('_') 0166 0167 # ----------------------------------------------------- module manipulation 0168 0169 def ispackage(path): 0170 """Guess whether a path refers to a package directory.""" 0171 if os.path.isdir(path): 0172 for ext in ['.py', '.pyc', '.pyo']: 0173 if os.path.isfile(os.path.join(path, '__init__' + ext)): 0174 return True 0175 return False 0176 0177 def synopsis(filename, cache={}): 0178 """Get the one-line summary out of a module file.""" 0179 mtime = os.stat(filename).st_mtime 0180 lastupdate, result = cache.get(filename, (0, None)) 0181 if lastupdate < mtime: 0182 info = inspect.getmoduleinfo(filename) 0183 file = open(filename) 0184 if info and 'b' in info[2]: # binary modules have to be imported 0185 try: module = imp.load_module('__temp__', file, filename, info[1:]) 0186 except: return None 0187 result = split(module.__doc__ or '', '\n')[0] 0188 del sys.modules['__temp__'] 0189 else: # text modules can be directly examined 0190 line = file.readline() 0191 while line[:1] == '#' or not strip(line): 0192 line = file.readline() 0193 if not line: break 0194 line = strip(line) 0195 if line[:4] == 'r"""': line = line[1:] 0196 if line[:3] == '"""': 0197 line = line[3:] 0198 if line[-1:] == '\\': line = line[:-1] 0199 while not strip(line): 0200 line = file.readline() 0201 if not line: break 0202 result = strip(split(line, '"""')[0]) 0203 else: result = None 0204 file.close() 0205 cache[filename] = (mtime, result) 0206 return result 0207 0208 class ErrorDuringImport(Exception): 0209 """Errors that occurred while trying to import something to document it.""" 0210 def __init__(self, filename, (exc, value, tb)): 0211 self.filename = filename 0212 self.exc = exc 0213 self.value = value 0214 self.tb = tb 0215 0216 def __str__(self): 0217 exc = self.exc 0218 if type(exc) is types.ClassType: 0219 exc = exc.__name__ 0220 return 'problem in %s - %s: %s' % (self.filename, exc, self.value) 0221 0222 def importfile(path): 0223 """Import a Python source file or compiled file given its path.""" 0224 magic = imp.get_magic() 0225 file = open(path, 'r') 0226 if file.read(len(magic)) == magic: 0227 kind = imp.PY_COMPILED 0228 else: 0229 kind = imp.PY_SOURCE 0230 file.close() 0231 filename = os.path.basename(path) 0232 name, ext = os.path.splitext(filename) 0233 file = open(path, 'r') 0234 try: 0235 module = imp.load_module(name, file, path, (ext, 'r', kind)) 0236 except: 0237 raise ErrorDuringImport(path, sys.exc_info()) 0238 file.close() 0239 return module 0240 0241 def safeimport(path, forceload=0, cache={}): 0242 """Import a module; handle errors; return None if the module isn't found. 0243 0244 If the module *is* found but an exception occurs, it's wrapped in an 0245 ErrorDuringImport exception and reraised. Unlike __import__, if a 0246 package path is specified, the module at the end of the path is returned, 0247 not the package at the beginning. If the optional 'forceload' argument 0248 is 1, we reload the module from disk (unless it's a dynamic extension).""" 0249 if forceload and path in sys.modules: 0250 # This is the only way to be sure. Checking the mtime of the file 0251 # isn't good enough (e.g. what if the module contains a class that 0252 # inherits from another module that has changed?). 0253 if path not in sys.builtin_module_names: 0254 # Python never loads a dynamic extension a second time from the 0255 # same path, even if the file is changed or missing. Deleting 0256 # the entry in sys.modules doesn't help for dynamic extensions, 0257 # so we're not even going to try to keep them up to date. 0258 info = inspect.getmoduleinfo(sys.modules[path].__file__) 0259 if info[3] != imp.C_EXTENSION: 0260 cache[path] = sys.modules[path] # prevent module from clearing 0261 del sys.modules[path] 0262 try: 0263 module = __import__(path) 0264 except: 0265 # Did the error occur before or after the module was found? 0266 (exc, value, tb) = info = sys.exc_info() 0267 if path in sys.modules: 0268 # An error occured while executing the imported module. 0269 raise ErrorDuringImport(sys.modules[path].__file__, info) 0270 elif exc is SyntaxError: 0271 # A SyntaxError occurred before we could execute the module. 0272 raise ErrorDuringImport(value.filename, info) 0273 elif exc is ImportError and \ 0274 split(lower(str(value)))[:2] == ['no', 'module']: 0275 # The module was not found. 0276 return None 0277 else: 0278 # Some other error occurred during the importing process. 0279 raise ErrorDuringImport(path, sys.exc_info()) 0280 for part in split(path, '.')[1:]: 0281 try: module = getattr(module, part) 0282 except AttributeError: return None 0283 return module 0284 0285 # ---------------------------------------------------- formatter base class 0286 0287 class Doc: 0288 def document(self, object, name=None, *args): 0289 """Generate documentation for an object.""" 0290 args = (object, name) + args 0291 # 'try' clause is to attempt to handle the possibility that inspect 0292 # identifies something in a way that pydoc itself has issues handling; 0293 # think 'super' and how it is a descriptor (which raises the exception 0294 # by lacking a __name__ attribute) and an instance. 0295 try: 0296 if inspect.ismodule(object): return self.docmodule(*args) 0297 if inspect.isclass(object): return self.docclass(*args) 0298 if inspect.isroutine(object): return self.docroutine(*args) 0299 except AttributeError: 0300 pass 0301 return self.docother(*args) 0302 0303 def fail(self, object, name=None, *args): 0304 """Raise an exception for unimplemented types.""" 0305 message = "don't know how to document object%s of type %s" % ( 0306 name and ' ' + repr(name), type(object).__name__) 0307 raise TypeError, message 0308 0309 docmodule = docclass = docroutine = docother = fail 0310 0311 def getdocloc(self, object): 0312 """Return the location of module docs or None""" 0313 0314 try: 0315 file = inspect.getabsfile(object) 0316 except TypeError: 0317 file = '(built-in)' 0318 0319 docloc = os.environ.get("PYTHONDOCS", 0320 "http://www.python.org/doc/current/lib") 0321 basedir = os.path.join(sys.exec_prefix, "lib", 0322 "python"+sys.version[0:3]) 0323 if (isinstance(object, type(os)) and 0324 (object.__name__ in ('errno', 'exceptions', 'gc', 'imp', 0325 'marshal', 'posix', 'signal', 'sys', 0326 'thread', 'zipimport') or 0327 (file.startswith(basedir) and 0328 not file.startswith(os.path.join(basedir, 'site-packages'))))): 0329 htmlfile = "module-%s.html" % object.__name__ 0330 if docloc.startswith("http://"): 0331 docloc = "%s/%s" % (docloc.rstrip("/"), htmlfile) 0332 else: 0333 docloc = os.path.join(docloc, htmlfile) 0334 else: 0335 docloc = None 0336 return docloc 0337 0338 # -------------------------------------------- HTML documentation generator 0339 0340 class HTMLRepr(Repr): 0341 """Class for safely making an HTML representation of a Python object.""" 0342 def __init__(self): 0343 Repr.__init__(self) 0344 self.maxlist = self.maxtuple = 20 0345 self.maxdict = 10 0346 self.maxstring = self.maxother = 100 0347 0348 def escape(self, text): 0349 return replace(text, '&', '&', '<', '<', '>', '>') 0350 0351 def repr(self, object): 0352 return Repr.repr(self, object) 0353 0354 def repr1(self, x, level): 0355 if hasattr(type(x), '__name__'): 0356 methodname = 'repr_' + join(split(type(x).__name__), '_') 0357 if hasattr(self, methodname): 0358 return getattr(self, methodname)(x, level) 0359 return self.escape(cram(stripid(repr(x)), self.maxother)) 0360 0361 def repr_string(self, x, level): 0362 test = cram(x, self.maxstring) 0363 testrepr = repr(test) 0364 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''): 0365 # Backslashes are only literal in the string and are never 0366 # needed to make any special characters, so show a raw string. 0367 return 'r' + testrepr[0] + self.escape(test) + testrepr[0] 0368 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)', 0369 r'<font color="#c040c0">\1</font>', 0370 self.escape(testrepr)) 0371 0372 repr_str = repr_string 0373 0374 def repr_instance(self, x, level): 0375 try: 0376 return self.escape(cram(stripid(repr(x)), self.maxstring)) 0377 except: 0378 return self.escape('<%s instance>' % x.__class__.__name__) 0379 0380 repr_unicode = repr_string 0381 0382 class HTMLDoc(Doc): 0383 """Formatter class for HTML documentation.""" 0384 0385 # ------------------------------------------- HTML formatting utilities 0386 0387 _repr_instance = HTMLRepr() 0388 repr = _repr_instance.repr 0389 escape = _repr_instance.escape 0390 0391 def page(self, title, contents): 0392 """Format an HTML page.""" 0393 return ''' 0394 <!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 0395 <html><head><title>Python: %s</title> 0396 </head><body bgcolor="#f0f0f8"> 0397 %s 0398 </body></html>''' % (title, contents) 0399 0400 def heading(self, title, fgcol, bgcol, extras=''): 0401 """Format a page heading.""" 0402 return ''' 0403 <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading"> 0404 <tr bgcolor="%s"> 0405 <td valign=bottom> <br> 0406 <font color="%s" face="helvetica, arial"> <br>%s</font></td 0407 ><td align=right valign=bottom 0408 ><font color="%s" face="helvetica, arial">%s</font></td></tr></table> 0409 ''' % (bgcol, fgcol, title, fgcol, extras or ' ') 0410 0411 def section(self, title, fgcol, bgcol, contents, width=6, 0412 prelude='', marginalia=None, gap=' '): 0413 """Format a section with a heading.""" 0414 if marginalia is None: 0415 marginalia = '<tt>' + ' ' * width + '</tt>' 0416 result = '''<p> 0417 <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section"> 0418 <tr bgcolor="%s"> 0419 <td colspan=3 valign=bottom> <br> 0420 <font color="%s" face="helvetica, arial">%s</font></td></tr> 0421 ''' % (bgcol, fgcol, title) 0422 if prelude: 0423 result = result + ''' 0424 <tr bgcolor="%s"><td rowspan=2>%s</td> 0425 <td colspan=2>%s</td></tr> 0426 <tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap) 0427 else: 0428 result = result + ''' 0429 <tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap) 0430 0431 return result + '\n<td width="100%%">%s</td></tr></table>' % contents 0432 0433 def bigsection(self, title, *args): 0434 """Format a section with a big heading.""" 0435 title = '<big><strong>%s</strong></big>' % title 0436 return self.section(title, *args) 0437 0438 def preformat(self, text): 0439 """Format literal preformatted text.""" 0440 text = self.escape(expandtabs(text)) 0441 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n', 0442 ' ', ' ', '\n', '<br>\n') 0443 0444 def multicolumn(self, list, format, cols=4): 0445 """Format a list of items into a multi-column list.""" 0446 result = '' 0447 rows = (len(list)+cols-1)/cols 0448 for col in range(cols): 0449 result = result + '<td width="%d%%" valign=top>' % (100/cols) 0450 for i in range(rows*col, rows*col+rows): 0451 if i < len(list): 0452 result = result + format(list[i]) + '<br>\n' 0453 result = result + '</td>' 0454 return '<table width="100%%" summary="list"><tr>%s</tr></table>' % result 0455 0456 def grey(self, text): return '<font color="#909090">%s</font>' % text 0457 0458 def namelink(self, name, *dicts): 0459 """Make a link for an identifier, given name-to-URL mappings.""" 0460 for dict in dicts: 0461 if name in dict: 0462 return '<a href="%s">%s</a>' % (dict[name], name) 0463 return name 0464 0465 def classlink(self, object, modname): 0466 """Make a link for a class.""" 0467 name, module = object.__name__, sys.modules.get(object.__module__) 0468 if hasattr(module, name) and getattr(module, name) is object: 0469 return '<a href="%s.html#%s">%s</a>' % ( 0470 module.__name__, name, classname(object, modname)) 0471 return classname(object, modname) 0472 0473 def modulelink(self, object): 0474 """Make a link for a module.""" 0475 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__) 0476 0477 def modpkglink(self, (name, path, ispackage, shadowed)): 0478 """Make a link for a module or package to display in an index.""" 0479 if shadowed: 0480 return self.grey(name) 0481 if path: 0482 url = '%s.%s.html' % (path, name) 0483 else: 0484 url = '%s.html' % name 0485 if ispackage: 0486 text = '<strong>%s</strong> (package)' % name 0487 else: 0488 text = name 0489 return '<a href="%s">%s</a>' % (url, text) 0490 0491 def markup(self, text, escape=None, funcs={}, classes={}, methods={}): 0492 """Mark up some plain text, given a context of symbols to look for. 0493 Each context dictionary maps object names to anchor names.""" 0494 escape = escape or self.escape 0495 results = [] 0496 here = 0 0497 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|' 0498 r'RFC[- ]?(\d+)|' 0499 r'PEP[- ]?(\d+)|' 0500 r'(self\.)?(\w+))') 0501 while True: 0502 match = pattern.search(text, here) 0503 if not match: break 0504 start, end = match.span() 0505 results.append(escape(text[here:start])) 0506 0507 all, scheme, rfc, pep, selfdot, name = match.groups() 0508 if scheme: 0509 url = escape(all).replace('"', '"') 0510 results.append('<a href="%s">%s</a>' % (url, url)) 0511 elif rfc: 0512 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc) 0513 results.append('<a href="%s">%s</a>' % (url, escape(all))) 0514 elif pep: 0515 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep) 0516 results.append('<a href="%s">%s</a>' % (url, escape(all))) 0517 elif text[end:end+1] == '(': 0518 results.append(self.namelink(name, methods, funcs, classes)) 0519 elif selfdot: 0520 results.append('self.<strong>%s</strong>' % name) 0521 else: 0522 results.append(self.namelink(name, classes)) 0523 here = end 0524 results.append(escape(text[here:])) 0525 return join(results, '') 0526 0527 # ---------------------------------------------- type-specific routines 0528 0529 def formattree(self, tree, modname, parent=None): 0530 """Produce HTML for a class tree as given by inspect.getclasstree().""" 0531 result = '' 0532 for entry in tree: 0533 if type(entry) is type(()): 0534 c, bases = entry 0535 result = result + '<dt><font face="helvetica, arial">' 0536 result = result + self.classlink(c, modname) 0537 if bases and bases != (parent,): 0538 parents = [] 0539 for base in bases: 0540 parents.append(self.classlink(base, modname)) 0541 result = result + '(' + join(parents, ', ') + ')' 0542 result = result + '\n</font></dt>' 0543 elif type(entry) is type([]): 0544 result = result + '<dd>\n%s</dd>\n' % self.formattree( 0545 entry, modname, c) 0546 return '<dl>\n%s</dl>\n' % result 0547 0548 def docmodule(self, object, name=None, mod=None, *ignored): 0549 """Produce HTML documentation for a module object.""" 0550 name = object.__name__ # ignore the passed-in name 0551 try: 0552 all = object.__all__ 0553 except AttributeError: 0554 all = None 0555 parts = split(name, '.') 0556 links = [] 0557 for i in range(len(parts)-1): 0558 links.append( 0559 '<a href="%s.html"><font color="#ffffff">%s</font></a>' % 0560 (join(parts[:i+1], '.'), parts[i])) 0561 linkedname = join(links + parts[-1:], '.') 0562 head = '<big><big><strong>%s</strong></big></big>' % linkedname 0563 try: 0564 path = inspect.getabsfile(object) 0565 url = path 0566 if sys.platform == 'win32': 0567 import nturl2path 0568 url = nturl2path.pathname2url(path) 0569 filelink = '<a href="file:%s">%s</a>' % (url, path) 0570 except TypeError: 0571 filelink = '(built-in)' 0572 info = [] 0573 if hasattr(object, '__version__'): 0574 version = str(object.__version__) 0575 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$': 0576 version = strip(version[11:-1]) 0577 info.append('version %s' % self.escape(version)) 0578 if hasattr(object, '__date__'): 0579 info.append(self.escape(str(object.__date__))) 0580 if info: 0581 head = head + ' (%s)' % join(info, ', ') 0582 docloc = self.getdocloc(object) 0583 if docloc is not None: 0584 docloc = '<br><a href="%(docloc)s">Module Docs</a>' % locals() 0585 else: 0586 docloc = '' 0587 result = self.heading( 0588 head, '#ffffff', '#7799ee', 0589 '<a href=".">index</a><br>' + filelink + docloc) 0590 0591 modules = inspect.getmembers(object, inspect.ismodule) 0592 0593 classes, cdict = [], {} 0594 for key, value in inspect.getmembers(object, inspect.isclass): 0595 # if __all__ exists, believe it. Otherwise use old heuristic. 0596 if (all is not None or 0597 (inspect.getmodule(value) or object) is object): 0598 if visiblename(key, all): 0599 classes.append((key, value)) 0600 cdict[key] = cdict[value] = '#' + key 0601 for key, value in classes: 0602 for base in value.__bases__: 0603 key, modname = base.__name__, base.__module__ 0604 module = sys.modules.get(modname) 0605 if modname != name and module and hasattr(module, key): 0606 if getattr(module, key) is base: 0607 if not key in cdict: 0608 cdict[key] = cdict[base] = modname + '.html#' + key 0609 funcs, fdict = [], {} 0610 for key, value in inspect.getmembers(object, inspect.isroutine): 0611 # if __all__ exists, believe it. Otherwise use old heuristic. 0612 if (all is not None or 0613 inspect.isbuiltin(value) or inspect.getmodule(value) is object): 0614 if visiblename(key, all): 0615 funcs.append((key, value)) 0616 fdict[key] = '#-' + key 0617 if inspect.isfunction(value): fdict[value] = fdict[key] 0618 data = [] 0619 for key, value in inspect.getmembers(object, isdata): 0620 if visiblename(key, all): 0621 data.append((key, value)) 0622 0623 doc = self.markup(getdoc(object), self.preformat, fdict, cdict) 0624 doc = doc and '<tt>%s</tt>' % doc 0625 result = result + '<p>%s</p>\n' % doc 0626 0627 if hasattr(object, '__path__'): 0628 modpkgs = [] 0629 modnames = [] 0630 for file in os.listdir(object.__path__[0]): 0631 path = os.path.join(object.__path__[0], file) 0632 modname = inspect.getmodulename(file) 0633 if modname != '__init__': 0634 if modname and modname not in modnames: 0635 modpkgs.append((modname, name, 0, 0)) 0636 modnames.append(modname) 0637 elif ispackage(path): 0638 modpkgs.append((file, name, 1, 0)) 0639 modpkgs.sort() 0640 contents = self.multicolumn(modpkgs, self.modpkglink) 0641 result = result + self.bigsection( 0642 'Package Contents', '#ffffff', '#aa55cc', contents) 0643 elif modules: 0644 contents = self.multicolumn( 0645 modules, lambda (key, value), s=self: s.modulelink(value)) 0646 result = result + self.bigsection( 0647 'Modules', '#fffff', '#aa55cc', contents) 0648 0649 if classes: 0650 classlist = map(lambda (key, value): value, classes) 0651 contents = [ 0652 self.formattree(inspect.getclasstree(classlist, 1), name)] 0653 for key, value in classes: 0654 contents.append(self.document(value, key, name, fdict, cdict)) 0655 result = result + self.bigsection( 0656 'Classes', '#ffffff', '#ee77aa', join(contents)) 0657 if funcs: 0658 contents = [] 0659 for key, value in funcs: 0660 contents.append(self.document(value, key, name, fdict, cdict)) 0661 result = result + self.bigsection( 0662 'Functions', '#ffffff', '#eeaa77', join(contents)) 0663 if data: 0664 contents = [] 0665 for key, value in data: 0666 contents.append(self.document(value, key)) 0667 result = result + self.bigsection( 0668 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n')) 0669 if hasattr(object, '__author__'): 0670 contents = self.markup(str(object.__author__), self.preformat) 0671 result = result + self.bigsection( 0672 'Author', '#ffffff', '#7799ee', contents) 0673 if hasattr(object, '__credits__'): 0674 contents = self.markup(str(object.__credits__), self.preformat) 0675 result = result + self.bigsection( 0676 'Credits', '#ffffff', '#7799ee', contents) 0677 0678 return result 0679 0680 def docclass(self, object, name=None, mod=None, funcs={}, classes={}, 0681 *ignored): 0682 """Produce HTML documentation for a class object.""" 0683 realname = object.__name__ 0684 name = name or realname 0685 bases = object.__bases__ 0686 0687 contents = [] 0688 push = contents.append 0689 0690 # Cute little class to pump out a horizontal rule between sections. 0691 class HorizontalRule: 0692 def __init__(self): 0693 self.needone = 0 0694 def maybe(self): 0695 if self.needone: 0696 push('<hr>\n') 0697 self.needone = 1 0698 hr = HorizontalRule() 0699 0700 # List the mro, if non-trivial. 0701 mro = deque(inspect.getmro(object)) 0702 if len(mro) > 2: 0703 hr.maybe() 0704 push('<dl><dt>Method resolution order:</dt>\n') 0705 for base in mro: 0706 push('<dd>%s</dd>\n' % self.classlink(base, 0707 object.__module__)) 0708 push('</dl>\n') 0709 0710 def spill(msg, attrs, predicate): 0711 ok, attrs = _split_list(attrs, predicate) 0712 if ok: 0713 hr.maybe() 0714 push(msg) 0715 for name, kind, homecls, value in ok: 0716 push(self.document(getattr(object, name), name, mod, 0717 funcs, classes, mdict, object)) 0718 push('\n') 0719 return attrs 0720 0721 def spillproperties(msg, attrs, predicate): 0722 ok, attrs = _split_list(attrs, predicate) 0723 if ok: 0724 hr.maybe() 0725 push(msg) 0726 for name, kind, homecls, value in ok: 0727 push('<dl><dt><strong>%s</strong></dt>\n' % name) 0728 if value.__doc__ is not None: 0729 doc = self.markup(value.__doc__, self.preformat, 0730 funcs, classes, mdict) 0731 push('<dd><tt>%s</tt></dd>\n' % doc) 0732 for attr, tag in [('fget', '<em>get</em>'), 0733 ('fset', '<em>set</em>'), 0734 ('fdel', '<em>delete</em>')]: 0735 func = getattr(value, attr) 0736 if func is not None: 0737 base = self.document(func, tag, mod, 0738 funcs, classes, mdict, object) 0739 push('<dd>%s</dd>\n' % base) 0740 push('</dl>\n') 0741 return attrs 0742 0743 def spilldata(msg, attrs, predicate): 0744 ok, attrs = _split_list(attrs, predicate) 0745 if ok: 0746 hr.maybe() 0747 push(msg) 0748 for name, kind, homecls, value in ok: 0749 base = self.docother(getattr(object, name), name, mod) 0750 if callable(value) or inspect.isdatadescriptor(value): 0751 doc = getattr(value, "__doc__", None) 0752 else: 0753 doc = None 0754 if doc is None: 0755 push('<dl><dt>%s</dl>\n' % base) 0756 else: 0757 doc = self.markup(getdoc(value), self.preformat, 0758 funcs, classes, mdict) 0759 doc = '<dd><tt>%s</tt>' % doc 0760 push('<dl><dt>%s%s</dl>\n' % (base, doc)) 0761 push('\n') 0762 return attrs 0763 0764 attrs = filter(lambda (name, kind, cls, value): visiblename(name), 0765 inspect.classify_class_attrs(object)) 0766 mdict = {} 0767 for key, kind, homecls, value in attrs: 0768 mdict[key] = anchor = '#' + name + '-' + key 0769 value = getattr(object, key) 0770 try: 0771 # The value may not be hashable (e.g., a data attr with 0772 # a dict or list value). 0773 mdict[value] = anchor 0774 except TypeError: 0775 pass 0776 0777 while attrs: 0778 if mro: 0779 thisclass = mro.popleft() 0780 else: 0781 thisclass = attrs[0][2] 0782 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass) 0783 0784 if thisclass is __builtin__.object: 0785 attrs = inherited 0786 continue 0787 elif thisclass is object: 0788 tag = 'defined here' 0789 else: 0790 tag = 'inherited from %s' % self.classlink(thisclass, 0791 object.__module__) 0792 tag += ':<br>\n' 0793 0794 # Sort attrs by name. 0795 attrs.sort(key=lambda t: t[0]) 0796 0797 # Pump out the attrs, segregated by kind. 0798 attrs = spill('Methods %s' % tag, attrs, 0799 lambda t: t[1] == 'method') 0800 attrs = spill('Class methods %s' % tag, attrs, 0801 lambda t: t[1] == 'class method') 0802 attrs = spill('Static methods %s' % tag, attrs, 0803 lambda t: t[1] == 'static method') 0804 attrs = spillproperties('Properties %s' % tag, attrs, 0805 lambda t: t[1] == 'property') 0806 attrs = spilldata('Data and other attributes %s' % tag, attrs, 0807 lambda t: t[1] == 'data') 0808 assert attrs == [] 0809 attrs = inherited 0810 0811 contents = ''.join(contents) 0812 0813 if name == realname: 0814 title = '<a name="%s">class <strong>%s</strong></a>' % ( 0815 name, realname) 0816 else: 0817 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % ( 0818 name, name, realname) 0819 if bases: 0820 parents = [] 0821 for base in bases: 0822 parents.append(self.classlink(base, object.__module__)) 0823 title = title + '(%s)' % join(parents, ', ') 0824 doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict) 0825 doc = doc and '<tt>%s<br> </tt>' % doc 0826 0827 return self.section(title, '#000000', '#ffc8d8', contents, 3, doc) 0828 0829 def formatvalue(self, object): 0830 """Format an argument default value as text.""" 0831 return self.grey('=' + self.repr(object)) 0832 0833 def docroutine(self, object, name=None, mod=None, 0834 funcs={}, classes={}, methods={}, cl=None): 0835 """Produce HTML documentation for a function or method object.""" 0836 realname = object.__name__ 0837 name = name or realname 0838 anchor = (cl and cl.__name__ or '') + '-' + name 0839 note = '' 0840 skipdocs = 0 0841 if inspect.ismethod(object): 0842 imclass = object.im_class 0843 if cl: 0844 if imclass is not cl: 0845 note = ' from ' + self.classlink(imclass, mod) 0846 else: 0847 if object.im_self: 0848 note = ' method of %s instance' % self.classlink( 0849 object.im_self.__class__, mod) 0850 else: 0851 note = ' unbound %s method' % self.classlink(imclass,mod) 0852 object = object.im_func 0853 0854 if name == realname: 0855 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname) 0856 else: 0857 if (cl and realname in cl.__dict__ and 0858 cl.__dict__[realname] is object): 0859 reallink = '<a href="#%s">%s</a>' % ( 0860 cl.__name__ + '-' + realname, realname) 0861 skipdocs = 1 0862 else: 0863 reallink = realname 0864 title = '<a name="%s"><strong>%s</strong></a> = %s' % ( 0865 anchor, name, reallink) 0866 if inspect.isfunction(object): 0867 args, varargs, varkw, defaults = inspect.getargspec(object) 0868 argspec = inspect.formatargspec( 0869 args, varargs, varkw, defaults, formatvalue=self.formatvalue) 0870 if realname == '<lambda>': 0871 title = '<strong>%s</strong> <em>lambda</em> ' % name 0872 argspec = argspec[1:-1] # remove parentheses 0873 else: 0874 argspec = '(...)' 0875 0876 decl = title + argspec + (note and self.grey( 0877 '<font face="helvetica, arial">%s</font>' % note)) 0878 0879 if skipdocs: 0880 return '<dl><dt>%s</dt></dl>\n' % decl 0881 else: 0882 doc = self.markup( 0883 getdoc(object), self.preformat, funcs, classes, methods) 0884 doc = doc and '<dd><tt>%s</tt></dd>' % doc 0885 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc) 0886 0887 def docother(self, object, name=None, mod=None, *ignored): 0888 """Produce HTML documentation for a data object.""" 0889 lhs = name and '<strong>%s</strong> = ' % name or '' 0890 return lhs + self.repr(object) 0891 0892 def index(self, dir, shadowed=None): 0893 """Generate an HTML index for a directory of modules.""" 0894 modpkgs = [] 0895 if shadowed is None: shadowed = {} 0896 seen = {} 0897 files = os.listdir(dir) 0898 0899 def found(name, ispackage, 0900 modpkgs=modpkgs, shadowed=shadowed, seen=seen): 0901 if name not in seen: 0902 modpkgs.append((name, '', ispackage, name in shadowed)) 0903 seen[name] = 1 0904 shadowed[name] = 1 0905 0906 # Package spam/__init__.py takes precedence over module spam.py. 0907 for file in files: 0908 path = os.path.join(dir, file) 0909 if ispackage(path): found(file, 1) 0910 for file in files: 0911 path = os.path.join(dir, file) 0912 if os.path.isfile(path): 0913 modname = inspect.getmodulename(file) 0914 if modname: found(modname, 0) 0915 0916 modpkgs.sort() 0917 contents = self.multicolumn(modpkgs, self.modpkglink) 0918 return self.bigsection(dir, '#ffffff', '#ee77aa', contents) 0919 0920 # -------------------------------------------- text documentation generator 0921 0922 class TextRepr(Repr): 0923 """Class for safely making a text representation of a Python object.""" 0924 def __init__(self): 0925 Repr.__init__(self) 0926 self.maxlist = self.maxtuple = 20 0927 self.maxdict = 10 0928 self.maxstring = self.maxother = 100 0929 0930 def repr1(self, x, level): 0931 if hasattr(type(x), '__name__'): 0932 methodname = 'repr_' + join(split(type(x).__name__), '_') 0933 if hasattr(self, methodname): 0934 return getattr(self, methodname)(x, level) 0935 return cram(stripid(repr(x)), self.maxother) 0936 0937 def repr_string(self, x, level): 0938 test = cram(x, self.maxstring) 0939 testrepr = repr(test) 0940 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''): 0941 # Backslashes are only literal in the string and are never 0942 # needed to make any special characters, so show a raw string. 0943 return 'r' + testrepr[0] + test + testrepr[0] 0944 return testrepr 0945 0946 repr_str = repr_string 0947 0948 def repr_instance(self, x, level): 0949 try: 0950 return cram(stripid(repr(x)), self.maxstring) 0951 except: 0952 return '<%s instance>' % x.__class__.__name__ 0953 0954 class TextDoc(Doc): 0955 """Formatter class for text documentation.""" 0956 0957 # ------------------------------------------- text formatting utilities 0958 0959 _repr_instance = TextRepr() 0960 repr = _repr_instance.repr 0961 0962 def bold(self, text): 0963 """Format a string in bold by overstriking.""" 0964 return join(map(lambda ch: ch + '\b' + ch, text), '') 0965 0966 def indent(self, text, prefix=' '): 0967 """Indent text by prepending a given prefix to each line.""" 0968 if not text: return '' 0969 lines = split(text, '\n') 0970 lines = map(lambda line, prefix=prefix: prefix + line, lines) 0971 if lines: lines[-1] = rstrip(lines[-1]) 0972 return join(lines, '\n') 0973 0974 def section(self, title, contents): 0975 """Format a section with a given heading.""" 0976 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n' 0977 0978 # ---------------------------------------------- type-specific routines 0979 0980 def formattree(self, tree, modname, parent=None, prefix=''): 0981 """Render in text a class tree as returned by inspect.getclasstree().""" 0982 result = '' 0983 for entry in tree: 0984 if type(entry) is type(()): 0985 c, bases = entry 0986 result = result + prefix + classname(c, modname) 0987 if bases and bases != (parent,): 0988 parents = map(lambda c, m=modname: classname(c, m), bases) 0989 result = result + '(%s)' % join(parents, ', ') 0990 result = result + '\n' 0991 elif type(entry) is type([]): 0992 result = result + self.formattree( 0993 entry, modname, c, prefix + ' ') 0994 return result 0995 0996 def docmodule(self, object, name=None, mod=None): 0997 """Produce text documentation for a given module object.""" 0998 name = object.__name__ # ignore the passed-in name 0999 synop, desc = splitdoc(getdoc(object)) 1000 result = self.section('NAME', name + (synop and ' - ' + synop)) 1001 1002 try: 1003 all = object.__all__ 1004 except AttributeError: 1005 all = None 1006 1007 try: 1008 file = inspect.getabsfile(object) 1009 except TypeError: 1010 file = '(built-in)' 1011 result = result + self.section('FILE', file) 1012 1013 docloc = self.getdocloc(object) 1014 if docloc is not None: 1015 result = result + self.section('MODULE DOCS', docloc) 1016 1017 if desc: 1018 result = result + self.section('DESCRIPTION', desc) 1019 1020 classes = [] 1021 for key, value in inspect.getmembers(object, inspect.isclass): 1022 # if __all__ exists, believe it. Otherwise use old heuristic. 1023 if (all is not None 1024 or (inspect.getmodule(value) or object) is object): 1025 if visiblename(key, all): 1026 classes.append((key, value)) 1027 funcs = [] 1028 for key, value in inspect.getmembers(object, inspect.isroutine): 1029 # if __all__ exists, believe it. Otherwise use old heuristic. 1030 if (all is not None or 1031 inspect.isbuiltin(value) or inspect.getmodule(value) is object): 1032 if visiblename(key, all): 1033 funcs.append((key, value)) 1034 data = [] 1035 for key, value in inspect.getmembers(object, isdata): 1036 if visiblename(key, all): 1037 data.append((key, value)) 1038 1039 if hasattr(object, '__path__'): 1040 modpkgs = [] 1041 for file in os.listdir(object.__path__[0]): 1042 path = os.path.join(object.__path__[0], file) 1043 modname = inspect.getmodulename(file) 1044 if modname != '__init__': 1045 if modname and modname not in modpkgs: 1046 modpkgs.append(modname) 1047 elif ispackage(path): 1048 modpkgs.append(file + ' (package)') 1049 modpkgs.sort() 1050 result = result + self.section( 1051 'PACKAGE CONTENTS', join(modpkgs, '\n')) 1052 1053 if classes: 1054 classlist = map(lambda (key, value): value, classes) 1055 contents = [self.formattree( 1056 inspect.getclasstree(classlist, 1), name)] 1057 for key, value in classes: 1058 contents.append(self.document(value, key, name)) 1059 result = result + self.section('CLASSES', join(contents, '\n')) 1060 1061 if funcs: 1062 contents = [] 1063 for key, value in funcs: 1064 contents.append(self.document(value, key, name)) 1065 result = result + self.section('FUNCTIONS', join(contents, '\n')) 1066 1067 if data: 1068 contents = [] 1069 for key, value in data: 1070 contents.append(self.docother(value, key, name, 70)) 1071 result = result + self.section('DATA', join(contents, '\n')) 1072 1073 if hasattr(object, '__version__'): 1074 version = str(object.__version__) 1075 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$': 1076 version = strip(version[11:-1]) 1077 result = result + self.section('VERSION', version) 1078 if hasattr(object, '__date__'): 1079 result = result + self.section('DATE', str(object.__date__)) 1080 if hasattr(object, '__author__'): 1081 result = result + self.section('AUTHOR', str(object.__author__)) 1082 if hasattr(object, '__credits__'): 1083 result = result + self.section('CREDITS', str(object.__credits__)) 1084 return result 1085 1086 def docclass(self, object, name=None, mod=None): 1087 """Produce text documentation for a given class object.""" 1088 realname = object.__name__ 1089 name = name or realname 1090 bases = object.__bases__ 1091 1092 def makename(c, m=object.__module__): 1093 return classname(c, m) 1094 1095 if name == realname: 1096 title = 'class ' + self.bold(realname) 1097 else: 1098 title = self.bold(name) + ' = class ' + realname 1099 if bases: 1100 parents = map(makename, bases) 1101 title = title + '(%s)' % join(parents, ', ') 1102 1103 doc = getdoc(object) 1104 contents = doc and [doc + '\n'] or [] 1105 push = contents.append 1106 1107 # List the mro, if non-trivial. 1108 mro = deque(inspect.getmro(object)) 1109 if len(mro) > 2: 1110 push("Method resolution order:") 1111 for base in mro: 1112 push(' ' + makename(base)) 1113 push('') 1114 1115 # Cute little class to pump out a horizontal rule between sections. 1116 class HorizontalRule: 1117 def __init__(self): 1118 self.needone = 0 1119 def maybe(self): 1120 if self.needone: 1121 push('-' * 70) 1122 self.needone = 1 1123 hr = HorizontalRule() 1124 1125 def spill(msg, attrs, predicate): 1126 ok, attrs = _split_list(attrs, predicate) 1127 if ok: 1128 hr.maybe() 1129 push(msg) 1130 for name, kind, homecls, value in ok: 1131 push(self.document(getattr(object, name), 1132 name, mod, object)) 1133 return attrs 1134 1135 def spillproperties(msg, attrs, predicate): 1136 ok, attrs = _split_list(attrs, predicate) 1137 if ok: 1138 hr.maybe() 1139 push(msg) 1140 for name, kind, homecls, value in ok: 1141 push(name) 1142 need_blank_after_doc = 0 1143 doc = getdoc(value) or '' 1144 if doc: 1145 push(self.indent(doc)) 1146 need_blank_after_doc = 1 1147 for attr, tag in [('fget', '<get>'), 1148 ('fset', '<set>'), 1149 ('fdel', '<delete>')]: 1150 func = getattr(value, attr) 1151 if func is not None: 1152 if need_blank_after_doc: 1153 push('') 1154 need_blank_after_doc = 0 1155 base = self.document(func, tag, mod) 1156 push(self.indent(base)) 1157 return attrs 1158 1159 def spilldata(msg, attrs, predicate): 1160 ok, attrs = _split_list(attrs, predicate) 1161 if ok: 1162 hr.maybe() 1163 push(msg) 1164 for name, kind, homecls, value in ok: 1165 if callable(value) or inspect.isdatadescriptor(value): 1166 doc = getattr(value, "__doc__", None) 1167 else: 1168 doc = None 1169 push(self.docother(getattr(object, name), 1170 name, mod, 70, doc) + '\n') 1171 return attrs 1172 1173 attrs = filter(lambda (name, kind, cls, value): visiblename(name), 1174 inspect.classify_class_attrs(object)) 1175 while attrs: 1176 if mro: 1177 thisclass = mro.popleft() 1178 else: 1179 thisclass = attrs[0][2] 1180 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass) 1181 1182 if thisclass is __builtin__.object: 1183 attrs = inherited 1184 continue 1185 elif thisclass is object: 1186 tag = "defined here" 1187 else: 1188 tag = "inherited from %s" % classname(thisclass, 1189 object.__module__) 1190 filter(lambda t: not t[0].startswith('_'), attrs) 1191 1192 # Sort attrs by name. 1193 attrs.sort() 1194 1195 # Pump out the attrs, segregated by kind. 1196 attrs = spill("Methods %s:\n" % tag, attrs, 1197 lambda t: t[1] == 'method') 1198 attrs = spill("Class methods %s:\n" % tag, attrs, 1199 lambda t: t[1] == 'class method') 1200 attrs = spill("Static methods %s:\n" % tag, attrs, 1201 lambda t: t[1] == 'static method') 1202 attrs = spillproperties("Properties %s:\n" % tag, attrs, 1203 lambda t: t[1] == 'property') 1204 attrs = spilldata("Data and other attributes %s:\n" % tag, attrs, 1205 lambda t: t[1] == 'data') 1206 assert attrs == [] 1207 attrs = inherited 1208 1209 contents = '\n'.join(contents) 1210 if not contents: 1211 return title + '\n' 1212 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n' 1213 1214 def formatvalue(self, object): 1215 """Format an argument default value as text.""" 1216 return '=' + self.repr(object) 1217 1218 def docroutine(self, object, name=None, mod=None, cl=None): 1219 """Produce text documentation for a function or method object.""" 1220 realname = object.__name__ 1221 name = name or realname 1222 note = '' 1223 skipdocs = 0 1224 if inspect.ismethod(object): 1225 imclass = object.im_class 1226 if cl: 1227 if imclass is not cl: 1228 note = ' from ' + classname(imclass, mod) 1229 else: 1230 if object.im_self: 1231 note = ' method of %s instance' % classname( 1232 object.im_self.__class__, mod) 1233 else: 1234 note = ' unbound %s method' % classname(imclass,mod) 1235 object = object.im_func 1236 1237 if name == realname: 1238 title = self.bold(realname) 1239 else: 1240 if (cl and realname in cl.__dict__ and 1241 cl.__dict__[realname] is object): 1242 skipdocs = 1 1243 title = self.bold(name) + ' = ' + realname 1244 if inspect.isfunction(object): 1245 args, varargs, varkw, defaults = inspect.getargspec(object) 1246 argspec = inspect.formatargspec( 1247 args, varargs, varkw, defaults, formatvalue=self.formatvalue) 1248 if realname == '<lambda>': 1249 title = 'lambda' 1250 argspec = argspec[1:-1] # remove parentheses 1251 else: 1252 argspec = '(...)' 1253 decl = title + argspec + note 1254 1255 if skipdocs: 1256 return decl + '\n' 1257 else: 1258 doc = getdoc(object) or '' 1259 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n') 1260 1261 def docother(self, object, name=None, mod=None, maxlen=None, doc=None): 1262 """Produce text documentation for a data object.""" 1263 repr = self.repr(object) 1264 if maxlen: 1265 line = (name and name + ' = ' or '') + repr 1266 chop = maxlen - len(line) 1267 if chop < 0: repr = repr[:chop] + '...' 1268 line = (name and self.bold(name) + ' = ' or '') + repr 1269 if doc is not None: 1270 line += '\n' + self.indent(str(doc)) 1271 return line 1272 1273 # --------------------------------------------------------- user interfaces 1274 1275 def pager(text): 1276 """The first time this is called, determine what kind of pager to use.""" 1277 global pager 1278 pager = getpager() 1279 pager(text) 1280 1281 def getpager(): 1282 """Decide what method to use for paging through text.""" 1283 if type(sys.stdout) is not types.FileType: 1284 return plainpager 1285 if not sys.stdin.isatty() or not sys.stdout.isatty(): 1286 return plainpager 1287 if os.environ.get('TERM') in ['dumb', 'emacs']: 1288 return plainpager 1289 if 'PAGER' in os.environ: 1290 if sys.platform == 'win32': # pipes completely broken in Windows 1291 return lambda text: tempfilepager(plain(text), os.environ['PAGER']) 1292 elif os.environ.get('TERM') in ['dumb', 'emacs']: 1293 return lambda text: pipepager(plain(text), os.environ['PAGER']) 1294 else: 1295 return lambda text: pipepager(text, os.environ['PAGER']) 1296 if sys.platform == 'win32' or sys.platform.startswith('os2'): 1297 return lambda text: tempfilepager(plain(text), 'more <') 1298 if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0: 1299 return lambda text: pipepager(text, 'less') 1300 1301 import tempfile 1302 (fd, filename) = tempfile.mkstemp() 1303 os.close(fd) 1304 try: 1305 if hasattr(os, 'system') and os.system('more %s' % filename) == 0: 1306 return lambda text: pipepager(text, 'more') 1307 else: 1308 return ttypager 1309 finally: 1310 os.unlink(filename) 1311 1312 def plain(text): 1313 """Remove boldface formatting from text.""" 1314 return re.sub('.\b', '', text) 1315 1316 def pipepager(text, cmd): 1317 """Page through text by feeding it to another program.""" 1318 pipe = os.popen(cmd, 'w') 1319 try: 1320 pipe.write(text) 1321 pipe.close() 1322 except IOError: 1323 pass # Ignore broken pipes caused by quitting the pager program. 1324 1325 def tempfilepager(text, cmd): 1326 """Page through text by invoking a program on a temporary file.""" 1327 import tempfile 1328 filename = tempfile.mktemp() 1329 file = open(filename, 'w') 1330 file.write(text) 1331 file.close() 1332 try: 1333 os.system(cmd + ' ' + filename) 1334 finally: 1335 os.unlink(filename) 1336 1337 def ttypager(text): 1338 """Page through text on a text terminal.""" 1339 lines = split(plain(text), '\n') 1340 try: 1341 import tty 1342 fd = sys.stdin.fileno() 1343 old = tty.tcgetattr(fd) 1344 tty.setcbreak(fd) 1345 getchar = lambda: sys.stdin.read(1) 1346 except (ImportError, AttributeError): 1347 tty = None 1348 getchar = lambda: sys.stdin.readline()[:-1][:1] 1349 1350 try: 1351 r = inc = os.environ.get('LINES', 25) - 1 1352 sys.stdout.write(join(lines[:inc], '\n') + '\n') 1353 while lines[r:]: 1354 sys.stdout.write('-- more --') 1355 sys.stdout.flush() 1356 c = getchar() 1357 1358 if c in ['q', 'Q']: 1359 sys.stdout.write('\r \r') 1360 break 1361 elif c in ['\r', '\n']: 1362 sys.stdout.write('\r \r' + lines[r] + '\n') 1363 r = r + 1 1364 continue 1365 if c in ['b', 'B', '\x1b']: 1366 r = r - inc - inc 1367 if r < 0: r = 0 1368 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n') 1369 r = r + inc 1370 1371 finally: 1372 if tty: 1373 tty.tcsetattr(fd, tty.TCSAFLUSH, old) 1374 1375 def plainpager(text): 1376 """Simply print unformatted text. This is the ultimate fallback.""" 1377 sys.stdout.write(plain(text)) 1378 1379 def describe(thing): 1380 """Produce a short description of the given thing.""" 1381 if inspect.ismodule(thing): 1382 if thing.__name__ in sys.builtin_module_names: 1383 return 'built-in module ' + thing.__name__ 1384 if hasattr(thing, '__path__'): 1385 return 'package ' + thing.__name__ 1386 else: 1387 return 'module ' + thing.__name__ 1388 if inspect.isbuiltin(thing): 1389 return 'built-in function ' + thing.__name__ 1390 if inspect.isclass(thing): 1391 return 'class ' + thing.__name__ 1392 if inspect.isfunction(thing): 1393 return 'function ' + thing.__name__ 1394 if inspect.ismethod(thing): 1395 return 'method ' + thing.__name__ 1396 if type(thing) is types.InstanceType: 1397 return 'instance of ' + thing.__class__.__name__ 1398 return type(thing).__name__ 1399 1400 def locate(path, forceload=0): 1401 """Locate an object by name or dotted path, importing as necessary.""" 1402 parts = [part for part in split(path, '.') if part] 1403 module, n = None, 0 1404 while n < len(parts): 1405 nextmodule = safeimport(join(parts[:n+1], '.'), forceload) 1406 if nextmodule: module, n = nextmodule, n + 1 1407 else: break 1408 if module: 1409 object = module 1410 for part in parts[n:]: 1411 try: object = getattr(object, part) 1412 except AttributeError: return None 1413 return object 1414 else: 1415 if hasattr(__builtin__, path): 1416 return getattr(__builtin__, path) 1417 1418 # --------------------------------------- interactive interpreter interface 1419 1420 text = TextDoc() 1421 html = HTMLDoc() 1422 1423 def resolve(thing, forceload=0): 1424 """Given an object or a path to an object, get the object and its name.""" 1425 if isinstance(thing, str): 1426 object = locate(thing, forceload) 1427 if not object: 1428 raise ImportError, 'no Python documentation found for %r' % thing 1429 return object, thing 1430 else: 1431 return thing, getattr(thing, '__name__', None) 1432 1433 def doc(thing, title='Python Library Documentation: %s', forceload=0): 1434 """Display text documentation, given an object or a path to an object.""" 1435 try: 1436 object, name = resolve(thing, forceload) 1437 desc = describe(object) 1438 module = inspect.getmodule(object) 1439 if name and '.' in name: 1440 desc += ' in ' + name[:name.rfind('.')] 1441 elif module and module is not object: 1442 desc += ' in module ' + module.__name__ 1443 pager(title % desc + '\n\n' + text.document(object, name)) 1444 except (ImportError, ErrorDuringImport), value: 1445 print value 1446 1447 def writedoc(thing, forceload=0): 1448 """Write HTML documentation to a file in the current directory.""" 1449 try: 1450 object, name = resolve(thing, forceload) 1451 page = html.page(describe(object), html.document(object, name)) 1452 file = open(name + '.html', 'w') 1453 file.write(page) 1454 file.close() 1455 print 'wrote', name + '.html' 1456 except (ImportError, ErrorDuringImport), value: 1457 print value 1458 1459 def writedocs(dir, pkgpath='', done=None): 1460 """Write out HTML documentation for all modules in a directory tree.""" 1461 if done is None: done = {} 1462 for file in os.listdir(dir): 1463 path = os.path.join(dir, file) 1464 if ispackage(path): 1465 writedocs(path, pkgpath + file + '.', done) 1466 elif os.path.isfile(path): 1467 modname = inspect.getmodulename(path) 1468 if modname: 1469 if modname == '__init__': 1470 modname = pkgpath[:-1] # remove trailing period 1471 else: 1472 modname = pkgpath + modname 1473 if modname not in done: 1474 done[modname] = 1 1475 writedoc(modname) 1476 1477 class Helper: 1478 keywords = { 1479 'and': 'BOOLEAN', 1480 'assert': ('ref/assert', ''), 1481 'break': ('ref/break', 'while for'), 1482 'class': ('ref/class', 'CLASSES SPECIALMETHODS'), 1483 'continue': ('ref/continue', 'while for'), 1484 'def': ('ref/function', ''), 1485 'del': ('ref/del', 'BASICMETHODS'), 1486 'elif': 'if', 1487 'else': ('ref/if', 'while for'), 1488 'except': 'try', 1489 'exec': ('ref/exec', ''), 1490 'finally': 'try', 1491 'for': ('ref/for', 'break continue while'), 1492 'from': 'import', 1493 'global': ('ref/global', 'NAMESPACES'), 1494 'if': ('ref/if', 'TRUTHVALUE'), 1495 'import': ('ref/import', 'MODULES'), 1496 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'), 1497 'is': 'COMPARISON', 1498 'lambda': ('ref/lambdas', 'FUNCTIONS'), 1499 'not': 'BOOLEAN', 1500 'or': 'BOOLEAN', 1501 'pass': ('ref/pass', ''), 1502 'print': ('ref/print', ''), 1503 'raise': ('ref/raise', 'EXCEPTIONS'), 1504 'return': ('ref/return', 'FUNCTIONS'), 1505 'try': ('ref/try', 'EXCEPTIONS'), 1506 'while': ('ref/while', 'break continue if TRUTHVALUE'), 1507 'yield': ('ref/yield', ''), 1508 } 1509 1510 topics = { 1511 'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'), 1512 'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'), 1513 'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'), 1514 'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'), 1515 'UNICODE': ('ref/strings', 'encodings unicode SEQUENCES STRINGMETHODS FORMATTING TYPES'), 1516 'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'), 1517 'INTEGER': ('ref/integers', 'int range'), 1518 'FLOAT': ('ref/floating', 'float math'), 1519 'COMPLEX': ('ref/imaginary', 'complex cmath'), 1520 'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'), 1521 'MAPPINGS': 'DICTIONARIES', 1522 'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'), 1523 'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'), 1524 'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'), 1525 'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'), 1526 'FRAMEOBJECTS': 'TYPES', 1527 'TRACEBACKS': 'TYPES', 1528 'NONE': ('lib/bltin-null-object', ''), 1529 'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'), 1530 'FILES': ('lib/bltin-file-objects', ''), 1531 'SPECIALATTRIBUTES': ('lib/specialattrs', ''), 1532 'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'), 1533 'MODULES': ('lib/typesmodules', 'import'), 1534 'PACKAGES': 'import', 1535 'EXPRESSIONS': ('ref/summary', 'lambda or and not in is BOOLEAN COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES LISTS DICTIONARIES BACKQUOTES'), 1536 'OPERATORS': 'EXPRESSIONS', 1537 'PRECEDENCE': 'EXPRESSIONS', 1538 'OBJECTS': ('ref/objects', 'TYPES'), 1539 'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'), 1540 'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'), 1541 'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'), 1542 'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'), 1543 'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'), 1544 'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'), 1545 'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'), 1546 'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'), 1547 'EXECUTION': ('ref/execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'), 1548 'NAMESPACES': ('ref/naming', 'global ASSIGNMENT DELETION DYNAMICFEATURES'), 1549 'DYNAMICFEATURES': ('ref/dynamic-features', ''), 1550 'SCOPING': 'NAMESPACES', 1551 'FRAMES': 'NAMESPACES', 1552 'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'), 1553 'COERCIONS': ('ref/coercion-rules','CONVERSIONS'), 1554 'CONVERSIONS': ('ref/conversions', 'COERCIONS'), 1555 'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'), 1556 'SPECIALIDENTIFIERS': ('ref/id-classes', ''), 1557 'PRIVATENAMES': ('ref/atom-identifiers', ''), 1558 'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'), 1559 'TUPLES': 'SEQUENCES', 1560 'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'), 1561 'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'), 1562 'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'), 1563 'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'), 1564 'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'), 1565 'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'), 1566 'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'), 1567 'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'), 1568 'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'), 1569 'CALLS': ('ref/calls', 'EXPRESSIONS'), 1570 'POWER': ('ref/power', 'EXPRESSIONS'), 1571 'UNARY': ('ref/unary', 'EXPRESSIONS'), 1572 'BINARY': ('ref/binary', 'EXPRESSIONS'), 1573 'SHIFTING': ('ref/shifting', 'EXPRESSIONS'), 1574 'BITWISE': ('ref/bitwise', 'EXPRESSIONS'), 1575 'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'), 1576 'BOOLEAN': ('ref/Booleans', 'EXPRESSIONS TRUTHVALUE'), 1577 'ASSERTION': 'assert', 1578 'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'), 1579 'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'), 1580 'DELETION': 'del', 1581 'PRINTING': 'print', 1582 'RETURNING': 'return', 1583 'IMPORTING': 'import', 1584 'CONDITIONAL': 'if', 1585 'LOOPING': ('ref/compound', 'for while break continue'), 1586 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'), 1587 'DEBUGGING': ('lib/module-pdb', 'pdb'), 1588 } 1589 1590 def __init__(self, input, output): 1591 self.input = input 1592 self.output = output 1593 self.docdir = None 1594 execdir = os.path.dirname(sys.executable) 1595 homedir = os.environ.get('PYTHONHOME') 1596 for dir in [os.environ.get('PYTHONDOCS'), 1597 homedir and os.path.join(homedir, 'doc'), 1598 os.path.join(execdir, 'doc'), 1599 '/usr/doc/python-docs-' + split(sys.version)[0], 1600 '/usr/doc/python-' + split(sys.version)[0], 1601 '/usr/doc/python-docs-' + sys.version[:3], 1602 '/usr/doc/python-' + sys.version[:3], 1603 os.path.join(sys.prefix, 'Resources/English.lproj/Documentation')]: 1604 if dir and os.path.isdir(os.path.join(dir, 'lib')): 1605 self.docdir = dir 1606 1607 def __repr__(self): 1608 if inspect.stack()[1][3] == '?': 1609 self() 1610 return '' 1611 return '<pydoc.Helper instance>' 1612 1613 def __call__(self, request=None): 1614 if request is not None: 1615 self.help(request) 1616 else: 1617 self.intro() 1618 self.interact() 1619 self.output.write(''' 1620 You are now leaving help and returning to the Python interpreter. 1621 If you want to ask for help on a particular object directly from the 1622 interpreter, you can type "help(object)". Executing "help('string')" 1623 has the same effect as typing a particular string at the help> prompt. 1624 ''') 1625 1626 def interact(self): 1627 self.output.write('\n') 1628 while True: 1629 try: 1630 request = self.getline('help> ') 1631 if not request: break 1632 except (KeyboardInterrupt, EOFError): 1633 break 1634 request = strip(replace(request, '"', '', "'", '')) 1635 if lower(request) in ['q', 'quit']: break 1636 self.help(request) 1637 1638 def getline(self, prompt): 1639 """Read one line, using raw_input when available.""" 1640 if self.input is sys.stdin: 1641 return raw_input(prompt) 1642 else: 1643 self.output.write(prompt) 1644 self.output.flush() 1645 return self.input.readline() 1646 1647 def help(self, request): 1648 if type(request) is type(''): 1649 if request == 'help': self.intro() 1650 elif request == 'keywords': self.listkeywords() 1651 elif request == 'topics': self.listtopics() 1652 elif request == 'modules': self.listmodules() 1653 elif request[:8] == 'modules ': 1654 self.listmodules(split(request)[1]) 1655 elif request in self.keywords: self.showtopic(request) 1656 elif request in self.topics: self.showtopic(request) 1657 elif request: doc(request, 'Help on %s:') 1658 elif isinstance(request, Helper): self() 1659 else: doc(request, 'Help on %s:') 1660 self.output.write('\n') 1661 1662 def intro(self): 1663 self.output.write(''' 1664 Welcome to Python %s! This is the online help utility. 1665 1666 If this is your first time using Python, you should definitely check out 1667 the tutorial on the Internet at http://www.python.org/doc/tut/. 1668 1669 Enter the name of any module, keyword, or topic to get help on writing 1670 Python programs and using Python modules. To quit this help utility and 1671 return to the interpreter, just type "quit". 1672 1673 To get a list of available modules, keywords, or topics, type "modules", 1674 "keywords", or "topics". Each module also comes with a one-line summary 1675 of what it does; to list the modules whose summaries contain a given word 1676 such as "spam", type "modules spam". 1677 ''' % sys.version[:3]) 1678 1679 def list(self, items, columns=4, width=80): 1680 items = items[:] 1681 items.sort() 1682 colw = width / columns 1683 rows = (len(items) + columns - 1) / columns 1684 for row in range(rows): 1685 for col in range(columns): 1686 i = col * rows + row 1687 if i < len(items): 1688 self.output.write(items[i]) 1689 if col < columns - 1: 1690 self.output.write(' ' + ' ' * (colw-1 - len(items[i]))) 1691 self.output.write('\n') 1692 1693 def listkeywords(self): 1694 self.output.write(''' 1695 Here is a list of the Python keywords. Enter any keyword to get more help. 1696 1697 ''') 1698 self.list(self.keywords.keys()) 1699 1700 def listtopics(self): 1701 self.output.write(''' 1702 Here is a list of available topics. Enter any topic name to get more help. 1703 1704 ''') 1705 self.list(self.topics.keys()) 1706 1707 def showtopic(self, topic): 1708 if not self.docdir: 1709 self.output.write(''' 1710 Sorry, topic and keyword documentation is not available because the Python 1711 HTML documentation files could not be found. If you have installed them, 1712 please set the environment variable PYTHONDOCS to indicate their location. 1713 ''') 1714 return 1715 target = self.topics.get(topic, self.keywords.get(topic)) 1716 if not target: 1717 self.output.write('no documentation found for %s\n' % repr(topic)) 1718 return 1719 if type(target) is type(''): 1720 return self.showtopic(target) 1721 1722 filename, xrefs = target 1723 filename = self.docdir + '/' + filename + '.html' 1724 try: 1725 file = open(filename) 1726 except: 1727 self.output.write('could not read docs from %s\n' % filename) 1728 return 1729 1730 divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S) 1731 addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S) 1732 document = re.sub(addrpat, '', re.sub(divpat, '', file.read())) 1733 file.close() 1734 1735 import htmllib, formatter, StringIO 1736 buffer = StringIO.StringIO() 1737 parser = htmllib.HTMLParser( 1738 formatter.AbstractFormatter(formatter.DumbWriter(buffer))) 1739 parser.start_table = parser.do_p 1740 parser.end_table = lambda parser=parser: parser.do_p({}) 1741 parser.start_tr = parser.do_br 1742 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t') 1743 parser.feed(document) 1744 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ') 1745 pager(' ' + strip(buffer) + '\n') 1746 if xrefs: 1747 buffer = StringIO.StringIO() 1748 formatter.DumbWriter(buffer).send_flowing_data( 1749 'Related help topics: ' + join(split(xrefs), ', ') + '\n') 1750 self.output.write('\n%s\n' % buffer.getvalue()) 1751 1752 def listmodules(self, key=''): 1753 if key: 1754 self.output.write(''' 1755 Here is a list of matching modules. Enter any module name to get more help. 1756 1757 ''') 1758 apropos(key) 1759 else: 1760 self.output.write(''' 1761 Please wait a moment while I gather a list of all available modules... 1762 1763 ''') 1764 modules = {} 1765 def callback(path, modname, desc, modules=modules): 1766 if modname and modname[-9:] == '.__init__': 1767 modname = modname[:-9] + ' (package)' 1768 if find(modname, '.') < 0: 1769 modules[modname] = 1 1770 ModuleScanner().run(callback) 1771 self.list(modules.keys()) 1772 self.output.write(''' 1773 Enter any module name to get more help. Or, type "modules spam" to search 1774 for modules whose descriptions contain the word "spam". 1775 ''') 1776 1777 help = Helper(sys.stdin, sys.stdout) 1778 1779 class Scanner: 1780 """A generic tree iterator.""" 1781 def __init__(self, roots, children, descendp): 1782 self.roots = roots[:] 1783 self.state = [] 1784 self.children = children 1785 self.descendp = descendp 1786 1787 def next(self): 1788 if not self.state: 1789 if not self.roots: 1790 return None 1791 root = self.roots.pop(0) 1792 self.state = [(root, self.children(root))] 1793 node, children = self.state[-1] 1794 if not children: 1795 self.state.pop() 1796 return self.next() 1797 child = children.pop(0) 1798 if self.descendp(child): 1799 self.state.append((child, self.children(child))) 1800 return child 1801 1802 class ModuleScanner(Scanner): 1803 """An interruptible scanner that searches module synopses.""" 1804 def __init__(self): 1805 roots = map(lambda dir: (dir, ''), pathdirs()) 1806 Scanner.__init__(self, roots, self.submodules, self.isnewpackage) 1807 self.inodes = map(lambda (dir, pkg): os.stat(dir).st_ino, roots) 1808 1809 def submodules(self, (dir, package)): 1810 children = [] 1811 for file in os.listdir(dir): 1812 path = os.path.join(dir, file) 1813 if ispackage(path): 1814 children.append((path, package + (package and '.') + file)) 1815 else: 1816 children.append((path, package)) 1817 children.sort() # so that spam.py comes before spam.pyc or spam.pyo 1818 return children 1819 1820 def isnewpackage(self, (dir, package)): 1821 inode = os.path.exists(dir) and os.stat(dir).st_ino 1822 if not (os.path.islink(dir) and inode in self.inodes): 1823 self.inodes.append(inode) # detect circular symbolic links 1824 return ispackage(dir) 1825 return False 1826 1827 def run(self, callback, key=None, completer=None): 1828 if key: key = lower(key) 1829 self.quit = False 1830 seen = {} 1831 1832 for modname in sys.builtin_module_names: 1833 if modname != '__main__': 1834 seen[modname] = 1 1835 if key is None: 1836 callback(None, modname, '') 1837 else: 1838 desc = split(__import__(modname).__doc__ or '', '\n')[0] 1839 if find(lower(modname + ' - ' + desc), key) >= 0: 1840 callback(None, modname, desc) 1841 1842 while not self.quit: 1843 node = self.next() 1844 if not node: break 1845 path, package = node 1846 modname = inspect.getmodulename(path) 1847 if os.path.isfile(path) and modname: 1848 modname = package + (package and '.') + modname 1849 if not modname in seen: 1850 seen[modname] = 1 # if we see spam.py, skip spam.pyc 1851 if key is None: 1852 callback(path, modname, '') 1853 else: 1854 desc = synopsis(path) or '' 1855 if find(lower(modname + ' - ' + desc), key) >= 0: 1856 callback(path, modname, desc) 1857 if completer: completer() 1858 1859 def apropos(key): 1860 """Print all the one-line module summaries that contain a substring.""" 1861 def callback(path, modname, desc): 1862 if modname[-9:] == '.__init__': 1863 modname = modname[:-9] + ' (package)' 1864 print modname, desc and '- ' + desc 1865 try: import warnings 1866 except ImportError: pass 1867 else: warnings.filterwarnings('ignore') # ignore problems during import 1868 ModuleScanner().run(callback, key) 1869 1870 # --------------------------------------------------- web browser interface 1871 1872 def serve(port, callback=None, completer=None): 1873 import BaseHTTPServer, mimetools, select 1874 1875 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded. 1876 class Message(mimetools.Message): 1877 def __init__(self, fp, seekable=1): 1878 Message = self.__class__ 1879 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable) 1880 self.encodingheader = self.getheader('content-transfer-encoding') 1881 self.typeheader = self.getheader('content-type') 1882 self.parsetype() 1883 self.parseplist() 1884 1885 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler): 1886 def send_document(self, title, contents): 1887 try: 1888 self.send_response(200) 1889 self.send_header('Content-Type', 'text/html') 1890 self.end_headers() 1891 self.wfile.write(html.page(title, contents)) 1892 except IOError: pass 1893 1894 def do_GET(self): 1895 path = self.path 1896 if path[-5:] == '.html': path = path[:-5] 1897 if path[:1] == '/': path = path[1:] 1898 if path and path != '.': 1899 try: 1900 obj = locate(path, forceload=1) 1901 except ErrorDuringImport, value: 1902 self.send_document(path, html.escape(str(value))) 1903 return 1904 if obj: 1905 self.send_document(describe(obj), html.document(obj, path)) 1906 else: 1907 self.send_document(path, 1908 'no Python documentation found for %s' % repr(path)) 1909 else: 1910 heading = html.heading( 1911 '<big><big><strong>Python: Index of Modules</strong></big></big>', 1912 '#ffffff', '#7799ee') 1913 def bltinlink(name): 1914 return '<a href="%s.html">%s</a>' % (name, name) 1915 names = filter(lambda x: x != '__main__', 1916 sys.builtin_module_names) 1917 contents = html.multicolumn(names, bltinlink) 1918 indices = ['<p>' + html.bigsection( 1919 'Built-in Modules', '#ffffff', '#ee77aa', contents)] 1920 1921 seen = {} 1922 for dir in pathdirs(): 1923 indices.append(html.index(dir, seen)) 1924 contents = heading + join(indices) + '''<p align=right> 1925 <font color="#909090" face="helvetica, arial"><strong> 1926 pydoc</strong> by Ka-Ping Yee <ping@lfw.org></font>''' 1927 self.send_document('Index of Modules', contents) 1928 1929 def log_message(self, *args): pass 1930 1931 class DocServer(BaseHTTPServer.HTTPServer): 1932 def __init__(self, port, callback): 1933 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost' 1934 self.address = ('', port) 1935 self.url = 'http://%s:%d/' % (host, port) 1936 self.callback = callback 1937 self.base.__init__(self, self.address, self.handler) 1938 1939 def serve_until_quit(self): 1940 import select 1941 self.quit = False 1942 while not self.quit: 1943 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1) 1944 if rd: self.handle_request() 1945 1946 def server_activate(self): 1947 self.base.server_activate(self) 1948 if self.callback: self.callback(self) 1949 1950 DocServer.base = BaseHTTPServer.HTTPServer 1951 DocServer.handler = DocHandler 1952 DocHandler.MessageClass = Message 1953 try: 1954 try: 1955 DocServer(port, callback).serve_until_quit() 1956 except (KeyboardInterrupt, select.error): 1957 pass 1958 finally: 1959 if completer: completer() 1960 1961 # ----------------------------------------------------- graphical interface 1962 1963 def gui(): 1964 """Graphical interface (starts web server and pops up a control window).""" 1965 class GUI: 1966 def __init__(self, window, port=7464): 1967 self.window = window 1968 self.server = None 1969 self.scanner = None 1970 1971 import Tkinter 1972 self.server_frm = Tkinter.Frame(window) 1973 self.title_lbl = Tkinter.Label(self.server_frm, 1974 text='Starting server...\n ') 1975 self.open_btn = Tkinter.Button(self.server_frm, 1976 text='open browser', command=self.open, state='disabled') 1977 self.quit_btn = Tkinter.Button(self.server_frm, 1978 text='quit serving', command=self.quit, state='disabled') 1979 1980 self.search_frm = Tkinter.Frame(window) 1981 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for') 1982 self.search_ent = Tkinter.Entry(self.search_frm) 1983 self.search_ent.bind('<Return>', self.search) 1984 self.stop_btn = Tkinter.Button(self.search_frm, 1985 text='stop', pady=0, command=self.stop, state='disabled') 1986 if sys.platform == 'win32': 1987 # Trying to hide and show this button crashes under Windows. 1988 self.stop_btn.pack(side='right') 1989 1990 self.window.title('pydoc') 1991 self.window.protocol('WM_DELETE_WINDOW', self.quit) 1992 self.title_lbl.pack(side='top', fill='x') 1993 self.open_btn.pack(side='left', fill='x', expand=1) 1994 self.quit_btn.pack(side='right', fill='x', expand=1) 1995 self.server_frm.pack(side='top', fill='x') 1996 1997 self.search_lbl.pack(side='left') 1998 self.search_ent.pack(side='right', fill='x', expand=1) 1999 self.search_frm.pack(side='top', fill='x') 2000 self.search_ent.focus_set() 2001 2002 font = ('helvetica', sys.platform == 'win32' and 8 or 10) 2003 self.result_lst = Tkinter.Listbox(window, font=font, height=6) 2004 self.result_lst.bind('<Button-1>', self.select) 2005 self.result_lst.bind('<Double-Button-1>', self.goto) 2006 self.result_scr = Tkinter.Scrollbar(window, 2007 orient='vertical', command=self.result_lst.yview) 2008 self.result_lst.config(yscrollcommand=self.result_scr.set) 2009 2010 self.result_frm = Tkinter.Frame(window) 2011 self.goto_btn = Tkinter.Button(self.result_frm, 2012 text='go to selected', command=self.goto) 2013 self.hide_btn = Tkinter.Button(self.result_frm, 2014 text='hide results', command=self.hide) 2015 self.goto_btn.pack(side='left', fill='x', expand=1) 2016 self.hide_btn.pack(side='right', fill='x', expand=1) 2017 2018 self.window.update() 2019 self.minwidth = self.window.winfo_width() 2020 self.minheight = self.window.winfo_height() 2021 self.bigminheight = (self.server_frm.winfo_reqheight() + 2022 self.search_frm.winfo_reqheight() + 2023 self.result_lst.winfo_reqheight() + 2024 self.result_frm.winfo_reqheight()) 2025 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight 2026 self.expanded = 0 2027 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight)) 2028 self.window.wm_minsize(self.minwidth, self.minheight) 2029 self.window.tk.willdispatch() 2030 2031 import threading 2032 threading.Thread( 2033 target=serve, args=(port, self.ready, self.quit)).start() 2034 2035 def ready(self, server): 2036 self.server = server 2037 self.title_lbl.config( 2038 text='Python documentation server at\n' + server.url) 2039 self.open_btn.config(state='normal') 2040 self.quit_btn.config(state='normal') 2041 2042 def open(self, event=None, url=None): 2043 url = url or self.server.url 2044 try: 2045 import webbrowser 2046 webbrowser.open(url) 2047 except ImportError: # pre-webbrowser.py compatibility 2048 if sys.platform == 'win32': 2049 os.system('start "%s"' % url) 2050 elif sys.platform == 'mac': 2051 try: import ic 2052 except ImportError: pass 2053 else: ic.launchurl(url) 2054 else: 2055 rc = os.system('netscape -remote "openURL(%s)" &' % url) 2056 if rc: os.system('netscape "%s" &' % url) 2057 2058 def quit(self, event=None): 2059 if self.server: 2060 self.server.quit = 1 2061 self.window.quit() 2062 2063 def search(self, event=None): 2064 key = self.search_ent.get() 2065 self.stop_btn.pack(side='right') 2066 self.stop_btn.config(state='normal') 2067 self.search_lbl.config(text='Searching for "%s"...' % key) 2068 self.search_ent.forget() 2069 self.search_lbl.pack(side='left') 2070 self.result_lst.delete(0, 'end') 2071 self.goto_btn.config(state='disabled') 2072 self.expand() 2073 2074 import threading 2075 if self.scanner: 2076 self.scanner.quit = 1 2077 self.scanner = ModuleScanner() 2078 threading.Thread(target=self.scanner.run, 2079 args=(self.update, key, self.done)).start() 2080 2081 def update(self, path, modname, desc): 2082 if modname[-9:] == '.__init__': 2083 modname = modname[:-9] + ' (package)' 2084 self.result_lst.insert('end', 2085 modname + ' - ' + (desc or '(no description)')) 2086 2087 def stop(self, event=None): 2088 if self.scanner: 2089 self.scanner.quit = 1 2090 self.scanner = None 2091 2092 def done(self): 2093 self.scanner = None 2094 self.search_lbl.config(text='Search for') 2095 self.search_lbl.pack(side='left') 2096 self.search_ent.pack(side='right', fill='x', expand=1) 2097 if sys.platform != 'win32': self.stop_btn.forget() 2098 self.stop_btn.config(state='disabled') 2099 2100 def select(self, event=None): 2101 self.goto_btn.config(state='normal') 2102 2103 def goto(self, event=None): 2104 selection = self.result_lst.curselection() 2105 if selection: 2106 modname = split(self.result_lst.get(selection[0]))[0] 2107 self.open(url=self.server.url + modname + '.html') 2108 2109 def collapse(self): 2110 if not self.expanded: return 2111 self.result_frm.forget() 2112 self.result_scr.forget() 2113 self.result_lst.forget() 2114 self.bigwidth = self.window.winfo_width() 2115 self.bigheight = self.window.winfo_height() 2116 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight)) 2117 self.window.wm_minsize(self.minwidth, self.minheight) 2118 self.expanded = 0 2119 2120 def expand(self): 2121 if self.expanded: return 2122 self.result_frm.pack(side='bottom', fill='x') 2123 self.result_scr.pack(side='right', fill='y') 2124 self.result_lst.pack(side='top', fill='both', expand=1) 2125 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight)) 2126 self.window.wm_minsize(self.minwidth, self.bigminheight) 2127 self.expanded = 1 2128 2129 def hide(self, event=None): 2130 self.stop() 2131 self.collapse() 2132 2133 import Tkinter 2134 try: 2135 root = Tkinter.Tk() 2136 # Tk will crash if pythonw.exe has an XP .manifest 2137 # file and the root has is not destroyed explicitly. 2138 # If the problem is ever fixed in Tk, the explicit 2139 # destroy can go. 2140 try: 2141 gui = GUI(root) 2142 root.mainloop() 2143 finally: 2144 root.destroy() 2145 except KeyboardInterrupt: 2146 pass 2147 2148 # -------------------------------------------------- command-line interface 2149 2150 def ispath(x): 2151 return isinstance(x, str) and find(x, os.sep) >= 0 2152 2153 def cli(): 2154 """Command-line interface (looks at sys.argv to decide what to do).""" 2155 import getopt 2156 class BadUsage: pass 2157 2158 # Scripts don't get the current directory in their path by default. 2159 scriptdir = os.path.dirname(sys.argv[0]) 2160 if scriptdir in sys.path: 2161 sys.path.remove(scriptdir) 2162 sys.path.insert(0, '.') 2163 2164 try: 2165 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w') 2166 writing = 0 2167 2168 for opt, val in opts: 2169 if opt == '-g': 2170 gui() 2171 return 2172 if opt == '-k': 2173 apropos(val) 2174 return 2175 if opt == '-p': 2176 try: 2177 port = int(val) 2178 except ValueError: 2179 raise BadUsage 2180 def ready(server): 2181 print 'pydoc server ready at %s' % server.url 2182 def stopped(): 2183 print 'pydoc server stopped' 2184 serve(port, ready, stopped) 2185 return 2186 if opt == '-w': 2187 writing = 1 2188 2189 if not args: raise BadUsage 2190 for arg in args: 2191 if ispath(arg) and not os.path.exists(arg): 2192 print 'file %r does not exist' % arg 2193 break 2194 try: 2195 if ispath(arg) and os.path.isfile(arg): 2196 arg = importfile(arg) 2197 if writing: 2198 if ispath(arg) and os.path.isdir(arg): 2199 writedocs(arg) 2200 else: 2201 writedoc(arg) 2202 else: 2203 help.help(arg) 2204 except ErrorDuringImport, value: 2205 print value 2206 2207 except (getopt.error, BadUsage): 2208 cmd = os.path.basename(sys.argv[0]) 2209 print """pydoc - the Python documentation tool 2210 2211 %s <name> ... 2212 Show text documentation on something. <name> may be the name of a 2213 Python keyword, topic, function, module, or package, or a dotted 2214 reference to a class or function within a module or module in a 2215 package. If <name> contains a '%s', it is used as the path to a 2216 Python source file to document. If name is 'keywords', 'topics', 2217 or 'modules', a listing of these things is displayed. 2218 2219 %s -k <keyword> 2220 Search for a keyword in the synopsis lines of all available modules. 2221 2222 %s -p <port> 2223 Start an HTTP server on the given port on the local machine. 2224 2225 %s -g 2226 Pop up a graphical interface for finding and serving documentation. 2227 2228 %s -w <name> ... 2229 Write out the HTML documentation for a module to a file in the current 2230 directory. If <name> contains a '%s', it is treated as a filename; if 2231 it names a directory, documentation is written for all the contents. 2232 """ % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep) 2233 2234 if __name__ == '__main__': cli() 2235
Generated by PyXR 0.9.4