diff options
author | Gábor Bernát <jokerjokerer@gmail.com> | 2017-12-12 14:36:04 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-12-12 14:36:04 +0000 |
commit | 6b16b095f8b603c23aefe8155cb4e6867db8c178 (patch) | |
tree | 206e208eb1bee9d79bab9153415267dc9fa67d13 | |
parent | 087ed06a73ce78b229660ff79c83706fbeacbca7 (diff) | |
download | tox-git-6b16b095f8b603c23aefe8155cb4e6867db8c178.tar.gz |
cmd simplify, use pytest stderr/stdout management, and use regex instead of fnmatch (#711)
* Python 2.6 support dropped, this line can never be reached
* main args is required, set at entry level
* fix pypy failure
* remove dead code
-rw-r--r-- | tests/conftest.py | 4 | ||||
-rw-r--r-- | tests/test_config.py | 132 | ||||
-rw-r--r-- | tests/test_interpreters.py | 5 | ||||
-rw-r--r-- | tests/test_pytest_plugins.py | 50 | ||||
-rw-r--r-- | tests/test_venv.py | 5 | ||||
-rw-r--r-- | tests/test_z_cmdline.py | 439 | ||||
-rw-r--r-- | tox.ini | 3 | ||||
-rw-r--r-- | tox/__init__.py | 8 | ||||
-rw-r--r-- | tox/__main__.py | 4 | ||||
-rw-r--r-- | tox/_pytestplugin.py | 173 | ||||
-rw-r--r-- | tox/_quickstart.py | 4 | ||||
-rw-r--r-- | tox/_verlib.py | 1 | ||||
-rwxr-xr-x | tox/config.py | 44 | ||||
-rw-r--r-- | tox/session.py | 23 | ||||
-rwxr-xr-x | tox/venv.py | 2 |
15 files changed, 389 insertions, 508 deletions
diff --git a/tests/conftest.py b/tests/conftest.py index 8b36b3b6..120a44bf 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1 +1,3 @@ -from tox._pytestplugin import * # noqa +from __future__ import unicode_literals + +pytest_plugins = ['tox._pytestplugin'] diff --git a/tests/test_config.py b/tests/test_config.py index 119220b0..e8e8d559 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,4 +1,5 @@ import os +import re import sys from textwrap import dedent @@ -2085,18 +2086,16 @@ class TestParseEnv: class TestCmdInvocation: def test_help(self, cmd): - result = cmd.run("tox", "-h") + result = cmd("-h") assert not result.ret - result.stdout.fnmatch_lines([ - "*help*" - ]) + assert not result.err + assert re.match(r'usage:.*help.*', result.out, re.DOTALL) def test_version_simple(self, cmd): - result = cmd.run("tox", "--version") + result = cmd("--version") assert not result.ret - stdout = result.stdout.str() - assert tox.__version__ in stdout - assert "imported from" in stdout + from tox import __version__ + assert "{} imported from".format(__version__) in result.out def test_version_no_plugins(self): pm = PluginManager('fakeprject') @@ -2163,14 +2162,8 @@ class TestCmdInvocation: changedir = docs ''' }) - result = cmd.run("tox", "-l") - result.stdout.fnmatch_lines(""" - py36 - py27 - py34 - pypy - docs - """) + result = cmd("-l") + assert result.outlines == ['py36', 'py27', 'py34', 'pypy', 'docs'] def test_listenvs_verbose_description(self, cmd, initproj): initproj('listenvs_verbose_description', filedefs={ @@ -2193,15 +2186,14 @@ class TestCmdInvocation: description = let me overwrite that ''' }) - result = cmd.run("tox", "-lv") - result.stdout.fnmatch_lines(""" - default environments: - py36 -> run pytest on Python 3.6 - py27 -> run pytest on Python 2.7 - py34 -> run pytest on Python 3.4 - pypy -> publish to pypy - docs -> let me overwrite that - """) + result = cmd("-lv") + assert result.outlines[2:] == [ + 'default environments:', + 'py36 -> run pytest on Python 3.6', + 'py27 -> run pytest on Python 2.7', + 'py34 -> run pytest on Python 3.4', + 'pypy -> publish to pypy', + 'docs -> let me overwrite that'] def test_listenvs_all(self, cmd, initproj): initproj('listenvs_all', filedefs={ @@ -2216,15 +2208,8 @@ class TestCmdInvocation: changedir = docs ''' }) - result = cmd.run("tox", "-a") - result.stdout.fnmatch_lines(""" - py36 - py27 - py34 - pypy - docs - notincluded - """) + result = cmd("-a") + assert result.outlines == ['py36', 'py27', 'py34', 'pypy', 'docs', 'notincluded'] def test_listenvs_all_verbose_description(self, cmd, initproj): initproj('listenvs_all_verbose_description', filedefs={ @@ -2241,19 +2226,19 @@ class TestCmdInvocation: [testenv:docs] changedir = docs - ''' + ''', }) - result = cmd.run("tox", "-av") - result.stdout.fnmatch_lines(""" - default environments: - py27-windows -> run pytest on Python 2.7 on Windows platform - py27-linux -> run pytest on Python 2.7 on Linux platform - py36-windows -> run pytest on Python 3.6 on Windows platform - py36-linux -> run pytest on Python 3.6 on Linux platform - - additional environments: - docs -> generate documentation - """) + result = cmd("-av") + expected = [ + "default environments:", + "py27-windows -> run pytest on Python 2.7 on Windows platform", + "py27-linux -> run pytest on Python 2.7 on Linux platform", + "py36-windows -> run pytest on Python 3.6 on Windows platform", + "py36-linux -> run pytest on Python 3.6 on Linux platform", + "", + "additional environments:", + "docs -> generate documentation"] + assert result.outlines[-len(expected):] == expected def test_listenvs_all_verbose_description_no_additional_environments(self, cmd, initproj): initproj('listenvs_all_verbose_description', filedefs={ @@ -2262,29 +2247,25 @@ class TestCmdInvocation: envlist=py27,py36 ''' }) - result = cmd.run("tox", "-av") - result.stdout.fnmatch_lines(""" - default environments: - py27 -> [no description] - py36 -> [no description] - """) - assert 'additional environments' not in result.stdout.str() + result = cmd("-av") + expected = ["default environments:", + "py27 -> [no description]", + "py36 -> [no description]"] + assert result.out.splitlines()[-3:] == expected + assert 'additional environments' not in result.out def test_config_specific_ini(self, tmpdir, cmd): ini = tmpdir.ensure("hello.ini") - result = cmd.run("tox", "-c", ini, "--showconfig") + result = cmd("-c", ini, "--showconfig") assert not result.ret - result.stdout.fnmatch_lines([ - "*config-file*hello.ini*" - ]) + assert result.outlines[1] == 'config-file: {}'.format(ini) def test_no_tox_ini(self, cmd, initproj): - initproj("noini-0.5") - result = cmd.run("tox") + initproj("noini-0.5", ) + result = cmd() assert result.ret - result.stderr.fnmatch_lines([ - "*ERROR*tox.ini*not*found*" - ]) + assert result.out == '' + assert result.err == "ERROR: toxini file 'tox.ini' not found\n" def test_override_workdir(self, tmpdir, cmd, initproj): baddir = "badworkdir-123" @@ -2293,13 +2274,12 @@ class TestCmdInvocation: 'tox.ini': ''' [tox] toxworkdir=%s - ''' % baddir + ''' % baddir, }) - result = cmd.run("tox", "--workdir", gooddir, "--showconfig") + result = cmd("--workdir", gooddir, "--showconfig") assert not result.ret - stdout = result.stdout.str() - assert gooddir in stdout - assert baddir not in stdout + assert gooddir in result.out + assert baddir not in result.out assert py.path.local(gooddir).check() assert not py.path.local(baddir).check() @@ -2312,20 +2292,16 @@ class TestCmdInvocation: deps= dep1==2.3 dep2 - ''' + ''', }) - result = cmd.run("tox", "--showconfig") + result = cmd("--showconfig") assert result.ret == 0 - result.stdout.fnmatch_lines([ - r'*deps*dep1==2.3, dep2*' - ]) + assert any(re.match(r'.*deps.*dep1==2.3, dep2.*', l) for l in result.outlines) # override dep1 specific version, and force version for dep2 - result = cmd.run("tox", "--showconfig", "--force-dep=dep1", - "--force-dep=dep2==5.0") + result = cmd("--showconfig", "--force-dep=dep1", + "--force-dep=dep2==5.0") assert result.ret == 0 - result.stdout.fnmatch_lines([ - r'*deps*dep1, dep2==5.0*' - ]) + assert any(re.match(r'.*deps.*dep1, dep2==5.0.*', l) for l in result.outlines) @pytest.mark.xfail( "'pypy' not in sys.executable", @@ -2340,7 +2316,7 @@ class TestCmdInvocation: commands = pip --version ''' }) - result = cmd.run("tox") + result = cmd() assert result.ret == 0 diff --git a/tests/test_interpreters.py b/tests/test_interpreters.py index fcc5a7a1..8d3cdcaf 100644 --- a/tests/test_interpreters.py +++ b/tests/test_interpreters.py @@ -47,6 +47,7 @@ def test_locate_via_py(monkeypatch): @staticmethod def communicate(): return sys.executable.encode(), None + return proc # Monkeypatch modules to return our faked value @@ -59,6 +60,7 @@ def test_tox_get_python_executable(): class envconfig: basepython = sys.executable envname = "pyxx" + p = tox_get_python_executable(envconfig) assert p == py.path.local(sys.executable) for ver in "2.7 3.4 3.5 3.6".split(): @@ -86,6 +88,7 @@ def test_find_executable_extra(monkeypatch): @staticmethod def sysfind(x): return "hello" + monkeypatch.setattr(py.path.local, "sysfind", sysfind) class envconfig: @@ -122,6 +125,7 @@ class TestInterpreters: class envconfig: basepython = "1lkj23" envname = "pyxx" + assert not interpreters.get_executable(envconfig) info = interpreters.get_info(envconfig) assert not info.version_info @@ -133,6 +137,7 @@ class TestInterpreters: class envconfig: basepython = sys.executable envname = "123" + info = interpreters.get_info(envconfig) s = interpreters.get_sitepackagesdir(info, "") assert s diff --git a/tests/test_pytest_plugins.py b/tests/test_pytest_plugins.py index d5e943a5..3eb1a12f 100644 --- a/tests/test_pytest_plugins.py +++ b/tests/test_pytest_plugins.py @@ -12,10 +12,10 @@ from tox._pytestplugin import _path_parts class TestInitProj: @pytest.mark.parametrize('kwargs', ( - {}, - {'src_root': None}, - {'src_root': ''}, - {'src_root': '.'})) + {}, + {'src_root': None}, + {'src_root': ''}, + {'src_root': '.'})) def test_no_src_root(self, kwargs, tmpdir, initproj): initproj('black_knight-42', **kwargs) init_file = tmpdir.join('black_knight', 'black_knight', '__init__.py') @@ -64,14 +64,14 @@ class TestInitProj: class TestPathParts: @pytest.mark.parametrize('input, expected', ( - ('', []), - ('/', ['/']), - ('//', ['//']), - ('/a', ['/', 'a']), - ('/a/', ['/', 'a']), - ('/a/b', ['/', 'a', 'b']), - ('a', ['a']), - ('a/b', ['a', 'b']))) + ('', []), + ('/', ['/']), + ('//', ['//']), + ('/a', ['/', 'a']), + ('/a/', ['/', 'a']), + ('/a/b', ['/', 'a', 'b']), + ('a', ['a']), + ('a/b', ['a', 'b']))) def test_path_parts(self, input, expected): assert _path_parts(input) == expected @@ -82,18 +82,18 @@ class TestPathParts: @pytest.mark.parametrize('base, filedefs, target, expected', ( - ('/base', {}, '', False), - ('/base', {}, '/base', False), - ('/base', {'a': {'b': 'data'}}, '', True), - ('/base', {'a': {'b': 'data'}}, 'a', True), - ('/base', {'a': {'b': 'data'}}, 'a/b', True), - ('/base', {'a': {'b': 'data'}}, 'a/x', False), - ('/base', {'a': {'b': 'data'}}, 'a/b/c', False), - ('/base', {'a': {'b': 'data'}}, '/base', True), - ('/base', {'a': {'b': 'data'}}, '/base/a', True), - ('/base', {'a': {'b': 'data'}}, '/base/a/b', True), - ('/base', {'a': {'b': 'data'}}, '/base/a/x', False), - ('/base', {'a': {'b': 'data'}}, '/base/a/b/c', False), - ('/base', {'a': {'b': 'data'}}, '/a', False))) + ('/base', {}, '', False), + ('/base', {}, '/base', False), + ('/base', {'a': {'b': 'data'}}, '', True), + ('/base', {'a': {'b': 'data'}}, 'a', True), + ('/base', {'a': {'b': 'data'}}, 'a/b', True), + ('/base', {'a': {'b': 'data'}}, 'a/x', False), + ('/base', {'a': {'b': 'data'}}, 'a/b/c', False), + ('/base', {'a': {'b': 'data'}}, '/base', True), + ('/base', {'a': {'b': 'data'}}, '/base/a', True), + ('/base', {'a': {'b': 'data'}}, '/base/a/b', True), + ('/base', {'a': {'b': 'data'}}, '/base/a/x', False), + ('/base', {'a': {'b': 'data'}}, '/base/a/b/c', False), + ('/base', {'a': {'b': 'data'}}, '/a', False))) def test_filedefs_contains(base, filedefs, target, expected): assert bool(_filedefs_contains(base, filedefs, target)) == expected diff --git a/tests/test_venv.py b/tests/test_venv.py index af6a84b9..c2ca3e46 100644 --- a/tests/test_venv.py +++ b/tests/test_venv.py @@ -14,6 +14,7 @@ from tox.venv import tox_testenv_create from tox.venv import tox_testenv_install_deps from tox.venv import VirtualEnv + # def test_global_virtualenv(capfd): # v = VirtualEnv() # assert v.list() @@ -544,7 +545,7 @@ class TestVenvTest: monkeypatch.setenv("PATH", "xyz") sysfind_calls = [] monkeypatch.setattr("py.path.local.sysfind", classmethod( - lambda *args, **kwargs: sysfind_calls.append(kwargs) or 0 / 0)) + lambda *args, **kwargs: sysfind_calls.append(kwargs) or 0 / 0)) with pytest.raises(ZeroDivisionError): venv._install(list('123'), action=action) @@ -633,7 +634,7 @@ def test_env_variables_added_to_pcall(tmpdir, mocksession, newconfig, monkeypatc assert pcalls[0].env["YY"] == "456" assert "YY" not in pcalls[1].env - assert {"ENV_VAR", "VIRTUAL_ENV", "PYTHONHASHSEED", "X123", "PATH"}\ + assert {"ENV_VAR", "VIRTUAL_ENV", "PYTHONHASHSEED", "X123", "PATH"} \ .issubset(pcalls[1].env) # setenv does not trigger PYTHONPATH warnings diff --git a/tests/test_z_cmdline.py b/tests/test_z_cmdline.py index 5acb57a4..bdcc68d0 100644 --- a/tests/test_z_cmdline.py +++ b/tests/test_z_cmdline.py @@ -1,11 +1,13 @@ import os import platform +import re import py import pytest import tox from tox._pytestplugin import ReportExpectMock + try: import json except ImportError: @@ -179,11 +181,9 @@ def XXX_test_package(cmd, initproj): """, 'tox.ini': '' }) - result = cmd.run("tox", "package") + result = cmd("package") assert not result.ret - result.stdout.fnmatch_lines([ - "*created sdist package at*", - ]) + assert any(re.match(r'.*created sdist package at.*', l) for l in result.outlines) def test_minversion(cmd, initproj): @@ -194,10 +194,9 @@ def test_minversion(cmd, initproj): minversion = 6.0 ''' }) - result = cmd.run("tox", "-v") - result.stdout.fnmatch_lines([ - "*ERROR*tox version is * required is at least 6.0*" - ]) + result = cmd("-v") + assert re.match(r'ERROR: MinVersionError: tox version is .*,' + r' required is at least 6.0', result.out) assert result.ret @@ -205,13 +204,10 @@ def test_notoxini_help_still_works(initproj, cmd): initproj("example123-0.5", filedefs={ 'tests': {'test_hello.py': "def test_hello(): pass"}, }) - result = cmd.run("tox", "-h") - result.stderr.fnmatch_lines([ - "*ERROR*tox.ini*" - ]) - result.stdout.fnmatch_lines([ - "*--help*" - ]) + result = cmd("-h") + assert result.err == "ERROR: toxini file 'tox.ini' not found\n" + assert result.out.startswith('usage: ') + assert any('--help' in l for l in result.outlines) assert not result.ret @@ -219,13 +215,8 @@ def test_notoxini_help_ini_still_works(initproj, cmd): initproj("example123-0.5", filedefs={ 'tests': {'test_hello.py': "def test_hello(): pass"}, }) - result = cmd.run("tox", "--help-ini") - result.stderr.fnmatch_lines([ - "*ERROR*tox.ini*" - ]) - result.stdout.fnmatch_lines([ - "*setenv*" - ]) + result = cmd("--help-ini") + assert any('setenv' in l for l in result.outlines) assert not result.ret @@ -236,11 +227,9 @@ def test_envdir_equals_toxini_errors_out(cmd, initproj): envdir={toxinidir} ''' }) - result = cmd.run("tox") - result.stdout.fnmatch_lines([ - "ERROR*venv*delete*", - "*ConfigError*envdir must not equal toxinidir*", - ]) + result = cmd() + assert result.outlines[1] == "ERROR: ConfigError: envdir must not equal toxinidir" + assert re.match(r'ERROR: venv \'python\' in .* would delete project', result.outlines[0]) assert result.ret @@ -251,10 +240,9 @@ def test_run_custom_install_command_error(cmd, initproj): install_command=./tox.ini {opts} {packages} ''' }) - result = cmd.run("tox") - result.stdout.fnmatch_lines([ - "ERROR: invocation failed (errno *), args: ['*/tox.ini*", - ]) + result = cmd() + assert re.match(r"ERROR: invocation failed \(errno \d+\), args: \['.*[/\\]tox\.ini", + result.outlines[-1]) assert result.ret @@ -268,17 +256,13 @@ def test_unknown_interpreter_and_env(cmd, initproj): changedir=tests ''' }) - result = cmd.run("tox") + result = cmd() assert result.ret - result.stdout.fnmatch_lines([ - "*ERROR*InterpreterNotFound*xyz_unknown_interpreter*", - ]) + assert any('ERROR: InterpreterNotFound: xyz_unknown_interpreter' == l for l in result.outlines) - result = cmd.run("tox", "-exyz") + result = cmd("-exyz") assert result.ret - result.stdout.fnmatch_lines([ - "*ERROR*unknown*", - ]) + assert result.out == "ERROR: unknown environment 'xyz'\n" def test_unknown_interpreter(cmd, initproj): @@ -291,11 +275,9 @@ def test_unknown_interpreter(cmd, initproj): changedir=tests ''' }) - result = cmd.run("tox") + result = cmd() assert result.ret - result.stdout.fnmatch_lines([ - "*ERROR*InterpreterNotFound*xyz_unknown_interpreter*", - ]) + assert any('ERROR: InterpreterNotFound: xyz_unknown_interpreter' == l for l in result.outlines) def test_skip_platform_mismatch(cmd, initproj): @@ -307,11 +289,9 @@ def test_skip_platform_mismatch(cmd, initproj): platform=x123 ''' }) - result = cmd.run("tox") + result = cmd() assert not result.ret - result.stdout.fnmatch_lines(""" - SKIPPED*platform mismatch* - """) + assert any('SKIPPED: python: platform mismatch' == l for l in result.outlines) def test_skip_unknown_interpreter(cmd, initproj): @@ -324,11 +304,10 @@ def test_skip_unknown_interpreter(cmd, initproj): changedir=tests ''' }) - result = cmd.run("tox", "--skip-missing-interpreters") + result = cmd("--skip-missing-interpreters") assert not result.ret - result.stdout.fnmatch_lines([ - "*SKIPPED*InterpreterNotFound*xyz_unknown_interpreter*", - ]) + msg = 'SKIPPED: python: InterpreterNotFound: xyz_unknown_interpreter' + assert any(msg == l for l in result.outlines) def test_unknown_dep(cmd, initproj): @@ -340,11 +319,9 @@ def test_unknown_dep(cmd, initproj): changedir=tests ''' }) - result = cmd.run("tox") + result = cmd() assert result.ret - result.stdout.fnmatch_lines([ - "*ERROR*could not install*qweqwe123*", - ]) + assert result.outlines[-1].startswith('ERROR: python: could not install deps [qweqwe123];') def test_venv_special_chars_issue252(cmd, initproj): @@ -357,22 +334,19 @@ def test_venv_special_chars_issue252(cmd, initproj): changedir=tests ''' }) - result = cmd.run("tox") + result = cmd() assert result.ret == 0 - result.stdout.fnmatch_lines([ - "*installed*pkg123*" - ]) + pattern = re.compile('special&&1 installed: .*pkg123==0.7.*') + assert any(pattern.match(line) for line in result.outlines) def test_unknown_environment(cmd, initproj): initproj("env123-0.7", filedefs={ 'tox.ini': '' }) - result = cmd.run("tox", "-e", "qpwoei") + result = cmd("-e", "qpwoei") assert result.ret - result.stdout.fnmatch_lines([ - "*ERROR*unknown*environment*qpwoei*", - ]) + assert result.out == "ERROR: unknown environment 'qpwoei'\n" def test_skip_sdist(cmd, initproj): @@ -388,7 +362,7 @@ def test_skip_sdist(cmd, initproj): commands=python -c "print('done')" ''' }) - result = cmd.run("tox") + result = cmd() assert result.ret == 0 @@ -398,13 +372,10 @@ def test_minimal_setup_py_empty(cmd, initproj): 'setup.py': """ """, 'tox.ini': '' - }) - result = cmd.run("tox") + result = cmd() assert result.ret == 1 - result.stdout.fnmatch_lines([ - "*ERROR*empty*", - ]) + assert result.outlines[-1] == 'ERROR: setup.py is empty' def test_minimal_setup_py_comment_only(cmd, initproj): @@ -416,11 +387,9 @@ def test_minimal_setup_py_comment_only(cmd, initproj): 'tox.ini': '' }) - result = cmd.run("tox") + result = cmd() assert result.ret == 1 - result.stdout.fnmatch_lines([ - "*ERROR*empty*", - ]) + assert result.outlines[-1] == 'ERROR: setup.py is empty' def test_minimal_setup_py_non_functional(cmd, initproj): @@ -433,11 +402,9 @@ def test_minimal_setup_py_non_functional(cmd, initproj): 'tox.ini': '' }) - result = cmd.run("tox") + result = cmd() assert result.ret == 1 - result.stdout.fnmatch_lines([ - "*ERROR*check setup.py*", - ]) + assert any(re.match(r'.*ERROR.*check setup.py.*', l) for l in result.outlines) def test_sdist_fails(cmd, initproj): @@ -448,11 +415,9 @@ def test_sdist_fails(cmd, initproj): """, 'tox.ini': '', }) - result = cmd.run("tox") + result = cmd() assert result.ret - result.stdout.fnmatch_lines([ - "*FAIL*could not package project*", - ]) + assert any(re.match(r'.*FAIL.*could not package project.*', l) for l in result.outlines) def test_no_setup_py_exits(cmd, initproj): @@ -463,11 +428,9 @@ def test_no_setup_py_exits(cmd, initproj): """ }) os.remove("setup.py") - result = cmd.run("tox") + result = cmd() assert result.ret - result.stdout.fnmatch_lines([ - "*ERROR*No setup.py file found*" - ]) + assert any(re.match(r'.*ERROR.*No setup.py file found.*', l) for l in result.outlines) def test_package_install_fails(cmd, initproj): @@ -487,81 +450,73 @@ def test_package_install_fails(cmd, initproj): """, 'tox.ini': '', }) - result = cmd.run("tox") + result = cmd() assert result.ret - result.stdout.fnmatch_lines([ - "*InvocationError*", - ]) + assert result.outlines[-1].startswith('ERROR: python: InvocationError: ') -class TestToxRun: - @pytest.fixture - def example123(self, initproj): - initproj("example123-0.5", filedefs={ - 'tests': { - 'test_hello.py': """ - def test_hello(pytestconfig): - pass - """, - }, - 'tox.ini': ''' - [testenv] - changedir=tests - commands= pytest --basetemp={envtmpdir} \ - --junitxml=junit-{envname}.xml - deps=pytest - ''' - }) +@pytest.fixture +def example123(initproj): + yield initproj("example123-0.5", filedefs={ + 'tests': { + 'test_hello.py': """ + def test_hello(pytestconfig): + pass + """, + }, + 'tox.ini': ''' + [testenv] + changedir=tests + commands= pytest --basetemp={envtmpdir} \ + --junitxml=junit-{envname}.xml + deps=pytest + ''' + }) + + +def test_toxuone_env(cmd, example123): + result = cmd() + assert not result.ret + assert re.match(r'.*generated\W+xml\W+file.*junit-python\.xml' + r'.*\W+1\W+passed.*', result.out, re.DOTALL) + result = cmd("-epython", ) + assert not result.ret + assert re.match(r'.*\W+1\W+passed.*' + r'summary.*' + r'python:\W+commands\W+succeeded.*', result.out, re.DOTALL) + + +def test_different_config_cwd(cmd, example123, monkeypatch): + # see that things work with a different CWD + monkeypatch.chdir(example123.dirname) + result = cmd("-c", "example123/tox.ini") + assert not result.ret + assert re.match(r'.*\W+1\W+passed.*' + r'summary.*' + r'python:\W+commands\W+succeeded.*', result.out, re.DOTALL) + + +def test_json(cmd, example123): + # see that tests can also fail and retcode is correct + testfile = py.path.local("tests").join("test_hello.py") + assert testfile.check() + testfile.write("def test_fail(): assert 0") + jsonpath = example123.join("res.json") + result = cmd("--result-json", jsonpath) + assert result.ret == 1 + data = json.load(jsonpath.open("r")) + verify_json_report_format(data) + assert re.match(r'.*\W+1\W+failed.*' + r'summary.*' + r'python:\W+commands\W+failed.*', result.out, re.DOTALL) + - def test_toxuone_env(self, cmd, example123): - result = cmd.run("tox") - assert not result.ret - result.stdout.fnmatch_lines([ - "*junit-python.xml*", - "*1 passed*", - ]) - result = cmd.run("tox", "-epython") - assert not result.ret - result.stdout.fnmatch_lines([ - "*1 passed*", - "*summary*", - "*python: commands succeeded" - ]) - - def test_different_config_cwd(self, cmd, example123, monkeypatch): - # see that things work with a different CWD - monkeypatch.chdir(cmd.tmpdir) - result = cmd.run("tox", "-c", "example123/tox.ini") - assert not result.ret - result.stdout.fnmatch_lines([ - "*1 passed*", - "*summary*", - "*python: commands succeeded" - ]) - - def test_json(self, cmd, example123): - # see that tests can also fail and retcode is correct - testfile = py.path.local("tests").join("test_hello.py") - assert testfile.check() - testfile.write("def test_fail(): assert 0") - jsonpath = cmd.tmpdir.join("res.json") - result = cmd.run("tox", "--result-json", jsonpath) - assert result.ret == 1 - data = json.load(jsonpath.open("r")) - verify_json_report_format(data) - result.stdout.fnmatch_lines([ - "*1 failed*", - "*summary*", - "*python: *failed*", - ]) - - -def test_develop(initproj, cmd): +def test_developz(initproj, cmd): initproj("example123", filedefs={'tox.ini': """ """}) - result = cmd.run("tox", "-vv", "--develop") + result = cmd("-vv", "--develop") assert not result.ret - assert "sdist-make" not in result.stdout.str() + assert "sdist-make" not in result.out def test_usedevelop(initproj, cmd): @@ -569,9 +524,9 @@ def test_usedevelop(initproj, cmd): [testenv] usedevelop=True """}) - result = cmd.run("tox", "-vv") + result = cmd("-vv") assert not result.ret - assert "sdist-make" not in result.stdout.str() + assert "sdist-make" not in result.out def test_usedevelop_mixed(initproj, cmd): @@ -583,19 +538,19 @@ def test_usedevelop_mixed(initproj, cmd): """}) # running only 'devenv' should not do sdist - result = cmd.run("tox", "-vv", "-e", "devenv") + result = cmd("-vv", "-e", "devenv") assert not result.ret - assert "sdist-make" not in result.stdout.str() + assert "sdist-make" not in result.out # running all envs should do sdist - result = cmd.run("tox", "-vv") + result = cmd("-vv") assert not result.ret - assert "sdist-make" in result.stdout.str() + assert "sdist-make" in result.out @pytest.mark.parametrize("src_root", [".", "src"]) -def test_test_usedevelop(cmd, initproj, src_root): - initproj("example123-0.5", src_root=src_root, filedefs={ +def test_test_usedevelop(cmd, initproj, src_root, monkeypatch): + base = initproj("example123-0.5", src_root=src_root, filedefs={ 'tests': { 'test_hello.py': """ def test_hello(pytestconfig): @@ -611,50 +566,45 @@ def test_test_usedevelop(cmd, initproj, src_root): deps=pytest ''' }) - result = cmd.run("tox", "-v") + result = cmd("-v") assert not result.ret - result.stdout.fnmatch_lines([ - "*junit-python.xml*", - "*1 passed*", - ]) - assert "sdist-make" not in result.stdout.str() - result = cmd.run("tox", "-epython") + assert re.match(r'.*generated\W+xml\W+file.*junit-python\.xml' + r'.*\W+1\W+passed.*', result.out, re.DOTALL) + assert "sdist-make" not in result.out + result = cmd("-epython", ) assert not result.ret - assert "develop-inst-noop" in result.stdout.str() - result.stdout.fnmatch_lines([ - "*1 passed*", - "*summary*", - "*python: commands succeeded" - ]) + assert "develop-inst-noop" in result.out + assert re.match(r'.*\W+1\W+passed.*' + r'summary.*' + r'python:\W+commands\W+succeeded.*', result.out, re.DOTALL) + # see that things work with a different CWD - old = cmd.tmpdir.chdir() - result = cmd.run("tox", "-c", "example123/tox.ini") + monkeypatch.chdir(base.dirname) + result = cmd("-c", "example123/tox.ini") assert not result.ret - assert "develop-inst-noop" in result.stdout.str() - result.stdout.fnmatch_lines([ - "*1 passed*", - "*summary*", - "*python: commands succeeded" - ]) - old.chdir() + assert "develop-inst-noop" in result.out + assert re.match(r'.*\W+1\W+passed.*' + r'summary.*' + r'python:\W+commands\W+succeeded.*', result.out, re.DOTALL) + monkeypatch.chdir(base) + # see that tests can also fail and retcode is correct testfile = py.path.local("tests").join("test_hello.py") assert testfile.check() testfile.write("def test_fail(): assert 0") - result = cmd.run("tox") + result = cmd() assert result.ret - assert "develop-inst-noop" in result.stdout.str() - result.stdout.fnmatch_lines([ - "*1 failed*", - "*summary*", - "*python: *failed*", - ]) + assert "develop-inst-noop" in result.out + assert re.match(r'.*\W+1\W+failed.*' + r'summary.*' + r'python:\W+commands\W+failed.*', result.out, re.DOTALL) + # test develop is called if setup.py changes setup_py = py.path.local("setup.py") setup_py.write(setup_py.read() + ' ') - result = cmd.run("tox") + result = cmd() assert result.ret - assert "develop-inst-nodeps" in result.stdout.str() + assert "develop-inst-nodeps" in result.out def _alwayscopy_not_supported(): @@ -667,16 +617,16 @@ def _alwayscopy_not_supported(): return False -@pytest.mark.skipif(_alwayscopy_not_supported(), reason="Platform doesn't support alwayscopy") +@pytest.mark.skipif(_alwayscopy_not_supported(), reason="Platform doesnt support alwayscopy") def test_alwayscopy(initproj, cmd): initproj("example123", filedefs={'tox.ini': """ [testenv] commands={envpython} --version alwayscopy=True """}) - result = cmd.run("tox", "-vv") + result = cmd("-vv") assert not result.ret - assert "virtualenv --always-copy" in result.stdout.str() + assert "virtualenv --always-copy" in result.out def test_alwayscopy_default(initproj, cmd): @@ -684,9 +634,9 @@ def test_alwayscopy_default(initproj, cmd): [testenv] commands={envpython} --version """}) - result = cmd.run("tox", "-vv") + result = cmd("-vv") assert not result.ret - assert "virtualenv --always-copy" not in result.stdout.str() + assert "virtualenv --always-copy" not in result.out def test_empty_activity_ignored(initproj, cmd): @@ -695,9 +645,9 @@ def test_empty_activity_ignored(initproj, cmd): list_dependencies_command=echo commands={envpython} --version """}) - result = cmd.run("tox") + result = cmd() assert not result.ret - assert "installed:" not in result.stdout.str() + assert "installed:" not in result.out def test_empty_activity_shown_verbose(initproj, cmd): @@ -706,9 +656,9 @@ def test_empty_activity_shown_verbose(initproj, cmd): list_dependencies_command=echo commands={envpython} --version """}) - result = cmd.run("tox", "-v") + result = cmd("-v") assert not result.ret - assert "installed:" in result.stdout.str() + assert "installed:" in result.out def test_test_piphelp(initproj, cmd): @@ -716,74 +666,64 @@ def test_test_piphelp(initproj, cmd): # content of: tox.ini [testenv] commands=pip -h - [testenv:py36] + [testenv:py26] basepython=python [testenv:py27] basepython=python """}) - result = cmd.run("tox") + result = cmd() assert not result.ret def test_notest(initproj, cmd): initproj("example123", filedefs={'tox.ini': """ # content of: tox.ini - [testenv:py36] + [testenv:py26] basepython=python """}) - result = cmd.run("tox", "-v", "--notest") + result = cmd("-v", "--notest") assert not result.ret - result.stdout.fnmatch_lines([ - "*summary*", - "*py36*skipped tests*", - ]) - result = cmd.run("tox", "-v", "--notest", "-epy36") + assert re.match(r'.*summary.*' + r'py26\W+skipped\W+tests.*', result.out, re.DOTALL) + result = cmd("-v", "--notest", "-epy26") assert not result.ret - result.stdout.fnmatch_lines([ - "*py36*reusing*", - ]) + assert re.match(r'.*py26\W+reusing.*', result.out, re.DOTALL) def test_PYC(initproj, cmd, monkeypatch): initproj("example123", filedefs={'tox.ini': ''}) monkeypatch.setenv("PYTHONDOWNWRITEBYTECODE", 1) - result = cmd.run("tox", "-v", "--notest") + result = cmd("-v", "--notest") assert not result.ret - result.stdout.fnmatch_lines([ - "*create*", - ]) + assert 'create' in result.out def test_env_VIRTUALENV_PYTHON(initproj, cmd, monkeypatch): initproj("example123", filedefs={'tox.ini': ''}) monkeypatch.setenv("VIRTUALENV_PYTHON", '/FOO') - result = cmd.run("tox", "-v", "--notest") - assert not result.ret, result.stdout.lines - result.stdout.fnmatch_lines([ - "*create*", - ]) + result = cmd("-v", "--notest") + assert not result.ret, result.outlines + assert 'create' in result.out def test_sdistonly(initproj, cmd): initproj("example123", filedefs={'tox.ini': """ """}) - result = cmd.run("tox", "-v", "--sdistonly") + result = cmd("-v", "--sdistonly") assert not result.ret - result.stdout.fnmatch_lines([ - "*sdist-make*setup.py*", - ]) - assert "-mvirtualenv" not in result.stdout.str() + assert re.match(r'.*sdist-make.*setup.py.*', result.out, re.DOTALL) + assert "-mvirtualenv" not in result.out -def test_separate_sdist_no_sdistfile(cmd, initproj): - distshare = cmd.tmpdir.join("distshare") +def test_separate_sdist_no_sdistfile(cmd, initproj, tmpdir): + distshare = tmpdir.join("distshare") initproj(("pkg123-foo", "0.7"), filedefs={ 'tox.ini': """ [tox] - distshare=%s - """ % distshare + distshare={} + """.format(distshare) }) - result = cmd.run("tox", "--sdistonly") + result = cmd("--sdistonly") assert not result.ret distshare_files = distshare.listdir() assert len(distshare_files) == 1 @@ -791,8 +731,8 @@ def test_separate_sdist_no_sdistfile(cmd, initproj): assert 'pkg123-foo-0.7.zip' in str(sdistfile) -def test_separate_sdist(cmd, initproj): - distshare = cmd.tmpdir.join("distshare") +def test_separate_sdist(cmd, initproj, tmpdir): + distshare = tmpdir.join("distshare") initproj("pkg123-0.7", filedefs={ 'tox.ini': """ [tox] @@ -800,16 +740,14 @@ def test_separate_sdist(cmd, initproj): sdistsrc={distshare}/pkg123-0.7.zip """ % distshare }) - result = cmd.run("tox", "--sdistonly") + result = cmd("--sdistonly") assert not result.ret sdistfiles = distshare.listdir() assert len(sdistfiles) == 1 sdistfile = sdistfiles[0] - result = cmd.run("tox", "-v", "--notest") + result = cmd("-v", "--notest") assert not result.ret - result.stdout.fnmatch_lines([ - "*inst*%s*" % sdistfile, - ]) + assert "python inst: {}".format(sdistfile) in result.out def test_sdist_latest(tmpdir, newconfig): @@ -841,11 +779,9 @@ def test_envsitepackagesdir(cmd, initproj): commands= python -c "print(r'X:{envsitepackagesdir}')" """}) - result = cmd.run("tox") + result = cmd() assert result.ret == 0 - result.stdout.fnmatch_lines(""" - X:*tox*site-packages* - """) + assert re.match(r'.*\nX:.*tox.*site-packages.*', result.out, re.DOTALL) def test_envsitepackagesdir_skip_missing_issue280(cmd, initproj): @@ -856,11 +792,9 @@ def test_envsitepackagesdir_skip_missing_issue280(cmd, initproj): commands= {envsitepackagesdir} """}) - result = cmd.run("tox", "--skip-missing-interpreters") + result = cmd("--skip-missing-interpreters") assert result.ret == 0 - result.stdout.fnmatch_lines(""" - SKIPPED:*qwelkj* - """) + assert re.match(r'.*SKIPPED:.*qwelkj.*', result.out, re.DOTALL) @pytest.mark.parametrize('verbosity', ['', '-v', '-vv']) @@ -869,7 +803,7 @@ def test_verbosity(cmd, initproj, verbosity): 'tox.ini': """ [testenv] """}) - result = cmd.run("tox", verbosity) + result = cmd(verbosity) assert result.ret == 0 needle = "Successfully installed pkgX-0.0.5" @@ -918,15 +852,16 @@ def test_envtmpdir(initproj, cmd): ''' }) - result = cmd.run("tox") + result = cmd() assert not result.ret - result = cmd.run("tox") + result = cmd() assert not result.ret def test_missing_env_fails(initproj, cmd): initproj("foo", filedefs={'tox.ini': "[testenv:foo]\ncommands={env:VAR}"}) - result = cmd.run("tox") + result = cmd() assert result.ret == 1 - result.stdout.fnmatch_lines(["*foo: unresolvable substitution(s): 'VAR'*"]) + assert result.out.endswith("foo: unresolvable substitution(s): 'VAR'." + " Environment variables are missing or defined recursively.\n") @@ -68,9 +68,10 @@ commands = echo {posargs} [testenv:dev] description = generate a DEV environment -extras = testing, docs, lint +extras = testing, docs # required to make looponfail reload on every source code change usedevelop = True +basepython = python3.6 commands = python -m pip list --format=columns python -c 'import sys; print(sys.executable)' diff --git a/tox/__init__.py b/tox/__init__.py index 4f2c0bdb..83b0e80d 100644 --- a/tox/__init__.py +++ b/tox/__init__.py @@ -19,23 +19,31 @@ class exception: class MissingSubstitution(Error): FLAG = 'TOX_MISSING_SUBSTITUTION' """placeholder for debugging configurations""" + def __init__(self, name): self.name = name class ConfigError(Error): """ error in tox configuration. """ + class UnsupportedInterpreter(Error): """signals an unsupported Interpreter""" + class InterpreterNotFound(Error): """signals that an interpreter could not be found""" + class InvocationError(Error): """ an error while invoking a script. """ + class MissingFile(Error): """ an error while invoking a script. """ + class MissingDirectory(Error): """ a directory did not exist. """ + class MissingDependency(Error): """ a dependency could not be found or determined. """ + class MinVersionError(Error): """ the installed tox version is lower than requested minversion. """ diff --git a/tox/__main__.py b/tox/__main__.py index 04719886..d1509550 100644 --- a/tox/__main__.py +++ b/tox/__main__.py @@ -1,4 +1,6 @@ +import sys + from tox.session import main if __name__ == '__main__': - main() + main(sys.argv[1:]) diff --git a/tox/_pytestplugin.py b/tox/_pytestplugin.py index e63acc75..77ee7711 100644 --- a/tox/_pytestplugin.py +++ b/tox/_pytestplugin.py @@ -1,8 +1,7 @@ from __future__ import print_function +from __future__ import unicode_literals import os -import os.path -import subprocess import sys import textwrap import time @@ -15,7 +14,7 @@ import six import tox from .config import parseconfig from .result import ResultLog -from .session import Action +from .session import main from .venv import VirtualEnv @@ -33,7 +32,7 @@ def pytest_addoption(parser): def pytest_report_header(): - return "tox comes from: %r" % (tox.__file__) + return "tox comes from: {}".format(repr(tox.__file__)) @pytest.fixture @@ -50,14 +49,62 @@ def newconfig(request, tmpdir): return parseconfig(args, plugins=plugins) finally: old.chdir() + return newconfig @pytest.fixture -def cmd(request): +def cmd(request, capfd, monkeypatch): if request.config.option.no_network: pytest.skip("--no-network was specified, test cannot run") - return Cmd(request) + request.addfinalizer(py.path.local().chdir) + + def run(*argv): + if sys.version_info[:2] < (2, 7): + pytest.skip("can not run tests involving calling tox on python2.6. " + "(and python2.6 is about to be deprecated anyway)") + key = str(b'PYTHONPATH') + python_paths = (i for i in (str(os.getcwd()), os.getenv(key)) if i) + monkeypatch.setenv(key, os.pathsep.join(python_paths)) + with RunResult(capfd, argv) as result: + try: + main([str(x) for x in argv]) + assert False # this should always exist with SystemExit + except SystemExit as exception: + result.ret = exception.code + except OSError as e: + result.ret = e.errno + return result + + yield run + + +class RunResult: + def __init__(self, capfd, args): + self._capfd = capfd + self.args = args + self.ret = None + self.duration = None + self.out = None + self.err = None + + def __enter__(self): + self._start = time.time() + # noinspection PyProtectedMember + self._capfd._start() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.duration = time.time() - self._start + self.out, self.err = self._capfd.readouterr() + + @property + def outlines(self): + return self.out.splitlines() + + def __repr__(self): + return 'RunResult(ret={}, args={}, out=\n{}\n, err=\n{})'.format( + self.ret, ' '.join(str(i) for i in self.args), self.out, self.err) class ReportExpectMock: @@ -78,13 +125,9 @@ class ReportExpectMock: def generic_report(*args, **kwargs): self._calls.append((name,) + args) - print("%s" % (self._calls[-1], )) - return generic_report + print("%s" % (self._calls[-1],)) - def action(self, venv, msg, *args): - self._calls.append(("action", venv, msg)) - print("%s" % (self._calls[-1], )) - return Action(self.session, venv, msg, args) + return generic_report def getnext(self, cat): __tracebackhide__ = True @@ -170,6 +213,7 @@ def mocksession(request): pm = pcallMock(args, cwd, env, stdout, stderr, shell) self._pcalls.append(pm) return pm + return MockSession() @@ -181,55 +225,8 @@ def newmocksession(request): def newmocksession(args, source, plugins=()): mocksession.config = newconfig(args, source, plugins=plugins) return mocksession - return newmocksession - -class Cmd: - def __init__(self, request): - self.tmpdir = request.getfixturevalue("tmpdir") - self.request = request - current = py.path.local() - self.request.addfinalizer(current.chdir) - - def chdir(self, target): - target.chdir() - - def popen(self, argv, stdout, stderr, **kw): - env = os.environ.copy() - env['PYTHONPATH'] = ":".join(filter(None, [ - str(os.getcwd()), env.get('PYTHONPATH', '')])) - kw['env'] = env - # print "env", env - return subprocess.Popen(argv, stdout=stdout, stderr=stderr, **kw) - - def run(self, *argv): - argv = [str(x) for x in argv] - assert py.path.local.sysfind(str(argv[0])), argv[0] - p1 = self.tmpdir.join("stdout") - p2 = self.tmpdir.join("stderr") - print("%s$ %s" % (os.getcwd(), " ".join(argv))) - f1 = p1.open("wb") - f2 = p2.open("wb") - now = time.time() - popen = self.popen(argv, stdout=f1, stderr=f2, - close_fds=(sys.platform != "win32")) - ret = popen.wait() - f1.close() - f2.close() - out = p1.read("rb") - out = getdecoded(out).splitlines() - err = p2.read("rb") - err = getdecoded(err).splitlines() - - def dump_lines(lines, fp): - try: - for line in lines: - print(line, file=fp) - except UnicodeEncodeError: - print("couldn't print to %s because of encoding" % (fp,)) - dump_lines(out, sys.stdout) - dump_lines(err, sys.stderr) - return RunResult(ret, out, err, time.time() - now) + return newmocksession def getdecoded(out): @@ -240,55 +237,6 @@ def getdecoded(out): py.io.saferepr(out),) -class RunResult: - def __init__(self, ret, outlines, errlines, duration): - self.ret = ret - self.outlines = outlines - self.errlines = errlines - self.stdout = LineMatcher(outlines) - self.stderr = LineMatcher(errlines) - self.duration = duration - - -class LineMatcher: - def __init__(self, lines): - self.lines = lines - - def str(self): - return "\n".join(self.lines) - - def fnmatch_lines(self, lines2): - if isinstance(lines2, str): - lines2 = py.code.Source(lines2) - if isinstance(lines2, py.code.Source): - lines2 = lines2.strip().lines - - from fnmatch import fnmatch - lines1 = self.lines[:] - nextline = None - extralines = [] - __tracebackhide__ = True - for line in lines2: - nomatchprinted = False - while lines1: - nextline = lines1.pop(0) - if line == nextline: - print("exact match:", repr(line)) - break - elif fnmatch(nextline, line): - print("fnmatch:", repr(line)) - print(" with:", repr(nextline)) - break - else: - if not nomatchprinted: - print("nomatch:", repr(line)) - nomatchprinted = True - print(" and:", repr(nextline)) - extralines.append(nextline) - else: - assert line == nextline - - @pytest.fixture def initproj(request, tmpdir): """Create a factory function for creating example projects @@ -315,13 +263,14 @@ def initproj(request, tmpdir): setup.py """ + def initproj(nameversion, filedefs=None, src_root="."): if filedefs is None: filedefs = {} if not src_root: src_root = '.' if isinstance(nameversion, six.string_types): - parts = nameversion.split("-") + parts = nameversion.split(str("-")) if len(parts) == 1: parts.append("0.1") name, version = parts @@ -362,6 +311,8 @@ def initproj(request, tmpdir): print("created project in %s" % (base,)) base.chdir() + return base + return initproj @@ -414,6 +365,6 @@ def create_files(base, filedefs): for key, value in filedefs.items(): if isinstance(value, dict): create_files(base.ensure(key, dir=1), value) - elif isinstance(value, str): + elif isinstance(value, six.string_types): s = textwrap.dedent(value) base.join(key).write(s) diff --git a/tox/_quickstart.py b/tox/_quickstart.py index 4d3c683f..d685f12a 100644 --- a/tox/_quickstart.py +++ b/tox/_quickstart.py @@ -55,7 +55,6 @@ try: except NameError: term_input = input - all_envs = ['py27', 'py34', 'py35', 'py36', 'pypy', 'jython'] PROMPT_PREFIX = '> ' @@ -90,6 +89,7 @@ def choice(*l): if x not in l: raise ValidationError('Please enter one of %s.' % ', '.join(l)) return x + return val @@ -119,7 +119,7 @@ def do_prompt(d, key, text, default=None, validator=nonempty): x = term_input(prompt) if default and not x: x = default - if sys.version_info < (3, ) and not isinstance(x, unicode): # noqa + if sys.version_info < (3,) and not isinstance(x, unicode): # noqa # for Python 2.x, try to get a Unicode string out of it if x.decode('ascii', 'replace').encode('ascii', 'replace') != x: if TERM_ENCODING: diff --git a/tox/_verlib.py b/tox/_verlib.py index dca82156..79f9a11e 100644 --- a/tox/_verlib.py +++ b/tox/_verlib.py @@ -73,6 +73,7 @@ class NormalizedVersion(object): 1.2a # release level must have a release serial 1.2.3b """ + def __init__(self, s, error_on_huge_major_num=True): """Create a NormalizedVersion instance from a version string. diff --git a/tox/config.py b/tox/config.py index abbe3d87..d03c97e0 100755 --- a/tox/config.py +++ b/tox/config.py @@ -224,19 +224,15 @@ class InstallcmdOption: return value -def parseconfig(args=None, plugins=()): +def parseconfig(args, plugins=()): """ - :param list[str] args: Optional list of arguments. + :param list[str] args: list of arguments. :type pkg: str :rtype: :class:`Config` :raise SystemExit: toxinit file is not found """ pm = get_plugin_manager(plugins) - - if args is None: - args = sys.argv[1:] - # prepare command line options parser = Parser() pm.hook.tox_addoption(parser=parser) @@ -357,7 +353,7 @@ def tox_addoption(parser): help="show help about ini-names") parser.add_argument("-v", action='count', dest="verbose_level", default=0, help="increase verbosity of reporting output. -vv mode turns off " - "output redirection for package installation") + "output redirection for package installation") parser.add_argument("-q", action="count", dest="quiet_level", default=0, help="progressively silence reporting output.") parser.add_argument("--showconfig", action="store_true", @@ -400,7 +396,7 @@ def tox_addoption(parser): parser.add_argument("--result-json", action="store", dest="resultjson", metavar="PATH", help="write a json file with detailed information " - "about all commands and results involved.") + "about all commands and results involved.") # We choose 1 to 4294967295 because it is the range of PYTHONHASHSEED. parser.add_argument("--hashseed", action="store", @@ -526,16 +522,16 @@ def tox_addoption(parser): # so we just pass it on by default for now. if sys.platform == "win32": passenv.add("SYSTEMDRIVE") # needed for pip6 - passenv.add("SYSTEMROOT") # needed for python's crypto module - passenv.add("PATHEXT") # needed for discovering executables - passenv.add("COMSPEC") # needed for distutils cygwincompiler + passenv.add("SYSTEMROOT") # needed for python's crypto module + passenv.add("PATHEXT") # needed for discovering executables + passenv.add("COMSPEC") # needed for distutils cygwincompiler passenv.add("TEMP") passenv.add("TMP") # for `multiprocessing.cpu_count()` on Windows # (prior to Python 3.4). passenv.add("NUMBER_OF_PROCESSORS") passenv.add("USERPROFILE") # needed for `os.path.expanduser()` - passenv.add("MSYSTEM") # fixes #429 + passenv.add("MSYSTEM") # fixes #429 else: passenv.add("TMPDIR") for spec in value: @@ -621,6 +617,7 @@ def tox_addoption(parser): class Config(object): """ Global Tox config object. """ + def __init__(self, pluginmanager, option, interpreters): #: dictionary containing envname to envconfig mappings self.envconfigs = {} @@ -643,6 +640,7 @@ class TestenvConfig: In addition to some core attributes/properties this config object holds all per-testenv ini attributes as attributes, see "tox --help-ini" for an overview. """ + def __init__(self, envname, config, factors, reader): #: test environment name self.envname = envname @@ -662,7 +660,7 @@ class TestenvConfig: def get_envbindir(self): """ path to directory where scripts/binaries reside. """ if sys.platform == "win32" and "jython" not in self.basepython and \ - "pypy" not in self.basepython: + "pypy" not in self.basepython: return self.envdir.join("Scripts") else: return self.envdir.join("bin") @@ -709,9 +707,6 @@ class TestenvConfig: if not info.version_info: raise tox.exception.InvocationError( 'Failed to get version_info for %s: %s' % (info.name, info.err)) - if info.version_info < (2, 6): - raise tox.exception.UnsupportedInterpreter( - "python2.5 is not supported anymore, sorry") return info.executable @@ -885,10 +880,10 @@ class parseini: return tc def _getenvdata(self, reader): - envstr = self.config.option.env \ - or os.environ.get("TOXENV") \ - or reader.getstring("envlist", replace=False) \ - or [] + envstr = self.config.option.env \ + or os.environ.get("TOXENV") \ + or reader.getstring("envlist", replace=False) \ + or [] envlist = _split_env(envstr) # collect section envs @@ -970,6 +965,7 @@ class DepConfig: return self.name return ":%s:%s" % (self.indexserver.name, self.name) return str(self.name) + __repr__ = __str__ @@ -1048,7 +1044,7 @@ class SectionReader: s = default if s is None: raise KeyError("no config value [%s] %s found" % ( - self.section_name, name)) + self.section_name, name)) if not isinstance(s, bool): if s.lower() == "true": @@ -1094,8 +1090,8 @@ class SectionReader: expr, line = m.groups() if any(included <= self.factors - and not any(x in self.factors for x in excluded) - for included, excluded in _split_factor_expr(expr)): + and not any(x in self.factors for x in excluded) + for included, excluded in _split_factor_expr(expr)): return line lines = s.strip().splitlines() @@ -1137,6 +1133,7 @@ class Replacer: ''' Recursively expand substitutions starting from the innermost expression ''' + def substitute_once(x): return self.RE_ITEM_REF.sub(self._replace_match, x) @@ -1294,7 +1291,6 @@ class _ArgvlistReader: class CommandParser(object): - class State(object): def __init__(self): self.word = '' diff --git a/tox/session.py b/tox/session.py index c50f1690..51794e40 100644 --- a/tox/session.py +++ b/tox/session.py @@ -34,10 +34,12 @@ def prepare(args): return config -def main(args=None): +def main(args): try: config = prepare(args) retcode = Session(config).runcommand() + if retcode is None: + retcode = 0 raise SystemExit(retcode) except KeyboardInterrupt: raise SystemExit(2) @@ -198,7 +200,7 @@ class Action(object): raise tox.exception.InvocationError( "%s (see %s)" % (invoked, outpath), ret) else: - raise tox.exception.InvocationError("%r" % (invoked, ), ret) + raise tox.exception.InvocationError("%r" % (invoked,), ret) if not out and outpath: out = outpath.read() if hasattr(self, "commandlog"): @@ -462,9 +464,9 @@ class Session: def setupenv(self, venv): if venv.envconfig.missing_subs: venv.status = ( - "unresolvable substitution(s): %s. " - "Environment variables are missing or defined recursively." % - (','.join(["'%s'" % m for m in venv.envconfig.missing_subs]))) + "unresolvable substitution(s): %s. " + "Environment variables are missing or defined recursively." % + (','.join(["'%s'" % m for m in venv.envconfig.missing_subs]))) return if not venv.matching_platform(): venv.status = "platform mismatch" @@ -479,13 +481,13 @@ class Session: if e.args[0] != 2: raise status = ( - "Error creating virtualenv. Note that spaces in paths are " - "not supported by virtualenv. Error details: %r" % e) + "Error creating virtualenv. Note that spaces in paths are " + "not supported by virtualenv. Error details: %r" % e) except tox.exception.InvocationError as e: status = ( - "Error creating virtualenv. Note that some special " - "characters (e.g. ':' and unicode symbols) in paths are " - "not supported by virtualenv. Error details: %r" % e) + "Error creating virtualenv. Note that some special " + "characters (e.g. ':' and unicode symbols) in paths are " + "not supported by virtualenv. Error details: %r" % e) if status: commandlog = envlog.get_commandlog("setup") commandlog.add_command(["setup virtualenv"], str(status), 1) @@ -681,6 +683,7 @@ class Session: else: msg = e self.report.line(msg) + for e in default: report_env(e) if all_envs and extra: diff --git a/tox/venv.py b/tox/venv.py index 851a330d..26bb42ae 100755 --- a/tox/venv.py +++ b/tox/venv.py @@ -158,7 +158,7 @@ class VirtualEnv(object): """ rconfig = CreationConfig.readconfig(self.path_config) if not self.envconfig.recreate and rconfig and \ - rconfig.matches(self._getliveconfig()): + rconfig.matches(self._getliveconfig()): action.info("reusing", self.envconfig.envdir) return if rconfig is None: |