From 8a8aaf5a6006cbd8d7957eece08c88c33b2e442e Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Tue, 4 Aug 2015 13:45:41 -0700 Subject: [PATCH] Add a hook to verify python ast. --- README.md | 1 + hooks.yaml | 6 ++++++ pre_commit_hooks/check_ast.py | 36 +++++++++++++++++++++++++++++++++++ setup.py | 1 + tests/check_ast_test.py | 15 +++++++++++++++ 5 files changed, 59 insertions(+) create mode 100644 pre_commit_hooks/check_ast.py create mode 100644 tests/check_ast_test.py diff --git a/README.md b/README.md index 93b6360..65e8617 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ Add this to your `.pre-commit-config.yaml` see `.pre-commit-config.yaml` in this repository for an example. - `check-added-large-files` - Prevent giant files from being committed. - Specify what is "too large" with `args: ['--maxkb=123']` (default=500kB). +- `check-ast` - Simply check whether files parse as valid python. - `check-case-conflict` - Check for files with names that would conflict on a case-insensitive filesystem like MacOS HFS+ or Windows FAT. - `check-docstring-first` - Checks for a common error of placing code before diff --git a/hooks.yaml b/hooks.yaml index 9282867..8fcbe78 100644 --- a/hooks.yaml +++ b/hooks.yaml @@ -12,6 +12,12 @@ language: python # Match all files files: '' +- id: check-ast + name: Check python ast + description: Simply check whether the files parse as valid python. + entry: check-ast + language: python + files: '\.py$' - id: check-case-conflict name: Check for case conflicts description: Check for files that would conflict in case-insensitive filesystems diff --git a/pre_commit_hooks/check_ast.py b/pre_commit_hooks/check_ast.py new file mode 100644 index 0000000..c993e6a --- /dev/null +++ b/pre_commit_hooks/check_ast.py @@ -0,0 +1,36 @@ +from __future__ import absolute_import +from __future__ import print_function +from __future__ import unicode_literals + +import argparse +import ast +import os.path +import sys +import traceback + + +def check_ast(argv=None): + parser = argparse.ArgumentParser() + parser.add_argument('filenames', nargs='*') + args = parser.parse_args(argv) + + _, interpreter = os.path.split(sys.executable) + + retval = 0 + for filename in args.filenames: + + try: + ast.parse(open(filename, 'rb').read(), filename=filename) + except SyntaxError: + print('{0}: failed parsing with {1}:'.format( + filename, interpreter, + )) + print('\n{0}'.format( + ' ' + traceback.format_exc().replace('\n', '\n ') + )) + retval = 1 + return retval + + +if __name__ == '__main__': + exit(check_ast()) diff --git a/setup.py b/setup.py index 7a66dcf..4fefeaa 100644 --- a/setup.py +++ b/setup.py @@ -38,6 +38,7 @@ setup( 'console_scripts': [ 'autopep8-wrapper = pre_commit_hooks.autopep8_wrapper:main', 'check-added-large-files = pre_commit_hooks.check_added_large_files:main', + 'check-ast = pre_commit_hooks.check_ast:check_ast', 'check-case-conflict = pre_commit_hooks.check_case_conflict:main', 'check-docstring-first = pre_commit_hooks.check_docstring_first:main', 'check-json = pre_commit_hooks.check_json:check_json', diff --git a/tests/check_ast_test.py b/tests/check_ast_test.py new file mode 100644 index 0000000..64916ba --- /dev/null +++ b/tests/check_ast_test.py @@ -0,0 +1,15 @@ +from __future__ import absolute_import +from __future__ import unicode_literals + +from pre_commit_hooks.check_ast import check_ast +from testing.util import get_resource_path + + +def test_failing_file(): + ret = check_ast([get_resource_path('cannot_parse_ast.notpy')]) + assert ret == 1 + + +def test_passing_file(): + ret = check_ast([__file__]) + assert ret == 0