[Python-checkins] python/dist/src/Lib pyclbr.py,1.28,1.29

gvanrossum@users.sourceforge.net gvanrossum@users.sourceforge.net
Mon, 02 Dec 2002 06:54:22 -0800


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

Modified Files:
	pyclbr.py 
Log Message:
Moderately heavy reorganization of pyclbr to fix package-related bugs.

- The _modules cache now uses the full module name.

- The meaning of the (internal!!!) inpackage argument is changed: it
  now is the parent package name, or None.  readmodule() doesn't
  support this argument any more.

- The meaning of the path argument is changed: when inpackage is set,
  the module *must* be found in this path (as is the case for the real
  package search).

- Miscellaneous cleanup, e.g. fixed __all__, changed some comments and
  doc strings, etc.

- Adapted the unit tests to the new semantics (nothing much changed,
  really).  Added some debugging code to the unit tests that print
  helpful extra info to stderr when a test fails (interpreting the
  test failures turned out to be hard without these).



Index: pyclbr.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/pyclbr.py,v
retrieving revision 1.28
retrieving revision 1.29
diff -C2 -d -r1.28 -r1.29
*** pyclbr.py	16 Sep 2002 16:36:02 -0000	1.28
--- pyclbr.py	2 Dec 2002 14:54:19 -0000	1.29
***************
*** 17,20 ****
--- 17,21 ----
  A class is described by the class Class in this module.  Instances
  of this class have the following instance variables:
+         module -- the module name
          name -- the name of the class
          super -- a list of super classes (Class instances)
***************
*** 30,51 ****
  shouldn't happen often.
  
! XXX describe the Function class.
  
  BUGS
  - Nested classes and functions can confuse it.
  
! PACKAGE RELATED BUGS
! - If you have a package and a module inside that or another package
!   with the same name, module caching doesn't work properly since the
!   key is the base name of the module/package.
! - The only entry that is returned when you readmodule a package is a
!   __path__ whose value is a list which confuses certain class browsers.
! - When code does:
!   from package import subpackage
!   class MyClass(subpackage.SuperClass):
!     ...
!   It can't locate the parent.  It probably needs to have the same
!   hairy logic that the import locator already does.  (This logic
!   exists coded in Python in the freeze package.)
  """
  
--- 31,49 ----
  shouldn't happen often.
  
! A function is described by the class Function in this module.
! Instances of this class have the following instance variables:
!         module -- the module name
!         name -- the name of the class
!         file -- the file in which the class was defined
!         lineno -- the line in the file on which the class statement occurred
! 
  
  BUGS
  - Nested classes and functions can confuse it.
  
! PACKAGE CAVEAT
! - When you call readmodule_ex for a package, dict['__path__'] is a
!   list, which may confuse older class browsers.  (readmodule filters
!   these out though.)
  """
  
***************
*** 55,59 ****
  from token import NAME
  
! __all__ = ["readmodule"]
  
  _modules = {}                           # cache of modules we've seen
--- 53,57 ----
  from token import NAME
  
! __all__ = ["readmodule", "readmodule_ex", "Class", "Function"]
  
  _modules = {}                           # cache of modules we've seen
***************
*** 75,148 ****
          self.methods[name] = lineno
  
! class Function(Class):
      '''Class to represent a top-level Python function'''
      def __init__(self, module, name, file, lineno):
!         Class.__init__(self, module, name, None, file, lineno)
!     def _addmethod(self, name, lineno):
!         assert 0, "Function._addmethod() shouldn't be called"
  
! def readmodule(module, path=[], inpackage=False):
      '''Backwards compatible interface.
  
!     Like readmodule_ex() but strips Function objects from the
      resulting dictionary.'''
  
!     dict = readmodule_ex(module, path, inpackage)
      res = {}
      for key, value in dict.items():
!         if not isinstance(value, Function):
              res[key] = value
      return res
  
! def readmodule_ex(module, path=[], inpackage=False):
      '''Read a module file and return a dictionary of classes.
  
      Search for MODULE in PATH and sys.path, read and parse the
      module and return a dictionary with one entry for each class
!     found in the module.'''
  
      dict = {}
  
      i = module.rfind('.')
      if i >= 0:
!         # Dotted module name
!         package = module[:i].strip()
!         submodule = module[i+1:].strip()
          parent = readmodule_ex(package, path, inpackage)
!         child = readmodule_ex(submodule, parent['__path__'], True)
!         return child
! 
!     if module in _modules:
!         # we've seen this module before...
!         return _modules[module]
!     if module in sys.builtin_module_names:
!         # this is a built-in module
!         _modules[module] = dict
!         return dict
  
!     # search the path for the module
      f = None
      if inpackage:
!         try:
!             f, file, (suff, mode, type) = \
!                     imp.find_module(module, path)
!         except ImportError:
!             f = None
!     if f is None:
!         fullpath = list(path) + sys.path
!         f, file, (suff, mode, type) = imp.find_module(module, fullpath)
      if type == imp.PKG_DIRECTORY:
          dict['__path__'] = [file]
-         _modules[module] = dict
          path = [file] + path
!         f, file, (suff, mode, type) = \
!                         imp.find_module('__init__', [file])
      if type != imp.PY_SOURCE:
          # not Python source, can't do anything with this module
          f.close()
-         _modules[module] = dict
          return dict
  
-     _modules[module] = dict
      classstack = [] # stack of (class, indent) pairs
  
--- 73,154 ----
          self.methods[name] = lineno
  
! class Function:
      '''Class to represent a top-level Python function'''
      def __init__(self, module, name, file, lineno):
!         self.module = module
!         self.name = name
!         self.file = file
!         self.lineno = lineno
  
! def readmodule(module, path=[]):
      '''Backwards compatible interface.
  
!     Call readmodule_ex() and then only keep Class objects from the
      resulting dictionary.'''
  
!     dict = readmodule_ex(module, path)
      res = {}
      for key, value in dict.items():
!         if isinstance(value, Class):
              res[key] = value
      return res
  
! def readmodule_ex(module, path=[], inpackage=None):
      '''Read a module file and return a dictionary of classes.
  
      Search for MODULE in PATH and sys.path, read and parse the
      module and return a dictionary with one entry for each class
!     found in the module.
! 
!     If INPACKAGE is true, it must be the dotted name of the package in
!     which we are searching for a submodule, and then PATH must be the
!     package search path; otherwise, we are searching for a top-level
!     module, and PATH is combined with sys.path.
!     '''
! 
!     # Compute the full module name (prepending inpackage if set)
!     if inpackage:
!         fullmodule = "%s.%s" % (inpackage, module)
!     else:
!         fullmodule = module
! 
!     # Check in the cache
!     if fullmodule in _modules:
!         return _modules[fullmodule]
  
+     # Initialize the dict for this module's contents
      dict = {}
  
+     # Check if it is a built-in module; we don't do much for these
+     if module in sys.builtin_module_names and not inpackage:
+         _modules[module] = dict
+         return dict
+ 
+     # Check for a dotted module name
      i = module.rfind('.')
      if i >= 0:
!         package = module[:i]
!         submodule = module[i+1:]
          parent = readmodule_ex(package, path, inpackage)
!         if inpackage:
!             package = "%s.%s" % (inpackage, package)
!         return readmodule_ex(submodule, parent['__path__'], package)
  
!     # Search the path for the module
      f = None
      if inpackage:
!         f, file, (suff, mode, type) = imp.find_module(module, path)
!     else:
!         f, file, (suff, mode, type) = imp.find_module(module, path + sys.path)
      if type == imp.PKG_DIRECTORY:
          dict['__path__'] = [file]
          path = [file] + path
!         f, file, (suff, mode, type) = imp.find_module('__init__', [file])
!     _modules[fullmodule] = dict
      if type != imp.PY_SOURCE:
          # not Python source, can't do anything with this module
          f.close()
          return dict
  
      classstack = [] # stack of (class, indent) pairs
  
***************
*** 222,226 ****
                      try:
                          # Recursively read the imported module
!                         readmodule_ex(mod, path, inpackage)
                      except:
                          # If we can't find or parse the imported module,
--- 228,238 ----
                      try:
                          # Recursively read the imported module
!                         if not inpackage:
!                             readmodule_ex(mod, path)
!                         else:
!                             try:
!                                 readmodule_ex(mod, path, inpackage)
!                             except ImportError:
!                                 readmodule_ex(mod)
                      except:
                          # If we can't find or parse the imported module,