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