Skip to content

Commit

Permalink
Add tox_on_install and tox_env_teardown plugin hooks (#2687)
Browse files Browse the repository at this point in the history
  • Loading branch information
gaborbernat committed Dec 13, 2022
1 parent 2df6015 commit dbeec44
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 7 deletions.
2 changes: 2 additions & 0 deletions docs/changelog/2687.feature.rst
@@ -0,0 +1,2 @@
Add :meth:`tox_on_install <tox.plugin.spec.tox_on_install>` and
:meth:`tox_env_teardown <tox.plugin.spec.tox_env_teardown>` plugin hooks - by :user:`gaborbernat`.
7 changes: 7 additions & 0 deletions src/tox/plugin/manager.py
Expand Up @@ -3,6 +3,7 @@

from pathlib import Path
from types import ModuleType
from typing import Any

import pluggy

Expand Down Expand Up @@ -76,6 +77,12 @@ def tox_before_run_commands(self, tox_env: ToxEnv) -> None:
def tox_after_run_commands(self, tox_env: ToxEnv, exit_code: int, outcomes: list[Outcome]) -> None:
self.manager.hook.tox_after_run_commands(tox_env=tox_env, exit_code=exit_code, outcomes=outcomes)

def tox_on_install(self, tox_env: ToxEnv, arguments: Any, section: str, of_type: str) -> None:
self.manager.hook.tox_on_install(tox_env=tox_env, arguments=arguments, section=section, of_type=of_type)

def tox_env_teardown(self, tox_env: ToxEnv) -> None:
self.manager.hook.tox_env_teardown(tox_env=tox_env)

def load_plugins(self, path: Path) -> None:
for _plugin in self.manager.get_plugins(): # make sure we start with a clean state, repeated in memory run
self.manager.unregister(_plugin)
Expand Down
23 changes: 23 additions & 0 deletions src/tox/plugin/spec.py
Expand Up @@ -83,6 +83,27 @@ def tox_after_run_commands(tox_env: ToxEnv, exit_code: int, outcomes: list[Outco
"""


@_spec
def tox_on_install(tox_env: ToxEnv, arguments: Any, section: str, of_type: str) -> None: # noqa: U100
"""
Called before executing an installation command.
:param tox_env: the tox environment where the command runs in
:param arguments: installation arguments
:param section: section of the installation
:param of_type: type of the installation
"""


@_spec
def tox_env_teardown(tox_env: ToxEnv) -> None: # noqa: U100
"""
Called before executing an installation command.
:param tox_env: the tox environment
"""


__all__ = [
"NAME",
"tox_register_tox_env",
Expand All @@ -91,4 +112,6 @@ def tox_after_run_commands(tox_env: ToxEnv, exit_code: int, outcomes: list[Outco
"tox_add_env_config",
"tox_before_run_commands",
"tox_after_run_commands",
"tox_on_install",
"tox_env_teardown",
]
9 changes: 9 additions & 0 deletions src/tox/tox_env/api.py
Expand Up @@ -89,6 +89,12 @@ def executor(self) -> Execute:
def installer(self) -> Installer[Any]:
raise NotImplementedError

def _install(self, arguments: Any, section: str, of_type: str) -> None:
from tox.plugin.manager import MANAGER

MANAGER.tox_on_install(self, arguments, section, of_type)
self.installer.install(arguments, section, of_type)

def __repr__(self) -> str:
return f"{self.__class__.__name__}(name={self.conf['env_name']})"

Expand Down Expand Up @@ -251,6 +257,9 @@ def teardown(self) -> None:
try:
self._teardown()
finally:
from tox.plugin.manager import MANAGER

MANAGER.tox_env_teardown(self)
self._run_state["teardown"] = True

def _teardown(self) -> None: # noqa: B027 # empty abstract base class
Expand Down
2 changes: 1 addition & 1 deletion src/tox/tox_env/python/package.py
Expand Up @@ -55,7 +55,7 @@ def __init__(self, create_args: ToxEnvCreateArgs) -> None:
def _setup_env(self) -> None:
"""setup the tox environment"""
super()._setup_env()
self.installer.install(self.requires(), PythonPackageToxEnv.__name__, "requires")
self._install(self.requires(), PythonPackageToxEnv.__name__, "requires")

@abstractmethod
def requires(self) -> tuple[Requirement, ...] | PythonDeps:
Expand Down
2 changes: 1 addition & 1 deletion src/tox/tox_env/python/runner.py
Expand Up @@ -100,7 +100,7 @@ def _setup_env(self) -> None:

def _install_deps(self) -> None:
requirements_file: PythonDeps = self.conf["deps"]
self.installer.install(requirements_file, PythonRun.__name__, "deps")
self._install(requirements_file, PythonRun.__name__, "deps")

def _build_packages(self) -> list[Package]:
package_env = self.package_env
Expand Down
6 changes: 3 additions & 3 deletions src/tox/tox_env/python/virtual_env/package/pyproject.py
Expand Up @@ -148,13 +148,13 @@ def _setup_env(self) -> None:
if not self._frontend.optional_hooks["build_editable"]:
raise BuildEditableNotSupported
build_requires = self._frontend.get_requires_for_build_editable().requires
self.installer.install(build_requires, PythonPackageToxEnv.__name__, "requires_for_build_editable")
self._install(build_requires, PythonPackageToxEnv.__name__, "requires_for_build_editable")
if "wheel" in self.builds:
build_requires = self._frontend.get_requires_for_build_wheel().requires
self.installer.install(build_requires, PythonPackageToxEnv.__name__, "requires_for_build_wheel")
self._install(build_requires, PythonPackageToxEnv.__name__, "requires_for_build_wheel")
if "sdist" in self.builds or "external" in self.builds:
build_requires = self._frontend.get_requires_for_build_sdist().requires
self.installer.install(build_requires, PythonPackageToxEnv.__name__, "requires_for_build_sdist")
self._install(build_requires, PythonPackageToxEnv.__name__, "requires_for_build_sdist")

def _teardown(self) -> None:
executor = self._frontend.backend_executor
Expand Down
2 changes: 1 addition & 1 deletion src/tox/tox_env/runner.py
Expand Up @@ -170,7 +170,7 @@ def _register_package_conf(self) -> bool:

def _setup_pkg(self) -> None:
self._packages = self._build_packages()
self.installer.install(self._packages, RunToxEnv.__name__, "package")
self._install(self._packages, RunToxEnv.__name__, "package")
self._handle_journal_package(self.journal, self._packages)

@staticmethod
Expand Down
20 changes: 19 additions & 1 deletion tests/plugin/test_plugin.py
Expand Up @@ -3,6 +3,7 @@
import logging
import os
import sys
from typing import Any
from unittest.mock import patch

import pytest
Expand Down Expand Up @@ -47,6 +48,14 @@ def tox_before_run_commands(tox_env: ToxEnv) -> None:
assert isinstance(tox_env, ToxEnv)
logging.warning("tox_before_run_commands")

@impl
def tox_on_install(tox_env: ToxEnv, arguments: Any, section: str, of_type: str) -> None:
assert isinstance(tox_env, ToxEnv)
assert arguments is not None
assert isinstance(section, str)
assert isinstance(of_type, str)
logging.warning(f"tox_on_install {section} {of_type}")

@impl
def tox_after_run_commands(tox_env: ToxEnv, exit_code: int, outcomes: list[Outcome]) -> None:
assert isinstance(tox_env, ToxEnv)
Expand All @@ -55,8 +64,13 @@ def tox_after_run_commands(tox_env: ToxEnv, exit_code: int, outcomes: list[Outco
assert all(isinstance(i, Outcome) for i in outcomes)
logging.warning("tox_after_run_commands")

@impl
def tox_env_teardown(tox_env: ToxEnv) -> None:
assert isinstance(tox_env, ToxEnv)
logging.warning("teardown")

plugins = tuple(v for v in locals().values() if callable(v) and hasattr(v, "tox_impl"))
assert len(plugins) == 6
assert len(plugins) == 8
register_inline_plugin(mocker, *plugins)
project = tox_project({"tox.ini": "[testenv]\npackage=skip\ncommands=python -c 'print(1)'"})
result = project.run("r", "-e", "a,b")
Expand All @@ -68,15 +82,19 @@ def tox_after_run_commands(tox_env: ToxEnv, exit_code: int, outcomes: list[Outco
"ROOT: tox_add_core_config",
"a: tox_add_env_config",
"b: tox_add_env_config",
"a: tox_on_install PythonRun deps",
"a: tox_before_run_commands",
f"a: commands[0]> python -c {cmd}",
mocker.ANY, # output a
"a: tox_after_run_commands",
"a: teardown",
mocker.ANY, # report finished A
"b: tox_on_install PythonRun deps",
"b: tox_before_run_commands",
f"b: commands[0]> python -c {cmd}",
mocker.ANY, # output b
"b: tox_after_run_commands",
"b: teardown",
mocker.ANY, # report a
mocker.ANY, # report b
mocker.ANY, # overall report
Expand Down

0 comments on commit dbeec44

Please sign in to comment.