[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()