summaryrefslogtreecommitdiffstats
path: root/Tools/scripts/checkappend.py
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2000-02-29 13:05:49 (GMT)
committerGuido van Rossum <guido@python.org>2000-02-29 13:05:49 (GMT)
commitaacf5ce1ad93189ab9cc03ae6b5fbc2a16e93958 (patch)
treeaaaf81a415be7f45f48cb72b875a97f5a8b136c3 /Tools/scripts/checkappend.py
parent67dd17f730000cc369c8d48e747c1cf2af10c381 (diff)
downloadcpython-aacf5ce1ad93189ab9cc03ae6b5fbc2a16e93958.zip
cpython-aacf5ce1ad93189ab9cc03ae6b5fbc2a16e93958.tar.gz
cpython-aacf5ce1ad93189ab9cc03ae6b5fbc2a16e93958.tar.bz2
Script by Tim Peters to discover illegal append() calls.
Diffstat (limited to 'Tools/scripts/checkappend.py')
-rwxr-xr-xTools/scripts/checkappend.py168
1 files changed, 168 insertions, 0 deletions
diff --git a/Tools/scripts/checkappend.py b/Tools/scripts/checkappend.py
new file mode 100755
index 0000000..727d5ee
--- /dev/null
+++ b/Tools/scripts/checkappend.py
@@ -0,0 +1,168 @@
+#! /usr/bin/env python
+
+# Released to the public domain, by Tim Peters, 28 February 2000.
+
+"""checkappend.py -- search for multi-argument .append() calls.
+
+Usage: specify one or more file or directory paths:
+ checkappend [-v] file_or_dir [file_or_dir] ...
+
+Each file_or_dir is checked for multi-argument .append() calls. 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.
+
+In the absence of -v, the only output is pairs of the form
+
+ filename(linenumber):
+ line containing the suspicious append
+
+Note that this finds multi-argument append calls regardless of whether
+they're attached to list objects. If a module defines a class with an
+append method that takes more than one argument, calls to that method
+will be listed.
+
+Note that this will not find 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
+
+def errprint(*args):
+ msg = string.join(args)
+ sys.stderr.write(msg)
+ sys.stderr.write("\n")
+
+def main():
+ args = sys.argv[1:]
+ global verbose
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "v")
+ except getopt.error, msg:
+ errprint(msg + "\n\n" + __doc__)
+ return
+ for opt, optarg in opts:
+ if opt == '-v':
+ verbose = verbose + 1
+ 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 = AppendChecker(file, f).run()
+ if verbose and ok:
+ print "%s: Clean bill of health." % `file`
+
+[FIND_DOT,
+ FIND_APPEND,
+ FIND_LPAREN,
+ FIND_COMMA,
+ FIND_STMT] = range(5)
+
+class AppendChecker:
+ 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_APPEND
+
+ elif state is FIND_APPEND:
+ if type is NAME and token == "append":
+ 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()