[Python-checkins] CVS: python/dist/src/Lib pydoc.py,1.21,1.22

Ka-Ping Yee ping@users.sourceforge.net
Tue, 10 Apr 2001 04:46:04 -0700


Update of /cvsroot/python/python/dist/src/Lib
In directory usw-pr-cvs1:/tmp/cvs-serv3267

Modified Files:
	pydoc.py 
Log Message:
Fix synopsis() so it can handle binary module files.
Avoid ever using popen on Windows, since it's broken there.
Factor out the business of getting the summary line into splitdoc().
Use the modulename() routine in inspect.
Show all members of modules and classes rather than filtering on leading '_'.
Small typo and formtating fixes.
Don't show warnings when running "pydoc -k".


Index: pydoc.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/pydoc.py,v
retrieving revision 1.21
retrieving revision 1.22
diff -C2 -r1.21 -r1.22
*** pydoc.py	2001/03/27 08:13:42	1.21
--- pydoc.py	2001/04/10 11:46:02	1.22
***************
*** 38,43 ****
  # Note: this module is designed to deploy instantly and run under any
  # version of Python from 1.5 and up.  That's why it's a single file and
! # some 2.0 features (like string methods) are conspicuously avoided.
  
  import sys, imp, os, stat, re, types, inspect
  from repr import Repr
--- 38,51 ----
  # Note: this module is designed to deploy instantly and run under any
  # version of Python from 1.5 and up.  That's why it's a single file and
! # some 2.0 features (like string methods) are conspicuously absent.
  
+ # Known bugs that can't be fixed here:
+ #   - imp.load_module() cannot be prevented from clobbering existing
+ #     loaded modules, so calling synopsis() on a binary module file
+ #     changes the contents of any existing module with the same name.
+ #   - If the __file__ attribute on a module is a relative path and
+ #     the current directory is changed with os.chdir(), an incorrect
+ #     path will be displayed.
+ 
  import sys, imp, os, stat, re, types, inspect
  from repr import Repr
***************
*** 50,70 ****
      mtime = os.stat(filename)[stat.ST_MTIME]
      lastupdate, result = cache.get(filename, (0, None))
-     # XXX what if ext is 'rb' type in imp_getsuffixes?
      if lastupdate < mtime:
          file = open(filename)
!         line = file.readline()
!         while line[:1] == '#' or strip(line) == '':
              line = file.readline()
!             if not line: break
!         if line[-2:] == '\\\n':
!             line = line[:-2] + file.readline()
!         line = strip(line)
!         if line[:3] == '"""':
!             line = line[3:]
!             while strip(line) == '':
                  line = file.readline()
                  if not line: break
!             result = strip(split(line, '"""')[0])
!         else: result = None
          file.close()
          cache[filename] = (mtime, result)
--- 58,83 ----
      mtime = os.stat(filename)[stat.ST_MTIME]
      lastupdate, result = cache.get(filename, (0, None))
      if lastupdate < mtime:
+         info = inspect.getmoduleinfo(filename)
          file = open(filename)
!         if info and 'b' in info[2]: # binary modules have to be imported
!             try: module = imp.load_module(info[0], file, filename, info[1:])
!             except: return None
!             result = split(module.__doc__ or '', '\n')[0]
!         else: # text modules can be directly examined
              line = file.readline()
!             while line[:1] == '#' or strip(line) == '':
                  line = file.readline()
                  if not line: break
!             line = strip(line)
!             if line[:4] == 'r"""': line = line[1:]
!             if line[:3] == '"""':
!                 line = line[3:]
!                 if line[-1:] == '\\': line = line[:-1]
!                 while not strip(line):
!                     line = file.readline()
!                     if not line: break
!                 result = strip(split(line, '"""')[0])
!             else: result = None
          file.close()
          cache[filename] = (mtime, result)
***************
*** 88,91 ****
--- 101,113 ----
      return result and re.sub('^ *\n', '', rstrip(result)) or ''
  
+ def splitdoc(doc):
+     """Split a doc string into a synopsis line (if any) and the rest."""
+     lines = split(strip(doc), '\n')
+     if len(lines) == 1:
+         return lines[0], ''
+     elif len(lines) >= 2 and not rstrip(lines[1]):
+         return lines[0], join(lines[2:], '\n')
+     return '', join(lines, '\n')
+ 
  def classname(object, modname):
      """Get a class name and qualify it with a module name if necessary."""
***************
*** 118,122 ****
  def stripid(text):
      """Remove the hexadecimal id from a Python object representation."""
!     # The behaviour of %p is implementation-dependent, so we need an example.
      for pattern in [' at 0x[0-9a-f]{6,}>$', ' at [0-9A-F]{8,}>$']:
          if re.search(pattern, repr(Exception)):
--- 140,144 ----
  def stripid(text):
      """Remove the hexadecimal id from a Python object representation."""
!     # The behaviour of %p is implementation-dependent; we check two cases.
      for pattern in [' at 0x[0-9a-f]{6,}>$', ' at [0-9A-F]{8,}>$']:
          if re.search(pattern, repr(Exception)):
***************
*** 124,138 ****
      return text
  
- def modulename(path):
-     """Return the Python module name for a given path, or None."""
-     filename = os.path.basename(path)
-     suffixes = map(lambda (suffix, mode, kind): (len(suffix), suffix),
-                    imp.get_suffixes())
-     suffixes.sort()
-     suffixes.reverse() # try longest suffixes first, in case they overlap
-     for length, suffix in suffixes:
-         if len(filename) > length and filename[-length:] == suffix:
-             return filename[:-length]
- 
  def allmethods(cl):
      methods = {}
--- 146,149 ----
***************
*** 233,237 ****
              # needed to make any special characters, so show a raw string.
              return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
!         return re.sub(r'((\\[\\abfnrtv\'"]|\\x..|\\u....)+)',
                        r'<font color="#c040c0">\1</font>',
                        self.escape(testrepr))
--- 244,248 ----
              # needed to make any special characters, so show a raw string.
              return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
!         return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
                        r'<font color="#c040c0">\1</font>',
                        self.escape(testrepr))
***************
*** 276,284 ****
      ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
  
!     def section(self, title, fgcol, bgcol, contents, width=20,
                  prelude='', marginalia=None, gap='&nbsp;&nbsp;'):
          """Format a section with a heading."""
          if marginalia is None:
!             marginalia = '&nbsp;' * width
          result = '''
  <p><table width="100%%" cellspacing=0 cellpadding=2 border=0>
--- 287,295 ----
      ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')
  
!     def section(self, title, fgcol, bgcol, contents, width=10,
                  prelude='', marginalia=None, gap='&nbsp;&nbsp;'):
          """Format a section with a heading."""
          if marginalia is None:
!             marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
          result = '''
  <p><table width="100%%" cellspacing=0 cellpadding=2 border=0>
***************
*** 296,300 ****
  <tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
  
!         return result + '<td width="100%%">%s</td></tr></table>' % contents
  
      def bigsection(self, title, *args):
--- 307,311 ----
  <tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)
  
!         return result + '\n<td width="100%%">%s</td></tr></table>' % contents
  
      def bigsection(self, title, *args):
***************
*** 446,458 ****
          modules = inspect.getmembers(object, inspect.ismodule)
  
-         if 0 and hasattr(object, '__all__'): # disabled for now
-             visible = lambda key, all=object.__all__: key in all
-         else:
-             visible = lambda key: key[:1] != '_'
- 
          classes, cdict = [], {}
          for key, value in inspect.getmembers(object, inspect.isclass):
!             if visible(key) and (
!                 inspect.getmodule(value) or object) is object:
                  classes.append((key, value))
                  cdict[key] = cdict[value] = '#' + key
--- 457,463 ----
          modules = inspect.getmembers(object, inspect.ismodule)
  
          classes, cdict = [], {}
          for key, value in inspect.getmembers(object, inspect.isclass):
!             if (inspect.getmodule(value) or object) is object:
                  classes.append((key, value))
                  cdict[key] = cdict[value] = '#' + key
***************
*** 467,472 ****
          funcs, fdict = [], {}
          for key, value in inspect.getmembers(object, inspect.isroutine):
!             if visible(key) and (inspect.isbuiltin(value) or
!                                  inspect.getmodule(value) is object):
                  funcs.append((key, value))
                  fdict[key] = '#-' + key
--- 472,476 ----
          funcs, fdict = [], {}
          for key, value in inspect.getmembers(object, inspect.isroutine):
!             if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
                  funcs.append((key, value))
                  fdict[key] = '#-' + key
***************
*** 474,479 ****
          constants = []
          for key, value in inspect.getmembers(object, isconstant):
!             if visible(key):
!                 constants.append((key, value))
  
          doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
--- 478,482 ----
          constants = []
          for key, value in inspect.getmembers(object, isconstant):
!             constants.append((key, value))
  
          doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
***************
*** 485,496 ****
              modnames = []
              for file in os.listdir(object.__path__[0]):
!                 if file[:1] != '_':
!                     path = os.path.join(object.__path__[0], file)
!                     modname = modulename(file)
!                     if modname and modname not in modnames:
!                         modpkgs.append((modname, name, 0, 0))
!                         modnames.append(modname)
!                     elif ispackage(path):
!                         modpkgs.append((file, name, 1, 0))
              modpkgs.sort()
              contents = self.multicolumn(modpkgs, self.modpkglink)
--- 488,498 ----
              modnames = []
              for file in os.listdir(object.__path__[0]):
!                 path = os.path.join(object.__path__[0], file)
!                 modname = inspect.getmodulename(file)
!                 if modname and modname not in modnames:
!                     modpkgs.append((modname, name, 0, 0))
!                     modnames.append(modname)
!                 elif ispackage(path):
!                     modpkgs.append((file, name, 1, 0))
              modpkgs.sort()
              contents = self.multicolumn(modpkgs, self.modpkglink)
***************
*** 564,569 ****
          doc = self.markup(
              getdoc(object), self.preformat, funcs, classes, mdict)
!         doc = self.small(doc and '<tt>%s<br>&nbsp;</tt>' % doc or '<tt>&nbsp;</tt>')
!         return self.section(title, '#000000', '#ffc8d8', contents, 10, doc)
  
      def formatvalue(self, object):
--- 566,572 ----
          doc = self.markup(
              getdoc(object), self.preformat, funcs, classes, mdict)
!         doc = self.small(doc and '<tt>%s<br>&nbsp;</tt>' % doc or
!                                  self.small('&nbsp;'))
!         return self.section(title, '#000000', '#ffc8d8', contents, 5, doc)
  
      def formatvalue(self, object):
***************
*** 626,631 ****
              doc = self.markup(
                  getdoc(object), self.preformat, funcs, classes, methods)
!             doc = doc and '<tt>%s</tt>' % doc
!             return '<dl><dt>%s<dd>%s</dl>\n' % (decl, self.small(doc))
  
      def docother(self, object, name=None):
--- 629,634 ----
              doc = self.markup(
                  getdoc(object), self.preformat, funcs, classes, methods)
!             doc = doc and '<dd>' + self.small('<tt>%s</tt>' % doc)
!             return '<dl><dt>%s%s</dl>\n' % (decl, doc)
  
      def docother(self, object, name=None):
***************
*** 653,658 ****
          for file in files:
              path = os.path.join(dir, file)
!             if file[:1] != '_' and os.path.isfile(path):
!                 modname = modulename(file)
                  if modname: found(modname, 0)
  
--- 656,661 ----
          for file in files:
              path = os.path.join(dir, file)
!             if os.path.isfile(path):
!                 modname = inspect.getmodulename(file)
                  if modname: found(modname, 0)
  
***************
*** 737,749 ****
          """Produce text documentation for a given module object."""
          name = object.__name__ # ignore the passed-in name
!         namesec = name
!         lines = split(strip(getdoc(object)), '\n')
!         if len(lines) == 1:
!             if lines[0]: namesec = namesec + ' - ' + lines[0]
!             lines = []
!         elif len(lines) >= 2 and not rstrip(lines[1]):
!             if lines[0]: namesec = namesec + ' - ' + lines[0]
!             lines = lines[2:]
!         result = self.section('NAME', namesec)
  
          try:
--- 740,745 ----
          """Produce text documentation for a given module object."""
          name = object.__name__ # ignore the passed-in name
!         synop, desc = splitdoc(getdoc(object))
!         result = self.section('NAME', name + (synop and ' - ' + synop))
  
          try:
***************
*** 752,757 ****
              file = '(built-in)'
          result = result + self.section('FILE', file)
!         if lines:
!             result = result + self.section('DESCRIPTION', join(lines, '\n'))
  
          classes = []
--- 748,753 ----
              file = '(built-in)'
          result = result + self.section('FILE', file)
!         if desc:
!             result = result + self.section('DESCRIPTION', desc)
  
          classes = []
***************
*** 765,781 ****
          constants = []
          for key, value in inspect.getmembers(object, isconstant):
!             if key[:1] != '_':
!                 constants.append((key, value))
  
          if hasattr(object, '__path__'):
              modpkgs = []
              for file in os.listdir(object.__path__[0]):
!                 if file[:1] != '_':
!                     path = os.path.join(object.__path__[0], file)
!                     modname = modulename(file)
!                     if modname and modname not in modpkgs:
!                         modpkgs.append(modname)
!                     elif ispackage(path):
!                         modpkgs.append(file + ' (package)')
              modpkgs.sort()
              result = result + self.section(
--- 761,775 ----
          constants = []
          for key, value in inspect.getmembers(object, isconstant):
!             constants.append((key, value))
  
          if hasattr(object, '__path__'):
              modpkgs = []
              for file in os.listdir(object.__path__[0]):
!                 path = os.path.join(object.__path__[0], file)
!                 modname = inspect.getmodulename(file)
!                 if modname and modname not in modpkgs:
!                     modpkgs.append(modname)
!                 elif ispackage(path):
!                     modpkgs.append(file + ' (package)')
              modpkgs.sort()
              result = result + self.section(
***************
*** 915,919 ****
          return plainpager
      if os.environ.has_key('PAGER'):
!         return lambda a: pipepager(a, os.environ['PAGER'])
      if sys.platform == 'win32':
          return lambda a: tempfilepager(a, 'more <')
--- 909,916 ----
          return plainpager
      if os.environ.has_key('PAGER'):
!         if sys.platform == 'win32': # pipes completely broken in Windows
!             return lambda a: tempfilepager(a, os.environ['PAGER'])
!         else:
!             return lambda a: pipepager(a, os.environ['PAGER'])
      if sys.platform == 'win32':
          return lambda a: tempfilepager(a, 'more <')
***************
*** 1073,1077 ****
              else:
                  # Some other error occurred before executing the module.
!                 raise DocImportError(filename, sys.exc_info())
          try:
              x = module
--- 1070,1074 ----
              else:
                  # Some other error occurred before executing the module.
!                 raise ErrorDuringImport(filename, sys.exc_info())
          try:
              x = module
***************
*** 1119,1123 ****
      else:
          if object:
!             page = html.page('Python: ' + describe(object),
                               html.document(object, object.__name__))
              file = open(key + '.html', 'w')
--- 1116,1120 ----
      else:
          if object:
!             page = html.page(describe(object),
                               html.document(object, object.__name__))
              file = open(key + '.html', 'w')
***************
*** 1135,1139 ****
              writedocs(path, pkgpath + file + '.')
          elif os.path.isfile(path):
!             modname = modulename(path)
              if modname:
                  modname = pkgpath + modname
--- 1132,1136 ----
              writedocs(path, pkgpath + file + '.')
          elif os.path.isfile(path):
!             modname = inspect.getmodulename(path)
              if modname:
                  modname = pkgpath + modname
***************
*** 1228,1232 ****
              if not node: break
              path, package = node
!             modname = modulename(path)
              if os.path.isfile(path) and modname:
                  modname = package + (package and '.') + modname
--- 1225,1229 ----
              if not node: break
              path, package = node
!             modname = inspect.getmodulename(path)
              if os.path.isfile(path) and modname:
                  modname = package + (package and '.') + modname
***************
*** 1243,1247 ****
          if modname[-9:] == '.__init__':
              modname = modname[:-9] + ' (package)'
!         print modname, '-', desc or '(no description)'
      ModuleScanner().run(key, callback)
  
--- 1240,1247 ----
          if modname[-9:] == '.__init__':
              modname = modname[:-9] + ' (package)'
!         print modname, desc and '- ' + desc
!     try: import warnings
!     except ImportError: pass
!     else: warnings.filterwarnings('ignore') # ignore problems during import
      ModuleScanner().run(key, callback)
  
***************
*** 1423,1430 ****
                      os.system('start "%s"' % url)
                  elif sys.platform == 'mac':
!                     try:
!                         import ic
!                         ic.launchurl(url)
                      except ImportError: pass
                  else:
                      rc = os.system('netscape -remote "openURL(%s)" &' % url)
--- 1423,1429 ----
                      os.system('start "%s"' % url)
                  elif sys.platform == 'mac':
!                     try: import ic
                      except ImportError: pass
+                     else: ic.launchurl(url)
                  else:
                      rc = os.system('netscape -remote "openURL(%s)" &' % url)
***************
*** 1584,1592 ****
  
  %s -g
!     Pop up a graphical interface for serving and finding documentation.
  
  %s -w <name> ...
      Write out the HTML documentation for a module to a file in the current
!     directory.  If <name> contains a '%s', it is treated as a filename.
  """ % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)
  
--- 1583,1592 ----
  
  %s -g
!     Pop up a graphical interface for finding and serving documentation.
  
  %s -w <name> ...
      Write out the HTML documentation for a module to a file in the current
!     directory.  If <name> contains a '%s', it is treated as a filename; if
!     it names a directory, documentation is written for all the contents.
  """ % (cmd, os.sep, cmd, cmd, cmd, cmd, os.sep)