From 0b07bd63793b51593cf5065d5dc1b3c578ff89d7 Mon Sep 17 00:00:00 2001 From: Uros <115799310+KatanicU@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:01:51 +0200 Subject: [PATCH 01/11] add new path --- pre_commit_hooks/tests_should_end_in_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pre_commit_hooks/tests_should_end_in_test.py b/pre_commit_hooks/tests_should_end_in_test.py index 07af277d..58e4a06e 100644 --- a/pre_commit_hooks/tests_should_end_in_test.py +++ b/pre_commit_hooks/tests_should_end_in_test.py @@ -14,8 +14,8 @@ def main(argv: Sequence[str] | None = None) -> int: '--pytest', dest='pattern', action='store_const', - const=r'.*_test\.py', - default=r'.*_test\.py', + const=r'.tests_*\.py', + default=r'.tests_*\.py', help='(the default) ensure tests match %(const)s', ) mutex.add_argument( From d75d74b41e1e506d2cf196c9f8448a14f7c2eee7 Mon Sep 17 00:00:00 2001 From: Uros <115799310+KatanicU@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:07:49 +0200 Subject: [PATCH 02/11] add new path --- pre_commit_hooks/tests_should_end_in_test.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pre_commit_hooks/tests_should_end_in_test.py b/pre_commit_hooks/tests_should_end_in_test.py index 58e4a06e..0452027d 100644 --- a/pre_commit_hooks/tests_should_end_in_test.py +++ b/pre_commit_hooks/tests_should_end_in_test.py @@ -14,8 +14,8 @@ def main(argv: Sequence[str] | None = None) -> int: '--pytest', dest='pattern', action='store_const', - const=r'.tests_*\.py', - default=r'.tests_*\.py', + const=r'tests_*\.py', + default=r'tests_*\.py', help='(the default) ensure tests match %(const)s', ) mutex.add_argument( @@ -41,7 +41,8 @@ def main(argv: Sequence[str] | None = None) -> int: if ( not reg.fullmatch(base) and not base == '__init__.py' and - not base == 'conftest.py' + not base == 'conftest.py' and + not base == 'models.py' ): retcode = 1 print(f'{filename} does not match pattern "{args.pattern}"') From f66937947c99a5e7a2a01840af9cd7c3dc3af47d Mon Sep 17 00:00:00 2001 From: Uros <115799310+KatanicU@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:17:53 +0200 Subject: [PATCH 03/11] change path --- pre_commit_hooks/tests_should_end_in_test.py | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/pre_commit_hooks/tests_should_end_in_test.py b/pre_commit_hooks/tests_should_end_in_test.py index 0452027d..8c342150 100644 --- a/pre_commit_hooks/tests_should_end_in_test.py +++ b/pre_commit_hooks/tests_should_end_in_test.py @@ -14,30 +14,17 @@ def main(argv: Sequence[str] | None = None) -> int: '--pytest', dest='pattern', action='store_const', - const=r'tests_*\.py', - default=r'tests_*\.py', + const=r'.*/tests_*\.py', + default=r'.*/tests_*\.py', help='(the default) ensure tests match %(const)s', ) - mutex.add_argument( - '--pytest-test-first', - dest='pattern', - action='store_const', - const=r'test_.*\.py', - help='ensure tests match %(const)s', - ) - mutex.add_argument( - '--django', '--unittest', - dest='pattern', - action='store_const', - const=r'test.*\.py', - help='ensure tests match %(const)s', - ) args = parser.parse_args(argv) retcode = 0 reg = re.compile(args.pattern) for filename in args.filenames: base = os.path.basename(filename) + print(base) if ( not reg.fullmatch(base) and not base == '__init__.py' and From 0e93be09a8bb8ca31088d08bb6c37ef0746c8837 Mon Sep 17 00:00:00 2001 From: Uros <115799310+KatanicU@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:21:21 +0200 Subject: [PATCH 04/11] change path --- pre_commit_hooks/tests_should_end_in_test.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pre_commit_hooks/tests_should_end_in_test.py b/pre_commit_hooks/tests_should_end_in_test.py index 8c342150..483aaac9 100644 --- a/pre_commit_hooks/tests_should_end_in_test.py +++ b/pre_commit_hooks/tests_should_end_in_test.py @@ -14,8 +14,8 @@ def main(argv: Sequence[str] | None = None) -> int: '--pytest', dest='pattern', action='store_const', - const=r'.*/tests_*\.py', - default=r'.*/tests_*\.py', + const=r'tests/.*/tests_*\.py', + default=r'tests/.*/tests_*\.py', help='(the default) ensure tests match %(const)s', ) args = parser.parse_args(argv) @@ -27,9 +27,10 @@ def main(argv: Sequence[str] | None = None) -> int: print(base) if ( not reg.fullmatch(base) and - not base == '__init__.py' and - not base == 'conftest.py' and - not base == 'models.py' + not base == '.*/__init__.py' and + not base == '.*/conftest.py' and + not base == '.*/models.py' + ): retcode = 1 print(f'{filename} does not match pattern "{args.pattern}"') From 8215312c10985bd9304f432984ad7c8399d5eb44 Mon Sep 17 00:00:00 2001 From: Uros <115799310+KatanicU@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:29:14 +0200 Subject: [PATCH 05/11] add new regex --- pre_commit_hooks/tests_should_end_in_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pre_commit_hooks/tests_should_end_in_test.py b/pre_commit_hooks/tests_should_end_in_test.py index 483aaac9..326e2732 100644 --- a/pre_commit_hooks/tests_should_end_in_test.py +++ b/pre_commit_hooks/tests_should_end_in_test.py @@ -14,8 +14,8 @@ def main(argv: Sequence[str] | None = None) -> int: '--pytest', dest='pattern', action='store_const', - const=r'tests/.*/tests_*\.py', - default=r'tests/.*/tests_*\.py', + const=r'^tests\/(?:[a-zA-Z0-9_]+\/)*tests[a-zA-Z0-9_]*\.py$', + default=r'^tests\/(?:[a-zA-Z0-9_]+\/)*tests[a-zA-Z0-9_]*\.py$', help='(the default) ensure tests match %(const)s', ) args = parser.parse_args(argv) From 68581c54e54d41b538b40c4ee42eb8f721abfe72 Mon Sep 17 00:00:00 2001 From: Uros <115799310+KatanicU@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:43:32 +0200 Subject: [PATCH 06/11] new regex --- pre_commit_hooks/tests_should_end_in_test.py | 21 +++++++------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/pre_commit_hooks/tests_should_end_in_test.py b/pre_commit_hooks/tests_should_end_in_test.py index 326e2732..c32610c7 100644 --- a/pre_commit_hooks/tests_should_end_in_test.py +++ b/pre_commit_hooks/tests_should_end_in_test.py @@ -1,11 +1,8 @@ from __future__ import annotations - import argparse import os.path import re from collections.abc import Sequence - - def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*') @@ -14,27 +11,23 @@ def main(argv: Sequence[str] | None = None) -> int: '--pytest', dest='pattern', action='store_const', - const=r'^tests\/(?:[a-zA-Z0-9_]+\/)*tests[a-zA-Z0-9_]*\.py$', - default=r'^tests\/(?:[a-zA-Z0-9_]+\/)*tests[a-zA-Z0-9_]*\.py$', + const=r'^tests\/(?:[a-zA-Z0-9_]+\/)*tests_[a-zA-Z0-9_]*\.py$', + default=r'^tests\/(?:[a-zA-Z0-9_]+\/)*tests_[a-zA-Z0-9_]*\.py$', help='(the default) ensure tests match %(const)s', ) args = parser.parse_args(argv) - retcode = 0 reg = re.compile(args.pattern) for filename in args.filenames: base = os.path.basename(filename) print(base) - if ( - not reg.fullmatch(base) and - not base == '.*/__init__.py' and - not base == '.*/conftest.py' and - not base == '.*/models.py' - - ): + # Check for files that should be ignored + if base in ('__init__.py', 'conftest.py', 'models.py'): + continue + # Raise an exception if filename doesn't start with 'tests_' and doesn't match the pattern + if not reg.fullmatch(filename): retcode = 1 print(f'{filename} does not match pattern "{args.pattern}"') - return retcode From 17cb1642cc25c26278b6d402b6d3cf3e485f4c30 Mon Sep 17 00:00:00 2001 From: Uros <115799310+KatanicU@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:47:15 +0200 Subject: [PATCH 07/11] new path --- pre_commit_hooks/tests_should_end_in_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pre_commit_hooks/tests_should_end_in_test.py b/pre_commit_hooks/tests_should_end_in_test.py index c32610c7..1f219384 100644 --- a/pre_commit_hooks/tests_should_end_in_test.py +++ b/pre_commit_hooks/tests_should_end_in_test.py @@ -20,7 +20,6 @@ def main(argv: Sequence[str] | None = None) -> int: reg = re.compile(args.pattern) for filename in args.filenames: base = os.path.basename(filename) - print(base) # Check for files that should be ignored if base in ('__init__.py', 'conftest.py', 'models.py'): continue From 5e4fe2c16414c900239e80b37f45fd169f225768 Mon Sep 17 00:00:00 2001 From: Uros <115799310+KatanicU@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:26:38 +0100 Subject: [PATCH 08/11] Add new logic --- .../forbid_articles_in_test_filenames.py | 48 ++++++++++++++++ .../test_forbid_articles_in_test_filenames.py | 56 +++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100755 pre_commit_hooks/forbid_articles_in_test_filenames.py create mode 100644 tests/test_forbid_articles_in_test_filenames.py diff --git a/pre_commit_hooks/forbid_articles_in_test_filenames.py b/pre_commit_hooks/forbid_articles_in_test_filenames.py new file mode 100755 index 00000000..1a800bc8 --- /dev/null +++ b/pre_commit_hooks/forbid_articles_in_test_filenames.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 + +import subprocess +import sys +from pathlib import Path + +FORBIDDEN = {"a", "an", "the"} + + +def git_ls_python_files(): + result = subprocess.run( + ["git", "ls-files", "*.py"], + capture_output=True, + text=True, + check=True, + ) + return [Path(p) for p in result.stdout.splitlines()] + + +def is_test_file(path: Path) -> bool: + name = path.name + return ( + name.startswith("test_") + or name.startswith("tests_") + or name.endswith("_test.py") + ) + + +def has_forbidden_article(path: Path) -> bool: + parts = path.stem.split("_") + return any(part in FORBIDDEN for part in parts) + + +def main() -> int: + for path in git_ls_python_files(): + if not is_test_file(path): + continue + + if has_forbidden_article(path): + print("ERROR: Forbidden article in test filename:") + print(path) + return 1 + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/test_forbid_articles_in_test_filenames.py b/tests/test_forbid_articles_in_test_filenames.py new file mode 100644 index 00000000..95a0b9dd --- /dev/null +++ b/tests/test_forbid_articles_in_test_filenames.py @@ -0,0 +1,56 @@ +import subprocess +import sys +from pathlib import Path + + +HOOK = Path(__file__).parents[1] / "pre_commit_hooks" / "forbid_articles_in_test_filenames.py" + + +def run_hook(repo_path: Path): + """Run the hook in a temporary git repo and return (exit_code, stdout).""" + result = subprocess.run( + [sys.executable, str(HOOK)], + cwd=repo_path, + capture_output=True, + text=True, + ) + return result.returncode, result.stdout.strip() + + +def init_git_repo(tmp_path: Path): + subprocess.run(["git", "init"], cwd=tmp_path, check=True) + subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=tmp_path, check=True) + subprocess.run(["git", "config", "user.name", "Test User"], cwd=tmp_path, check=True) + + +def git_add_all(tmp_path: Path): + subprocess.run(["git", "add", "."], cwd=tmp_path, check=True) + + +def test_fails_on_forbidden_article_in_test_filename(tmp_path: Path): + init_git_repo(tmp_path) + + bad_test = tmp_path / "tests_create_an_address.py" + bad_test.write_text("def test_something(): pass\n") + + git_add_all(tmp_path) + + code, output = run_hook(tmp_path) + + assert code == 1 + assert "ERROR: Forbidden article in test filename:" in output + assert "tests_create_an_address.py" in output + + +def test_passes_on_valid_test_filename(tmp_path: Path): + init_git_repo(tmp_path) + + good_test = tmp_path / "tests_create_address.py" + good_test.write_text("def test_something(): pass\n") + + git_add_all(tmp_path) + + code, output = run_hook(tmp_path) + + assert code == 0 + assert output == "" \ No newline at end of file From 46646b779e9602fd777fbee70789539f6142c318 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 17 Dec 2025 10:27:14 +0000 Subject: [PATCH 09/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../forbid_articles_in_test_filenames.py | 17 ++++++------ pre_commit_hooks/tests_should_end_in_test.py | 3 +++ .../test_forbid_articles_in_test_filenames.py | 26 ++++++++++--------- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/pre_commit_hooks/forbid_articles_in_test_filenames.py b/pre_commit_hooks/forbid_articles_in_test_filenames.py index 1a800bc8..0c8fd609 100755 --- a/pre_commit_hooks/forbid_articles_in_test_filenames.py +++ b/pre_commit_hooks/forbid_articles_in_test_filenames.py @@ -1,15 +1,16 @@ #!/usr/bin/env python3 +from __future__ import annotations import subprocess import sys from pathlib import Path -FORBIDDEN = {"a", "an", "the"} +FORBIDDEN = {'a', 'an', 'the'} def git_ls_python_files(): result = subprocess.run( - ["git", "ls-files", "*.py"], + ['git', 'ls-files', '*.py'], capture_output=True, text=True, check=True, @@ -20,14 +21,14 @@ def git_ls_python_files(): def is_test_file(path: Path) -> bool: name = path.name return ( - name.startswith("test_") - or name.startswith("tests_") - or name.endswith("_test.py") + name.startswith('test_') or + name.startswith('tests_') or + name.endswith('_test.py') ) def has_forbidden_article(path: Path) -> bool: - parts = path.stem.split("_") + parts = path.stem.split('_') return any(part in FORBIDDEN for part in parts) @@ -37,12 +38,12 @@ def main() -> int: continue if has_forbidden_article(path): - print("ERROR: Forbidden article in test filename:") + print('ERROR: Forbidden article in test filename:') print(path) return 1 return 0 -if __name__ == "__main__": +if __name__ == '__main__': sys.exit(main()) diff --git a/pre_commit_hooks/tests_should_end_in_test.py b/pre_commit_hooks/tests_should_end_in_test.py index 1f219384..9f3733f6 100644 --- a/pre_commit_hooks/tests_should_end_in_test.py +++ b/pre_commit_hooks/tests_should_end_in_test.py @@ -1,8 +1,11 @@ from __future__ import annotations + import argparse import os.path import re from collections.abc import Sequence + + def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument('filenames', nargs='*') diff --git a/tests/test_forbid_articles_in_test_filenames.py b/tests/test_forbid_articles_in_test_filenames.py index 95a0b9dd..fc4f02f3 100644 --- a/tests/test_forbid_articles_in_test_filenames.py +++ b/tests/test_forbid_articles_in_test_filenames.py @@ -1,9 +1,11 @@ +from __future__ import annotations + import subprocess import sys from pathlib import Path -HOOK = Path(__file__).parents[1] / "pre_commit_hooks" / "forbid_articles_in_test_filenames.py" +HOOK = Path(__file__).parents[1] / 'pre_commit_hooks' / 'forbid_articles_in_test_filenames.py' def run_hook(repo_path: Path): @@ -18,39 +20,39 @@ def run_hook(repo_path: Path): def init_git_repo(tmp_path: Path): - subprocess.run(["git", "init"], cwd=tmp_path, check=True) - subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=tmp_path, check=True) - subprocess.run(["git", "config", "user.name", "Test User"], cwd=tmp_path, check=True) + subprocess.run(['git', 'init'], cwd=tmp_path, check=True) + subprocess.run(['git', 'config', 'user.email', 'test@example.com'], cwd=tmp_path, check=True) + subprocess.run(['git', 'config', 'user.name', 'Test User'], cwd=tmp_path, check=True) def git_add_all(tmp_path: Path): - subprocess.run(["git", "add", "."], cwd=tmp_path, check=True) + subprocess.run(['git', 'add', '.'], cwd=tmp_path, check=True) def test_fails_on_forbidden_article_in_test_filename(tmp_path: Path): init_git_repo(tmp_path) - bad_test = tmp_path / "tests_create_an_address.py" - bad_test.write_text("def test_something(): pass\n") + bad_test = tmp_path / 'tests_create_an_address.py' + bad_test.write_text('def test_something(): pass\n') git_add_all(tmp_path) code, output = run_hook(tmp_path) assert code == 1 - assert "ERROR: Forbidden article in test filename:" in output - assert "tests_create_an_address.py" in output + assert 'ERROR: Forbidden article in test filename:' in output + assert 'tests_create_an_address.py' in output def test_passes_on_valid_test_filename(tmp_path: Path): init_git_repo(tmp_path) - good_test = tmp_path / "tests_create_address.py" - good_test.write_text("def test_something(): pass\n") + good_test = tmp_path / 'tests_create_address.py' + good_test.write_text('def test_something(): pass\n') git_add_all(tmp_path) code, output = run_hook(tmp_path) assert code == 0 - assert output == "" \ No newline at end of file + assert output == '' From 5ecef685250742ce877b49ba3c4760ad2a55a508 Mon Sep 17 00:00:00 2001 From: Uros <115799310+KatanicU@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:28:49 +0100 Subject: [PATCH 10/11] Add new logic --- ...est_filenames.py => forbid_articles_in_test_filenames_test.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{test_forbid_articles_in_test_filenames.py => forbid_articles_in_test_filenames_test.py} (100%) diff --git a/tests/test_forbid_articles_in_test_filenames.py b/tests/forbid_articles_in_test_filenames_test.py similarity index 100% rename from tests/test_forbid_articles_in_test_filenames.py rename to tests/forbid_articles_in_test_filenames_test.py From bd1709160b8711c0b625d75691794c6af769db07 Mon Sep 17 00:00:00 2001 From: Uros <115799310+KatanicU@users.noreply.github.com> Date: Wed, 17 Dec 2025 11:33:18 +0100 Subject: [PATCH 11/11] Remove test's --- .../forbid_articles_in_test_filenames_test.py | 58 ------------------- 1 file changed, 58 deletions(-) delete mode 100644 tests/forbid_articles_in_test_filenames_test.py diff --git a/tests/forbid_articles_in_test_filenames_test.py b/tests/forbid_articles_in_test_filenames_test.py deleted file mode 100644 index fc4f02f3..00000000 --- a/tests/forbid_articles_in_test_filenames_test.py +++ /dev/null @@ -1,58 +0,0 @@ -from __future__ import annotations - -import subprocess -import sys -from pathlib import Path - - -HOOK = Path(__file__).parents[1] / 'pre_commit_hooks' / 'forbid_articles_in_test_filenames.py' - - -def run_hook(repo_path: Path): - """Run the hook in a temporary git repo and return (exit_code, stdout).""" - result = subprocess.run( - [sys.executable, str(HOOK)], - cwd=repo_path, - capture_output=True, - text=True, - ) - return result.returncode, result.stdout.strip() - - -def init_git_repo(tmp_path: Path): - subprocess.run(['git', 'init'], cwd=tmp_path, check=True) - subprocess.run(['git', 'config', 'user.email', 'test@example.com'], cwd=tmp_path, check=True) - subprocess.run(['git', 'config', 'user.name', 'Test User'], cwd=tmp_path, check=True) - - -def git_add_all(tmp_path: Path): - subprocess.run(['git', 'add', '.'], cwd=tmp_path, check=True) - - -def test_fails_on_forbidden_article_in_test_filename(tmp_path: Path): - init_git_repo(tmp_path) - - bad_test = tmp_path / 'tests_create_an_address.py' - bad_test.write_text('def test_something(): pass\n') - - git_add_all(tmp_path) - - code, output = run_hook(tmp_path) - - assert code == 1 - assert 'ERROR: Forbidden article in test filename:' in output - assert 'tests_create_an_address.py' in output - - -def test_passes_on_valid_test_filename(tmp_path: Path): - init_git_repo(tmp_path) - - good_test = tmp_path / 'tests_create_address.py' - good_test.write_text('def test_something(): pass\n') - - git_add_all(tmp_path) - - code, output = run_hook(tmp_path) - - assert code == 0 - assert output == ''