diff --git a/pytest_container/runtime.py b/pytest_container/runtime.py index fcaa01b..6c72333 100644 --- a/pytest_container/runtime.py +++ b/pytest_container/runtime.py @@ -10,7 +10,6 @@ from abc import ABC from abc import abstractmethod from dataclasses import dataclass -from dataclasses import field from os import getenv from pathlib import Path from subprocess import check_output @@ -157,7 +156,7 @@ def __generate_cmp( ) -> Callable[["Version", Any], bool]: def cmp(self: Version, other: Any) -> bool: if not isinstance(other, Version): - return NotImplemented + return NotImplemented # type: ignore if self.major == other.major: if self.minor == other.minor: @@ -180,25 +179,28 @@ def __gt__(self, other: Any) -> bool: return Version.__generate_cmp(lambda m, n: m > n)(self, other) -@dataclass(frozen=True) -class _OciRuntimeBase: - #: command that builds the Dockerfile in the current working directory - build_command: List[str] = field(default_factory=list) - #: the "main" binary of this runtime, e.g. podman or docker - runner_binary: str = "" - _runtime_functional: bool = False - - class OciRuntimeABC(ABC): """The abstract base class defining the interface of a container runtime.""" - @staticmethod - @abstractmethod - def _runtime_error_message() -> str: - """Returns a human readable error message why the runtime does not - function. + def __init__(self, build_command: List[str], runner_binary: str) -> None: + #: command that builds the Dockerfile in the current working directory + self._build_command = build_command + + #: the "main" binary of this runtime, e.g. podman or docker + self._runner_binary: str = runner_binary + + @property + def build_command(self) -> List[str]: + """Command that builds the :file:`Dockerfile` in the current working + directory. """ + return self._build_command + + @property + def runner_binary(self) -> str: + """The "main" binary of this runtime, e.g. podman or docker.""" + return self._runner_binary def get_container_health(self, container_id: str) -> ContainerHealth: """Inspects the running container with the supplied id and returns its current @@ -229,21 +231,9 @@ def supports_healthcheck_inherit_from_base(self) -> bool: """ -class OciRuntimeBase(_OciRuntimeBase, OciRuntimeABC, ToParamMixin): +class OciRuntimeBase(OciRuntimeABC, ToParamMixin): """Base class of the Container Runtimes.""" - def __post_init__(self) -> None: - if not self.build_command or not self.runner_binary: - raise ValueError( - f"build_command ({self.build_command}) or runner_binary " - f"({self.runner_binary}) were not specified" - ) - if not self._runtime_functional: - raise RuntimeError( - f"The runtime {self.__class__.__name__} is not functional: " - + self._runtime_error_message() - ) - @staticmethod def get_image_id_from_iidfile(iidfile_path: str) -> str: """Returns the image id/hash from the iidfile that has been created by @@ -434,7 +424,7 @@ def _get_podman_version(version_stdout: str) -> Version: def _get_buildah_version() -> Version: - version_stdout = LOCALHOST.run_expect([0], "buildah --version").stdout + version_stdout = LOCALHOST.check_output("buildah --version") build_version_begin = "buildah version " if not version_stdout.startswith(build_version_begin): raise RuntimeError( @@ -452,21 +442,12 @@ class PodmanRuntime(OciRuntimeBase): """ - _runtime_functional = LOCALHOST.run("podman ps").succeeded - _buildah_functional = LOCALHOST.run("buildah").succeeded - - @staticmethod - def _runtime_error_message() -> str: - if PodmanRuntime._runtime_functional: - return "" - + def __init__(self) -> None: podman_ps = LOCALHOST.run("podman ps") - assert not podman_ps.succeeded, ( - "podman runtime is not functional, but 'podman ps' succeeded" - ) - return str(podman_ps.stderr) + if not podman_ps.succeeded: + raise RuntimeError(f"`podman ps` failed with {podman_ps.stderr}") - def __init__(self) -> None: + self._buildah_functional = LOCALHOST.run("buildah").succeeded super().__init__( build_command=( ["buildah", "bud", "--layers", "--force-rm"] @@ -474,7 +455,6 @@ def __init__(self) -> None: else ["podman", "build", "--layers", "--force-rm"] ), runner_binary="podman", - _runtime_functional=self._runtime_functional, ) # pragma pylint: disable=used-before-assignment @@ -558,23 +538,14 @@ class DockerRuntime(OciRuntimeBase): """The container runtime using :command:`docker` for building and running containers.""" - _runtime_functional = LOCALHOST.run("docker ps").succeeded - - @staticmethod - def _runtime_error_message() -> str: - if DockerRuntime._runtime_functional: - return "" + def __init__(self) -> None: docker_ps = LOCALHOST.run("docker ps") - assert not docker_ps.succeeded, ( - "docker runtime is not functional, but 'docker ps' succeeded" - ) - return str(docker_ps.stderr) + if not docker_ps.succeeded: + raise RuntimeError(f"`docker ps` failed with {docker_ps.stderr}") - def __init__(self) -> None: super().__init__( build_command=["docker", "build", "--force-rm"], runner_binary="docker", - _runtime_functional=self._runtime_functional, ) @cached_property diff --git a/tests/test_runtime.py b/tests/test_runtime.py index 5ad208d..d87a0ae 100644 --- a/tests/test_runtime.py +++ b/tests/test_runtime.py @@ -5,6 +5,7 @@ import pytest +from pytest_container.runtime import LOCALHOST from pytest_container.runtime import DockerRuntime from pytest_container.runtime import OciRuntimeBase from pytest_container.runtime import PodmanRuntime @@ -36,7 +37,22 @@ def test_runtime_selection( # pylint: disable-next=redefined-outer-name,unused-argument container_runtime_envvar: None, runtime: OciRuntimeBase, + monkeypatch: pytest.MonkeyPatch, ): + class Succeeded: + @property + def succeeded(self) -> bool: + return True + + @property + def rc(self) -> int: + return 0 + + def mock_run(*args, **kwargs): + return Succeeded() + + monkeypatch.setattr(LOCALHOST, "run", mock_run) + assert get_selected_runtime() == runtime