Source code for dwas.predefined._pytest

from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Any, Sequence

# XXX: All imports here should be done from the top level. If we need it,
#      users might need it
from .. import Step, StepRunner, parametrize, set_defaults

if TYPE_CHECKING:
    from pathlib import Path

LOGGER = logging.getLogger(__name__)


@set_defaults({"dependencies": ["pytest"], "args": []})
class Pytest(Step):
    def __init__(self) -> None:
        self.__name__ = "pytest"

    def gather_artifacts(self, step: StepRunner) -> dict[str, list[Any]]:
        coverage_file = self._get_coverage_file(step)
        if coverage_file is None or not coverage_file.exists():
            return {}

        return {"coverage_files": [str(coverage_file)]}

    def __call__(
        self,
        step: StepRunner,
        args: Sequence[str],
        user_args: Sequence[str] | None,
    ) -> None:
        if user_args is None:
            user_args = []

        step.run(
            ["pytest", *args, *user_args],
            env={"COVERAGE_FILE": str(self._get_coverage_file(step))},
        )

    def _get_coverage_file(self, step: StepRunner) -> Path:
        return step.cache_path / "reports" / "coverage"


[docs] def pytest(*, args: Sequence[str] | None = None) -> Step: """ Run `pytest`_. By default, it will depend on :python:`["pytest"]`, when registered with :py:func:`dwas.register_managed_step`. :param args: arguments to pass to the ``pytest`` invocation. Defaults to :python:`[]`. :return: The step so that you can add additional parameters to it if needed. .. tip:: If you use ``pytest-cov``, it will also automatically expose a ``coverage_files`` :term:`artifact` that can be used by dependent steps, for an example, see :py:func:`coverage` :Examples: For running pytest with the a specific version of python, with your code in the `PYTHONPATH`: .. code-block:: dwas.register_managed_step(dwas.predefined.pytest(), python="3.9") Or, for a more concrete example, across multiple versions of python and testing your installed application: .. code-block:: # Setup a "package" step, to install the source code automatically # for the tests dwas.register_managed_step(dwas.predefined.package()) dwas.register_managed_step( dwas.parametrize("python", ["3.9", "3.10", "3.11"])( dwas.predefined.pytest() ), dependencies=["pytest", "pytest-cov"], requires=["package"], ) Leveraging :py:func:`dwas.parametrize`, this generates 4 different steps: ``pytest``, which is a :term:`step group` which depends on the other ``pytest[3.9]``, ``pytest[3.10]`` and ``pytest[3.11]`` which each run pytest with the given version of python. Similarly, if you wanted to test multiple multiple versions of a python package, with different versions of python: .. code-block:: dwas.register_managed_step( dwas.parametrize("python", ["3.9", "3.10", "3.11"])( dwas.parametrize( "dependencies", [["pytest", "django==3.0"], ["pytest", "django==4.0"]], ids=["django3", "django4"], )(dwas.predefined.pytest()), requires=["package"], ) """ pytest_ = Pytest() if args is not None: pytest_ = parametrize("args", [args])(pytest_) return pytest_