Skip to content
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

Document and type hint commands part 3 #4312

Merged
merged 3 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 0 additions & 41 deletions .config/pydoclint-baseline.txt
Original file line number Diff line number Diff line change
@@ -1,44 +1,3 @@
src/molecule/command/dependency.py
DOC101: Method `Dependency.execute`: Docstring contains fewer arguments than in function signature.
DOC106: Method `Dependency.execute`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature
DOC107: Method `Dependency.execute`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints
DOC103: Method `Dependency.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 `dependency`: Docstring contains fewer arguments than in function signature.
DOC106: Function `dependency`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature
DOC107: Function `dependency`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints
DOC103: Function `dependency`: 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/destroy.py
DOC101: Method `Destroy.execute`: Docstring contains fewer arguments than in function signature.
DOC106: Method `Destroy.execute`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature
DOC107: Method `Destroy.execute`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints
DOC103: Method `Destroy.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 `Destroy.execute` does not have a return section in docstring
DOC101: Function `destroy`: Docstring contains fewer arguments than in function signature.
DOC106: Function `destroy`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature
DOC107: Function `destroy`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints
DOC103: Function `destroy`: 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: , ctx: , driver_name: , parallel: , scenario_name: ].
--------------------
src/molecule/command/drivers.py
DOC101: Function `drivers`: Docstring contains fewer arguments than in function signature.
DOC106: Function `drivers`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature
DOC107: Function `drivers`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints
DOC103: Function `drivers`: 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: , format: ].
--------------------
src/molecule/command/idempotence.py
DOC101: Method `Idempotence.execute`: Docstring contains fewer arguments than in function signature.
DOC106: Method `Idempotence.execute`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature
DOC107: Method `Idempotence.execute`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints
DOC103: Method `Idempotence.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: ].
DOC106: Method `Idempotence._is_idempotent`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature
DOC107: Method `Idempotence._is_idempotent`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints
DOC106: Method `Idempotence._non_idempotent_tasks`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature
DOC107: Method `Idempotence._non_idempotent_tasks`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints
DOC101: Function `idempotence`: Docstring contains fewer arguments than in function signature.
DOC106: Function `idempotence`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature
DOC107: Function `idempotence`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints
DOC103: Function `idempotence`: 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: [ansible_args: , ctx: , scenario_name: ].
--------------------
src/molecule/command/init/base.py
DOC601: Class `Base`: Class docstring contains fewer class attributes than actual class attributes. (Please read https://jsh9.github.io/pydoclint/checking_class_attributes.html on how to correctly document class attributes.)
DOC603: Class `Base`: Class docstring attributes are different from actual class attributes. (Or could be other formatting issues: https://jsh9.github.io/pydoclint/violation_codes.html#notes-on-doc103 ). Attributes in the class definition but not in the docstring: [__metaclass__: ]. (Please read https://jsh9.github.io/pydoclint/checking_class_attributes.html on how to correctly document class attributes.)
Expand Down
27 changes: 20 additions & 7 deletions src/molecule/command/dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@


if TYPE_CHECKING:
from molecule.types import CommandArgs
from molecule.types import CommandArgs, MoleculeArgs


LOG = logging.getLogger(__name__)
Expand All @@ -39,9 +39,14 @@
class Dependency(base.Base):
"""Dependency 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 dependency` and returns None."""
self._config.dependency.execute() # type: ignore[union-attr]
def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
"""Execute the actions necessary to perform a `molecule dependency`.

Args:
action_args: Arguments for this command. Unused.
"""
if self._config.dependency:
self._config.dependency.execute() # type: ignore[no-untyped-call]


@base.click_command_ex()
Expand All @@ -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 dependency(ctx, scenario_name): # type: ignore[no-untyped-def] # pragma: no cover # noqa: ANN001, ANN201
"""Manage the role's dependencies."""
args = ctx.obj.get("args")
def dependency(
ctx: click.Context,
scenario_name: str,
) -> None: # pragma: no cover
"""Manage the role's dependencies.

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}

Expand Down
33 changes: 26 additions & 7 deletions src/molecule/command/destroy.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@


if TYPE_CHECKING:
from molecule.types import CommandArgs
from molecule.types import CommandArgs, MoleculeArgs


LOG = logging.getLogger(__name__)
Expand All @@ -44,14 +44,19 @@
class Destroy(base.Base):
"""Destroy 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 destroy` and returns None."""
def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
"""Execute the actions necessary to perform a `molecule destroy`.

Args:
action_args: Arguments for this command. Unused.
"""
if self._config.command_args.get("destroy") == "never":
msg = "Skipping, '--destroy=never' requested."
LOG.warning(msg)
return

self._config.provisioner.destroy() # type: ignore[union-attr]
if self._config.provisioner:
self._config.provisioner.destroy() # type: ignore[no-untyped-call]
self._config.state.reset()


Expand Down Expand Up @@ -80,9 +85,23 @@ def execute(self, action_args=None): # type: ignore[no-untyped-def] # noqa: AN
default=False,
help="Enable or disable parallel mode. Default is disabled.",
)
def destroy(ctx, scenario_name, driver_name, __all, parallel): # type: ignore[no-untyped-def] # pragma: no cover # noqa: ANN001, ANN201
"""Use the provisioner to destroy the instances."""
args = ctx.obj.get("args")
def destroy(
ctx: click.Context,
scenario_name: str | None,
driver_name: str,
__all: bool, # noqa: FBT001
parallel: bool, # noqa: FBT001
) -> None: # pragma: no cover
"""Use the provisioner to destroy the instances.

Args:
ctx: Click context object holding commandline arguments.
scenario_name: Name of the scenario to target.
driver_name: Molecule driver to use.
__all: Whether molecule should target scenario_name or all scenarios.
parallel: Whether the scenario(s) should be run in parallel mode.
"""
args: MoleculeArgs = ctx.obj.get("args")
subcommand = base._get_subcommand(__name__) # noqa: SLF001
command_args: CommandArgs = {
"parallel": parallel,
Expand Down
12 changes: 10 additions & 2 deletions src/molecule/command/drivers.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,16 @@
default="simple",
help="Change output format. (simple)",
)
def drivers(ctx, format): # type: ignore[no-untyped-def] # pragma: no cover # noqa: ANN001, ANN201, A002, ARG001
"""List drivers."""
def drivers(
ctx: click.Context, # noqa: ARG001
format: str, # noqa: A002
) -> None: # pragma: no cover
"""List drivers.

Args:
ctx: Click context object holding commandline arguments.
format: Output format to use.
"""
drivers = [] # pylint: disable=redefined-outer-name
for driver in api.drivers().values():
description = str(driver)
Expand Down
46 changes: 30 additions & 16 deletions src/molecule/command/idempotence.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@


if TYPE_CHECKING:
from molecule.types import CommandArgs
from molecule.types import CommandArgs, MoleculeArgs


LOG = logging.getLogger(__name__)
Expand All @@ -46,24 +46,29 @@ class Idempotence(base.Base):
the scenario will be considered idempotent.
"""

def execute(self, action_args=None): # type: ignore[no-untyped-def] # noqa: ANN001, ANN201, ARG002
"""Execute the actions necessary to perform a `molecule idempotence` and returns None."""
def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
"""Execute the actions necessary to perform a `molecule idempotence`.

Args:
action_args: Arguments for this command. Unused.
"""
if not self._config.state.converged:
msg = "Instances not converged. Please converge instances first."
util.sysexit_with_message(msg)

output = self._config.provisioner.converge() # type: ignore[union-attr]
if self._config.provisioner:
output = self._config.provisioner.converge() # type: ignore[no-untyped-call]

idempotent = self._is_idempotent(output) # type: ignore[no-untyped-call]
if idempotent:
msg = "Idempotence completed successfully."
LOG.info(msg)
else:
details = "\n".join(self._non_idempotent_tasks(output)) # type: ignore[no-untyped-call]
msg = f"Idempotence test failed because of the following tasks:\n{details}"
util.sysexit_with_message(msg)
idempotent = self._is_idempotent(output)
if idempotent:
msg = "Idempotence completed successfully."
LOG.info(msg)
else:
details = "\n".join(self._non_idempotent_tasks(output))
msg = f"Idempotence test failed because of the following tasks:\n{details}"
util.sysexit_with_message(msg)

def _is_idempotent(self, output): # type: ignore[no-untyped-def] # noqa: ANN001, ANN202
def _is_idempotent(self, output: str) -> bool:
"""Parse the output of the provisioning for changed and returns a bool.

Args:
Expand All @@ -80,7 +85,7 @@ def _is_idempotent(self, output): # type: ignore[no-untyped-def] # noqa: ANN00

return not bool(changed)

def _non_idempotent_tasks(self, output): # type: ignore[no-untyped-def] # noqa: ANN001, ANN202
def _non_idempotent_tasks(self, output: str) -> list[str]:
"""Parse the output to identify the non idempotent tasks.

Args:
Expand Down Expand Up @@ -120,12 +125,21 @@ def _non_idempotent_tasks(self, output): # type: ignore[no-untyped-def] # noqa
help=f"Name of the scenario to target. ({base.MOLECULE_DEFAULT_SCENARIO_NAME})",
)
@click.argument("ansible_args", nargs=-1, type=click.UNPROCESSED)
def idempotence(ctx, scenario_name, ansible_args): # type: ignore[no-untyped-def] # pragma: no cover # noqa: ANN001, ANN201
def idempotence(
ctx: click.Context,
scenario_name: str,
ansible_args: tuple[str, ...],
) -> None: # pragma: no cover
"""Use the provisioner to configure the instances.

After parse the output to determine idempotence.

Args:
ctx: Click context object holding commandline arguments.
scenario_name: Name of the scenario to target.
ansible_args: Arguments to forward to Ansible.
"""
args = ctx.obj.get("args")
args: MoleculeArgs = ctx.obj.get("args")
subcommand = base._get_subcommand(__name__) # noqa: SLF001
command_args: CommandArgs = {"subcommand": subcommand}

Expand Down
16 changes: 10 additions & 6 deletions tests/unit/command/test_dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@


if TYPE_CHECKING:
from unittest.mock import Mock

import pytest

from pytest_mock import MockerFixture

from molecule import config
Expand All @@ -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_dependency_execute( # type: ignore[no-untyped-def] # noqa: ANN201, D103
def test_dependency_execute( # noqa: D103
mocker: MockerFixture, # noqa: ARG001
caplog, # noqa: ANN001
patched_ansible_galaxy, # noqa: ANN001
patched_config_validate, # noqa: ANN001, ARG001
caplog: pytest.LogCaptureFixture,
patched_ansible_galaxy: Mock,
patched_config_validate: Mock, # noqa: ARG001
config_instance: config.Config,
):
) -> None:
d = dependency.Dependency(config_instance)
d.execute() # type: ignore[no-untyped-call]
d.execute()

patched_ansible_galaxy.assert_called_once_with()

Expand Down
30 changes: 16 additions & 14 deletions tests/unit/command/test_destroy.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,34 +27,36 @@


if TYPE_CHECKING:
from unittest.mock import MagicMock, Mock

from pytest_mock import MockerFixture

from molecule import config


@pytest.fixture()
def _patched_ansible_destroy(mocker): # type: ignore[no-untyped-def] # noqa: ANN001, ANN202
def _patched_ansible_destroy(mocker: MockerFixture) -> MagicMock:
return mocker.patch("molecule.provisioner.ansible.Ansible.destroy")


@pytest.fixture()
def _patched_destroy_setup(mocker): # type: ignore[no-untyped-def] # noqa: ANN001, ANN202
def _patched_destroy_setup(mocker: MockerFixture) -> MagicMock:
return mocker.patch("molecule.command.destroy.Destroy._setup")


# 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.
@pytest.mark.skip(reason="destroy not running for delegated")
def test_destroy_execute( # type: ignore[no-untyped-def] # noqa: ANN201, D103
def test_destroy_execute( # noqa: D103
mocker: MockerFixture, # noqa: ARG001
caplog, # noqa: ANN001
patched_config_validate, # noqa: ANN001, ARG001
_patched_ansible_destroy, # noqa: ANN001, PT019
caplog: pytest.LogCaptureFixture,
patched_config_validate: Mock, # noqa: ARG001
_patched_ansible_destroy: Mock, # noqa: PT019
config_instance: config.Config,
):
) -> None:
d = destroy.Destroy(config_instance)
d.execute() # type: ignore[no-untyped-call]
d.execute()

assert "destroy" in caplog.text

Expand All @@ -71,16 +73,16 @@ def test_destroy_execute( # type: ignore[no-untyped-def] # noqa: ANN201, D103
["command_driver_delegated_section_data"], # noqa: PT007
indirect=True,
)
def test_execute_skips_when_destroy_strategy_is_never( # type: ignore[no-untyped-def] # noqa: ANN201, D103
_patched_destroy_setup, # noqa: ANN001, PT019
caplog, # noqa: ANN001
_patched_ansible_destroy, # noqa: ANN001, PT019
def test_execute_skips_when_destroy_strategy_is_never( # noqa: D103
_patched_destroy_setup: Mock, # noqa: PT019
caplog: pytest.LogCaptureFixture,
_patched_ansible_destroy: Mock, # noqa: PT019
config_instance: config.Config,
):
) -> None:
config_instance.command_args = {"destroy": "never"}

d = destroy.Destroy(config_instance)
d.execute() # type: ignore[no-untyped-call]
d.execute()

msg = "Skipping, '--destroy=never' requested."
assert msg in caplog.text
Expand Down