summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG5
-rw-r--r--doc/Makefile3
-rw-r--r--doc/announce/release-2.0.txt54
-rw-r--r--doc/conf.py2
-rw-r--r--setup.py2
-rw-r--r--tests/test_config.py62
-rw-r--r--tests/test_z_cmdline.py22
-rw-r--r--tox.ini2
-rw-r--r--tox/__init__.py2
-rw-r--r--tox/_cmdline.py73
-rw-r--r--tox/_config.py12
-rw-r--r--tox/_venv.py1
-rw-r--r--tox/result.py3
13 files changed, 167 insertions, 76 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 720a48b..ca59f7c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,4 @@
-2.0.0.dev1
+2.0.0
-----------
- (new) introduce environment variable isolation:
@@ -23,6 +23,8 @@
2.0). If ``False`` (the default), then a non-zero exit code from one command
will abort execution of commands for that environment.
+- show and store in json the version dependency information for each venv
+
- remove the long-deprecated "distribute" option as it has no effect these days.
- fix issue233: avoid hanging with tox-setuptools integration example. Thanks simonb.
@@ -46,6 +48,7 @@
for testenv sections. Can be used from plugins through the
tox_add_option hook.
+
1.9.2
-----------
diff --git a/doc/Makefile b/doc/Makefile
index d3393d6..4ffb98b 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -37,7 +37,8 @@ clean:
-rm -rf $(BUILDDIR)/*
install: clean html
- @rsync -avz $(BUILDDIR)/html/ testrun.org:/www/testrun.org/tox/dev
+ @rsync -avz $(BUILDDIR)/html/ testrun.org:/www/testrun.org/tox/latest
+ #dev
#latexpdf
#@scp $(BUILDDIR)/latex/*.pdf testrun.org:www-tox/latest
diff --git a/doc/announce/release-2.0.txt b/doc/announce/release-2.0.txt
new file mode 100644
index 0000000..9f87844
--- /dev/null
+++ b/doc/announce/release-2.0.txt
@@ -0,0 +1,54 @@
+tox-2.0: plugins, platform, env isolation
+==========================================
+
+tox-2.0 was released to pypi, a major new release with *mostly*
+backward-compatible enhancements and fixes:
+
+- experimental support for plugins, see https://testrun.org/tox/dev/plugins.html
+ which includes also a refined internal registration mechanism for new testenv
+ ini options. You can now ask tox which testenv ini parameters exist
+ with ``tox --help-ini``.
+
+- ENV isolation: only pass through very few environment variables from the
+ tox invocation to the test environments. This may break test runs that
+ previously worked with tox-1.9 -- you need to either use the
+ ``setenv`` or ``passenv`` ini variables to set appropriate environment
+ variables.
+
+- PLATFORM support: you can set ``platform=REGEX`` in your testenv sections
+ which lets tox skip the environment if the REGEX does not match ``sys.platform``.
+
+- tox now stops execution of test commands if the first of them fails unless
+ you set ``ignore_errors=True``.
+
+Thanks to Volodymyr Vitvitski, Daniel Hahler, Marc Abramowitz, Anthon van
+der Neuth and others for contributions.
+
+More documentation about tox in general:
+
+ http://tox.testrun.org/
+
+Installation:
+
+ pip install -U tox
+
+code hosting and issue tracking on bitbucket:
+
+ https://bitbucket.org/hpk42/tox
+
+What is tox?
+----------------
+
+tox standardizes and automates tedious test activities driven from a
+simple ``tox.ini`` file, including:
+
+* creation and management of different virtualenv environments
+ with different Python interpreters
+* packaging and installing your package into each of them
+* running your test tool of choice, be it nose, py.test or unittest2 or other tools such as "sphinx" doc checks
+* testing dev packages against each other without needing to upload to PyPI
+
+best,
+Holger Krekel, merlinux GmbH
+
+
diff --git a/doc/conf.py b/doc/conf.py
index 73f5168..802ccd4 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -41,7 +41,7 @@ master_doc = 'index'
# General information about the project.
project = u'tox'
-copyright = u'2013, holger krekel and others'
+copyright = u'2015, holger krekel and others'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
diff --git a/setup.py b/setup.py
index 663b47e..8ea49c8 100644
--- a/setup.py
+++ b/setup.py
@@ -26,7 +26,7 @@ def main():
description='virtualenv-based automation of test activities',
long_description=open("README.rst").read(),
url='http://tox.testrun.org/',
- version='2.0.0.dev2',
+ version='2.0.0.dev4',
license='http://opensource.org/licenses/MIT',
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
author='holger krekel',
diff --git a/tests/test_config.py b/tests/test_config.py
index 87f7f6d..e9c941a 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -827,13 +827,13 @@ class TestConfigTestEnv:
def test_substitution_error(tmpdir, newconfig):
py.test.raises(tox.exception.ConfigError, newconfig, """
- [testenv:py24]
+ [testenv:py27]
basepython={xyz}
""")
def test_substitution_defaults(tmpdir, newconfig):
config = newconfig("""
- [testenv:py24]
+ [testenv:py27]
commands =
{toxinidir}
{toxworkdir}
@@ -845,7 +845,7 @@ class TestConfigTestEnv:
{distshare}
{envlogdir}
""")
- conf = config.envconfigs['py24']
+ conf = config.envconfigs['py27']
argv = conf.commands
assert argv[0][0] == config.toxinidir
assert argv[1][0] == config.toxworkdir
@@ -859,18 +859,18 @@ class TestConfigTestEnv:
def test_substitution_positional(self, newconfig):
inisource = """
- [testenv:py24]
+ [testenv:py27]
commands =
cmd1 [hello] \
world
cmd1 {posargs:hello} \
world
"""
- conf = newconfig([], inisource).envconfigs['py24']
+ conf = newconfig([], inisource).envconfigs['py27']
argv = conf.commands
assert argv[0] == ["cmd1", "[hello]", "world"]
assert argv[1] == ["cmd1", "hello", "world"]
- conf = newconfig(['brave', 'new'], inisource).envconfigs['py24']
+ conf = newconfig(['brave', 'new'], inisource).envconfigs['py27']
argv = conf.commands
assert argv[0] == ["cmd1", "[hello]", "world"]
assert argv[1] == ["cmd1", "brave", "new", "world"]
@@ -886,58 +886,58 @@ class TestConfigTestEnv:
def test_posargs_backslashed_or_quoted(self, tmpdir, newconfig):
inisource = """
- [testenv:py24]
+ [testenv:py27]
commands =
echo "\{posargs\}" = {posargs}
echo "posargs = " "{posargs}"
"""
- conf = newconfig([], inisource).envconfigs['py24']
+ conf = newconfig([], inisource).envconfigs['py27']
argv = conf.commands
assert argv[0] == ['echo', '\\{posargs\\}', '=']
assert argv[1] == ['echo', 'posargs = ', ""]
- conf = newconfig(['dog', 'cat'], inisource).envconfigs['py24']
+ conf = newconfig(['dog', 'cat'], inisource).envconfigs['py27']
argv = conf.commands
assert argv[0] == ['echo', '\\{posargs\\}', '=', 'dog', 'cat']
assert argv[1] == ['echo', 'posargs = ', 'dog cat']
def test_rewrite_posargs(self, tmpdir, newconfig):
inisource = """
- [testenv:py24]
+ [testenv:py27]
args_are_paths = True
changedir = tests
commands = cmd1 {posargs:hello}
"""
- conf = newconfig([], inisource).envconfigs['py24']
+ conf = newconfig([], inisource).envconfigs['py27']
argv = conf.commands
assert argv[0] == ["cmd1", "hello"]
- conf = newconfig(["tests/hello"], inisource).envconfigs['py24']
+ conf = newconfig(["tests/hello"], inisource).envconfigs['py27']
argv = conf.commands
assert argv[0] == ["cmd1", "tests/hello"]
tmpdir.ensure("tests", "hello")
- conf = newconfig(["tests/hello"], inisource).envconfigs['py24']
+ conf = newconfig(["tests/hello"], inisource).envconfigs['py27']
argv = conf.commands
assert argv[0] == ["cmd1", "hello"]
def test_rewrite_simple_posargs(self, tmpdir, newconfig):
inisource = """
- [testenv:py24]
+ [testenv:py27]
args_are_paths = True
changedir = tests
commands = cmd1 {posargs}
"""
- conf = newconfig([], inisource).envconfigs['py24']
+ conf = newconfig([], inisource).envconfigs['py27']
argv = conf.commands
assert argv[0] == ["cmd1"]
- conf = newconfig(["tests/hello"], inisource).envconfigs['py24']
+ conf = newconfig(["tests/hello"], inisource).envconfigs['py27']
argv = conf.commands
assert argv[0] == ["cmd1", "tests/hello"]
tmpdir.ensure("tests", "hello")
- conf = newconfig(["tests/hello"], inisource).envconfigs['py24']
+ conf = newconfig(["tests/hello"], inisource).envconfigs['py27']
argv = conf.commands
assert argv[0] == ["cmd1", "hello"]
@@ -947,12 +947,12 @@ class TestConfigTestEnv:
deps=
pytest
pytest-cov
- [testenv:py24]
+ [testenv:py27]
deps=
{[testenv]deps}
fun
"""
- conf = newconfig([], inisource).envconfigs['py24']
+ conf = newconfig([], inisource).envconfigs['py27']
packages = [dep.name for dep in conf.deps]
assert packages == ['pytest', 'pytest-cov', 'fun']
@@ -1165,11 +1165,11 @@ class TestGlobalOptions:
monkeypatch, newconfig):
monkeypatch.setenv("HUDSON_URL", "xyz")
config = newconfig("""
- [testenv:py24]
+ [testenv:py27]
commands =
{distshare}
""")
- conf = config.envconfigs['py24']
+ conf = config.envconfigs['py27']
argv = conf.commands
expect_path = config.toxworkdir.join("distshare")
assert argv[0][0] == expect_path
@@ -1180,11 +1180,11 @@ class TestGlobalOptions:
config = newconfig("""
[tox:jenkins]
distshare = {env:WORKSPACE}/hello
- [testenv:py24]
+ [testenv:py27]
commands =
{distshare}
""")
- conf = config.envconfigs['py24']
+ conf = config.envconfigs['py27']
argv = conf.commands
assert argv[0][0] == config.distshare
assert config.distshare == tmpdir.join("hello")
@@ -1230,7 +1230,7 @@ class TestGlobalOptions:
assert str(env.basepython) == sys.executable
def test_default_environments(self, tmpdir, newconfig, monkeypatch):
- envs = "py26,py27,py31,py32,py33,py34,jython,pypy,pypy3"
+ envs = "py26,py27,py32,py33,py34,py35,py36,jython,pypy,pypy3"
inisource = """
[tox]
envlist = %s
@@ -1291,21 +1291,21 @@ class TestGlobalOptions:
assert not config.option.skip_missing_interpreters
def test_defaultenv_commandline(self, tmpdir, newconfig, monkeypatch):
- config = newconfig(["-epy24"], "")
- env = config.envconfigs['py24']
- assert env.basepython == "python2.4"
+ config = newconfig(["-epy27"], "")
+ env = config.envconfigs['py27']
+ assert env.basepython == "python2.7"
assert not env.commands
def test_defaultenv_partial_override(self, tmpdir, newconfig, monkeypatch):
inisource = """
[tox]
- envlist = py24
- [testenv:py24]
+ envlist = py27
+ [testenv:py27]
commands= xyz
"""
config = newconfig([], inisource)
- env = config.envconfigs['py24']
- assert env.basepython == "python2.4"
+ env = config.envconfigs['py27']
+ assert env.basepython == "python2.7"
assert env.commands == [['xyz']]
diff --git a/tests/test_z_cmdline.py b/tests/test_z_cmdline.py
index e793804..b5cbc4e 100644
--- a/tests/test_z_cmdline.py
+++ b/tests/test_z_cmdline.py
@@ -83,15 +83,15 @@ class TestSession:
})
config = parseconfig([])
session = Session(config)
- sdist = session.sdist()
+ sdist = session.get_installpkg_path()
assert sdist.check()
assert sdist.ext == ".zip"
assert sdist == config.distdir.join(sdist.basename)
- sdist2 = session.sdist()
+ sdist2 = session.get_installpkg_path()
assert sdist2 == sdist
sdist.write("hello")
assert sdist.stat().size < 10
- sdist_new = Session(config).sdist()
+ sdist_new = Session(config).get_installpkg_path()
assert sdist_new == sdist
assert sdist_new.stat().size > 10
@@ -106,7 +106,7 @@ class TestSession:
})
config = parseconfig([])
session = Session(config)
- sdist = session.sdist()
+ sdist = session.get_installpkg_path()
assert sdist.check()
assert sdist.ext == ".zip"
assert sdist == config.distdir.join(sdist.basename)
@@ -683,7 +683,7 @@ def test_sdist_latest(tmpdir, newconfig):
p = distshare.ensure("pkg123-1.4.5.zip")
distshare.ensure("pkg123-1.4.5a1.zip")
session = Session(config)
- sdist_path = session.sdist()
+ sdist_path = session.get_installpkg_path()
assert sdist_path == p
@@ -691,7 +691,7 @@ def test_installpkg(tmpdir, newconfig):
p = tmpdir.ensure("pkg123-1.0.zip")
config = newconfig(["--installpkg=%s" % p], "")
session = Session(config)
- sdist_path = session.sdist()
+ sdist_path = session.get_installpkg_path()
assert sdist_path == p
@@ -722,7 +722,9 @@ def verify_json_report_format(data, testenvs=True):
for command in envdata[commandtype]:
assert command["output"]
assert command["retcode"]
- pyinfo = envdata["python"]
- assert isinstance(pyinfo["version_info"], list)
- assert pyinfo["version"]
- assert pyinfo["executable"]
+ if envname != "GLOB":
+ assert isinstance(envdata["installed_packages"], list)
+ pyinfo = envdata["python"]
+ assert isinstance(pyinfo["version_info"], list)
+ assert pyinfo["version"]
+ assert pyinfo["executable"]
diff --git a/tox.ini b/tox.ini
index 2214c7d..4f8b49c 100644
--- a/tox.ini
+++ b/tox.ini
@@ -5,7 +5,7 @@ envlist=py27,py26,py34,py33,pypy,flakes
commands=echo {posargs}
[testenv]
-commands= py.test --timeout=60 {posargs}
+commands= py.test --timeout=180 {posargs}
deps=pytest>=2.3.5
pytest-timeout
diff --git a/tox/__init__.py b/tox/__init__.py
index e0bff5a..7e71062 100644
--- a/tox/__init__.py
+++ b/tox/__init__.py
@@ -1,5 +1,5 @@
#
-__version__ = '2.0.0.dev2'
+__version__ = '2.0.0.dev4'
from .hookspecs import hookspec, hookimpl # noqa
diff --git a/tox/_cmdline.py b/tox/_cmdline.py
index 2a9f391..0c2f9d6 100644
--- a/tox/_cmdline.py
+++ b/tox/_cmdline.py
@@ -67,10 +67,12 @@ class Action(object):
self.venvname = self.venv.name
else:
self.venvname = "GLOB"
- cat = {"runtests": "test", "getenv": "setup"}.get(msg)
- if cat:
- envlog = session.resultlog.get_envlog(self.venvname)
- self.commandlog = envlog.get_commandlog(cat)
+ if msg == "runtests":
+ cat = "test"
+ else:
+ cat = "setup"
+ envlog = session.resultlog.get_envlog(self.venvname)
+ self.commandlog = envlog.get_commandlog(cat)
def __enter__(self):
self.report.logaction_start(self)
@@ -106,7 +108,7 @@ class Action(object):
resultjson = self.session.config.option.resultjson
if resultjson or redirect:
fout = self._initlogpath(self.id)
- fout.write("actionid=%s\nmsg=%s\ncmdargs=%r\nenv=%s\n" % (
+ fout.write("actionid: %s\nmsg: %s\ncmdargs: %r\nenv: %s\n\n" % (
self.id, self.msg, args, env))
fout.flush()
self.popen_outpath = outpath = py.path.local(fout.name)
@@ -442,47 +444,47 @@ class Session:
venv.status = sys.exc_info()[1]
return False
- def installpkg(self, venv, sdist_path):
- """Install source package in the specified virtual environment.
+ def installpkg(self, venv, path):
+ """Install package in the specified virtual environment.
:param :class:`tox._config.VenvConfig`: Destination environment
- :param str sdist_path: Path to the source distribution.
+ :param str path: Path to the distribution package.
:return: True if package installed otherwise False.
:rtype: bool
"""
- self.resultlog.set_header(installpkg=py.path.local(sdist_path))
- action = self.newaction(venv, "installpkg", sdist_path)
+ self.resultlog.set_header(installpkg=py.path.local(path))
+ action = self.newaction(venv, "installpkg", path)
with action:
try:
- venv.installpkg(sdist_path, action)
+ venv.installpkg(path, action)
return True
except tox.exception.InvocationError:
venv.status = sys.exc_info()[1]
return False
- def sdist(self):
+ def get_installpkg_path(self):
"""
- :return: Path to the source distribution
+ :return: Path to the distribution
:rtype: py.path.local
"""
if not self.config.option.sdistonly and (self.config.sdistsrc or
self.config.option.installpkg):
- sdist_path = self.config.option.installpkg
- if not sdist_path:
- sdist_path = self.config.sdistsrc
- sdist_path = self._resolve_pkg(sdist_path)
+ path = self.config.option.installpkg
+ if not path:
+ path = self.config.sdistsrc
+ path = self._resolve_pkg(path)
self.report.info("using package %r, skipping 'sdist' activity " %
- str(sdist_path))
+ str(path))
else:
try:
- sdist_path = self._makesdist()
+ path = self._makesdist()
except tox.exception.InvocationError:
v = sys.exc_info()[1]
self.report.error("FAIL could not package project - v = %r" %
v)
return
- sdistfile = self.config.distshare.join(sdist_path.basename)
- if sdistfile != sdist_path:
+ sdistfile = self.config.distshare.join(path.basename)
+ if sdistfile != path:
self.report.info("copying new sdistfile to %r" %
str(sdistfile))
try:
@@ -491,16 +493,16 @@ class Session:
self.report.warning("could not copy distfile to %s" %
sdistfile.dirpath())
else:
- sdist_path.copy(sdistfile)
- return sdist_path
+ path.copy(sdistfile)
+ return path
def subcommand_test(self):
if self.config.skipsdist:
self.report.info("skipping sdist step")
- sdist_path = None
+ path = None
else:
- sdist_path = self.sdist()
- if not sdist_path:
+ path = self.get_installpkg_path()
+ if not path:
return 2
if self.config.option.sdistonly:
return
@@ -514,7 +516,22 @@ class Session:
elif self.config.skipsdist or venv.envconfig.skip_install:
self.finishvenv(venv)
else:
- self.installpkg(venv, sdist_path)
+ self.installpkg(venv, path)
+
+ # write out version dependency information
+ action = self.newaction(venv, "envreport")
+ with action:
+ pip = venv.getcommandpath("pip")
+ # we can't really call internal helpers here easily :/
+ # output = venv._pcall([str(pip), "freeze"],
+ # cwd=self.config.toxinidir,
+ # action=action)
+ output = py.process.cmdexec("%s freeze" % (pip))
+ packages = output.strip().split("\n")
+ action.setactivity("installed", ",".join(packages))
+ envlog = self.resultlog.get_envlog(venv.name)
+ envlog.set_installed(packages)
+
self.runtestenv(venv)
retcode = self._summary()
return retcode
@@ -589,6 +606,8 @@ class Session:
self.report.line(" envdir= %s" % envconfig.envdir)
self.report.line(" downloadcache=%s" % envconfig.downloadcache)
self.report.line(" usedevelop=%s" % envconfig.usedevelop)
+ self.report.line(" setenv=%s" % envconfig.setenv)
+ self.report.line(" passenv=%s" % envconfig.passenv)
def showenvs(self):
for env in self.config.envlist:
diff --git a/tox/_config.py b/tox/_config.py
index cbbf5e9..85ff822 100644
--- a/tox/_config.py
+++ b/tox/_config.py
@@ -21,7 +21,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,35'.split(','):
+for version in '26,27,32,33,34,35,36'.split(','):
default_factors['py' + version] = 'python%s.%s' % tuple(version)
hookimpl = pluggy.HookimplMarker("tox")
@@ -361,9 +361,18 @@ def tox_addoption(parser):
def passenv(config, reader, section_val):
passenv = set(["PATH"])
+
+ # we ensure that tmp directory settings are passed on
+ # we could also set it to the per-venv "envtmpdir"
+ # but this leads to very long paths when run with jenkins
+ # so we just pass it on by default for now.
if sys.platform == "win32":
passenv.add("SYSTEMROOT") # needed for python's crypto module
passenv.add("PATHEXT") # needed for discovering executables
+ passenv.add("TEMPDIR")
+ passenv.add("TMP")
+ else:
+ passenv.add("TMPDIR")
for spec in section_val:
for name in os.environ:
if fnmatchcase(name.upper(), spec.upper()):
@@ -626,6 +635,7 @@ class parseini:
factors=factors)
reader.addsubstitutions(**subs)
reader.addsubstitutions(envname=name)
+ reader.vc = vc
for env_attr in config._testenv_attr:
atype = env_attr.type
diff --git a/tox/_venv.py b/tox/_venv.py
index d4852bc..d52a6e5 100644
--- a/tox/_venv.py
+++ b/tox/_venv.py
@@ -327,7 +327,6 @@ class VirtualEnv(object):
env['VIRTUAL_ENV'] = str(self.path)
env.update(extraenv)
-
return env
def test(self, redirect=False):
diff --git a/tox/result.py b/tox/result.py
index 7bedd3f..96ac612 100644
--- a/tox/result.py
+++ b/tox/result.py
@@ -63,6 +63,9 @@ class EnvLog:
l = self.dict.setdefault(name, [])
return CommandLog(self, l)
+ def set_installed(self, packages):
+ self.dict["installed_packages"] = packages
+
class CommandLog:
def __init__(self, envlog, list):