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

gvanrossum@users.sourceforge.net gvanrossum@users.sourceforge.net
2002年12月03日 00:14:37 -0800


Update of /cvsroot/python/python/dist/src/Lib
In directory sc8-pr-cvs1:/tmp/cvs-serv6177
Modified Files:
	pyclbr.py 
Log Message:
Another big update, fixing all known bugs related to nesting functions
and classes. Also add a mini main program that dumps the results for
a given file or module.
Index: pyclbr.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/pyclbr.py,v
retrieving revision 1.29
retrieving revision 1.30
diff -C2 -d -r1.29 -r1.30
*** pyclbr.py	2 Dec 2002 14:54:19 -0000	1.29
--- pyclbr.py	3 Dec 2002 08:14:35 -0000	1.30
***************
*** 1,17 ****
! """Parse a Python file and retrieve classes and methods.
 
! Parse enough of a Python file to recognize class and method
! definitions and to find out the superclasses of a class.
 
 The interface consists of a single function:
! readmodule_ex(module [, path[, inpackage]])
! module is the name of a Python module, path is an optional list of
! directories where the module is to be searched. If present, path is
! prepended to the system search path sys.path. (inpackage is used
! internally to search for a submodule of a package.)
! The return value is a dictionary. The keys of the dictionary are
! the names of the classes defined in the module (including classes
! that are defined via the from XXX import YYY construct). The values
! are class instances of the class Class defined here.
 
 A class is described by the class Class in this module. Instances
--- 1,18 ----
! """Parse a Python module and describe its classes and methods.
 
! Parse enough of a Python file to recognize imports and class and
! method definitions, and to find out the superclasses of a class.
 
 The interface consists of a single function:
! readmodule_ex(module [, path])
! where module is the name of a Python module, and path is an optional
! list of directories where the module is to be searched. If present,
! path is prepended to the system search path sys.path. The return
! value is a dictionary. The keys of the dictionary are the names of
! the classes defined in the module (including classes that are defined
! via the from XXX import YYY construct). The values are class
! instances of the class Class defined here. One special key/value pair
! is present for packages: the key '__path__' has a list as its value
! which contains the package search path.
 
 A class is described by the class Class in this module. Instances
***************
*** 37,49 ****
 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.)
 """
 
--- 38,41 ----
***************
*** 51,55 ****
 import imp
 import tokenize # Python tokenizer
! from token import NAME
 
 __all__ = ["readmodule", "readmodule_ex", "Class", "Function"]
--- 43,47 ----
 import imp
 import tokenize # Python tokenizer
! from token import NAME, DEDENT, NEWLINE
 
 __all__ = ["readmodule", "readmodule_ex", "Class", "Function"]
***************
*** 87,91 ****
 resulting dictionary.'''
 
! dict = readmodule_ex(module, path)
 res = {}
 for key, value in dict.items():
--- 79,83 ----
 resulting dictionary.'''
 
! dict = _readmodule(module, path)
 res = {}
 for key, value in dict.items():
***************
*** 94,98 ****
 return res
 
! def readmodule_ex(module, path=[], inpackage=None):
 '''Read a module file and return a dictionary of classes.
 
--- 86,90 ----
 return res
 
! def readmodule_ex(module, path=[]):
 '''Read a module file and return a dictionary of classes.
 
***************
*** 106,110 ****
--- 98,105 ----
 module, and PATH is combined with sys.path.
 '''
+ return _readmodule(module, path)
 
+ def _readmodule(module, path, inpackage=None):
+ '''Do the hard work for readmodule[_ex].'''
 # Compute the full module name (prepending inpackage if set)
 if inpackage:
***************
*** 130,137 ****
 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
--- 125,132 ----
 package = module[:i]
 submodule = module[i+1:]
! parent = _readmodule(package, path, inpackage)
 if inpackage:
 package = "%s.%s" % (inpackage, package)
! return _readmodule(submodule, parent['__path__'], package)
 
 # Search the path for the module
***************
*** 151,184 ****
 return dict
 
! classstack = [] # stack of (class, indent) pairs
 
 g = tokenize.generate_tokens(f.readline)
 try:
 for tokentype, token, start, end, line in g:
! if token == 'def':
 lineno, thisindent = start
 tokentype, meth_name, start, end, line = g.next()
 if tokentype != NAME:
 continue # Syntax error
! # close all classes indented at least as much
! while classstack and \
! classstack[-1][1] >= thisindent:
! del classstack[-1]
! if classstack:
! # it's a class method
! cur_class = classstack[-1][0]
! cur_class._addmethod(meth_name, lineno)
 else:
 # it's a function
 dict[meth_name] = Function(module, meth_name, file, lineno)
 elif token == 'class':
 lineno, thisindent = start
 tokentype, class_name, start, end, line = g.next()
 if tokentype != NAME:
 continue # Syntax error
- # close all classes indented at least as much
- while classstack and \
- classstack[-1][1] >= thisindent:
- del classstack[-1]
 # parse what follows the class name
 tokentype, token, start, end, line = g.next()
--- 146,185 ----
 return dict
 
! stack = [] # stack of (class, indent) pairs
 
 g = tokenize.generate_tokens(f.readline)
 try:
 for tokentype, token, start, end, line in g:
! if tokentype == DEDENT:
! lineno, thisindent = start
! # close nested classes and defs
! while stack and stack[-1][1] >= thisindent:
! del stack[-1]
! elif token == 'def':
 lineno, thisindent = start
+ # close previous nested classes and defs
+ while stack and stack[-1][1] >= thisindent:
+ del stack[-1]
 tokentype, meth_name, start, end, line = g.next()
 if tokentype != NAME:
 continue # Syntax error
! if stack:
! cur_class = stack[-1][0]
! if isinstance(cur_class, Class):
! # it's a method
! cur_class._addmethod(meth_name, lineno)
! # else it's a nested def
 else:
 # it's a function
 dict[meth_name] = Function(module, meth_name, file, lineno)
+ stack.append((None, thisindent)) # Marker for nested fns
 elif token == 'class':
 lineno, thisindent = start
+ # close previous nested classes and defs
+ while stack and stack[-1][1] >= thisindent:
+ del stack[-1]
 tokentype, class_name, start, end, line = g.next()
 if tokentype != NAME:
 continue # Syntax error
 # parse what follows the class name
 tokentype, token, start, end, line = g.next()
***************
*** 209,212 ****
--- 210,214 ----
 n = d[c]
 names.append(n)
+ super = []
 if token == '(':
 level += 1
***************
*** 221,226 ****
 inherit = names
 cur_class = Class(module, class_name, inherit, file, lineno)
! dict[class_name] = cur_class
! classstack.append((cur_class, thisindent))
 elif token == 'import' and start[1] == 0:
 modules = _getnamelist(g)
--- 223,229 ----
 inherit = names
 cur_class = Class(module, class_name, inherit, file, lineno)
! if not stack:
! dict[class_name] = cur_class
! stack.append((cur_class, thisindent))
 elif token == 'import' and start[1] == 0:
 modules = _getnamelist(g)
***************
*** 229,238 ****
 # 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,
--- 232,241 ----
 # Recursively read the imported module
 if not inpackage:
! _readmodule(mod, path)
 else:
 try:
! _readmodule(mod, path, inpackage)
 except ImportError:
! _readmodule(mod, [])
 except:
 # If we can't find or parse the imported module,
***************
*** 246,250 ****
 try:
 # Recursively read the imported module
! d = readmodule_ex(mod, path, inpackage)
 except:
 # If we can't find or parse the imported module,
--- 249,253 ----
 try:
 # Recursively read the imported module
! d = _readmodule(mod, path, inpackage)
 except:
 # If we can't find or parse the imported module,
***************
*** 257,265 ****
 dict[n2 or n] = d[n]
 elif n == '*':
! # only add a name if not already there (to mimic
! # what Python does internally) also don't add
! # names that start with _
 for n in d:
! if n[0] != '_' and not n in dict:
 dict[n] = d[n]
 except StopIteration:
--- 260,266 ----
 dict[n2 or n] = d[n]
 elif n == '*':
! # don't add names that start with _
 for n in d:
! if n[0] != '_':
 dict[n] = d[n]
 except StopIteration:
***************
*** 307,308 ****
--- 308,338 ----
 parts.append(token)
 return (".".join(parts), token)
+ 
+ def _main():
+ # Main program for testing.
+ import os
+ mod = sys.argv[1]
+ if os.path.exists(mod):
+ path = [os.path.dirname(mod)]
+ mod = os.path.basename(mod)
+ if mod.lower().endswith(".py"):
+ mod = mod[:-3]
+ else:
+ path = []
+ dict = readmodule_ex(mod, path)
+ objs = dict.values()
+ objs.sort(lambda a, b: cmp(getattr(a, 'lineno', 0),
+ getattr(b, 'lineno', 0)))
+ for obj in objs:
+ if isinstance(obj, Class):
+ print "class", obj.name, obj.super, obj.lineno
+ methods = obj.methods.items()
+ methods.sort(lambda a, b: cmp(a[1], b[1]))
+ for name, lineno in methods:
+ if name != "__path__":
+ print " def", name, lineno
+ elif isinstance(obj, Function):
+ print "def", obj.name, obj.lineno
+ 
+ if __name__ == "__main__":
+ _main()

AltStyle によって変換されたページ (->オリジナル) /