From 2b102a940d174d5f3e446f875460cbd39a3e69d2 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Sat, 15 Jul 2023 17:36:47 -0700 Subject: [PATCH] symbex --check option, closes #35 --- README.md | 11 +++++++++++ symbex/cli.py | 12 ++++++++++-- tests/test_filters.py | 11 +++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 71c18e5..7f788f1 100644 --- a/README.md +++ b/README.md @@ -343,6 +343,16 @@ When I ran this the result was a `second_function` definition like this: def second_function(a: int, b: int) -> int: return a + b + 3 ``` +## Using in CI + +The `--check` option causes `symbex` to return a non-zero exit code if any matches are found for your query. + +You can use this in CI to guard against things like functions being added without documentation: + +```bash +symbex --function --undocumented --check +``` +This will fail silently but set a `1` exit code if there are any undocumented functions. ## Similar tools @@ -425,6 +435,7 @@ Options: --partially-typed Filter functions with partial type annotations --fully-typed Filter functions with full type annotations --no-init Filter to exclude any __init__ methods + --check Exit with non-zero code if any matches found --replace Replace matching symbol with text from stdin --help Show this message and exit. diff --git a/symbex/cli.py b/symbex/cli.py index d6a246c..76e84a6 100644 --- a/symbex/cli.py +++ b/symbex/cli.py @@ -138,6 +138,9 @@ is_flag=True, help="Filter to exclude any __init__ methods", ) +@click.option( + "--check", is_flag=True, help="Exit with non-zero code if any matches found" +) @click.option( "--replace", is_flag=True, @@ -167,6 +170,7 @@ def cli( partially_typed, fully_typed, no_init, + check, replace, ): """ @@ -391,9 +395,10 @@ def filter(node: ast.AST) -> bool: for node, class_name in nodes: if not filter(node): continue - if count: + if count or check: num_matches += 1 - continue + if count or not signatures: + continue # If file is within pwd, print relative path if pwd in file.resolve().parents: path = file.resolve().relative_to(pwd) @@ -425,6 +430,9 @@ def filter(node: ast.AST) -> bool: if count: click.echo(num_matches) + if check and num_matches > 0: + sys.exit(1) + if replace: # Only works if we got a single match if len(replace_matches) != 1: diff --git a/tests/test_filters.py b/tests/test_filters.py index b7a2735..c9dcad2 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -210,3 +210,14 @@ def test_filters(args, expected): ) assert result2.exit_code == 0 assert result2.stdout.strip() == str(expected_count) + + # And the --check option + result3 = runner.invoke( + cli, + full_args + ["--check"], + catch_exceptions=False, + ) + if expected: + assert result3.exit_code == 1 + else: + assert result3.exit_code == 0