PageRenderTime 142ms CodeModel.GetById 129ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

/Tools/scripts/checkappend.py

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