[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