Hello community, here is the log from the commit of package python3-tox for openSUSE:Factory checked in at 2015-12-18 21:51:35 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python3-tox (Old) and /work/SRC/openSUSE:Factory/.python3-tox.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python3-tox" Changes: -------- --- /work/SRC/openSUSE:Factory/python3-tox/python3-tox.changes 2015-11-24 22:32:29.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.python3-tox.new/python3-tox.changes 2015-12-18 21:51:36.000000000 +0100 @@ -1,0 +2,26 @@ +Sun Dec 13 20:26:34 UTC 2015 - [email protected] + +- update to version 2.3.0: + * DEPRECATE use of "indexservers" in tox.ini. It complicates the + internal code and it is recommended to rather use the + devpi system for managing indexes for pip. + * fix issue285: make setenv processing fully lazy to fix regressions + of tox-2.2.X and so that we can now have testenv attributes like + "basepython" depend on environment variables that are set in a + setenv section. Thanks Nelfin for some tests and initial work on a + PR. + * allow "#" in commands. This is slightly incompatible with + commands sections that used a comment after a "\" line + continuation. Thanks David Stanek for the PR. + * fix issue289: fix build_sphinx target, thanks Barry Warsaw. + * fix issue252: allow environment names with special characters. + Thanks Julien Castets for initial PR and patience. + * introduce experimental tox_testenv_create(venv, action) and + tox_testenv_install_deps(venv, action) hooks to allow plugins to + do additional work on creation or installing deps. These hooks + are experimental mainly because of the involved "venv" and session + objects whose current public API is not fully guranteed. + * internal: push some optional object creation into tests because + tox core doesn't need it. + +------------------------------------------------------------------- Old: ---- tox-2.2.1.tar.gz New: ---- tox-2.3.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python3-tox.spec ++++++ --- /var/tmp/diff_new_pack.QQWIpi/_old 2015-12-18 21:51:37.000000000 +0100 +++ /var/tmp/diff_new_pack.QQWIpi/_new 2015-12-18 21:51:37.000000000 +0100 @@ -17,7 +17,7 @@ Name: python3-tox -Version: 2.2.1 +Version: 2.3.0 Release: 0 Summary: Virtualenv-based automation of test activities License: MIT ++++++ tox-2.2.1.tar.gz -> tox-2.3.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/CHANGELOG new/tox-2.3.0/CHANGELOG --- old/tox-2.2.1/CHANGELOG 2015-11-11 15:57:13.000000000 +0100 +++ new/tox-2.3.0/CHANGELOG 2015-12-09 13:38:11.000000000 +0100 @@ -1,3 +1,35 @@ +2.3.0 +----- + +- DEPRECATE use of "indexservers" in tox.ini. It complicates + the internal code and it is recommended to rather use the + devpi system for managing indexes for pip. + +- fix issue285: make setenv processing fully lazy to fix regressions + of tox-2.2.X and so that we can now have testenv attributes like + "basepython" depend on environment variables that are set in + a setenv section. Thanks Nelfin for some tests and initial + work on a PR. + +- allow "#" in commands. This is slightly incompatible with commands + sections that used a comment after a "\" line continuation. + Thanks David Stanek for the PR. + +- fix issue289: fix build_sphinx target, thanks Barry Warsaw. + +- fix issue252: allow environment names with special characters. + Thanks Julien Castets for initial PR and patience. + +- introduce experimental tox_testenv_create(venv, action) and + tox_testenv_install_deps(venv, action) hooks to allow + plugins to do additional work on creation or installing + deps. These hooks are experimental mainly because of + the involved "venv" and session objects whose current public + API is not fully guranteed. + +- internal: push some optional object creation into tests because + tox core doesn't need it. + 2.2.1 ----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/PKG-INFO new/tox-2.3.0/PKG-INFO --- old/tox-2.2.1/PKG-INFO 2015-11-11 15:57:14.000000000 +0100 +++ new/tox-2.3.0/PKG-INFO 2015-12-09 13:38:12.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: tox -Version: 2.2.1 +Version: 2.3.0 Summary: virtualenv-based automation of test activities Home-page: http://tox.testrun.org/ Author: holger krekel diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/doc/Makefile new/tox-2.3.0/doc/Makefile --- old/tox-2.2.1/doc/Makefile 2015-11-11 15:57:13.000000000 +0100 +++ new/tox-2.3.0/doc/Makefile 2015-12-09 13:38:11.000000000 +0100 @@ -12,6 +12,8 @@ PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +SITETARGET=$(shell ./_getdoctarget.py) + .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @@ -36,8 +38,10 @@ clean: -rm -rf $(BUILDDIR)/* + install: clean html @rsync -avz $(BUILDDIR)/html/ testrun.org:/www/testrun.org/tox/latest + @rsync -avz $(BUILDDIR)/html/ testrun.org:/www/testrun.org/tox/$(SITETARGET) #dev #latexpdf #@scp $(BUILDDIR)/latex/*.pdf testrun.org:www-tox/latest diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/doc/_getdoctarget.py new/tox-2.3.0/doc/_getdoctarget.py --- old/tox-2.2.1/doc/_getdoctarget.py 1970-01-01 01:00:00.000000000 +0100 +++ new/tox-2.3.0/doc/_getdoctarget.py 2015-12-09 13:38:11.000000000 +0100 @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +import py + +def get_version_string(): + fn = py.path.local(__file__).join("..", "..", + "tox", "__init__.py") + for line in fn.readlines(): + if "version" in line and not line.strip().startswith('#'): + return eval(line.split("=")[-1]) + +def get_minor_version_string(): + return ".".join(get_version_string().split(".")[:2]) + +if __name__ == "__main__": + print (get_minor_version_string()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/doc/conf.py new/tox-2.3.0/doc/conf.py --- old/tox-2.2.1/doc/conf.py 2015-11-11 15:57:13.000000000 +0100 +++ new/tox-2.3.0/doc/conf.py 2015-12-09 13:38:11.000000000 +0100 @@ -13,6 +13,13 @@ import sys, os +# The short X.Y version. +sys.path.insert(0, os.path.dirname(__file__)) +import _getdoctarget + +version = _getdoctarget.get_minor_version_string() +release = _getdoctarget.get_version_string() + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. @@ -47,9 +54,6 @@ # |version| and |release|, also used in various other places throughout the # built documents. # -# The short X.Y version. -release = "2.2" -version = "2.2.0" # The full version, including alpha/beta/rc tags. # The language for content autogenerated by Sphinx. Refer to documentation diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/doc/config-v2.txt new/tox-2.3.0/doc/config-v2.txt --- old/tox-2.2.1/doc/config-v2.txt 2015-11-11 15:57:13.000000000 +0100 +++ new/tox-2.3.0/doc/config-v2.txt 2015-12-09 13:38:11.000000000 +0100 @@ -143,7 +143,7 @@ A testenv can define a new ``platform`` setting. If its value is not contained in the string obtained from calling -``platform.platform()`` the environment will be skipped. +``sys.platform`` the environment will be skipped. Expanding the ``envlist`` setting ---------------------------------------------------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/doc/config.txt new/tox-2.3.0/doc/config.txt --- old/tox-2.2.1/doc/config.txt 2015-11-11 15:57:13.000000000 +0100 +++ new/tox-2.3.0/doc/config.txt 2015-12-09 13:38:11.000000000 +0100 @@ -93,9 +93,6 @@ .. versionadded:: 1.6 - **WARNING**: This setting is **EXPERIMENTAL** so use with care - and be ready to adapt your tox.ini's with post-1.6 tox releases. - the ``install_command`` setting is used for installing packages into the virtual environment; both the package under test and any defined dependencies. Must contain the substitution key @@ -166,7 +163,8 @@ package installation. Each line defines a dependency, which will be passed to the installer command for processing. Each line specifies a file, a URL or a package name. You can additionally specify - an :confval:`indexserver` to use for installing this dependency. + an :confval:`indexserver` to use for installing this dependency + but this functionality is deprecated since tox-2.3. All derived dependencies (deps required by the dep) will then be retrieved from the specified indexserver:: @@ -259,8 +257,9 @@ .. versionadded:: 0.9 - Multi-line ``name = URL`` definitions of python package servers. - Dependencies can specify using a specified index server through the + (DEPRECATED, will be removed in a future version) Multi-line ``name = + URL`` definitions of python package servers. Dependencies can + specify using a specified index server through the ``:indexservername:depname`` pattern. The ``default`` indexserver definition determines where unscoped dependencies and the sdist install installs from. Example:: @@ -441,7 +440,7 @@ {[sectionname]valuename} which you can use to avoid repetition of config values. -You can put default values in one section and reference them in others to avoid repeting the same values:: +You can put default values in one section and reference them in others to avoid repeating the same values:: [base] deps = @@ -455,7 +454,7 @@ {[base]deps} [testenv:mercurial] - dep = + deps = mercurial {[base]deps} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/doc/example/basic.txt new/tox-2.3.0/doc/example/basic.txt --- old/tox-2.2.1/doc/example/basic.txt 2015-11-11 15:57:13.000000000 +0100 +++ new/tox-2.3.0/doc/example/basic.txt 2015-12-09 13:38:11.000000000 +0100 @@ -30,6 +30,7 @@ Available "default" test environments names are:: + py py24 py25 py26 @@ -43,6 +44,8 @@ pypy pypy3 +The environment ``py`` uses the version of Python used to invoke tox. + However, you can also create your own test environment names, see some of the examples in :doc:`examples <../examples>`. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/doc/plugins.txt new/tox-2.3.0/doc/plugins.txt --- old/tox-2.2.1/doc/plugins.txt 2015-11-11 15:57:13.000000000 +0100 +++ new/tox-2.3.0/doc/plugins.txt 2015-12-09 13:38:11.000000000 +0100 @@ -80,3 +80,9 @@ .. autoclass:: tox.config.TestenvConfig() :members: + +.. autoclass:: tox.venv.VirtualEnv() + :members: + +.. autoclass:: tox.session.Session() + :members: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/setup.cfg new/tox-2.3.0/setup.cfg --- old/tox-2.2.1/setup.cfg 2015-11-11 15:57:14.000000000 +0100 +++ new/tox-2.3.0/setup.cfg 2015-12-09 13:38:12.000000000 +0100 @@ -1,5 +1,5 @@ [build_sphinx] -source-dir = doc/en/ +source-dir = doc/ build-dir = doc/build all_files = 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/setup.py new/tox-2.3.0/setup.py --- old/tox-2.2.1/setup.py 2015-11-11 15:57:13.000000000 +0100 +++ new/tox-2.3.0/setup.py 2015-12-09 13:38:11.000000000 +0100 @@ -48,7 +48,7 @@ description='virtualenv-based automation of test activities', long_description=open("README.rst").read(), url='http://tox.testrun.org/', - version='2.2.1', + version='2.3.0', license='http://opensource.org/licenses/MIT', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], author='holger krekel', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/tests/test_config.py new/tox-2.3.0/tests/test_config.py --- old/tox-2.2.1/tests/test_config.py 2015-11-11 15:57:13.000000000 +0100 +++ new/tox-2.3.0/tests/test_config.py 2015-12-09 13:38:11.000000000 +0100 @@ -283,18 +283,9 @@ commands = ls {env:TEST} """) - reader = SectionReader("testenv:py27", config._cfg) - x = reader.getargvlist("commands") - assert x == [ - "ls testvalue".split() - ] - assert x != [ - "ls {env:TEST}".split() - ] - y = reader.getargvlist("setenv") - assert y == [ - "TEST=testvalue".split() - ] + envconfig = config.envconfigs["py27"] + assert envconfig.commands == [["ls", "testvalue"]] + assert envconfig.setenv["TEST"] == "testvalue" class TestIniParser: @@ -459,7 +450,7 @@ config = newconfig(""" [section] key2= - cmd1 {item1} \ # a comment + cmd1 {item1} \ {item2} """) reader = SectionReader("section", config._cfg) @@ -474,12 +465,32 @@ config = newconfig(""" [section] key1= - cmd1 'with space' \ # a comment - 'after the comment' + cmd1 'part one' \ + 'part two' + """) + reader = SectionReader("section", config._cfg) + x = reader.getargvlist("key1") + assert x == [["cmd1", "part one", "part two"]] + + def test_argvlist_comment_after_command(self, tmpdir, newconfig): + config = newconfig(""" + [section] + key1= + cmd1 --flag # run the flag on the command """) reader = SectionReader("section", config._cfg) x = reader.getargvlist("key1") - assert x == [["cmd1", "with space", "after the comment"]] + assert x == [["cmd1", "--flag"]] + + def test_argvlist_command_contains_hash(self, tmpdir, newconfig): + config = newconfig(""" + [section] + key1= + cmd1 --re "use the # symbol for an arg" + """) + reader = SectionReader("section", config._cfg) + x = reader.getargvlist("key1") + assert x == [["cmd1", "--re", "use the # symbol for an arg"]] def test_argvlist_positional_substitution(self, tmpdir, newconfig): config = newconfig(""" @@ -633,7 +644,7 @@ assert envconfig.usedevelop is False assert envconfig.ignore_errors is False assert envconfig.envlogdir == envconfig.envdir.join("log") - assert list(envconfig.setenv.keys()) == ['PYTHONHASHSEED'] + assert list(envconfig.setenv.definitions.keys()) == ['PYTHONHASHSEED'] hashseed = envconfig.setenv['PYTHONHASHSEED'] assert isinstance(hashseed, str) # The following line checks that hashseed parses to an integer. @@ -724,46 +735,6 @@ if bp == "jython": assert envconfig.envpython == envconfig.envbindir.join(bp) - def test_setenv_overrides(self, tmpdir, newconfig): - config = newconfig(""" - [testenv] - setenv = - PYTHONPATH = something - ANOTHER_VAL=else - """) - assert len(config.envconfigs) == 1 - envconfig = config.envconfigs['python'] - assert 'PYTHONPATH' in envconfig.setenv - assert 'ANOTHER_VAL' in envconfig.setenv - assert envconfig.setenv['PYTHONPATH'] == 'something' - assert envconfig.setenv['ANOTHER_VAL'] == 'else' - - def test_setenv_with_envdir_and_basepython(self, tmpdir, newconfig): - config = newconfig(""" - [testenv] - setenv = - VAL = {envdir} - basepython = {env:VAL} - """) - assert len(config.envconfigs) == 1 - envconfig = config.envconfigs['python'] - assert 'VAL' in envconfig.setenv - assert envconfig.setenv['VAL'] == envconfig.envdir - assert envconfig.basepython == envconfig.envdir - - def test_setenv_ordering_1(self, tmpdir, newconfig): - config = newconfig(""" - [testenv] - setenv= - VAL={envdir} - commands=echo {env:VAL} - """) - assert len(config.envconfigs) == 1 - envconfig = config.envconfigs['python'] - assert 'VAL' in envconfig.setenv - assert envconfig.setenv['VAL'] == envconfig.envdir - assert str(envconfig.envdir) in envconfig.commands[0] - @pytest.mark.parametrize("plat", ["win32", "linux2"]) def test_passenv_as_multiline_list(self, tmpdir, newconfig, monkeypatch, plat): monkeypatch.setattr(sys, "platform", plat) @@ -1505,7 +1476,7 @@ return envconfigs["python"] def _check_hashseed(self, envconfig, expected): - assert envconfig.setenv == {'PYTHONHASHSEED': expected} + assert envconfig.setenv['PYTHONHASHSEED'] == expected def _check_testenv(self, newconfig, expected, args=None, tox_ini=None): envconfig = self._get_envconfig(newconfig, args=args, tox_ini=tox_ini) @@ -1554,7 +1525,7 @@ def test_noset(self, tmpdir, newconfig): args = ['--hashseed', 'noset'] envconfig = self._get_envconfig(newconfig, args=args) - assert envconfig.setenv == {} + assert not envconfig.setenv.definitions def test_noset_with_setenv(self, tmpdir, newconfig): tox_ini = """ @@ -1598,6 +1569,125 @@ self._check_hashseed(envconfigs["hash2"], '123456789') +class TestSetenv: + def test_getdict_lazy(self, tmpdir, newconfig, monkeypatch): + monkeypatch.setenv("X", "2") + config = newconfig(""" + [testenv:X] + key0 = + key1 = {env:X} + key2 = {env:Y:1} + """) + envconfig = config.envconfigs["X"] + val = envconfig._reader.getdict_setenv("key0") + assert val["key1"] == "2" + assert val["key2"] == "1" + + def test_getdict_lazy_update(self, tmpdir, newconfig, monkeypatch): + monkeypatch.setenv("X", "2") + config = newconfig(""" + [testenv:X] + key0 = + key1 = {env:X} + key2 = {env:Y:1} + """) + envconfig = config.envconfigs["X"] + val = envconfig._reader.getdict_setenv("key0") + d = {} + d.update(val) + assert d == {"key1": "2", "key2": "1"} + + def test_setenv_uses_os_environ(self, tmpdir, newconfig, monkeypatch): + monkeypatch.setenv("X", "1") + config = newconfig(""" + [testenv:env1] + setenv = + X = {env:X} + """) + assert config.envconfigs["env1"].setenv["X"] == "1" + + def test_setenv_default_os_environ(self, tmpdir, newconfig, monkeypatch): + monkeypatch.delenv("X", raising=False) + config = newconfig(""" + [testenv:env1] + setenv = + X = {env:X:2} + """) + assert config.envconfigs["env1"].setenv["X"] == "2" + + def test_setenv_uses_other_setenv(self, tmpdir, newconfig): + config = newconfig(""" + [testenv:env1] + setenv = + Y = 5 + X = {env:Y} + """) + assert config.envconfigs["env1"].setenv["X"] == "5" + + def test_setenv_recursive_direct(self, tmpdir, newconfig): + config = newconfig(""" + [testenv:env1] + setenv = + X = {env:X:3} + """) + assert config.envconfigs["env1"].setenv["X"] == "3" + + def test_setenv_overrides(self, tmpdir, newconfig): + config = newconfig(""" + [testenv] + setenv = + PYTHONPATH = something + ANOTHER_VAL=else + """) + assert len(config.envconfigs) == 1 + envconfig = config.envconfigs['python'] + assert 'PYTHONPATH' in envconfig.setenv + assert 'ANOTHER_VAL' in envconfig.setenv + assert envconfig.setenv['PYTHONPATH'] == 'something' + assert envconfig.setenv['ANOTHER_VAL'] == 'else' + + def test_setenv_with_envdir_and_basepython(self, tmpdir, newconfig): + config = newconfig(""" + [testenv] + setenv = + VAL = {envdir} + basepython = {env:VAL} + """) + assert len(config.envconfigs) == 1 + envconfig = config.envconfigs['python'] + assert 'VAL' in envconfig.setenv + assert envconfig.setenv['VAL'] == envconfig.envdir + assert envconfig.basepython == envconfig.envdir + + def test_setenv_ordering_1(self, tmpdir, newconfig): + config = newconfig(""" + [testenv] + setenv= + VAL={envdir} + commands=echo {env:VAL} + """) + assert len(config.envconfigs) == 1 + envconfig = config.envconfigs['python'] + assert 'VAL' in envconfig.setenv + assert envconfig.setenv['VAL'] == envconfig.envdir + assert str(envconfig.envdir) in envconfig.commands[0] + + @pytest.mark.xfail(reason="we don't implement cross-section substitution for setenv") + def test_setenv_cross_section_subst(self, monkeypatch, newconfig): + """test that we can do cross-section substitution with setenv""" + monkeypatch.delenv('TEST', raising=False) + config = newconfig(""" + [section] + x = + NOT_TEST={env:TEST:defaultvalue} + + [testenv] + setenv = {[section]x} + """) + envconfig = config.envconfigs["python"] + assert envconfig.setenv["NOT_TEST"] == "defaultvalue" + + class TestIndexServer: def test_indexserver(self, tmpdir, newconfig): config = newconfig(""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/tests/test_venv.py new/tox-2.3.0/tests/test_venv.py --- old/tox-2.2.1/tests/test_venv.py 2015-11-11 15:57:13.000000000 +0100 +++ new/tox-2.3.0/tests/test_venv.py 2015-12-09 13:38:11.000000000 +0100 @@ -56,7 +56,8 @@ venv = VirtualEnv(envconfig, session=mocksession) assert venv.path == envconfig.envdir assert not venv.path.check() - venv.create() + action = mocksession.newaction(venv, "getenv") + tox_testenv_create(action=action, venv=venv) l = mocksession._pcalls assert len(l) >= 1 args = l[0].args @@ -96,7 +97,8 @@ """) envconfig = config.envconfigs['site'] venv = VirtualEnv(envconfig, session=mocksession) - venv.create() + action = mocksession.newaction(venv, "getenv") + tox_testenv_create(action=action, venv=venv) l = mocksession._pcalls assert len(l) >= 1 args = l[0].args @@ -105,7 +107,8 @@ envconfig = config.envconfigs['nosite'] venv = VirtualEnv(envconfig, session=mocksession) - venv.create() + action = mocksession.newaction(venv, "getenv") + tox_testenv_create(action=action, venv=venv) l = mocksession._pcalls assert len(l) >= 1 args = l[0].args @@ -122,14 +125,15 @@ {distshare}/dep1-* """) venv = mocksession.getenv("py123") - venv.create() + action = mocksession.newaction(venv, "getenv") + tox_testenv_create(action=action, venv=venv) l = mocksession._pcalls assert len(l) == 1 distshare = venv.session.config.distshare distshare.ensure("dep1-1.0.zip") distshare.ensure("dep1-1.1.zip") - venv.install_deps() + tox_testenv_install_deps(action=action, venv=venv) assert len(l) == 2 args = l[-1].args assert l[-1].cwd == venv.envconfig.config.toxinidir @@ -154,11 +158,12 @@ dep2 """) venv = mocksession.getenv("py123") - venv.create() + action = mocksession.newaction(venv, "getenv") + tox_testenv_create(action=action, venv=venv) l = mocksession._pcalls assert len(l) == 1 - venv.install_deps() + tox_testenv_install_deps(action=action, venv=venv) assert len(l) == 2 args = l[-1].args assert l[-1].cwd == venv.envconfig.config.toxinidir @@ -183,12 +188,13 @@ :abc2:dep3 """) venv = mocksession.getenv('py123') - venv.create() + action = mocksession.newaction(venv, "getenv") + tox_testenv_create(action=action, venv=venv) l = mocksession._pcalls assert len(l) == 1 l[:] = [] - venv.install_deps() + tox_testenv_install_deps(action=action, venv=venv) # two different index servers, two calls assert len(l) == 3 args = " ".join(l[0].args) @@ -211,12 +217,13 @@ dep1 """) venv = mocksession.getenv('python') - venv.create() + action = mocksession.newaction(venv, "getenv") + tox_testenv_create(action=action, venv=venv) l = mocksession._pcalls assert len(l) == 1 l[:] = [] - venv.install_deps() + tox_testenv_install_deps(action=action, venv=venv) assert len(l) == 1 args = " ".join(l[0].args) assert "--pre " in args @@ -246,10 +253,12 @@ deps=xyz """) venv = mocksession.getenv('python') - venv.update() + + action = mocksession.newaction(venv, "update") + venv.update(action) mocksession.installpkg(venv, pkg) mocksession.report.expect("verbosity0", "*create*") - venv.update() + venv.update(action) mocksession.report.expect("verbosity0", "*recreate*") @@ -263,7 +272,8 @@ finally: tox.config.make_hashseed = original_make_hashseed venv = mocksession.getenv('python') - venv.update() + action = mocksession.newaction(venv, "update") + venv.update(action) venv.test() mocksession.report.expect("verbosity0", "python runtests: PYTHONHASHSEED='123456789'") @@ -274,7 +284,8 @@ commands = echo foo bar ''') venv = mocksession.getenv('python') - venv.update() + action = mocksession.newaction(venv, "update") + venv.update(action) venv.test() mocksession.report.expect("verbosity0", "*runtests*commands?0? | echo foo bar") @@ -343,7 +354,8 @@ dep2 """) venv = mocksession.getenv('py123') - venv.create() + action = mocksession.newaction(venv, "getenv") + tox_testenv_create(action=action, venv=venv) l = mocksession._pcalls assert len(l) == 1 args = l[0].args @@ -429,7 +441,8 @@ envconfig = config.envconfigs['python'] venv = VirtualEnv(envconfig, session=mocksession) cconfig = venv._getliveconfig() - venv.update() + action = mocksession.newaction(venv, "update") + venv.update(action) assert not venv.path_config.check() mocksession.installpkg(venv, pkg) assert venv.path_config.check() @@ -439,36 +452,42 @@ mocksession.report.expect("*", "*create*") # modify config and check that recreation happens mocksession._clearmocks() - venv.update() + action = mocksession.newaction(venv, "update") + venv.update(action) mocksession.report.expect("*", "*reusing*") mocksession._clearmocks() + action = mocksession.newaction(venv, "update") cconfig.python = py.path.local("balla") cconfig.writeconfig(venv.path_config) - venv.update() + venv.update(action) mocksession.report.expect("verbosity0", "*recreate*") def test_dep_recreation(self, newconfig, mocksession): config = newconfig([], "") envconfig = config.envconfigs['python'] venv = VirtualEnv(envconfig, session=mocksession) - venv.update() + action = mocksession.newaction(venv, "update") + venv.update(action) cconfig = venv._getliveconfig() cconfig.deps[:] = [("1" * 32, "xyz.zip")] cconfig.writeconfig(venv.path_config) mocksession._clearmocks() - venv.update() + action = mocksession.newaction(venv, "update") + venv.update(action) mocksession.report.expect("*", "*recreate*") def test_develop_recreation(self, newconfig, mocksession): config = newconfig([], "") envconfig = config.envconfigs['python'] venv = VirtualEnv(envconfig, session=mocksession) - venv.update() + action = mocksession.newaction(venv, "update") + venv.update(action) cconfig = venv._getliveconfig() cconfig.usedevelop = True cconfig.writeconfig(venv.path_config) mocksession._clearmocks() - venv.update() + action = mocksession.newaction(venv, "update") + venv.update(action) mocksession.report.expect("verbosity0", "*recreate*") @@ -481,21 +500,25 @@ commands=abc """) venv = mocksession.getenv("python") + action = mocksession.newaction(venv, "getenv") monkeypatch.setenv("PATH", "xyz") l = [] monkeypatch.setattr("py.path.local.sysfind", classmethod( lambda *args, **kwargs: l.append(kwargs) or 0 / 0)) - py.test.raises(ZeroDivisionError, "venv._install(list('123'))") + with pytest.raises(ZeroDivisionError): + venv._install(list('123'), action=action) assert l.pop()["paths"] == [venv.envconfig.envbindir] - py.test.raises(ZeroDivisionError, "venv.test()") + with pytest.raises(ZeroDivisionError): + venv.test(action) assert l.pop()["paths"] == [venv.envconfig.envbindir] - py.test.raises(ZeroDivisionError, "venv.run_install_command(['qwe'])") + with pytest.raises(ZeroDivisionError): + venv.run_install_command(['qwe'], action=action) assert l.pop()["paths"] == [venv.envconfig.envbindir] monkeypatch.setenv("PIP_RESPECT_VIRTUALENV", "1") monkeypatch.setenv("PIP_REQUIRE_VIRTUALENV", "1") monkeypatch.setenv("__PYVENV_LAUNCHER__", "1") - py.test.raises(ZeroDivisionError, "venv.run_install_command(['qwe'])") + py.test.raises(ZeroDivisionError, "venv.run_install_command(['qwe'], action=action)") assert 'PIP_RESPECT_VIRTUALENV' not in os.environ assert 'PIP_REQUIRE_VIRTUALENV' not in os.environ assert '__PYVENV_LAUNCHER__' not in os.environ @@ -626,3 +649,26 @@ assert venv.status == "ignored failed command" mocksession.report.expect("warning", "*command failed but result from " "testenv is ignored*") + + +def test_tox_testenv_create(newmocksession): + l = [] + + class Plugin: + @hookimpl + def tox_testenv_create(self, action, venv): + l.append(1) + + @hookimpl + def tox_testenv_install_deps(self, action, venv): + l.append(2) + + mocksession = newmocksession([], """ + [testenv] + commands=testenv_fail + ignore_outcome=True + """, plugins=[Plugin()]) + + venv = mocksession.getenv('python') + venv.update(action=mocksession.newaction(venv, "getenv")) + assert l == [1, 2] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/tests/test_z_cmdline.py new/tox-2.3.0/tests/test_z_cmdline.py --- old/tox-2.2.1/tests/test_z_cmdline.py 2015-11-11 15:57:13.000000000 +0100 +++ new/tox-2.3.0/tests/test_z_cmdline.py 2015-12-09 13:38:11.000000000 +0100 @@ -34,7 +34,8 @@ report = session.report report.expect("using") venv = session.getvenv("mypython") - venv.update() + action = session.newaction(venv, "update") + venv.update(action) report.expect("logpopen") @@ -301,6 +302,23 @@ ]) +def test_venv_special_chars_issue252(cmd, initproj): + initproj("pkg123-0.7", filedefs={ + 'tests': {'test_hello.py': "def test_hello(): pass"}, + 'tox.ini': ''' + [tox] + envlist = special&&1 + [testenv:special&&1] + changedir=tests + ''' + }) + result = cmd.run("tox", ) + assert result.ret == 0 + result.stdout.fnmatch_lines([ + "*installed*pkg123*" + ]) + + def test_unknown_environment(cmd, initproj): initproj("env123-0.7", filedefs={ 'tox.ini': '' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/tox/__init__.py new/tox-2.3.0/tox/__init__.py --- old/tox-2.2.1/tox/__init__.py 2015-11-11 15:57:13.000000000 +0100 +++ new/tox-2.3.0/tox/__init__.py 2015-12-09 13:38:11.000000000 +0100 @@ -1,5 +1,5 @@ # -__version__ = '2.2.1' +__version__ = '2.3.0' from .hookspecs import hookspec, hookimpl # noqa diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/tox/_pytestplugin.py new/tox-2.3.0/tox/_pytestplugin.py --- old/tox-2.2.1/tox/_pytestplugin.py 2015-11-11 15:57:13.000000000 +0100 +++ new/tox-2.3.0/tox/_pytestplugin.py 2015-12-09 13:38:11.000000000 +0100 @@ -31,7 +31,7 @@ @pytest.fixture def newconfig(request, tmpdir): - def newconfig(args, source=None): + def newconfig(args, source=None, plugins=()): if source is None: source = args args = [] @@ -40,7 +40,7 @@ p.write(s) old = tmpdir.chdir() try: - return parseconfig(args) + return parseconfig(args, plugins=plugins) finally: old.chdir() return newconfig @@ -168,9 +168,8 @@ mocksession = request.getfuncargvalue("mocksession") newconfig = request.getfuncargvalue("newconfig") - def newmocksession(args, source): - config = newconfig(args, source) - mocksession.config = config + def newmocksession(args, source, plugins=()): + mocksession.config = newconfig(args, source, plugins=plugins) return mocksession return newmocksession diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/tox/config.py new/tox-2.3.0/tox/config.py --- old/tox-2.2.1/tox/config.py 2015-11-11 15:57:13.000000000 +0100 +++ new/tox-2.3.0/tox/config.py 2015-12-09 13:38:11.000000000 +0100 @@ -26,14 +26,20 @@ hookimpl = pluggy.HookimplMarker("tox") +_dummy = object() -def get_plugin_manager(): + +def get_plugin_manager(plugins=()): # initialize plugin manager + import tox.venv pm = pluggy.PluginManager("tox") pm.add_hookspecs(hookspecs) pm.register(tox.config) pm.register(tox.interpreters) + pm.register(tox.venv) pm.load_setuptools_entrypoints("tox") + for plugin in plugins: + pm.register(plugin) pm.check_pending() return pm @@ -184,7 +190,7 @@ return value -def parseconfig(args=None): +def parseconfig(args=None, plugins=()): """ :param list[str] args: Optional list of arguments. :type pkg: str @@ -192,7 +198,7 @@ :raise SystemExit: toxinit file is not found """ - pm = get_plugin_manager() + pm = get_plugin_manager(plugins) if args is None: args = sys.argv[1:] @@ -253,6 +259,47 @@ setattr(namespace, self.dest, 0) +class SetenvDict: + def __init__(self, dict, reader): + self.reader = reader + self.definitions = dict + self.resolved = {} + self._lookupstack = [] + + def __contains__(self, name): + return name in self.definitions + + def get(self, name, default=None): + try: + return self.resolved[name] + except KeyError: + try: + if name in self._lookupstack: + raise KeyError(name) + val = self.definitions[name] + except KeyError: + return os.environ.get(name, default) + self._lookupstack.append(name) + try: + self.resolved[name] = res = self.reader._replace(val) + finally: + self._lookupstack.pop() + return res + + def __getitem__(self, name): + x = self.get(name, _dummy) + if x is _dummy: + raise KeyError(name) + return x + + def keys(self): + return self.definitions.keys() + + def __setitem__(self, name, value): + self.definitions[name] = value + self.resolved[name] = value + + @hookimpl def tox_addoption(parser): # formatter_class=argparse.ArgumentDefaultsHelpFormatter) @@ -323,11 +370,22 @@ parser.add_argument("args", nargs="*", help="additional arguments available to command positional substitution") - # add various core venv interpreter attributes parser.add_testenv_attribute( name="envdir", type="path", default="{toxworkdir}/{envname}", help="venv directory") + # add various core venv interpreter attributes + def setenv(testenv_config, value): + setenv = value + config = testenv_config.config + if "PYTHONHASHSEED" not in setenv and config.hashseed is not None: + setenv['PYTHONHASHSEED'] = config.hashseed + return setenv + + parser.add_testenv_attribute( + name="setenv", type="dict_setenv", postprocess=setenv, + help="list of X=Y lines with environment variable settings") + def basepython_default(testenv_config, value): if value is None: for f in testenv_config.factors: @@ -385,17 +443,6 @@ name="recreate", type="bool", default=False, postprocess=recreate, help="always recreate this test environment.") - def setenv(testenv_config, value): - setenv = value - config = testenv_config.config - if "PYTHONHASHSEED" not in setenv and config.hashseed is not None: - setenv['PYTHONHASHSEED'] = config.hashseed - return setenv - - parser.add_testenv_attribute( - name="setenv", type="dict", postprocess=setenv, - help="list of X=Y lines with environment variable settings") - def passenv(testenv_config, value): # Flatten the list to deal with space-separated values. value = list( @@ -515,8 +562,7 @@ self.factors = factors self._reader = reader - @property - def envbindir(self): + def get_envbindir(self): """ path to directory where scripts/binaries reside. """ if (sys.platform == "win32" and "jython" not in self.basepython @@ -526,7 +572,15 @@ return self.envdir.join("bin") @property + def envbindir(self): + return self.get_envbindir() + + @property def envpython(self): + """ path to python executable. """ + return self.get_envpython() + + def get_envpython(self): """ path to python/jython executable. """ if "jython" in str(self.basepython): name = "jython" @@ -534,8 +588,7 @@ name = "python" return self.envbindir.join(name) - # no @property to avoid early calling (see callable(subst[key]) checks) - def envsitepackagesdir(self): + def get_envsitepackagesdir(self): """ return sitepackagesdir of the virtualenv environment. (only available during execution, not parsing) """ @@ -696,10 +749,13 @@ vc = TestenvConfig(config=config, envname=name, factors=factors, reader=reader) reader.addsubstitutions(**subs) reader.addsubstitutions(envname=name) + reader.addsubstitutions(envbindir=vc.get_envbindir, + envsitepackagesdir=vc.get_envsitepackagesdir, + envpython=vc.get_envpython) for env_attr in config._testenv_attr: atype = env_attr.type - if atype in ("bool", "path", "string", "dict", "argv", "argvlist"): + if atype in ("bool", "path", "string", "dict", "dict_setenv", "argv", "argvlist"): meth = getattr(reader, "get" + atype) res = meth(env_attr.name, env_attr.default) elif atype == "space-separated-list": @@ -716,9 +772,6 @@ if atype == "path": reader.addsubstitutions(**{env_attr.name: res}) - if env_attr.name == "basepython": - reader.addsubstitutions(envbindir=vc.envbindir, envpython=vc.envpython, - envsitepackagesdir=vc.envsitepackagesdir) return vc def _getenvdata(self, reader): @@ -799,16 +852,6 @@ is_section_substitution = re.compile("{\[[^{}\s]+\]\S+?}").match -RE_ITEM_REF = re.compile( - r''' - (?<!\\)[{] - (?:(?P<sub_type>[^[:{}]+):)? # optional sub_type for special rules - (?P<substitution_value>[^{}]*) # substitution key - [}] - ''', - re.VERBOSE) - - class SectionReader: def __init__(self, section_name, cfgparser, fallbacksections=None, factors=()): self.section_name = section_name @@ -817,6 +860,12 @@ self.factors = factors self._subs = {} self._subststack = [] + self._setenv = None + + def get_environ_value(self, name): + if self._setenv is None: + return os.environ.get(name) + return self._setenv.get(name) def addsubstitutions(self, _posargs=None, **kw): self._subs.update(kw) @@ -836,17 +885,26 @@ return [x.strip() for x in s.split(sep) if x.strip()] def getdict(self, name, default=None, sep="\n"): - s = self.getstring(name, None) - if s is None: + value = self.getstring(name, None) + return self._getdict(value, default=default, sep=sep) + + def getdict_setenv(self, name, default=None, sep="\n"): + value = self.getstring(name, None, replace=False) + definitions = self._getdict(value, default=default, sep=sep) + self._setenv = SetenvDict(definitions, reader=self) + return self._setenv + + def _getdict(self, value, default, sep): + if value is None: return default or {} - value = {} - for line in s.split(sep): + d = {} + for line in value.split(sep): if line.strip(): name, rest = line.split('=', 1) - value[name.strip()] = rest.strip() + d[name.strip()] = rest.strip() - return value + return d def getbool(self, name, default=None): s = self.getstring(name, default) @@ -888,11 +946,7 @@ x = self._apply_factors(x) if replace and x and hasattr(x, 'replace'): - self._subststack.append((self.section_name, name)) - try: - x = self._replace(x) - finally: - assert self._subststack.pop() == (self.section_name, name) + x = self._replace(x, name=name) # print "getstring", self.section_name, name, "returned", repr(x) return x @@ -909,8 +963,58 @@ lines = s.strip().splitlines() return '\n'.join(filter(None, map(factor_line, lines))) + def _replace(self, value, name=None, section_name=None): + if '{' not in value: + return value + + section_name = section_name if section_name else self.section_name + self._subststack.append((section_name, name)) + try: + return Replacer(self).do_replace(value) + finally: + assert self._subststack.pop() == (section_name, name) + + +class Replacer: + RE_ITEM_REF = re.compile( + r''' + (?<!\\)[{] + (?:(?P<sub_type>[^[:{}]+):)? # optional sub_type for special rules + (?P<substitution_value>[^{}]*) # substitution key + [}] + ''', + re.VERBOSE) + + def __init__(self, reader): + self.reader = reader + + def do_replace(self, x): + return self.RE_ITEM_REF.sub(self._replace_match, x) + + def _replace_match(self, match): + g = match.groupdict() + + # special case: opts and packages. Leave {opts} and + # {packages} intact, they are replaced manually in + # _venv.VirtualEnv.run_install_command. + sub_value = g['substitution_value'] + if sub_value in ('opts', 'packages'): + return '{%s}' % sub_value + + try: + sub_type = g['sub_type'] + except KeyError: + raise tox.exception.ConfigError( + "Malformed substitution; no substitution type provided") + + if sub_type == "env": + return self._replace_env(match) + if sub_type is not None: + raise tox.exception.ConfigError( + "No support for the %s substitution type" % sub_type) + return self._replace_substitution(match) + def _replace_env(self, match): - env_list = self.getdict('setenv') match_value = match.group('substitution_value') if not match_value: raise tox.exception.ConfigError( @@ -924,75 +1028,40 @@ else: envkey = match_value - if envkey not in os.environ and default is None: - if envkey not in env_list and default is None: + envvalue = self.reader.get_environ_value(envkey) + if envvalue is None: + if default is None: raise tox.exception.ConfigError( - "substitution env:%r: unknown environment variable %r" % + "substitution env:%r: unknown environment variable %r " + " or recursive definition." % (envkey, envkey)) - if envkey in os.environ: - return os.environ.get(envkey, default) - else: - return env_list.get(envkey, default) + return default + return envvalue def _substitute_from_other_section(self, key): if key.startswith("[") and "]" in key: i = key.find("]") section, item = key[1:i], key[i + 1:] - if section in self._cfg and item in self._cfg[section]: - if (section, item) in self._subststack: + cfg = self.reader._cfg + if section in cfg and item in cfg[section]: + if (section, item) in self.reader._subststack: raise ValueError('%s already in %s' % ( - (section, item), self._subststack)) - x = str(self._cfg[section][item]) - self._subststack.append((section, item)) - try: - return self._replace(x) - finally: - self._subststack.pop() + (section, item), self.reader._subststack)) + x = str(cfg[section][item]) + return self.reader._replace(x, name=item, section_name=section) raise tox.exception.ConfigError( "substitution key %r not found" % key) def _replace_substitution(self, match): sub_key = match.group('substitution_value') - val = self._subs.get(sub_key, None) + val = self.reader._subs.get(sub_key, None) if val is None: val = self._substitute_from_other_section(sub_key) if py.builtin.callable(val): val = val() return str(val) - def _replace_match(self, match): - g = match.groupdict() - - # special case: opts and packages. Leave {opts} and - # {packages} intact, they are replaced manually in - # _venv.VirtualEnv.run_install_command. - sub_value = g['substitution_value'] - if sub_value in ('opts', 'packages'): - return '{%s}' % sub_value - - handlers = { - 'env': self._replace_env, - None: self._replace_substitution, - } - try: - sub_type = g['sub_type'] - except KeyError: - raise tox.exception.ConfigError( - "Malformed substitution; no substitution type provided") - - try: - handler = handlers[sub_type] - except KeyError: - raise tox.exception.ConfigError("No support for the %s substitution type" % sub_type) - - return handler(match) - - def _replace(self, x): - if '{' in x: - return RE_ITEM_REF.sub(self._replace_match, x) - return x - class _ArgvlistReader: @classmethod @@ -1010,9 +1079,6 @@ current_command = "" for line in value.splitlines(): line = line.rstrip() - i = line.find("#") - if i != -1: - line = line[:i].rstrip() if not line: continue if line.endswith("\\"): @@ -1064,7 +1130,6 @@ shlexer = shlex.shlex(newcommand, posix=True) shlexer.whitespace_split = True shlexer.escape = '' - shlexer.commenters = '' argv = list(shlexer) return argv diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/tox/hookspecs.py new/tox-2.3.0/tox/hookspecs.py --- old/tox-2.2.1/tox/hookspecs.py 2015-11-11 15:57:13.000000000 +0100 +++ new/tox-2.3.0/tox/hookspecs.py 2015-12-09 13:38:11.000000000 +0100 @@ -30,3 +30,14 @@ per-testenv configuration, notably the ``.envname`` and ``.basepython`` setting. """ + + +@hookspec +def tox_testenv_create(venv, action): + """ [experimental] perform creation action for this venv. + """ + + +@hookspec +def tox_testenv_install_deps(venv, action): + """ [experimental] perform install dependencies action for this venv. """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/tox/session.py new/tox-2.3.0/tox/session.py --- old/tox-2.2.1/tox/session.py 2015-11-11 15:57:13.000000000 +0100 +++ new/tox-2.3.0/tox/session.py 2015-12-09 13:38:11.000000000 +0100 @@ -51,7 +51,7 @@ "(overridable by '-e')") tw.line("TOX_TESTENV_PASSENV: space-separated list of extra " "environment variables to be passed into test command " - "environemnts") + "environments") def show_help_ini(config): @@ -211,7 +211,7 @@ if sys.platform == "win32": ext = os.path.splitext(str(newargs[0]))[1].lower() if ext == '.py' and self.venv: - newargs = [str(self.venv.getcommandpath())] + newargs + newargs = [str(self.envconfig.envpython)] + newargs return newargs @@ -313,6 +313,8 @@ class Session: + """ (unstable API). the session object that ties + together configuration, reporting, venv creation, testing. """ def __init__(self, config, popen=subprocess.Popen, Report=Reporter): self.config = config @@ -533,11 +535,11 @@ 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)) + output = venv._pcall([str(pip), "freeze"], + cwd=self.config.toxinidir, + action=action) + # the output contains a mime-header, skip it + output = output.split("\n\n")[-1] packages = output.strip().split("\n") action.setactivity("installed", ",".join(packages)) envlog = self.resultlog.get_envlog(venv.name) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/tox/venv.py new/tox-2.3.0/tox/venv.py --- old/tox-2.2.1/tox/venv.py 2015-11-11 15:57:13.000000000 +0100 +++ new/tox-2.3.0/tox/venv.py 2015-12-09 13:38:11.000000000 +0100 @@ -5,7 +5,7 @@ import codecs import py import tox -from .config import DepConfig +from .config import DepConfig, hookimpl class CreationConfig: @@ -56,19 +56,34 @@ def __init__(self, envconfig=None, session=None): self.envconfig = envconfig self.session = session - self.path = envconfig.envdir - self.path_config = self.path.join(".tox-config1") + + @property + def hook(self): + return self.envconfig.config.pluginmanager.hook + + @property + def path(self): + """ Path to environment base dir. """ + return self.envconfig.envdir + + @property + def path_config(self): + return self.path.join(".tox-config1") @property def name(self): + """ test environment name. """ return self.envconfig.envname def __repr__(self): return "<VirtualEnv at %r>" % (self.path) - def getcommandpath(self, name=None, venv=True, cwd=None): - if name is None: - return self.envconfig.envpython + def getcommandpath(self, name, venv=True, cwd=None): + """ return absolute path (str or localpath) for specified + command name. If it's a localpath we will rewrite it as + as a relative path. If venv is True we will check if the + command is coming from the venv or is whitelisted to come + from external. """ name = str(name) if os.path.isabs(name): return name @@ -114,12 +129,10 @@ def _ispython3(self): return "python3" in str(self.envconfig.basepython) - def update(self, action=None): + def update(self, action): """ return status string for updating actual venv to match configuration. if status string is empty, all is ok. """ - if action is None: - action = self.session.newaction(self, "update") rconfig = CreationConfig.readconfig(self.path_config) if not self.envconfig.recreate and rconfig and \ rconfig.matches(self._getliveconfig()): @@ -130,13 +143,14 @@ else: action.setactivity("recreate", self.envconfig.envdir) try: - self.create(action) + self.hook.tox_testenv_create(action=action, venv=self) + self.just_created = True except tox.exception.UnsupportedInterpreter: return sys.exc_info()[1] except tox.exception.InterpreterNotFound: return sys.exc_info()[1] try: - self.install_deps(action) + self.hook.tox_testenv_install_deps(action=action, venv=self) except tox.exception.InvocationError: v = sys.exc_info()[1] return "could not install deps %s; v = %r" % ( @@ -172,31 +186,6 @@ def matching_platform(self): return re.match(self.envconfig.platform, sys.platform) - def create(self, action=None): - # if self.getcommandpath("activate").dirpath().check(): - # return - if action is None: - action = self.session.newaction(self, "create") - - config_interpreter = self.getsupportedinterpreter() - args = [sys.executable, '-m', 'virtualenv'] - if self.envconfig.sitepackages: - args.append('--system-site-packages') - # add interpreter explicitly, to prevent using - # default (virtualenv.ini) - args.extend(['--python', str(config_interpreter)]) - # if sys.platform == "win32": - # f, path, _ = py.std.imp.find_module("virtualenv") - # f.close() - # args[:1] = [str(config_interpreter), str(path)] - # else: - self.session.make_emptydir(self.path) - basepath = self.path.dirpath() - basepath.ensure(dir=1) - args.append(self.path.basename) - self._pcall(args, venv=False, action=action, cwd=basepath) - self.just_created = True - def finish(self): self._getliveconfig().writeconfig(self.path_config) @@ -239,15 +228,6 @@ extraopts = ['-U', '--no-deps'] self._install([sdistpath], extraopts=extraopts, action=action) - def install_deps(self, action=None): - if action is None: - action = self.session.newaction(self, "install_deps") - deps = self._getresolvedeps() - if deps: - depinfo = ", ".join(map(str, deps)) - action.setactivity("installdeps", "%s" % depinfo) - self._install(deps, action=action) - def _installopts(self, indexserver): l = [] if indexserver: @@ -259,7 +239,7 @@ l.append("--pre") return l - def run_install_command(self, packages, options=(), action=None): + def run_install_command(self, packages, action, options=()): argv = self.envconfig.install_command[:] # use pip-script on win32 to avoid the executable locking i = argv.index('{packages}') @@ -387,3 +367,35 @@ if not path.check(file=1): return "0" * 32 return path.computehash() + + +@hookimpl +def tox_testenv_create(venv, action): + # if self.getcommandpath("activate").dirpath().check(): + # return + config_interpreter = venv.getsupportedinterpreter() + args = [sys.executable, '-m', 'virtualenv'] + if venv.envconfig.sitepackages: + args.append('--system-site-packages') + # add interpreter explicitly, to prevent using + # default (virtualenv.ini) + args.extend(['--python', str(config_interpreter)]) + # if sys.platform == "win32": + # f, path, _ = py.std.imp.find_module("virtualenv") + # f.close() + # args[:1] = [str(config_interpreter), str(path)] + # else: + venv.session.make_emptydir(venv.path) + basepath = venv.path.dirpath() + basepath.ensure(dir=1) + args.append(venv.path.basename) + venv._pcall(args, venv=False, action=action, cwd=basepath) + + +@hookimpl +def tox_testenv_install_deps(venv, action): + deps = venv._getresolvedeps() + if deps: + depinfo = ", ".join(map(str, deps)) + action.setactivity("installdeps", "%s" % depinfo) + venv._install(deps, action=action) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/tox.egg-info/PKG-INFO new/tox-2.3.0/tox.egg-info/PKG-INFO --- old/tox-2.2.1/tox.egg-info/PKG-INFO 2015-11-11 15:57:14.000000000 +0100 +++ new/tox-2.3.0/tox.egg-info/PKG-INFO 2015-12-09 13:38:12.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: tox -Version: 2.2.1 +Version: 2.3.0 Summary: virtualenv-based automation of test activities Home-page: http://tox.testrun.org/ Author: holger krekel diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/tox.egg-info/SOURCES.txt new/tox-2.3.0/tox.egg-info/SOURCES.txt --- old/tox-2.2.1/tox.egg-info/SOURCES.txt 2015-11-11 15:57:14.000000000 +0100 +++ new/tox-2.3.0/tox.egg-info/SOURCES.txt 2015-12-09 13:38:12.000000000 +0100 @@ -8,6 +8,7 @@ setup.py tox.ini doc/Makefile +doc/_getdoctarget.py doc/changelog.txt doc/check_sphinx.py doc/conf.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/tox-2.2.1/tox.ini new/tox-2.3.0/tox.ini --- old/tox-2.2.1/tox.ini 2015-11-11 15:57:13.000000000 +0100 +++ new/tox-2.3.0/tox.ini 2015-12-09 13:38:11.000000000 +0100 @@ -1,11 +1,11 @@ [tox] -envlist=py27,py26,py34,py33,pypy,flakes,py26-bare +envlist=py27,py26,py34,py33,py35,pypy,flakes,py26-bare [testenv:X] commands=echo {posargs} [testenv] -commands= py.test --timeout=180 {posargs} +commands= py.test --timeout=180 {posargs:tests} deps=pytest>=2.3.5 pytest-timeout
