Skip to content

Commit

Permalink
Provision: ignore other test environments (#2865)
Browse files Browse the repository at this point in the history
Fix #2862
  • Loading branch information
masenf committed Jan 16, 2023
1 parent a2222e9 commit e4c65bb
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 7 deletions.
4 changes: 4 additions & 0 deletions docs/changelog/2862.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
The provision environment (``.tox``) will never inherit from ``testenv``.
During provisioning, other test environments are not processed, allowing the
use of keys and values that may be registered by later tox version or
provisioned plugins - by :user:`masenf`.
7 changes: 7 additions & 0 deletions docs/upgrading.rst
Original file line number Diff line number Diff line change
Expand Up @@ -317,3 +317,10 @@ version is above the version this feature was added to it, for example for setup
[testenv:dev]
deps = setuptools>=64
package = editable
Provisioning environment
------------------------

The provisioning environment is triggered when ``minversion`` or ``requires`` are specified and the current environment
does not satisfy the requirement. In tox 4, the provisioning environment (``.tox`` by default) must be explicitly
configured and will not inherit values from ``[testenv]`` section.
3 changes: 2 additions & 1 deletion src/tox/provision.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ def add_tox_requires_min_version(reqs: list[Requirement]) -> list[Requirement]:
recreate=state.conf.options.recreate and not state.conf.options.no_recreate_provision,
)
provision_tox_env: str = state.conf.core["provision_tox_env"]
state.envs._mark_provision(bool(missing), provision_tox_env, loader)
state.conf.memory_seed_loaders[provision_tox_env].append(loader)
state.envs._mark_provision(bool(missing), provision_tox_env)

from tox.plugin.manager import MANAGER

Expand Down
13 changes: 7 additions & 6 deletions src/tox/session/env_select.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
from tox.tox_env.register import REGISTER
from tox.tox_env.runner import RunToxEnv

from ..config.loader.memory import MemoryLoader
from ..config.types import EnvList
from ..report import HandledError
from ..tox_env.errors import Skip
Expand Down Expand Up @@ -134,7 +133,7 @@ def __init__(self, state: State) -> None:
self._manager = MANAGER
self._log_handler = self._state._options.log_handler
self._journal = self._state._journal
self._provision: None | tuple[bool, str, MemoryLoader] = None
self._provision: None | tuple[bool, str] = None

self._state.conf.core.add_config("labels", Dict[str, EnvList], {}, "core labels")
tox_env_filter_regex = getattr(state.conf.options, "skip_env", "").strip()
Expand Down Expand Up @@ -242,10 +241,12 @@ def _finalize_config(self) -> None:

def _build_run_env(self, name: str) -> RunToxEnv | None:
if self._provision is not None and self._provision[0] is False and name == self._provision[1]:
# ignore provision env unless this is a provision run
return None
if self._provision is not None and self._provision[0] and name != self._provision[1]:
# ignore other envs when this is a provision run
return None
env_conf = self._state.conf.get_env(name, package=False)
if self._provision is not None and self._provision[1] == name:
env_conf.loaders.insert(0, self._provision[2])
desc = "the tox execute used to evaluate this environment"
env_conf.add_config(keys="runner", desc=desc, of_type=str, default=self._state.conf.options.default_runner)
runner = REGISTER.runner(cast(str, env_conf["runner"]))
Expand Down Expand Up @@ -367,8 +368,8 @@ def ensure_only_run_env_is_active(self) -> None:
if invalid:
raise HandledError(f"cannot run packaging environment(s) {','.join(invalid)}")

def _mark_provision(self, on: bool, provision_tox_env: str, loader: MemoryLoader) -> None:
self._provision = on, provision_tox_env, loader
def _mark_provision(self, on: bool, provision_tox_env: str) -> None:
self._provision = on, provision_tox_env


__all__ = [
Expand Down
27 changes: 27 additions & 0 deletions tests/demo_pkg_inline/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,38 @@
version = "1.0.0"
dist_info = "{}-{}.dist-info".format(name, version)
logic = "{}/__init__.py".format(name)
plugin = "{}/example_plugin.py".format(name)
entry_points = "{}/entry_points.txt".format(dist_info)
metadata = "{}/METADATA".format(dist_info)
wheel = "{}/WHEEL".format(dist_info)
record = "{}/RECORD".format(dist_info)
content = {
logic: "def do():\n print('greetings from {}')".format(name),
plugin: dedent(
"""
try:
from tox.plugin import impl
from tox.tox_env.python.virtual_env.runner import VirtualEnvRunner
from tox.tox_env.register import ToxEnvRegister
except ImportError:
pass
else:
class ExampleVirtualEnvRunner(VirtualEnvRunner):
@staticmethod
def id() -> str:
return "example"
@impl
def tox_register_tox_env(register: ToxEnvRegister) -> None:
register.add_run_env(ExampleVirtualEnvRunner)
""",
),
entry_points: dedent(
"""
[tox]
example = {}.example_plugin""".format(
name,
),
),
metadata: """
Metadata-Version: 2.1
Name: {}
Expand Down
25 changes: 25 additions & 0 deletions tests/test_provision.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,28 @@ def test_provision_no_recreate_json(tox_project: ToxProjectCreator) -> None:
with (project.path / "out.json").open() as file_handler:
requires = json.load(file_handler)
assert requires == {"minversion": None, "requires": ["p", "tox"]}


@pytest.mark.integration()
@pytest.mark.usefixtures("_pypi_index_self")
@pytest.mark.parametrize("plugin_testenv", ["testenv", "testenv:a"])
def test_provision_plugin_runner(tox_project: ToxProjectCreator, tmp_path: Path, plugin_testenv: str) -> None:
"""Ensure that testenv runner doesn't affect the provision env."""
log = tmp_path / "out.log"
proj = tox_project({"tox.ini": f"[tox]\nrequires=demo-pkg-inline\n[{plugin_testenv}]\nrunner=example"})
result_first = proj.run("r", "-e", "py", "--result-json", str(log))
result_first.assert_success()
prov_msg = (
f"ROOT: will run in automatically provisioned tox, host {sys.executable} is missing"
f" [requires (has)]: demo-pkg-inline"
)
assert prov_msg in result_first.out


@pytest.mark.integration()
def test_provision_plugin_runner_in_provision(tox_project: ToxProjectCreator, tmp_path: Path) -> None:
"""Ensure that provision environment can be explicitly configured."""
log = tmp_path / "out.log"
proj = tox_project({"tox.ini": "[tox]\nrequires=somepkg123xyz\n[testenv:.tox]\nrunner=example"})
with pytest.raises(KeyError, match="example"):
proj.run("r", "-e", "py", "--result-json", str(log))

0 comments on commit e4c65bb

Please sign in to comment.