source: python/trunk/Lib/pydoc.py

Last change on this file was 1551, checked in by Silvan Scherrer, 7 years ago

prefer less over more. use text when less fails and more is used. ticket #326

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