summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorholger krekel <holger@merlinux.eu>2014-05-15 11:33:28 +0200
committerholger krekel <holger@merlinux.eu>2014-05-15 11:33:28 +0200
commit360ee98eae10511bb32c82997052ff1ab278033f (patch)
treed8b30b7e0fed866b427ec3992bc427f797d4ca19
parent23380da3f729a37106cc6ca2023429bdb547c780 (diff)
parent20de40c837b9616bb08c155fe108b61cc50cfae6 (diff)
downloadtox-360ee98eae10511bb32c82997052ff1ab278033f.tar.gz
Merged in cboylan/tox (pull request #85)
Fix command expansion and parsing.
-rw-r--r--.hgignore1
-rw-r--r--[-rwxr-xr-x]CHANGELOG25
-rw-r--r--CONTRIBUTORS4
-rw-r--r--README.rst2
-rw-r--r--doc/conf.py2
-rw-r--r--doc/config.txt56
-rw-r--r--doc/example/devenv.txt81
-rw-r--r--doc/example/jenkins.txt7
-rwxr-xr-xdoc/example/pytest.txt2
-rw-r--r--doc/index.txt5
-rw-r--r--doc/install.txt2
-rw-r--r--setup.py2
-rw-r--r--tests/conftest.py2
-rw-r--r--tests/test_config.py27
-rw-r--r--tests/test_interpreters.py2
-rw-r--r--tests/test_quickstart.py180
-rw-r--r--tests/test_venv.py17
-rw-r--r--tests/test_z_cmdline.py35
-rw-r--r--tox.ini6
-rw-r--r--tox/__init__.py4
-rw-r--r--tox/_cmdline.py25
-rw-r--r--tox/_config.py34
-rw-r--r--tox/_quickstart.py2
-rw-r--r--tox/_venv.py10
-rw-r--r--tox/_verlib.py1
-rw-r--r--tox/interpreters.py4
26 files changed, 394 insertions, 144 deletions
diff --git a/.hgignore b/.hgignore
index cf24d86..8cacf58 100644
--- a/.hgignore
+++ b/.hgignore
@@ -14,3 +14,4 @@ dist
doc/_build/
tox.egg-info
.tox
+.cache
diff --git a/CHANGELOG b/CHANGELOG
index 2fe431f..1617043 100755..100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,28 @@
+development
+-----------
+
+- fix issue59: add option "--skip-missing-interpreters" which won't fail the
+ build if Python interpreters listed in tox.ini are missing. Thanks
+ Alexandre Conrad for PR104.
+
+- fix issue164: better traceback info in case of failing test commands.
+ Thanks Marc Abramowitz for PR92.
+
+- support optional env variable substitution, thanks Morgan Fainberg
+ for PR86.
+
+- limit python hashseed to 1024 on Windows to prevent possible
+ memory errors. Thanks March Schlaich for the PR90.
+
+1.7.1
+---------
+
+- fix issue162: don't list python 2.5 as compatibiliy/supported
+
+- fix issue158 and fix issue155: windows/virtualenv properly works now:
+ call virtualenv through "python -m virtualenv" with the same
+ interpreter which invoked tox. Thanks Chris Withers, Ionel Maries Cristian.
+
1.7.0
---------
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index a9ac89a..e2f428c 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -22,3 +22,7 @@ Anthon van der Neuth
Matt Good
Mattieu Agopian
Asmund Grammeltwedt
+Ionel Maries Cristian
+Alexandre Conrad
+Morgan Fainberg
+Marc Schlaich
diff --git a/README.rst b/README.rst
index 2beeadd..c107df0 100644
--- a/README.rst
+++ b/README.rst
@@ -21,5 +21,5 @@ For more information and the repository please checkout:
have fun,
-holger krekel, January 2014
+holger krekel, 2014
diff --git a/doc/conf.py b/doc/conf.py
index fda4ac9..76dc4c3 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -48,7 +48,7 @@ copyright = u'2013, holger krekel and others'
# built documents.
#
# The short X.Y version.
-release = version = "1.7.0"
+release = version = "1.7.1"
# The full version, including alpha/beta/rc tags.
# The language for content autogenerated by Sphinx. Refer to documentation
diff --git a/doc/config.txt b/doc/config.txt
index 8eb8f3a..a6ca4c8 100644
--- a/doc/config.txt
+++ b/doc/config.txt
@@ -28,9 +28,9 @@ List of optional global options::
(by checking for existence of the ``JENKINS_URL`` environment variable)
and will first lookup global tox settings in this section::
- [tox:hudson]
- ... # override [tox] settings for the hudson context
- # note: for hudson distshare defaults to ``{toxworkdir}/distshare``.
+ [tox:jenkins]
+ ... # override [tox] settings for the jenkins context
+ # note: for jenkins distshare defaults to ``{toxworkdir}/distshare``.
envlist setting
@@ -80,7 +80,7 @@ Complete list of settings that you can put into ``testenv*`` sections:
.. versionadded:: 1.6
- **WARNING**: This setting is **EXPERIMENTAL** so use with care
+ **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
@@ -94,22 +94,14 @@ Complete list of settings that you can put into ``testenv*`` sections:
if you have configured it.
**default**::
-
- pip install --pre {opts} {packages}
-
- **default on environments using python2.5**::
-
- pip install {opts} {packages}``
- this will use pip<1.4 which has no ``--pre`` option. Note also
- that for python2.5 support you may need to install ssl and/or
- use ``setenv = PIP_INSECURE=1`` in a py25 based testenv.
+ pip install --pre {opts} {packages}
.. confval:: whitelist_externals=MULTI-LINE-LIST
each line specifies a command name (in glob-style pattern format)
which can be used in the ``commands`` section without triggering
- a "not installed in virtualenv" warning. Example: if you use the
+ a "not installed in virtualenv" warning. Example: if you use the
unix ``make`` for running tests you can list ``whitelist_externals=make``
or ``whitelist_externals=/usr/bin/make`` if you want more precision.
If you don't want tox to issue a warning in any case, just use
@@ -136,7 +128,7 @@ Complete list of settings that you can put into ``testenv*`` sections:
using the ``{toxinidir}`` as the current working directory.
.. confval:: setenv=MULTI-LINE-LIST
-
+
.. versionadded:: 0.9
each line contains a NAME=VALUE environment variable setting which
@@ -164,17 +156,17 @@ Complete list of settings that you can put into ``testenv*`` sections:
**DEPRECATED** -- as of August 2013 you should use setuptools
which has merged most of distribute_ 's changes. Just use
- the default, Luke! In future versions of tox this option might
- be ignored and setuptools always chosen.
+ the default, Luke! In future versions of tox this option might
+ be ignored and setuptools always chosen.
**default:** False.
.. confval:: sitepackages=True|False
Set to ``True`` if you want to create virtual environments that also
- have access to globally installed packages.
+ have access to globally installed packages.
- **default:** False, meaning that virtualenvs will be
+ **default:** False, meaning that virtualenvs will be
created without inheriting the global site packages.
.. confval:: args_are_paths=BOOL
@@ -182,7 +174,7 @@ Complete list of settings that you can put into ``testenv*`` sections:
treat positional arguments passed to ``tox`` as file system paths
and - if they exist on the filesystem - rewrite them according
to the ``changedir``.
- **default**: True (due to the exists-on-filesystem check it's
+ **default**: True (due to the exists-on-filesystem check it's
usually safe to try rewriting).
.. confval:: envtmpdir=path
@@ -202,7 +194,7 @@ Complete list of settings that you can put into ``testenv*`` sections:
.. versionadded:: 0.9
Multi-line ``name = URL`` definitions of python package servers.
- Depedencies can specify using a specified index server through the
+ 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::
@@ -292,6 +284,26 @@ then the value will be retrieved as ``os.environ['KEY']``
and raise an Error if the environment variable
does not exist.
+
+environment variable substitutions with default values
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+If you specify a substitution string like this::
+
+ {env:KEY:DEFAULTVALUE}
+
+then the value will be retrieved as ``os.environ['KEY']``
+and replace with DEFAULTVALUE if the environment variable does not
+exist.
+
+If you specify a substitution string like this::
+
+ {env:KEY:}
+
+then the value will be retrieved as ``os.environ['KEY']``
+and replace with and empty string if the environment variable does not
+exist.
+
.. _`command positional substitution`:
.. _`positional substitution`:
@@ -347,7 +359,7 @@ You can put default values in one section and reference them in others to avoid
pytest
mock
pytest-xdist
-
+
[testenv:dulwich]
deps =
dulwich
diff --git a/doc/example/devenv.txt b/doc/example/devenv.txt
index 571f89c..f4afc37 100644
--- a/doc/example/devenv.txt
+++ b/doc/example/devenv.txt
@@ -1,61 +1,78 @@
-
+=======================
Development environment
=======================
-Tox can be used to prepare development virtual environment for local projects.
-This feature can be useful in order to preserve environment across team members
-working on same project. It can be also used by deployment tools to prepare
-proper environments.
+Tox can be used for just preparing different virtual environments required by a
+project.
+
+This feature can be used by deployment tools when preparing deployed project
+environments. It can also be used for setting up normalized project development
+environments and thus help reduce the risk of different team members using
+mismatched development environments.
+
+Here are some examples illustrating how to set up a project's development
+environment using tox. For illustration purposes, let us call the development
+environment ``devenv``.
-Configuration
--------------
+Example 1: Basic scenario
+=========================
-Firstly, you need to prepare configuration for your development environment. In
-order to do that, we must define proper section at ``tox.ini`` file and tell at
-what directory environment should be created. Moreover, we need to specify
-python version that should be picked, and that the package should be installed
-with ``setup.py develop``::
+Step 1 - Configure the development environment
+----------------------------------------------
+
+First, we prepare the tox configuration for our development environment by
+defining a ``[testenv:devenv]`` section in the project's ``tox.ini``
+configuration file::
[testenv:devenv]
envdir = devenv
basepython = python2.7
usedevelop = True
- commands =
- deps =
+In it we state:
-Actually, you can configure a lot more, those are the only required settings.
-In example you can add ``deps`` and ``commands`` settings. Here, we tell tox
-not to pick ``commands`` or ``deps`` from base ``testenv`` configuration.
+- what directory to locate the environment in,
+- what Python executable to use in the environment,
+- that our project should be installed into the environment using ``setup.py
+ develop``, as opposed to building and installing its source distribution using
+ ``setup.py install``.
+Actually, we can configure a lot more, and these are only the required settings.
+For example, we can add the following to our configuration, telling tox not to
+reuse ``commands`` or ``deps`` settings from the base ``[testenv]``
+configuration::
+
+ commands =
+ deps =
-Creating development environment
---------------------------------
-Once ``devenv`` section is defined we can instrument tox to create our
-environment::
+Step 2 - Create the development environment
+-------------------------------------------
+
+Once the ``[testenv:devenv]`` configuration section has been defined, we create
+the actual development environment by running the following::
tox -e devenv
-This will create an environment at path specified by ``envdir`` under
-``[testenv:devenv]`` section.
+This creates the environment at the path specified by the environment's
+``envdir`` configuration value.
+
+Example 2: A more complex scenario
+==================================
-Full configuration example
---------------------------
+Let us say we want our project development environment to:
-Let's say we want our development environment sit at ``devenv`` and pull
-packages from ``requirements.txt`` file which we create at the same directory
-as ``tox.ini`` file. We also want to speciy Python version to be 2.7, and use
-``setup.py develop`` to work in development mode instead of building and
-installing an ``sdist`` package.
+- be located in the ``devenv`` directory,
+- use Python executable ``python2.7``,
+- pull packages from ``requirements.txt``, located in the same directory as
+ ``tox.ini``.
-Here is example configuration for that::
+Here is an example configuration for the described scenario::
[testenv:devenv]
envdir = devenv
basepython = python2.7
usedevelop = True
deps = -rrequirements.txt
-
diff --git a/doc/example/jenkins.txt b/doc/example/jenkins.txt
index bfdceee..2d8dc25 100644
--- a/doc/example/jenkins.txt
+++ b/doc/example/jenkins.txt
@@ -32,7 +32,9 @@ using these steps:
The last point requires that your test command creates JunitXML files,
for example with ``py.test`` it is done like this:
- commands=py.test --junitxml=junit-{envname}.xml
+.. code-block:: ini
+
+ commands = py.test --junitxml=junit-{envname}.xml
@@ -57,7 +59,7 @@ with this::
exec urllib.urlopen(url).read() in d
d['cmdline'](['--recreate'])
-The downloaded `toxbootstrap.py`_ file downloads all neccessary files to
+The downloaded `toxbootstrap.py` file downloads all neccessary files to
install ``tox`` in a virtual sub environment. Notes:
* uncomment the line containing ``USETOXDEV`` to use the latest
@@ -68,7 +70,6 @@ install ``tox`` in a virtual sub environment. Notes:
will cause tox to reinstall all virtual environments all the time
which is often what one wants in CI server contexts)
-.. _`toxbootstrap.py`: https://bitbucket.org/hpk42/tox/raw/default/toxbootstrap.py
Integrating "sphinx" documentation checks in a Jenkins job
----------------------------------------------------------------
diff --git a/doc/example/pytest.txt b/doc/example/pytest.txt
index cba8741..6f98cf3 100755
--- a/doc/example/pytest.txt
+++ b/doc/example/pytest.txt
@@ -105,7 +105,7 @@ directories; pytest will still find and import them by adding their
parent directory to ``sys.path`` but they won't be copied to
other places or be found by Python's import system outside of pytest.
-.. _`fully qualified name`: http://pytest.org/latest/goodpractises.html#package-name
+.. _`fully qualified name`: http://pytest.org/latest/goodpractises.html#test-package-name
.. include:: ../links.txt
diff --git a/doc/index.txt b/doc/index.txt
index 7fcc310..17d063d 100644
--- a/doc/index.txt
+++ b/doc/index.txt
@@ -68,9 +68,8 @@ Current features
support for configuring the installer command
through :confval:`install_command=ARGV`.
-* **cross-Python compatible**: Python-2.5 up to Python-3.3,
- Jython and pypy_ support. Python-2.5 is supported through
- a vendored ``virtualenv-1.9.1`` script.
+* **cross-Python compatible**: CPython-2.6, 2.7, 3.2 and higher,
+ Jython and pypy_.
* **cross-platform**: Windows and Unix style environments
diff --git a/doc/install.txt b/doc/install.txt
index 5914ac1..2851571 100644
--- a/doc/install.txt
+++ b/doc/install.txt
@@ -4,7 +4,7 @@ tox installation
Install info in a nutshell
----------------------------------
-**Pythons**: CPython 2.4-3.3, Jython-2.5.1, pypy-1.9ff
+**Pythons**: CPython 2.6-3.3, Jython-2.5.1, pypy-1.9ff
**Operating systems**: Linux, Windows, OSX, Unix
diff --git a/setup.py b/setup.py
index 5a61aa1..582d315 100644
--- a/setup.py
+++ b/setup.py
@@ -28,7 +28,7 @@ def main():
description='virtualenv-based automation of test activities',
long_description=open("README.rst").read(),
url='http://tox.testrun.org/',
- version='1.7.0',
+ version='1.7.1',
license='http://opensource.org/licenses/MIT',
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
author='holger krekel',
diff --git a/tests/conftest.py b/tests/conftest.py
index 1d4bdde..3e2493b 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,2 +1,2 @@
-from tox._pytestplugin import *
+from tox._pytestplugin import * # noqa
diff --git a/tests/test_config.py b/tests/test_config.py
index 93d7659..c55f43a 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -1,12 +1,11 @@
-import tox
-import pytest
-import os, sys
-import subprocess
+import sys
from textwrap import dedent
import py
+import pytest
+import tox
import tox._config
-from tox._config import *
+from tox._config import * # noqa
from tox._config import _split_env
@@ -110,7 +109,6 @@ class TestConfigPackage:
def test_defaults_distshare(self, tmpdir, newconfig):
config = newconfig([], "")
- envconfig = config.envconfigs['python']
assert config.distshare == config.homedir.join(".tox", "distshare")
def test_defaults_changed_dir(self, tmpdir, newconfig):
@@ -168,6 +166,7 @@ class TestIniParser:
key2={xyz}
""")
reader = IniReader(config._cfg, fallbacksections=['mydefault'])
+ assert reader is not None
py.test.raises(tox.exception.ConfigError,
'reader.getdefault("mydefault", "key2")')
@@ -242,6 +241,22 @@ class TestIniParser:
py.test.raises(tox.exception.ConfigError,
'reader.getdefault("section", "key2")')
+ def test_getdefault_environment_substitution_with_default(self, monkeypatch, newconfig):
+ monkeypatch.setenv("KEY1", "hello")
+ config = newconfig("""
+ [section]
+ key1={env:KEY1:DEFAULT_VALUE}
+ key2={env:KEY2:DEFAULT_VALUE}
+ key3={env:KEY3:}
+ """)
+ reader = IniReader(config._cfg)
+ x = reader.getdefault("section", "key1")
+ assert x == "hello"
+ x = reader.getdefault("section", "key2")
+ assert x == "DEFAULT_VALUE"
+ x = reader.getdefault("section", "key3")
+ assert x == ""
+
def test_getdefault_other_section_substitution(self, newconfig):
config = newconfig("""
[section]
diff --git a/tests/test_interpreters.py b/tests/test_interpreters.py
index 39eabc6..f06a69a 100644
--- a/tests/test_interpreters.py
+++ b/tests/test_interpreters.py
@@ -2,7 +2,7 @@ import sys
import os
import pytest
-from tox.interpreters import *
+from tox.interpreters import * # noqa
@pytest.fixture
def interpreters():
diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py
index c376d4b..df8a98f 100644
--- a/tests/test_quickstart.py
+++ b/tests/test_quickstart.py
@@ -6,6 +6,7 @@ import tox._quickstart
def cleandir(tmpdir):
tmpdir.chdir()
+
class TestToxQuickstartMain(object):
def mock_term_input_return_values(self, return_values):
@@ -23,12 +24,26 @@ class TestToxQuickstartMain(object):
return mock_term_input
- def test_quickstart_main_choose_individual_pythons_and_pytest(self,
- monkeypatch):
+ def test_quickstart_main_choose_individual_pythons_and_pytest(
+ self,
+ monkeypatch):
monkeypatch.setattr(
tox._quickstart, 'term_input',
self.get_mock_term_input(
- ['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'py.test', 'pytest']))
+ [
+ '4', # Python versions: choose one by one
+ 'Y', # py26
+ 'Y', # py27
+ 'Y', # py32
+ 'Y', # py33
+ 'Y', # py34
+ 'Y', # pypy
+ 'N', # jython
+ 'py.test', # command to run tests
+ 'pytest' # test dependencies
+ ]
+ )
+ )
tox._quickstart.main(argv=['tox-quickstart'])
@@ -39,7 +54,7 @@ class TestToxQuickstartMain(object):
# and then run "tox" from this directory.
[tox]
-envlist = py26, py27, py32, py33, pypy
+envlist = py26, py27, py32, py33, py34, pypy
[testenv]
commands = py.test
@@ -49,11 +64,26 @@ deps =
result = open('tox.ini').read()
assert(result == expected_tox_ini)
- def test_quickstart_main_choose_individual_pythons_and_nose_adds_deps(self, monkeypatch):
+ def test_quickstart_main_choose_individual_pythons_and_nose_adds_deps(
+ self,
+ monkeypatch):
monkeypatch.setattr(
tox._quickstart, 'term_input',
- self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'N',
- 'nosetests', '']))
+ self.get_mock_term_input(
+ [
+ '4', # Python versions: choose one by one
+ 'Y', # py26
+ 'Y', # py27
+ 'Y', # py32
+ 'Y', # py33
+ 'Y', # py34
+ 'Y', # pypy
+ 'N', # jython
+ 'nosetests', # command to run tests
+ '' # test dependencies
+ ]
+ )
+ )
tox._quickstart.main(argv=['tox-quickstart'])
@@ -64,7 +94,7 @@ deps =
# and then run "tox" from this directory.
[tox]
-envlist = py26, py27, py32, py33, pypy
+envlist = py26, py27, py32, py33, py34, pypy
[testenv]
commands = nosetests
@@ -74,11 +104,26 @@ deps =
result = open('tox.ini').read()
assert(result == expected_tox_ini)
- def test_quickstart_main_choose_individual_pythons_and_trial_adds_deps(self, monkeypatch):
+ def test_quickstart_main_choose_individual_pythons_and_trial_adds_deps(
+ self,
+ monkeypatch):
monkeypatch.setattr(
tox._quickstart, 'term_input',
- self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'N',
- 'trial', '']))
+ self.get_mock_term_input(
+ [
+ '4', # Python versions: choose one by one
+ 'Y', # py26
+ 'Y', # py27
+ 'Y', # py32
+ 'Y', # py33
+ 'Y', # py34
+ 'Y', # pypy
+ 'N', # jython
+ 'trial', # command to run tests
+ '' # test dependencies
+ ]
+ )
+ )
tox._quickstart.main(argv=['tox-quickstart'])
@@ -89,7 +134,7 @@ deps =
# and then run "tox" from this directory.
[tox]
-envlist = py26, py27, py32, py33, pypy
+envlist = py26, py27, py32, py33, py34, pypy
[testenv]
commands = trial
@@ -99,11 +144,26 @@ deps =
result = open('tox.ini').read()
assert(result == expected_tox_ini)
- def test_quickstart_main_choose_individual_pythons_and_pytest_adds_deps(self, monkeypatch):
+ def test_quickstart_main_choose_individual_pythons_and_pytest_adds_deps(
+ self,
+ monkeypatch):
monkeypatch.setattr(
tox._quickstart, 'term_input',
- self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'Y', 'N',
- 'py.test', '']))
+ self.get_mock_term_input(
+ [
+ '4', # Python versions: choose one by one
+ 'Y', # py26
+ 'Y', # py27
+ 'Y', # py32
+ 'Y', # py33
+ 'Y', # py34
+ 'Y', # pypy
+ 'N', # jython
+ 'py.test', # command to run tests
+ '' # test dependencies
+ ]
+ )
+ )
tox._quickstart.main(argv=['tox-quickstart'])
expected_tox_ini = """
@@ -113,7 +173,7 @@ deps =
# and then run "tox" from this directory.
[tox]
-envlist = py26, py27, py32, py33, pypy
+envlist = py26, py27, py32, py33, py34, pypy
[testenv]
commands = py.test
@@ -123,10 +183,19 @@ deps =
result = open('tox.ini').read()
assert(result == expected_tox_ini)
- def test_quickstart_main_choose_py27_and_pytest_adds_deps(self, monkeypatch):
+ def test_quickstart_main_choose_py27_and_pytest_adds_deps(
+ self,
+ monkeypatch):
monkeypatch.setattr(
tox._quickstart, 'term_input',
- self.get_mock_term_input(['1', 'py.test', '']))
+ self.get_mock_term_input(
+ [
+ '1', # py27
+ 'py.test', # command to run tests
+ '' # test dependencies
+ ]
+ )
+ )
tox._quickstart.main(argv=['tox-quickstart'])
@@ -147,10 +216,19 @@ deps =
result = open('tox.ini').read()
assert(result == expected_tox_ini)
- def test_quickstart_main_choose_py27_and_py33_and_pytest_adds_deps(self, monkeypatch):
+ def test_quickstart_main_choose_py27_and_py33_and_pytest_adds_deps(
+ self,
+ monkeypatch):
monkeypatch.setattr(
tox._quickstart, 'term_input',
- self.get_mock_term_input(['2', 'py.test', '']))
+ self.get_mock_term_input(
+ [
+ '2', # py27 and py33
+ 'py.test', # command to run tests
+ '' # test dependencies
+ ]
+ )
+ )
tox._quickstart.main(argv=['tox-quickstart'])
@@ -171,10 +249,19 @@ deps =
result = open('tox.ini').read()
assert(result == expected_tox_ini)
- def test_quickstart_main_choose_all_pythons_and_pytest_adds_deps(self, monkeypatch):
+ def test_quickstart_main_choose_all_pythons_and_pytest_adds_deps(
+ self,
+ monkeypatch):
monkeypatch.setattr(
tox._quickstart, 'term_input',
- self.get_mock_term_input(['3', 'py.test', '']))
+ self.get_mock_term_input(
+ [
+ '3', # all Python versions
+ 'py.test', # command to run tests
+ '' # test dependencies
+ ]
+ )
+ )
tox._quickstart.main(argv=['tox-quickstart'])
@@ -185,7 +272,7 @@ deps =
# and then run "tox" from this directory.
[tox]
-envlist = py26, py27, py32, py33, pypy, jython
+envlist = py26, py27, py32, py33, py34, pypy, jython
[testenv]
commands = py.test
@@ -195,10 +282,26 @@ deps =
result = open('tox.ini').read()
assert(result == expected_tox_ini)
- def test_quickstart_main_choose_individual_pythons_and_defaults(self, monkeypatch):
+ def test_quickstart_main_choose_individual_pythons_and_defaults(
+ self,
+ monkeypatch):
monkeypatch.setattr(
tox._quickstart, 'term_input',
- self.get_mock_term_input(['4', '', '', '', '', '', '', '', '', '', '']))
+ self.get_mock_term_input(
+ [
+ '4', # Python versions: choose one by one
+ '', # py26
+ '', # py27
+ '', # py32
+ '', # py33
+ '', # py34
+ '', # pypy
+ '', # jython
+ '', # command to run tests
+ '' # test dependencies
+ ]
+ )
+ )
tox._quickstart.main(argv=['tox-quickstart'])
@@ -209,7 +312,7 @@ deps =
# and then run "tox" from this directory.
[tox]
-envlist = py26, py27, py32, py33, pypy, jython
+envlist = py26, py27, py32, py33, py34, pypy, jython
[testenv]
commands = {envpython} setup.py test
@@ -228,7 +331,22 @@ deps =
monkeypatch.setattr(
tox._quickstart, 'term_input',
- self.get_mock_term_input(['4', '', '', '', '', '', '', '', '', '', '', '']))
+ self.get_mock_term_input(
+ [
+ '4', # Python versions: choose one by one
+ '', # py26
+ '', # py27
+ '', # py32
+ '', # py33
+ '', # py34
+ '', # pypy
+ '', # jython
+ '', # command to run tests
+ '', # test dependencies
+ '', # tox.ini already exists; overwrite?
+ ]
+ )
+ )
tox._quickstart.main(argv=['tox-quickstart'])
@@ -239,7 +357,7 @@ deps =
# and then run "tox" from this directory.
[tox]
-envlist = py26, py27, py32, py33, pypy, jython
+envlist = py26, py27, py32, py33, py34, pypy, jython
[testenv]
commands = {envpython} setup.py test
@@ -257,6 +375,7 @@ class TestToxQuickstart(object):
'py27': True,
'py32': True,
'py33': True,
+ 'py34': True,
'pypy': True,
'commands': 'py.test',
'deps': 'pytest',
@@ -268,7 +387,7 @@ class TestToxQuickstart(object):
# and then run "tox" from this directory.
[tox]
-envlist = py26, py27, py32, py33, pypy
+envlist = py26, py27, py32, py33, py34, pypy
[testenv]
commands = py.test
@@ -339,6 +458,7 @@ deps =
'py27': True,
'py32': True,
'py33': True,
+ 'py34': True,
'pypy': True,
'commands': 'nosetests -v',
'deps': 'nose',
@@ -350,7 +470,7 @@ deps =
# and then run "tox" from this directory.
[tox]
-envlist = py27, py32, py33, pypy
+envlist = py27, py32, py33, py34, pypy
[testenv]
commands = nosetests -v
diff --git a/tests/test_venv.py b/tests/test_venv.py
index c2ed371..dbfec41 100644
--- a/tests/test_venv.py
+++ b/tests/test_venv.py
@@ -3,7 +3,7 @@ import tox
import pytest
import os, sys
import tox._config
-from tox._venv import *
+from tox._venv import * # noqa
#def test_global_virtualenv(capfd):
# v = VirtualEnv()
@@ -48,8 +48,11 @@ def test_create(monkeypatch, mocksession, newconfig):
l = mocksession._pcalls
assert len(l) >= 1
args = l[0].args
- assert "virtualenv" in str(args[0])
+ assert "virtualenv" in str(args[1])
if sys.platform != "win32":
+ # realpath is needed for stuff like the debian symlinks
+ assert py.path.local(sys.executable).realpath() \
+ == py.path.local(args[0]).realpath()
#assert Envconfig.toxworkdir in args
assert venv.getcommandpath("easy_install", cwd=py.path.local())
interp = venv._getliveconfig().python
@@ -274,7 +277,7 @@ def test_install_command_not_installed(newmocksession, monkeypatch):
venv = mocksession.getenv('python')
venv.test()
mocksession.report.expect("warning", "*test command found but not*")
- assert venv.status == "commands failed"
+ assert venv.status == 0
def test_install_command_whitelisted(newmocksession, monkeypatch):
mocksession = newmocksession(['--recreate'], """
@@ -292,7 +295,7 @@ def test_install_command_whitelisted(newmocksession, monkeypatch):
assert venv.status == "commands failed"
@pytest.mark.skipif("not sys.platform.startswith('linux')")
-def test_install_command_not_installed(newmocksession):
+def test_install_command_not_installed_bash(newmocksession):
mocksession = newmocksession(['--recreate'], """
[testenv]
commands=
@@ -318,7 +321,7 @@ def test_install_python3(tmpdir, newmocksession):
l = mocksession._pcalls
assert len(l) == 1
args = l[0].args
- assert str(args[0]).endswith('virtualenv')
+ assert str(args[1]).endswith('virtualenv')
l[:] = []
action = mocksession.newaction(venv, "hello")
venv._install(["hello"], action=action)
@@ -384,7 +387,7 @@ class TestCreationConfig:
[testenv]
deps={distshare}/xyz-*
""")
- xyz = config.distshare.ensure("xyz-1.2.0.zip")
+ config.distshare.ensure("xyz-1.2.0.zip")
xyz2 = config.distshare.ensure("xyz-1.2.1.zip")
envconfig = config.envconfigs['python']
venv = VirtualEnv(envconfig, session=mocksession)
@@ -504,7 +507,6 @@ def test_setenv_added_to_pcall(tmpdir, mocksession, newconfig):
l = mocksession._pcalls
assert len(l) == 2
for x in l:
- args = x.args
env = x.env
assert env is not None
assert 'ENV_VAR' in env
@@ -582,6 +584,7 @@ def test_command_relative_issue26(newmocksession, tmpdir, monkeypatch):
mocksession.report.not_expect("warning", "*test command found but not*")
monkeypatch.setenv("PATH", str(tmpdir))
x4 = venv.getcommandpath("x", cwd=tmpdir)
+ assert x4.endswith('/x')
mocksession.report.expect("warning", "*test command found but not*")
def test_sethome_only_on_option(newmocksession, monkeypatch):
diff --git a/tests/test_z_cmdline.py b/tests/test_z_cmdline.py
index 343c142..283c993 100644
--- a/tests/test_z_cmdline.py
+++ b/tests/test_z_cmdline.py
@@ -1,7 +1,6 @@
import tox
import py
import pytest
-import sys
from tox._pytestplugin import ReportExpectMock
try:
import json
@@ -129,7 +128,6 @@ class TestSession:
})
config = parseconfig([])
session = Session(config)
- envlist = ['hello', 'world']
envs = session.venvlist
assert len(envs) == 2
env1, env2 = envs
@@ -193,6 +191,19 @@ def test_minversion(cmd, initproj):
])
assert result.ret
+def test_run_custom_install_command_error(cmd, initproj):
+ initproj("interp123-0.5", filedefs={
+ 'tox.ini': '''
+ [testenv]
+ install_command=./tox.ini {opts} {packages}
+ '''
+ })
+ result = cmd.run("tox")
+ result.stdout.fnmatch_lines([
+ "ERROR: invocation failed, args: ['*/tox.ini*",
+ ])
+ assert result.ret
+
def test_unknown_interpreter_and_env(cmd, initproj):
initproj("interp123-0.5", filedefs={
'tests': {'test_hello.py': "def test_hello(): pass"},
@@ -231,6 +242,22 @@ def test_unknown_interpreter(cmd, initproj):
"*ERROR*InterpreterNotFound*xyz_unknown_interpreter*",
])
+def test_skip_unknown_interpreter(cmd, initproj):
+ initproj("interp123-0.5", filedefs={
+ 'tests': {'test_hello.py': "def test_hello(): pass"},
+ 'tox.ini': '''
+ [testenv:python]
+ basepython=xyz_unknown_interpreter
+ [testenv]
+ changedir=tests
+ '''
+ })
+ result = cmd.run("tox", "--skip-missing-interpreters")
+ assert not result.ret
+ result.stdout.fnmatch_lines([
+ "*SKIPPED*InterpreterNotFound*xyz_unknown_interpreter*",
+ ])
+
def test_unknown_dep(cmd, initproj):
initproj("dep123-0.7", filedefs={
'tests': {'test_hello.py': "def test_hello(): pass"},
@@ -567,7 +594,7 @@ def test_sdistonly(initproj, cmd):
result.stdout.fnmatch_lines([
"*sdist-make*setup.py*",
])
- assert "virtualenv" not in result.stdout.str()
+ assert "-mvirtualenv" not in result.stdout.str()
def test_separate_sdist_no_sdistfile(cmd, initproj):
distshare = cmd.tmpdir.join("distshare")
@@ -582,6 +609,7 @@ def test_separate_sdist_no_sdistfile(cmd, initproj):
l = distshare.listdir()
assert len(l) == 1
sdistfile = l[0]
+ assert 'pkg123-0.7.zip' in str(sdistfile)
def test_separate_sdist(cmd, initproj):
distshare = cmd.tmpdir.join("distshare")
@@ -611,7 +639,6 @@ def test_sdist_latest(tmpdir, newconfig):
distshare=%s
sdistsrc={distshare}/pkg123-*
""" % distshare)
- p0 = distshare.ensure("pkg123-1.3.5.zip")
p = distshare.ensure("pkg123-1.4.5.zip")
distshare.ensure("pkg123-1.4.5a1.zip")
session = Session(config)
diff --git a/tox.ini b/tox.ini
index f2fc791..6bcca5e 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist=py27,py33,py26,py32,pypy
+envlist=py27,py26,py34,py33,py32,pypy,flakes
[testenv:X]
commands=echo {posargs}
@@ -18,6 +18,10 @@ commands=
--junitxml={envlogdir}/junit-{envname}.xml \
check_sphinx.py {posargs}
+[testenv:flakes]
+deps = pytest-flakes>=0.2
+commands = py.test --flakes -m flakes tox tests
+
[testenv:py25]
setenv= PIP_INSECURE=1
diff --git a/tox/__init__.py b/tox/__init__.py
index 5ff0e85..d7b2155 100644
--- a/tox/__init__.py
+++ b/tox/__init__.py
@@ -1,5 +1,5 @@
#
-__version__ = '1.7.0'
+__version__ = '1.7.1'
class exception:
class Error(Exception):
@@ -20,4 +20,4 @@ class exception:
class MissingDependency(Error):
""" a dependency could not be found or determined. """
-from tox._cmdline import main as cmdline
+from tox._cmdline import main as cmdline # noqa
diff --git a/tox/_cmdline.py b/tox/_cmdline.py
index 7c6cd69..aed71ec 100644
--- a/tox/_cmdline.py
+++ b/tox/_cmdline.py
@@ -79,7 +79,6 @@ class Action(object):
return f
def popen(self, args, cwd=None, env=None, redirect=True, returnout=False):
- logged_command = "%s$ %s" %(cwd, " ".join(map(str, args)))
f = outpath = None
resultjson = self.session.config.option.resultjson
if resultjson or redirect:
@@ -93,8 +92,13 @@ class Action(object):
if cwd is None:
# XXX cwd = self.session.config.cwd
cwd = py.path.local()
- popen = self._popen(args, cwd, env=env,
- stdout=f, stderr=STDOUT)
+ try:
+ popen = self._popen(args, cwd, env=env,
+ stdout=f, stderr=STDOUT)
+ except OSError:
+ self.report.error("invocation failed, args: %s, cwd: %s" %
+ (args, cwd))
+ raise
popen.outpath = outpath
popen.args = [str(x) for x in args]
popen.cwd = cwd
@@ -223,6 +227,9 @@ class Reporter(object):
def error(self, msg):
self.logline("ERROR: " + msg, red=True)
+ def skip(self, msg):
+ self.logline("SKIPPED:" + msg, yellow=True)
+
def logline(self, msg, **opts):
self._reportedlines.append(msg)
self.tw.line("%s" % msg, **opts)
@@ -411,7 +418,8 @@ class Session:
sdist_path = self._makesdist()
except tox.exception.InvocationError:
v = sys.exc_info()[1]
- self.report.error("FAIL could not package project")
+ self.report.error("FAIL could not package project - v = %r" %
+ v)
return
sdistfile = self.config.distshare.join(sdist_path.basename)
if sdistfile != sdist_path:
@@ -461,7 +469,14 @@ class Session:
retcode = 0
for venv in self.venvlist:
status = venv.status
- if status and status != "skipped tests":
+ if isinstance(status, tox.exception.InterpreterNotFound):
+ msg = " %s: %s" %(venv.envconfig.envname, str(status))
+ if self.config.option.skip_missing_interpreters:
+ self.report.skip(msg)
+ else:
+ retcode = 1
+ self.report.error(msg)
+ elif status and status != "skipped tests":
msg = " %s: %s" %(venv.envconfig.envname, str(status))
self.report.error(msg)
retcode = 1
diff --git a/tox/_config.py b/tox/_config.py
index 7639f63..5c6398a 100644
--- a/tox/_config.py
+++ b/tox/_config.py
@@ -1,13 +1,10 @@
import argparse
-import distutils.sysconfig
import os
import random
import sys
import re
import shlex
import string
-import subprocess
-import textwrap
import pkg_resources
from tox.interpreters import Interpreters
@@ -123,7 +120,8 @@ def prepare_parse(pkgname):
parser.add_argument("--hashseed", action="store",
metavar="SEED", default=None,
help="set PYTHONHASHSEED to SEED before running commands. "
- "Defaults to a random integer in the range 1 to 4294967295. "
+ "Defaults to a random integer in the range [1, 4294967295] "
+ "([1, 1024] on Windows). "
"Passing 'noset' suppresses this behavior.")
parser.add_argument("--force-dep", action="append",
metavar="REQ", default=None,
@@ -132,6 +130,8 @@ def prepare_parse(pkgname):
"'pytest<2.7' or 'django>=1.6'.")
parser.add_argument("--sitepackages", action="store_true",
help="override sitepackages setting to True in all envs")
+ parser.add_argument("--skip-missing-interpreters", action="store_true",
+ help="don't fail tests for missing interpreters")
parser.add_argument("args", nargs="*",
help="additional arguments available to command positional substitution")
@@ -200,12 +200,15 @@ def get_homedir():
return None
def make_hashseed():
- return str(random.randint(1, 4294967295))
+ max_seed = 4294967295
+ if sys.platform == 'win32' and sys.version_info < (3, 4):
+ max_seed = 1024
+ return str(random.randint(1, max_seed))
class parseini:
def __init__(self, config, inipath):
config.toxinipath = inipath
- config.toxinidir = toxinidir = config.toxinipath.dirpath()
+ config.toxinidir = config.toxinipath.dirpath()
self._cfg = py.iniconfig.IniConfig(config.toxinipath)
config._cfg = self._cfg
@@ -239,7 +242,7 @@ class parseini:
# determine indexserver dictionary
config.indexserver = {'default': IndexServerConfig('default')}
prefix = "indexserver"
- for line in reader.getlist(toxsection, "indexserver"):
+ for line in reader.getlist(toxsection, prefix):
name, url = map(lambda x: x.strip(), line.split("=", 1))
config.indexserver[name] = IndexServerConfig(name, url)
@@ -601,17 +604,25 @@ class IniReader:
return x
def _replace_env(self, match):
- envkey = match.group('substitution_value')
- if not envkey:
+ match_value = match.group('substitution_value')
+ if not match_value:
raise tox.exception.ConfigError(
'env: requires an environment variable name')
- if not envkey in os.environ:
+ default = None
+ envkey_split = match_value.split(':', 1)
+
+ if len(envkey_split) is 2:
+ envkey, default = envkey_split
+ else:
+ envkey = match_value
+
+ if not envkey in os.environ and default is None:
raise tox.exception.ConfigError(
"substitution env:%r: unkown environment variable %r" %
(envkey, envkey))
- return os.environ[envkey]
+ return os.environ.get(envkey, default)
def _substitute_from_other_section(self, key):
if key.startswith("[") and "]" in key:
@@ -739,3 +750,4 @@ def getcontextname():
if 'HUDSON_URL' in os.environ:
return 'jenkins'
return None
+
diff --git a/tox/_quickstart.py b/tox/_quickstart.py
index f66f1e2..098eb61 100644
--- a/tox/_quickstart.py
+++ b/tox/_quickstart.py
@@ -56,7 +56,7 @@ except NameError:
term_input = input
-all_envs = ['py26', 'py27', 'py32', 'py33', 'pypy', 'jython']
+all_envs = ['py26', 'py27', 'py32', 'py33', 'py34', 'pypy', 'jython']
PROMPT_PREFIX = '> '
diff --git a/tox/_venv.py b/tox/_venv.py
index f8282d8..7e9f45f 100644
--- a/tox/_venv.py
+++ b/tox/_venv.py
@@ -1,6 +1,5 @@
from __future__ import with_statement
-import sys, os, re
-import subprocess
+import sys, os
import py
import tox
from tox._config import DepConfig
@@ -121,8 +120,6 @@ class VirtualEnv(object):
"""
if action is None:
action = self.session.newaction(self, "update")
- report = self.session.report
- name = self.envconfig.envname
rconfig = CreationConfig.readconfig(self.path_config)
if not self.envconfig.recreate and rconfig and \
rconfig.matches(self._getliveconfig()):
@@ -142,7 +139,8 @@ class VirtualEnv(object):
self.install_deps(action)
except tox.exception.InvocationError:
v = sys.exc_info()[1]
- return "could not install deps %s" %(self.envconfig.deps,)
+ return "could not install deps %s; v = %r" % (
+ self.envconfig.deps, v)
def _getliveconfig(self):
python = self.envconfig._basepython_info.executable
@@ -179,7 +177,7 @@ class VirtualEnv(object):
action = self.session.newaction(self, "create")
config_interpreter = self.getsupportedinterpreter()
- args = ['virtualenv']
+ args = [sys.executable, '-mvirtualenv']
if self.envconfig.distribute:
args.append("--distribute")
else:
diff --git a/tox/_verlib.py b/tox/_verlib.py
index 1df3645..a234176 100644
--- a/tox/_verlib.py
+++ b/tox/_verlib.py
@@ -8,7 +8,6 @@ licensed under the PSF license (i guess)
"""
-import sys
import re
class IrrationalVersionError(Exception):
diff --git a/tox/interpreters.py b/tox/interpreters.py
index e225fcc..75318c5 100644
--- a/tox/interpreters.py
+++ b/tox/interpreters.py
@@ -1,8 +1,6 @@
import sys
-import os
import py
import re
-import subprocess
import inspect
class Interpreters:
@@ -161,7 +159,7 @@ else:
# The standard executables can be found as a last resort via the
# Python launcher py.exe
if m:
- locate_via_py(*m.groups())
+ return locate_via_py(*m.groups())
def pyinfo():
import sys