diff --git a/doc-source/hooks.rst b/doc-source/hooks.rst index e4e1e8f..4f878bc 100644 --- a/doc-source/hooks.rst +++ b/doc-source/hooks.rst @@ -30,6 +30,15 @@ Pull ``# noqa: ...`` comments that immediately follow docstrings back up to the This hook takes no arguments. +``check_ast`` +-------------------- + +Check the source can be parsed as a Python Abstract Syntax Tree. +This could be called early in the execution -- to check the file is valid before starting reformatting -- and again at the end to ensure no errors were introduced by the reformatting. + +This hook takes no arguments. + + ``ellipsis_reformat`` ----------------------- diff --git a/formate/mini_hooks.py b/formate/mini_hooks.py index e10a84b..6c47da2 100644 --- a/formate/mini_hooks.py +++ b/formate/mini_hooks.py @@ -27,9 +27,10 @@ # # stdlib +import ast import re -__all__ = ["noqa_reformat"] +__all__ = ["noqa_reformat", "check_ast"] def noqa_reformat(source: str) -> str: @@ -42,3 +43,16 @@ def noqa_reformat(source: str) -> str: """ return re.sub(r'"""[\n\s]+#\s+noqa', '""" # noqa', source) + + +def check_ast(source: str) -> str: + """ + Check the source can be parsed as a Python Abstract Syntax Tree. + + :param source: The source to check. + + :raises SyntaxError: If the source is not valid Python. + """ + + ast.parse(source) + return source diff --git a/tests/test_integration.py b/tests/test_integration.py index 05fd8d6..7310f7a 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -3,8 +3,8 @@ # 3rd party import pytest -from coincidence.selectors import not_pypy, only_pypy from coincidence.regressions import AdvancedDataRegressionFixture, check_file_output, check_file_regression +from coincidence.selectors import not_pypy, only_pypy from consolekit.terminal_colours import strip_ansi from consolekit.testing import CliRunner, Result from domdf_python_tools.paths import PathPlus, in_directory diff --git a/tests/test_mini_hooks.py b/tests/test_mini_hooks.py index 3b195fb..8bf5f41 100644 --- a/tests/test_mini_hooks.py +++ b/tests/test_mini_hooks.py @@ -1,5 +1,8 @@ +# 3rd party +import pytest + # this package -from formate.mini_hooks import noqa_reformat +from formate.mini_hooks import check_ast, noqa_reformat def test_noqa_reformat(): @@ -20,3 +23,40 @@ def test_noqa_reformat(): assert noqa_reformat('\n'.join(code)) == '\n'.join(expected) assert noqa_reformat('\n'.join(expected)) == '\n'.join(expected) + + +def test_check_ast(): + code = [ + "def foo(:", + '\t"""', + "\tDoes something,", + '\t"""', + '', + " # noqa: D400 ", + ] + + with pytest.raises(SyntaxError): + check_ast('\n'.join(code)) + + code = [ + "def foo():", + '"""', + "\tDoes something,", + '\t"""', + '', + " # noqa: D400 ", + ] + + with pytest.raises(SyntaxError): + check_ast('\n'.join(code)) + + code = [ + "def foo():", + '\t"""', + "\tDoes something,", + '\t"""', + '', + " # noqa: D400 ", + ] + + assert check_ast('\n'.join(code)) == '\n'.join(code)