summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorholger krekel <holger@merlinux.eu>2015-02-21 20:33:15 +0100
committerholger krekel <holger@merlinux.eu>2015-02-21 20:33:15 +0100
commit152f30d55d68973356083521523e47a2796d45c6 (patch)
tree9cf1df8d179feffef131d5db3a7ca2b0d5d0fb5d
parenteca4bc058aae8beed720d71b0b4803fc05c26005 (diff)
parent192089fb9a72e4247489a9d841827d3d2ec8da1d (diff)
downloadtox-152f30d55d68973356083521523e47a2796d45c6.tar.gz
Merged in suor/tox/issue198 (pull request #129)
-rw-r--r--CHANGELOG7
-rw-r--r--CONTRIBUTORS2
-rw-r--r--doc/config.txt12
-rw-r--r--doc/example/basic.txt16
-rw-r--r--tests/test_config.py12
-rw-r--r--tests/test_quickstart.py23
-rw-r--r--tox/_cmdline.py40
-rw-r--r--tox/_config.py14
-rw-r--r--tox/_quickstart.py2
-rw-r--r--tox/_venv.py17
10 files changed, 124 insertions, 21 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 55ab8ae..8ccd135 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -10,6 +10,13 @@
- refine determination if we run from Jenkins, thanks Borge Lanes.
+- echo output to stdout when ``--report-json`` is used
+
+- fix issue11: add a ``skip_install`` per-testenv setting which
+ prevents the installation of a package. Thanks Julian Krause.
+
+- fix issue124: ignore command exit codes; when a command has a "-" prefix,
+ tox will ignore the exit code of that command
1.8.1
-----------
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index ef8576d..b3e523e 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -24,8 +24,10 @@ Matt Good
Mattieu Agopian
Asmund Grammeltwedt
Ionel Maries Cristian
+Julian Krause
Alexandre Conrad
Morgan Fainberg
Marc Schlaich
Clark Boylan
Eugene Yunak
+Mark Hirota
diff --git a/doc/config.txt b/doc/config.txt
index 6ffd02b..d743e1b 100644
--- a/doc/config.txt
+++ b/doc/config.txt
@@ -86,6 +86,8 @@ Complete list of settings that you can put into ``testenv*`` sections:
will be appended (and may contain another ``\`` character ...).
For eventually performing a call to ``subprocess.Popen(args, ...)``
``args`` are determined by splitting the whole command by whitespace.
+ Similar to ``make`` recipe lines, any command with a leading ``-``
+ will ignore the exit code.
.. confval:: install_command=ARGV
@@ -252,6 +254,16 @@ Complete list of settings that you can put into ``testenv*`` sections:
**default**: ``False``
+.. confval:: skip_install=BOOL
+
+ .. versionadded:: 1.9
+
+ Do not install the current package. This can be used when you need the
+ virtualenv management but do not want to install the current package
+ into that environment.
+
+ **default**: ``False``
+
Substitutions
-------------
diff --git a/doc/example/basic.txt b/doc/example/basic.txt
index 49baeee..916990e 100644
--- a/doc/example/basic.txt
+++ b/doc/example/basic.txt
@@ -244,3 +244,19 @@ using the ``--tox-args`` or ``-a`` command-line options. For example::
python setup.py test -a "-epy27"
is equivalent to running ``tox -epy27``.
+
+Ignoring a command exit code
+----------------------------
+
+In some cases, you may want to ignore a command exit code. For example::
+
+ [testenv:py27]
+ commands = coverage erase
+ {envbindir}/python setup.py develop
+ coverage run -p setup.py test
+ coverage combine
+ - coverage html
+ {envbindir}/flake8 loads
+
+By using the ``-`` prefix, similar to a ``make`` recipe line, you can ignore
+the exit code for that command.
diff --git a/tests/test_config.py b/tests/test_config.py
index 229f035..f957771 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -960,6 +960,18 @@ class TestConfigTestEnv:
assert configs["py27"].setenv["X"] == "1"
assert "X" not in configs["py26"].setenv
+ @pytest.mark.issue191
+ def test_factor_use_not_checked(self, newconfig):
+ inisource="""
+ [tox]
+ envlist = py27-{a,b}
+
+ [testenv]
+ deps = b: test
+ """
+ configs = newconfig([], inisource).envconfigs
+ assert set(configs.keys()) == set(['py27-a', 'py27-b'])
+
@pytest.mark.issue198
def test_factors_groups_touch(self, newconfig):
inisource="""
diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py
index df8a98f..5aaacc2 100644
--- a/tests/test_quickstart.py
+++ b/tests/test_quickstart.py
@@ -37,6 +37,7 @@ class TestToxQuickstartMain(object):
'Y', # py32
'Y', # py33
'Y', # py34
+ 'Y', # py35
'Y', # pypy
'N', # jython
'py.test', # command to run tests
@@ -54,7 +55,7 @@ class TestToxQuickstartMain(object):
# and then run "tox" from this directory.
[tox]
-envlist = py26, py27, py32, py33, py34, pypy
+envlist = py26, py27, py32, py33, py34, py35, pypy
[testenv]
commands = py.test
@@ -77,6 +78,7 @@ deps =
'Y', # py32
'Y', # py33
'Y', # py34
+ 'Y', # py35
'Y', # pypy
'N', # jython
'nosetests', # command to run tests
@@ -94,7 +96,7 @@ deps =
# and then run "tox" from this directory.
[tox]
-envlist = py26, py27, py32, py33, py34, pypy
+envlist = py26, py27, py32, py33, py34, py35, pypy
[testenv]
commands = nosetests
@@ -117,6 +119,7 @@ deps =
'Y', # py32
'Y', # py33
'Y', # py34
+ 'Y', # py35
'Y', # pypy
'N', # jython
'trial', # command to run tests
@@ -134,7 +137,7 @@ deps =
# and then run "tox" from this directory.
[tox]
-envlist = py26, py27, py32, py33, py34, pypy
+envlist = py26, py27, py32, py33, py34, py35, pypy
[testenv]
commands = trial
@@ -157,6 +160,7 @@ deps =
'Y', # py32
'Y', # py33
'Y', # py34
+ 'Y', # py35
'Y', # pypy
'N', # jython
'py.test', # command to run tests
@@ -173,7 +177,7 @@ deps =
# and then run "tox" from this directory.
[tox]
-envlist = py26, py27, py32, py33, py34, pypy
+envlist = py26, py27, py32, py33, py34, py35, pypy
[testenv]
commands = py.test
@@ -272,7 +276,7 @@ deps =
# and then run "tox" from this directory.
[tox]
-envlist = py26, py27, py32, py33, py34, pypy, jython
+envlist = py26, py27, py32, py33, py34, py35, pypy, jython
[testenv]
commands = py.test
@@ -295,6 +299,7 @@ deps =
'', # py32
'', # py33
'', # py34
+ '', # py35
'', # pypy
'', # jython
'', # command to run tests
@@ -312,7 +317,7 @@ deps =
# and then run "tox" from this directory.
[tox]
-envlist = py26, py27, py32, py33, py34, pypy, jython
+envlist = py26, py27, py32, py33, py34, py35, pypy, jython
[testenv]
commands = {envpython} setup.py test
@@ -339,6 +344,7 @@ deps =
'', # py32
'', # py33
'', # py34
+ '', # py35
'', # pypy
'', # jython
'', # command to run tests
@@ -357,7 +363,7 @@ deps =
# and then run "tox" from this directory.
[tox]
-envlist = py26, py27, py32, py33, py34, pypy, jython
+envlist = py26, py27, py32, py33, py34, py35, pypy, jython
[testenv]
commands = {envpython} setup.py test
@@ -459,6 +465,7 @@ deps =
'py32': True,
'py33': True,
'py34': True,
+ 'py35': True,
'pypy': True,
'commands': 'nosetests -v',
'deps': 'nose',
@@ -470,7 +477,7 @@ deps =
# and then run "tox" from this directory.
[tox]
-envlist = py27, py32, py33, py34, pypy
+envlist = py27, py32, py33, py34, py35, pypy
[testenv]
commands = nosetests -v
diff --git a/tox/_cmdline.py b/tox/_cmdline.py
index 0df2f17..9362d86 100644
--- a/tox/_cmdline.py
+++ b/tox/_cmdline.py
@@ -11,6 +11,7 @@ import py
import os
import sys
import subprocess
+import time
from tox._verlib import NormalizedVersion, IrrationalVersionError
from tox._venv import VirtualEnv
from tox._config import parseconfig
@@ -78,8 +79,8 @@ class Action(object):
f.flush()
return f
- def popen(self, args, cwd=None, env=None, redirect=True, returnout=False):
- f = outpath = None
+ def popen(self, args, cwd=None, env=None, redirect=True, returnout=False, ignore_ret=False):
+ stdout = outpath = None
resultjson = self.session.config.option.resultjson
if resultjson or redirect:
f = self._initlogpath(self.id)
@@ -87,14 +88,18 @@ class Action(object):
self.id, self.msg, args, env))
f.flush()
self.popen_outpath = outpath = py.path.local(f.name)
+ if resultjson:
+ stdout = subprocess.PIPE
+ else:
+ stdout = f
elif returnout:
- f = subprocess.PIPE
+ stdout = subprocess.PIPE
if cwd is None:
# XXX cwd = self.session.config.cwd
cwd = py.path.local()
try:
popen = self._popen(args, cwd, env=env,
- stdout=f, stderr=STDOUT)
+ stdout=stdout, stderr=STDOUT)
except OSError as e:
self.report.error("invocation failed (errno %d), args: %s, cwd: %s" %
(e.errno, args, cwd))
@@ -107,7 +112,28 @@ class Action(object):
try:
self.report.logpopen(popen, env=env)
try:
- out, err = popen.communicate()
+ if resultjson and not redirect:
+ assert popen.stderr is None # prevent deadlock
+ out = None
+ last_time = time.time()
+ while 1:
+ # we have to read one byte at a time, otherwise there
+ # might be no output for a long time with slow tests
+ data = popen.stdout.read(1)
+ if data:
+ sys.stdout.write(data)
+ if '\n' in data or (time.time() - last_time) > 5:
+ # we flush on newlines or after 5 seconds to
+ # provide quick enough feedback to the user
+ # when printing a dot per test
+ sys.stdout.flush()
+ last_time = time.time()
+ f.write(data)
+ elif popen.poll() is not None:
+ popen.stdout.close()
+ break
+ else:
+ out, err = popen.communicate()
except KeyboardInterrupt:
self.report.keyboard_interrupt()
popen.wait()
@@ -115,7 +141,7 @@ class Action(object):
ret = popen.wait()
finally:
self._popenlist.remove(popen)
- if ret:
+ if ret and not ignore_ret:
invoked = " ".join(map(str, popen.args))
if outpath:
self.report.error("invocation failed (exit code %d), logfile: %s" %
@@ -448,7 +474,7 @@ class Session:
if self.setupenv(venv):
if venv.envconfig.develop:
self.developpkg(venv, self.config.setupdir)
- elif self.config.skipsdist:
+ elif self.config.skipsdist or venv.envconfig.skip_install:
self.finishvenv(venv)
else:
self.installpkg(venv, sdist_path)
diff --git a/tox/_config.py b/tox/_config.py
index 82154e3..3d26686 100644
--- a/tox/_config.py
+++ b/tox/_config.py
@@ -18,7 +18,7 @@ iswin32 = sys.platform == "win32"
default_factors = {'jython': 'jython', 'pypy': 'pypy', 'pypy3': 'pypy3',
'py': sys.executable}
-for version in '24,25,26,27,30,31,32,33,34'.split(','):
+for version in '24,25,26,27,30,31,32,33,34,35'.split(','):
default_factors['py' + version] = 'python%s.%s' % tuple(version)
def parseconfig(args=None, pkg=None):
@@ -287,10 +287,18 @@ class parseini:
config.envlist, all_envs = self._getenvdata(reader, toxsection)
- # configure testenvs
+ # factors used in config or predefined
known_factors = self._list_section_factors("testenv")
known_factors.update(default_factors)
known_factors.add("python")
+
+ # factors stated in config envlist
+ stated_envlist = reader.getdefault(toxsection, "envlist", replace=False)
+ if stated_envlist:
+ for env in _split_env(stated_envlist):
+ known_factors.update(env.split('-'))
+
+ # configure testenvs
for name in all_envs:
section = testenvprefix + name
factors = set(name.split('-'))
@@ -395,6 +403,8 @@ class parseini:
vc.pip_pre = config.option.pre or reader.getbool(
section, "pip_pre", False)
+ vc.skip_install = reader.getbool(section, "skip_install", False)
+
return vc
def _getenvdata(self, reader, toxsection):
diff --git a/tox/_quickstart.py b/tox/_quickstart.py
index 098eb61..e7c4f03 100644
--- a/tox/_quickstart.py
+++ b/tox/_quickstart.py
@@ -56,7 +56,7 @@ except NameError:
term_input = input
-all_envs = ['py26', 'py27', 'py32', 'py33', 'py34', 'pypy', 'jython']
+all_envs = ['py26', 'py27', 'py32', 'py33', 'py34', 'py35', 'pypy', 'jython']
PROMPT_PREFIX = '> '
diff --git a/tox/_venv.py b/tox/_venv.py
index 937a881..6bcb88a 100644
--- a/tox/_venv.py
+++ b/tox/_venv.py
@@ -345,8 +345,19 @@ class VirtualEnv(object):
message = "commands[%s] | %s" % (i, ' '.join(
[str(x) for x in argv]))
action.setactivity("runtests", message)
+ # check to see if we need to ignore the return code
+ # if so, we need to alter the command line arguments
+ if argv[0].startswith("-"):
+ ignore_ret = True
+ if argv[0] == "-":
+ del argv[0]
+ else:
+ argv[0] = argv[0].lstrip("-")
+ else:
+ ignore_ret = False
+
try:
- self._pcall(argv, cwd=cwd, action=action, redirect=redirect)
+ self._pcall(argv, cwd=cwd, action=action, redirect=redirect, ignore_ret=ignore_ret)
except tox.exception.InvocationError:
val = sys.exc_info()[1]
self.session.report.error(str(val))
@@ -357,7 +368,7 @@ class VirtualEnv(object):
raise
def _pcall(self, args, venv=True, cwd=None, extraenv={},
- action=None, redirect=True):
+ action=None, redirect=True, ignore_ret=False):
for name in ("VIRTUALENV_PYTHON", "PYTHONDONTWRITEBYTECODE"):
try:
del os.environ[name]
@@ -369,7 +380,7 @@ class VirtualEnv(object):
try:
args[0] = self.getcommandpath(args[0], venv, cwd)
env = self._getenv(extraenv)
- return action.popen(args, cwd=cwd, env=env, redirect=redirect)
+ return action.popen(args, cwd=cwd, env=env, redirect=redirect, ignore_ret=ignore_ret)
finally:
os.environ['PATH'] = old