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