summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiro Hrončok <miro@hroncok.cz>2019-11-11 11:45:29 +0100
committerBernát Gábor <bgabor8@bloomberg.net>2019-11-11 10:45:29 +0000
commit0a44dccb5bcc6c133c622d65e143c83a652c5fa9 (patch)
treeb58432561fce936431f79c7a876971bf4d0af660
parent3980da78d1e9fb8f618a12c6b90953c1aa25b8ab (diff)
downloadtox-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.rst5
-rw-r--r--src/tox/_pytestplugin.py10
-rw-r--r--src/tox/config/__init__.py9
-rw-r--r--src/tox/config/parallel.py3
-rw-r--r--src/tox/session/__init__.py6
-rw-r--r--src/tox/session/commands/run/parallel.py6
-rw-r--r--src/tox/venv.py4
-rw-r--r--tests/integration/test_parallel_inception.py52
-rw-r--r--tests/unit/config/test_config.py6
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