Updated for 2.1a3
[python/dscho.git] / Tools / scripts / checkappend.py
blobc1188e24177001b6e89b11882f85651e6b0f4af5
1 #! /usr/bin/env python
3 # Released to the public domain, by Tim Peters, 28 February 2000.
5 """checkappend.py -- search for multi-argument .append() calls.
7 Usage: specify one or more file or directory paths:
8 checkappend [-v] file_or_dir [file_or_dir] ...
10 Each file_or_dir is checked for multi-argument .append() calls. When
11 a directory, all .py files in the directory, and recursively in its
12 subdirectories, are checked.
14 Use -v for status msgs. Use -vv for more status msgs.
16 In the absence of -v, the only output is pairs of the form
18 filename(linenumber):
19 line containing the suspicious append
21 Note that this finds multi-argument append calls regardless of whether
22 they're attached to list objects. If a module defines a class with an
23 append method that takes more than one argument, calls to that method
24 will be listed.
26 Note that this will not find multi-argument list.append calls made via a
27 bound method object. For example, this is not caught:
29 somelist = []
30 push = somelist.append
31 push(1, 2, 3)
32 """
34 __version__ = 1, 0, 0
36 import os
37 import sys
38 import string
39 import getopt
40 import tokenize
42 verbose = 0
44 def errprint(*args):
45 msg = string.join(args)
46 sys.stderr.write(msg)
47 sys.stderr.write("\n")
49 def main():
50 args = sys.argv[1:]
51 global verbose
52 try:
53 opts, args = getopt.getopt(sys.argv[1:], "v")
54 except getopt.error, msg:
55 errprint(str(msg) + "\n\n" + __doc__)
56 return
57 for opt, optarg in opts:
58 if opt == '-v':
59 verbose = verbose + 1
60 if not args:
61 errprint(__doc__)
62 return
63 for arg in args:
64 check(arg)
66 def check(file):
67 if os.path.isdir(file) and not os.path.islink(file):
68 if verbose:
69 print "%s: listing directory" % `file`
70 names = os.listdir(file)
71 for name in names:
72 fullname = os.path.join(file, name)
73 if ((os.path.isdir(fullname) and
74 not os.path.islink(fullname))
75 or os.path.normcase(name[-3:]) == ".py"):
76 check(fullname)
77 return
79 try:
80 f = open(file)
81 except IOError, msg:
82 errprint("%s: I/O Error: %s" % (`file`, str(msg)))
83 return
85 if verbose > 1:
86 print "checking", `file`, "..."
88 ok = AppendChecker(file, f).run()
89 if verbose and ok:
90 print "%s: Clean bill of health." % `file`
92 [FIND_DOT,
93 FIND_APPEND,
94 FIND_LPAREN,
95 FIND_COMMA,
96 FIND_STMT] = range(5)
98 class AppendChecker:
99 def __init__(self, fname, file):
100 self.fname = fname
101 self.file = file
102 self.state = FIND_DOT
103 self.nerrors = 0
105 def run(self):
106 try:
107 tokenize.tokenize(self.file.readline, self.tokeneater)
108 except tokenize.TokenError, msg:
109 errprint("%s: Token Error: %s" % (`self.fname`, str(msg)))
110 self.nerrors = self.nerrors + 1
111 return self.nerrors == 0
113 def tokeneater(self, type, token, start, end, line,
114 NEWLINE=tokenize.NEWLINE,
115 JUNK=(tokenize.COMMENT, tokenize.NL),
116 OP=tokenize.OP,
117 NAME=tokenize.NAME):
119 state = self.state
121 if type in JUNK:
122 pass
124 elif state is FIND_DOT:
125 if type is OP and token == ".":
126 state = FIND_APPEND
128 elif state is FIND_APPEND:
129 if type is NAME and token == "append":
130 self.line = line
131 self.lineno = start[0]
132 state = FIND_LPAREN
133 else:
134 state = FIND_DOT
136 elif state is FIND_LPAREN:
137 if type is OP and token == "(":
138 self.level = 1
139 state = FIND_COMMA
140 else:
141 state = FIND_DOT
143 elif state is FIND_COMMA:
144 if type is OP:
145 if token in ("(", "{", "["):
146 self.level = self.level + 1
147 elif token in (")", "}", "]"):
148 self.level = self.level - 1
149 if self.level == 0:
150 state = FIND_DOT
151 elif token == "," and self.level == 1:
152 self.nerrors = self.nerrors + 1
153 print "%s(%d):\n%s" % (self.fname, self.lineno,
154 self.line)
155 # don't gripe about this stmt again
156 state = FIND_STMT
158 elif state is FIND_STMT:
159 if type is NEWLINE:
160 state = FIND_DOT
162 else:
163 raise SystemError("unknown internal state '%s'" % `state`)
165 self.state = state
167 if __name__ == '__main__':
168 main()