[Pytest-commit] commit/tox: 5 new changesets
commits-noreply at bitbucket.org
commits-noreply at bitbucket.org
Mon Feb 9 12:13:59 CET 2015
5 new commits in tox:
https://bitbucket.org/hpk42/tox/commits/f6f6e21a6172/
Changeset: f6f6e21a6172
Branch: echo-captured-output
User: fschulze
Date: 2015-01-29 12:59:26+00:00
Summary: Add ``--result-tee`` option to echo output captured for the json result to stdout.
Affected #: 2 files
diff -r df35850bb8d3d8a482f4415a6cab86dad23c951a -r f6f6e21a6172a3ad3dd264746123f0660576dd69 tox/_cmdline.py
--- a/tox/_cmdline.py
+++ b/tox/_cmdline.py
@@ -11,6 +11,7 @@
import os
import sys
import subprocess
+import time
from tox._verlib import NormalizedVersion, IrrationalVersionError
from tox._venv import VirtualEnv
from tox._config import parseconfig
@@ -80,20 +81,24 @@
def popen(self, args, cwd=None, env=None, redirect=True, returnout=False):
logged_command = "%s$ %s" %(cwd, " ".join(map(str, args)))
- f = outpath = None
+ stdout = outpath = None
resultjson = self.session.config.option.resultjson
+ resulttee = self.session.config.option.resulttee
if resultjson or redirect:
f = self._initlogpath(self.id)
f.write("actionid=%s\nmsg=%s\ncmdargs=%r\nenv=%s\n" %(
self.id, self.msg, args, env))
f.flush()
self.popen_outpath = outpath = py.path.local(f.name)
+ stdout = f
elif returnout:
- f = subprocess.PIPE
+ stdout = subprocess.PIPE
+ if resultjson and resulttee:
+ stdout = subprocess.PIPE
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)
+ popen = self._popen(args, cwd, env=env, stdout=stdout, stderr=STDOUT)
popen.outpath = outpath
popen.args = [str(x) for x in args]
popen.cwd = cwd
@@ -102,7 +107,23 @@
try:
self.report.logpopen(popen, env=env)
try:
- out, err = popen.communicate()
+ if resultjson and resulttee:
+ assert popen.stderr is None # prevent deadlock
+ out = None
+ last_time = time.time()
+ while 1:
+ data = popen.stdout.read(1)
+ if data:
+ sys.stdout.write(data)
+ if '\n' in data or (time.time() - last_time) > 5:
+ sys.stdout.flush()
+ last_time = time.time()
+ f.write(data)
+ elif popen.poll() is not None:
+ popen.stdout.close()
+ break
+ else:
+ out, err = popen.communicate()
except KeyboardInterrupt:
self.report.keyboard_interrupt()
popen.wait()
diff -r df35850bb8d3d8a482f4415a6cab86dad23c951a -r f6f6e21a6172a3ad3dd264746123f0660576dd69 tox/_config.py
--- a/tox/_config.py
+++ b/tox/_config.py
@@ -116,6 +116,9 @@
"all commands and results involved. This will turn off "
"pass-through output from running test commands which is "
"instead captured into the json result file.")
+ parser.add_argument("--result-tee", action="store_true",
+ dest="resulttee",
+ help="echo output of --result-json to stdout while it is captured.")
parser.add_argument("args", nargs="*",
help="additional arguments available to command positional substitution")
return parser
https://bitbucket.org/hpk42/tox/commits/8b053b6f34d4/
Changeset: 8b053b6f34d4
Branch: echo-captured-output
User: fschulze
Date: 2015-01-29 13:25:16+00:00
Summary: Merged default
Affected #: 36 files
diff -r f6f6e21a6172a3ad3dd264746123f0660576dd69 -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf .hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -14,3 +14,4 @@
doc/_build/
tox.egg-info
.tox
+.cache
diff -r f6f6e21a6172a3ad3dd264746123f0660576dd69 -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf .hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -15,3 +15,8 @@
8fcc1bed3e918b625d85864cc3f4623578852e7e 1.5.0
33e5e5dff406e699893a65ecd5044d3eee35b69b 1.6.0
2e580ee6feea934cc2e683635abded27c0de0be9 1.6.1
+5b4e536b8d3810c791b742b2a8723c53b8d3d720 1.7.0
+c7155565c89d1bb3684c881ca774d921188223a0 1.7.1
+e319e464470a5885505ab3e1da1a3a7abe5f86e2 1.7.2
+b7374e501bde055c5c2b572e6512d22e10f60088 1.8.0
+2aa9b587d12ae4b325cb4d5a9a801a222ffc328c 1.8.1
diff -r f6f6e21a6172a3ad3dd264746123f0660576dd69 -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,9 +1,139 @@
-1.6.2.dev
+1.9.0.dev
+-----------
+
+- fix issue193: Remove ``--pre`` from the default ``install_command``; by
+ default tox will now only install final releases from PyPI for unpinned
+ dependencies. Use ``pip_pre = true`` in a testenv or the ``--pre``
+ command-line option to restore the previous behavior.
+
+- fix issue199: fill resultlog structure ahead of virtualenv creation
+
+- refine determination if we run from Jenkins, thanks Borge Lanes.
+
+
+1.8.1
+-----------
+
+- fix issue190: allow setenv to be empty.
+
+- allow escaping curly braces with "\". Thanks Marc Abramowitz for the PR.
+
+- allow "." names in environment names such that "py27-django1.7" is a
+ valid environment name. Thanks Alex Gaynor and Alex Schepanovski.
+
+- report subprocess exit code when execution fails. Thanks Marius
+ Gedminas.
+
+1.8.0
+-----------
+
+- new multi-dimensional configuration support. Many thanks to
+ Alexander Schepanovski for the complete PR with docs.
+ And to Mike Bayer and others for testing and feedback.
+
+- fix issue148: remove "__PYVENV_LAUNCHER__" from os.environ when starting
+ subprocesses. Thanks Steven Myint.
+
+- fix issue152: set VIRTUAL_ENV when running test commands,
+ thanks Florian Ludwig.
+
+- better report if we can't get version_info from an interpreter
+ executable. Thanks Floris Bruynooghe.
+
+
+1.7.2
+-----------
+
+- fix issue150: parse {posargs} more like we used to do it pre 1.7.0.
+ The 1.7.0 behaviour broke a lot of OpenStack projects.
+ See PR85 and the issue discussions for (far) more details, hopefully
+ resulting in a more refined behaviour in the 1.8 series.
+ And thanks to Clark Boylan for the PR.
+
+- fix issue59: add a config variable ``skip-missing-interpreters`` as well as
+ command line 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
+---------
+
+- don't lookup "pip-script" anymore but rather just "pip" on windows
+ as this is a pip implementation detail and changed with pip-1.5.
+ It might mean that tox-1.7 is not able to install a different pip
+ version into a virtualenv anymore.
+
+- drop Python2.5 compatibility because it became too hard due
+ to the setuptools-2.0 dropping support. tox now has no
+ support for creating python2.5 based environments anymore
+ and all internal special-handling has been removed.
+
+- merged PR81: new option --force-dep which allows to
+ override tox.ini specified dependencies in setuptools-style.
+ For example "--force-dep 'django<1.6'" will make sure
+ that any environment using "django" as a dependency will
+ get the latest 1.5 release. Thanks Bruno Oliveria for
+ the complete PR.
+
+- merged PR125: tox now sets "PYTHONHASHSEED" to a random value
+ and offers a "--hashseed" option to repeat a test run with a specific seed.
+ You can also use --hashsheed=noset to instruct tox to leave the value
+ alone. Thanks Chris Jerdonek for all the work behind this.
+
+- fix issue132: removing zip_safe setting (so it defaults to false)
+ to allow installation of tox
+ via easy_install/eggs. Thanks Jenisys.
+
+- fix issue126: depend on virtualenv>=1.11.2 so that we can rely
+ (hopefully) on a pip version which supports --pre. (tox by default
+ uses to --pre). also merged in PR84 so that we now call "virtualenv"
+ directly instead of looking up interpreters. Thanks Ionel Maries Cristian.
+ This also fixes issue140.
+
+- fix issue130: you can now set install_command=easy_install {opts} {packages}
+ and expect it to work for repeated tox runs (previously it only worked
+ when always recreating). Thanks jenisys for precise reporting.
+
+- fix issue129: tox now uses Popen(..., universal_newlines=True) to force
+ creation of unicode stdout/stderr streams. fixes a problem on specific
+ platform configs when creating virtualenvs with Python3.3. Thanks
+ Jorgen Schäfer or investigation and solution sketch.
+
- fix issue128: enable full substitution in install_command,
thanks for the PR to Ronald Evers
+- rework and simplify "commands" parsing and in particular posargs
+ substitutions to avoid various win32/posix related quoting issues.
+
+- make sure that the --installpkg option trumps any usedevelop settings
+ in tox.ini or
+
+- introduce --no-network to tox's own test suite to skip tests
+ requiring networks
+
+- introduce --sitepackages to force sitepackages=True in all
+ environments.
+
+- fix issue105 -- don't depend on an existing HOME directory from tox tests.
+
1.6.1
-----
diff -r f6f6e21a6172a3ad3dd264746123f0660576dd69 -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf CONTRIBUTORS
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -3,6 +3,7 @@
Krisztian Fekete
Marc Abramowitz
+Aleaxner Schepanovski
Sridhar Ratnakumar
Barry Warsaw
Chris Rose
@@ -11,3 +12,20 @@
Lukasz Balcerzak
Philip Thiem
Monty Taylor
+Bruno Oliveira
+Ionel Maries Cristian
+Anatoly techntonik
+Matt Jeffery
+Chris Jerdonek
+Ronald Evers
+Carl Meyer
+Anthon van der Neuth
+Matt Good
+Mattieu Agopian
+Asmund Grammeltwedt
+Ionel Maries Cristian
+Alexandre Conrad
+Morgan Fainberg
+Marc Schlaich
+Clark Boylan
+Eugene Yunak
diff -r f6f6e21a6172a3ad3dd264746123f0660576dd69 -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf README.rst
--- a/README.rst
+++ b/README.rst
@@ -21,5 +21,5 @@
have fun,
-holger krekel, May 2013
+holger krekel, 2014
diff -r f6f6e21a6172a3ad3dd264746123f0660576dd69 -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf doc/announce/release-1.8.txt
--- /dev/null
+++ b/doc/announce/release-1.8.txt
@@ -0,0 +1,54 @@
+tox 1.8: Generative/combinatorial environments specs
+=============================================================================
+
+I am happy to announce tox 1.8 which implements parametrized environments.
+
+See https://tox.testrun.org/latest/config.html#generating-environments-conditional-settings
+for examples and the new backward compatible syntax in your tox.ini file.
+
+Many thanks to Alexander Schepanovski for implementing and refining
+it based on the specifcation draft.
+
+More documentation about tox in general:
+
+ http://tox.testrun.org/
+
+Installation:
+
+ pip install -U tox
+
+code hosting and issue tracking on bitbucket:
+
+ https://bitbucket.org/hpk42/tox
+
+What is tox?
+----------------
+
+tox standardizes and automates tedious test activities driven from a
+simple ``tox.ini`` file, including:
+
+* creation and management of different virtualenv environments
+ with different Python interpreters
+* packaging and installing your package into each of them
+* running your test tool of choice, be it nose, py.test or unittest2 or other tools such as "sphinx" doc checks
+* testing dev packages against each other without needing to upload to PyPI
+
+best,
+Holger Krekel, merlinux GmbH
+
+
+Changes 1.8 (compared to 1.7.2)
+---------------------------------------
+
+- new multi-dimensional configuration support. Many thanks to
+ Alexander Schepanovski for the complete PR with docs.
+ And to Mike Bayer and others for testing and feedback.
+
+- fix issue148: remove "__PYVENV_LAUNCHER__" from os.environ when starting
+ subprocesses. Thanks Steven Myint.
+
+- fix issue152: set VIRTUAL_ENV when running test commands,
+ thanks Florian Ludwig.
+
+- better report if we can't get version_info from an interpreter
+ executable. Thanks Floris Bruynooghe.
diff -r f6f6e21a6172a3ad3dd264746123f0660576dd69 -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf doc/conf.py
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -48,7 +48,8 @@
# built documents.
#
# The short X.Y version.
-release = version = "1.6.1"
+release = "1.8"
+version = "1.8.1"
# The full version, including alpha/beta/rc tags.
# The language for content autogenerated by Sphinx. Refer to documentation
diff -r f6f6e21a6172a3ad3dd264746123f0660576dd69 -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf doc/config-v2.txt
--- a/doc/config-v2.txt
+++ b/doc/config-v2.txt
@@ -195,7 +195,7 @@
tox comes with predefined settings for certain variants, namely:
* ``{easy,pip}`` use easy_install or pip respectively
-* ``{py24,py25,py26,py27,py31,py32,py33,pypy19]`` use the respective
+* ``{py24,py25,py26,py27,py31,py32,py33,py34,pypy19]`` use the respective
pythonNN or PyPy interpreter
* ``{win32,linux,darwin}`` defines the according ``platform``.
diff -r f6f6e21a6172a3ad3dd264746123f0660576dd69 -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf doc/config.txt
--- a/doc/config.txt
+++ b/doc/config.txt
@@ -28,20 +28,31 @@
(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``.
+.. confval:: skip_missing_interpreters=BOOL
-envlist setting
-+++++++++++++++
+ .. versionadded:: 1.7.2
-Determining the environment list that ``tox`` is to operate on
-happens in this order:
+ Setting this to ``True`` is equivalent of passing the
+ ``--skip-missing-interpreters`` command line option, and will force ``tox`` to
+ return success even if some of the specified environments were missing. This is
+ useful for some CI systems or running on a developer box, where you might only
+ have a subset of all your supported interpreters installed but don't want to
+ mark the build as failed because of it. As expected, the command line switch
+ always overrides this setting if passed on the invokation.
+ **Default:** ``False``
-* command line option ``-eENVLIST``
-* environment variable ``TOXENV``
-* ``tox.ini`` file's ``envlist``
+.. confval:: envlist=CSV
+
+ Determining the environment list that ``tox`` is to operate on
+ happens in this order (if any is found, no further lookups are made):
+
+ * command line option ``-eENVLIST``
+ * environment variable ``TOXENV``
+ * ``tox.ini`` file's ``envlist``
Virtualenv test environment settings
@@ -80,7 +91,7 @@
.. 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 +105,29 @@
if you have configured it.
**default**::
-
- pip install --pre {opts} {packages}
-
- **default on environments using python2.5**::
- pip install {opts} {packages}``
+ 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.
+.. confval:: pip_pre=True|False(default)
+
+ .. versionadded:: 1.9
+
+ If ``True``, adds ``--pre`` to the ``opts`` passed to
+ :confval:`install_command`. If :confval:`install_command` uses pip, this
+ will cause it to install the latest available pre-release of any
+ dependencies without a specified version. If ``False`` (the default), pip
+ will only install final releases of unpinned dependencies.
+
+ Passing the ``--pre`` command-line option to tox will force this to
+ ``True`` for all testenvs.
+
+ Don't set this option if your :confval:`install_command` does not use pip.
.. 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 +154,7 @@
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 +182,17 @@
**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 +200,7 @@
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 +220,7 @@
.. 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::
@@ -241,6 +259,10 @@
Any ``key=value`` setting in an ini-file can make use
of value substitution through the ``{...}`` string-substitution pattern.
+You can escape curly braces with the ``\`` character if you need them, for example::
+
+ commands = echo "\{posargs\}" = {posargs}
+
Globally available substitutions
++++++++++++++++++++++++++++++++
@@ -292,6 +314,26 @@
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 +389,7 @@
pytest
mock
pytest-xdist
-
+
[testenv:dulwich]
deps =
dulwich
@@ -359,6 +401,152 @@
{[base]deps}
+Generating environments, conditional settings
+---------------------------------------------
+
+.. versionadded:: 1.8
+
+Suppose you want to test your package against python2.6, python2.7 and against
+several versions of a dependency, say Django 1.5 and Django 1.6. You can
+accomplish that by writing down 2*2 = 4 ``[testenv:*]`` sections and then
+listing all of them in ``envlist``.
+
+However, a better approach looks like this::
+
+ [tox]
+ envlist = {py26,py27}-django{15,16}
+
+ [testenv]
+ basepython =
+ py26: python2.6
+ py27: python2.7
+ deps =
+ pytest
+ django15: Django>=1.5,<1.6
+ django16: Django>=1.6,<1.7
+ py26: unittest2
+ commands = py.test
+
+This uses two new facilities of tox-1.8:
+
+- generative envlist declarations where each envname
+ consists of environment parts or "factors"
+
+- "factor" specific settings
+
+Let's go through this step by step.
+
+
+Generative envlist
++++++++++++++++++++++++
+
+::
+
+ envlist = {py26,py27}-django{15,16}
+
+This is bash-style syntax and will create ``2*2=4`` environment names
+like this::
+
+ py26-django15
+ py26-django16
+ py27-django15
+ py27-django16
+
+You can still list environments explicitly along with generated ones::
+
+ envlist = {py26,py27}-django{15,16}, docs, flake
+
+.. note::
+
+ To help with understanding how the variants will produce section values,
+ you can ask tox to show their expansion with a new option::
+
+ $ tox -l
+ py26-django15
+ py26-django16
+ py27-django15
+ py27-django16
+ docs
+ flake
+
+
+Factors and factor-conditional settings
+++++++++++++++++++++++++++++++++++++++++
+
+Parts of an environment name delimited by hyphens are called factors and can
+be used to set values conditionally::
+
+ basepython =
+ py26: python2.6
+ py27: python2.7
+
+This conditional setting will lead to either ``python2.6`` or
+``python2.7`` used as base python, e.g. ``python2.6`` is selected if current
+environment contains ``py26`` factor.
+
+In list settings such as ``deps`` or ``commands`` you can freely intermix
+optional lines with unconditional ones::
+
+ deps =
+ pytest
+ django15: Django>=1.5,<1.6
+ django16: Django>=1.6,<1.7
+ py26: unittest2
+
+Reading it line by line:
+
+- ``pytest`` will be included unconditionally,
+- ``Django>=1.5,<1.6`` will be included for environments containing ``django15`` factor,
+- ``Django>=1.6,<1.7`` similarly depends on ``django16`` factor,
+- ``unittest`` will be loaded for Python 2.6 environments.
+
+.. note::
+
+ Tox provides good defaults for basepython setting, so the above
+ ini-file can be further reduced by omitting the ``basepython``
+ setting.
+
+
+Complex factor conditions
++++++++++++++++++++++++++
+
+Sometimes you need to specify same line for several factors or create a special
+case for a combination of factors. Here is how you do it::
+
+ [tox]
+ envlist = py{26,27,33}-django{15,16}-{sqlite,mysql}
+
+ [testenv]
+ deps =
+ py33-mysql: PyMySQL ; use if both py33 and mysql are in an env name
+ py26,py27: urllib3 ; use if any of py26 or py27 are in an env name
+ py{26,27}-sqlite: mock ; mocking sqlite in python 2.x
+
+Take a look at first ``deps`` line. It shows how you can special case something
+for a combination of factors, you just join combining factors with a hyphen.
+This particular line states that ``PyMySQL`` will be loaded for python 3.3,
+mysql environments, e.g. ``py33-django15-mysql`` and ``py33-django16-mysql``.
+
+The second line shows how you use same line for several factors - by listing
+them delimited by commas. It's possible to list not only simple factors, but
+also their combinations like ``py26-sqlite,py27-sqlite``.
+
+Finally, factor expressions are expanded the same way as envlist, so last
+example could be rewritten as ``py{26,27}-sqlite``.
+
+.. note::
+
+ Factors don't do substring matching against env name, instead every
+ hyphenated expression is split by ``-`` and if ALL the factors in an
+ expression are also factors of an env then that condition is considered
+ hold.
+
+ For example, environment ``py26-mysql``:
+
+ - could be matched with expressions ``py26``, ``py26-mysql``,
+ ``mysql-py26``,
+ - but not with ``py2`` or ``py26-sql``.
+
Other Rules and notes
=====================
diff -r f6f6e21a6172a3ad3dd264746123f0660576dd69 -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf doc/example/basic.txt
--- a/doc/example/basic.txt
+++ b/doc/example/basic.txt
@@ -1,4 +1,3 @@
-
Basic usage
=============================================
@@ -13,6 +12,7 @@
[tox]
envlist = py26,py27
[testenv]
+ deps=pytest # or 'nose' or ...
commands=py.test # or 'nosetests' or ...
To sdist-package, install and test your project, you can
@@ -38,8 +38,10 @@
py31
py32
py33
+ py34
jython
pypy
+ pypy3
However, you can also create your own test environment names,
see some of the examples in :doc:`examples <../examples>`.
@@ -74,7 +76,7 @@
deps = -rrequirements.txt
-All installation commands are executed using ``{toxinidir}}``
+All installation commands are executed using ``{toxinidir}``
(the directory where ``tox.ini`` resides) as the current
working directory. Therefore, the underlying ``pip`` installation
will assume ``requirements.txt`` to exist at ``{toxinidir}/requirements.txt``.
@@ -175,6 +177,31 @@
from the ``subdir`` below the directory where your ``tox.ini``
file resides.
+special handling of PYTHONHASHSEED
+-------------------------------------------
+
+.. versionadded:: 1.6.2
+
+By default, Tox sets PYTHONHASHSEED_ for test commands to a random integer
+generated when ``tox`` is invoked. This mimics Python's hash randomization
+enabled by default starting `in Python 3.3`_. To aid in reproducing test
+failures, Tox displays the value of ``PYTHONHASHSEED`` in the test output.
+
+You can tell Tox to use an explicit hash seed value via the ``--hashseed``
+command-line option to ``tox``. You can also override the hash seed value
+per test environment in ``tox.ini`` as follows::
+
+ [testenv]
+ setenv =
+ PYTHONHASHSEED = 100
+
+If you wish to disable this feature, you can pass the command line option
+``--hashseed=noset`` when ``tox`` is invoked. You can also disable it from the
+``tox.ini`` by setting ``PYTHONHASHSEED = 0`` as described above.
+
+.. _`in Python 3.3`: http://docs.python.org/3/whatsnew/3.3.html#builtin-functions-and-types
+.. _PYTHONHASHSEED: http://docs.python.org/using/cmdline.html#envvar-PYTHONHASHSEED
+
Integration with setuptools/distribute test commands
----------------------------------------------------
@@ -186,6 +213,10 @@
import sys
class Tox(TestCommand):
+ user_options = [('tox-args=', 'a', "Arguments to pass to tox")]
+ def initialize_options(self):
+ TestCommand.initialize_options(self)
+ self.tox_args = None
def finalize_options(self):
TestCommand.finalize_options(self)
self.test_args = []
@@ -193,7 +224,8 @@
def run_tests(self):
#import here, cause outside the eggs aren't loaded
import tox
- errno = tox.cmdline(self.test_args)
+ import shlex
+ errno = tox.cmdline(args=shlex.split(self.tox_args))
sys.exit(errno)
setup(
@@ -206,5 +238,9 @@
python setup.py test
-this will install tox and then run tox.
+this will install tox and then run tox. You can pass arguments to ``tox``
+using the ``--tox-args`` or ``-a`` command-line options. For example::
+ python setup.py test -a "-epy27"
+
+is equivalent to running ``tox -epy27``.
diff -r f6f6e21a6172a3ad3dd264746123f0660576dd69 -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf doc/example/devenv.txt
--- 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.
-Configuration
--------------
+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``.
-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``::
+
+Example 1: Basic scenario
+=========================
+
+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
+
+In it we state:
+
+- 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 =
-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.
+Step 2 - Create the development environment
+-------------------------------------------
-
-Creating development environment
---------------------------------
-
-Once ``devenv`` section is defined we can instrument tox to create our
-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.
-Full configuration example
---------------------------
+Example 2: A more complex scenario
+==================================
-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.
+Let us say we want our project development environment to:
-Here is example configuration for that::
+- 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 an example configuration for the described scenario::
[testenv:devenv]
envdir = devenv
basepython = python2.7
usedevelop = True
deps = -rrequirements.txt
-
diff -r f6f6e21a6172a3ad3dd264746123f0660576dd69 -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf doc/example/jenkins.txt
--- a/doc/example/jenkins.txt
+++ b/doc/example/jenkins.txt
@@ -32,7 +32,9 @@
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 @@
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 @@
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 -r f6f6e21a6172a3ad3dd264746123f0660576dd69 -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf doc/example/pytest.txt
--- a/doc/example/pytest.txt
+++ b/doc/example/pytest.txt
@@ -23,7 +23,7 @@
deps=pytest # PYPI package providing py.test
commands=
py.test \
- [] # substitute with tox' positional arguments
+ {posargs} # substitute with tox' positional arguments
you can now invoke ``tox`` in the directory where your ``tox.ini`` resides.
``tox`` will sdist-package your project, create two virtualenv environments
@@ -49,7 +49,7 @@
commands=
py.test \
--basetemp={envtmpdir} \ # py.test tempdir setting
- [] # substitute with tox' positional arguments
+ {posargs} # substitute with tox' positional arguments
you can invoke ``tox`` in the directory where your ``tox.ini`` resides.
Differently than in the previous example the ``py.test`` command
@@ -73,7 +73,7 @@
--basetemp={envtmpdir} \
--confcutdir=.. \
-n 3 \ # use three sub processes
- []
+ {posargs}
.. _`listed as a known issue`:
@@ -99,16 +99,13 @@
files are outside the import path, then pass ``--pyargs mypkg`` to
pytest.
-Installed tests are particularly convenient when combined with
-`Distribute's 2to3 support` (``use_2to3``).
+With tests that won't be installed, the simplest way to run them
+against your installed package is to avoid ``__init__.py`` files in test
+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.
-With tests that won't be installed, the simplest way is to avoid
-``__init__.py`` files in test directories; pytest will still find them
-but they won't be copied to other places or be found by Python's import
-system.
+.. _`fully qualified name`: http://pytest.org/latest/goodpractises.html#test-package-name
-.. _`fully qualified name`: http://pytest.org/latest/goodpractises.html#package-name
-
-.. _`Distribute's 2to3 support`: http://packages.python.org/distribute/python3.html
.. include:: ../links.txt
diff -r f6f6e21a6172a3ad3dd264746123f0660576dd69 -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf doc/index.txt
--- a/doc/index.txt
+++ b/doc/index.txt
@@ -1,8 +1,6 @@
Welcome to the tox automation project
===============================================
-.. note:: second training: `professional testing with Python <http://www.python-academy.com/courses/specialtopics/python_course_testing.html>`_ , 25-27th November 2013, Leipzig.
-
vision: standardize testing in Python
---------------------------------------------
@@ -70,9 +68,8 @@
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 -r f6f6e21a6172a3ad3dd264746123f0660576dd69 -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf doc/install.txt
--- a/doc/install.txt
+++ b/doc/install.txt
@@ -4,7 +4,7 @@
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 -r f6f6e21a6172a3ad3dd264746123f0660576dd69 -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf setup.py
--- a/setup.py
+++ b/setup.py
@@ -18,17 +18,15 @@
def main():
version = sys.version_info[:2]
- install_requires = ['virtualenv>=1.9.1', 'py>=1.4.15', ]
- if version < (2, 7) or (3, 0) <= version <= (3, 1):
+ install_requires = ['virtualenv>=1.11.2', 'py>=1.4.17', ]
+ if version < (2, 7):
install_requires += ['argparse']
- if version < (2,6):
- install_requires += ["simplejson"]
setup(
name='tox',
description='virtualenv-based automation of test activities',
long_description=open("README.rst").read(),
url='http://tox.testrun.org/',
- version='1.6.2.dev1',
+ version='1.8.2.dev1',
license='http://opensource.org/licenses/MIT',
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
author='holger krekel',
@@ -40,7 +38,6 @@
tests_require=['tox'],
cmdclass={"test": Tox},
install_requires=install_requires,
- zip_safe=True,
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
diff -r f6f6e21a6172a3ad3dd264746123f0660576dd69 -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf tests/conftest.py
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,2 +1,2 @@
-from tox._pytestplugin import *
+from tox._pytestplugin import * # noqa
diff -r f6f6e21a6172a3ad3dd264746123f0660576dd69 -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf tests/test_config.py
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -1,11 +1,11 @@
-import tox
-import pytest
-import os, sys
-import subprocess
+import sys
from textwrap import dedent
import py
-from tox._config import *
+import pytest
+import tox
+import tox._config
+from tox._config import * # noqa
from tox._config import _split_env
@@ -61,6 +61,43 @@
envconfig = config.envconfigs['devenv']
assert envconfig.envdir == config.toxworkdir.join('foobar')
+ def test_force_dep_version(self, initproj):
+ """
+ Make sure we can override dependencies configured in tox.ini when using the command line option
+ --force-dep.
+ """
+ initproj("example123-0.5", filedefs={
+ 'tox.ini': '''
+ [tox]
+
+ [testenv]
+ deps=
+ dep1==1.0
+ dep2>=2.0
+ dep3
+ dep4==4.0
+ '''
+ })
+ config = parseconfig(
+ ['--force-dep=dep1==1.5', '--force-dep=dep2==2.1',
+ '--force-dep=dep3==3.0'])
+ assert config.option.force_dep== [
+ 'dep1==1.5', 'dep2==2.1', 'dep3==3.0']
+ assert [str(x) for x in config.envconfigs['python'].deps] == [
+ 'dep1==1.5', 'dep2==2.1', 'dep3==3.0', 'dep4==4.0',
+ ]
+
+ def test_is_same_dep(self):
+ """
+ Ensure correct parseini._is_same_dep is working with a few samples.
+ """
+ assert parseini._is_same_dep('pkg_hello-world3==1.0', 'pkg_hello-world3')
+ assert parseini._is_same_dep('pkg_hello-world3==1.0', 'pkg_hello-world3>=2.0')
+ assert parseini._is_same_dep('pkg_hello-world3==1.0', 'pkg_hello-world3>2.0')
+ assert parseini._is_same_dep('pkg_hello-world3==1.0', 'pkg_hello-world3<2.0')
+ assert parseini._is_same_dep('pkg_hello-world3==1.0', 'pkg_hello-world3<=2.0')
+ assert not parseini._is_same_dep('pkg_hello-world3==1.0', 'otherpkg>=2.0')
+
class TestConfigPackage:
def test_defaults(self, tmpdir, newconfig):
config = newconfig([], "")
@@ -69,10 +106,10 @@
envconfig = config.envconfigs['python']
assert envconfig.args_are_paths
assert not envconfig.recreate
+ assert not envconfig.pip_pre
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):
@@ -111,6 +148,20 @@
assert get_homedir() == "123"
+class TestGetcontextname:
+ def test_blank(self, monkeypatch):
+ monkeypatch.setattr(os, "environ", {})
+ assert getcontextname() is None
+
+ def test_jenkins(self, monkeypatch):
+ monkeypatch.setattr(os, "environ", {"JENKINS_URL": "xyz"})
+ assert getcontextname() == "jenkins"
+
+ def test_hudson_legacy(self, monkeypatch):
+ monkeypatch.setattr(os, "environ", {"HUDSON_URL": "xyz"})
+ assert getcontextname() == "jenkins"
+
+
class TestIniParser:
def test_getdefault_single(self, tmpdir, newconfig):
config = newconfig("""
@@ -130,6 +181,7 @@
key2={xyz}
""")
reader = IniReader(config._cfg, fallbacksections=['mydefault'])
+ assert reader is not None
py.test.raises(tox.exception.ConfigError,
'reader.getdefault("mydefault", "key2")')
@@ -204,6 +256,22 @@
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]
@@ -240,9 +308,19 @@
# "reader.getargvlist('section', 'key1')")
assert reader.getargvlist('section', 'key1') == []
x = reader.getargvlist("section", "key2")
- assert x == [["cmd1", "with space", "grr"],
+ assert x == [["cmd1", "with", "space", "grr"],
["cmd2", "grr"]]
+ def test_argvlist_windows_escaping(self, tmpdir, newconfig):
+ config = newconfig("""
+ [section]
+ comm = py.test {posargs}
+ """)
+ reader = IniReader(config._cfg)
+ reader.addsubstitutions([r"hello\this"])
+ argv = reader.getargv("section", "comm")
+ assert argv == ["py.test", "hello\\this"]
+
def test_argvlist_multiline(self, tmpdir, newconfig):
config = newconfig("""
[section]
@@ -256,7 +334,7 @@
# "reader.getargvlist('section', 'key1')")
assert reader.getargvlist('section', 'key1') == []
x = reader.getargvlist("section", "key2")
- assert x == [["cmd1", "with space", "grr"]]
+ assert x == [["cmd1", "with", "space", "grr"]]
def test_argvlist_quoting_in_command(self, tmpdir, newconfig):
@@ -298,7 +376,36 @@
assert argvlist[0] == ["cmd1"]
assert argvlist[1] == ["cmd2", "value2", "other"]
- def test_positional_arguments_are_only_replaced_when_standing_alone(self, tmpdir, newconfig):
+ def test_argvlist_quoted_posargs(self, tmpdir, newconfig):
+ config = newconfig("""
+ [section]
+ key2=
+ cmd1 --foo-args='{posargs}'
+ cmd2 -f '{posargs}'
+ cmd3 -f {posargs}
+ """)
+ reader = IniReader(config._cfg)
+ reader.addsubstitutions(["foo", "bar"])
+ assert reader.getargvlist('section', 'key1') == []
+ x = reader.getargvlist("section", "key2")
+ assert x == [["cmd1", "--foo-args=foo bar"],
+ ["cmd2", "-f", "foo bar"],
+ ["cmd3", "-f", "foo", "bar"]]
+
+ def test_argvlist_posargs_with_quotes(self, tmpdir, newconfig):
+ config = newconfig("""
+ [section]
+ key2=
+ cmd1 -f {posargs}
+ """)
+ reader = IniReader(config._cfg)
+ reader.addsubstitutions(["foo", "'bar", "baz'"])
+ assert reader.getargvlist('section', 'key1') == []
+ x = reader.getargvlist("section", "key2")
+ assert x == [["cmd1", "-f", "foo", "bar baz"]]
+
+ def test_positional_arguments_are_only_replaced_when_standing_alone(self,
+ tmpdir, newconfig):
config = newconfig("""
[section]
key=
@@ -394,7 +501,25 @@
assert envconfig.sitepackages == False
assert envconfig.develop == False
assert envconfig.envlogdir == envconfig.envdir.join("log")
- assert envconfig.setenv is None
+ assert list(envconfig.setenv.keys()) == ['PYTHONHASHSEED']
+ hashseed = envconfig.setenv['PYTHONHASHSEED']
+ assert isinstance(hashseed, str)
+ # The following line checks that hashseed parses to an integer.
+ int_hashseed = int(hashseed)
+ # hashseed is random by default, so we can't assert a specific value.
+ assert int_hashseed > 0
+
+ def test_sitepackages_switch(self, tmpdir, newconfig):
+ config = newconfig(["--sitepackages"], "")
+ envconfig = config.envconfigs['python']
+ assert envconfig.sitepackages == True
+
+ def test_installpkg_tops_develop(self, newconfig):
+ config = newconfig(["--installpkg=abc"], """
+ [testenv]
+ usedevelop = True
+ """)
+ assert not config.envconfigs["python"].develop
def test_specific_command_overrides(self, tmpdir, newconfig):
config = newconfig("""
@@ -444,7 +569,7 @@
envconfig = config.envconfigs['python']
assert envconfig.envpython == envconfig.envbindir.join("python")
- @pytest.mark.parametrize("bp", ["jython", "pypy"])
+ @pytest.mark.parametrize("bp", ["jython", "pypy", "pypy3"])
def test_envbindir_jython(self, tmpdir, newconfig, bp):
config = newconfig("""
[testenv]
@@ -484,36 +609,6 @@
assert envconfig.changedir.basename == "abc"
assert envconfig.changedir == config.setupdir.join("abc")
- def test_install_command_defaults_py25(self, newconfig, monkeypatch):
- from tox.interpreters import Interpreters
- def get_info(self, name):
- if "x25" in name:
- class I:
- runnable = True
- executable = "python2.5"
- version_info = (2,5)
- else:
- class I:
- runnable = False
- executable = "python"
- return I
- monkeypatch.setattr(Interpreters, "get_info", get_info)
- config = newconfig("""
- [testenv:x25]
- basepython = x25
- [testenv:py25-x]
- basepython = x25
- [testenv:py26]
- basepython = "python"
- """)
- for name in ("x25", "py25-x"):
- env = config.envconfigs[name]
- assert env.install_command == \
- "pip install {opts} {packages}".split()
- env = config.envconfigs["py26"]
- assert env.install_command == \
- "pip install --pre {opts} {packages}".split()
-
def test_install_command_setting(self, newconfig):
config = newconfig("""
[testenv]
@@ -540,6 +635,24 @@
'some_install', '--arg=%s/foo' % config.toxinidir, 'python',
'{opts}', '{packages}']
+ def test_pip_pre(self, newconfig):
+ config = newconfig("""
+ [testenv]
+ pip_pre=true
+ """)
+ envconfig = config.envconfigs['python']
+ assert envconfig.pip_pre
+
+ def test_pip_pre_cmdline_override(self, newconfig):
+ config = newconfig(
+ ['--pre'],
+ """
+ [testenv]
+ pip_pre=false
+ """)
+ envconfig = config.envconfigs['python']
+ assert envconfig.pip_pre
+
def test_downloadcache(self, newconfig, monkeypatch):
monkeypatch.delenv("PIP_DOWNLOAD_CACHE", raising=False)
config = newconfig("""
@@ -567,14 +680,14 @@
def test_simple(tmpdir, newconfig):
config = newconfig("""
- [testenv:py24]
- basepython=python2.4
- [testenv:py25]
- basepython=python2.5
+ [testenv:py26]
+ basepython=python2.6
+ [testenv:py27]
+ basepython=python2.7
""")
assert len(config.envconfigs) == 2
- assert "py24" in config.envconfigs
- assert "py25" in config.envconfigs
+ assert "py26" in config.envconfigs
+ assert "py27" in config.envconfigs
def test_substitution_error(tmpdir, newconfig):
py.test.raises(tox.exception.ConfigError, newconfig, """
@@ -626,6 +739,23 @@
assert argv[0] == ["cmd1", "[hello]", "world"]
assert argv[1] == ["cmd1", "brave", "new", "world"]
+ def test_posargs_backslashed_or_quoted(self, tmpdir, newconfig):
+ inisource = """
+ [testenv:py24]
+ commands =
+ echo "\{posargs\}" = {posargs}
+ echo "posargs = " "{posargs}"
+ """
+ conf = newconfig([], inisource).envconfigs['py24']
+ argv = conf.commands
+ assert argv[0] == ['echo', '\\{posargs\\}', '=']
+ assert argv[1] == ['echo', 'posargs = ', ""]
+
+ conf = newconfig(['dog', 'cat'], inisource).envconfigs['py24']
+ argv = conf.commands
+ assert argv[0] == ['echo', '\\{posargs\\}', '=', 'dog', 'cat']
+ assert argv[1] == ['echo', 'posargs = ', 'dog cat']
+
def test_rewrite_posargs(self, tmpdir, newconfig):
inisource = """
[testenv:py24]
@@ -752,6 +882,100 @@
assert conf.changedir.basename == 'testing'
assert conf.changedir.dirpath().realpath() == tmpdir.realpath()
+ def test_factors(self, newconfig):
+ inisource="""
+ [tox]
+ envlist = a-x,b
+
+ [testenv]
+ deps=
+ dep-all
+ a: dep-a
+ b: dep-b
+ x: dep-x
+ """
+ conf = newconfig([], inisource)
+ configs = conf.envconfigs
+ assert [dep.name for dep in configs['a-x'].deps] == \
+ ["dep-all", "dep-a", "dep-x"]
+ assert [dep.name for dep in configs['b'].deps] == ["dep-all", "dep-b"]
+
+ def test_factor_ops(self, newconfig):
+ inisource="""
+ [tox]
+ envlist = {a,b}-{x,y}
+
+ [testenv]
+ deps=
+ a,b: dep-a-or-b
+ a-x: dep-a-and-x
+ {a,b}-y: dep-ab-and-y
+ """
+ configs = newconfig([], inisource).envconfigs
+ get_deps = lambda env: [dep.name for dep in configs[env].deps]
+ assert get_deps("a-x") == ["dep-a-or-b", "dep-a-and-x"]
+ assert get_deps("a-y") == ["dep-a-or-b", "dep-ab-and-y"]
+ assert get_deps("b-x") == ["dep-a-or-b"]
+ assert get_deps("b-y") == ["dep-a-or-b", "dep-ab-and-y"]
+
+ def test_default_factors(self, newconfig):
+ inisource="""
+ [tox]
+ envlist = py{26,27,33,34}-dep
+
+ [testenv]
+ deps=
+ dep: dep
+ """
+ conf = newconfig([], inisource)
+ configs = conf.envconfigs
+ for name, config in configs.items():
+ assert config.basepython == 'python%s.%s' % (name[2], name[3])
+
+ @pytest.mark.issue188
+ def test_factors_in_boolean(self, newconfig):
+ inisource="""
+ [tox]
+ envlist = py{27,33}
+
+ [testenv]
+ recreate =
+ py27: True
+ """
+ configs = newconfig([], inisource).envconfigs
+ assert configs["py27"].recreate
+ assert not configs["py33"].recreate
+
+ @pytest.mark.issue190
+ def test_factors_in_setenv(self, newconfig):
+ inisource="""
+ [tox]
+ envlist = py27,py26
+
+ [testenv]
+ setenv =
+ py27: X = 1
+ """
+ configs = newconfig([], inisource).envconfigs
+ assert configs["py27"].setenv["X"] == "1"
+ assert "X" not in configs["py26"].setenv
+
+ def test_period_in_factor(self, newconfig):
+ inisource="""
+ [tox]
+ envlist = py27-{django1.6,django1.7}
+
+ [testenv]
+ deps =
+ django1.6: Django==1.6
+ django1.7: Django==1.7
+ """
+ configs = newconfig([], inisource).envconfigs
+ assert sorted(configs) == ["py27-django1.6", "py27-django1.7"]
+ assert [d.name for d in configs["py27-django1.6"].deps] \
+ == ["Django==1.6"]
+
+
class TestGlobalOptions:
def test_notest(self, newconfig):
config = newconfig([], "")
@@ -836,7 +1060,7 @@
assert str(env.basepython) == sys.executable
def test_default_environments(self, tmpdir, newconfig, monkeypatch):
- envs = "py24,py25,py26,py27,py31,py32,jython,pypy"
+ envs = "py26,py27,py31,py32,py33,py34,jython,pypy,pypy3"
inisource = """
[tox]
envlist = %s
@@ -848,13 +1072,30 @@
env = config.envconfigs[name]
if name == "jython":
assert env.basepython == "jython"
- elif name == "pypy":
- assert env.basepython == "pypy"
+ elif name.startswith("pypy"):
+ assert env.basepython == name
else:
assert name.startswith("py")
bp = "python%s.%s" %(name[2], name[3])
assert env.basepython == bp
+ def test_envlist_expansion(self, newconfig):
+ inisource = """
+ [tox]
+ envlist = py{26,27},docs
+ """
+ config = newconfig([], inisource)
+ assert config.envlist == ["py26", "py27", "docs"]
+
+ def test_envlist_cross_product(self, newconfig):
+ inisource = """
+ [tox]
+ envlist = py{26,27}-dep{1,2}
+ """
+ config = newconfig([], inisource)
+ assert config.envlist == \
+ ["py26-dep1", "py26-dep2", "py27-dep1", "py27-dep2"]
+
def test_minversion(self, tmpdir, newconfig, monkeypatch):
inisource = """
[tox]
@@ -863,6 +1104,22 @@
config = newconfig([], inisource)
assert config.minversion == "3.0"
+ def test_skip_missing_interpreters_true(self, tmpdir, newconfig, monkeypatch):
+ inisource = """
+ [tox]
+ skip_missing_interpreters = True
+ """
+ config = newconfig([], inisource)
+ assert config.option.skip_missing_interpreters
+
+ def test_skip_missing_interpreters_false(self, tmpdir, newconfig, monkeypatch):
+ inisource = """
+ [tox]
+ skip_missing_interpreters = False
+ """
+ config = newconfig([], inisource)
+ assert not config.option.skip_missing_interpreters
+
def test_defaultenv_commandline(self, tmpdir, newconfig, monkeypatch):
config = newconfig(["-epy24"], "")
env = config.envconfigs['py24']
@@ -881,6 +1138,123 @@
assert env.basepython == "python2.4"
assert env.commands == [['xyz']]
+class TestHashseedOption:
+
+ def _get_envconfigs(self, newconfig, args=None, tox_ini=None,
+ make_hashseed=None):
+ if args is None:
+ args = []
+ if tox_ini is None:
+ tox_ini = """
+ [testenv]
+ """
+ if make_hashseed is None:
+ make_hashseed = lambda: '123456789'
+ original_make_hashseed = tox._config.make_hashseed
+ tox._config.make_hashseed = make_hashseed
+ try:
+ config = newconfig(args, tox_ini)
+ finally:
+ tox._config.make_hashseed = original_make_hashseed
+ return config.envconfigs
+
+ def _get_envconfig(self, newconfig, args=None, tox_ini=None):
+ envconfigs = self._get_envconfigs(newconfig, args=args,
+ tox_ini=tox_ini)
+ return envconfigs["python"]
+
+ def _check_hashseed(self, envconfig, expected):
+ assert envconfig.setenv == {'PYTHONHASHSEED': expected}
+
+ def _check_testenv(self, newconfig, expected, args=None, tox_ini=None):
+ envconfig = self._get_envconfig(newconfig, args=args, tox_ini=tox_ini)
+ self._check_hashseed(envconfig, expected)
+
+ def test_default(self, tmpdir, newconfig):
+ self._check_testenv(newconfig, '123456789')
+
+ def test_passing_integer(self, tmpdir, newconfig):
+ args = ['--hashseed', '1']
+ self._check_testenv(newconfig, '1', args=args)
+
+ def test_passing_string(self, tmpdir, newconfig):
+ args = ['--hashseed', 'random']
+ self._check_testenv(newconfig, 'random', args=args)
+
+ def test_passing_empty_string(self, tmpdir, newconfig):
+ args = ['--hashseed', '']
+ self._check_testenv(newconfig, '', args=args)
+
+ @pytest.mark.xfail(sys.version_info >= (3,2),
+ reason="at least Debian python 3.2/3.3 have a bug: "
+ "http://bugs.python.org/issue11884")
+ def test_passing_no_argument(self, tmpdir, newconfig):
+ """Test that passing no arguments to --hashseed is not allowed."""
+ args = ['--hashseed']
+ try:
+ self._check_testenv(newconfig, '', args=args)
+ except SystemExit:
+ e = sys.exc_info()[1]
+ assert e.code == 2
+ return
+ assert False # getting here means we failed the test.
+
+ def test_setenv(self, tmpdir, newconfig):
+ """Check that setenv takes precedence."""
+ tox_ini = """
+ [testenv]
+ setenv =
+ PYTHONHASHSEED = 2
+ """
+ self._check_testenv(newconfig, '2', tox_ini=tox_ini)
+ args = ['--hashseed', '1']
+ self._check_testenv(newconfig, '2', args=args, tox_ini=tox_ini)
+
+ def test_noset(self, tmpdir, newconfig):
+ args = ['--hashseed', 'noset']
+ envconfig = self._get_envconfig(newconfig, args=args)
+ assert envconfig.setenv is None
+
+ def test_noset_with_setenv(self, tmpdir, newconfig):
+ tox_ini = """
+ [testenv]
+ setenv =
+ PYTHONHASHSEED = 2
+ """
+ args = ['--hashseed', 'noset']
+ self._check_testenv(newconfig, '2', args=args, tox_ini=tox_ini)
+
+ def test_one_random_hashseed(self, tmpdir, newconfig):
+ """Check that different testenvs use the same random seed."""
+ tox_ini = """
+ [testenv:hash1]
+ [testenv:hash2]
+ """
+ next_seed = [1000]
+ # This function is guaranteed to generate a different value each time.
+ def make_hashseed():
+ next_seed[0] += 1
+ return str(next_seed[0])
+ # Check that make_hashseed() works.
+ assert make_hashseed() == '1001'
+ envconfigs = self._get_envconfigs(newconfig, tox_ini=tox_ini,
+ make_hashseed=make_hashseed)
+ self._check_hashseed(envconfigs["hash1"], '1002')
+ # Check that hash2's value is not '1003', for example.
+ self._check_hashseed(envconfigs["hash2"], '1002')
+
+ def test_setenv_in_one_testenv(self, tmpdir, newconfig):
+ """Check using setenv in one of multiple testenvs."""
+ tox_ini = """
+ [testenv:hash1]
+ setenv =
+ PYTHONHASHSEED = 2
+ [testenv:hash2]
+ """
+ envconfigs = self._get_envconfigs(newconfig, tox_ini=tox_ini)
+ self._check_hashseed(envconfigs["hash1"], '2')
+ self._check_hashseed(envconfigs["hash2"], '123456789')
+
class TestIndexServer:
def test_indexserver(self, tmpdir, newconfig):
config = newconfig("""
@@ -1000,6 +1374,29 @@
"*ERROR*tox.ini*not*found*",
])
+ def test_showconfig_with_force_dep_version(self, cmd, initproj):
+ initproj('force_dep_version', filedefs={
+ 'tox.ini': '''
+ [tox]
+
+ [testenv]
+ deps=
+ dep1==2.3
+ dep2
+ ''',
+ })
+ result = cmd.run("tox", "--showconfig")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines([
+ r'*deps=*dep1==2.3, dep2*',
+ ])
+ # override dep1 specific version, and force version for dep2
+ result = cmd.run("tox", "--showconfig", "--force-dep=dep1",
+ "--force-dep=dep2==5.0")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines([
+ r'*deps=*dep1, dep2==5.0*',
+ ])
class TestArgumentParser:
@@ -1077,3 +1474,13 @@
p = CommandParser(cmd)
parsed = list(p.words())
assert parsed == ['nosetests', ' ', '-v', ' ', '-a', ' ', '!deferred', ' ', '--with-doctest', ' ', '[]']
+
+
+ @pytest.mark.skipif("sys.platform != 'win32'")
+ def test_commands_with_backslash(self, newconfig):
+ config = newconfig([r"hello\world"], """
+ [testenv:py26]
+ commands = some {posargs}
+ """)
+ envconfig = config.envconfigs["py26"]
+ assert envconfig.commands[0] == ["some", r"hello\world"]
diff -r f6f6e21a6172a3ad3dd264746123f0660576dd69 -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf tests/test_interpreters.py
--- a/tests/test_interpreters.py
+++ b/tests/test_interpreters.py
@@ -2,7 +2,7 @@
import os
import pytest
-from tox.interpreters import *
+from tox.interpreters import * # noqa
@pytest.fixture
def interpreters():
This diff is so big that we needed to truncate the remainder.
https://bitbucket.org/hpk42/tox/commits/a56d17ed9e34/
Changeset: a56d17ed9e34
Branch: echo-captured-output
User: fschulze
Date: 2015-02-05 13:21:44+00:00
Summary: Always tee the output to stdout when --report-json is used.
Affected #: 2 files
diff -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf -r a56d17ed9e345db64962bd856d521d4f92c5af65 tox/_cmdline.py
--- a/tox/_cmdline.py
+++ b/tox/_cmdline.py
@@ -80,21 +80,20 @@
return f
def popen(self, args, cwd=None, env=None, redirect=True, returnout=False):
- logged_command = "%s$ %s" %(cwd, " ".join(map(str, args)))
stdout = outpath = None
resultjson = self.session.config.option.resultjson
- resulttee = self.session.config.option.resulttee
if resultjson or redirect:
f = self._initlogpath(self.id)
f.write("actionid=%s\nmsg=%s\ncmdargs=%r\nenv=%s\n" %(
self.id, self.msg, args, env))
f.flush()
self.popen_outpath = outpath = py.path.local(f.name)
- stdout = f
+ if resultjson:
+ stdout = subprocess.PIPE
+ else:
+ stdout = f
elif returnout:
stdout = subprocess.PIPE
- if resultjson and resulttee:
- stdout = subprocess.PIPE
if cwd is None:
# XXX cwd = self.session.config.cwd
cwd = py.path.local()
@@ -113,15 +112,20 @@
try:
self.report.logpopen(popen, env=env)
try:
- if resultjson and resulttee:
+ if resultjson and not redirect:
assert popen.stderr is None # prevent deadlock
out = None
last_time = time.time()
while 1:
+ # we have to read one byte at a time, otherwise there
+ # might be no output for a long time with slow tests
data = popen.stdout.read(1)
if data:
sys.stdout.write(data)
if '\n' in data or (time.time() - last_time) > 5:
+ # we flush on newlines or after 5 seconds to
+ # provide quick enough feedback to the user
+ # when printing a dot per test
sys.stdout.flush()
last_time = time.time()
f.write(data)
diff -r 8b053b6f34d40c9d31ae9ac3f95736131b5648cf -r a56d17ed9e345db64962bd856d521d4f92c5af65 tox/_config.py
--- a/tox/_config.py
+++ b/tox/_config.py
@@ -117,9 +117,6 @@
"all commands and results involved. This will turn off "
"pass-through output from running test commands which is "
"instead captured into the json result file.")
- parser.add_argument("--result-tee", action="store_true",
- dest="resulttee",
- help="echo output of --result-json to stdout while it is captured.")
# We choose 1 to 4294967295 because it is the range of PYTHONHASHSEED.
parser.add_argument("--hashseed", action="store",
metavar="SEED", default=None,
https://bitbucket.org/hpk42/tox/commits/f52220770a97/
Changeset: f52220770a97
Branch: echo-captured-output
User: fschulze
Date: 2015-02-05 13:24:39+00:00
Summary: Add changelog entry.
Affected #: 1 file
diff -r a56d17ed9e345db64962bd856d521d4f92c5af65 -r f52220770a979458e126ac7fcc5fb2acefe1ec44 CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -10,6 +10,7 @@
- refine determination if we run from Jenkins, thanks Borge Lanes.
+- echo output to stdout when ``--report-json`` is used
1.8.1
-----------
https://bitbucket.org/hpk42/tox/commits/27b8bdcddd6a/
Changeset: 27b8bdcddd6a
User: hpk42
Date: 2015-02-09 11:13:56+00:00
Summary: Merged in fschulze/tox/echo-captured-output (pull request #132)
Add ``--result-tee`` option to echo output captured for the json result to stdout.
Affected #: 3 files
diff -r 50f3b98fc65b951ab07e6b5e3fbd5a2fc9c48f2c -r 27b8bdcddd6a197ad600afac03a1e3fb61cc6a5b CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -10,6 +10,7 @@
- refine determination if we run from Jenkins, thanks Borge Lanes.
+- echo output to stdout when ``--report-json`` is used
1.8.1
-----------
diff -r 50f3b98fc65b951ab07e6b5e3fbd5a2fc9c48f2c -r 27b8bdcddd6a197ad600afac03a1e3fb61cc6a5b tox/_cmdline.py
--- a/tox/_cmdline.py
+++ b/tox/_cmdline.py
@@ -11,6 +11,7 @@
import os
import sys
import subprocess
+import time
from tox._verlib import NormalizedVersion, IrrationalVersionError
from tox._venv import VirtualEnv
from tox._config import parseconfig
@@ -79,7 +80,7 @@
return f
def popen(self, args, cwd=None, env=None, redirect=True, returnout=False):
- f = outpath = None
+ stdout = outpath = None
resultjson = self.session.config.option.resultjson
if resultjson or redirect:
f = self._initlogpath(self.id)
@@ -87,14 +88,18 @@
self.id, self.msg, args, env))
f.flush()
self.popen_outpath = outpath = py.path.local(f.name)
+ if resultjson:
+ stdout = subprocess.PIPE
+ else:
+ stdout = f
elif returnout:
- f = subprocess.PIPE
+ stdout = subprocess.PIPE
if cwd is None:
# XXX cwd = self.session.config.cwd
cwd = py.path.local()
try:
popen = self._popen(args, cwd, env=env,
- stdout=f, stderr=STDOUT)
+ stdout=stdout, stderr=STDOUT)
except OSError as e:
self.report.error("invocation failed (errno %d), args: %s, cwd: %s" %
(e.errno, args, cwd))
@@ -107,7 +112,28 @@
try:
self.report.logpopen(popen, env=env)
try:
- out, err = popen.communicate()
+ if resultjson and not redirect:
+ assert popen.stderr is None # prevent deadlock
+ out = None
+ last_time = time.time()
+ while 1:
+ # we have to read one byte at a time, otherwise there
+ # might be no output for a long time with slow tests
+ data = popen.stdout.read(1)
+ if data:
+ sys.stdout.write(data)
+ if '\n' in data or (time.time() - last_time) > 5:
+ # we flush on newlines or after 5 seconds to
+ # provide quick enough feedback to the user
+ # when printing a dot per test
+ sys.stdout.flush()
+ last_time = time.time()
+ f.write(data)
+ elif popen.poll() is not None:
+ popen.stdout.close()
+ break
+ else:
+ out, err = popen.communicate()
except KeyboardInterrupt:
self.report.keyboard_interrupt()
popen.wait()
Repository URL: https://bitbucket.org/hpk42/tox/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
More information about the pytest-commit
mailing list