Added debug statement hook.
diff --git a/.gitignore b/.gitignore index 831f634..abd9e46 100644 --- a/.gitignore +++ b/.gitignore
@@ -9,3 +9,4 @@ dist *.egg-info *.iml +.pre-commit-files
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..89bae15 --- /dev/null +++ b/.pre-commit-config.yaml
@@ -0,0 +1,19 @@ + +- + repo: git@github.com:pre-commit/pre-commit-hooks + sha: 5e713f8878b7d100c0e059f8cc34be4fc2e8f897 + hooks: + - + id: pyflakes + files: '\.py$' + +- + repo: git@github.com:pre-commit/pre-commit + sha: c77d65d9cbbcf30e2be005f5ba8b63447deedc1e + hooks: + - + id: validate_manifest + files: /manifest.yaml + - + id: validate_config + files: /\.pre-commit-config.yaml
diff --git a/Makefile b/Makefile index 161f6d0..9aedace 100644 --- a/Makefile +++ b/Makefile
@@ -30,7 +30,7 @@ coverage run `which py.test` tests $(TEST_TARGETS) && \ coverage report -m' -py_env: requirements.txt +py_env: requirements.txt setup.py rm -rf py_env virtualenv py_env bash -c 'source py_env/bin/activate && \
diff --git a/manifest.yaml b/manifest.yaml index 0f24380..f7e7e47 100644 --- a/manifest.yaml +++ b/manifest.yaml
@@ -5,3 +5,9 @@ description: This validator runs pyflakes. entry: pyflakes language: python +- + id: debug-statements + name: Debug Statements (Python) + description: This hook checks that debug statements (pdb, ipdb, pudb) are not imported on commit. + entry: debug-statement-hook + language: python
diff --git a/pre_commit_hooks/debug_statement_hook.py b/pre_commit_hooks/debug_statement_hook.py new file mode 100644 index 0000000..1335cb0 --- /dev/null +++ b/pre_commit_hooks/debug_statement_hook.py
@@ -0,0 +1,63 @@ + +import argparse +import ast +import collections +import sys + + +DEBUG_STATEMENTS = set(['pdb', 'ipdb', 'pudb']) + + +DebugStatement = collections.namedtuple( + 'DebugStatement', ['name', 'line', 'col'], +) + + +class ImportStatementParser(ast.NodeVisitor): + def __init__(self): + self.debug_import_statements = [] + + def visit_Import(self, node): + for node_name in node.names: + if node_name.name in DEBUG_STATEMENTS: + self.debug_import_statements.append( + DebugStatement(node_name.name, node.lineno, node.col_offset), + ) + + def visit_ImportFrom(self, node): + if node.module in DEBUG_STATEMENTS: + self.debug_import_statements.append( + DebugStatement(node.module, node.lineno, node.col_offset) + ) + + +def check_file_for_debug_statements(filename): + ast_obj = ast.parse(open(filename).read()) + visitor = ImportStatementParser() + visitor.visit(ast_obj) + if visitor.debug_import_statements: + for debug_statement in visitor.debug_import_statements: + print '{0}:{2}:{3} - {1} imported'.format(filename, *debug_statement) + return 1 + else: + return 0 + + +def debug_statement_hook(argv): + parser = argparse.ArgumentParser() + parser.add_argument('filenames', nargs='+', help='Filenames to run') + args = parser.parse_args() + + retv = 0 + for filename in args.filenames: + retv |= check_file_for_debug_statements(filename) + + return retv + + +def entry(): + return debug_statement_hook(sys.argv[1:]) + + +if __name__ == '__main__': + sys.exit(entry()) \ No newline at end of file
diff --git a/requirements.txt b/requirements.txt index 5539ad9..0c043a5 100644 --- a/requirements.txt +++ b/requirements.txt
@@ -6,4 +6,5 @@ coverage ipdb mock +git+git://github.com/pre-commit/pre-commit#egg=pre-commit pytest
diff --git a/setup.py b/setup.py index 496f5aa..ccca970 100644 --- a/setup.py +++ b/setup.py
@@ -10,4 +10,9 @@ 'pyflakes', 'simplejson', ], + entry_points={ + 'console_scripts': [ + 'debug-statement-hook = pre_commit_hooks.debug_statement_hook:entry', + ], + }, )
diff --git a/tests/debug_statement_hook_test.py b/tests/debug_statement_hook_test.py new file mode 100644 index 0000000..ecfdbc0 --- /dev/null +++ b/tests/debug_statement_hook_test.py
@@ -0,0 +1,56 @@ + +import ast +import pytest + +from pre_commit_hooks.debug_statement_hook import ImportStatementParser, \ + DebugStatement + + +@pytest.fixture +def ast_with_no_debug_imports(): + return ast.parse(""" +import foo +import bar +import baz +from foo import bar +""") + + +@pytest.fixture +def ast_with_debug_import_form_1(): + return ast.parse(""" + +import ipdb; ipdb.set_trace() + +""") + + +@pytest.fixture +def ast_with_debug_import_form_2(): + return ast.parse(""" + +from pudb import set_trace; set_trace() + +""") + + +def test_returns_no_debug_statements(ast_with_no_debug_imports): + visitor = ImportStatementParser() + visitor.visit(ast_with_no_debug_imports) + assert visitor.debug_import_statements == [] + + +def test_returns_one_form_1(ast_with_debug_import_form_1): + visitor = ImportStatementParser() + visitor.visit(ast_with_debug_import_form_1) + assert visitor.debug_import_statements == [ + DebugStatement('ipdb', 3, 0) + ] + + +def test_returns_one_form_2(ast_with_debug_import_form_2): + visitor = ImportStatementParser() + visitor.visit(ast_with_debug_import_form_2) + assert visitor.debug_import_statements == [ + DebugStatement('pudb', 3, 0) + ] \ No newline at end of file