From dc992c9bbb7b394e66a5e927c373707b6704b442 Mon Sep 17 00:00:00 2001 From: Kate Case Date: Wed, 30 Oct 2024 11:04:37 -0400 Subject: [PATCH 1/3] Add docstrings and type hints to the rest of the commands. --- .config/pydoclint-baseline.txt | 46 ---------------------------- src/molecule/command/reset.py | 16 +++++++--- src/molecule/command/side_effect.py | 31 +++++++++++++------ src/molecule/command/syntax.py | 27 ++++++++++++----- src/molecule/command/test.py | 47 ++++++++++++++++++++--------- src/molecule/command/verify.py | 24 +++++++++++---- 6 files changed, 104 insertions(+), 87 deletions(-) diff --git a/.config/pydoclint-baseline.txt b/.config/pydoclint-baseline.txt index 69b3c962f..395b30a0b 100644 --- a/.config/pydoclint-baseline.txt +++ b/.config/pydoclint-baseline.txt @@ -1,49 +1,3 @@ -src/molecule/command/reset.py - DOC101: Function `reset`: Docstring contains fewer arguments than in function signature. - DOC106: Function `reset`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature - DOC107: Function `reset`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints - DOC103: Function `reset`: Docstring arguments are different from function arguments. (Or could be other formatting issues: https://jsh9.github.io/pydoclint/violation_codes.html#notes-on-doc103 ). Arguments in the function signature but not in the docstring: [ctx: , scenario_name: ]. --------------------- -src/molecule/command/side_effect.py - DOC101: Method `SideEffect.execute`: Docstring contains fewer arguments than in function signature. - DOC106: Method `SideEffect.execute`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature - DOC107: Method `SideEffect.execute`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints - DOC103: Method `SideEffect.execute`: Docstring arguments are different from function arguments. (Or could be other formatting issues: https://jsh9.github.io/pydoclint/violation_codes.html#notes-on-doc103 ). Arguments in the function signature but not in the docstring: [action_args: ]. - DOC201: Method `SideEffect.execute` does not have a return section in docstring - DOC101: Function `side_effect`: Docstring contains fewer arguments than in function signature. - DOC106: Function `side_effect`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature - DOC107: Function `side_effect`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints - DOC103: Function `side_effect`: Docstring arguments are different from function arguments. (Or could be other formatting issues: https://jsh9.github.io/pydoclint/violation_codes.html#notes-on-doc103 ). Arguments in the function signature but not in the docstring: [ctx: , scenario_name: ]. --------------------- -src/molecule/command/syntax.py - DOC101: Method `Syntax.execute`: Docstring contains fewer arguments than in function signature. - DOC106: Method `Syntax.execute`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature - DOC107: Method `Syntax.execute`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints - DOC103: Method `Syntax.execute`: Docstring arguments are different from function arguments. (Or could be other formatting issues: https://jsh9.github.io/pydoclint/violation_codes.html#notes-on-doc103 ). Arguments in the function signature but not in the docstring: [action_args: ]. - DOC101: Function `syntax`: Docstring contains fewer arguments than in function signature. - DOC106: Function `syntax`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature - DOC107: Function `syntax`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints - DOC103: Function `syntax`: Docstring arguments are different from function arguments. (Or could be other formatting issues: https://jsh9.github.io/pydoclint/violation_codes.html#notes-on-doc103 ). Arguments in the function signature but not in the docstring: [ctx: , scenario_name: ]. --------------------- -src/molecule/command/test.py - DOC101: Method `Test.execute`: Docstring contains fewer arguments than in function signature. - DOC106: Method `Test.execute`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature - DOC107: Method `Test.execute`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints - DOC103: Method `Test.execute`: Docstring arguments are different from function arguments. (Or could be other formatting issues: https://jsh9.github.io/pydoclint/violation_codes.html#notes-on-doc103 ). Arguments in the function signature but not in the docstring: [action_args: ]. - DOC101: Function `test`: Docstring contains fewer arguments than in function signature. - DOC106: Function `test`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature - DOC107: Function `test`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints - DOC103: Function `test`: Docstring arguments are different from function arguments. (Or could be other formatting issues: https://jsh9.github.io/pydoclint/violation_codes.html#notes-on-doc103 ). Arguments in the function signature but not in the docstring: [__all: , ansible_args: , ctx: , destroy: , driver_name: , parallel: , platform_name: , scenario_name: ]. --------------------- -src/molecule/command/verify.py - DOC101: Method `Verify.execute`: Docstring contains fewer arguments than in function signature. - DOC106: Method `Verify.execute`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature - DOC107: Method `Verify.execute`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints - DOC103: Method `Verify.execute`: Docstring arguments are different from function arguments. (Or could be other formatting issues: https://jsh9.github.io/pydoclint/violation_codes.html#notes-on-doc103 ). Arguments in the function signature but not in the docstring: [action_args: ]. - DOC101: Function `verify`: Docstring contains fewer arguments than in function signature. - DOC106: Function `verify`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature - DOC107: Function `verify`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints - DOC103: Function `verify`: Docstring arguments are different from function arguments. (Or could be other formatting issues: https://jsh9.github.io/pydoclint/violation_codes.html#notes-on-doc103 ). Arguments in the function signature but not in the docstring: [ctx: , scenario_name: ]. -------------------- src/molecule/console.py DOC101: Function `to_bool`: Docstring contains fewer arguments than in function signature. diff --git a/src/molecule/command/reset.py b/src/molecule/command/reset.py index 72f49f238..1e7b587fd 100644 --- a/src/molecule/command/reset.py +++ b/src/molecule/command/reset.py @@ -31,7 +31,7 @@ if TYPE_CHECKING: - from molecule.types import CommandArgs + from molecule.types import CommandArgs, MoleculeArgs LOG = logging.getLogger(__name__) @@ -45,9 +45,17 @@ default=base.MOLECULE_DEFAULT_SCENARIO_NAME, help=f"Name of the scenario to target. ({base.MOLECULE_DEFAULT_SCENARIO_NAME})", ) -def reset(ctx, scenario_name): # type: ignore[no-untyped-def] # pragma: no cover # noqa: ANN001, ANN201 - """Reset molecule temporary folders.""" - args = ctx.obj.get("args") +def reset( + ctx: click.Context, + scenario_name: str, +) -> None: # pragma: no cover + """Reset molecule temporary folders. + + Args: + ctx: Click context object holding commandline arguments. + scenario_name: Name of the scenario to target. + """ + args: MoleculeArgs = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) # noqa: SLF001 command_args: CommandArgs = {"subcommand": subcommand} diff --git a/src/molecule/command/side_effect.py b/src/molecule/command/side_effect.py index b87bd0379..cb7edbd08 100644 --- a/src/molecule/command/side_effect.py +++ b/src/molecule/command/side_effect.py @@ -42,14 +42,19 @@ class SideEffect(base.Base): See the provisioners documentation for further details. """ - def execute(self, action_args=None): # type: ignore[no-untyped-def] # noqa: ANN001, ANN201 - """Execute the actions necessary to perform a `molecule side-effect` and returns None.""" - if not self._config.provisioner.playbooks.side_effect: # type: ignore[union-attr] - msg = "Skipping, side effect playbook not configured." - LOG.warning(msg) - return + def execute(self, action_args: list[str] | None = None) -> None: + """Execute the actions necessary to perform a `molecule side-effect`. - self._config.provisioner.side_effect(action_args) # type: ignore[union-attr] + Args: + action_args: Arguments for this command. + """ + if self._config.provisioner: + if not self._config.provisioner.playbooks.side_effect: + msg = "Skipping, side effect playbook not configured." + LOG.warning(msg) + return + + self._config.provisioner.side_effect(action_args) # type: ignore[no-untyped-call] @base.click_command_ex() @@ -60,8 +65,16 @@ def execute(self, action_args=None): # type: ignore[no-untyped-def] # noqa: AN default=base.MOLECULE_DEFAULT_SCENARIO_NAME, help=f"Name of the scenario to target. ({base.MOLECULE_DEFAULT_SCENARIO_NAME})", ) -def side_effect(ctx, scenario_name): # type: ignore[no-untyped-def] # pragma: no cover # noqa: ANN001, ANN201 - """Use the provisioner to perform side-effects to the instances.""" +def side_effect( + ctx: click.Context, + scenario_name: str, +) -> None: # pragma: no cover + """Use the provisioner to perform side-effects to the instances. + + Args: + ctx: Click context object holding commandline arguments. + scenario_name: Name of the scenario to target. + """ args = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) # noqa: SLF001 command_args: CommandArgs = {"subcommand": subcommand} diff --git a/src/molecule/command/syntax.py b/src/molecule/command/syntax.py index 4f5f720f1..cad3df406 100644 --- a/src/molecule/command/syntax.py +++ b/src/molecule/command/syntax.py @@ -30,7 +30,7 @@ if TYPE_CHECKING: - from molecule.types import CommandArgs + from molecule.types import CommandArgs, MoleculeArgs LOG = logging.getLogger(__name__) @@ -39,9 +39,14 @@ class Syntax(base.Base): """Syntax Command Class.""" - def execute(self, action_args=None): # type: ignore[no-untyped-def] # noqa: ANN001, ANN201, ARG002 - """Execute the actions necessary to perform a `molecule syntax` and returns None.""" - self._config.provisioner.syntax() # type: ignore[union-attr] + def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002 + """Execute the actions necessary to perform a `molecule syntax`. + + Args: + action_args: Arguments for this command. Unused. + """ + if self._config.provisioner: + self._config.provisioner.syntax() # type: ignore[no-untyped-call] @base.click_command_ex() @@ -52,9 +57,17 @@ def execute(self, action_args=None): # type: ignore[no-untyped-def] # noqa: AN default=base.MOLECULE_DEFAULT_SCENARIO_NAME, help=f"Name of the scenario to target. ({base.MOLECULE_DEFAULT_SCENARIO_NAME})", ) -def syntax(ctx, scenario_name): # type: ignore[no-untyped-def] # pragma: no cover # noqa: ANN001, ANN201 - """Use the provisioner to syntax check the role.""" - args = ctx.obj.get("args") +def syntax( + ctx: click.Context, + scenario_name: str, +) -> None: # pragma: no cover + """Use the provisioner to syntax check the role. + + Args: + ctx: Click context object holding commandline arguments. + scenario_name: Name of the scenario to target. + """ + args: MoleculeArgs = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) # noqa: SLF001 command_args: CommandArgs = {"subcommand": subcommand} diff --git a/src/molecule/command/test.py b/src/molecule/command/test.py index 8f8cb566c..348ac5733 100644 --- a/src/molecule/command/test.py +++ b/src/molecule/command/test.py @@ -34,7 +34,9 @@ if TYPE_CHECKING: - from molecule.types import CommandArgs + from typing import Literal + + from molecule.types import CommandArgs, MoleculeArgs LOG = logging.getLogger(__name__) @@ -45,8 +47,12 @@ class Test(base.Base): """Test Command Class.""" - def execute(self, action_args=None): # type: ignore[no-untyped-def] # noqa: ANN001, ANN201 - """Execute the actions necessary to perform a `molecule test` and returns None.""" + def execute(self, action_args: list[str] | None = None) -> None: + """Execute the actions necessary to perform a `molecule test`. + + Args: + action_args: Arguments for this command. Unused. + """ @base.click_command_ex() @@ -87,18 +93,29 @@ def execute(self, action_args=None): # type: ignore[no-untyped-def] # noqa: AN help="Enable or disable parallel mode. Default is disabled.", ) @click.argument("ansible_args", nargs=-1, type=click.UNPROCESSED) -def test( # type: ignore[no-untyped-def] # noqa: ANN201, PLR0913 - ctx, # noqa: ANN001 - scenario_name, # noqa: ANN001 - driver_name, # noqa: ANN001 - __all, # noqa: ANN001 - destroy, # noqa: ANN001 - parallel, # noqa: ANN001 - ansible_args, # noqa: ANN001 - platform_name, # noqa: ANN001 -): # pragma: no cover - """Test (dependency, cleanup, destroy, syntax, create, prepare, converge, idempotence, side_effect, verify, cleanup, destroy).""" # noqa: E501 - args = ctx.obj.get("args") +def test( # noqa: PLR0913 + ctx: click.Context, + scenario_name: str | None, + driver_name: str, + __all: bool, # noqa: FBT001 + destroy: Literal["always", "never"], + parallel: bool, # noqa: FBT001 + ansible_args: tuple[str, ...], + platform_name: str, +) -> None: # pragma: no cover + """Test (dependency, cleanup, destroy, syntax, create, prepare, converge, idempotence, side_effect, verify, cleanup, destroy). + + Args: + ctx: Click context object holding commandline arguments. + scenario_name: Name of the scenario to target. + driver_name: Name of the driver to use. + __all: Whether molecule should target scenario_name or all scenarios. + destroy: The destroy strategy to use. + parallel: Whether the scenario(s) should be run in parallel mode. + ansible_args: Arguments to forward to Ansible. + platform_name: Name of the platform to use. + """ # noqa: E501 + args: MoleculeArgs = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) # noqa: SLF001 command_args: CommandArgs = { "parallel": parallel, diff --git a/src/molecule/command/verify.py b/src/molecule/command/verify.py index 3b98af830..199cdf9ed 100644 --- a/src/molecule/command/verify.py +++ b/src/molecule/command/verify.py @@ -30,7 +30,7 @@ if TYPE_CHECKING: - from molecule.types import CommandArgs + from molecule.types import CommandArgs, MoleculeArgs LOG = logging.getLogger(__name__) @@ -39,8 +39,12 @@ class Verify(base.Base): """Verify Command Class.""" - def execute(self, action_args=None): # type: ignore[no-untyped-def] # noqa: ANN001, ANN201 - """Execute the actions necessary to perform a `molecule verify` and returns None.""" + def execute(self, action_args: list[str] | None = None) -> None: + """Execute the actions necessary to perform a `molecule verify`. + + Args: + action_args: Arguments for this command. + """ self._config.verifier.execute(action_args) @@ -52,9 +56,17 @@ def execute(self, action_args=None): # type: ignore[no-untyped-def] # noqa: AN default=base.MOLECULE_DEFAULT_SCENARIO_NAME, help=f"Name of the scenario to target. ({base.MOLECULE_DEFAULT_SCENARIO_NAME})", ) -def verify(ctx, scenario_name="default"): # type: ignore[no-untyped-def] # pragma: no cover # noqa: ANN001, ANN201 - """Run automated tests against instances.""" - args = ctx.obj.get("args") +def verify( + ctx: click.Context, + scenario_name: str = "default", +) -> None: # pragma: no cover + """Run automated tests against instances. + + Args: + ctx: Click context object holding commandline arguments. + scenario_name: Name of the scenario to target. + """ + args: MoleculeArgs = ctx.obj.get("args") subcommand = base._get_subcommand(__name__) # noqa: SLF001 command_args: CommandArgs = {"subcommand": subcommand} From 1cb8024ee40109c8ed96c644080c2628514bb1d4 Mon Sep 17 00:00:00 2001 From: Kate Case Date: Wed, 30 Oct 2024 13:49:53 -0400 Subject: [PATCH 2/3] Fix tests --- tests/unit/command/test_side_effect.py | 33 ++++++++++++++++---------- tests/unit/command/test_syntax.py | 16 +++++++------ tests/unit/command/test_verify.py | 16 ++++++++----- 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/tests/unit/command/test_side_effect.py b/tests/unit/command/test_side_effect.py index 2b3a908aa..8724d7ecc 100644 --- a/tests/unit/command/test_side_effect.py +++ b/tests/unit/command/test_side_effect.py @@ -30,11 +30,18 @@ if TYPE_CHECKING: + from typing import Literal + from unittest.mock import MagicMock, Mock + from pytest_mock import MockerFixture + from molecule.types import ProvisionerData + @pytest.fixture() -def _command_provisioner_section_with_side_effect_data(): # type: ignore[no-untyped-def] # noqa: ANN202 +def _command_provisioner_section_with_side_effect_data() -> ( + dict[Literal["provisioner"], ProvisionerData] +): return { "provisioner": { "name": "ansible", @@ -44,7 +51,7 @@ def _command_provisioner_section_with_side_effect_data(): # type: ignore[no-unt @pytest.fixture() -def _patched_ansible_side_effect(mocker): # type: ignore[no-untyped-def] # noqa: ANN001, ANN202 +def _patched_ansible_side_effect(mocker: MockerFixture) -> MagicMock: return mocker.patch("molecule.provisioner.ansible.Ansible.side_effect") @@ -56,18 +63,18 @@ def _patched_ansible_side_effect(mocker): # type: ignore[no-untyped-def] # noq ["_command_provisioner_section_with_side_effect_data"], # noqa: PT007 indirect=True, ) -def test_side_effect_execute( # type: ignore[no-untyped-def] # noqa: ANN201, D103 +def test_side_effect_execute( # noqa: D103 mocker: MockerFixture, # noqa: ARG001 - _patched_ansible_side_effect, # noqa: ANN001, PT019 - caplog, # noqa: ANN001 - patched_config_validate, # noqa: ANN001, ARG001 + _patched_ansible_side_effect: Mock, # noqa: PT019 + caplog: pytest.LogCaptureFixture, + patched_config_validate: Mock, # noqa: ARG001 config_instance: config.Config, -): +) -> None: pb = os.path.join(config_instance.scenario.directory, "side_effect.yml") # noqa: PTH118 util.write_file(pb, "") se = side_effect.SideEffect(config_instance) - se.execute() # type: ignore[no-untyped-call] + se.execute() assert "default" in caplog.text assert "side_effect" in caplog.text @@ -75,13 +82,13 @@ def test_side_effect_execute( # type: ignore[no-untyped-def] # noqa: ANN201, D _patched_ansible_side_effect.assert_called_once_with(None) -def test_side_effect_execute_skips_when_playbook_not_configured( # type: ignore[no-untyped-def] # noqa: ANN201, D103 - caplog, # noqa: ANN001 - _patched_ansible_side_effect, # noqa: ANN001, PT019 +def test_side_effect_execute_skips_when_playbook_not_configured( # noqa: D103 + caplog: pytest.LogCaptureFixture, + _patched_ansible_side_effect: Mock, # noqa: PT019 config_instance: config.Config, -): +) -> None: se = side_effect.SideEffect(config_instance) - se.execute() # type: ignore[no-untyped-call] + se.execute() msg = "Skipping, side effect playbook not configured." assert msg in caplog.text diff --git a/tests/unit/command/test_syntax.py b/tests/unit/command/test_syntax.py index 38a61a9e2..0ec21629e 100644 --- a/tests/unit/command/test_syntax.py +++ b/tests/unit/command/test_syntax.py @@ -27,28 +27,30 @@ if TYPE_CHECKING: + from unittest.mock import MagicMock, Mock + from pytest_mock import MockerFixture from molecule import config @pytest.fixture() -def _patched_ansible_syntax(mocker): # type: ignore[no-untyped-def] # noqa: ANN001, ANN202 +def _patched_ansible_syntax(mocker: MockerFixture) -> MagicMock: return mocker.patch("molecule.provisioner.ansible.Ansible.syntax") # NOTE(retr0h): The use of the `patched_config_validate` fixture, disables # config.Config._validate from executing. Thus preventing odd side-effects # throughout patched.assert_called unit tests. -def test_syntax_execute( # type: ignore[no-untyped-def] # noqa: ANN201, D103 +def test_syntax_execute( # noqa: D103 mocker: MockerFixture, # noqa: ARG001 - caplog, # noqa: ANN001 - _patched_ansible_syntax, # noqa: ANN001, PT019 - patched_config_validate, # noqa: ANN001, ARG001 + caplog: pytest.LogCaptureFixture, + _patched_ansible_syntax: Mock, # noqa: PT019 + patched_config_validate: Mock, # noqa: ARG001 config_instance: config.Config, -): +) -> None: s = syntax.Syntax(config_instance) - s.execute() # type: ignore[no-untyped-call] + s.execute() assert "default" in caplog.text assert "syntax" in caplog.text diff --git a/tests/unit/command/test_verify.py b/tests/unit/command/test_verify.py index b188512ea..4efca710f 100644 --- a/tests/unit/command/test_verify.py +++ b/tests/unit/command/test_verify.py @@ -25,6 +25,10 @@ if TYPE_CHECKING: + from unittest.mock import Mock + + import pytest + from pytest_mock import MockerFixture from molecule import config @@ -33,15 +37,15 @@ # NOTE(retr0h): The use of the `patched_config_validate` fixture, disables # config.Config._validate from executing. Thus preventing odd side-effects # throughout patched.assert_called unit tests. -def test_verify_execute( # type: ignore[no-untyped-def] # noqa: ANN201, D103 +def test_verify_execute( # noqa: D103 mocker: MockerFixture, # noqa: ARG001 - caplog, # noqa: ANN001 - patched_default_verifier, # noqa: ANN001, ARG001 - patched_config_validate, # noqa: ANN001, ARG001 + caplog: pytest.LogCaptureFixture, + patched_default_verifier: Mock, # noqa: ARG001 + patched_config_validate: Mock, # noqa: ARG001 config_instance: config.Config, -): +) -> None: v = verify.Verify(config_instance) - v.execute() # type: ignore[no-untyped-call] + v.execute() assert "default" in caplog.text assert "verify" in caplog.text From 49c4f959688e80d94c3d82b159514186e4665233 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 14:33:51 +0000 Subject: [PATCH 3/3] chore: auto fixes from pre-commit.com hooks --- .config/pydoclint-baseline.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/.config/pydoclint-baseline.txt b/.config/pydoclint-baseline.txt index 395b30a0b..d7f1d76b3 100644 --- a/.config/pydoclint-baseline.txt +++ b/.config/pydoclint-baseline.txt @@ -1,4 +1,3 @@ --------------------- src/molecule/console.py DOC101: Function `to_bool`: Docstring contains fewer arguments than in function signature. DOC103: Function `to_bool`: Docstring arguments are different from function arguments. (Or could be other formatting issues: https://jsh9.github.io/pydoclint/violation_codes.html#notes-on-doc103 ). Arguments in the function signature but not in the docstring: [a: Any].