-
-
Notifications
You must be signed in to change notification settings - Fork 550
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore: Backpush to main changes to skeleton of python hooks #741
base: master
Are you sure you want to change the base?
Conversation
modifications incoming
mock_subcommand_modules, | ||
) | ||
|
||
from pre_commit_terraform._cli_subcommands import ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not add imports within functions. It's a really bad idea that makes runtime less predictable / more difficult to interpret. Also, it's already imported at the top, it's not going to load the file for the second time, it's already cached. Don't disable the linters that report that this is dumb.
import os | ||
from os.path import join |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Double import. Don't. Just don't. Enable the linters and fix the problems instead of showing this to other humans.
# ? | ||
# ? get_unique_dirs | ||
# ? | ||
def test_get_unique_dirs_empty(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docstring?
def test_parse_env_vars_empty(): | ||
env_var_strs = [] | ||
result = parse_env_vars(env_var_strs) | ||
assert result == {} | ||
|
||
|
||
def test_parse_env_vars_single(): | ||
env_var_strs = ['VAR1=value1'] | ||
result = parse_env_vars(env_var_strs) | ||
assert result == {'VAR1': 'value1'} | ||
|
||
|
||
def test_parse_env_vars_multiple(): | ||
env_var_strs = ['VAR1=value1', 'VAR2=value2'] | ||
result = parse_env_vars(env_var_strs) | ||
assert result == {'VAR1': 'value1', 'VAR2': 'value2'} | ||
|
||
|
||
def test_parse_env_vars_with_quotes(): | ||
env_var_strs = ['VAR1="value1"', 'VAR2="value2"'] | ||
result = parse_env_vars(env_var_strs) | ||
assert result == {'VAR1': 'value1', 'VAR2': 'value2'} | ||
|
||
|
||
def test_parse_env_vars_with_equal_sign_in_value(): | ||
env_var_strs = ['VAR1=value=1', 'VAR2=value=2'] | ||
result = parse_env_vars(env_var_strs) | ||
assert result == {'VAR1': 'value=1', 'VAR2': 'value=2'} | ||
|
||
|
||
def test_parse_env_vars_with_empty_value(): | ||
env_var_strs = ['VAR1=', 'VAR2='] | ||
result = parse_env_vars(env_var_strs) | ||
assert result == {'VAR1': '', 'VAR2': ''} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parametrize
def test_expand_env_vars_no_vars(): | ||
args = ['arg1', 'arg2'] | ||
env_vars = {} | ||
result = expand_env_vars(args, env_vars) | ||
assert result == ['arg1', 'arg2'] | ||
|
||
|
||
def test_expand_env_vars_single_var(): | ||
args = ['arg1', '${VAR1}', 'arg3'] | ||
env_vars = {'VAR1': 'value1'} | ||
result = expand_env_vars(args, env_vars) | ||
assert result == ['arg1', 'value1', 'arg3'] | ||
|
||
|
||
def test_expand_env_vars_multiple_vars(): | ||
args = ['${VAR1}', 'arg2', '${VAR2}'] | ||
env_vars = {'VAR1': 'value1', 'VAR2': 'value2'} | ||
result = expand_env_vars(args, env_vars) | ||
assert result == ['value1', 'arg2', 'value2'] | ||
|
||
|
||
def test_expand_env_vars_no_expansion(): | ||
args = ['arg1', 'arg2'] | ||
env_vars = {'VAR1': 'value1'} | ||
result = expand_env_vars(args, env_vars) | ||
assert result == ['arg1', 'arg2'] | ||
|
||
|
||
def test_expand_env_vars_partial_expansion(): | ||
args = ['arg1', '${VAR1}', '${VAR2}'] | ||
env_vars = {'VAR1': 'value1'} | ||
result = expand_env_vars(args, env_vars) | ||
assert result == ['arg1', 'value1', '${VAR2}'] | ||
|
||
|
||
def test_expand_env_vars_with_special_chars(): | ||
args = ['arg1', '${VAR_1}', 'arg3'] | ||
env_vars = {'VAR_1': 'value1'} | ||
result = expand_env_vars(args, env_vars) | ||
assert result == ['arg1', 'value1', 'arg3'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parametrize
def test_get_tf_binary_path_from_hook_config(): | ||
hook_config = ['--tf-path=/custom/path/to/terraform'] | ||
result = get_tf_binary_path(hook_config) | ||
assert result == '/custom/path/to/terraform' | ||
|
||
|
||
def test_get_tf_binary_path_from_pct_tfpath_env_var(mocker): | ||
hook_config = [] | ||
mocker.patch.dict(os.environ, {'PCT_TFPATH': '/env/path/to/terraform'}) | ||
result = get_tf_binary_path(hook_config) | ||
assert result == '/env/path/to/terraform' | ||
|
||
|
||
def test_get_tf_binary_path_from_terragrunt_tfpath_env_var(mocker): | ||
hook_config = [] | ||
mocker.patch.dict(os.environ, {'TERRAGRUNT_TFPATH': '/env/path/to/terragrunt'}) | ||
result = get_tf_binary_path(hook_config) | ||
assert result == '/env/path/to/terragrunt' | ||
|
||
|
||
def test_get_tf_binary_path_from_system_path_terraform(mocker): | ||
hook_config = [] | ||
mocker.patch('shutil.which', return_value='/usr/local/bin/terraform') | ||
result = get_tf_binary_path(hook_config) | ||
assert result == '/usr/local/bin/terraform' | ||
|
||
|
||
def test_get_tf_binary_path_from_system_path_tofu(mocker): | ||
hook_config = [] | ||
mocker.patch('shutil.which', side_effect=[None, '/usr/local/bin/tofu']) | ||
result = get_tf_binary_path(hook_config) | ||
assert result == '/usr/local/bin/tofu' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parametrize
|
||
def test_get_tf_binary_path_from_pct_tfpath_env_var(mocker): | ||
hook_config = [] | ||
mocker.patch.dict(os.environ, {'PCT_TFPATH': '/env/path/to/terraform'}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use monkeypatch.setenv()
with pytest.raises( | ||
BinaryNotFoundError, | ||
match='Neither Terraform nor OpenTofu binary could be found. Please either set the "--tf-path"' | ||
+ ' hook configuration argument, or set the "PCT_TFPATH" environment variable, or set the' | ||
+ ' "TERRAGRUNT_TFPATH" environment variable, or install Terraform or OpenTofu globally.', | ||
): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with pytest.raises( | |
BinaryNotFoundError, | |
match='Neither Terraform nor OpenTofu binary could be found. Please either set the "--tf-path"' | |
+ ' hook configuration argument, or set the "PCT_TFPATH" environment variable, or set the' | |
+ ' "TERRAGRUNT_TFPATH" environment variable, or install Terraform or OpenTofu globally.', | |
): | |
error_msg = ( | |
r'^Neither Terraform nor OpenTofu binary could be found. Please either set the "--tf-path" ' | |
'hook configuration argument, or set the "PCT_TFPATH" environment variable, or set the ' | |
'"TERRAGRUNT_TFPATH" environment variable, or install Terraform or OpenTofu globally\.$' | |
) | |
with pytest.raises(BinaryNotFoundError, match=error_msg): |
tests/pytest/test__errors.py
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This module is rather pointless. Delete it.
def sample_function(): | ||
pass | ||
|
||
scope = globals() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, this is nasty! Messing up the entire process state for all the test functions, eh? Don't do this. At least copy the thing instead of side-effecting everything...
scope = globals() | |
scope = globals().copy() |
scope = globals() | ||
scope['sample_function'] = sample_function |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But really.. Can't you just not go the unnecessarily complicated route? KISS.
scope = globals() | |
scope['sample_function'] = sample_function | |
scope = {'sample_function': sample_function} |
# ? | ||
# ? is_function_defined | ||
# ? | ||
def test_is_function_defined_existing_function(): | ||
def sample_function(): | ||
pass | ||
|
||
scope = globals() | ||
scope['sample_function'] = sample_function | ||
|
||
assert is_function_defined('sample_function', scope) is True | ||
|
||
|
||
def test_is_function_defined_non_existing_function(): | ||
scope = globals() | ||
|
||
assert is_function_defined('non_existing_function', scope) is False | ||
|
||
|
||
def test_is_function_defined_non_callable(): | ||
non_callable = 'I am not a function' | ||
scope = globals() | ||
scope['non_callable'] = non_callable | ||
|
||
assert is_function_defined('non_callable', scope) is False | ||
|
||
|
||
def test_is_function_defined_callable_object(): | ||
class CallableObject: | ||
def __call__(self): | ||
pass | ||
|
||
callable_object = CallableObject() | ||
scope = globals() | ||
scope['callable_object'] = callable_object | ||
|
||
assert is_function_defined('callable_object', scope) is True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Parametrize
def test_is_hook_run_on_whole_repo(mocker, mock_git_ls_files, mock_hooks_config): | ||
# Mock the return value of git ls-files | ||
mocker.patch('subprocess.check_output', return_value='\n'.join(mock_git_ls_files)) | ||
# Mock the return value of reading the .pre-commit-hooks.yaml file | ||
mocker.patch('builtins.open', mocker.mock_open(read_data=yaml.dump(mock_hooks_config))) | ||
# Mock the Path object to return a specific path | ||
mock_path = mocker.patch('pathlib.Path.resolve') | ||
mock_path.return_value.parents.__getitem__.return_value = Path('/mocked/path') | ||
# Mock the read_text method of Path to return the hooks config | ||
mocker.patch('pathlib.Path.read_text', return_value=yaml.dump(mock_hooks_config)) | ||
|
||
# Test case where files match the included pattern and do not match the excluded pattern | ||
files = [ | ||
'environment/prd/backends.tf', | ||
'environment/prd/data.tf', | ||
'environment/prd/main.tf', | ||
'environment/prd/outputs.tf', | ||
'environment/prd/providers.tf', | ||
'environment/prd/variables.tf', | ||
'environment/prd/versions.tf', | ||
'environment/qa/backends.tf', | ||
] | ||
assert is_hook_run_on_whole_repo('example_hook_id', files) is True | ||
|
||
# Test case where files do not match the included pattern | ||
files = ['environment/prd/README.md'] | ||
assert is_hook_run_on_whole_repo('example_hook_id', files) is False | ||
|
||
# Test case where files match the excluded pattern | ||
files = ['environment/prd/.terraform/config.tf'] | ||
assert is_hook_run_on_whole_repo('example_hook_id', files) is False | ||
|
||
# Test case where hook_id is not found | ||
with pytest.raises( | ||
ValueError, | ||
match='Hook ID "non_existing_hook_id" not found in .pre-commit-hooks.yaml', | ||
): | ||
is_hook_run_on_whole_repo('non_existing_hook_id', files) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually multiple tests stuffed into one. They should be split, perhaps parametrized.
Of mocking, you can use tmp_path
and generate an actual example Git repo and just run the test against that. With mocks you're mostly testing your mocks and not logic. Especially, with this many in place.
tests/pytest/test__structs.py
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't very useful either. Delete.
assert isinstance(MockSubcommandModule(), CLISubcommandModuleProtocol) | ||
|
||
class InvalidSubcommandModule: | ||
HOOK_ID = 'invalid_hook' | ||
|
||
def populate_argument_parser(self, subcommand_parser: ArgumentParser) -> None: | ||
pass | ||
|
||
assert not isinstance(InvalidSubcommandModule(), CLISubcommandModuleProtocol) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like two separate tests, they shouldn't be one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, this is merely checking that CPython stdlib works. I don't see it being useful and would just remove it.
@MaxymVlasov look into how the tests are structured in the test infra PR and follow that. Avoid mocks where possible. When we get to have a call, I'll show you how to inspect coverage locally to judge if you even need a test. |
args: [--force-single-line, --profile=black] | ||
exclude: | | ||
(?x) | ||
# Uses incorrect indentation in imports in places where it shouldn't |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, this ignore is irrelevant because your code defies best practices. It should be fixed instead of skipping the checks.
❌ 18 Tests Failed:
View the top 3 failed tests by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
This PR has been automatically marked as stale because it has been open 30 days with no activity. Remove stale label or comment or this PR will be closed in 10 days |
WalkthroughThe changes update configuration files, source code, and tests for the pre-commit Terraform tool. Configuration modifications include an upgraded Python version in GitHub Actions and new linting and formatting hooks. Source code updates adjust import paths, enhance CLI argument parsing, add common utilities, improve logging and error handling, and update subcommand protocols. A new file introduces reusable functions for environment variable processing and binary resolution. Additionally, tests have been extended to cover the new functionalities and modifications across multiple modules. Changes
Sequence Diagram(s)sequenceDiagram
participant U as User
participant M as __main__.py
participant P as _cli_parsing.py
participant C as _cli.py
participant E as _errors.py
U->>M: Execute CLI command
M->>P: Initialize parser and parse arguments
P-->>M: Return parsed args
M->>C: Call invoke_cli_app(parsed_args)
alt Successful execution
C-->>M: Return OK code
else Exception raised
C->>E: Handle exception and format error message
E-->>C: Return error code
end
M->>U: Exit with return code
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 13
🔭 Outside diff range comments (1)
tests/pytest/test__run_on_whole_repo.py (1)
1-109
: Rename file to match test pattern and refactor tests.
The file name should be renamed to match the pattern '.*_test.py'.
The tests should be parametrized for better maintainability.
The global state modification should be avoided.
Rename the file:
-tests/pytest/test__run_on_whole_repo.py +tests/pytest/run_on_whole_repo_test.py
- Parametrize the tests:
@pytest.mark.parametrize( ('function_name', 'expected_result'), [ ('sample_function', True), ('non_existing_function', False), ('non_callable', False), ('callable_object', True), ], ) def test_is_function_defined(function_name: str, expected_result: bool) -> None: scope = {'sample_function': lambda: None, 'non_callable': 'not a function', 'callable_object': type('CallableObject', (), {'__call__': lambda x: None})()} assert is_function_defined(function_name, scope) is expected_result
- Use a temporary directory for Git repository tests:
def test_is_hook_run_on_whole_repo(tmp_path: Path) -> None: # Initialize Git repo repo_dir = tmp_path / "repo" repo_dir.mkdir() subprocess.run(['git', 'init'], cwd=repo_dir, check=True) # Create test files (repo_dir / "test.tf").write_text("") subprocess.run(['git', 'add', '.'], cwd=repo_dir, check=True) # Test hook execution assert is_hook_run_on_whole_repo('example_hook_id', ['test.tf']) is True🧰 Tools
🪛 GitHub Actions: Common issues check
[error] 1-1: tests/pytest/test__run_on_whole_repo.py does not match pattern '.*_test.py'
♻️ Duplicate comments (2)
tests/pytest/test__types.py (1)
18-36
: 🛠️ Refactor suggestionSplit the test into positive and negative test cases.
Following the previous review feedback, this should be split into two separate test functions for better clarity and maintainability.
Apply this diff to split the tests:
-def test_cli_subcommand_module_protocol(): +def test_cli_subcommand_module_protocol_valid() -> None: class MockSubcommandModule: HOOK_ID = 'mock_hook' def populate_argument_parser(self, subcommand_parser: ArgumentParser) -> None: pass def invoke_cli_app(self, parsed_cli_args: Namespace) -> ReturnCodeType: return ReturnCode.OK assert isinstance(MockSubcommandModule(), CLISubcommandModuleProtocol) +def test_cli_subcommand_module_protocol_invalid() -> None: class InvalidSubcommandModule: HOOK_ID = 'invalid_hook' def populate_argument_parser(self, subcommand_parser: ArgumentParser) -> None: pass assert not isinstance(InvalidSubcommandModule(), CLISubcommandModuleProtocol)src/pre_commit_terraform/_cli_parsing.py (1)
35-41
: 🛠️ Refactor suggestionUpdate help text to be more abstract.
The help text should be tool-agnostic as it could be used with different tools like
tofu
,terraform-alpha
, etc.Apply this diff to make the help text more abstract:
- help='Arguments for `tf init` command', + help="Arguments to tool's `init` sub-command",
🧹 Nitpick comments (6)
src/pre_commit_terraform/_types.py (1)
19-20
: Update docstring to use correct terminology.The constant represents a subcommand identifier, not just a CLI.
HOOK_ID: str - """This constant contains a CLI.""" + """This constant contains the subcommand identifier."""src/pre_commit_terraform/_logger.py (1)
52-88
: Add type hints to environment variable access.The environment variable access could benefit from type hints to make the code more maintainable.
Apply this diff to add type hints:
+from typing import Literal, Optional +LogLevel = Literal['error', 'warn', 'warning', 'info', 'debug'] def setup_logging() -> None: log_level = { 'error': logging.ERROR, 'warn': logging.WARNING, 'warning': logging.WARNING, 'info': logging.INFO, 'debug': logging.DEBUG, - }[os.environ.get('PCT_LOG', 'warning').lower()] + }[os.environ.get('PCT_LOG', 'warning').lower()] # type: ignore[index]tests/pytest/terraform_docs_replace_test.py (1)
85-85
: Improve readability by using a more explicit implementation.The list comprehension is concise but less readable. Consider using a more explicit implementation:
- executed_commands = [cmd for ((cmd,), _shell) in check_call_mock.call_args_list] + executed_commands = [] + for (cmd,), shell in check_call_mock.call_args_list: + executed_commands.append(cmd).pre-commit-hooks.yaml (1)
45-169
: Further removal of Terraform-related hooks.
Hooks including 'terraform_validate', 'terraform_providers_lock', 'terraform_tflint', the various terragrunt hooks, 'terraform_tfsec', 'terraform_trivy', 'checkov', 'terraform_checkov', 'terraform_wrapper_module_for_each', 'terrascan', and 'tfupdate' have been removed. This change focuses the hook configuration on core Python-based functionality. Please verify that any dependent processes are updated to reflect these removals..pre-commit-config.yaml (2)
134-143
: Add Ruff and Ruff-format hooks for Python linting.
The new hooks from 'astral-sh/ruff-pre-commit' provide fast linting and formatting fixes. Note that the ruff-format hook passes arguments with spaces around '=' (e.g. "format.quote-style = 'single'"); verify that these arguments are parsed correctly. Consider revising the format to “--config=format.quote-style=single” if needed.
196-219
: Integrate wemake-python-styleguide hook with custom thresholds and exclusion.
The new hook from 'wemake-services/wemake-python-styleguide' is configured with increased limits for local variables, returns, and imports. It uses a multiline regex (with (?x) mode) to exclude specific test files and a deprecated hook file. While the extended regex allows inline comments, consider whether a single-line version might improve readability in future revisions.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (27)
.github/workflows/pre-commit.yaml
(1 hunks).pre-commit-config.yaml
(2 hunks).pre-commit-hooks.yaml
(1 hunks).vscode/settings.json
(1 hunks)hatch.toml
(2 hunks)pyproject.toml
(1 hunks)src/pre_commit_terraform/__main__.py
(1 hunks)src/pre_commit_terraform/_cli.py
(2 hunks)src/pre_commit_terraform/_cli_parsing.py
(2 hunks)src/pre_commit_terraform/_cli_subcommands.py
(1 hunks)src/pre_commit_terraform/_common.py
(1 hunks)src/pre_commit_terraform/_errors.py
(1 hunks)src/pre_commit_terraform/_logger.py
(1 hunks)src/pre_commit_terraform/_run_on_whole_repo.py
(1 hunks)src/pre_commit_terraform/_types.py
(1 hunks)src/pre_commit_terraform/terraform_docs_replace.py
(1 hunks)tests/pytest/_cli_test.py
(3 hunks)tests/pytest/terraform_docs_replace_test.py
(6 hunks)tests/pytest/test___main__.py
(1 hunks)tests/pytest/test__cli.py
(1 hunks)tests/pytest/test__cli_parsing.py
(1 hunks)tests/pytest/test__cli_subcommands.py
(1 hunks)tests/pytest/test__common.py
(1 hunks)tests/pytest/test__errors.py
(1 hunks)tests/pytest/test__run_on_whole_repo.py
(1 hunks)tests/pytest/test__structs.py
(1 hunks)tests/pytest/test__types.py
(1 hunks)
✅ Files skipped from review due to trivial changes (3)
- src/pre_commit_terraform/_errors.py
- tests/pytest/_cli_test.py
- src/pre_commit_terraform/_cli.py
🧰 Additional context used
🪛 GitHub Actions: Common issues check
tests/pytest/test__errors.py
[error] 8-8: Function is missing a return type annotation.
[error] 13-13: Function is missing a return type annotation.
[error] 18-18: Function is missing a return type annotation.
tests/pytest/test__types.py
[error] 9-9: Function is missing a return type annotation.
tests/pytest/test___main__.py
[error] 1-1: tests/pytest/test___main__.py does not match pattern '.*_test.py'
src/pre_commit_terraform/_types.py
[error] 15-15: Name 'runtime_checkable' is not defined.
tests/pytest/test__structs.py
[error] 6-6: Function is missing a return type annotation.
[error] 11-11: Function is missing a return type annotation.
[error] 16-16: Function is missing a return type annotation.
tests/pytest/test__cli_subcommands.py
[error] 1-1: tests/pytest/test__cli_subcommands.py does not match pattern '.*_test.py'
tests/pytest/test__run_on_whole_repo.py
[error] 1-1: tests/pytest/test__run_on_whole_repo.py does not match pattern '.*_test.py'
src/pre_commit_terraform/_run_on_whole_repo.py
[error] 8-8: Library stubs not installed for 'yaml'.
[error] 13-13: Missing type parameters for generic type 'dict'.
tests/pytest/test__cli.py
[error] 10-10: Function is missing a type annotation.
[error] 25-25: Function is missing a type annotation.
[error] 40-40: Function is missing a type annotation.
[error] 55-55: Function is missing a type annotation.
[error] 70-70: Function is missing a type annotation.
tests/pytest/test__cli_parsing.py
[error] 9-9: Function is missing a return type annotation.
[error] 13-13: Function is missing a type annotation.
[error] 27-27: Function is missing a type annotation.
[error] 39-39: Function is missing a type annotation.
[error] 75-75: Function is missing a type annotation.
[error] 101-101: Function is missing a type annotation.
[error] 116-116: Function is missing a type annotation.
[error] 152-152: Function is missing a type annotation.
[error] 178-178: Function is missing a type annotation.
[error] 193-193: Function is missing a type annotation.
tests/pytest/test__common.py
[error] 17-17: Function is missing a return type annotation.
[error] 23-23: Function is missing a return type annotation.
[error] 29-29: Function is missing a return type annotation.
[error] 35-35: Function is missing a return type annotation.
[error] 41-41: Function is missing a return type annotation.
[error] 148-148: Function is missing a return type annotation.
[error] 154-154: Function is missing a return type annotation.
[error] 160-160: Function is missing a return type annotation.
[error] 166-166: Function is missing a return type annotation.
[error] 172-172: Function is missing a return type annotation.
[error] 178-178: Function is missing a return type annotation.
[error] 187-187: Function is missing a return type annotation.
[error] 194-194: Function is missing a return type annotation.
[error] 201-201: Function is missing a return type annotation.
[error] 208-208: Function is missing a return type annotation.
[error] 215-215: Function is missing a return type annotation.
[error] 222-222: Function is missing a return type annotation.
[error] 232-232: Function is missing a return type annotation.
[error] 238-238: Function is missing a return type annotation.
[error] 252-252: Function is missing a return type annotation.
[error] 259-259: Function is missing a return type annotation.
[error] 266-266: Function is missing a return type annotation.
🪛 GitHub Actions: 🔀 PR 741 triggered by: pull_request of refs/pull/741/merge branch (workflow run ID: 12675889873; number: 33; attempt: 1)
tests/pytest/test__errors.py
[error] 8-8: Function is missing a return type annotation.
[error] 13-13: Function is missing a return type annotation.
[error] 18-18: Function is missing a return type annotation.
tests/pytest/test__types.py
[error] 5-5: NameError: name 'runtime_checkable' is not defined
tests/pytest/terraform_docs_replace_test.py
[error] 11-11: NameError: name 'runtime_checkable' is not defined
src/pre_commit_terraform/_types.py
[error] 15-15: Undefined variable 'runtime_checkable'.
tests/pytest/test__structs.py
[error] 6-6: Function is missing a return type annotation.
tests/pytest/test__cli_subcommands.py
[error] 1-1: NameError: name 'runtime_checkable' is not defined
src/pre_commit_terraform/_run_on_whole_repo.py
[error] 8-8: Library stubs not installed for 'yaml'.
[error] 13-13: Missing type parameters for generic type 'dict'.
[error] 77-77: Line too long (101/100).
tests/pytest/test__cli.py
[error] 3-3: NameError: name 'runtime_checkable' is not defined
tests/pytest/test__cli_parsing.py
[error] 5-5: NameError: name 'runtime_checkable' is not defined
tests/pytest/test__common.py
[error] 17-17: Function is missing a return type annotation.
[error] 18-18: Need type annotation for 'files'.
[error] 23-23: Function is missing a return type annotation.
[error] 29-29: Function is missing a return type annotation.
[error] 35-35: Function is missing a return type annotation.
[error] 41-41: Function is missing a return type annotation.
[error] 148-148: Function is missing a return type annotation.
[error] 149-149: Need type annotation for 'env_var_strs'.
[error] 154-154: Function is missing a return type annotation.
[error] 160-160: Function is missing a return type annotation.
[error] 166-166: Function is missing a return type annotation.
[error] 172-172: Function is missing a return type annotation.
[error] 178-178: Function is missing a return type annotation.
[error] 187-187: Function is missing a return type annotation.
[error] 194-194: Function is missing a return type annotation.
[error] 201-201: Function is missing a return type annotation.
[error] 208-208: Function is missing a return type annotation.
[error] 215-215: Function is missing a return type annotation.
[error] 222-222: Function is missing a return type annotation.
[error] 232-232: Function is missing a return type annotation.
[error] 238-238: Function is missing a type annotation.
[error] 245-245: Function is missing a type annotation.
[error] 252-252: Function is missing a type annotation.
[error] 259-259: Function is missing a type annotation.
[error] 266-266: Function is missing a type annotation.
🪛 GitHub Actions: 🌱 Commit 1bdb83e triggered by: push of refs/heads/rewrite_hooks_on_python_back_to_main branch (workflow run ID: 12675889103; number: 32; attempt: 1)
tests/pytest/test__errors.py
[error] 8-8: Function is missing a return type annotation.
[error] 13-13: Function is missing a return type annotation.
[error] 18-18: Function is missing a return type annotation.
tests/pytest/test__types.py
[error] 5-5: NameError: name 'runtime_checkable' is not defined
tests/pytest/terraform_docs_replace_test.py
[error] 11-11: NameError: name 'runtime_checkable' is not defined
src/pre_commit_terraform/_types.py
[error] 15-15: Undefined variable 'runtime_checkable'.
tests/pytest/test__structs.py
[error] 6-6: Function is missing a return type annotation.
[error] 11-11: Function is missing a return type annotation.
[error] 16-16: Function is missing a return type annotation.
tests/pytest/test__cli_subcommands.py
[error] 1-1: NameError: name 'runtime_checkable' is not defined
src/pre_commit_terraform/_run_on_whole_repo.py
[error] 8-8: Library stubs not installed for 'yaml'.
[error] 13-13: Missing type parameters for generic type 'dict'.
[error] 24-24: Expression type contains 'Any' (has type 'dict[Any, Any]').
[error] 25-25: Expression type contains 'Any' (has type 'dict[Any, Any]').
tests/pytest/test__cli.py
[error] 3-3: NameError: name 'runtime_checkable' is not defined
tests/pytest/test__cli_parsing.py
[error] 5-5: NameError: name 'runtime_checkable' is not defined
tests/pytest/test__common.py
[error] 17-17: Function is missing a return type annotation.
[error] 23-23: Function is missing a return type annotation.
[error] 29-29: Function is missing a return type annotation.
[error] 35-35: Function is missing a return type annotation.
[error] 41-41: Function is missing a return type annotation.
[error] 148-148: Function is missing a return type annotation.
[error] 154-154: Function is missing a return type annotation.
[error] 160-160: Function is missing a return type annotation.
[error] 166-166: Function is missing a return type annotation.
[error] 172-172: Function is missing a return type annotation.
[error] 178-178: Function is missing a return type annotation.
[error] 187-187: Function is missing a return type annotation.
[error] 194-194: Function is missing a return type annotation.
[error] 201-201: Function is missing a return type annotation.
[error] 208-208: Function is missing a return type annotation.
[error] 215-215: Function is missing a return type annotation.
[error] 222-222: Function is missing a return type annotation.
[error] 232-232: Function is missing a return type annotation.
[error] 238-238: Function is missing a return type annotation.
[error] 245-245: Function is missing a return type annotation.
[error] 252-252: Function is missing a return type annotation.
[error] 259-259: Function is missing a return type annotation.
[error] 266-266: Function is missing a return type annotation.
🪛 Ruff (0.8.2)
src/pre_commit_terraform/_types.py
15-15: Undefined name runtime_checkable
(F821)
src/pre_commit_terraform/terraform_docs_replace.py
65-65: No explicit stacklevel
keyword argument found
(B028)
82-82: Loop control variable directory
not used within loop body
Rename unused directory
to _directory
(B007)
🔇 Additional comments (36)
src/pre_commit_terraform/_common.py (6)
1-5
: Module docstring is concise and informative.
No concerns here; it clearly states its purpose.
17-35
: Consider handling improperly formatted inputs.
Splitting on "=" will fail if the string does not contain "=", or has unexpected whitespace. You may wish to strip whitespace or skip empty entries, as previously discussed.
38-49
: Efficient generator expression usage.
Using a generator expression inside set() to compute unique directories is a clean and memory-efficient approach.
51-75
: Double-check multi-expansion logic.
When an arg contains multiple placeholders or repeated environment variables, the loop may replace them successively. Ensure this behavior is desired in all scenarios.
78-120
: Review final exit code logic.
Currently, if multiple directories fail, only the last non-zero exit code is returned. Consider whether aggregating or otherwise combining failures might be preferable.
122-179
: Fallback strategy for locating Terraform/OpenTofu is sound.
The environment and PATH checks are well-ordered. No major issues identified.src/pre_commit_terraform/__main__.py (2)
3-4
: Separate imports improve clarity.
Explicitly importing argv and exit_with_return_code helps with readability.
6-6
: Absolute import avoids confusion.
Using an explicit package path helps prevent relative import conflicts.src/pre_commit_terraform/_cli_subcommands.py (1)
3-8
: Immutability and absolute imports.
Defining SUBCOMMAND_MODULES as a tuple and using absolute imports aligns with Python best practices.tests/pytest/test__structs.py (1)
6-19
: LGTM! Well-structured tests covering essential functionality.The tests effectively verify:
- Return code values
- Return code names
- Error handling for invalid values
🧰 Tools
🪛 GitHub Actions: Common issues check
[error] 6-6: Function is missing a return type annotation.
[error] 11-11: Function is missing a return type annotation.
[error] 16-16: Function is missing a return type annotation.
🪛 GitHub Actions: 🔀 PR 741 triggered by: pull_request of refs/pull/741/merge branch (workflow run ID: 12675889873; number: 33; attempt: 1)
[error] 6-6: Function is missing a return type annotation.
🪛 GitHub Actions: 🌱 Commit 1bdb83e triggered by: push of refs/heads/rewrite_hooks_on_python_back_to_main branch (workflow run ID: 12675889103; number: 32; attempt: 1)
[error] 6-6: Function is missing a return type annotation.
[error] 11-11: Function is missing a return type annotation.
[error] 16-16: Function is missing a return type annotation.
tests/pytest/test__errors.py (1)
1-21
: Consider removing this test module.These tests only verify that exceptions can be raised, which is basic Python functionality. They don't test any specific error conditions or error message formatting. Consider removing this module as it doesn't provide significant value.
🧰 Tools
🪛 GitHub Actions: Common issues check
[error] 8-8: Function is missing a return type annotation.
[error] 13-13: Function is missing a return type annotation.
[error] 18-18: Function is missing a return type annotation.
🪛 GitHub Actions: 🔀 PR 741 triggered by: pull_request of refs/pull/741/merge branch (workflow run ID: 12675889873; number: 33; attempt: 1)
[error] 8-8: Function is missing a return type annotation.
[error] 13-13: Function is missing a return type annotation.
[error] 18-18: Function is missing a return type annotation.
🪛 GitHub Actions: 🌱 Commit 1bdb83e triggered by: push of refs/heads/rewrite_hooks_on_python_back_to_main branch (workflow run ID: 12675889103; number: 32; attempt: 1)
[error] 8-8: Function is missing a return type annotation.
[error] 13-13: Function is missing a return type annotation.
[error] 18-18: Function is missing a return type annotation.
tests/pytest/test__cli_subcommands.py (2)
15-17
: Move import statement to the top of the file.Importing within functions makes runtime behavior less predictable. The module is already imported at the top, and Python's import caching means it won't be loaded twice.
- from pre_commit_terraform._cli_subcommands import ( - SUBCOMMAND_MODULES as patched_subcommand_modules, - )Move the import to the top of the file and use the variable directly:
from pre_commit_terraform import terraform_docs_replace from pre_commit_terraform._cli_subcommands import SUBCOMMAND_MODULES +from pre_commit_terraform._cli_subcommands import SUBCOMMAND_MODULES as patched_subcommand_modules
5-24
: LGTM! Well-structured tests for subcommand modules.The tests effectively verify:
- Mocking and patching of subcommand modules
- Integration of terraform_docs_replace within the command structure
src/pre_commit_terraform/_types.py (1)
14-30
: LGTM! Well-structured protocol definition.The protocol effectively defines the interface for subcommand modules with:
- Clear attribute definitions
- Well-defined method signatures
- Proper type annotations
🧰 Tools
🪛 Ruff (0.8.2)
15-15: Undefined name
runtime_checkable
(F821)
🪛 GitHub Actions: Common issues check
[error] 15-15: Name 'runtime_checkable' is not defined.
🪛 GitHub Actions: 🔀 PR 741 triggered by: pull_request of refs/pull/741/merge branch (workflow run ID: 12675889873; number: 33; attempt: 1)
[error] 15-15: Undefined variable 'runtime_checkable'.
🪛 GitHub Actions: 🌱 Commit 1bdb83e triggered by: push of refs/heads/rewrite_hooks_on_python_back_to_main branch (workflow run ID: 12675889103; number: 32; attempt: 1)
[error] 15-15: Undefined variable 'runtime_checkable'.
tests/pytest/test___main__.py (1)
1-45
: Revise test implementation and rename file.The test has the following issues:
Filename doesn't match the pattern '.*_test.py'
Test implementation uses mocking instead of the recommended
runpy
approachRename the file from
test___main__.py
to__main__test.py
Revise the implementation to use
runpy
:-import importlib import sys +import subprocess +from pathlib import Path -def test_main_success(mocker): - mock_argv = ['__main__.py', 'arg1', 'arg2'] - mock_return_code = 0 +def test_main_success() -> None: + result = subprocess.run( + [sys.executable, '-m', 'pre_commit_terraform', '--help'], + capture_output=True, + text=True, + ) + assert result.returncode == 0 + assert 'usage:' in result.stdout - mocker.patch('sys.argv', mock_argv) - mock_invoke_cli_app = mocker.patch( - 'pre_commit_terraform._cli.invoke_cli_app', - return_value=mock_return_code, - ) - mock_exit = mocker.patch('sys.exit') - - # Reload the module to trigger the main logic - if 'pre_commit_terraform.__main__' in sys.modules: - importlib.reload(sys.modules['pre_commit_terraform.__main__']) - else: - import pre_commit_terraform.__main__ # noqa: F401 - - mock_invoke_cli_app.assert_called_once_with(mock_argv[1:]) - mock_exit.assert_called_once_with(mock_return_code) - -def test_main_failure(mocker): - mock_argv = ['__main__.py', 'arg1', 'arg2'] - mock_return_code = 1 - - mocker.patch('sys.argv', mock_argv) - mock_invoke_cli_app = mocker.patch( - 'pre_commit_terraform._cli.invoke_cli_app', - return_value=mock_return_code, - ) - mock_exit = mocker.patch('sys.exit') - - # Reload the module to trigger the main logic - if 'pre_commit_terraform.__main__' in sys.modules: - importlib.reload(sys.modules['pre_commit_terraform.__main__']) - else: - import pre_commit_terraform.__main__ # noqa: F401 - - mock_invoke_cli_app.assert_called_once_with(mock_argv[1:]) - mock_exit.assert_called_once_with(mock_return_code) +def test_main_failure() -> None: + result = subprocess.run( + [sys.executable, '-m', 'pre_commit_terraform', '--invalid-flag'], + capture_output=True, + text=True, + ) + assert result.returncode == 1 + assert 'error:' in result.stderr🧰 Tools
🪛 GitHub Actions: Common issues check
[error] 1-1: tests/pytest/test___main__.py does not match pattern '.*_test.py'
src/pre_commit_terraform/_logger.py (1)
8-50
: Consider using Rich for better color support.The current implementation uses ANSI escape codes directly. Consider using the Rich library which provides better color support, handles terminal compatibility, and offers additional features like syntax highlighting.
Here's how you could refactor this using Rich:
-class ColoredFormatter(logging.Formatter): +from rich.logging import RichHandler +from rich.console import Console + +class EnhancedFormatter(logging.Formatter): """A logging formatter that adds color to the log messages.""" def __init__(self, pattern: str) -> None: - """ - Initialize the formatter with the given pattern. - - Args: - pattern (str): The log message format pattern. - """ super().__init__(pattern) - self.disable_color = os.environ.get('PRE_COMMIT_COLOR') == 'never' - - def format(self, record: logging.LogRecord) -> str: - """ - Format the log record and add color to the levelname. - - Args: - record (logging.LogRecord): The log record to format. - - Returns: - str: The formatted log message. - """ - if self.disable_color: - return super().format(record) - - color_mapping = { - 'DEBUG': 37, # white - 'INFO': 36, # cyan - 'WARNING': 33, # yellow - 'ERROR': 31, # red - 'CRITICAL': 41, # white on red background - } - - colored_record = copy(record) - levelname = colored_record.levelname - - set_color = f'\033[{color_mapping.get(levelname, 37)}' # default white # noqa: WPS432 - reset_color = '\033[0m' - colored_record.levelname = f'{set_color}m{levelname}{reset_color}' - - return super().format(colored_record) + self.console = Console( + force_terminal=os.environ.get('PRE_COMMIT_COLOR') != 'never' + )Then update the
setup_logging
function to use Rich:- formatter = ColoredFormatter(log_format) - log_handler = logging.StreamHandler() + formatter = EnhancedFormatter(log_format) + log_handler = RichHandler(console=formatter.console, show_time=False)src/pre_commit_terraform/terraform_docs_replace.py (1)
14-14
: 🛠️ Refactor suggestionUpdate HOOK_ID definition to follow the convention.
The HOOK_ID should be defined using
rpartition()
andreplace()
to maintain consistency with other hooks.-HOOK_ID: Final[str] = __name__.rpartition('.')[-1] +HOOK_ID: Final[str] = __name__.rpartition('.')[-1].replace('_', '.')Likely invalid or redundant comment.
src/pre_commit_terraform/_run_on_whole_repo.py (1)
77-78
: 🛠️ Refactor suggestionFix line length and improve security.
The line exceeds the maximum length and could benefit from using
shutil.which()
for better security.+from shutil import which + - # S607 disabled as we need to maintain ability to call git command no matter where it is located. - git_ls_files_cmd = ['git', 'ls-files'] # noqa: S607 + git_path = which('git') + if not git_path: + raise RuntimeError('git command not found') + git_ls_files_cmd = [git_path, 'ls-files']Likely invalid or redundant comment.
🧰 Tools
🪛 GitHub Actions: 🔀 PR 741 triggered by: pull_request of refs/pull/741/merge branch (workflow run ID: 12675889873; number: 33; attempt: 1)
[error] 77-77: Line too long (101/100).
tests/pytest/test__cli_parsing.py (1)
10-12
: Replace# ?
comments with descriptive comments.The
# ?
comments are not human-readable and should be replaced with descriptive comments.Replace the comments with descriptive ones:
-# ? -# ? populate_common_argument_parser -# ? +# Tests for populate_common_argument_parser function +# Verifies that the function correctly populates the argument parser +# with common arguments and their default values.tests/pytest/test__common.py (1)
240-240
: Usemonkeypatch.setenv
instead ofmocker.patch.dict
.For setting environment variables in tests, it's recommended to use
monkeypatch.setenv
.- mocker.patch.dict(os.environ, {'PCT_TFPATH': '/env/path/to/terraform'}) + monkeypatch.setenv('PCT_TFPATH', '/env/path/to/terraform').vscode/settings.json (1)
10-12
: Add test directories to Python analysis paths.Include test directories in the Python analysis paths for better code analysis coverage.
"python.analysis.extraPaths": [ - "./src" + "./src", + "./tests" ],hatch.toml (2)
3-3
: Include .pre-commit-hooks.yaml in sdist target.
The addition of '.pre-commit-hooks.yaml' in the include list guarantees that the hooks configuration file is bundled with the source distribution.
17-19
: Force-include .pre-commit-hooks.yaml in wheel build.
The new [build.targets.wheel.force-include] section forces inclusion of '.pre-commit-hooks.yaml' from a custom source path, ensuring that the hooks file is present in the wheel package.pyproject.toml (1)
41-44
: Update project metadata and add dependency.
The project description has been updated and the dependency list now includes 'pyyaml' to support YAML parsing. This change aligns the project metadata with the updated functionality..github/workflows/pre-commit.yaml (1)
41-41
: Upgrade Python version in workflow setup.
The python-version parameter has been updated from '3.9' to '3.10' to ensure compatibility with newer tooling and libraries used in subsequent steps..pre-commit-hooks.yaml (2)
1-35
: Remove legacy Terraform hooks.
Hooks such as 'infracost_breakdown', 'terraform_fmt', 'terraform_docs', and 'terraform_docs_without_aggregate_type_defaults' have been removed. This cleanup streamlines the configuration by dropping deprecated or unused Terraform-related hooks.
36-44
: Update terraform_docs_replace hook command.
The 'terraform_docs_replace' hook now uses the command
python -Im pre_commit_terraform terraform_docs_replace
which aligns with the intended CLI naming conventions. Ensure all documentation and dependent references are updated accordingly..pre-commit-config.yaml (9)
43-63
: Multiple MyPy configurations for distinct Python versions.
The configuration introduces separate MyPy hooks (with aliases for Python 3.13, 3.11, and 3.9) each with its own reporting parameters. This approach helps tailor type-checking to target environments.
98-103
: Removal of shfmt and shellcheck repo from pre-commit-hooks.
The previous entry for the 'jumanjihouse/pre-commit-hooks' repository (which provided shfmt and shellcheck hooks) has been removed. Ensure that their functionality is now handled elsewhere (for example, via the dedicated workflow steps) or is no longer needed.
107-120
: Update hadolint revision.
The hadolint hook’s repository revision has been updated to 'v2.13.1-beta'. Please verify that this beta version is stable and compatible with the Dockerfile linting requirements in your project.
122-130
: Removal of mirrors-prettier repo for JSON5 linting.
The configuration for the mirrors-prettier repository (used for JSON5 files) has been removed. Confirm that JSON5 linting is either no longer required or is handled by another tool in the workflow.
144-156
: Configure isort hook with custom exclusion regex.
The isort hook is set with '--force-single-line' and the 'black' profile, and it uses a multiline regex to exclude 'tests/pytest/test__cli_subcommands.py'. Please verify that the regex (utilizing the extended mode with (?x)) performs as intended.
157-160
: Add trailing comma hook.
The addition of the 'add-trailing-comma' hook (v3.1.0) helps enforce a consistent code style with trailing commas.
162-169
: Introduce autopep8 hook for formatting.
The autopep8 hook is configured to run in-place with a maximum line length of 100. This should help standardize code formatting automatically.
170-182
: Configure pylint hook with tailored disables and exclusions.
The pylint hook now disables several warnings (such as import errors and ungrouped imports) and excludes test files. These settings appear to align with the project’s coding guidelines; ensure that these disable flags are acceptable for all contributors.
184-195
: Additional MyPy configuration block – potential duplication.
An extra MyPy configuration block includes 'types-PyYAML' as an additional dependency and sets various arguments (ignoring missing imports, disallowing untyped calls, and warning on redundant casts). This may overlap with the earlier MyPy hooks for different Python versions. Please verify if both configurations are needed or consider consolidating them.
from pre_commit_terraform._structs import ReturnCode | ||
|
||
|
||
def test_return_code_values(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add return type annotations to test functions.
Add return type annotations to comply with type checking requirements:
-def test_return_code_values():
+def test_return_code_values() -> None:
-def test_return_code_names():
+def test_return_code_names() -> None:
-def test_return_code_invalid_value():
+def test_return_code_invalid_value() -> None:
Also applies to: 11-11, 16-16
🧰 Tools
🪛 GitHub Actions: Common issues check
[error] 6-6: Function is missing a return type annotation.
🪛 GitHub Actions: 🔀 PR 741 triggered by: pull_request of refs/pull/741/merge branch (workflow run ID: 12675889873; number: 33; attempt: 1)
[error] 6-6: Function is missing a return type annotation.
🪛 GitHub Actions: 🌱 Commit 1bdb83e triggered by: push of refs/heads/rewrite_hooks_on_python_back_to_main branch (workflow run ID: 12675889103; number: 32; attempt: 1)
[error] 6-6: Function is missing a return type annotation.
from pre_commit_terraform._errors import PreCommitTerraformRuntimeError | ||
|
||
|
||
def test_pre_commit_terraform_base_error(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add return type annotations to test functions.
Add return type annotations to comply with type checking requirements:
-def test_pre_commit_terraform_base_error():
+def test_pre_commit_terraform_base_error() -> None:
-def test_pre_commit_terraform_runtime_error():
+def test_pre_commit_terraform_runtime_error() -> None:
-def test_pre_commit_terraform_exit():
+def test_pre_commit_terraform_exit() -> None:
Also applies to: 13-13, 18-18
🧰 Tools
🪛 GitHub Actions: Common issues check
[error] 8-8: Function is missing a return type annotation.
🪛 GitHub Actions: 🔀 PR 741 triggered by: pull_request of refs/pull/741/merge branch (workflow run ID: 12675889873; number: 33; attempt: 1)
[error] 8-8: Function is missing a return type annotation.
🪛 GitHub Actions: 🌱 Commit 1bdb83e triggered by: push of refs/heads/rewrite_hooks_on_python_back_to_main branch (workflow run ID: 12675889103; number: 32; attempt: 1)
[error] 8-8: Function is missing a return type annotation.
@@ -0,0 +1,23 @@ | |||
from pre_commit_terraform import terraform_docs_replace |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename file to match test pattern.
The file name doesn't match the required pattern '.*_test.py'. Rename it to cli_subcommands_test.py
.
🧰 Tools
🪛 GitHub Actions: Common issues check
[error] 1-1: tests/pytest/test__cli_subcommands.py does not match pattern '.*_test.py'
🪛 GitHub Actions: 🔀 PR 741 triggered by: pull_request of refs/pull/741/merge branch (workflow run ID: 12675889873; number: 33; attempt: 1)
[error] 1-1: NameError: name 'runtime_checkable' is not defined
🪛 GitHub Actions: 🌱 Commit 1bdb83e triggered by: push of refs/heads/rewrite_hooks_on_python_back_to_main branch (workflow run ID: 12675889103; number: 32; attempt: 1)
[error] 1-1: NameError: name 'runtime_checkable' is not defined
from typing import Protocol | ||
from typing import Union |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add missing import for runtime_checkable
.
The runtime_checkable
decorator is undefined. Add the import from typing:
-from typing import Protocol
+from typing import Protocol, runtime_checkable
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
from typing import Protocol | |
from typing import Union | |
from typing import Protocol, runtime_checkable | |
from typing import Union |
def test_return_code_type(): | ||
assert isinstance(ReturnCode.OK, ReturnCodeType) | ||
assert isinstance(ReturnCode.ERROR, ReturnCodeType) | ||
assert isinstance(0, ReturnCodeType) | ||
assert isinstance(1, ReturnCodeType) | ||
assert not isinstance(2.5, ReturnCodeType) | ||
assert not isinstance('string', ReturnCodeType) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add return type annotation to the test function.
Apply this diff to fix the issue:
-def test_return_code_type():
+def test_return_code_type() -> None:
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
def test_return_code_type(): | |
assert isinstance(ReturnCode.OK, ReturnCodeType) | |
assert isinstance(ReturnCode.ERROR, ReturnCodeType) | |
assert isinstance(0, ReturnCodeType) | |
assert isinstance(1, ReturnCodeType) | |
assert not isinstance(2.5, ReturnCodeType) | |
assert not isinstance('string', ReturnCodeType) | |
def test_return_code_type() -> None: | |
assert isinstance(ReturnCode.OK, ReturnCodeType) | |
assert isinstance(ReturnCode.ERROR, ReturnCodeType) | |
assert isinstance(0, ReturnCodeType) | |
assert isinstance(1, ReturnCodeType) | |
assert not isinstance(2.5, ReturnCodeType) | |
assert not isinstance('string', ReturnCodeType) |
🧰 Tools
🪛 GitHub Actions: Common issues check
[error] 9-9: Function is missing a return type annotation.
def test_invoke_cli_app_success(mocker): | ||
mock_parsed_args = mocker.MagicMock() | ||
mock_parsed_args.invoke_cli_app.return_value = ReturnCode.OK | ||
|
||
mock_initialize_argument_parser = mocker.patch( | ||
'pre_commit_terraform._cli.initialize_argument_parser', | ||
) | ||
mock_initialize_argument_parser.return_value.parse_args.return_value = mock_parsed_args | ||
|
||
result = invoke_cli_app(['mock_arg']) | ||
|
||
assert result == ReturnCode.OK | ||
mock_parsed_args.invoke_cli_app.assert_called_once_with(mock_parsed_args) | ||
|
||
|
||
def test_invoke_cli_app_pre_commit_terraform_exit(mocker): | ||
mock_parsed_args = mocker.MagicMock() | ||
mock_parsed_args.invoke_cli_app.side_effect = PreCommitTerraformExit('Exit error') | ||
|
||
mock_initialize_argument_parser = mocker.patch( | ||
'pre_commit_terraform._cli.initialize_argument_parser', | ||
) | ||
mock_initialize_argument_parser.return_value.parse_args.return_value = mock_parsed_args | ||
|
||
with pytest.raises(PreCommitTerraformExit): | ||
invoke_cli_app(['mock_arg']) | ||
|
||
mock_parsed_args.invoke_cli_app.assert_called_once_with(mock_parsed_args) | ||
|
||
|
||
def test_invoke_cli_app_pre_commit_terraform_runtime_error(mocker): | ||
mock_parsed_args = mocker.MagicMock() | ||
mock_parsed_args.invoke_cli_app.side_effect = PreCommitTerraformRuntimeError('Runtime error') | ||
|
||
mock_initialize_argument_parser = mocker.patch( | ||
'pre_commit_terraform._cli.initialize_argument_parser', | ||
) | ||
mock_initialize_argument_parser.return_value.parse_args.return_value = mock_parsed_args | ||
|
||
result = invoke_cli_app(['mock_arg']) | ||
|
||
assert result == ReturnCode.ERROR | ||
mock_parsed_args.invoke_cli_app.assert_called_once_with(mock_parsed_args) | ||
|
||
|
||
def test_invoke_cli_app_pre_commit_terraform_base_error(mocker): | ||
mock_parsed_args = mocker.MagicMock() | ||
mock_parsed_args.invoke_cli_app.side_effect = PreCommitTerraformBaseError('Base error') | ||
|
||
mock_initialize_argument_parser = mocker.patch( | ||
'pre_commit_terraform._cli.initialize_argument_parser', | ||
) | ||
mock_initialize_argument_parser.return_value.parse_args.return_value = mock_parsed_args | ||
|
||
result = invoke_cli_app(['mock_arg']) | ||
|
||
assert result == ReturnCode.ERROR | ||
mock_parsed_args.invoke_cli_app.assert_called_once_with(mock_parsed_args) | ||
|
||
|
||
def test_invoke_cli_app_keyboard_interrupt(mocker): | ||
mock_parsed_args = mocker.MagicMock() | ||
mock_parsed_args.invoke_cli_app.side_effect = KeyboardInterrupt('Interrupt') | ||
|
||
mock_initialize_argument_parser = mocker.patch( | ||
'pre_commit_terraform._cli.initialize_argument_parser', | ||
) | ||
mock_initialize_argument_parser.return_value.parse_args.return_value = mock_parsed_args | ||
|
||
result = invoke_cli_app(['mock_arg']) | ||
|
||
assert result == ReturnCode.ERROR | ||
mock_parsed_args.invoke_cli_app.assert_called_once_with(mock_parsed_args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add type annotations to test functions.
The test functions are missing type annotations as required by the pipeline.
-def test_invoke_cli_app_success(mocker):
+def test_invoke_cli_app_success(mocker: pytest.MockFixture) -> None:
-def test_invoke_cli_app_pre_commit_terraform_exit(mocker):
+def test_invoke_cli_app_pre_commit_terraform_exit(mocker: pytest.MockFixture) -> None:
-def test_invoke_cli_app_pre_commit_terraform_runtime_error(mocker):
+def test_invoke_cli_app_pre_commit_terraform_runtime_error(mocker: pytest.MockFixture) -> None:
-def test_invoke_cli_app_pre_commit_terraform_base_error(mocker):
+def test_invoke_cli_app_pre_commit_terraform_base_error(mocker: pytest.MockFixture) -> None:
-def test_invoke_cli_app_keyboard_interrupt(mocker):
+def test_invoke_cli_app_keyboard_interrupt(mocker: pytest.MockFixture) -> None:
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
def test_invoke_cli_app_success(mocker): | |
mock_parsed_args = mocker.MagicMock() | |
mock_parsed_args.invoke_cli_app.return_value = ReturnCode.OK | |
mock_initialize_argument_parser = mocker.patch( | |
'pre_commit_terraform._cli.initialize_argument_parser', | |
) | |
mock_initialize_argument_parser.return_value.parse_args.return_value = mock_parsed_args | |
result = invoke_cli_app(['mock_arg']) | |
assert result == ReturnCode.OK | |
mock_parsed_args.invoke_cli_app.assert_called_once_with(mock_parsed_args) | |
def test_invoke_cli_app_pre_commit_terraform_exit(mocker): | |
mock_parsed_args = mocker.MagicMock() | |
mock_parsed_args.invoke_cli_app.side_effect = PreCommitTerraformExit('Exit error') | |
mock_initialize_argument_parser = mocker.patch( | |
'pre_commit_terraform._cli.initialize_argument_parser', | |
) | |
mock_initialize_argument_parser.return_value.parse_args.return_value = mock_parsed_args | |
with pytest.raises(PreCommitTerraformExit): | |
invoke_cli_app(['mock_arg']) | |
mock_parsed_args.invoke_cli_app.assert_called_once_with(mock_parsed_args) | |
def test_invoke_cli_app_pre_commit_terraform_runtime_error(mocker): | |
mock_parsed_args = mocker.MagicMock() | |
mock_parsed_args.invoke_cli_app.side_effect = PreCommitTerraformRuntimeError('Runtime error') | |
mock_initialize_argument_parser = mocker.patch( | |
'pre_commit_terraform._cli.initialize_argument_parser', | |
) | |
mock_initialize_argument_parser.return_value.parse_args.return_value = mock_parsed_args | |
result = invoke_cli_app(['mock_arg']) | |
assert result == ReturnCode.ERROR | |
mock_parsed_args.invoke_cli_app.assert_called_once_with(mock_parsed_args) | |
def test_invoke_cli_app_pre_commit_terraform_base_error(mocker): | |
mock_parsed_args = mocker.MagicMock() | |
mock_parsed_args.invoke_cli_app.side_effect = PreCommitTerraformBaseError('Base error') | |
mock_initialize_argument_parser = mocker.patch( | |
'pre_commit_terraform._cli.initialize_argument_parser', | |
) | |
mock_initialize_argument_parser.return_value.parse_args.return_value = mock_parsed_args | |
result = invoke_cli_app(['mock_arg']) | |
assert result == ReturnCode.ERROR | |
mock_parsed_args.invoke_cli_app.assert_called_once_with(mock_parsed_args) | |
def test_invoke_cli_app_keyboard_interrupt(mocker): | |
mock_parsed_args = mocker.MagicMock() | |
mock_parsed_args.invoke_cli_app.side_effect = KeyboardInterrupt('Interrupt') | |
mock_initialize_argument_parser = mocker.patch( | |
'pre_commit_terraform._cli.initialize_argument_parser', | |
) | |
mock_initialize_argument_parser.return_value.parse_args.return_value = mock_parsed_args | |
result = invoke_cli_app(['mock_arg']) | |
assert result == ReturnCode.ERROR | |
mock_parsed_args.invoke_cli_app.assert_called_once_with(mock_parsed_args) | |
def test_invoke_cli_app_success(mocker: pytest.MockFixture) -> None: | |
mock_parsed_args = mocker.MagicMock() | |
mock_parsed_args.invoke_cli_app.return_value = ReturnCode.OK | |
mock_initialize_argument_parser = mocker.patch( | |
'pre_commit_terraform._cli.initialize_argument_parser', | |
) | |
mock_initialize_argument_parser.return_value.parse_args.return_value = mock_parsed_args | |
result = invoke_cli_app(['mock_arg']) | |
assert result == ReturnCode.OK | |
mock_parsed_args.invoke_cli_app.assert_called_once_with(mock_parsed_args) | |
def test_invoke_cli_app_pre_commit_terraform_exit(mocker: pytest.MockFixture) -> None: | |
mock_parsed_args = mocker.MagicMock() | |
mock_parsed_args.invoke_cli_app.side_effect = PreCommitTerraformExit('Exit error') | |
mock_initialize_argument_parser = mocker.patch( | |
'pre_commit_terraform._cli.initialize_argument_parser', | |
) | |
mock_initialize_argument_parser.return_value.parse_args.return_value = mock_parsed_args | |
with pytest.raises(PreCommitTerraformExit): | |
invoke_cli_app(['mock_arg']) | |
mock_parsed_args.invoke_cli_app.assert_called_once_with(mock_parsed_args) | |
def test_invoke_cli_app_pre_commit_terraform_runtime_error(mocker: pytest.MockFixture) -> None: | |
mock_parsed_args = mocker.MagicMock() | |
mock_parsed_args.invoke_cli_app.side_effect = PreCommitTerraformRuntimeError('Runtime error') | |
mock_initialize_argument_parser = mocker.patch( | |
'pre_commit_terraform._cli.initialize_argument_parser', | |
) | |
mock_initialize_argument_parser.return_value.parse_args.return_value = mock_parsed_args | |
result = invoke_cli_app(['mock_arg']) | |
assert result == ReturnCode.ERROR | |
mock_parsed_args.invoke_cli_app.assert_called_once_with(mock_parsed_args) | |
def test_invoke_cli_app_pre_commit_terraform_base_error(mocker: pytest.MockFixture) -> None: | |
mock_parsed_args = mocker.MagicMock() | |
mock_parsed_args.invoke_cli_app.side_effect = PreCommitTerraformBaseError('Base error') | |
mock_initialize_argument_parser = mocker.patch( | |
'pre_commit_terraform._cli.initialize_argument_parser', | |
) | |
mock_initialize_argument_parser.return_value.parse_args.return_value = mock_parsed_args | |
result = invoke_cli_app(['mock_arg']) | |
assert result == ReturnCode.ERROR | |
mock_parsed_args.invoke_cli_app.assert_called_once_with(mock_parsed_args) | |
def test_invoke_cli_app_keyboard_interrupt(mocker: pytest.MockFixture) -> None: | |
mock_parsed_args = mocker.MagicMock() | |
mock_parsed_args.invoke_cli_app.side_effect = KeyboardInterrupt('Interrupt') | |
mock_initialize_argument_parser = mocker.patch( | |
'pre_commit_terraform._cli.initialize_argument_parser', | |
) | |
mock_initialize_argument_parser.return_value.parse_args.return_value = mock_parsed_args | |
result = invoke_cli_app(['mock_arg']) | |
assert result == ReturnCode.ERROR | |
mock_parsed_args.invoke_cli_app.assert_called_once_with(mock_parsed_args) |
🧰 Tools
🪛 GitHub Actions: Common issues check
[error] 10-10: Function is missing a type annotation.
[error] 25-25: Function is missing a type annotation.
[error] 40-40: Function is missing a type annotation.
[error] 55-55: Function is missing a type annotation.
[error] 70-70: Function is missing a type annotation.
logger = logging.getLogger(__name__) | ||
|
||
|
||
def is_function_defined(func_name: str, scope: dict) -> bool: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add type parameters to generic dict type.
The dict type needs type parameters to satisfy type checking.
-def is_function_defined(func_name: str, scope: dict) -> bool:
+def is_function_defined(func_name: str, scope: dict[str, object]) -> bool:
🧰 Tools
🪛 GitHub Actions: Common issues check
[error] 13-13: Missing type parameters for generic type 'dict'.
🪛 GitHub Actions: 🔀 PR 741 triggered by: pull_request of refs/pull/741/merge branch (workflow run ID: 12675889873; number: 33; attempt: 1)
[error] 13-13: Missing type parameters for generic type 'dict'.
🪛 GitHub Actions: 🌱 Commit 1bdb83e triggered by: push of refs/heads/rewrite_hooks_on_python_back_to_main branch (workflow run ID: 12675889103; number: 32; attempt: 1)
[error] 13-13: Missing type parameters for generic type 'dict'.
import subprocess | ||
from importlib.resources import files as access_artifacts_of | ||
|
||
import yaml |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Install type stubs for PyYAML.
Add types-PyYAML
to development dependencies to resolve type checking issues.
# setup.py or pyproject.toml
install_requires=[
'pyyaml',
+ 'types-PyYAML', # dev dependency
]
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
import yaml | |
# setup.py or pyproject.toml | |
install_requires=[ | |
'pyyaml', | |
'types-PyYAML', # dev dependency | |
] |
🧰 Tools
🪛 GitHub Actions: Common issues check
[error] 8-8: Library stubs not installed for 'yaml'.
🪛 GitHub Actions: 🔀 PR 741 triggered by: pull_request of refs/pull/741/merge branch (workflow run ID: 12675889873; number: 33; attempt: 1)
[error] 8-8: Library stubs not installed for 'yaml'.
🪛 GitHub Actions: 🌱 Commit 1bdb83e triggered by: push of refs/heads/rewrite_hooks_on_python_back_to_main branch (workflow run ID: 12675889103; number: 32; attempt: 1)
[error] 8-8: Library stubs not installed for 'yaml'.
# ? | ||
# ? populate_common_argument_parser | ||
# ? | ||
def test_populate_common_argument_parser(mocker): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add type annotations to function parameters and return type.
The function is missing type annotations as indicated by pipeline failures.
-def test_populate_common_argument_parser(mocker):
+def test_populate_common_argument_parser(mocker: pytest_mock.MockerFixture) -> None:
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
def test_populate_common_argument_parser(mocker): | |
def test_populate_common_argument_parser(mocker: pytest_mock.MockerFixture) -> None: |
🧰 Tools
🪛 GitHub Actions: Common issues check
[error] 13-13: Function is missing a type annotation.
assert result == '/custom/path/to/terraform' | ||
|
||
|
||
def test_get_tf_binary_path_from_pct_tfpath_env_var(mocker): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add type annotations to function parameters and return type.
The function is missing type annotations as indicated by pipeline failures.
-def test_get_tf_binary_path_from_pct_tfpath_env_var(mocker):
+def test_get_tf_binary_path_from_pct_tfpath_env_var(mocker: pytest_mock.MockerFixture) -> None:
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
def test_get_tf_binary_path_from_pct_tfpath_env_var(mocker): | |
def test_get_tf_binary_path_from_pct_tfpath_env_var(mocker: pytest_mock.MockerFixture) -> None: |
🧰 Tools
🪛 GitHub Actions: Common issues check
[error] 238-238: Function is missing a return type annotation.
🪛 GitHub Actions: 🔀 PR 741 triggered by: pull_request of refs/pull/741/merge branch (workflow run ID: 12675889873; number: 33; attempt: 1)
[error] 238-238: Function is missing a type annotation.
🪛 GitHub Actions: 🌱 Commit 1bdb83e triggered by: push of refs/heads/rewrite_hooks_on_python_back_to_main branch (workflow run ID: 12675889103; number: 32; attempt: 1)
[error] 238-238: Function is missing a return type annotation.
Put an
x
into the box if that apply:Description of your changes
This PR includes only changes to skeleton of python hooks, w/o addition of hooks itself, as it will be required a new release.
Changes includes but not limited to:
CLI_SUBCOMMAND_NAME
toHOOK_ID
)terraform_docs_replace
using python 3 syntax here from hook additionmypy
-error: No parent module -- cannot perform relative import - Found 1 error in 1 file (errors prevented further checking)
_common.sh
functionality. Located mostly in_common.py
,_run_on_whole_repo.py
and_logger.py
.common::parse_cmdline
function was fully reworked by @webknjaz in prev PRs and now populated with common functionality in_cli_parsing.py
This PR DOES NOT include any hooks additions or breaking changes for existing hooks. In branch with hooks exist a bunch of TODOs part of which could result in rework of functional in this PR. But there are already many changes, and this could block @webknjaz to implement testing best practices, so I checkout to new branch, dropped everything which could end in new release and created this PR to merge non-user-faced changes to
master
How can we test changes
You can
or play around using updated CONTIBUTING.md notes
Additional references
This PR includes and based on work of @webknjaz who set all these best-practice CLI structure in pre PRs and fixed essential bug of accessing .pre-commit-hooks.yaml values in not-yet-released python hooks (#740 (included in this PR)) and initial work of @ericfrederich who shown us in #652 that reimplementation in Python can be done relatively easy
Summary by CodeRabbit
New Features
- Expanded pre-commit hook support with new linting, formatting, and static analysis checks.
- Improved CLI argument parsing that offers clearer usage instructions and more robust error handling.
Improvements
- Updated CI/CD to run on Python 3.10 for enhanced compatibility.
- Enhanced IDE configuration for improved module resolution.
- Refined build and package settings to ensure essential configuration files are included.
- Updated project metadata and dependencies for better alignment with current standards.
Tests
- Extended test coverage to bolster stability and validate command-line behavior.