/Tools/scripts/checkappend.py

http://unladen-swallow.googlecode.com/ · Python · 167 lines · 128 code · 8 blank · 31 comment · 5 complexity · be468930a510f370b8f6834a0be18f9e MD5 · raw file

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