| OLD | NEW |
| (Empty) | |
| 1 #!/usr/bin/env python |
| 2 import argparse |
| 3 import ast |
| 4 import logging |
| 5 import os |
| 6 import sys |
| 7 |
| 8 logging.basicConfig(format='%(levelname)s:%(message)s', stream=sys.stdout) |
| 9 |
| 10 |
| 11 def is_true(node): |
| 12 return isinstance(node, ast.Name) and node.id == 'True' |
| 13 |
| 14 |
| 15 def node_repr(node): |
| 16 if isinstance(node, ast.Name): |
| 17 return node.id |
| 18 else: |
| 19 raise NotImplementedError(ast.dump(node)) |
| 20 |
| 21 |
| 22 def lint_shell_command_calls(basename, filename): |
| 23 with open(os.path.join(basename, filename)) as f: |
| 24 try: |
| 25 tree = ast.parse(f.read(), filename) |
| 26 except Exception: |
| 27 logging.warning('%s: Parse error, skipping.', filename) |
| 28 return |
| 29 |
| 30 for node in ast.walk(tree): |
| 31 if (isinstance(node, ast.Call) and |
| 32 isinstance(node.func, ast.Attribute) and |
| 33 node.func.attr == 'RunShellCommand'): |
| 34 |
| 35 if node.args and isinstance(node.args[0], ast.Str): |
| 36 logging.warning( |
| 37 '%s:%s: Avoid passing command as a string.', filename, node.lineno) |
| 38 |
| 39 kwargs = {kw.arg: kw.value for kw in node.keywords} |
| 40 check_return = kwargs.get('check_return') |
| 41 if check_return is None: |
| 42 logging.warning( |
| 43 '%s:%s: Missing check_return=True.', filename, node.lineno) |
| 44 elif not is_true(check_return): |
| 45 logging.warning( |
| 46 '%s:%s: Consider switching check_return=%s to True.', |
| 47 filename, node.lineno, node_repr(check_return)) |
| 48 |
| 49 |
| 50 def ignore_dir(dirname): |
| 51 return dirname.startswith('.') or dirname == 'third_party' |
| 52 |
| 53 |
| 54 def iter_python_files(root): |
| 55 for abs_dirpath, dirnames, filenames in os.walk(root): |
| 56 dirpath = os.path.relpath(abs_dirpath, root) |
| 57 for filename in filenames: |
| 58 if filename.endswith('.py'): |
| 59 yield os.path.join(dirpath, filename) |
| 60 dirnames[:] = [d for d in dirnames if not ignore_dir(d)] |
| 61 |
| 62 |
| 63 def main(): |
| 64 parser = argparse.ArgumentParser() |
| 65 parser.add_argument('root') |
| 66 args = parser.parse_args() |
| 67 |
| 68 args.root = os.path.realpath(args.root) |
| 69 for filename in iter_python_files(args.root): |
| 70 lint_shell_command_calls(args.root, filename) |
| 71 |
| 72 |
| 73 if __name__ == '__main__': |
| 74 sys.exit(main()) |
| OLD | NEW |