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