SF #1499797, Fix for memory leak in WindowsError_str
[python.git] / Lib / pydoc.py
blobff6e7ca21f84e0e118ca4fc5ac0c7ece08f29319
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://www.python.org/doc/current/lib/
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 repr 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 try:
322 if inspect.ismodule(object): return self.docmodule(*args)
323 if inspect.isclass(object): return self.docclass(*args)
324 if inspect.isroutine(object): return self.docroutine(*args)
325 except AttributeError:
326 pass
327 if isinstance(object, property): return self.docproperty(*args)
328 return self.docother(*args)
330 def fail(self, object, name=None, *args):
331 """Raise an exception for unimplemented types."""
332 message = "don't know how to document object%s of type %s" % (
333 name and ' ' + repr(name), type(object).__name__)
334 raise TypeError, message
336 docmodule = docclass = docroutine = docother = fail
338 def getdocloc(self, object):
339 """Return the location of module docs or None"""
341 try:
342 file = inspect.getabsfile(object)
343 except TypeError:
344 file = '(built-in)'
346 docloc = os.environ.get("PYTHONDOCS",
347 "http://www.python.org/doc/current/lib")
348 basedir = os.path.join(sys.exec_prefix, "lib",
349 "python"+sys.version[0:3])
350 if (isinstance(object, type(os)) and
351 (object.__name__ in ('errno', 'exceptions', 'gc', 'imp',
352 'marshal', 'posix', 'signal', 'sys',
353 'thread', 'zipimport') or
354 (file.startswith(basedir) and
355 not file.startswith(os.path.join(basedir, 'site-packages'))))):
356 htmlfile = "module-%s.html" % object.__name__
357 if docloc.startswith("http://"):
358 docloc = "%s/%s" % (docloc.rstrip("/"), htmlfile)
359 else:
360 docloc = os.path.join(docloc, htmlfile)
361 else:
362 docloc = None
363 return docloc
365 # -------------------------------------------- HTML documentation generator
367 class HTMLRepr(Repr):
368 """Class for safely making an HTML representation of a Python object."""
369 def __init__(self):
370 Repr.__init__(self)
371 self.maxlist = self.maxtuple = 20
372 self.maxdict = 10
373 self.maxstring = self.maxother = 100
375 def escape(self, text):
376 return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')
378 def repr(self, object):
379 return Repr.repr(self, object)
381 def repr1(self, x, level):
382 if hasattr(type(x), '__name__'):
383 methodname = 'repr_' + join(split(type(x).__name__), '_')
384 if hasattr(self, methodname):
385 return getattr(self, methodname)(x, level)
386 return self.escape(cram(stripid(repr(x)), self.maxother))
388 def repr_string(self, x, level):
389 test = cram(x, self.maxstring)
390 testrepr = repr(test)
391 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
392 # Backslashes are only literal in the string and are never
393 # needed to make any special characters, so show a raw string.
394 return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
395 return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
396 r'<font color="#c040c0">\1</font>',
397 self.escape(testrepr))
399 repr_str = repr_string
401 def repr_instance(self, x, level):
402 try:
403 return self.escape(cram(stripid(repr(x)), self.maxstring))
404 except:
405 return self.escape('<%s instance>' % x.__class__.__name__)
407 repr_unicode = repr_string
409 class HTMLDoc(Doc):
410 """Formatter class for HTML documentation."""
412 # ------------------------------------------- HTML formatting utilities
414 _repr_instance = HTMLRepr()
415 repr = _repr_instance.repr
416 escape = _repr_instance.escape
418 def page(self, title, contents):
419 """Format an HTML page."""
420 return '''
421 <!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
422 <html><head><title>Python: %s</title>
423 </head><body bgcolor="#f0f0f8">
425 </body></html>''' % (title, contents)
427 def heading(self, title, fgcol, bgcol, extras=''):
428 """Format a page heading."""
429 return '''
430 <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading">
431 <tr bgcolor="%s">
432 <td valign=bottom>&nbsp;<br>
433 <font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
434 ><td align=right valign=bottom
435 ><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
436 ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
438 def section(self, title, fgcol, bgcol, contents, width=6,
439 prelude='', marginalia=None, gap='&nbsp;'):
440 """Format a section with a heading."""
441 if marginalia is None:
442 marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
443 result = '''<p>
444 <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
445 <tr bgcolor="%s">
446 <td colspan=3 valign=bottom>&nbsp;<br>
447 <font color="%s" face="helvetica, arial">%s</font></td></tr>
448 ''' % (bgcol, fgcol, title)
449 if prelude:
450 result = result + '''
451 <tr bgcolor="%s"><td rowspan=2>%s</td>
452 <td colspan=2>%s</td></tr>
453 <tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
454 else:
455 result = result + '''
456 <tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
458 return result + '\n<td width="100%%">%s</td></tr></table>' % contents
460 def bigsection(self, title, *args):
461 """Format a section with a big heading."""
462 title = '<big><strong>%s</strong></big>' % title
463 return self.section(title, *args)
465 def preformat(self, text):
466 """Format literal preformatted text."""
467 text = self.escape(expandtabs(text))
468 return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
469 ' ', '&nbsp;', '\n', '<br>\n')
471 def multicolumn(self, list, format, cols=4):
472 """Format a list of items into a multi-column list."""
473 result = ''
474 rows = (len(list)+cols-1)/cols
475 for col in range(cols):
476 result = result + '<td width="%d%%" valign=top>' % (100/cols)
477 for i in range(rows*col, rows*col+rows):
478 if i < len(list):
479 result = result + format(list[i]) + '<br>\n'
480 result = result + '</td>'
481 return '<table width="100%%" summary="list"><tr>%s</tr></table>' % result
483 def grey(self, text): return '<font color="#909090">%s</font>' % text
485 def namelink(self, name, *dicts):
486 """Make a link for an identifier, given name-to-URL mappings."""
487 for dict in dicts:
488 if name in dict:
489 return '<a href="%s">%s</a>' % (dict[name], name)
490 return name
492 def classlink(self, object, modname):
493 """Make a link for a class."""
494 name, module = object.__name__, sys.modules.get(object.__module__)
495 if hasattr(module, name) and getattr(module, name) is object:
496 return '<a href="%s.html#%s">%s</a>' % (
497 module.__name__, name, classname(object, modname))
498 return classname(object, modname)
500 def modulelink(self, object):
501 """Make a link for a module."""
502 return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
504 def modpkglink(self, (name, path, ispackage, shadowed)):
505 """Make a link for a module or package to display in an index."""
506 if shadowed:
507 return self.grey(name)
508 if path:
509 url = '%s.%s.html' % (path, name)
510 else:
511 url = '%s.html' % name
512 if ispackage:
513 text = '<strong>%s</strong>&nbsp;(package)' % name
514 else:
515 text = name
516 return '<a href="%s">%s</a>' % (url, text)
518 def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
519 """Mark up some plain text, given a context of symbols to look for.
520 Each context dictionary maps object names to anchor names."""
521 escape = escape or self.escape
522 results = []
523 here = 0
524 pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
525 r'RFC[- ]?(\d+)|'
526 r'PEP[- ]?(\d+)|'
527 r'(self\.)?(\w+))')
528 while True:
529 match = pattern.search(text, here)
530 if not match: break
531 start, end = match.span()
532 results.append(escape(text[here:start]))
534 all, scheme, rfc, pep, selfdot, name = match.groups()
535 if scheme:
536 url = escape(all).replace('"', '&quot;')
537 results.append('<a href="%s">%s</a>' % (url, url))
538 elif rfc:
539 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
540 results.append('<a href="%s">%s</a>' % (url, escape(all)))
541 elif pep:
542 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
543 results.append('<a href="%s">%s</a>' % (url, escape(all)))
544 elif text[end:end+1] == '(':
545 results.append(self.namelink(name, methods, funcs, classes))
546 elif selfdot:
547 results.append('self.<strong>%s</strong>' % name)
548 else:
549 results.append(self.namelink(name, classes))
550 here = end
551 results.append(escape(text[here:]))
552 return join(results, '')
554 # ---------------------------------------------- type-specific routines
556 def formattree(self, tree, modname, parent=None):
557 """Produce HTML for a class tree as given by inspect.getclasstree()."""
558 result = ''
559 for entry in tree:
560 if type(entry) is type(()):
561 c, bases = entry
562 result = result + '<dt><font face="helvetica, arial">'
563 result = result + self.classlink(c, modname)
564 if bases and bases != (parent,):
565 parents = []
566 for base in bases:
567 parents.append(self.classlink(base, modname))
568 result = result + '(' + join(parents, ', ') + ')'
569 result = result + '\n</font></dt>'
570 elif type(entry) is type([]):
571 result = result + '<dd>\n%s</dd>\n' % self.formattree(
572 entry, modname, c)
573 return '<dl>\n%s</dl>\n' % result
575 def docmodule(self, object, name=None, mod=None, *ignored):
576 """Produce HTML documentation for a module object."""
577 name = object.__name__ # ignore the passed-in name
578 try:
579 all = object.__all__
580 except AttributeError:
581 all = None
582 parts = split(name, '.')
583 links = []
584 for i in range(len(parts)-1):
585 links.append(
586 '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
587 (join(parts[:i+1], '.'), parts[i]))
588 linkedname = join(links + parts[-1:], '.')
589 head = '<big><big><strong>%s</strong></big></big>' % linkedname
590 try:
591 path = inspect.getabsfile(object)
592 url = path
593 if sys.platform == 'win32':
594 import nturl2path
595 url = nturl2path.pathname2url(path)
596 filelink = '<a href="file:%s">%s</a>' % (url, path)
597 except TypeError:
598 filelink = '(built-in)'
599 info = []
600 if hasattr(object, '__version__'):
601 version = str(object.__version__)
602 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
603 version = strip(version[11:-1])
604 info.append('version %s' % self.escape(version))
605 if hasattr(object, '__date__'):
606 info.append(self.escape(str(object.__date__)))
607 if info:
608 head = head + ' (%s)' % join(info, ', ')
609 docloc = self.getdocloc(object)
610 if docloc is not None:
611 docloc = '<br><a href="%(docloc)s">Module Docs</a>' % locals()
612 else:
613 docloc = ''
614 result = self.heading(
615 head, '#ffffff', '#7799ee',
616 '<a href=".">index</a><br>' + filelink + docloc)
618 modules = inspect.getmembers(object, inspect.ismodule)
620 classes, cdict = [], {}
621 for key, value in inspect.getmembers(object, inspect.isclass):
622 # if __all__ exists, believe it. Otherwise use old heuristic.
623 if (all is not None or
624 (inspect.getmodule(value) or object) is object):
625 if visiblename(key, all):
626 classes.append((key, value))
627 cdict[key] = cdict[value] = '#' + key
628 for key, value in classes:
629 for base in value.__bases__:
630 key, modname = base.__name__, base.__module__
631 module = sys.modules.get(modname)
632 if modname != name and module and hasattr(module, key):
633 if getattr(module, key) is base:
634 if not key in cdict:
635 cdict[key] = cdict[base] = modname + '.html#' + key
636 funcs, fdict = [], {}
637 for key, value in inspect.getmembers(object, inspect.isroutine):
638 # if __all__ exists, believe it. Otherwise use old heuristic.
639 if (all is not None or
640 inspect.isbuiltin(value) or inspect.getmodule(value) is object):
641 if visiblename(key, all):
642 funcs.append((key, value))
643 fdict[key] = '#-' + key
644 if inspect.isfunction(value): fdict[value] = fdict[key]
645 data = []
646 for key, value in inspect.getmembers(object, isdata):
647 if visiblename(key, all):
648 data.append((key, value))
650 doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
651 doc = doc and '<tt>%s</tt>' % doc
652 result = result + '<p>%s</p>\n' % doc
654 if hasattr(object, '__path__'):
655 modpkgs = []
656 for importer, modname, ispkg in pkgutil.iter_modules(object.__path__):
657 modpkgs.append((modname, name, ispkg, 0))
658 modpkgs.sort()
659 contents = self.multicolumn(modpkgs, self.modpkglink)
660 result = result + self.bigsection(
661 'Package Contents', '#ffffff', '#aa55cc', contents)
662 elif modules:
663 contents = self.multicolumn(
664 modules, lambda (key, value), s=self: s.modulelink(value))
665 result = result + self.bigsection(
666 'Modules', '#fffff', '#aa55cc', contents)
668 if classes:
669 classlist = map(lambda (key, value): value, classes)
670 contents = [
671 self.formattree(inspect.getclasstree(classlist, 1), name)]
672 for key, value in classes:
673 contents.append(self.document(value, key, name, fdict, cdict))
674 result = result + self.bigsection(
675 'Classes', '#ffffff', '#ee77aa', join(contents))
676 if funcs:
677 contents = []
678 for key, value in funcs:
679 contents.append(self.document(value, key, name, fdict, cdict))
680 result = result + self.bigsection(
681 'Functions', '#ffffff', '#eeaa77', join(contents))
682 if data:
683 contents = []
684 for key, value in data:
685 contents.append(self.document(value, key))
686 result = result + self.bigsection(
687 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))
688 if hasattr(object, '__author__'):
689 contents = self.markup(str(object.__author__), self.preformat)
690 result = result + self.bigsection(
691 'Author', '#ffffff', '#7799ee', contents)
692 if hasattr(object, '__credits__'):
693 contents = self.markup(str(object.__credits__), self.preformat)
694 result = result + self.bigsection(
695 'Credits', '#ffffff', '#7799ee', contents)
697 return result
699 def docclass(self, object, name=None, mod=None, funcs={}, classes={},
700 *ignored):
701 """Produce HTML documentation for a class object."""
702 realname = object.__name__
703 name = name or realname
704 bases = object.__bases__
706 contents = []
707 push = contents.append
709 # Cute little class to pump out a horizontal rule between sections.
710 class HorizontalRule:
711 def __init__(self):
712 self.needone = 0
713 def maybe(self):
714 if self.needone:
715 push('<hr>\n')
716 self.needone = 1
717 hr = HorizontalRule()
719 # List the mro, if non-trivial.
720 mro = deque(inspect.getmro(object))
721 if len(mro) > 2:
722 hr.maybe()
723 push('<dl><dt>Method resolution order:</dt>\n')
724 for base in mro:
725 push('<dd>%s</dd>\n' % self.classlink(base,
726 object.__module__))
727 push('</dl>\n')
729 def spill(msg, attrs, predicate):
730 ok, attrs = _split_list(attrs, predicate)
731 if ok:
732 hr.maybe()
733 push(msg)
734 for name, kind, homecls, value in ok:
735 push(self.document(getattr(object, name), name, mod,
736 funcs, classes, mdict, object))
737 push('\n')
738 return attrs
740 def spilldescriptors(msg, attrs, predicate):
741 ok, attrs = _split_list(attrs, predicate)
742 if ok:
743 hr.maybe()
744 push(msg)
745 for name, kind, homecls, value in ok:
746 push(self._docdescriptor(name, value, mod))
747 return attrs
749 def spilldata(msg, attrs, predicate):
750 ok, attrs = _split_list(attrs, predicate)
751 if ok:
752 hr.maybe()
753 push(msg)
754 for name, kind, homecls, value in ok:
755 base = self.docother(getattr(object, name), name, mod)
756 if callable(value) or inspect.isdatadescriptor(value):
757 doc = getattr(value, "__doc__", None)
758 else:
759 doc = None
760 if doc is None:
761 push('<dl><dt>%s</dl>\n' % base)
762 else:
763 doc = self.markup(getdoc(value), self.preformat,
764 funcs, classes, mdict)
765 doc = '<dd><tt>%s</tt>' % doc
766 push('<dl><dt>%s%s</dl>\n' % (base, doc))
767 push('\n')
768 return attrs
770 attrs = filter(lambda (name, kind, cls, value): visiblename(name),
771 classify_class_attrs(object))
772 mdict = {}
773 for key, kind, homecls, value in attrs:
774 mdict[key] = anchor = '#' + name + '-' + key
775 value = getattr(object, key)
776 try:
777 # The value may not be hashable (e.g., a data attr with
778 # a dict or list value).
779 mdict[value] = anchor
780 except TypeError:
781 pass
783 while attrs:
784 if mro:
785 thisclass = mro.popleft()
786 else:
787 thisclass = attrs[0][2]
788 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
790 if thisclass is __builtin__.object:
791 attrs = inherited
792 continue
793 elif thisclass is object:
794 tag = 'defined here'
795 else:
796 tag = 'inherited from %s' % self.classlink(thisclass,
797 object.__module__)
798 tag += ':<br>\n'
800 # Sort attrs by name.
801 try:
802 attrs.sort(key=lambda t: t[0])
803 except TypeError:
804 attrs.sort(lambda t1, t2: cmp(t1[0], t2[0])) # 2.3 compat
806 # Pump out the attrs, segregated by kind.
807 attrs = spill('Methods %s' % tag, attrs,
808 lambda t: t[1] == 'method')
809 attrs = spill('Class methods %s' % tag, attrs,
810 lambda t: t[1] == 'class method')
811 attrs = spill('Static methods %s' % tag, attrs,
812 lambda t: t[1] == 'static method')
813 attrs = spilldescriptors('Data descriptors %s' % tag, attrs,
814 lambda t: t[1] == 'data descriptor')
815 attrs = spilldata('Data and other attributes %s' % tag, attrs,
816 lambda t: t[1] == 'data')
817 assert attrs == []
818 attrs = inherited
820 contents = ''.join(contents)
822 if name == realname:
823 title = '<a name="%s">class <strong>%s</strong></a>' % (
824 name, realname)
825 else:
826 title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
827 name, name, realname)
828 if bases:
829 parents = []
830 for base in bases:
831 parents.append(self.classlink(base, object.__module__))
832 title = title + '(%s)' % join(parents, ', ')
833 doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict)
834 doc = doc and '<tt>%s<br>&nbsp;</tt>' % doc
836 return self.section(title, '#000000', '#ffc8d8', contents, 3, doc)
838 def formatvalue(self, object):
839 """Format an argument default value as text."""
840 return self.grey('=' + self.repr(object))
842 def docroutine(self, object, name=None, mod=None,
843 funcs={}, classes={}, methods={}, cl=None):
844 """Produce HTML documentation for a function or method object."""
845 realname = object.__name__
846 name = name or realname
847 anchor = (cl and cl.__name__ or '') + '-' + name
848 note = ''
849 skipdocs = 0
850 if inspect.ismethod(object):
851 imclass = object.im_class
852 if cl:
853 if imclass is not cl:
854 note = ' from ' + self.classlink(imclass, mod)
855 else:
856 if object.im_self:
857 note = ' method of %s instance' % self.classlink(
858 object.im_self.__class__, mod)
859 else:
860 note = ' unbound %s method' % self.classlink(imclass,mod)
861 object = object.im_func
863 if name == realname:
864 title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
865 else:
866 if (cl and realname in cl.__dict__ and
867 cl.__dict__[realname] is object):
868 reallink = '<a href="#%s">%s</a>' % (
869 cl.__name__ + '-' + realname, realname)
870 skipdocs = 1
871 else:
872 reallink = realname
873 title = '<a name="%s"><strong>%s</strong></a> = %s' % (
874 anchor, name, reallink)
875 if inspect.isfunction(object):
876 args, varargs, varkw, defaults = inspect.getargspec(object)
877 argspec = inspect.formatargspec(
878 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
879 if realname == '<lambda>':
880 title = '<strong>%s</strong> <em>lambda</em> ' % name
881 argspec = argspec[1:-1] # remove parentheses
882 else:
883 argspec = '(...)'
885 decl = title + argspec + (note and self.grey(
886 '<font face="helvetica, arial">%s</font>' % note))
888 if skipdocs:
889 return '<dl><dt>%s</dt></dl>\n' % decl
890 else:
891 doc = self.markup(
892 getdoc(object), self.preformat, funcs, classes, methods)
893 doc = doc and '<dd><tt>%s</tt></dd>' % doc
894 return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
896 def _docdescriptor(self, name, value, mod):
897 results = []
898 push = results.append
900 if name:
901 push('<dl><dt><strong>%s</strong></dt>\n' % name)
902 if value.__doc__ is not None:
903 doc = self.markup(getdoc(value), self.preformat)
904 push('<dd><tt>%s</tt></dd>\n' % doc)
905 push('</dl>\n')
907 return ''.join(results)
909 def docproperty(self, object, name=None, mod=None, cl=None):
910 """Produce html documentation for a property."""
911 return self._docdescriptor(name, object, mod)
913 def docother(self, object, name=None, mod=None, *ignored):
914 """Produce HTML documentation for a data object."""
915 lhs = name and '<strong>%s</strong> = ' % name or ''
916 return lhs + self.repr(object)
918 def index(self, dir, shadowed=None):
919 """Generate an HTML index for a directory of modules."""
920 modpkgs = []
921 if shadowed is None: shadowed = {}
922 for importer, name, ispkg in pkgutil.iter_modules([dir]):
923 modpkgs.append((name, '', ispkg, name in shadowed))
924 shadowed[name] = 1
926 modpkgs.sort()
927 contents = self.multicolumn(modpkgs, self.modpkglink)
928 return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
930 # -------------------------------------------- text documentation generator
932 class TextRepr(Repr):
933 """Class for safely making a text representation of a Python object."""
934 def __init__(self):
935 Repr.__init__(self)
936 self.maxlist = self.maxtuple = 20
937 self.maxdict = 10
938 self.maxstring = self.maxother = 100
940 def repr1(self, x, level):
941 if hasattr(type(x), '__name__'):
942 methodname = 'repr_' + join(split(type(x).__name__), '_')
943 if hasattr(self, methodname):
944 return getattr(self, methodname)(x, level)
945 return cram(stripid(repr(x)), self.maxother)
947 def repr_string(self, x, level):
948 test = cram(x, self.maxstring)
949 testrepr = repr(test)
950 if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
951 # Backslashes are only literal in the string and are never
952 # needed to make any special characters, so show a raw string.
953 return 'r' + testrepr[0] + test + testrepr[0]
954 return testrepr
956 repr_str = repr_string
958 def repr_instance(self, x, level):
959 try:
960 return cram(stripid(repr(x)), self.maxstring)
961 except:
962 return '<%s instance>' % x.__class__.__name__
964 class TextDoc(Doc):
965 """Formatter class for text documentation."""
967 # ------------------------------------------- text formatting utilities
969 _repr_instance = TextRepr()
970 repr = _repr_instance.repr
972 def bold(self, text):
973 """Format a string in bold by overstriking."""
974 return join(map(lambda ch: ch + '\b' + ch, text), '')
976 def indent(self, text, prefix=' '):
977 """Indent text by prepending a given prefix to each line."""
978 if not text: return ''
979 lines = split(text, '\n')
980 lines = map(lambda line, prefix=prefix: prefix + line, lines)
981 if lines: lines[-1] = rstrip(lines[-1])
982 return join(lines, '\n')
984 def section(self, title, contents):
985 """Format a section with a given heading."""
986 return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
988 # ---------------------------------------------- type-specific routines
990 def formattree(self, tree, modname, parent=None, prefix=''):
991 """Render in text a class tree as returned by inspect.getclasstree()."""
992 result = ''
993 for entry in tree:
994 if type(entry) is type(()):
995 c, bases = entry
996 result = result + prefix + classname(c, modname)
997 if bases and bases != (parent,):
998 parents = map(lambda c, m=modname: classname(c, m), bases)
999 result = result + '(%s)' % join(parents, ', ')
1000 result = result + '\n'
1001 elif type(entry) is type([]):
1002 result = result + self.formattree(
1003 entry, modname, c, prefix + ' ')
1004 return result
1006 def docmodule(self, object, name=None, mod=None):
1007 """Produce text documentation for a given module object."""
1008 name = object.__name__ # ignore the passed-in name
1009 synop, desc = splitdoc(getdoc(object))
1010 result = self.section('NAME', name + (synop and ' - ' + synop))
1012 try:
1013 all = object.__all__
1014 except AttributeError:
1015 all = None
1017 try:
1018 file = inspect.getabsfile(object)
1019 except TypeError:
1020 file = '(built-in)'
1021 result = result + self.section('FILE', file)
1023 docloc = self.getdocloc(object)
1024 if docloc is not None:
1025 result = result + self.section('MODULE DOCS', docloc)
1027 if desc:
1028 result = result + self.section('DESCRIPTION', desc)
1030 classes = []
1031 for key, value in inspect.getmembers(object, inspect.isclass):
1032 # if __all__ exists, believe it. Otherwise use old heuristic.
1033 if (all is not None
1034 or (inspect.getmodule(value) or object) is object):
1035 if visiblename(key, all):
1036 classes.append((key, value))
1037 funcs = []
1038 for key, value in inspect.getmembers(object, inspect.isroutine):
1039 # if __all__ exists, believe it. Otherwise use old heuristic.
1040 if (all is not None or
1041 inspect.isbuiltin(value) or inspect.getmodule(value) is object):
1042 if visiblename(key, all):
1043 funcs.append((key, value))
1044 data = []
1045 for key, value in inspect.getmembers(object, isdata):
1046 if visiblename(key, all):
1047 data.append((key, value))
1049 if hasattr(object, '__path__'):
1050 modpkgs = []
1051 for importer, modname, ispkg in pkgutil.iter_modules(object.__path__):
1052 if ispkg:
1053 modpkgs.append(modname + ' (package)')
1054 else:
1055 modpkgs.append(modname)
1057 modpkgs.sort()
1058 result = result + self.section(
1059 'PACKAGE CONTENTS', join(modpkgs, '\n'))
1061 if classes:
1062 classlist = map(lambda (key, value): value, classes)
1063 contents = [self.formattree(
1064 inspect.getclasstree(classlist, 1), name)]
1065 for key, value in classes:
1066 contents.append(self.document(value, key, name))
1067 result = result + self.section('CLASSES', join(contents, '\n'))
1069 if funcs:
1070 contents = []
1071 for key, value in funcs:
1072 contents.append(self.document(value, key, name))
1073 result = result + self.section('FUNCTIONS', join(contents, '\n'))
1075 if data:
1076 contents = []
1077 for key, value in data:
1078 contents.append(self.docother(value, key, name, maxlen=70))
1079 result = result + self.section('DATA', join(contents, '\n'))
1081 if hasattr(object, '__version__'):
1082 version = str(object.__version__)
1083 if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
1084 version = strip(version[11:-1])
1085 result = result + self.section('VERSION', version)
1086 if hasattr(object, '__date__'):
1087 result = result + self.section('DATE', str(object.__date__))
1088 if hasattr(object, '__author__'):
1089 result = result + self.section('AUTHOR', str(object.__author__))
1090 if hasattr(object, '__credits__'):
1091 result = result + self.section('CREDITS', str(object.__credits__))
1092 return result
1094 def docclass(self, object, name=None, mod=None):
1095 """Produce text documentation for a given class object."""
1096 realname = object.__name__
1097 name = name or realname
1098 bases = object.__bases__
1100 def makename(c, m=object.__module__):
1101 return classname(c, m)
1103 if name == realname:
1104 title = 'class ' + self.bold(realname)
1105 else:
1106 title = self.bold(name) + ' = class ' + realname
1107 if bases:
1108 parents = map(makename, bases)
1109 title = title + '(%s)' % join(parents, ', ')
1111 doc = getdoc(object)
1112 contents = doc and [doc + '\n'] or []
1113 push = contents.append
1115 # List the mro, if non-trivial.
1116 mro = deque(inspect.getmro(object))
1117 if len(mro) > 2:
1118 push("Method resolution order:")
1119 for base in mro:
1120 push(' ' + makename(base))
1121 push('')
1123 # Cute little class to pump out a horizontal rule between sections.
1124 class HorizontalRule:
1125 def __init__(self):
1126 self.needone = 0
1127 def maybe(self):
1128 if self.needone:
1129 push('-' * 70)
1130 self.needone = 1
1131 hr = HorizontalRule()
1133 def spill(msg, attrs, predicate):
1134 ok, attrs = _split_list(attrs, predicate)
1135 if ok:
1136 hr.maybe()
1137 push(msg)
1138 for name, kind, homecls, value in ok:
1139 push(self.document(getattr(object, name),
1140 name, mod, object))
1141 return attrs
1143 def spilldescriptors(msg, attrs, predicate):
1144 ok, attrs = _split_list(attrs, predicate)
1145 if ok:
1146 hr.maybe()
1147 push(msg)
1148 for name, kind, homecls, value in ok:
1149 push(self._docdescriptor(name, value, mod))
1150 return attrs
1152 def spilldata(msg, attrs, predicate):
1153 ok, attrs = _split_list(attrs, predicate)
1154 if ok:
1155 hr.maybe()
1156 push(msg)
1157 for name, kind, homecls, value in ok:
1158 if callable(value) or inspect.isdatadescriptor(value):
1159 doc = getdoc(value)
1160 else:
1161 doc = None
1162 push(self.docother(getattr(object, name),
1163 name, mod, maxlen=70, doc=doc) + '\n')
1164 return attrs
1166 attrs = filter(lambda (name, kind, cls, value): visiblename(name),
1167 classify_class_attrs(object))
1168 while attrs:
1169 if mro:
1170 thisclass = mro.popleft()
1171 else:
1172 thisclass = attrs[0][2]
1173 attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
1175 if thisclass is __builtin__.object:
1176 attrs = inherited
1177 continue
1178 elif thisclass is object:
1179 tag = "defined here"
1180 else:
1181 tag = "inherited from %s" % classname(thisclass,
1182 object.__module__)
1183 filter(lambda t: not t[0].startswith('_'), attrs)
1185 # Sort attrs by name.
1186 attrs.sort()
1188 # Pump out the attrs, segregated by kind.
1189 attrs = spill("Methods %s:\n" % tag, attrs,
1190 lambda t: t[1] == 'method')
1191 attrs = spill("Class methods %s:\n" % tag, attrs,
1192 lambda t: t[1] == 'class method')
1193 attrs = spill("Static methods %s:\n" % tag, attrs,
1194 lambda t: t[1] == 'static method')
1195 attrs = spilldescriptors("Data descriptors %s:\n" % tag, attrs,
1196 lambda t: t[1] == 'data descriptor')
1197 attrs = spilldata("Data and other attributes %s:\n" % tag, attrs,
1198 lambda t: t[1] == 'data')
1199 assert attrs == []
1200 attrs = inherited
1202 contents = '\n'.join(contents)
1203 if not contents:
1204 return title + '\n'
1205 return title + '\n' + self.indent(rstrip(contents), ' | ') + '\n'
1207 def formatvalue(self, object):
1208 """Format an argument default value as text."""
1209 return '=' + self.repr(object)
1211 def docroutine(self, object, name=None, mod=None, cl=None):
1212 """Produce text documentation for a function or method object."""
1213 realname = object.__name__
1214 name = name or realname
1215 note = ''
1216 skipdocs = 0
1217 if inspect.ismethod(object):
1218 imclass = object.im_class
1219 if cl:
1220 if imclass is not cl:
1221 note = ' from ' + classname(imclass, mod)
1222 else:
1223 if object.im_self:
1224 note = ' method of %s instance' % classname(
1225 object.im_self.__class__, mod)
1226 else:
1227 note = ' unbound %s method' % classname(imclass,mod)
1228 object = object.im_func
1230 if name == realname:
1231 title = self.bold(realname)
1232 else:
1233 if (cl and realname in cl.__dict__ and
1234 cl.__dict__[realname] is object):
1235 skipdocs = 1
1236 title = self.bold(name) + ' = ' + realname
1237 if inspect.isfunction(object):
1238 args, varargs, varkw, defaults = inspect.getargspec(object)
1239 argspec = inspect.formatargspec(
1240 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
1241 if realname == '<lambda>':
1242 title = self.bold(name) + ' lambda '
1243 argspec = argspec[1:-1] # remove parentheses
1244 else:
1245 argspec = '(...)'
1246 decl = title + argspec + note
1248 if skipdocs:
1249 return decl + '\n'
1250 else:
1251 doc = getdoc(object) or ''
1252 return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
1254 def _docdescriptor(self, name, value, mod):
1255 results = []
1256 push = results.append
1258 if name:
1259 push(self.bold(name))
1260 push('\n')
1261 doc = getdoc(value) or ''
1262 if doc:
1263 push(self.indent(doc))
1264 push('\n')
1265 return ''.join(results)
1267 def docproperty(self, object, name=None, mod=None, cl=None):
1268 """Produce text documentation for a property."""
1269 return self._docdescriptor(name, object, mod)
1271 def docother(self, object, name=None, mod=None, parent=None, maxlen=None, doc=None):
1272 """Produce text documentation for a data object."""
1273 repr = self.repr(object)
1274 if maxlen:
1275 line = (name and name + ' = ' or '') + repr
1276 chop = maxlen - len(line)
1277 if chop < 0: repr = repr[:chop] + '...'
1278 line = (name and self.bold(name) + ' = ' or '') + repr
1279 if doc is not None:
1280 line += '\n' + self.indent(str(doc))
1281 return line
1283 # --------------------------------------------------------- user interfaces
1285 def pager(text):
1286 """The first time this is called, determine what kind of pager to use."""
1287 global pager
1288 pager = getpager()
1289 pager(text)
1291 def getpager():
1292 """Decide what method to use for paging through text."""
1293 if type(sys.stdout) is not types.FileType:
1294 return plainpager
1295 if not sys.stdin.isatty() or not sys.stdout.isatty():
1296 return plainpager
1297 if 'PAGER' in os.environ:
1298 if sys.platform == 'win32': # pipes completely broken in Windows
1299 return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
1300 elif os.environ.get('TERM') in ('dumb', 'emacs'):
1301 return lambda text: pipepager(plain(text), os.environ['PAGER'])
1302 else:
1303 return lambda text: pipepager(text, os.environ['PAGER'])
1304 if os.environ.get('TERM') in ('dumb', 'emacs'):
1305 return plainpager
1306 if sys.platform == 'win32' or sys.platform.startswith('os2'):
1307 return lambda text: tempfilepager(plain(text), 'more <')
1308 if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0:
1309 return lambda text: pipepager(text, 'less')
1311 import tempfile
1312 (fd, filename) = tempfile.mkstemp()
1313 os.close(fd)
1314 try:
1315 if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
1316 return lambda text: pipepager(text, 'more')
1317 else:
1318 return ttypager
1319 finally:
1320 os.unlink(filename)
1322 def plain(text):
1323 """Remove boldface formatting from text."""
1324 return re.sub('.\b', '', text)
1326 def pipepager(text, cmd):
1327 """Page through text by feeding it to another program."""
1328 pipe = os.popen(cmd, 'w')
1329 try:
1330 pipe.write(text)
1331 pipe.close()
1332 except IOError:
1333 pass # Ignore broken pipes caused by quitting the pager program.
1335 def tempfilepager(text, cmd):
1336 """Page through text by invoking a program on a temporary file."""
1337 import tempfile
1338 filename = tempfile.mktemp()
1339 file = open(filename, 'w')
1340 file.write(text)
1341 file.close()
1342 try:
1343 os.system(cmd + ' ' + filename)
1344 finally:
1345 os.unlink(filename)
1347 def ttypager(text):
1348 """Page through text on a text terminal."""
1349 lines = split(plain(text), '\n')
1350 try:
1351 import tty
1352 fd = sys.stdin.fileno()
1353 old = tty.tcgetattr(fd)
1354 tty.setcbreak(fd)
1355 getchar = lambda: sys.stdin.read(1)
1356 except (ImportError, AttributeError):
1357 tty = None
1358 getchar = lambda: sys.stdin.readline()[:-1][:1]
1360 try:
1361 r = inc = os.environ.get('LINES', 25) - 1
1362 sys.stdout.write(join(lines[:inc], '\n') + '\n')
1363 while lines[r:]:
1364 sys.stdout.write('-- more --')
1365 sys.stdout.flush()
1366 c = getchar()
1368 if c in ('q', 'Q'):
1369 sys.stdout.write('\r \r')
1370 break
1371 elif c in ('\r', '\n'):
1372 sys.stdout.write('\r \r' + lines[r] + '\n')
1373 r = r + 1
1374 continue
1375 if c in ('b', 'B', '\x1b'):
1376 r = r - inc - inc
1377 if r < 0: r = 0
1378 sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1379 r = r + inc
1381 finally:
1382 if tty:
1383 tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1385 def plainpager(text):
1386 """Simply print unformatted text. This is the ultimate fallback."""
1387 sys.stdout.write(plain(text))
1389 def describe(thing):
1390 """Produce a short description of the given thing."""
1391 if inspect.ismodule(thing):
1392 if thing.__name__ in sys.builtin_module_names:
1393 return 'built-in module ' + thing.__name__
1394 if hasattr(thing, '__path__'):
1395 return 'package ' + thing.__name__
1396 else:
1397 return 'module ' + thing.__name__
1398 if inspect.isbuiltin(thing):
1399 return 'built-in function ' + thing.__name__
1400 if inspect.isclass(thing):
1401 return 'class ' + thing.__name__
1402 if inspect.isfunction(thing):
1403 return 'function ' + thing.__name__
1404 if inspect.ismethod(thing):
1405 return 'method ' + thing.__name__
1406 if type(thing) is types.InstanceType:
1407 return 'instance of ' + thing.__class__.__name__
1408 return type(thing).__name__
1410 def locate(path, forceload=0):
1411 """Locate an object by name or dotted path, importing as necessary."""
1412 parts = [part for part in split(path, '.') if part]
1413 module, n = None, 0
1414 while n < len(parts):
1415 nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
1416 if nextmodule: module, n = nextmodule, n + 1
1417 else: break
1418 if module:
1419 object = module
1420 for part in parts[n:]:
1421 try: object = getattr(object, part)
1422 except AttributeError: return None
1423 return object
1424 else:
1425 if hasattr(__builtin__, path):
1426 return getattr(__builtin__, path)
1428 # --------------------------------------- interactive interpreter interface
1430 text = TextDoc()
1431 html = HTMLDoc()
1433 def resolve(thing, forceload=0):
1434 """Given an object or a path to an object, get the object and its name."""
1435 if isinstance(thing, str):
1436 object = locate(thing, forceload)
1437 if not object:
1438 raise ImportError, 'no Python documentation found for %r' % thing
1439 return object, thing
1440 else:
1441 return thing, getattr(thing, '__name__', None)
1443 def doc(thing, title='Python Library Documentation: %s', forceload=0):
1444 """Display text documentation, given an object or a path to an object."""
1445 try:
1446 object, name = resolve(thing, forceload)
1447 desc = describe(object)
1448 module = inspect.getmodule(object)
1449 if name and '.' in name:
1450 desc += ' in ' + name[:name.rfind('.')]
1451 elif module and module is not object:
1452 desc += ' in module ' + module.__name__
1453 if not (inspect.ismodule(object) or
1454 inspect.isclass(object) or
1455 inspect.isroutine(object) or
1456 isinstance(object, property)):
1457 # If the passed object is a piece of data or an instance,
1458 # document its available methods instead of its value.
1459 object = type(object)
1460 desc += ' object'
1461 pager(title % desc + '\n\n' + text.document(object, name))
1462 except (ImportError, ErrorDuringImport), value:
1463 print value
1465 def writedoc(thing, forceload=0):
1466 """Write HTML documentation to a file in the current directory."""
1467 try:
1468 object, name = resolve(thing, forceload)
1469 page = html.page(describe(object), html.document(object, name))
1470 file = open(name + '.html', 'w')
1471 file.write(page)
1472 file.close()
1473 print 'wrote', name + '.html'
1474 except (ImportError, ErrorDuringImport), value:
1475 print value
1477 def writedocs(dir, pkgpath='', done=None):
1478 """Write out HTML documentation for all modules in a directory tree."""
1479 if done is None: done = {}
1480 for importer, modname, ispkg in pkgutil.walk_packages([dir], pkgpath):
1481 writedoc(modname)
1482 return
1484 class Helper:
1485 keywords = {
1486 'and': 'BOOLEAN',
1487 'assert': ('ref/assert', ''),
1488 'break': ('ref/break', 'while for'),
1489 'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1490 'continue': ('ref/continue', 'while for'),
1491 'def': ('ref/function', ''),
1492 'del': ('ref/del', 'BASICMETHODS'),
1493 'elif': 'if',
1494 'else': ('ref/if', 'while for'),
1495 'except': 'try',
1496 'exec': ('ref/exec', ''),
1497 'finally': 'try',
1498 'for': ('ref/for', 'break continue while'),
1499 'from': 'import',
1500 'global': ('ref/global', 'NAMESPACES'),
1501 'if': ('ref/if', 'TRUTHVALUE'),
1502 'import': ('ref/import', 'MODULES'),
1503 'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1504 'is': 'COMPARISON',
1505 'lambda': ('ref/lambdas', 'FUNCTIONS'),
1506 'not': 'BOOLEAN',
1507 'or': 'BOOLEAN',
1508 'pass': ('ref/pass', ''),
1509 'print': ('ref/print', ''),
1510 'raise': ('ref/raise', 'EXCEPTIONS'),
1511 'return': ('ref/return', 'FUNCTIONS'),
1512 'try': ('ref/try', 'EXCEPTIONS'),
1513 'while': ('ref/while', 'break continue if TRUTHVALUE'),
1514 'yield': ('ref/yield', ''),
1517 topics = {
1518 'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'),
1519 'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'),
1520 'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'),
1521 'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'),
1522 'UNICODE': ('ref/strings', 'encodings unicode SEQUENCES STRINGMETHODS FORMATTING TYPES'),
1523 'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1524 'INTEGER': ('ref/integers', 'int range'),
1525 'FLOAT': ('ref/floating', 'float math'),
1526 'COMPLEX': ('ref/imaginary', 'complex cmath'),
1527 'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
1528 'MAPPINGS': 'DICTIONARIES',
1529 'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'),
1530 'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'),
1531 'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'),
1532 'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'),
1533 'FRAMEOBJECTS': 'TYPES',
1534 'TRACEBACKS': 'TYPES',
1535 'NONE': ('lib/bltin-null-object', ''),
1536 'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'),
1537 'FILES': ('lib/bltin-file-objects', ''),
1538 'SPECIALATTRIBUTES': ('lib/specialattrs', ''),
1539 'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'),
1540 'MODULES': ('lib/typesmodules', 'import'),
1541 'PACKAGES': 'import',
1542 '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'),
1543 'OPERATORS': 'EXPRESSIONS',
1544 'PRECEDENCE': 'EXPRESSIONS',
1545 'OBJECTS': ('ref/objects', 'TYPES'),
1546 'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
1547 'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'),
1548 'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1549 'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'),
1550 'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'),
1551 'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'),
1552 'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'),
1553 'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'),
1554 'EXECUTION': ('ref/execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'),
1555 'NAMESPACES': ('ref/naming', 'global ASSIGNMENT DELETION DYNAMICFEATURES'),
1556 'DYNAMICFEATURES': ('ref/dynamic-features', ''),
1557 'SCOPING': 'NAMESPACES',
1558 'FRAMES': 'NAMESPACES',
1559 'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'),
1560 'COERCIONS': ('ref/coercion-rules','CONVERSIONS'),
1561 'CONVERSIONS': ('ref/conversions', 'COERCIONS'),
1562 'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'),
1563 'SPECIALIDENTIFIERS': ('ref/id-classes', ''),
1564 'PRIVATENAMES': ('ref/atom-identifiers', ''),
1565 'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1566 'TUPLES': 'SEQUENCES',
1567 'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'),
1568 'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'),
1569 'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'),
1570 'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'),
1571 'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'),
1572 'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'),
1573 'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1574 'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'),
1575 'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'),
1576 'CALLS': ('ref/calls', 'EXPRESSIONS'),
1577 'POWER': ('ref/power', 'EXPRESSIONS'),
1578 'UNARY': ('ref/unary', 'EXPRESSIONS'),
1579 'BINARY': ('ref/binary', 'EXPRESSIONS'),
1580 'SHIFTING': ('ref/shifting', 'EXPRESSIONS'),
1581 'BITWISE': ('ref/bitwise', 'EXPRESSIONS'),
1582 'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'),
1583 'BOOLEAN': ('ref/Booleans', 'EXPRESSIONS TRUTHVALUE'),
1584 'ASSERTION': 'assert',
1585 'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'),
1586 'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'),
1587 'DELETION': 'del',
1588 'PRINTING': 'print',
1589 'RETURNING': 'return',
1590 'IMPORTING': 'import',
1591 'CONDITIONAL': 'if',
1592 'LOOPING': ('ref/compound', 'for while break continue'),
1593 'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'),
1594 'DEBUGGING': ('lib/module-pdb', 'pdb'),
1597 def __init__(self, input, output):
1598 self.input = input
1599 self.output = output
1600 self.docdir = None
1601 execdir = os.path.dirname(sys.executable)
1602 homedir = os.environ.get('PYTHONHOME')
1603 for dir in [os.environ.get('PYTHONDOCS'),
1604 homedir and os.path.join(homedir, 'doc'),
1605 os.path.join(execdir, 'doc'),
1606 '/usr/doc/python-docs-' + split(sys.version)[0],
1607 '/usr/doc/python-' + split(sys.version)[0],
1608 '/usr/doc/python-docs-' + sys.version[:3],
1609 '/usr/doc/python-' + sys.version[:3],
1610 os.path.join(sys.prefix, 'Resources/English.lproj/Documentation')]:
1611 if dir and os.path.isdir(os.path.join(dir, 'lib')):
1612 self.docdir = dir
1614 def __repr__(self):
1615 if inspect.stack()[1][3] == '?':
1616 self()
1617 return ''
1618 return '<pydoc.Helper instance>'
1620 def __call__(self, request=None):
1621 if request is not None:
1622 self.help(request)
1623 else:
1624 self.intro()
1625 self.interact()
1626 self.output.write('''
1627 You are now leaving help and returning to the Python interpreter.
1628 If you want to ask for help on a particular object directly from the
1629 interpreter, you can type "help(object)". Executing "help('string')"
1630 has the same effect as typing a particular string at the help> prompt.
1631 ''')
1633 def interact(self):
1634 self.output.write('\n')
1635 while True:
1636 try:
1637 request = self.getline('help> ')
1638 if not request: break
1639 except (KeyboardInterrupt, EOFError):
1640 break
1641 request = strip(replace(request, '"', '', "'", ''))
1642 if lower(request) in ('q', 'quit'): break
1643 self.help(request)
1645 def getline(self, prompt):
1646 """Read one line, using raw_input when available."""
1647 if self.input is sys.stdin:
1648 return raw_input(prompt)
1649 else:
1650 self.output.write(prompt)
1651 self.output.flush()
1652 return self.input.readline()
1654 def help(self, request):
1655 if type(request) is type(''):
1656 if request == 'help': self.intro()
1657 elif request == 'keywords': self.listkeywords()
1658 elif request == 'topics': self.listtopics()
1659 elif request == 'modules': self.listmodules()
1660 elif request[:8] == 'modules ':
1661 self.listmodules(split(request)[1])
1662 elif request in self.keywords: self.showtopic(request)
1663 elif request in self.topics: self.showtopic(request)
1664 elif request: doc(request, 'Help on %s:')
1665 elif isinstance(request, Helper): self()
1666 else: doc(request, 'Help on %s:')
1667 self.output.write('\n')
1669 def intro(self):
1670 self.output.write('''
1671 Welcome to Python %s! This is the online help utility.
1673 If this is your first time using Python, you should definitely check out
1674 the tutorial on the Internet at http://www.python.org/doc/tut/.
1676 Enter the name of any module, keyword, or topic to get help on writing
1677 Python programs and using Python modules. To quit this help utility and
1678 return to the interpreter, just type "quit".
1680 To get a list of available modules, keywords, or topics, type "modules",
1681 "keywords", or "topics". Each module also comes with a one-line summary
1682 of what it does; to list the modules whose summaries contain a given word
1683 such as "spam", type "modules spam".
1684 ''' % sys.version[:3])
1686 def list(self, items, columns=4, width=80):
1687 items = items[:]
1688 items.sort()
1689 colw = width / columns
1690 rows = (len(items) + columns - 1) / columns
1691 for row in range(rows):
1692 for col in range(columns):
1693 i = col * rows + row
1694 if i < len(items):
1695 self.output.write(items[i])
1696 if col < columns - 1:
1697 self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1698 self.output.write('\n')
1700 def listkeywords(self):
1701 self.output.write('''
1702 Here is a list of the Python keywords. Enter any keyword to get more help.
1704 ''')
1705 self.list(self.keywords.keys())
1707 def listtopics(self):
1708 self.output.write('''
1709 Here is a list of available topics. Enter any topic name to get more help.
1711 ''')
1712 self.list(self.topics.keys())
1714 def showtopic(self, topic):
1715 if not self.docdir:
1716 self.output.write('''
1717 Sorry, topic and keyword documentation is not available because the Python
1718 HTML documentation files could not be found. If you have installed them,
1719 please set the environment variable PYTHONDOCS to indicate their location.
1720 ''')
1721 return
1722 target = self.topics.get(topic, self.keywords.get(topic))
1723 if not target:
1724 self.output.write('no documentation found for %s\n' % repr(topic))
1725 return
1726 if type(target) is type(''):
1727 return self.showtopic(target)
1729 filename, xrefs = target
1730 filename = self.docdir + '/' + filename + '.html'
1731 try:
1732 file = open(filename)
1733 except:
1734 self.output.write('could not read docs from %s\n' % filename)
1735 return
1737 divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
1738 addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
1739 document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1740 file.close()
1742 import htmllib, formatter, StringIO
1743 buffer = StringIO.StringIO()
1744 parser = htmllib.HTMLParser(
1745 formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1746 parser.start_table = parser.do_p
1747 parser.end_table = lambda parser=parser: parser.do_p({})
1748 parser.start_tr = parser.do_br
1749 parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1750 parser.feed(document)
1751 buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n ')
1752 pager(' ' + strip(buffer) + '\n')
1753 if xrefs:
1754 buffer = StringIO.StringIO()
1755 formatter.DumbWriter(buffer).send_flowing_data(
1756 'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1757 self.output.write('\n%s\n' % buffer.getvalue())
1759 def listmodules(self, key=''):
1760 if key:
1761 self.output.write('''
1762 Here is a list of matching modules. Enter any module name to get more help.
1764 ''')
1765 apropos(key)
1766 else:
1767 self.output.write('''
1768 Please wait a moment while I gather a list of all available modules...
1770 ''')
1771 modules = {}
1772 def callback(path, modname, desc, modules=modules):
1773 if modname and modname[-9:] == '.__init__':
1774 modname = modname[:-9] + ' (package)'
1775 if find(modname, '.') < 0:
1776 modules[modname] = 1
1777 ModuleScanner().run(callback)
1778 self.list(modules.keys())
1779 self.output.write('''
1780 Enter any module name to get more help. Or, type "modules spam" to search
1781 for modules whose descriptions contain the word "spam".
1782 ''')
1784 help = Helper(sys.stdin, sys.stdout)
1786 class Scanner:
1787 """A generic tree iterator."""
1788 def __init__(self, roots, children, descendp):
1789 self.roots = roots[:]
1790 self.state = []
1791 self.children = children
1792 self.descendp = descendp
1794 def next(self):
1795 if not self.state:
1796 if not self.roots:
1797 return None
1798 root = self.roots.pop(0)
1799 self.state = [(root, self.children(root))]
1800 node, children = self.state[-1]
1801 if not children:
1802 self.state.pop()
1803 return self.next()
1804 child = children.pop(0)
1805 if self.descendp(child):
1806 self.state.append((child, self.children(child)))
1807 return child
1810 class ModuleScanner:
1811 """An interruptible scanner that searches module synopses."""
1813 def run(self, callback, key=None, completer=None):
1814 if key: key = lower(key)
1815 self.quit = False
1816 seen = {}
1818 for modname in sys.builtin_module_names:
1819 if modname != '__main__':
1820 seen[modname] = 1
1821 if key is None:
1822 callback(None, modname, '')
1823 else:
1824 desc = split(__import__(modname).__doc__ or '', '\n')[0]
1825 if find(lower(modname + ' - ' + desc), key) >= 0:
1826 callback(None, modname, desc)
1828 for importer, modname, ispkg in pkgutil.walk_packages():
1829 if self.quit:
1830 break
1831 if key is None:
1832 callback(None, modname, '')
1833 else:
1834 loader = importer.find_module(modname)
1835 if hasattr(loader,'get_source'):
1836 import StringIO
1837 desc = source_synopsis(
1838 StringIO.StringIO(loader.get_source(modname))
1839 ) or ''
1840 if hasattr(loader,'get_filename'):
1841 path = loader.get_filename(modname)
1842 else:
1843 path = None
1844 else:
1845 module = loader.load_module(modname)
1846 desc = (module.__doc__ or '').splitlines()[0]
1847 path = getattr(module,'__file__',None)
1848 if find(lower(modname + ' - ' + desc), key) >= 0:
1849 callback(path, modname, desc)
1851 if completer:
1852 completer()
1854 def apropos(key):
1855 """Print all the one-line module summaries that contain a substring."""
1856 def callback(path, modname, desc):
1857 if modname[-9:] == '.__init__':
1858 modname = modname[:-9] + ' (package)'
1859 print modname, desc and '- ' + desc
1860 try: import warnings
1861 except ImportError: pass
1862 else: warnings.filterwarnings('ignore') # ignore problems during import
1863 ModuleScanner().run(callback, key)
1865 # --------------------------------------------------- web browser interface
1867 def serve(port, callback=None, completer=None):
1868 import BaseHTTPServer, mimetools, select
1870 # Patch up mimetools.Message so it doesn't break if rfc822 is reloaded.
1871 class Message(mimetools.Message):
1872 def __init__(self, fp, seekable=1):
1873 Message = self.__class__
1874 Message.__bases__[0].__bases__[0].__init__(self, fp, seekable)
1875 self.encodingheader = self.getheader('content-transfer-encoding')
1876 self.typeheader = self.getheader('content-type')
1877 self.parsetype()
1878 self.parseplist()
1880 class DocHandler(BaseHTTPServer.BaseHTTPRequestHandler):
1881 def send_document(self, title, contents):
1882 try:
1883 self.send_response(200)
1884 self.send_header('Content-Type', 'text/html')
1885 self.end_headers()
1886 self.wfile.write(html.page(title, contents))
1887 except IOError: pass
1889 def do_GET(self):
1890 path = self.path
1891 if path[-5:] == '.html': path = path[:-5]
1892 if path[:1] == '/': path = path[1:]
1893 if path and path != '.':
1894 try:
1895 obj = locate(path, forceload=1)
1896 except ErrorDuringImport, value:
1897 self.send_document(path, html.escape(str(value)))
1898 return
1899 if obj:
1900 self.send_document(describe(obj), html.document(obj, path))
1901 else:
1902 self.send_document(path,
1903 'no Python documentation found for %s' % repr(path))
1904 else:
1905 heading = html.heading(
1906 '<big><big><strong>Python: Index of Modules</strong></big></big>',
1907 '#ffffff', '#7799ee')
1908 def bltinlink(name):
1909 return '<a href="%s.html">%s</a>' % (name, name)
1910 names = filter(lambda x: x != '__main__',
1911 sys.builtin_module_names)
1912 contents = html.multicolumn(names, bltinlink)
1913 indices = ['<p>' + html.bigsection(
1914 'Built-in Modules', '#ffffff', '#ee77aa', contents)]
1916 seen = {}
1917 for dir in sys.path:
1918 indices.append(html.index(dir, seen))
1919 contents = heading + join(indices) + '''<p align=right>
1920 <font color="#909090" face="helvetica, arial"><strong>
1921 pydoc</strong> by Ka-Ping Yee &lt;ping@lfw.org&gt;</font>'''
1922 self.send_document('Index of Modules', contents)
1924 def log_message(self, *args): pass
1926 class DocServer(BaseHTTPServer.HTTPServer):
1927 def __init__(self, port, callback):
1928 host = (sys.platform == 'mac') and '127.0.0.1' or 'localhost'
1929 self.address = ('', port)
1930 self.url = 'http://%s:%d/' % (host, port)
1931 self.callback = callback
1932 self.base.__init__(self, self.address, self.handler)
1934 def serve_until_quit(self):
1935 import select
1936 self.quit = False
1937 while not self.quit:
1938 rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
1939 if rd: self.handle_request()
1941 def server_activate(self):
1942 self.base.server_activate(self)
1943 if self.callback: self.callback(self)
1945 DocServer.base = BaseHTTPServer.HTTPServer
1946 DocServer.handler = DocHandler
1947 DocHandler.MessageClass = Message
1948 try:
1949 try:
1950 DocServer(port, callback).serve_until_quit()
1951 except (KeyboardInterrupt, select.error):
1952 pass
1953 finally:
1954 if completer: completer()
1956 # ----------------------------------------------------- graphical interface
1958 def gui():
1959 """Graphical interface (starts web server and pops up a control window)."""
1960 class GUI:
1961 def __init__(self, window, port=7464):
1962 self.window = window
1963 self.server = None
1964 self.scanner = None
1966 import Tkinter
1967 self.server_frm = Tkinter.Frame(window)
1968 self.title_lbl = Tkinter.Label(self.server_frm,
1969 text='Starting server...\n ')
1970 self.open_btn = Tkinter.Button(self.server_frm,
1971 text='open browser', command=self.open, state='disabled')
1972 self.quit_btn = Tkinter.Button(self.server_frm,
1973 text='quit serving', command=self.quit, state='disabled')
1975 self.search_frm = Tkinter.Frame(window)
1976 self.search_lbl = Tkinter.Label(self.search_frm, text='Search for')
1977 self.search_ent = Tkinter.Entry(self.search_frm)
1978 self.search_ent.bind('<Return>', self.search)
1979 self.stop_btn = Tkinter.Button(self.search_frm,
1980 text='stop', pady=0, command=self.stop, state='disabled')
1981 if sys.platform == 'win32':
1982 # Trying to hide and show this button crashes under Windows.
1983 self.stop_btn.pack(side='right')
1985 self.window.title('pydoc')
1986 self.window.protocol('WM_DELETE_WINDOW', self.quit)
1987 self.title_lbl.pack(side='top', fill='x')
1988 self.open_btn.pack(side='left', fill='x', expand=1)
1989 self.quit_btn.pack(side='right', fill='x', expand=1)
1990 self.server_frm.pack(side='top', fill='x')
1992 self.search_lbl.pack(side='left')
1993 self.search_ent.pack(side='right', fill='x', expand=1)
1994 self.search_frm.pack(side='top', fill='x')
1995 self.search_ent.focus_set()
1997 font = ('helvetica', sys.platform == 'win32' and 8 or 10)
1998 self.result_lst = Tkinter.Listbox(window, font=font, height=6)
1999 self.result_lst.bind('<Button-1>', self.select)
2000 self.result_lst.bind('<Double-Button-1>', self.goto)
2001 self.result_scr = Tkinter.Scrollbar(window,
2002 orient='vertical', command=self.result_lst.yview)
2003 self.result_lst.config(yscrollcommand=self.result_scr.set)
2005 self.result_frm = Tkinter.Frame(window)
2006 self.goto_btn = Tkinter.Button(self.result_frm,
2007 text='go to selected', command=self.goto)
2008 self.hide_btn = Tkinter.Button(self.result_frm,
2009 text='hide results', command=self.hide)
2010 self.goto_btn.pack(side='left', fill='x', expand=1)
2011 self.hide_btn.pack(side='right', fill='x', expand=1)
2013 self.window.update()
2014 self.minwidth = self.window.winfo_width()
2015 self.minheight = self.window.winfo_height()
2016 self.bigminheight = (self.server_frm.winfo_reqheight() +
2017 self.search_frm.winfo_reqheight() +
2018 self.result_lst.winfo_reqheight() +
2019 self.result_frm.winfo_reqheight())
2020 self.bigwidth, self.bigheight = self.minwidth, self.bigminheight
2021 self.expanded = 0
2022 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
2023 self.window.wm_minsize(self.minwidth, self.minheight)
2024 self.window.tk.willdispatch()
2026 import threading
2027 threading.Thread(
2028 target=serve, args=(port, self.ready, self.quit)).start()
2030 def ready(self, server):
2031 self.server = server
2032 self.title_lbl.config(
2033 text='Python documentation server at\n' + server.url)
2034 self.open_btn.config(state='normal')
2035 self.quit_btn.config(state='normal')
2037 def open(self, event=None, url=None):
2038 url = url or self.server.url
2039 try:
2040 import webbrowser
2041 webbrowser.open(url)
2042 except ImportError: # pre-webbrowser.py compatibility
2043 if sys.platform == 'win32':
2044 os.system('start "%s"' % url)
2045 elif sys.platform == 'mac':
2046 try: import ic
2047 except ImportError: pass
2048 else: ic.launchurl(url)
2049 else:
2050 rc = os.system('netscape -remote "openURL(%s)" &' % url)
2051 if rc: os.system('netscape "%s" &' % url)
2053 def quit(self, event=None):
2054 if self.server:
2055 self.server.quit = 1
2056 self.window.quit()
2058 def search(self, event=None):
2059 key = self.search_ent.get()
2060 self.stop_btn.pack(side='right')
2061 self.stop_btn.config(state='normal')
2062 self.search_lbl.config(text='Searching for "%s"...' % key)
2063 self.search_ent.forget()
2064 self.search_lbl.pack(side='left')
2065 self.result_lst.delete(0, 'end')
2066 self.goto_btn.config(state='disabled')
2067 self.expand()
2069 import threading
2070 if self.scanner:
2071 self.scanner.quit = 1
2072 self.scanner = ModuleScanner()
2073 threading.Thread(target=self.scanner.run,
2074 args=(self.update, key, self.done)).start()
2076 def update(self, path, modname, desc):
2077 if modname[-9:] == '.__init__':
2078 modname = modname[:-9] + ' (package)'
2079 self.result_lst.insert('end',
2080 modname + ' - ' + (desc or '(no description)'))
2082 def stop(self, event=None):
2083 if self.scanner:
2084 self.scanner.quit = 1
2085 self.scanner = None
2087 def done(self):
2088 self.scanner = None
2089 self.search_lbl.config(text='Search for')
2090 self.search_lbl.pack(side='left')
2091 self.search_ent.pack(side='right', fill='x', expand=1)
2092 if sys.platform != 'win32': self.stop_btn.forget()
2093 self.stop_btn.config(state='disabled')
2095 def select(self, event=None):
2096 self.goto_btn.config(state='normal')
2098 def goto(self, event=None):
2099 selection = self.result_lst.curselection()
2100 if selection:
2101 modname = split(self.result_lst.get(selection[0]))[0]
2102 self.open(url=self.server.url + modname + '.html')
2104 def collapse(self):
2105 if not self.expanded: return
2106 self.result_frm.forget()
2107 self.result_scr.forget()
2108 self.result_lst.forget()
2109 self.bigwidth = self.window.winfo_width()
2110 self.bigheight = self.window.winfo_height()
2111 self.window.wm_geometry('%dx%d' % (self.minwidth, self.minheight))
2112 self.window.wm_minsize(self.minwidth, self.minheight)
2113 self.expanded = 0
2115 def expand(self):
2116 if self.expanded: return
2117 self.result_frm.pack(side='bottom', fill='x')
2118 self.result_scr.pack(side='right', fill='y')
2119 self.result_lst.pack(side='top', fill='both', expand=1)
2120 self.window.wm_geometry('%dx%d' % (self.bigwidth, self.bigheight))
2121 self.window.wm_minsize(self.minwidth, self.bigminheight)
2122 self.expanded = 1
2124 def hide(self, event=None):
2125 self.stop()
2126 self.collapse()
2128 import Tkinter
2129 try:
2130 root = Tkinter.Tk()
2131 # Tk will crash if pythonw.exe has an XP .manifest
2132 # file and the root has is not destroyed explicitly.
2133 # If the problem is ever fixed in Tk, the explicit
2134 # destroy can go.
2135 try:
2136 gui = GUI(root)
2137 root.mainloop()
2138 finally:
2139 root.destroy()
2140 except KeyboardInterrupt:
2141 pass
2143 # -------------------------------------------------- command-line interface
2145 def ispath(x):
2146 return isinstance(x, str) and find(x, os.sep) >= 0
2148 def cli():
2149 """Command-line interface (looks at sys.argv to decide what to do)."""
2150 import getopt
2151 class BadUsage: pass
2153 # Scripts don't get the current directory in their path by default.
2154 scriptdir = os.path.dirname(sys.argv[0])
2155 if scriptdir in sys.path:
2156 sys.path.remove(scriptdir)
2157 sys.path.insert(0, '.')
2159 try:
2160 opts, args = getopt.getopt(sys.argv[1:], 'gk:p:w')
2161 writing = 0
2163 for opt, val in opts:
2164 if opt == '-g':
2165 gui()
2166 return
2167 if opt == '-k':
2168 apropos(val)
2169 return
2170 if opt == '-p':
2171 try:
2172 port = int(val)
2173 except ValueError:
2174 raise BadUsage
2175 def ready(server):
2176 print 'pydoc server ready at %s' % server.url
2177 def stopped():
2178 print 'pydoc server stopped'
2179 serve(port, ready, stopped)
2180 return
2181 if opt == '-w':
2182 writing = 1
2184 if not args: raise BadUsage
2185 for arg in args:
2186 if ispath(arg) and not os.path.exists(arg):
2187 print 'file %r does not exist' % arg
2188 break
2189 try:
2190 if ispath(arg) and os.path.isfile(arg):
2191 arg = importfile(arg)
2192 if writing:
2193 if ispath(arg) and os.path.isdir(arg):
2194 writedocs(arg)
2195 else:
2196 writedoc(arg)
2197 else:
2198 help.help(arg)
2199 except ErrorDuringImport, value:
2200 print value
2202 except (getopt.error, BadUsage):
2203 cmd = os.path.basename(sys.argv[0])
2204 print """pydoc - the Python documentation tool
2206 %s <name> ...
2207 Show text documentation on something. <name> may be the name of a
2208 Python keyword, topic, function, module, or package, or a dotted
2209 reference to a class or function within a module or module in a
2210 package. If <name> contains a '%s', it is used as the path to a
2211 Python source file to document. If name is 'keywords', 'topics',
2212 or 'modules', a listing of these things is displayed.
2214 %s -k <keyword>
2215 Search for a keyword in the synopsis lines of all available modules.
2217 %s -p <port>
2218 Start an HTTP server on the given port on the local machine.
2220 %s -g
2221 Pop up a graphical interface for finding and serving documentation.
2223 %s -w <name> ...
2224 Write out the HTML documentation for a module to a file in the current
2225 directory. If <name> contains a '%s', it is treated as a filename; if
2226 it names a directory, documentation is written for all the contents.
2227 """ % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
2229 if __name__ == '__main__': cli()