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

Ka-Ping Yee ping@users.sourceforge.net
Fri, 13 Apr 2001 02:55:51 -0700


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

Modified Files:
	pydoc.py 
Log Message:
Make force-loading optional; don't force-load in interactive mode.
Make synopsis() load modules as '__temp__' so they don't clobber anything.
Change "constants" section to "data" section.
Don't show __builtins__ or __doc__ in "data" section.
For Bob Weiner: don't boldface text in Emacs shells or dumb terminals.
Remove Helper.__repr__ (it really belongs in site.py, and should be                 guarded by a check for len(inspect.stack) <= 2).


Index: pydoc.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/pydoc.py,v
retrieving revision 1.30
retrieving revision 1.31
diff -C2 -r1.30 -r1.31
*** pydoc.py	2001/04/12 20:39:14	1.30
--- pydoc.py	2001/04/13 09:55:49	1.31
***************
*** 54,87 ****
  # --------------------------------------------------------- common routines
  
- def synopsis(filename, cache={}):
-     """Get the one-line summary out of a module file."""
-     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 not 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)
-     return result
- 
  def pathdirs():
      """Convert sys.path into a list of absolute, existing, unique paths."""
--- 54,57 ----
***************
*** 117,126 ****
      return name
  
! def isconstant(object):
!     """Check if an object is of a type that probably means it's a constant."""
!     return type(object) in [
!         types.FloatType, types.IntType, types.ListType, types.LongType,
!         types.StringType, types.TupleType, types.TypeType,
!         hasattr(types, 'UnicodeType') and types.UnicodeType or 0]
  
  def replace(text, *pairs):
--- 87,95 ----
      return name
  
! def isdata(object):
!     """Check if an object is of a type that probably means it's data."""
!     return not (inspect.ismodule(object) or inspect.isclass(object) or
!                 inspect.isroutine(object) or inspect.isframe(object) or
!                 inspect.istraceback(object) or inspect.iscode(object))
  
  def replace(text, *pairs):
***************
*** 157,160 ****
--- 126,169 ----
      return methods
  
+ # ----------------------------------------------------- module manipulation
+ 
+ def ispackage(path):
+     """Guess whether a path refers to a package directory."""
+     if os.path.isdir(path):
+         for ext in ['.py', '.pyc', '.pyo']:
+             if os.path.isfile(os.path.join(path, '__init__' + ext)):
+                 return 1
+ 
+ def synopsis(filename, cache={}):
+     """Get the one-line summary out of a module file."""
+     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('__temp__', file, filename, info[1:])
+             except: return None
+             result = split(module.__doc__ or '', '\n')[0]
+             del sys.modules['__temp__']
+         else: # text modules can be directly examined
+             line = file.readline()
+             while line[:1] == '#' or not 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)
+     return result
+ 
  class ErrorDuringImport(Exception):
      """Errors that occurred while trying to import something to document it."""
***************
*** 190,200 ****
      return module
  
! def ispackage(path):
!     """Guess whether a path refers to a package directory."""
!     if os.path.isdir(path):
!         for ext in ['.py', '.pyc', '.pyo']:
!             if os.path.isfile(os.path.join(path, '__init__' + ext)):
!                 return 1
  
  # ---------------------------------------------------- formatter base class
  
--- 199,246 ----
      return module
  
! def safeimport(path, forceload=0, cache={}):
!     """Import a module; handle errors; return None if the module isn't found.
  
+     If the module *is* found but an exception occurs, it's wrapped in an
+     ErrorDuringImport exception and reraised.  Unlike __import__, if a
+     package path is specified, the module at the end of the path is returned,
+     not the package at the beginning.  If the optional 'forceload' argument
+     is 1, we reload the module from disk (unless it's a dynamic extension)."""
+     if forceload and sys.modules.has_key(path):
+         # This is the only way to be sure.  Checking the mtime of the file
+         # isn't good enough (e.g. what if the module contains a class that
+         # inherits from another module that has changed?).
+         if path not in sys.builtin_module_names:
+             # Python never loads a dynamic extension a second time from the
+             # same path, even if the file is changed or missing.  Deleting
+             # the entry in sys.modules doesn't help for dynamic extensions,
+             # so we're not even going to try to keep them up to date.
+             info = inspect.getmoduleinfo(sys.modules[path].__file__)
+             if info[3] != imp.C_EXTENSION:
+                 cache[path] = sys.modules[path] # prevent module from clearing
+                 del sys.modules[path]
+     try:
+         module = __import__(path)
+     except:
+         # Did the error occur before or after the module was found?
+         (exc, value, tb) = info = sys.exc_info()
+         if sys.modules.has_key(path):
+             # An error occured while executing the imported module.
+             raise ErrorDuringImport(sys.modules[path].__file__, info)
+         elif exc is SyntaxError:
+             # A SyntaxError occurred before we could execute the module.
+             raise ErrorDuringImport(value.filename, info)
+         elif exc is ImportError and \
+              split(lower(str(value)))[:2] == ['no', 'module']:
+             # The module was not found.
+             return None
+         else:
+             # Some other error occurred during the importing process.
+             raise ErrorDuringImport(path, sys.exc_info())
+     for part in split(path, '.')[1:]:
+         try: module = getattr(module, part)
+         except AttributeError: return None
+     return module
+ 
  # ---------------------------------------------------- formatter base class
  
***************
*** 222,226 ****
      def __init__(self):
          Repr.__init__(self)
!         self.maxlist = self.maxtuple = self.maxdict = 10
          self.maxstring = self.maxother = 100
  
--- 268,273 ----
      def __init__(self):
          Repr.__init__(self)
!         self.maxlist = self.maxtuple = 20
!         self.maxdict = 10
          self.maxstring = self.maxother = 100
  
***************
*** 345,354 ****
      def classlink(self, object, modname):
          """Make a link for a class."""
!         name = classname(object, modname)
!         if sys.modules.has_key(object.__module__) and \
!             getattr(sys.modules[object.__module__], object.__name__) is object:
              return '<a href="%s.html#%s">%s</a>' % (
!                 object.__module__, object.__name__, name)
!         return name
  
      def modulelink(self, object):
--- 392,400 ----
      def classlink(self, object, modname):
          """Make a link for a class."""
!         name, module = object.__name__, sys.modules.get(object.__module__)
!         if hasattr(module, name) and getattr(module, name) is object:
              return '<a href="%s.html#%s">%s</a>' % (
!                 module.__name__, name, classname(object, modname))
!         return classname(object, modname)
  
      def modulelink(self, object):
***************
*** 476,482 ****
                  fdict[key] = '#-' + key
                  if inspect.isfunction(value): fdict[value] = fdict[key]
!         constants = []
!         for key, value in inspect.getmembers(object, isconstant):
!             constants.append((key, value))
  
          doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
--- 522,529 ----
                  fdict[key] = '#-' + key
                  if inspect.isfunction(value): fdict[value] = fdict[key]
!         data = []
!         for key, value in inspect.getmembers(object, isdata):
!             if key not in ['__builtins__', '__doc__']:
!                 data.append((key, value))
  
          doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
***************
*** 519,528 ****
              result = result + self.bigsection(
                  'Functions', '#ffffff', '#eeaa77', join(contents))
!         if constants:
              contents = []
!             for key, value in constants:
                  contents.append(self.document(value, key))
              result = result + self.bigsection(
!                 'Constants', '#ffffff', '#55aa55', join(contents, '<br>'))
          if hasattr(object, '__author__'):
              contents = self.markup(str(object.__author__), self.preformat)
--- 566,575 ----
              result = result + self.bigsection(
                  'Functions', '#ffffff', '#eeaa77', join(contents))
!         if data:
              contents = []
!             for key, value in data:
                  contents.append(self.document(value, key))
              result = result + self.bigsection(
!                 'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))
          if hasattr(object, '__author__'):
              contents = self.markup(str(object.__author__), self.preformat)
***************
*** 666,670 ****
      def __init__(self):
          Repr.__init__(self)
!         self.maxlist = self.maxtuple = self.maxdict = 10
          self.maxstring = self.maxother = 100
  
--- 713,718 ----
      def __init__(self):
          Repr.__init__(self)
!         self.maxlist = self.maxtuple = 20
!         self.maxdict = 10
          self.maxstring = self.maxother = 100
  
***************
*** 755,761 ****
              if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
                  funcs.append((key, value))
!         constants = []
!         for key, value in inspect.getmembers(object, isconstant):
!             constants.append((key, value))
  
          if hasattr(object, '__path__'):
--- 803,810 ----
              if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
                  funcs.append((key, value))
!         data = []
!         for key, value in inspect.getmembers(object, isdata):
!             if key not in ['__builtins__', '__doc__']:
!                 data.append((key, value))
  
          if hasattr(object, '__path__'):
***************
*** 786,794 ****
              result = result + self.section('FUNCTIONS', join(contents, '\n'))
  
!         if constants:
              contents = []
!             for key, value in constants:
                  contents.append(self.docother(value, key, name, 70))
!             result = result + self.section('CONSTANTS', join(contents, '\n'))
  
          if hasattr(object, '__version__'):
--- 835,843 ----
              result = result + self.section('FUNCTIONS', join(contents, '\n'))
  
!         if data:
              contents = []
!             for key, value in data:
                  contents.append(self.docother(value, key, name, 70))
!             result = result + self.section('DATA', join(contents, '\n'))
  
          if hasattr(object, '__version__'):
***************
*** 904,914 ****
      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 <')
      if hasattr(os, 'system') and os.system('less 2>/dev/null') == 0:
!         return lambda a: pipepager(a, 'less')
  
      import tempfile
--- 953,965 ----
      if os.environ.has_key('PAGER'):
          if sys.platform == 'win32': # pipes completely broken in Windows
!             return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
!         elif os.environ.get('TERM') in ['dumb', 'emacs']:
!             return lambda text: pipepager(plain(text), os.environ['PAGER'])
          else:
!             return lambda text: pipepager(text, os.environ['PAGER'])
      if sys.platform == 'win32':
!         return lambda text: tempfilepager(plain(text), 'more <')
      if hasattr(os, 'system') and os.system('less 2>/dev/null') == 0:
!         return lambda text: pipepager(text, 'less')
  
      import tempfile
***************
*** 923,926 ****
--- 974,981 ----
          os.unlink(filename)
  
+ def plain(text):
+     """Remove boldface formatting from text."""
+     return re.sub('.\b', '', text)
+ 
  def pipepager(text, cmd):
      """Page through text by feeding it to another program."""
***************
*** 944,951 ****
          os.unlink(filename)
  
- def plain(text):
-     """Remove boldface formatting from text."""
-     return re.sub('.\b', '', text)
- 
  def ttypager(text):
      """Page through text on a text terminal."""
--- 999,1002 ----
***************
*** 1010,1057 ****
          return 'instance of ' + thing.__class__.__name__
      return type(thing).__name__
- 
- def freshimport(path, cache={}):
-     """Import a module freshly from disk, making sure it's up to date."""
-     if sys.modules.has_key(path):
-         # This is the only way to be sure.  Checking the mtime of the file
-         # isn't good enough (e.g. what if the module contains a class that
-         # inherits from another module that has changed?).
-         if path not in sys.builtin_module_names:
-             # Python never loads a dynamic extension a second time from the
-             # same path, even if the file is changed or missing.  Deleting
-             # the entry in sys.modules doesn't help for dynamic extensions,
-             # so we're not even going to try to keep them up to date.
-             info = inspect.getmoduleinfo(sys.modules[path].__file__)
-             if info[3] != imp.C_EXTENSION:
-                 del sys.modules[path]
-     try:
-         module = __import__(path)
-     except:
-         # Did the error occur before or after the module was found?
-         (exc, value, tb) = info = sys.exc_info()
-         if sys.modules.has_key(path):
-             # An error occured while executing the imported module.
-             raise ErrorDuringImport(sys.modules[path].__file__, info)
-         elif exc is SyntaxError:
-             # A SyntaxError occurred before we could execute the module.
-             raise ErrorDuringImport(value.filename, info)
-         elif exc is ImportError and \
-              split(lower(str(value)))[:2] == ['no', 'module']:
-             # The module was not found.
-             return None
-         else:
-             # Some other error occurred during the importing process.
-             raise ErrorDuringImport(path, sys.exc_info())
-     for part in split(path, '.')[1:]:
-         try: module = getattr(module, part)
-         except AttributeError: return None
-     return module
  
! def locate(path):
      """Locate an object by name or dotted path, importing as necessary."""
      parts = split(path, '.')
      module, n = None, 0
      while n < len(parts):
!         nextmodule = freshimport(join(parts[:n+1], '.'))
          if nextmodule: module, n = nextmodule, n + 1
          else: break
--- 1061,1071 ----
          return 'instance of ' + thing.__class__.__name__
      return type(thing).__name__
  
! def locate(path, forceload=0):
      """Locate an object by name or dotted path, importing as necessary."""
      parts = split(path, '.')
      module, n = None, 0
      while n < len(parts):
!         nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
          if nextmodule: module, n = nextmodule, n + 1
          else: break
***************
*** 1072,1081 ****
  html = HTMLDoc()
  
! def doc(thing, title='Python Library Documentation: %s'):
      """Display text documentation, given an object or a path to an object."""
      suffix, name = '', None
      if type(thing) is type(''):
          try:
!             object = locate(thing)
          except ErrorDuringImport, value:
              print value
--- 1086,1095 ----
  html = HTMLDoc()
  
! def doc(thing, title='Python Library Documentation: %s', forceload=0):
      """Display text documentation, given an object or a path to an object."""
      suffix, name = '', None
      if type(thing) is type(''):
          try:
!             object = locate(thing, forceload)
          except ErrorDuringImport, value:
              print value
***************
*** 1095,1102 ****
      pager(title % (desc + suffix) + '\n\n' + text.document(thing, name))
  
! def writedoc(key):
      """Write HTML documentation to a file in the current directory."""
      try:
!         object = locate(key)
      except ErrorDuringImport, value:
          print value
--- 1109,1116 ----
      pager(title % (desc + suffix) + '\n\n' + text.document(thing, name))
  
! def writedoc(key, forceload=0):
      """Write HTML documentation to a file in the current directory."""
      try:
!         object = locate(key, forceload)
      except ErrorDuringImport, value:
          print value
***************
*** 1112,1121 ****
              print 'no Python documentation found for %s' % repr(key)
  
! def writedocs(dir, pkgpath='', done={}):
      """Write out HTML documentation for all modules in a directory tree."""
      for file in os.listdir(dir):
          path = os.path.join(dir, file)
          if ispackage(path):
!             writedocs(path, pkgpath + file + '.')
          elif os.path.isfile(path):
              modname = inspect.getmodulename(path)
--- 1126,1136 ----
              print 'no Python documentation found for %s' % repr(key)
  
! def writedocs(dir, pkgpath='', done=None):
      """Write out HTML documentation for all modules in a directory tree."""
+     if done is None: done = {}
      for file in os.listdir(dir):
          path = os.path.join(dir, file)
          if ispackage(path):
!             writedocs(path, pkgpath + file + '.', done)
          elif os.path.isfile(path):
              modname = inspect.getmodulename(path)
***************
*** 1252,1259 ****
                  self.docdir = dir
  
-     def __repr__(self):
-         self()
-         return ''
- 
      def __call__(self, request=None):
          if request is not None:
--- 1267,1270 ----
***************
*** 1261,1275 ****
          else:
              self.intro()
!             self.output.write('\n')
!             while 1:
!                 self.output.write('help> ')
!                 self.output.flush()
!                 try:
!                     request = self.input.readline()
!                     if not request: break
!                 except KeyboardInterrupt: break
!                 request = strip(replace(request, '"', '', "'", ''))
!                 if lower(request) in ['q', 'quit']: break
!                 self.help(request)
              self.output.write('''
  You're now leaving help and returning to the Python interpreter.
--- 1272,1276 ----
          else:
              self.intro()
!             self.interact()
              self.output.write('''
  You're now leaving help and returning to the Python interpreter.
***************
*** 1279,1282 ****
--- 1280,1296 ----
  ''')
  
+     def interact(self):
+         self.output.write('\n')
+         while 1:
+             self.output.write('help> ')
+             self.output.flush()
+             try:
+                 request = self.input.readline()
+                 if not request: break
+             except KeyboardInterrupt: break
+             request = strip(replace(request, '"', '', "'", ''))
+             if lower(request) in ['q', 'quit']: break
+             self.help(request)
+ 
      def help(self, request):
          if type(request) is type(''):
***************
*** 1362,1367 ****
              return
  
!         divpat = re.compile('<div[^>]*navigat.*?</div[^>]*>', re.I | re.S)
!         addrpat = re.compile('<address[^>]*>.*?</address[^>]*>', re.I | re.S)
          document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
          file.close()
--- 1376,1381 ----
              return
  
!         divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
!         addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
          document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
          file.close()
***************
*** 1461,1465 ****
                      callback(None, modname, '')
                  else:
!                     desc = split(freshimport(modname).__doc__ or '', '\n')[0]
                      if find(lower(modname + ' - ' + desc), key) >= 0:
                          callback(None, modname, desc)
--- 1475,1479 ----
                      callback(None, modname, '')
                  else:
!                     desc = split(__import__(modname).__doc__ or '', '\n')[0]
                      if find(lower(modname + ' - ' + desc), key) >= 0:
                          callback(None, modname, desc)
***************
*** 1523,1527 ****
              if path and path != '.':
                  try:
!                     obj = locate(path)
                  except ErrorDuringImport, value:
                      self.send_document(path, html.escape(str(value)))
--- 1537,1541 ----
              if path and path != '.':
                  try:
!                     obj = locate(path, forceload=1)
                  except ErrorDuringImport, value:
                      self.send_document(path, html.escape(str(value)))