ANN: argumentclinic.py, version 1
Fredrik Lundh
effbot at telia.com
Tue Apr 4 18:27:27 EDT 2000
#! /usr/bin/env python
# Released as 'checkappend.py' to the public domain, by Tim Peters, 28
# February 2000.
# With a little luck, the eff-bot managed to turn this into something
# that looks for a few more deprecated calls...
"""argumentclinic.py -- search for deprecated multi-argument calls.
Usage: specify one or more file or directory paths:
argumentclinic [-v] [-m method] file_or_dir [file_or_dir] ...
Each file_or_dir is checked for commonly used but deprecated multi-
argument method calls, such as append and connect. When a directory,
all .py files in the directory, and recursively in its subdirectories,
are checked.
Use -v for status msgs. Use -vv for more status msgs.
Use -m to look for just one given method. If omitted, the program
looks for append, bind, connect, connect_ex, count, has_key, and
remove. You can use -m multiple times, to look for a bunch of methods
of your own choice.
In the absence of -v, the only output is pairs of the form
filename(linenumber):
line containing the suspicious method
Note that this finds multi-argument methods calls regardless of
whether they're attached to the right kind of objects. If a module
defines a class with e.g. an connect method that takes more than one
argument, calls to that method will be listed.
Note that this will not find e.g. multi-argument list.append calls
made via a bound method object. For example, this is not caught:
somelist = []
push = somelist.append
push(1, 2, 3)
"""
__version__ = 1, 0, 0
import os
import sys
import string
import getopt
import tokenize
verbose = 0
methods = (
"append", "count", "remove", # lists
"has_key", # dictionaries
"bind", "connect", "connect_ex" # sockets
)
def errprint(*args):
msg = string.join(args)
sys.stderr.write(msg)
sys.stderr.write("\n")
def main():
args = sys.argv[1:]
global verbose, methods
try:
opts, args = getopt.getopt(sys.argv[1:], "vm:")
except getopt.error, msg:
errprint(msg + "\n\n" + __doc__)
return
for opt, optarg in opts:
if opt == '-v':
verbose = verbose + 1
elif opt == '-m':
try:
methods.append(optarg)
except AttributeError:
methods = [optarg]
if not args:
errprint(__doc__)
return
for arg in args:
check(arg)
def check(file):
if os.path.isdir(file) and not os.path.islink(file):
if verbose:
print "%s: listing directory" % `file`
names = os.listdir(file)
for name in names:
fullname = os.path.join(file, name)
if ((os.path.isdir(fullname) and
not os.path.islink(fullname))
or os.path.normcase(name[-3:]) == ".py"):
check(fullname)
return
try:
f = open(file)
except IOError, msg:
errprint("%s: I/O Error: %s" % (`file`, str(msg)))
return
if verbose > 1:
print "checking", `file`, "..."
ok = ArgumentChecker(file, f).run()
if verbose and ok:
print "%s: Clean bill of health." % `file`
[FIND_DOT,
FIND_METHOD,
FIND_LPAREN,
FIND_COMMA,
FIND_STMT] = range(5)
class ArgumentChecker:
def __init__(self, fname, file):
self.fname = fname
self.file = file
self.state = FIND_DOT
self.nerrors = 0
def run(self):
try:
tokenize.tokenize(self.file.readline, self.tokeneater)
except tokenize.TokenError, msg:
errprint("%s: Token Error: %s" % (`self.fname`, str(msg)))
self.nerrors = self.nerrors + 1
return self.nerrors == 0
def tokeneater(self, type, token, start, end, line,
NEWLINE=tokenize.NEWLINE,
JUNK=(tokenize.COMMENT, tokenize.NL),
OP=tokenize.OP,
NAME=tokenize.NAME):
state = self.state
if type in JUNK:
pass
elif state is FIND_DOT:
if type is OP and token == ".":
state = FIND_METHOD
elif state is FIND_METHOD:
if type is NAME and token in methods:
self.line = line
self.lineno = start[0]
state = FIND_LPAREN
else:
state = FIND_DOT
elif state is FIND_LPAREN:
if type is OP and token == "(":
self.level = 1
state = FIND_COMMA
else:
state = FIND_DOT
elif state is FIND_COMMA:
if type is OP:
if token in ("(", "{", "["):
self.level = self.level + 1
elif token in (")", "}", "]"):
self.level = self.level - 1
if self.level == 0:
state = FIND_DOT
elif token == "," and self.level == 1:
self.nerrors = self.nerrors + 1
print "%s(%d):\n%s" % (self.fname, self.lineno,
self.line)
# don't gripe about this stmt again
state = FIND_STMT
elif state is FIND_STMT:
if type is NEWLINE:
state = FIND_DOT
else:
raise SystemError("unknown internal state '%s'" % `state`)
self.state = state
if __name__ == '__main__':
main()
# --- end ---
More information about the Python-list
mailing list