diff options
author | Miro Hrončok <miro@hroncok.cz> | 2019-11-11 11:45:29 +0100 |
---|---|---|
committer | Bernát Gábor <bgabor8@bloomberg.net> | 2019-11-11 10:45:29 +0000 |
commit | 0a44dccb5bcc6c133c622d65e143c83a652c5fa9 (patch) | |
tree | b58432561fce936431f79c7a876971bf4d0af660 | |
parent | 3980da78d1e9fb8f618a12c6b90953c1aa25b8ab (diff) | |
download | tox-git-0a44dccb5bcc6c133c622d65e143c83a652c5fa9.tar.gz |
Use _TOX_PARALLEL_ENV environment variable to control the behav… (#1446)
...but keep the TOX_PARALLEL_ENV variable for others to consume.
The problem with using environment variables to let tox know it's being run
as a parallel worker is the following:
Either you don't pass the environment variable, but the test frameworks and
test suites are unaware that tox is running in parallel.
This makes it hard for cases like pytest-django:
https://github.com/tox-dev/tox/pull/1139
Or you pass the environment variable, but it makes tox invoked within tox
think it is a parallel worker even when it isn't.
This makes it hard to test various tox plugins that invoke tox in their
integration test suite:
Fixes https://github.com/tox-dev/tox/issues/1444
By introducing two variables we can use one ("private", _TOX_PARALLEL_ENV) to
control the behavior of tox (and don't pass it) and another ("public",
TOX_PARALLEL_ENV) to let the tests and test frameworks know tox is running
in parallel (and pass it by default).
The integration test is a tad complicated invoking subprocess.Popen instead
of subprocess.run, to support Python 2.7 and 3.4.
-rw-r--r-- | docs/changelog/1444.bugfix.rst | 5 | ||||
-rw-r--r-- | src/tox/_pytestplugin.py | 10 | ||||
-rw-r--r-- | src/tox/config/__init__.py | 9 | ||||
-rw-r--r-- | src/tox/config/parallel.py | 3 | ||||
-rw-r--r-- | src/tox/session/__init__.py | 6 | ||||
-rw-r--r-- | src/tox/session/commands/run/parallel.py | 6 | ||||
-rw-r--r-- | src/tox/venv.py | 4 | ||||
-rw-r--r-- | tests/integration/test_parallel_inception.py | 52 | ||||
-rw-r--r-- | tests/unit/config/test_config.py | 6 |
9 files changed, 85 insertions, 16 deletions
diff --git a/docs/changelog/1444.bugfix.rst b/docs/changelog/1444.bugfix.rst new file mode 100644 index 00000000..0c9d9cf9 --- /dev/null +++ b/docs/changelog/1444.bugfix.rst @@ -0,0 +1,5 @@ +Fix nested tox execution in the parallel mode by separating the environment +variable that let's tox know it is invoked in the parallel mode +(``_TOX_PARALLEL_ENV``) from the variable that informs the tests that tox is +running in parallel mode (``TOX_PARALLEL_ENV``). +— by :user:`hroncok` diff --git a/src/tox/_pytestplugin.py b/src/tox/_pytestplugin.py index e0381299..b22f0732 100644 --- a/src/tox/_pytestplugin.py +++ b/src/tox/_pytestplugin.py @@ -17,7 +17,8 @@ import tox import tox.session from tox import venv from tox.config import parseconfig -from tox.config.parallel import ENV_VAR_KEY as PARALLEL_ENV_VAR_KEY +from tox.config.parallel import ENV_VAR_KEY_PRIVATE as PARALLEL_ENV_VAR_KEY_PRIVATE +from tox.config.parallel import ENV_VAR_KEY_PUBLIC as PARALLEL_ENV_VAR_KEY_PUBLIC from tox.reporter import update_default_reporter from tox.venv import CreationConfig, VirtualEnv, getdigest @@ -66,7 +67,12 @@ def check_os_environ_stable(): to_clean = { k: os.environ.pop(k, None) - for k in {PARALLEL_ENV_VAR_KEY, str("TOX_WORK_DIR"), str("PYTHONPATH")} + for k in { + PARALLEL_ENV_VAR_KEY_PRIVATE, + PARALLEL_ENV_VAR_KEY_PUBLIC, + str("TOX_WORK_DIR"), + str("PYTHONPATH"), + } } yield diff --git a/src/tox/config/__init__.py b/src/tox/config/__init__.py index 1d66ed7a..76df09e6 100644 --- a/src/tox/config/__init__.py +++ b/src/tox/config/__init__.py @@ -35,7 +35,8 @@ from tox.reporter import ( from tox.util.path import ensure_empty_dir from tox.util.stdlib import importlib_metadata -from .parallel import ENV_VAR_KEY as PARALLEL_ENV_VAR_KEY +from .parallel import ENV_VAR_KEY_PRIVATE as PARALLEL_ENV_VAR_KEY_PRIVATE +from .parallel import ENV_VAR_KEY_PUBLIC as PARALLEL_ENV_VAR_KEY_PUBLIC from .parallel import add_parallel_config, add_parallel_flags from .reporter import add_verbosity_commands @@ -670,7 +671,7 @@ def tox_addoption(parser): "LD_LIBRARY_PATH", "TOX_WORK_DIR", str(REPORTER_TIMESTAMP_ON_ENV), - str(PARALLEL_ENV_VAR_KEY), + str(PARALLEL_ENV_VAR_KEY_PUBLIC), } # read in global passenv settings @@ -1030,7 +1031,7 @@ class ParseIni(object): config.sdistsrc = reader.getpath("sdistsrc", None) config.setupdir = reader.getpath("setupdir", "{toxinidir}") config.logdir = config.toxworkdir.join("log") - within_parallel = PARALLEL_ENV_VAR_KEY in os.environ + within_parallel = PARALLEL_ENV_VAR_KEY_PRIVATE in os.environ if not within_parallel and not WITHIN_PROVISION: ensure_empty_dir(config.logdir) @@ -1282,7 +1283,7 @@ class ParseIni(object): all_envs = self._getallenvs(reader) else: candidates = ( - (os.environ.get(PARALLEL_ENV_VAR_KEY), True), + (os.environ.get(PARALLEL_ENV_VAR_KEY_PRIVATE), True), (from_option, True), (from_environ, True), ("py" if self.config.option.devenv is not None else None, False), diff --git a/src/tox/config/parallel.py b/src/tox/config/parallel.py index 5cc859a2..ca0dbbd5 100644 --- a/src/tox/config/parallel.py +++ b/src/tox/config/parallel.py @@ -2,7 +2,8 @@ from __future__ import absolute_import, unicode_literals from argparse import ArgumentTypeError -ENV_VAR_KEY = "TOX_PARALLEL_ENV" +ENV_VAR_KEY_PUBLIC = "TOX_PARALLEL_ENV" +ENV_VAR_KEY_PRIVATE = "_TOX_PARALLEL_ENV" OFF_VALUE = 0 DEFAULT_PARALLEL = OFF_VALUE diff --git a/src/tox/session/__init__.py b/src/tox/session/__init__.py index 048feb94..b2e901e1 100644 --- a/src/tox/session/__init__.py +++ b/src/tox/session/__init__.py @@ -20,7 +20,7 @@ import tox from tox import reporter from tox.action import Action from tox.config import parseconfig -from tox.config.parallel import ENV_VAR_KEY as PARALLEL_ENV_VAR_KEY +from tox.config.parallel import ENV_VAR_KEY_PRIVATE as PARALLEL_ENV_VAR_KEY_PRIVATE from tox.config.parallel import OFF_VALUE as PARALLEL_OFF from tox.logs.result import ResultLog from tox.reporter import update_default_reporter @@ -212,7 +212,7 @@ class Session(object): if self.config.option.sdistonly: return - within_parallel = PARALLEL_ENV_VAR_KEY in os.environ + within_parallel = PARALLEL_ENV_VAR_KEY_PRIVATE in os.environ try: if not within_parallel and self.config.option.parallel != PARALLEL_OFF: run_parallel(self.config, self.venv_dict) @@ -241,7 +241,7 @@ class Session(object): return data def _summary(self): - is_parallel_child = PARALLEL_ENV_VAR_KEY in os.environ + is_parallel_child = PARALLEL_ENV_VAR_KEY_PRIVATE in os.environ if not is_parallel_child: reporter.separator("_", "summary", reporter.Verbosity.QUIET) exit_code = 0 diff --git a/src/tox/session/commands/run/parallel.py b/src/tox/session/commands/run/parallel.py index 76db9793..0bba9c3e 100644 --- a/src/tox/session/commands/run/parallel.py +++ b/src/tox/session/commands/run/parallel.py @@ -4,7 +4,8 @@ from collections import OrderedDict, deque from threading import Event, Semaphore, Thread from tox import reporter -from tox.config.parallel import ENV_VAR_KEY as PARALLEL_ENV_VAR_KEY +from tox.config.parallel import ENV_VAR_KEY_PRIVATE as PARALLEL_ENV_VAR_KEY_PRIVATE +from tox.config.parallel import ENV_VAR_KEY_PUBLIC as PARALLEL_ENV_VAR_KEY_PUBLIC from tox.exception import InvocationError from tox.util.main import MAIN_FILE from tox.util.spinner import Spinner @@ -37,7 +38,8 @@ def run_parallel(config, venv_dict): env_name = tox_env.envconfig.envname status = "skipped tests" if config.option.notest else None try: - os_env[str(PARALLEL_ENV_VAR_KEY)] = str(env_name) + os_env[str(PARALLEL_ENV_VAR_KEY_PRIVATE)] = str(env_name) + os_env[str(PARALLEL_ENV_VAR_KEY_PUBLIC)] = str(env_name) args_sub = list(args) if hasattr(tox_env, "package"): args_sub.insert(position, str(tox_env.package)) diff --git a/src/tox/venv.py b/src/tox/venv.py index ef1deb77..4edf741b 100644 --- a/src/tox/venv.py +++ b/src/tox/venv.py @@ -11,7 +11,7 @@ import py import tox from tox import reporter from tox.action import Action -from tox.config.parallel import ENV_VAR_KEY as PARALLEL_ENV_VAR_KEY +from tox.config.parallel import ENV_VAR_KEY_PRIVATE as PARALLEL_ENV_VAR_KEY_PRIVATE from tox.constants import INFO, PARALLEL_RESULT_JSON_PREFIX, PARALLEL_RESULT_JSON_SUFFIX from tox.package.local import resolve_package from tox.util.lock import get_unique_file @@ -693,7 +693,7 @@ def tox_testenv_create(venv, action): def cleanup_for_venv(venv): - within_parallel = PARALLEL_ENV_VAR_KEY in os.environ + within_parallel = PARALLEL_ENV_VAR_KEY_PRIVATE in os.environ # if the directory exists and it doesn't look like a virtualenv, produce # an error if venv.path.exists(): diff --git a/tests/integration/test_parallel_inception.py b/tests/integration/test_parallel_inception.py new file mode 100644 index 00000000..2170dd38 --- /dev/null +++ b/tests/integration/test_parallel_inception.py @@ -0,0 +1,52 @@ +def test_parallel_inception(initproj, cmd): + initproj( + "inception-1.2.3", + filedefs={ + # the outer config just has one env: graham + "tox.ini": """ + [tox] + envlist = graham + skipsdist = True + + [testenv] + commands = + python runner.py + """, + # the inner config has 3 different envs, 1 of them is graham + "inner": { + "tox.ini": """ + [tox] + envlist = graham,john,terry + skipsdist = True + + [testenv] + commands = + python -c 'pass' + """ + }, + # the outer test runs the inner tox and asserts all 3 envs were run + "runner.py": """ + import os + import subprocess + import sys + + os.chdir("inner") + p = subprocess.Popen(("tox"), stdout=subprocess.PIPE, universal_newlines=True) + stdout, _ = p.communicate() + sys.stdout.write(stdout) + assert "graham" in stdout + assert "john" in stdout + assert "terry" in stdout + """, + }, + add_missing_setup_py=False, + ) + + result = cmd("-p", "all", "-o") + result.assert_success() + + # 1 from the outer, 1 from the inner + assert result.out.count("graham: commands succeeded") == 2 + # those gentlemen are only inside + assert result.out.count("john: commands succeeded") == 1 + assert result.out.count("terry: commands succeeded") == 1 diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py index d3b17970..0d31974e 100644 --- a/tests/unit/config/test_config.py +++ b/tests/unit/config/test_config.py @@ -19,7 +19,8 @@ from tox.config import ( is_section_substitution, parseconfig, ) -from tox.config.parallel import ENV_VAR_KEY as PARALLEL_ENV_VAR_KEY +from tox.config.parallel import ENV_VAR_KEY_PRIVATE as PARALLEL_ENV_VAR_KEY_PRIVATE +from tox.config.parallel import ENV_VAR_KEY_PUBLIC as PARALLEL_ENV_VAR_KEY_PUBLIC class TestVenvConfig: @@ -1073,7 +1074,8 @@ class TestConfigTestEnv: assert "LANG" in envconfig.passenv assert "LANGUAGE" in envconfig.passenv assert "LD_LIBRARY_PATH" in envconfig.passenv - assert PARALLEL_ENV_VAR_KEY in envconfig.passenv + assert PARALLEL_ENV_VAR_KEY_PUBLIC in envconfig.passenv + assert PARALLEL_ENV_VAR_KEY_PRIVATE not in envconfig.passenv assert "A123A" in envconfig.passenv assert "A123B" in envconfig.passenv |