Skip to content

Commit

Permalink
Document and type hint commands part 2 (#4311)
Browse files Browse the repository at this point in the history
Covers commands check through create
  • Loading branch information
Qalthos authored Oct 29, 2024
1 parent 5f05fe6 commit 42c1c1b
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 88 deletions.
30 changes: 0 additions & 30 deletions .config/pydoclint-baseline.txt
Original file line number Diff line number Diff line change
@@ -1,33 +1,3 @@
src/molecule/command/cleanup.py
DOC101: Method `Cleanup.execute`: Docstring contains fewer arguments than in function signature.
DOC106: Method `Cleanup.execute`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature
DOC107: Method `Cleanup.execute`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints
DOC103: Method `Cleanup.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 `Cleanup.execute` does not have a return section in docstring
DOC101: Function `cleanup`: Docstring contains fewer arguments than in function signature.
DOC106: Function `cleanup`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature
DOC107: Function `cleanup`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints
DOC103: Function `cleanup`: 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/converge.py
DOC101: Method `Converge.execute`: Docstring contains fewer arguments than in function signature.
DOC103: Method `Converge.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: list[str] | None].
DOC101: Function `converge`: Docstring contains fewer arguments than in function signature.
DOC106: Function `converge`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature
DOC107: Function `converge`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints
DOC103: Function `converge`: 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/create.py
DOC101: Method `Create.execute`: Docstring contains fewer arguments than in function signature.
DOC106: Method `Create.execute`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature
DOC107: Method `Create.execute`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints
DOC103: Method `Create.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 `Create.execute` does not have a return section in docstring
DOC101: Function `create`: Docstring contains fewer arguments than in function signature.
DOC106: Function `create`: The option `--arg-type-hints-in-signature` is `True` but there are no argument type hints in the signature
DOC107: Function `create`: The option `--arg-type-hints-in-signature` is `True` but not all args in the signature have type hints
DOC103: Function `create`: 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: , driver_name: , scenario_name: ].
--------------------
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
Expand Down
9 changes: 3 additions & 6 deletions src/molecule/command/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,11 @@
class Check(base.Base):
"""Check Command Class."""

def execute(
self,
action_args: list[str] | None = None, # noqa: ARG002
) -> None:
def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
"""Execute the actions necessary to perform a `molecule check`.
Args:
action_args: Molecule cli arguments. Unused.
action_args: Arguments for this command. Unused.
"""
if self._config.provisioner is not None:
self._config.provisioner.check() # type: ignore[no-untyped-call]
Expand Down Expand Up @@ -78,7 +75,7 @@ def check( # pragma: no cover
Args:
ctx: Click context object holding commandline arguments.
scenario_name: Name of the scenario to run.
scenario_name: Name of the scenario to target.
parallel: Whether the scenario(s) should be run in parallel.
"""
args: MoleculeArgs = ctx.obj.get("args")
Expand Down
32 changes: 22 additions & 10 deletions src/molecule/command/cleanup.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,14 +39,19 @@
class Cleanup(base.Base):
"""Cleanup Command Class."""

def execute(self, action_args=None): # type: ignore[no-untyped-def] # noqa: ANN001, ANN201, ARG002
"""Execute the actions necessary to cleanup the instances and returns None."""
if not self._config.provisioner.playbooks.cleanup: # type: ignore[union-attr]
msg = "Skipping, cleanup playbook not configured."
LOG.warning(msg)
return
def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
"""Execute the actions necessary to cleanup the instances.
self._config.provisioner.cleanup() # type: ignore[union-attr]
Args:
action_args: Arguments for this command. Unused.
"""
if self._config.provisioner:
if not self._config.provisioner.playbooks.cleanup:
msg = "Skipping, cleanup playbook not configured."
LOG.warning(msg)
return

self._config.provisioner.cleanup() # type: ignore[no-untyped-call]


@base.click_command_ex()
Expand All @@ -57,12 +62,19 @@ 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 cleanup(ctx, scenario_name="default"): # type: ignore[no-untyped-def] # pragma: no cover # noqa: ANN001, ANN201
def cleanup(
ctx: click.Context,
scenario_name: str = "default",
) -> None: # pragma: no cover
"""Use the provisioner to cleanup any changes.
Any changes made to external systems during the stages of testing.
Args:
ctx: Click context object holding commandline arguments.
scenario_name: Name of the scenario to target.
"""
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
29 changes: 22 additions & 7 deletions src/molecule/command/converge.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 @@ -40,9 +40,14 @@ class Converge(base.Base):
"""Converge Command Class."""

def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
"""Execute the actions necessary to perform a `molecule converge` and returns None."""
self._config.provisioner.converge() # type: ignore[union-attr]
self._config.state.change_state("converged", True) # noqa: FBT003
"""Execute the actions necessary to perform a `molecule converge`.
Args:
action_args: Arguments for this command. Unused.
"""
if self._config.provisioner:
self._config.provisioner.converge() # type: ignore[no-untyped-call]
self._config.state.change_state("converged", value=True)


@base.click_command_ex()
Expand All @@ -54,9 +59,19 @@ def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
help=f"Name of the scenario to target. ({base.MOLECULE_DEFAULT_SCENARIO_NAME})",
)
@click.argument("ansible_args", nargs=-1, type=click.UNPROCESSED)
def converge(ctx, scenario_name, ansible_args): # type: ignore[no-untyped-def] # pragma: no cover # noqa: ANN001, ANN201
"""Use the provisioner to configure instances (dependency, create, prepare converge)."""
args = ctx.obj.get("args")
def converge(
ctx: click.Context,
scenario_name: str,
ansible_args: tuple[str],
) -> None: # pragma: no cover
"""Use the provisioner to configure instances (dependency, create, prepare converge).
Args:
ctx: Click context object holding commandline arguments.
scenario_name: Name of the scenario to target.
ansible_args: Arguments to forward to Ansible.
"""
args: MoleculeArgs = ctx.obj.get("args")
subcommand = base._get_subcommand(__name__) # noqa: SLF001
command_args: CommandArgs = {"subcommand": subcommand}

Expand Down
31 changes: 23 additions & 8 deletions src/molecule/command/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@


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


LOG = logging.getLogger(__name__)
Expand All @@ -41,18 +41,23 @@
class Create(base.Base):
"""Create 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 create` and returns None."""
def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
"""Execute the actions necessary to perform a `molecule create`.
Args:
action_args: Arguments for this command. Unused.
"""
self._config.state.change_state("driver", self._config.driver.name)

if self._config.state.created:
msg = "Skipping, instances already created."
LOG.warning(msg)
return

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

self._config.state.change_state("created", True) # noqa: FBT003
self._config.state.change_state("created", value=True)


@base.click_command_ex()
Expand All @@ -69,9 +74,19 @@ def execute(self, action_args=None): # type: ignore[no-untyped-def] # noqa: AN
type=click.Choice([str(s) for s in drivers()]),
help=f"Name of driver to use. ({DEFAULT_DRIVER})",
)
def create(ctx, scenario_name, driver_name): # type: ignore[no-untyped-def] # pragma: no cover # noqa: ANN001, ANN201
"""Use the provisioner to start the instances."""
args = ctx.obj.get("args")
def create(
ctx: click.Context,
scenario_name: str,
driver_name: str,
) -> None: # pragma: no cover
"""Use the provisioner to start the instances.
Args:
ctx: Click context object holding commandline arguments.
scenario_name: Name of the scenario to target.
driver_name: Name of the Molecule driver to use.
"""
args: MoleculeArgs = ctx.obj.get("args")
subcommand = base._get_subcommand(__name__) # noqa: SLF001
command_args: CommandArgs = {"subcommand": subcommand, "driver_name": driver_name}

Expand Down
4 changes: 2 additions & 2 deletions src/molecule/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class PlatformData(TypedDict):
name: str


class PlaybookData(TypedDict):
class PlaybookData(TypedDict, total=False):
"""Playbooks for a scenario.
Attributes:
Expand All @@ -93,7 +93,7 @@ class PlaybookData(TypedDict):
verify: str


class ProvisionerData(TypedDict):
class ProvisionerData(TypedDict, total=False):
"""Molecule provisioner configuration.
Attributes:
Expand Down
33 changes: 20 additions & 13 deletions tests/unit/command/test_cleanup.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,23 @@


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_cleanup_data(): # type: ignore[no-untyped-def] # noqa: ANN202
def _command_provisioner_section_with_cleanup_data() -> (
dict[Literal["provisioner"], ProvisionerData]
):
return {"provisioner": {"name": "ansible", "playbooks": {"cleanup": "cleanup.yml"}}}


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


Expand All @@ -51,31 +58,31 @@ def _patched_ansible_cleanup(mocker): # type: ignore[no-untyped-def] # noqa: A
["_command_provisioner_section_with_cleanup_data"], # noqa: PT007
indirect=True,
)
def test_cleanup_execute( # type: ignore[no-untyped-def] # noqa: ANN201, D103
def test_cleanup_execute( # noqa: D103
mocker: MockerFixture, # noqa: ARG001
_patched_ansible_cleanup, # noqa: ANN001, PT019
caplog, # noqa: ANN001
patched_config_validate, # noqa: ANN001, ARG001
_patched_ansible_cleanup: 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, "cleanup.yml") # noqa: PTH118
util.write_file(pb, "")

cu = cleanup.Cleanup(config_instance)
cu.execute() # type: ignore[no-untyped-call]
cu.execute()

assert "cleanup" in caplog.text

_patched_ansible_cleanup.assert_called_once_with()


def test_cleanup_execute_skips_when_playbook_not_configured( # type: ignore[no-untyped-def] # noqa: ANN201, D103
caplog, # noqa: ANN001
_patched_ansible_cleanup, # noqa: ANN001, PT019
def test_cleanup_execute_skips_when_playbook_not_configured( # noqa: D103
caplog: pytest.LogCaptureFixture,
_patched_ansible_cleanup: Mock, # noqa: PT019
config_instance: config.Config,
):
) -> None:
cu = cleanup.Cleanup(config_instance)
cu.execute() # type: ignore[no-untyped-call]
cu.execute()

msg = "Skipping, cleanup playbook not configured."
assert msg in caplog.text
Expand Down
26 changes: 14 additions & 12 deletions tests/unit/command/test_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,31 @@


if TYPE_CHECKING:
from unittest.mock import MagicMock, Mock

from pytest_mock import MockerFixture

from molecule import config


@pytest.fixture()
def _patched_create_setup(mocker): # type: ignore[no-untyped-def] # noqa: ANN001, ANN202
def _patched_create_setup(mocker: MockerFixture) -> MagicMock:
return mocker.patch("molecule.command.create.Create._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="create not running for delegated")
def test_create_execute( # type: ignore[no-untyped-def] # noqa: ANN201, D103
def test_create_execute( # noqa: D103
mocker: MockerFixture, # noqa: ARG001
caplog, # noqa: ANN001
command_patched_ansible_create, # noqa: ANN001
patched_config_validate, # noqa: ANN001, ARG001
caplog: pytest.LogCaptureFixture,
command_patched_ansible_create: Mock,
patched_config_validate: Mock, # noqa: ARG001
config_instance: config.Config,
):
) -> None:
c = create.Create(config_instance)
c.execute() # type: ignore[no-untyped-call]
c.execute()

assert "default" in caplog.text
assert "converge" in caplog.text
Expand All @@ -62,14 +64,14 @@ def test_create_execute( # type: ignore[no-untyped-def] # noqa: ANN201, D103


@pytest.mark.skip(reason="create not running for delegated")
def test_execute_skips_when_instances_already_created( # type: ignore[no-untyped-def] # noqa: ANN201, D103
caplog, # noqa: ANN001
command_patched_ansible_create, # noqa: ANN001
def test_execute_skips_when_instances_already_created( # noqa: D103
caplog: pytest.LogCaptureFixture,
command_patched_ansible_create: Mock,
config_instance: config.Config,
):
) -> None:
config_instance.state.change_state("created", True) # noqa: FBT003
c = create.Create(config_instance)
c.execute() # type: ignore[no-untyped-call]
c.execute()

msg = "Skipping, instances already created."
assert msg in caplog.text
Expand Down

0 comments on commit 42c1c1b

Please sign in to comment.