home *** CD-ROM | disk | FTP | other *** search
/ PC World 2001 April / PCWorld_2001-04_cd.bin / Software / TemaCD / webclean / !!!python!!! / BeOpen-Python-2.0.exe / CHECKAPPEND.PY < prev    next >
Encoding:
Python Source  |  2000-09-28  |  4.7 KB  |  169 lines

  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.  
  7. Usage:  specify one or more file or directory paths:
  8.     checkappend [-v] file_or_dir [file_or_dir] ...
  9.  
  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.
  13.  
  14. Use -v for status msgs.  Use -vv for more status msgs.
  15.  
  16. In the absence of -v, the only output is pairs of the form
  17.  
  18.     filename(linenumber):
  19.     line containing the suspicious append
  20.  
  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.
  25.  
  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:
  28.  
  29.     somelist = []
  30.     push = somelist.append
  31.     push(1, 2, 3)
  32. """
  33.  
  34. __version__ = 1, 0, 0
  35.  
  36. import os
  37. import sys
  38. import string
  39. import getopt
  40. import tokenize
  41.  
  42. verbose = 0
  43.  
  44. def errprint(*args):
  45.     msg = string.join(args)
  46.     sys.stderr.write(msg)
  47.     sys.stderr.write("\n")
  48.  
  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)
  65.  
  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
  78.  
  79.     try:
  80.         f = open(file)
  81.     except IOError, msg:
  82.         errprint("%s: I/O Error: %s" % (`file`, str(msg)))
  83.         return
  84.  
  85.     if verbose > 1:
  86.         print "checking", `file`, "..."
  87.  
  88.     ok = AppendChecker(file, f).run()
  89.     if verbose and ok:
  90.         print "%s: Clean bill of health." % `file`
  91.  
  92. [FIND_DOT,
  93.  FIND_APPEND,
  94.  FIND_LPAREN,
  95.  FIND_COMMA,
  96.  FIND_STMT]   = range(5)
  97.  
  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
  104.  
  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
  112.  
  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):
  118.  
  119.         state = self.state
  120.  
  121.         if type in JUNK:
  122.             pass
  123.  
  124.         elif state is FIND_DOT:
  125.             if type is OP and token == ".":
  126.                 state = FIND_APPEND
  127.  
  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
  135.  
  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
  142.  
  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
  157.  
  158.         elif state is FIND_STMT:
  159.             if type is NEWLINE:
  160.                 state = FIND_DOT
  161.  
  162.         else:
  163.             raise SystemError("unknown internal state '%s'" % `state`)
  164.  
  165.         self.state = state
  166.  
  167. if __name__ == '__main__':
  168.     main()
  169.