Repository: tox-dev/tox
Pull request #650 (2017-10-15 11:14:17)

Provide friendlier error for missing setup.py.
As suggested in #331, this PR makes `tox` output a friendlier exception when `setup.py` could not be found. When this is the case, the following is outputted instead of throwing a `MissingFile` exception:

![tox output missing setup.py](https://user-images.githubusercontent.com/22679924/31583698-598a2570-b1a0-11e7-952e-4880e7153458.png)

**Checklist**:
- [X] wrote descriptive pull request text
- [X] added/updated test(s)
 - [ ] updated/extended the documentation
Should this be documented and if yes, where?
- [X] added relevant [issue keyword](https://help.github.com/articles/closing-issues-using-keywords/)
      in message body
- [X] added news fragment in [changelog folder](https://github.com/tox-dev/tox/tree/master/changelog), see [this](https://github.com/Volcyy/tox/blob/nicer-missing-setup-output/changelog/331.misc.rst)
- [X] added yourself to `CONTRIBUTORS` (preserving alphabetical order)

codecov[bot]: # [Codecov](https://codecov.io/gh/tox-dev/tox/pull/650?src=pr&el=h1) Report
> Merging [#650](https://codecov.io/gh/tox-dev/tox/pull/650?src=pr&el=desc) into [master](https://codecov.io/gh/tox-dev/tox/commit/5cf2ef9295552ef7fe3f4d282fcd22468675b4fc?src=pr&el=desc) will **decrease** coverage by `0.03%`.
> The diff coverage is `0%`.

[![Impacted file tree graph](https://codecov.io/gh/tox-dev/tox/pull/650/graphs/tree.svg?width=650&token=DYodAwDCZ5&height=150&src=pr)](https://codecov.io/gh/tox-dev/tox/pull/650?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #650      +/-   ##
==========================================
- Coverage   93.86%   93.82%   -0.04%     
==========================================
  Files          11       11              
  Lines        2364     2365       +1     
==========================================
  Hits         2219     2219              
- Misses        145      146       +1
```


| [Impacted Files](https://codecov.io/gh/tox-dev/tox/pull/650?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [tox/session.py](https://codecov.io/gh/tox-dev/tox/pull/650?src=pr&el=tree#diff-dG94L3Nlc3Npb24ucHk=) | `94.17% <0%> (-0.18%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tox-dev/tox/pull/650?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tox-dev/tox/pull/650?src=pr&el=footer). Last update [5cf2ef9...d5a68ad](https://codecov.io/gh/tox-dev/tox/pull/650?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 9162a558..fced59c5 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,6 +1,6 @@
 repos:
 -   repo: https://github.com/pre-commit/pre-commit-hooks
-    sha: v0.9.3
+    sha: v1.1.1
     hooks:
     -   id: trailing-whitespace
     -   id: end-of-file-fixer
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index c1076cc0..8a4b8244 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -18,11 +18,13 @@ Cyril Roelandt
 Eli Collins
 Eugene Yunak
 Fernando L. Pereira
+Henk-Jaap Wagenaar
 Igor Duarte Cardoso
 Ionel Maries Cristian
 Ionel Maries Cristian
 Itxaka Serrano
 Jannis Leidel
+Johannes Christ
 Josh Smeaton
 Julian Krause
 Krisztian Fekete
diff --git a/MANIFEST.in b/MANIFEST.in
index 12c56f7e..e7995530 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,4 +1,4 @@
-include CHANGELOG
+include CHANGELOG.rst
 include README.rst
 include CONTRIBUTORS
 include LICENSE
diff --git a/changelog/331.misc.rst b/changelog/331.misc.rst
new file mode 100644
index 00000000..e77d0e2d
--- /dev/null
+++ b/changelog/331.misc.rst
@@ -0,0 +1,1 @@
+Running ``tox`` without a ``setup.py`` now has a more friendly error message and gives troubleshooting suggestions - by @Volcyy.
diff --git a/changelog/663.misc.rst b/changelog/663.misc.rst
new file mode 100644
index 00000000..6852e188
--- /dev/null
+++ b/changelog/663.misc.rst
@@ -0,0 +1,1 @@
+Fix pycodestyle (formerly pep8) errors E741 (ambiguous variable names, in this case, 'l's) and remove ignore of this error in tox.ini - by @cryvate
diff --git a/doc/config.rst b/doc/config.rst
index 1cb1bf6a..11d27cd2 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -567,6 +567,11 @@ You can still list environments explicitly along with generated ones::
 
     envlist = {py26,py27}-django{15,16}, docs, flake
 
+Keep in mind that whitespace characters (except newline) within ``{}``
+are stripped, so the following line defines the same environment names::
+
+    envlist = {py26, py27}-django{ 15, 16 }, docs, flake
+
 .. note::
 
     To help with understanding how the variants will produce section values,
diff --git a/doc/example/general.rst b/doc/example/general.rst
index 9c4078c3..d360c519 100644
--- a/doc/example/general.rst
+++ b/doc/example/general.rst
@@ -52,7 +52,7 @@ a tox test run.  Here is an example ``tox.ini`` configuration:
     basepython = python
     changedir = doc
     deps = sphinx
-    commands = sphinx-build -W -b html -d {envtmpdir}/doctrees .  {envtmpdir}/html
+    commands = sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html
 
 This will create a dedicated ``docs`` virtual environment and install
 the ``sphinx`` dependency which itself will install the ``sphinx-build`` tool
diff --git a/setup.py b/setup.py
index 35ce35e2..3e6440d3 100644
--- a/setup.py
+++ b/setup.py
@@ -1,21 +1,7 @@
 import io
-import os
 import sys
 
 import setuptools
-from setuptools.command.test import test as TestCommand
-
-
-class Tox(TestCommand):
-    def finalize_options(self):
-        TestCommand.finalize_options(self)
-        self.test_args = ["-v", "-epy"]
-        self.test_suite = True
-
-    def run_tests(self):
-        # import here, cause outside the eggs aren't loaded
-        import tox
-        tox.cmdline(self.test_args)
 
 
 def has_environment_marker_support():
@@ -40,9 +26,8 @@ def has_environment_marker_support():
 
 
 def get_long_description():
-    here = os.path.abspath('.')
-    with io.open(os.path.join(here, 'README.rst'), encoding='utf-8') as f:
-        with io.open(os.path.join(here, 'CHANGELOG.rst'), encoding='utf-8') as g:
+    with io.open('README.rst', encoding='utf-8') as f:
+        with io.open('CHANGELOG.rst', encoding='utf-8') as g:
             return "%s\n\n%s" % (f.read(), g.read())
 
 
@@ -75,24 +60,20 @@ def main():
         packages=['tox'],
         entry_points={'console_scripts': 'tox=tox:cmdline\ntox-quickstart=tox._quickstart:main'},
         setup_requires=['setuptools_scm'],
-        # we use a public tox version to test, see tox.ini's testenv
-        # "deps" definition for the required dependencies
-        tests_require=['tox'],
-        cmdclass={"test": Tox},
         install_requires=install_requires,
         extras_require=extras_require,
         classifiers=[
-                        'Development Status :: 5 - Production/Stable',
-                        'Intended Audience :: Developers',
-                        'License :: OSI Approved :: MIT License',
-                        'Operating System :: POSIX',
-                        'Operating System :: Microsoft :: Windows',
-                        'Operating System :: MacOS :: MacOS X',
-                        'Topic :: Software Development :: Testing',
-                        'Topic :: Software Development :: Libraries',
-                        'Topic :: Utilities'] + [
-                        ('Programming Language :: Python :: %s' % x) for x in
-                        '2 2.6 2.7 3 3.3 3.4 3.5 3.6'.split()]
+            'Development Status :: 5 - Production/Stable',
+            'Intended Audience :: Developers',
+            'License :: OSI Approved :: MIT License',
+            'Operating System :: POSIX',
+            'Operating System :: Microsoft :: Windows',
+            'Operating System :: MacOS :: MacOS X',
+            'Topic :: Software Development :: Testing',
+            'Topic :: Software Development :: Libraries',
+            'Topic :: Utilities'] + [
+                ('Programming Language :: Python :: %s' % x) for x in
+                '2 2.6 2.7 3 3.3 3.4 3.5 3.6'.split()]
     )
 
 
diff --git a/tox.ini b/tox.ini
index 4adc1878..b190e388 100644
--- a/tox.ini
+++ b/tox.ini
@@ -32,9 +32,12 @@ commands = sphinx-build -d {toxworkdir}/docs_doctree doc {toxworkdir}/docs_out -
 
 [testenv:fix-lint]
 basepython = python3.6
+passenv =
+    {[testenv]passenv}
+    HOMEPATH
 deps = flake8 == 3.4.1
        flake8-bugbear == 17.4.0
-       pre-commit == 1.0.1
+       pre-commit == 1.3.0
 description = run static analysis and style check using flake8
 commands = python -m flake8 --show-source tox setup.py {posargs}
            python -m flake8 --show-source doc tests {posargs}
diff --git a/tox/config.py b/tox/config.py
index 7860b871..f4620f6d 100755
--- a/tox/config.py
+++ b/tox/config.py
@@ -895,7 +895,7 @@ def _expand_envstr(envstr):
 
     def expand(env):
         tokens = re.split(r'\{([^}]+)\}', env)
-        parts = [token.split(',') for token in tokens]
+        parts = [re.sub('\s+', '', token).split(',') for token in tokens]
         return [''.join(variant) for variant in itertools.product(*parts)]
 
     return mapcat(expand, envlist)
diff --git a/tox/result.py b/tox/result.py
index 06835500..47f68853 100644
--- a/tox/result.py
+++ b/tox/result.py
@@ -63,8 +63,7 @@ class EnvLog:
             version=version)
 
     def get_commandlog(self, name):
-        l = self.dict.setdefault(name, [])
-        return CommandLog(self, l)
+        return CommandLog(self, self.dict.setdefault(name, []))
 
     def set_installed(self, packages):
         self.dict["installed_packages"] = packages
diff --git a/tox/session.py b/tox/session.py
index 35cb4dc3..b2aeec6a 100644
--- a/tox/session.py
+++ b/tox/session.py
@@ -109,12 +109,11 @@ class Action(object):
         else:
             logdir = self.session.config.logdir
         try:
-            l = logdir.listdir("%s-*" % actionid)
+            log_count = len(logdir.listdir("%s-*" % actionid))
         except (py.error.ENOENT, py.error.ENOTDIR):
             logdir.ensure(dir=1)
-            l = []
-        num = len(l)
-        path = logdir.join("%s-%s.log" % (actionid, num))
+            log_count = 0
+        path = logdir.join("%s-%s.log" % (actionid, log_count))
         f = path.open('w')
         f.flush()
         return f
@@ -404,7 +403,17 @@ class Session:
     def _makesdist(self):
         setup = self.config.setupdir.join("setup.py")
         if not setup.check():
-            raise tox.exception.MissingFile(setup)
+            self.report.error(
+                "No setup.py file found. The expected location is:\n"
+                "  %s\n"
+                "You can\n"
+                "  1. Create one:\n"
+                "     https://packaging.python.org/tutorials/distributing-packages/#setup-py\n"
+                "  2. Configure tox to avoid running sdist:\n"
+                "     http://tox.readthedocs.io/en/latest/example/general.html"
+                "#avoiding-expensive-sdist" % setup
+            )
+            raise SystemExit(1)
         action = self.newaction(None, "packaging")
         with action:
             action.setactivity("sdist-make", setup)
diff --git a/tox/venv.py b/tox/venv.py
index 69cc0c49..af2a80b7 100755
--- a/tox/venv.py
+++ b/tox/venv.py
@@ -195,14 +195,14 @@ class VirtualEnv(object):
                               sitepackages, develop, deps, alwayscopy)
 
     def _getresolvedeps(self):
-        l = []
+        deps = []
         for dep in self.envconfig.deps:
             if dep.indexserver is None:
                 res = self.session._resolve_pkg(dep.name)
                 if res != dep.name:
                     dep = dep.__class__(res)
-            l.append(dep)
-        return l
+            deps.append(dep)
+        return deps
 
     def getsupportedinterpreter(self):
         return self.envconfig.getsupportedinterpreter()
@@ -273,12 +273,12 @@ class VirtualEnv(object):
         self._install([sdistpath], extraopts=extraopts, action=action)
 
     def _installopts(self, indexserver):
-        l = []
+        options = []
         if indexserver:
-            l += ["-i", indexserver]
+            options += ["-i", indexserver]
         if self.envconfig.pip_pre:
-            l.append("--pre")
-        return l
+            options.append("--pre")
+        return options
 
     def run_install_command(self, packages, action, options=()):
         argv = self.envconfig.install_command[:]
@@ -312,7 +312,7 @@ class VirtualEnv(object):
         if not deps:
             return
         d = {}
-        l = []
+        ixservers = []
         for dep in deps:
             if isinstance(dep, (str, py.path.local)):
                 dep = DepConfig(str(dep), None)
@@ -322,11 +322,11 @@ class VirtualEnv(object):
             else:
                 ixserver = dep.indexserver
             d.setdefault(ixserver, []).append(dep.name)
-            if ixserver not in l:
-                l.append(ixserver)
+            if ixserver not in ixservers:
+                ixservers.append(ixserver)
             assert ixserver.url is None or isinstance(ixserver.url, str)
 
-        for ixserver in l:
+        for ixserver in ixservers:
             packages = d[ixserver]
             options = self._installopts(ixserver.url)
             if extraopts:


Test patch:
diff --git a/tests/test_config.py b/tests/test_config.py
index d83114f7..430daf30 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -160,7 +160,7 @@ class TestConfigPlatform:
         monkeypatch.setattr(sys, "platform", "win32")
         config = newconfig([], """
             [tox]
-            envlist = py27-{win,lin,osx}
+            envlist = py27-{win, lin,osx }
             [testenv]
             platform =
                 win: win32
diff --git a/tests/test_venv.py b/tests/test_venv.py
index 527da88d..1eae6e4d 100644
--- a/tests/test_venv.py
+++ b/tests/test_venv.py
@@ -16,8 +16,7 @@ from tox.venv import VirtualEnv
 
 # def test_global_virtualenv(capfd):
 #    v = VirtualEnv()
-#    l = v.list()
-#    assert l
+#    assert v.list()
 #    out, err = capfd.readouterr()
 #    assert not out
 #    assert not err
@@ -62,9 +61,9 @@ def test_create(monkeypatch, mocksession, newconfig):
     assert not venv.path.check()
     action = mocksession.newaction(venv, "getenv")
     tox_testenv_create(action=action, venv=venv)
-    l = mocksession._pcalls
-    assert len(l) >= 1
-    args = l[0].args
+    pcalls = mocksession._pcalls
+    assert len(pcalls) >= 1
+    args = pcalls[0].args
     assert "virtualenv" == str(args[2])
     if sys.platform != "win32":
         # realpath is needed for stuff like the debian symlinks
@@ -103,9 +102,9 @@ def test_create_sitepackages(monkeypatch, mocksession, newconfig):
     venv = VirtualEnv(envconfig, session=mocksession)
     action = mocksession.newaction(venv, "getenv")
     tox_testenv_create(action=action, venv=venv)
-    l = mocksession._pcalls
-    assert len(l) >= 1
-    args = l[0].args
+    pcalls = mocksession._pcalls
+    assert len(pcalls) >= 1
+    args = pcalls[0].args
     assert "--system-site-packages" in map(str, args)
     mocksession._clearmocks()
 
@@ -113,9 +112,9 @@ def test_create_sitepackages(monkeypatch, mocksession, newconfig):
     venv = VirtualEnv(envconfig, session=mocksession)
     action = mocksession.newaction(venv, "getenv")
     tox_testenv_create(action=action, venv=venv)
-    l = mocksession._pcalls
-    assert len(l) >= 1
-    args = l[0].args
+    pcalls = mocksession._pcalls
+    assert len(pcalls) >= 1
+    args = pcalls[0].args
     assert "--system-site-packages" not in map(str, args)
     assert "--no-site-packages" not in map(str, args)
 
@@ -131,16 +130,16 @@ def test_install_deps_wildcard(newmocksession):
     venv = mocksession.getenv("py123")
     action = mocksession.newaction(venv, "getenv")
     tox_testenv_create(action=action, venv=venv)
-    l = mocksession._pcalls
-    assert len(l) == 1
+    pcalls = mocksession._pcalls
+    assert len(pcalls) == 1
     distshare = venv.session.config.distshare
     distshare.ensure("dep1-1.0.zip")
     distshare.ensure("dep1-1.1.zip")
 
     tox_testenv_install_deps(action=action, venv=venv)
-    assert len(l) == 2
-    args = l[-1].args
-    assert l[-1].cwd == venv.envconfig.config.toxinidir
+    assert len(pcalls) == 2
+    args = pcalls[-1].args
+    assert pcalls[-1].cwd == venv.envconfig.config.toxinidir
     assert "pip" in str(args[0])
     assert args[1] == "install"
     args = [arg for arg in args if str(arg).endswith("dep1-1.1.zip")]
@@ -162,21 +161,21 @@ def test_install_deps_indexserver(newmocksession):
     venv = mocksession.getenv('py123')
     action = mocksession.newaction(venv, "getenv")
     tox_testenv_create(action=action, venv=venv)
-    l = mocksession._pcalls
-    assert len(l) == 1
-    l[:] = []
+    pcalls = mocksession._pcalls
+    assert len(pcalls) == 1
+    pcalls[:] = []
 
     tox_testenv_install_deps(action=action, venv=venv)
     # two different index servers, two calls
-    assert len(l) == 3
-    args = " ".join(l[0].args)
+    assert len(pcalls) == 3
+    args = " ".join(pcalls[0].args)
     assert "-i " not in args
     assert "dep1" in args
 
-    args = " ".join(l[1].args)
+    args = " ".join(pcalls[1].args)
     assert "-i ABC" in args
     assert "dep2" in args
-    args = " ".join(l[2].args)
+    args = " ".join(pcalls[2].args)
     assert "-i ABC" in args
     assert "dep3" in args
 
@@ -191,13 +190,13 @@ def test_install_deps_pre(newmocksession):
     venv = mocksession.getenv('python')
     action = mocksession.newaction(venv, "getenv")
     tox_testenv_create(action=action, venv=venv)
-    l = mocksession._pcalls
-    assert len(l) == 1
-    l[:] = []
+    pcalls = mocksession._pcalls
+    assert len(pcalls) == 1
+    pcalls[:] = []
 
     tox_testenv_install_deps(action=action, venv=venv)
-    assert len(l) == 1
-    args = " ".join(l[0].args)
+    assert len(pcalls) == 1
+    args = " ".join(pcalls[0].args)
     assert "--pre " in args
     assert "dep1" in args
 
@@ -209,12 +208,12 @@ def test_installpkg_indexserver(newmocksession, tmpdir):
             default = ABC
     """)
     venv = mocksession.getenv('python')
-    l = mocksession._pcalls
+    pcalls = mocksession._pcalls
     p = tmpdir.ensure("distfile.tar.gz")
     mocksession.installpkg(venv, p)
     # two different index servers, two calls
-    assert len(l) == 1
-    args = " ".join(l[0].args)
+    assert len(pcalls) == 1
+    args = " ".join(pcalls[0].args)
     assert "-i ABC" in args
 
 
@@ -243,12 +242,12 @@ def test_install_sdist_extras(newmocksession):
     venv = mocksession.getenv('python')
     action = mocksession.newaction(venv, "getenv")
     tox_testenv_create(action=action, venv=venv)
-    l = mocksession._pcalls
-    assert len(l) == 1
-    l[:] = []
+    pcalls = mocksession._pcalls
+    assert len(pcalls) == 1
+    pcalls[:] = []
 
     venv.installpkg('distfile.tar.gz', action=action)
-    assert 'distfile.tar.gz[testing,development]' in l[-1].args
+    assert 'distfile.tar.gz[testing,development]' in pcalls[-1].args
 
 
 def test_develop_extras(newmocksession, tmpdir):
@@ -260,13 +259,13 @@ def test_develop_extras(newmocksession, tmpdir):
     venv = mocksession.getenv('python')
     action = mocksession.newaction(venv, "getenv")
     tox_testenv_create(action=action, venv=venv)
-    l = mocksession._pcalls
-    assert len(l) == 1
-    l[:] = []
+    pcalls = mocksession._pcalls
+    assert len(pcalls) == 1
+    pcalls[:] = []
 
     venv.developpkg(tmpdir, action=action)
     expected = "%s[testing,development]" % tmpdir.strpath
-    assert expected in l[-1].args
+    assert expected in pcalls[-1].args
 
 
 def test_env_variables_added_to_needs_reinstall(tmpdir, mocksession, newconfig, monkeypatch):
@@ -285,9 +284,9 @@ def test_env_variables_added_to_needs_reinstall(tmpdir, mocksession, newconfig,
 
     venv._needs_reinstall(tmpdir, action)
 
-    l = mocksession._pcalls
-    assert len(l) == 2
-    env = l[0].env
+    pcalls = mocksession._pcalls
+    assert len(pcalls) == 2
+    env = pcalls[0].env
 
     # should have access to setenv vars
     assert 'CUSTOM_VAR' in env
@@ -397,15 +396,15 @@ def test_install_python3(tmpdir, newmocksession):
     venv = mocksession.getenv('py123')
     action = mocksession.newaction(venv, "getenv")
     tox_testenv_create(action=action, venv=venv)
-    l = mocksession._pcalls
-    assert len(l) == 1
-    args = l[0].args
+    pcalls = mocksession._pcalls
+    assert len(pcalls) == 1
+    args = pcalls[0].args
     assert str(args[2]) == 'virtualenv'
-    l[:] = []
+    pcalls[:] = []
     action = mocksession.newaction(venv, "hello")
     venv._install(["hello"], action=action)
-    assert len(l) == 1
-    args = l[0].args
+    assert len(pcalls) == 1
+    args = pcalls[0].args
     assert "pip" in args[0]
     for _ in args:
         assert "--download-cache" not in args, args
@@ -543,19 +542,19 @@ class TestVenvTest:
         venv = mocksession.getenv("python")
         action = mocksession.newaction(venv, "getenv")
         monkeypatch.setenv("PATH", "xyz")
-        l = []
+        sysfind_calls = []
         monkeypatch.setattr("py.path.local.sysfind", classmethod(
-                            lambda *args, **kwargs: l.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)
-        assert l.pop()["paths"] == [venv.envconfig.envbindir]
+        assert sysfind_calls.pop()["paths"] == [venv.envconfig.envbindir]
         with pytest.raises(ZeroDivisionError):
             venv.test(action)
-        assert l.pop()["paths"] == [venv.envconfig.envbindir]
+        assert sysfind_calls.pop()["paths"] == [venv.envconfig.envbindir]
         with pytest.raises(ZeroDivisionError):
             venv.run_install_command(['qwe'], action=action)
-        assert l.pop()["paths"] == [venv.envconfig.envbindir]
+        assert sysfind_calls.pop()["paths"] == [venv.envconfig.envbindir]
         monkeypatch.setenv("PIP_RESPECT_VIRTUALENV", "1")
         monkeypatch.setenv("PIP_REQUIRE_VIRTUALENV", "1")
         monkeypatch.setenv("__PYVENV_LAUNCHER__", "1")
@@ -576,9 +575,9 @@ class TestVenvTest:
         assert 'PYTHONPATH' not in os.environ
         mocksession.report.expect("warning", "*Discarding $PYTHONPATH from environment*")
 
-        l = mocksession._pcalls
-        assert len(l) == 1
-        assert 'PYTHONPATH' not in l[0].env
+        pcalls = mocksession._pcalls
+        assert len(pcalls) == 1
+        assert 'PYTHONPATH' not in pcalls[0].env
 
         # passenv = PYTHONPATH allows PYTHONPATH to stay in environment
         monkeypatch.setenv("PYTHONPATH", "/my/awesome/library")
@@ -593,9 +592,9 @@ class TestVenvTest:
         assert 'PYTHONPATH' in os.environ
         mocksession.report.not_expect("warning", "*Discarding $PYTHONPATH from environment*")
 
-        l = mocksession._pcalls
-        assert len(l) == 2
-        assert l[1].env['PYTHONPATH'] == '/my/awesome/library'
+        pcalls = mocksession._pcalls
+        assert len(pcalls) == 2
+        assert pcalls[1].env['PYTHONPATH'] == '/my/awesome/library'
 
 
 # FIXME this test fails when run in isolation - find what this depends on
@@ -618,9 +617,9 @@ def test_env_variables_added_to_pcall(tmpdir, mocksession, newconfig, monkeypatc
     mocksession.installpkg(venv, pkg)
     venv.test()
 
-    l = mocksession._pcalls
-    assert len(l) == 2
-    for x in l:
+    pcalls = mocksession._pcalls
+    assert len(pcalls) == 2
+    for x in pcalls:
         env = x.env
         assert env is not None
         assert 'ENV_VAR' in env
@@ -630,11 +629,11 @@ def test_env_variables_added_to_pcall(tmpdir, mocksession, newconfig, monkeypatc
         assert 'PYTHONPATH' in env
         assert env['PYTHONPATH'] == 'value'
     # all env variables are passed for installation
-    assert l[0].env["YY"] == "456"
-    assert "YY" not in l[1].env
+    assert pcalls[0].env["YY"] == "456"
+    assert "YY" not in pcalls[1].env
 
     assert set(["ENV_VAR", "VIRTUAL_ENV", "PYTHONHASHSEED", "X123", "PATH"])\
-        .issubset(l[1].env)
+        .issubset(pcalls[1].env)
 
     # setenv does not trigger PYTHONPATH warnings
     mocksession.report.not_expect("warning", "*Discarding $PYTHONPATH from environment*")
@@ -650,9 +649,9 @@ def test_installpkg_no_upgrade(tmpdir, newmocksession):
     venv.just_created = True
     venv.envconfig.envdir.ensure(dir=1)
     mocksession.installpkg(venv, pkg)
-    l = mocksession._pcalls
-    assert len(l) == 1
-    assert '-U' not in l[0].args
+    pcalls = mocksession._pcalls
+    assert len(pcalls) == 1
+    assert '-U' not in pcalls[0].args
 
 
 def test_installpkg_upgrade(newmocksession, tmpdir):
@@ -661,12 +660,12 @@ def test_installpkg_upgrade(newmocksession, tmpdir):
     venv = mocksession.getenv('python')
     assert not hasattr(venv, 'just_created')
     mocksession.installpkg(venv, pkg)
-    l = mocksession._pcalls
-    assert len(l) == 1
-    index = l[0].args.index(str(pkg))
+    pcalls = mocksession._pcalls
+    assert len(pcalls) == 1
+    index = pcalls[0].args.index(str(pkg))
     assert index >= 0
-    assert '-U' in l[0].args[:index]
-    assert '--no-deps' in l[0].args[:index]
+    assert '-U' in pcalls[0].args[:index]
+    assert '--no-deps' in pcalls[0].args[:index]
 
 
 def test_run_install_command(newmocksession):
@@ -676,11 +675,11 @@ def test_run_install_command(newmocksession):
     venv.envconfig.envdir.ensure(dir=1)
     action = mocksession.newaction(venv, "hello")
     venv.run_install_command(packages=["whatever"], action=action)
-    l = mocksession._pcalls
-    assert len(l) == 1
-    assert 'pip' in l[0].args[0]
-    assert 'install' in l[0].args
-    env = l[0].env
+    pcalls = mocksession._pcalls
+    assert len(pcalls) == 1
+    assert 'pip' in pcalls[0].args[0]
+    assert 'install' in pcalls[0].args
+    env = pcalls[0].env
     assert env is not None
 
 
@@ -694,10 +693,10 @@ def test_run_custom_install_command(newmocksession):
     venv.envconfig.envdir.ensure(dir=1)
     action = mocksession.newaction(venv, "hello")
     venv.run_install_command(packages=["whatever"], action=action)
-    l = mocksession._pcalls
-    assert len(l) == 1
-    assert 'easy_install' in l[0].args[0]
-    assert l[0].args[1:] == ['whatever']
+    pcalls = mocksession._pcalls
+    assert len(pcalls) == 1
+    assert 'easy_install' in pcalls[0].args[0]
+    assert pcalls[0].args[1:] == ['whatever']
 
 
 def test_command_relative_issue26(newmocksession, tmpdir, monkeypatch):
@@ -733,16 +732,16 @@ def test_ignore_outcome_failing_cmd(newmocksession):
 
 
 def test_tox_testenv_create(newmocksession):
-    l = []
+    log = []
 
     class Plugin:
         @hookimpl
         def tox_testenv_create(self, action, venv):
-            l.append(1)
+            log.append(1)
 
         @hookimpl
         def tox_testenv_install_deps(self, action, venv):
-            l.append(2)
+            log.append(2)
 
     mocksession = newmocksession([], """
         [testenv]
@@ -752,20 +751,20 @@ def test_tox_testenv_create(newmocksession):
 
     venv = mocksession.getenv('python')
     venv.update(action=mocksession.newaction(venv, "getenv"))
-    assert l == [1, 2]
+    assert log == [1, 2]
 
 
 def test_tox_testenv_pre_post(newmocksession):
-    l = []
+    log = []
 
     class Plugin:
         @hookimpl
         def tox_runtest_pre(self, venv):
-            l.append('started')
+            log.append('started')
 
         @hookimpl
         def tox_runtest_post(self, venv):
-            l.append('finished')
+            log.append('finished')
 
     mocksession = newmocksession([], """
         [testenv]
@@ -774,6 +773,6 @@ def test_tox_testenv_pre_post(newmocksession):
 
     venv = mocksession.getenv('python')
     venv.status = None
-    assert l == []
+    assert log == []
     mocksession.runtestenv(venv)
-    assert l == ['started', 'finished']
+    assert log == ['started', 'finished']
diff --git a/tests/test_z_cmdline.py b/tests/test_z_cmdline.py
index a856c2f4..91122456 100644
--- a/tests/test_z_cmdline.py
+++ b/tests/test_z_cmdline.py
@@ -1,3 +1,4 @@
+import os
 import platform
 
 import py
@@ -454,6 +455,21 @@ def test_sdist_fails(cmd, initproj):
     ])
 
 
+def test_no_setup_py_exits(cmd, initproj):
+    initproj("pkg123-0.7", filedefs={
+        'tox.ini': """
+            [testenv]
+            commands=python -c "2 + 2"
+        """
+    })
+    os.remove("setup.py")
+    result = cmd.run("tox", )
+    assert result.ret
+    result.stdout.fnmatch_lines([
+        "*ERROR*No setup.py file found*"
+    ])
+
+
 def test_package_install_fails(cmd, initproj):
     initproj("pkg123-0.7", filedefs={
         'tests': {'test_hello.py': "def test_hello(): pass"},
@@ -747,9 +763,9 @@ def test_separate_sdist_no_sdistfile(cmd, initproj):
     })
     result = cmd.run("tox", "--sdistonly")
     assert not result.ret
-    l = distshare.listdir()
-    assert len(l) == 1
-    sdistfile = l[0]
+    distshare_files = distshare.listdir()
+    assert len(distshare_files) == 1
+    sdistfile = distshare_files[0]
     assert 'pkg123-foo-0.7.zip' in str(sdistfile)
 
 
@@ -764,9 +780,9 @@ def test_separate_sdist(cmd, initproj):
     })
     result = cmd.run("tox", "--sdistonly")
     assert not result.ret
-    l = distshare.listdir()
-    assert len(l) == 1
-    sdistfile = l[0]
+    sdistfiles = distshare.listdir()
+    assert len(sdistfiles) == 1
+    sdistfile = sdistfiles[0]
     result = cmd.run("tox", "-v", "--notest")
     assert not result.ret
     result.stdout.fnmatch_lines([