[Python-modules-commits] [tox] 13/69: Import tox_1.5.0.orig.tar.gz
Barry Warsaw
barry at moszumanska.debian.org
Fri Sep 5 23:26:24 UTC 2014
This is an automated email from the git hooks/post-receive script.
barry pushed a commit to branch master
in repository tox.
commit fa3e42ce924fc77fe9f9e65bca75b955a1e5e17c
Author: Barry Warsaw <barry at python.org>
Date: Fri Sep 5 17:08:05 2014 -0400
Import tox_1.5.0.orig.tar.gz
---
.hg_archival.txt | 4 -
.hgignore | 16 --
.hgtags | 12 -
CHANGELOG | 75 +++++++
CONTRIBUTORS | 8 +-
ISSUES.txt | 75 +++++++
LICENSE | 24 +-
PKG-INFO | 52 +++++
README.txt => README.rst | 11 +-
doc/Makefile | 2 +-
doc/announce/release-1.4.3.txt | 97 ++++++++
doc/announce/release-1.4.txt | 2 +-
doc/conf.py | 4 +-
doc/config-v2.txt | 292 ++++++++++++++++++++++++
doc/config.txt | 29 ++-
doc/example/basic.txt | 17 ++
doc/example/jenkins.txt | 2 -
doc/example/pytest.txt | 28 ++-
doc/index.txt | 22 +-
doc/install.txt | 4 +-
doc/links.txt | 26 +--
doc/support.txt | 16 +-
setup.cfg | 5 +
setup.py | 58 ++---
tests/conftest.py | 289 +-----------------------
tests/test_config.py | 167 ++++++++++++--
tests/test_quickstart.py | 362 ++++++++++++++++++++++++++++++
tests/test_venv.py | 113 ++++++++--
tests/test_z_cmdline.py | 39 +++-
tox.egg-info/PKG-INFO | 52 +++++
tox.egg-info/SOURCES.txt | 57 +++++
tox.egg-info/dependency_links.txt | 1 +
tox.egg-info/entry_points.txt | 4 +
tox.egg-info/requires.txt | 2 +
tox.egg-info/top_level.txt | 1 +
tox.egg-info/zip-safe | 1 +
tox.ini | 17 +-
tox/__init__.py | 2 +-
tox/_cmdline.py | 84 ++++---
tox/_config.py | 216 ++++++++++++------
tests/conftest.py => tox/_pytestplugin.py | 26 +--
tox/_quickstart.py | 266 ++++++++++++++++++++++
tox/_venv.py | 141 +++++++-----
toxbootstrap.py | 9 +-
44 files changed, 2080 insertions(+), 650 deletions(-)
diff --git a/.hg_archival.txt b/.hg_archival.txt
deleted file mode 100644
index 82b89ce..0000000
--- a/.hg_archival.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-repo: bbc71705ea25a9ace4fd51f5a8d15abcaea7d684
-node: 668f66e4781b0beae509db8125dc02218f6efe4e
-branch: default
-tag: 1.4.2
diff --git a/.hgignore b/.hgignore
deleted file mode 100644
index cf24d86..0000000
--- a/.hgignore
+++ /dev/null
@@ -1,16 +0,0 @@
-
-syntax:glob
-*.pyc
-*.pyo
-*.swp
-*.html
-*.class
-*.orig
-*.rej
-*~
-
-build
-dist
-doc/_build/
-tox.egg-info
-.tox
diff --git a/.hgtags b/.hgtags
deleted file mode 100644
index 10f5e4a..0000000
--- a/.hgtags
+++ /dev/null
@@ -1,12 +0,0 @@
-4de489705645915e7f6147e60e64eac7750e48d9 0.5
-cdc2ffbb162ac897045542b73bd5b3c6f3e53290 0.6
-14c629f52d957273514b17bb8f774ed27c03e9cf 0.8
-e1ffa42a5513704fbd4b3e20fea549acff228d90 0.9
-53885d6bf89e044323cc0ffdb8658124c644d337 1.0
-53885d6bf89e044323cc0ffdb8658124c644d337 1.0
-b4db03a77e9e6f4c7e875c40886de9ceaa6baceb 1.0
-3f52ff320a6e5e1f9a14b5c9ebbf6bc521ef59b6 1.1
-e68d4bfd1af32feae87bf01db11792ce0d634975 1.2
-3ee108c91b2fdcd984026c8c488da846377a6a9c 1.3
-d5dbb01fc0eb73f7eca74842d1ce02554552b0a3 1.4
-3a68408fa3a210c2a2e52189e16f5ae6dfed287d 1.4.1
diff --git a/CHANGELOG b/CHANGELOG
old mode 100755
new mode 100644
index b703b8e..3cce0bf
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,78 @@
+1.5.0
+-----------------
+
+- fix issue104: use setuptools by default, instead of distribute,
+ now that setuptools has distribute merged.
+
+- make sure test commands are searched first in the virtualenv
+
+- re-fix issue2 - add whitelist_externals to be used in ``[testenv*]``
+ sections, allowing to avoid warnings for commands such as ``make``,
+ used from the commands value.
+
+- fix issue97 - allow substitutions to reference from other sections
+ (thanks Krisztian Fekete)
+
+- fix issue92 - fix {envsitepackagesdir} to actually work again
+
+- show (test) command that is being executed, thanks
+ Lukasz Balcerzak
+
+- re-license tox to MIT license
+
+- depend on virtualenv-1.9.1
+
+- rename README.txt to README.rst to make bitbucket happier
+
+
+1.4.3
+-----------------
+
+- use pip-script.py instead of pip.exe on win32 to avoid the lock exe
+ file on execution issue (thanks Philip Thiem)
+
+- introduce -l|--listenv option to list configured environments
+ (thanks Lukasz Balcerzak)
+
+- fix downloadcache determination to work according to docs: Only
+ make pip use a download cache if PIP_DOWNLOAD_CACHE or a
+ downloadcache=PATH testenv setting is present. (The ENV setting
+ takes precedence)
+
+- fix issue84 - pypy on windows creates a bin not a scripts venv directory
+ (thanks Lukasz Balcerzak)
+
+- experimentally introduce --installpkg=PATH option to install a package
+ rather than create/install an sdist package. This will still require
+ and use tox.ini and tests from the current working dir (and not from the
+ remote package).
+
+- substitute {envsitepackagesdir} with the package installation
+ directory (closes #72) (thanks g2p)
+
+- issue #70 remove PYTHONDONTWRITEBYTECODE workaround now that
+ virtualenv behaves properly (thanks g2p)
+
+- merged tox-quickstart command, contributed by Marc Abramowitz, which
+ generates a default tox.ini after asking a few questions
+
+- fix #48 - win32 detection of pypy and other interpreters that are on PATH
+ (thanks Gustavo Picon)
+
+- fix grouping of index servers, it is now done by name instead of
+ indexserver url, allowing to use it to separate dependencies
+ into groups even if using the same default indexserver.
+
+- look for "tox.ini" files in parent dirs of current dir (closes #34)
+
+- the "py" environment now by default uses the current interpreter
+ (sys.executable) make tox' own setup.py test execute tests with it
+ (closes #46)
+
+- change tests to not rely on os.path.expanduser (closes #60),
+ also make mock session return args[1:] for more precise checking (closes #61)
+ thanks to Barry Warsaw for both.
+
1.4.2
-----------------
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 74a1047..a6f2d71 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -1,6 +1,12 @@
-contributions (under the MIT license):
+contributions:
+Krisztian Fekete
+Marc Abramowitz
Sridhar Ratnakumar
+Barry Warsaw
Chris Rose
Jannis Leidl
+Ronny Pfannschmidt
+Lukasz Balcerzak
+Philip Thiem
diff --git a/ISSUES.txt b/ISSUES.txt
index 08e89b5..4b05dff 100644
--- a/ISSUES.txt
+++ b/ISSUES.txt
@@ -1,3 +1,76 @@
+introduce package and setup.py less sdist creation
+-----------------------------------------------------------
+
+Example sections for tox itself::
+
+ [pkgdef]
+ basename = pytest
+ description = virtualenv-based automation of test activities
+ authors = holger krekel <holger at merlinux.eu>
+ url = http://tox.testrun.org
+ entry_points = console_scripts: tox=tox:cmdline
+ requires = py
+ packages =
+ preuploadenvs = sphinx
+
+ classifiers=
+ Development Status :: 6 - Mature
+ Intended Audience :: Developers
+ License :: OSI Approved :: MIT License
+ Topic :: Software Development :: Testing
+ Topic :: Software Development :: Libraries
+ Topic :: Utilities
+
+This would generate three different packages:
+
+- the main one containing the app with the specified description, etc.
+ It has a test-requires pointing to the test package,
+ which classifiers
+- the test package only containing the tests and setup.py
+ depending on the main package and all requirements collected from the
+ testenv
+- the doc package containing generated html and txt files
+ (to be installable via a setup.py as well?)
+
+Here is what happens when tox is invoked now:
+
+- version gets auto-incremented (in setup.py and $PACKAGE/__init__.py files)
+- main setup.py generated, files copied, sdist generated
+- test setup.py generated, files copied, sdist generated
+- doc setup.py generated, doc environment run, files copied, sdist generated
+
+- if --upload is specified, all packages are uploaded under
+ their respective names:
+ tox-VER
+ tests-tox-VER
+ docs-tox-VER
+
+- tox --sync creates a test result file for the tests-tox-VER run
+ and uploads it to testrun.org -- .tox/projectkeys contains a file that
+ was created by visiting testrun.org and registering/logging in.
+
+- download toxslave and execute it:
+
+ toxslave --userkey=... [--project tox]
+
+ which will query testrun.org for outstanding testruns
+ [for the tox project], download packages, execute them
+ and report back to the server
+
+merge tox and detox?
+----------------------------------------------------------------
+
+maybe it's time to merge it?
+
+http://lists.idyll.org/pipermail/testing-in-python/2012-October/005205.html
+pyc files / test distributions
+-----------------------------------------------------------
+
+investigate pyc cleaning, see
+
+http://lists.idyll.org/pipermail/testing-in-python/2012-October/005205.html
+
+
allow config overlays
-----------------------------------------------------------
@@ -56,6 +129,8 @@ look into ways to support integration of tox with travis.
- generate .travis.yml from tox.ini
- generate tox.ini from .travis.yml
+For the last two, take a look at http://pypi.python.org/pypi/panci/
+
allow user-specific python interpreters
------------------------------------------------
diff --git a/LICENSE b/LICENSE
index 9246a20..97aff5c 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,10 +1,20 @@
-The execnet package is released under the provisions of the Gnu Public
-License (GPL), version 2 or later.
-See http://www.fsf.org/licensing/licenses/ for more information.
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
-If you have questions and/or want to use parts of
-the code under a different license than the GPL
-please contact me.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-holger krekel, May 2011, holger at merlinux eu
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..3335f00
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,52 @@
+Metadata-Version: 1.1
+Name: tox
+Version: 1.5.0
+Summary: virtualenv-based automation of test activities
+Home-page: http://tox.testrun.org/
+Author: holger krekel
+Author-email: holger at merlinux.eu
+License: http://opensource.org/licenses/MIT
+Description:
+ What is Tox?
+ --------------------
+
+ Tox as is a generic virtualenv_ management and test command line tool you can use for:
+
+ * checking your package installs correctly with different Python versions and
+ interpreters
+
+ * running your tests in each of the environments, configuring your test tool of choice
+
+ * acting as a frontend to Continuous Integration servers, greatly
+ reducing boilerplate and merging CI and shell-based testing.
+
+ For more information and the repository please checkout:
+
+ - homepage: http://tox.testrun.org
+
+ - repository: https://bitbucket.org/hpk42/tox
+
+ have fun,
+
+
+ have fun,
+
+ holger krekel, May 2013
+
+
+Platform: unix
+Platform: linux
+Platform: osx
+Platform: cygwin
+Platform: win32
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Topic :: Software Development :: Testing
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: Utilities
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
diff --git a/README.txt b/README.rst
similarity index 72%
rename from README.txt
rename to README.rst
index ea017dc..f53cef3 100644
--- a/README.txt
+++ b/README.rst
@@ -12,11 +12,16 @@ Tox as is a generic virtualenv_ management and test command line tool you can us
* acting as a frontend to Continuous Integration servers, greatly
reducing boilerplate and merging CI and shell-based testing.
-For more information, docs and many examples please checkout:
+For more information and the repository please checkout:
+
+- homepage: http://tox.testrun.org
+
+- repository: https://bitbucket.org/hpk42/tox
+
+have fun,
- http://tox.testrun.org
have fun,
-holger krekel, May 2012
+holger krekel, May 2013
diff --git a/doc/Makefile b/doc/Makefile
index 1142b1c..77b2083 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -37,7 +37,7 @@ clean:
-rm -rf $(BUILDDIR)/*
install: clean html
- @rsync -avz $(BUILDDIR)/html/ testrun.org:/www/tox.testrun.org/latest
+ @rsync -avz $(BUILDDIR)/html/ testrun.org:/www/testrun.org/tox/latest
#latexpdf
#@scp $(BUILDDIR)/latex/*.pdf testrun.org:www-tox/latest
diff --git a/doc/announce/release-1.4.3.txt b/doc/announce/release-1.4.3.txt
new file mode 100644
index 0000000..ca0bb19
--- /dev/null
+++ b/doc/announce/release-1.4.3.txt
@@ -0,0 +1,97 @@
+tox 1.4.3: the Python virtualenv-based testing automatizer
+=============================================================================
+
+tox 1.4.3 fixes some bugs and introduces a new script and two new options:
+
+- "tox-quickstart" - run this script, answer a few questions, and
+ get a tox.ini created for you (thanks Marc Abramowitz)
+
+- "tox -l" lists configured environment names (thanks Lukasz Balcerzak)
+
+- (experimental) "--installpkg=localpath" option which will skip the
+ sdist-creation of a package and instead install the given localpath package.
+
+- use pip-script.py instead of pip.exe on win32 to avoid windows locking
+ the .exe
+
+Note that the sister project "detox" should continue to work - it's a
+separately released project which drives tox test runs on multiple CPUs
+in parallel.
+
+More documentation:
+
+ http://tox.testrun.org/
+
+Installation:
+
+ pip install -U tox
+
+repository hosting and issue tracking on bitbucket:
+
+ https://bitbucket.org/hpk42/tox
+
+
+What is tox?
+----------------
+
+tox standardizes and automates tedious python driven 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
+
+
+CHANGELOG
+================
+
+1.4.3 (compared to 1.4.2)
+--------------------------------
+
+- introduce -l|--listenv option to list configured environments
+ (thanks Lukasz Balcerzak)
+
+- fix downloadcache determination to work according to docs: Only
+ make pip use a download cache if PIP_DOWNLOAD_CACHE or a
+ downloadcache=PATH testenv setting is present. (The ENV setting
+ takes precedence)
+
+- fix issue84 - pypy on windows creates a bin not a scripts venv directory
+ (thanks Lukasz Balcerzak)
+
+- experimentally introduce --installpkg=PATH option to install a package rather than
+ create/install an sdist package. This will still require and use
+ tox.ini and tests from the current working dir (and not from the remote
+ package).
+
+- substitute {envsitepackagesdir} with the package installation directory (closes #72)
+ (thanks g2p)
+
+- issue #70 remove PYTHONDONTWRITEBYTECODE workaround now that
+ virtualenv behaves properly (thanks g2p)
+
+- merged tox-quickstart command, contributed by Marc Abramowitz, which
+ generates a default tox.ini after asking a few questions
+
+- fix #48 - win32 detection of pypy and other interpreters that are on PATH
+ (thanks Gustavo Picon)
+
+- fix grouping of index servers, it is now done by name instead of
+ indexserver url, allowing to use it to separate dependencies
+ into groups even if using the same default indexserver.
+
+- look for "tox.ini" files in parent dirs of current dir (closes #34)
+
+- the "py" environment now by default uses the current interpreter
+ (sys.executable) make tox' own setup.py test execute tests with it
+ (closes #46)
+
+- change tests to not rely on os.path.expanduser (closes #60),
+ also make mock session return args[1:] for more precise checking (closes #61)
+ thanks to Barry Warszaw for both.
+
diff --git a/doc/announce/release-1.4.txt b/doc/announce/release-1.4.txt
index 3863c2a..5565bb3 100644
--- a/doc/announce/release-1.4.txt
+++ b/doc/announce/release-1.4.txt
@@ -4,7 +4,7 @@ tox 1.4: the virtualenv-based test run automatizer
I am happy to announce tox 1.4 which brings:
- improvements with configuration file syntax, now allowing re-using
- selected settings across config file sections. see http://bit.ly/Ly3K4f
+ selected settings across config file sections. see http://testrun.org/tox/latest/config.html#substition-for-values-from-other-sections
- terminal reporting was simplified and streamlined. Now with
verbosity==0 (the default), less information will be shown
diff --git a/doc/conf.py b/doc/conf.py
index ac558b4..f643b48 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -41,14 +41,14 @@ master_doc = 'index'
# General information about the project.
project = u'tox'
-copyright = u'2011, holger krekel and others'
+copyright = u'2013, holger krekel and others'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
-release = version = "1.4-3"
+release = version = "1.5.0"
# The full version, including alpha/beta/rc tags.
# The language for content autogenerated by Sphinx. Refer to documentation
diff --git a/doc/config-v2.txt b/doc/config-v2.txt
new file mode 100644
index 0000000..cf4cf3b
--- /dev/null
+++ b/doc/config-v2.txt
@@ -0,0 +1,292 @@
+V2: new tox multi-dimensional, platform-specific configuration
+--------------------------------------------------------------------
+
+.. note::
+
+ This is a draft document sketching a to-be-done implementation.
+ It does not fully specify each change yet but should give a good
+ idea of where things are heading. For feedback, mail the
+ testing-in-python mailing list or open a pull request on
+ https://bitbucket.org/hpk42/tox/src/84d8cf3c2a95fefd874f22c8b2d257e94365472f/doc/config-v2.txt?at=default
+
+**Abstract**: Adding multi-dimensional configuration, platform-specification
+and multiple installers to tox.ini.
+
+**Target audience**: Developers using or wanting to use tox for testing
+their python projects.
+
+Issues with current tox (1.4) configuration
+------------------------------------------------
+
+Tox is used as a tool for creating and managing virtualenv environments
+and running tests in them. As of tox-1.4 there are some issues frequently
+coming up with its configuration language:
+
+- there is no way to instruct tox to parametrize testenv specifications
+ other than to list all combinations by specifying a ``[testenv:...]``
+ section for each combination. Examples of real life situations
+ arising from this:
+
+ * http://code.larlet.fr/django-rest-framework/src/eed0f39a7e45/tox.ini
+
+ * https://bitbucket.org/tabo/django-treebeard/src/93b579395a9c/tox.ini
+
+- there is no way to have platform specific settings other than to
+ define specific testenvs and invoke tox with a platform-specific
+ testenv list.
+
+- there is no way to specify the platforms against which a project
+ shall successfully run.
+
+- tox always uses pip for installing packages currently. This has
+ several issues:
+
+ - no way to check if installing via easy_install works
+ - no installs of packages with compiled c-extensions (win32 standard)
+
+
+Goals, resolving those issues
+------------------------------------
+
+This document discusses a possible solution for each of these issues,
+namely these goals:
+
+- allow to more easily define and run dependency/interpreter variants
+ with testenvs
+- allow platform-specific settings
+- allow to specify platforms against which tests should run
+- allow to run installer-variants (easy_install or pip, xxx)
+- try to mimick/re-use bash-style syntax to ease learning curve.
+
+
+Example: Generating and selecting variants
+----------------------------------------------
+
+Suppose you want to test your package against python2.6, python2.7 and on the
+windows and linux platforms. Today you would have to
+write down 2*2 = 4 ``[testenv:*]`` sections and then instruct
+tox to run a specific list of environments on each platform.
+
+With tox-1.X you can directlys specify combinations::
+
+ # combination syntax gives 2 * 2 = 4 testenv names
+ #
+ envlist = {py26,py27}-{win,linux}
+
+ [testenv]
+ deps = pytest
+ platform =
+ win: windows
+ linux: linux
+ basepython =
+ py26: python2.6
+ py27: python2.7
+ commands = py.test
+
+Let's go through this step by step::
+
+ envlist = {py26,py27}-{windows,linux}
+
+This is bash-style syntax and will create ``2*2=4`` environment names
+like this::
+
+ py26-windows
+ py26-linux
+ py27-windows
+ py27-linux
+
+Our ``[testenv]`` uses a new templating style for the ``platform`` definition::
+
+ platform=
+ windows: windows
+ linux: linux
+
+These two conditional settings will lead to either ``windows`` or
+``linux`` as the platform string. When the test environment is run,
+its platform string needs to be contained in the string returned
+from ``platform.platform()``. Otherwise the environment will be skipped.
+
+The next configuration item in the ``testenv`` section deals with
+the python interpreter::
+
+ basepython =
+ py26: python2.6
+ py27: python2.7
+
+This defines a python executable, depending on if ``py26`` or ``py27``
+appears in the environment name.
+
+The last config item is simply the invocation of the test runner::
+
+ commands = py.test
+
+Nothing special here :)
+
+.. note::
+
+ Tox provides good defaults for platform and basepython
+ settings, so the above ini-file can be further reduced::
+
+ [tox]
+ envlist = {py26,py27}-{win,linux}
+
+ [testenv]
+ deps = pytest
+ commands = py.test
+
+ Voila, this multi-dimensional ``tox.ini`` configuration
+ defines 2*2=4 environments.
+
+
+The new "platform" setting
+--------------------------------------
+
+A testenv can define a new ``platform`` setting. If its value
+is not contained in the string obtained from calling
+``platform.platform()`` the environment will be skipped.
+
+Expanding the ``envlist`` setting
+----------------------------------------------------------
+
+The new ``envlist`` setting allows to use ``{}`` bash-style
+expressions. XXX explanation or pointer to bash-docs
+
+Templating based on environments names
+-------------------------------------------------
+
+For a given environment name, all lines in a testenv section which
+start with "NAME: ..." will be checked for being part in the environment
+name. If they are part of it, the remainder will be the new line.
+If they are not part of it, the whole line will be left out.
+Parts of an environment name are obtained by ``-``-splitting it.
+
+Variant specification with [variant:VARNAME]
+
+Showing all expanded sections
+-------------------------------
+
+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 [XXX output ommitted for now]
+
+Making sure your packages installs with easy_install
+------------------------------------------------------
+
+The new "installer" testenv setting allows to specify the tool for
+installation in a given test environment::
+
+ [testenv]
+ installer =
+ easy: easy_install
+ pip: pip
+
+If you want to have your package installed with both easy_install
+and pip, you can list them in your envlist likes this::
+
+ [tox]
+ envlist = py[26,27,32]-django[13,14]-[easy,pip]
+
+If no installer is specified, ``pip`` will be used.
+
+Default settings related to environments names/variants
+---------------------------------------------------------------
+
+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
+ pythonNN or PyPy interpreter
+* ``{win32,linux,darwin}`` defines the according ``platform``.
+
+You can use those in your “envlist” specification
+without the need to define them yourself.
+
+
+Use more bash-style syntax
+--------------------------------------
+
+tox leverages bash-style syntax if you specify mintoxversion = 1.4:
+
+- $VARNAME or ${...} syntax instead of the older {} substitution.
+- XXX go through config.txt and see how it would need to be changed
+
+
+Transforming the examples: django-rest
+------------------------------------------------
+
+The original `django-rest-framework tox.ini
+<http://code.larlet.fr/django-rest-framework/src/eed0f39a7e45/tox.ini>`_
+file has 159 lines and a lot of repetition, the new one would +have 20
+lines and almost no repetition::
+
+ [tox]
+ envlist = {py25,py26,py27}-{django12,django13}{,-example}
+
+ [testenv]
+ deps=
+ coverage==3.4
+ unittest-xml-reporting==1.2
+ Pyyaml==3.10
+ django12: django==1.2.4
+ django13: django==1.3.1
+ # some more deps for running examples
+ example: wsgiref==0.1.2
+ example: Pygments==1.4
+ example: httplib2==0.6.0
+ example: Markdown==2.0.3
+
+ commands =
+ !example: python setup.py test
+ example: python examples/runtests.py
+
+
+Note that ``{,-example}`` in the envlist denotes two values, an empty
+one and a ``example`` one. The empty value means that there are no specific
+settings and thus no need to define a variant name.
+
+Transforming the examples: django-treebeard
+------------------------------------------------
+
+Another `tox.ini
+<https://bitbucket.org/tabo/django-treebeard/raw/93b579395a9c/tox.ini>`_
+has 233 lines and runs tests against multiple Postgres and Mysql
+engines. It also performs backend-specific test commands, passing
+different command line options to the test script. With the new tox-1.X
+we not only can do the same with 32 non-repetive configuration lines but
+we also produce 36 specific testenvs with specific dependencies and test
+commands::
+
+ [tox]
+ envlist =
+ {py24,py25,py26,py27}-{django11,django12,django13}-{nodb,pg,mysql}, docs
+
+ [testenv:docs]
+ changedir = docs
+ deps =
+ Sphinx
+ Django
+ commands =
+ make clean
+ make html
+
+ [testenv]
+ deps=
+ coverage
+ pysqlite
+ django11: django==1.1.4
+ django12: django==1.2.7
+ django13: django==1.3.1
+ django14: django==1.4
+ nodb: pysqlite
+ pg: psycopg2
+ mysql: MySQL-python
+
+ commands =
+ nodb: {envpython} runtests.py {posargs}
+ pg: {envpython} runtests.py {posargs} \
+ --DATABASE_ENGINE=postgresql_psycopg2 \
+ --DATABASE_USER=postgres {posargs}
+ mysql: {envpython} runtests.py --DATABASE_ENGINE=mysql \
+ --DATABASE_USER=root {posargs}
+
diff --git a/doc/config.txt b/doc/config.txt
index 02511de..0964146 100644
--- a/doc/config.txt
+++ b/doc/config.txt
@@ -35,7 +35,7 @@ and will first lookup global tox settings in this section::
envlist setting
+++++++++++++++++++++++++
-Determining the environment list that ``tox`` is to operate one
+Determining the environment list that ``tox`` is to operate on
happens in this order:
* command line option ``-eENVLIST``
@@ -75,6 +75,16 @@ Complete list of settings that you can put into ``testenv*`` sections:
For eventually performing a call to ``subprocess.Popen(args, ...)``
``args`` are determined by splitting the whole command by whitespace.
+.. 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
+ 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
+ ``whitelist_externals=*`` which will match all commands (not recommended).
+
.. confval:: changedir=path
change to this working directory when executing the test command.
@@ -106,17 +116,21 @@ Complete list of settings that you can put into ``testenv*`` sections:
.. confval:: downloadcache=path
- (pip only) use this directory for caching downloads - this defaults to the
- environment variable ``PIP_DOWNLOAD_CACHE`` if it is set.
+ (pip only) use this directory for caching downloads. This value
+ is overriden by the environment variable ``PIP_DOWNLOAD_CACHE``
+ if it exists.
**default**: no download cache will be used.
**note**: if creating multiple environments use of a download cache greatly
speeds up the testing process.
.. confval:: distribute=True|False
- Set to ``False`` if you want to use setuptools_ instead of the default
- distribute_ in the virtual environment.
- **default:** True.
+ Set to ``True`` if you want to use distribute_ instead of the default
+ setuptools_ in the virtual environment. Prior to tox-1.5 the
+ default was True and now is False, meaning ``setuptools`` is used
+ (note that setuptools-0.7 merged with distribute). In future versions
+ of tox this option might be ignored and setuptools always chosen.
+ **default:** False.
.. confval:: sitepackages=True|False
@@ -199,6 +213,9 @@ substitutions for virtualenv-related sections
directory of the virtualenv hierarchy
``{envbindir}``
directory where executables are located
+``{envsitepackagesdir}``
+ directory where packages are installed.
+ Note that architecture-specific files may appear in a different directory.
``{envtmpdir}``
the environment temporary directory
``{envlogdir}``
diff --git a/doc/example/basic.txt b/doc/example/basic.txt
index 3276ab1..031c4f9 100644
--- a/doc/example/basic.txt
+++ b/doc/example/basic.txt
@@ -37,12 +37,29 @@ Available "default" test environments names are::
py30
py31
py32
+ py33
jython
pypy
However, you can also create your own test environment names,
see some of the examples in :doc:`examples <../examples>`.
+whitelisting a non-virtualenv commands
+-----------------------------------------------
+
+.. versionadded:: 1.5
+
+Sometimes you may want to use tools not contained in your
+virtualenv such as ``make``, ``bash`` or others. To avoid
+warnings you can use the ``whitelist_externals`` testenv
+configuration::
+
+ # content of tox.ini
+ [testenv]
+ whitelist_externals = make
+ /bin/bash
+
+
.. _virtualenv: http://pypi.python.org/pypi/virtualenv
.. _multiindex:
diff --git a/doc/example/jenkins.txt b/doc/example/jenkins.txt
index 2ec67ec..262aa15 100644
--- a/doc/example/jenkins.txt
+++ b/doc/example/jenkins.txt
@@ -34,9 +34,7 @@ for example with ``py.test`` it is done like this:
commands=py.test --junitxml=junit-{envname}.xml
-See a real-life example in action with the `pytest Jenkins job`_
-.. _`pytest Jenkins job`: http://hudson.testrun.org/view/pytest/job/pytest/
**zero-installation** for slaves
-------------------------------------------------------------
diff --git a/doc/example/pytest.txt b/doc/example/pytest.txt
old mode 100755
new mode 100644
index 59e5147..98fa077
--- a/doc/example/pytest.txt
+++ b/doc/example/pytest.txt
@@ -85,16 +85,30 @@ created files in your py.test run. Try to not use the "--basetemp" parameter.
**installed-versus-checkout version**. ``py.test`` collects test
modules on the filesystem and then tries to import them under their
-`fully qualified name`_. This means that if your test directory contains
-an ``__init__.py`` file then your ``py.test`` invocation may end up
+`fully qualified name`_. This means that if your test files are
+importable from somewhere then your ``py.test`` invocation may end up
importing the package from the checkout directory rather than the
-installed package. Therefore it is better to try to avoid
-``__init__.py`` files in test directories and also try to avoid custom
-``PYTHONPATH`` settings. After all, it is the job of your ``setup.py``
-file and the install tools to care for making the package properly
-available for importing.
+installed package.
+
+There are a few ways to prevent this.
+
+With installed tests (the tests packages are known to ``setup.py``), a
+safe and explicit option is to give the explicit path
+``{envsitepackagesdir}/mypkg`` to pytest.
+Alternatively, it is possible to use ``changedir`` so that checked-out
+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 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#package-name
+.. _`Distribute's 2to3 support`: http://packages.python.org/distribute/python3.html
.. include:: ../links.txt
diff --git a/doc/index.txt b/doc/index.txt
index f8ecf34..20d65d4 100644
--- a/doc/index.txt
+++ b/doc/index.txt
@@ -1,12 +1,14 @@
Welcome to the tox automation project
===============================================
-vision: merge packaging, testing and release
+.. note:: Upcoming: `professional testing with pytest and tox <http://www.python-academy.com/courses/specialtopics/python_course_testing.html>`_ , 24th-26th June 2013, Leipzig.
+
+vision: standardize testing in Python
---------------------------------------------
-``tox`` aims to automate state-of-the-art packaging, testing and
-releasing of Python software right from your console *or* CI
-server, invoking your tools of choice.
+``tox`` aims to automate and standardize testing in Python. It is part
+of a larger vision of easing the packaging, testing and release process
+of Python software.
What is Tox?
--------------------
@@ -36,6 +38,9 @@ right next to your ``setup.py`` file::
deps=pytest # install pytest in the venvs
commands=py.test # or 'nosetests' or ...
+You can also try generating a ``tox.ini`` file automatically, by running
+``tox-quickstart`` and then answering a few simple questions.
+
To sdist-package, install and test your project against Python2.6 and Python2.7, just type::
tox
@@ -61,10 +66,10 @@ Current features
* supports :ref:`using different / multiple PyPI index servers <multiindex>`
-* uses pip_ (for Python2 environments) and distribute_ (for all environments) by default
+* uses pip_ and distribute_ by default.
-* **cross-Python compatible**: Python-2.5 up to Python-3.2, Jython and
- Python3 support as well as for pypy_
+* **cross-Python compatible**: Python-2.5 up to Python-3.3, Jython and pypy_
+ support.
* **cross-platform**: Windows and Unix style environments
@@ -93,7 +98,7 @@ Current features
install
examples
config
- config-v1
+ config-v2
support
changelog
links
@@ -103,6 +108,7 @@ Current features
announce/release-1.2
announce/release-1.3
announce/release-1.4
+ announce/release-1.4.3
.. include:: links.txt
diff --git a/doc/install.txt b/doc/install.txt
index 6394920..c794692 100644
--- a/doc/install.txt
+++ b/doc/install.txt
@@ -4,13 +4,13 @@ tox installation
Install info in a nutshell
----------------------------------
-**Pythons**: CPython 2.4-3.2, Jython-2.5.1, pypy-1.5
+**Pythons**: CPython 2.4-3.3, Jython-2.5.1, pypy-1.9ff
**Operating systems**: Linux, Windows, OSX, Unix
**Installer Requirements**: setuptools_ or Distribute_
-**License**: GPLv2 or later
+**License**: MIT license
**hg repository**: http://bitbucket.org/hpk42/tox
diff --git a/doc/links.txt b/doc/links.txt
index 6362816..96f85c3 100644
--- a/doc/links.txt
+++ b/doc/links.txt
@@ -1,20 +1,20 @@
.. _Python: http://www.python.org
-.. _virtualenv: http://pypi.python.org/pypi/virtualenv
-.. _virtualenv3: http://pypi.python.org/pypi/virtualenv3
-.. _virtualenv5: http://pypi.python.org/pypi/virtualenv5
+.. _virtualenv: https://pypi.python.org/pypi/virtualenv
+.. _virtualenv3: https://pypi.python.org/pypi/virtualenv3
+.. _virtualenv5: https://pypi.python.org/pypi/virtualenv5
.. _`py.test`: http://pytest.org
.. _nosetests:
-.. _`nose`: http://pypi.python.org/pypi/nose
-.. _`Holger Krekel`: http://twitter.com/hpk42
-.. _`pytest-xdist`: http://pypi.python.org/pypi/pytest-xdist
+.. _`nose`: https://pypi.python.org/pypi/nose
+.. _`Holger Krekel`: https://twitter.com/hpk42
+.. _`pytest-xdist`: https://pypi.python.org/pypi/pytest-xdist
.. _`easy_install`: http://peak.telecommunity.com/DevCenter/EasyInstall
-.. _pip: http://pypi.python.org/pypi/pip
-.. _setuptools: http://pypi.python.org/pypi/setuptools
-.. _distribute: http://pypi.python.org/pypi/distribute
+.. _pip: https://pypi.python.org/pypi/pip
+.. _setuptools: https://pypi.python.org/pypi/setuptools
+.. _distribute: https://pypi.python.org/pypi/distribute
.. _`jenkins`: http://jenkins-ci.org/
-.. _sphinx: http://pypi.python.org/pypi/Sphinx
-.. _discover: http://pypi.python.org/pypi/discover
-.. _unittest2: http://pypi.python.org/pypi/unittest2
-.. _mock: http://pypi.python.org/pypi/mock/
+.. _sphinx: https://pypi.python.org/pypi/Sphinx
+.. _discover: https://pypi.python.org/pypi/discover
+.. _unittest2: https://pypi.python.org/pypi/unittest2
+.. _mock: https://pypi.python.org/pypi/mock/
diff --git a/doc/support.txt b/doc/support.txt
index 30b7b36..1ece16a 100644
--- a/doc/support.txt
+++ b/doc/support.txt
@@ -4,24 +4,24 @@
support and contact channels
=====================================
-You are welcome to:
+Getting in contact:
* join the `Testing In Python (TIP) mailing list`_ for general and tox/test-tool
interaction questions.
* file a `report on the issue tracker`_
-* join `tox-commit`_ to be notified of source changes
* hang out on the irc.freenode.net #pylib channel
* `clone the mercurial repository`_ and submit patches
-* follow the `tetamap blog`_ or `holger's twitter presence`_ or
- if all else fails contact holger krekel at gmail.
+* the `tetamap blog`_, `holger's twitter presence`_ or
+ for private inquiries holger krekel at gmail.
professional support
----------------------------
-If you are looking for on-site teaching and consulting report to setup
-and use state-of-the-start testing infrastructure with Python,
+.. note:: Upcoming: `professional testing with pytest and tox <`http://www.python-academy.com/courses/specialtopics/python_course_testing.html>`_ , 24th-26th June 2013, Leipzig.
+
+If you are looking for on-site teaching or consulting support,
contact holger at `merlinux.eu`_, an association of
-experienced and `well-known Python developers`_.
+experienced `well-known Python developers`_.
.. _`well-known Python developers`: http://merlinux.eu/people.txt
.. _`Maciej Fijalkowski`: http://www.ohloh.net/accounts/fijal
@@ -30,7 +30,7 @@ experienced and `well-known Python developers`_.
.. _`holger's twitter presence`: http://twitter.com/hpk42
.. _`merlinux.eu`: http://merlinux.eu
.. _`report on the issue tracker`: https://bitbucket.org/hpk42/tox/issues?status=new&status=open
-.. _`tetamap blog`: http://tetamap.wordpress.com
+.. _`tetamap blog`: http://holgerkrekel.net
.. _`tox-dev`: http://codespeak.net/mailman/listinfo/tox-dev
.. _`tox-commit`: http://codespeak.net/mailman/listinfo/tox-commit
.. _`clone the mercurial repository`: https://bitbucket.org/hpk42/tox
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..861a9f5
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,5 @@
+[egg_info]
+tag_build =
+tag_date = 0
+tag_svn_revision = 0
+
diff --git a/setup.py b/setup.py
index 75acf3b..095275f 100644
--- a/setup.py
+++ b/setup.py
@@ -2,57 +2,37 @@ import sys
from setuptools import setup
from setuptools.command.test import test as TestCommand
-long_description="""
-What is Tox?
-==========================
-Tox as is a generic virtualenv management and test command line tool you can
-use for:
-
-* checking your package installs correctly with different
- Python versions and interpreters
-
-* running your tests in each of the
- environments, configuring your test tool of choice
-
-* acting as a frontend to Continuous Integration
- servers, greatly reducing boilerplate and merging
- CI and shell-based testing.
-
-For more information, docs and many examples please checkout the `home page`_:
-
- http://tox.testrun.org/
-
-.. _`home page`: http://tox.testrun.org/
-"""
class Tox(TestCommand):
def finalize_options(self):
TestCommand.finalize_options(self)
- self.test_args = []
+ self.test_args = ["-v", "-epy"]
self.test_suite = True
+
def run_tests(self):
#import here, cause outside the eggs aren't loaded
import tox
tox.cmdline(self.test_args)
+
def main():
version = sys.version_info[:2]
- install_requires = ['virtualenv>=1.7', 'py>=1.4.9', ]
- if version < (2,7) or (3,0) <= version <= (3,1):
+ install_requires = ['virtualenv>=1.9.1', 'py>=1.4.15', ]
+ if version < (2, 7) or (3, 0) <= version <= (3, 1):
install_requires += ['argparse']
setup(
name='tox',
description='virtualenv-based automation of test activities',
- long_description=long_description,
- url='http://codespeak.net/tox',
- version='1.4.2',
- license='GPLv2 or later',
+ long_description=open("README.rst").read(),
+ url='http://tox.testrun.org/',
+ version='1.5.0',
+ license='http://opensource.org/licenses/MIT',
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
author='holger krekel',
author_email='holger at merlinux.eu',
packages=['tox', ],
- entry_points={'console_scripts': 'tox=tox:cmdline'},
+ entry_points={'console_scripts': 'tox=tox:cmdline\ntox-quickstart=tox._quickstart:main'},
# we use a public tox version to test, see tox.ini's testenv
# "deps" definition for the required dependencies
tests_require=['tox'],
@@ -62,15 +42,15 @@ def main():
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
- 'License :: OSI Approved :: GNU General Public License (GPL)',
- 'Operating System :: POSIX',
- 'Operating System :: Microsoft :: Windows',
- 'Operating System :: MacOS :: MacOS X',
- 'Topic :: Software Development :: Testing',
- 'Topic :: Software Development :: Libraries',
- 'Topic :: Utilities',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 3'],
+ 'License :: OSI Approved :: MIT License',
+ 'Operating System :: POSIX',
+ 'Operating System :: Microsoft :: Windows',
+ 'Operating System :: MacOS :: MacOS X',
+ 'Topic :: Software Development :: Testing',
+ 'Topic :: Software Development :: Libraries',
+ 'Topic :: Utilities',
+ 'Programming Language :: Python',
+ 'Programming Language :: Python :: 3'],
)
if __name__ == '__main__':
diff --git a/tests/conftest.py b/tests/conftest.py
index 51bc0a2..1d4bdde 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,289 +1,2 @@
-import py
-import tox
-import os
-import sys
-from py.builtin import print_
-from fnmatch import fnmatch
-import time
-from tox._config import parseconfig
-from tox._venv import VirtualEnv
-from tox._cmdline import Action
-def pytest_configure():
- if 'TOXENV' in os.environ:
- del os.environ['TOXENV']
- if 'HUDSON_URL' in os.environ:
- del os.environ['HUDSON_URL']
-
-def pytest_report_header():
- return "tox comes from: %r" % (tox.__file__)
-
-def pytest_funcarg__newconfig(request):
- tmpdir = request.getfuncargvalue("tmpdir")
- def newconfig(args, source=None):
- if source is None:
- source = args
- args = []
- s = py.std.textwrap.dedent(source)
- p = tmpdir.join("tox.ini")
- p.write(s)
- old = tmpdir.chdir()
- try:
- return parseconfig(args)
- finally:
- old.chdir()
- return newconfig
-
-def pytest_funcarg__tmpdir(request):
- tmpdir = request.getfuncargvalue("tmpdir")
- request.addfinalizer(py.path.local().chdir)
- tmpdir.chdir()
- return tmpdir
-
-def pytest_funcarg__cmd(request):
- return Cmd(request)
-
-class ReportExpectMock:
- def __init__(self, session):
- self._calls = []
- self._index = -1
- self.session = session
-
- def clear(self):
- self._calls[:] = []
-
- def __getattr__(self, name):
- if name[0] == "_":
- raise AttributeError(name)
-
- def generic_report(*args, **kwargs):
- self._calls.append((name,)+args)
- print ("%s" %(self._calls[-1], ))
- return generic_report
-
- def action(self, venv, msg, *args):
- self._calls.append(("action", venv, msg))
- print ("%s" %(self._calls[-1], ))
- return Action(self.session, venv, msg, args)
-
- def getnext(self, cat):
- __tracebackhide__ = True
- newindex = self._index + 1
- while newindex < len(self._calls):
- call = self._calls[newindex]
- lcat = call[0]
- if fnmatch(lcat, cat):
- self._index = newindex
- return call
- newindex += 1
- raise LookupError(
- "looking for %r, no reports found at >=%d in %r" %
- (cat, self._index+1, self._calls))
-
- def expect(self, cat, messagepattern="*", invert=False):
- __tracebackhide__ = True
- if not messagepattern.startswith("*"):
- messagepattern = "*" + messagepattern
- while self._index < len(self._calls):
- try:
- call = self.getnext(cat)
- except LookupError:
- break
- for lmsg in call[1:]:
- lmsg = str(lmsg).replace("\n", " ")
- if fnmatch(lmsg, messagepattern):
- if invert:
- raise AssertionError("found %s(%r), didn't expect it" %
- (cat, messagepattern))
- return
- if not invert:
- raise AssertionError(
- "looking for %s(%r), no reports found at >=%d in %r" %
- (cat, messagepattern, self._index+1, self._calls))
-
- def not_expect(self, cat, messagepattern="*"):
- return self.expect(cat, messagepattern, invert=True)
-
-class pcallMock:
- def __init__(self, args, cwd, env, stdout, stderr, shell):
- self.args = args
- self.cwd = cwd
- self.env = env
- self.stdout = stdout
- self.stderr = stderr
- self.shell = shell
-
- def communicate(self):
- return "", ""
- def wait(self):
- pass
-
-def pytest_funcarg__mocksession(request):
- from tox._cmdline import Session
- class MockSession(Session):
- def __init__(self):
- self._clearmocks()
- self.config = request.getfuncargvalue("newconfig")([], "")
- self._actions = []
- def getenv(self, name):
- return VirtualEnv(self.config.envconfigs[name], session=self)
- def _clearmocks(self):
- self._pcalls = []
- self._spec2pkg = {}
- self.report = ReportExpectMock(self)
- def make_emptydir(self, path):
- pass
- def popen(self, args, cwd, shell=None,
- stdout=None, stderr=None, env=None):
- pm = pcallMock(args, cwd, env, stdout, stderr, shell)
- self._pcalls.append(pm)
- return pm
- return MockSession()
-
-def pytest_funcarg__newmocksession(request):
- mocksession = request.getfuncargvalue("mocksession")
- newconfig = request.getfuncargvalue("newconfig")
- def newmocksession(args, source):
- config = newconfig(args, source)
- mocksession.config = config
- return mocksession
- return newmocksession
-
-class Cmd:
- def __init__(self, request):
- self.tmpdir = request.getfuncargvalue("tmpdir")
- self.request = request
- current = py.path.local()
- self.request.addfinalizer(current.chdir)
- def chdir(self, target):
- target.chdir()
-
- def popen(self, argv, stdout, stderr, **kw):
- if not hasattr(py.std, 'subprocess'):
- py.test.skip("no subprocess module")
- env = os.environ.copy()
- env['PYTHONPATH'] = ":".join(filter(None, [
- str(os.getcwd()), env.get('PYTHONPATH', '')]))
- kw['env'] = env
- #print "env", env
- return py.std.subprocess.Popen(argv, stdout=stdout, stderr=stderr, **kw)
-
- def run(self, *argv):
- argv = [str(x) for x in argv]
- p1 = self.tmpdir.join("stdout")
- p2 = self.tmpdir.join("stderr")
- print("%s$ %s" % (os.getcwd(), " ".join(argv)))
- f1 = p1.open("wb")
- f2 = p2.open("wb")
- now = time.time()
- popen = self.popen(argv, stdout=f1, stderr=f2,
- close_fds=(sys.platform != "win32"))
- ret = popen.wait()
- f1.close()
- f2.close()
- out = p1.read("rb")
- out = getdecoded(out).splitlines()
- err = p2.read("rb")
- err = getdecoded(err).splitlines()
- def dump_lines(lines, fp):
- try:
- for line in lines:
- py.builtin.print_(line, file=fp)
- except UnicodeEncodeError:
- print("couldn't print to %s because of encoding" % (fp,))
- dump_lines(out, sys.stdout)
- dump_lines(err, sys.stderr)
- return RunResult(ret, out, err, time.time()-now)
-
-def getdecoded(out):
- try:
- return out.decode("utf-8")
- except UnicodeDecodeError:
- return "INTERNAL not-utf8-decodeable, truncated string:\n%s" % (
- py.io.saferepr(out),)
-
-class RunResult:
- def __init__(self, ret, outlines, errlines, duration):
- self.ret = ret
- self.outlines = outlines
- self.errlines = errlines
- self.stdout = LineMatcher(outlines)
- self.stderr = LineMatcher(errlines)
- self.duration = duration
-
-class LineMatcher:
- def __init__(self, lines):
- self.lines = lines
-
- def str(self):
- return "\n".join(self.lines)
-
- def fnmatch_lines(self, lines2):
- if isinstance(lines2, str):
- lines2 = py.code.Source(lines2)
- if isinstance(lines2, py.code.Source):
- lines2 = lines2.strip().lines
-
- from fnmatch import fnmatch
- lines1 = self.lines[:]
- nextline = None
- extralines = []
- __tracebackhide__ = True
- for line in lines2:
- nomatchprinted = False
- while lines1:
- nextline = lines1.pop(0)
- if line == nextline:
- print_("exact match:", repr(line))
- break
- elif fnmatch(nextline, line):
- print_("fnmatch:", repr(line))
- print_(" with:", repr(nextline))
- break
- else:
- if not nomatchprinted:
- print_("nomatch:", repr(line))
- nomatchprinted = True
- print_(" and:", repr(nextline))
- extralines.append(nextline)
- else:
- assert line == nextline
-
-def pytest_funcarg__initproj(request):
- """ create a factory function for creating example projects. """
- tmpdir = request.getfuncargvalue("tmpdir")
- def initproj(name, filedefs=None):
- if filedefs is None:
- filedefs = {}
- parts = name.split("-")
- if len(parts) == 1:
- parts.append("0.1")
- name, version = parts
- base = tmpdir.ensure(name, dir=1)
- create_files(base, filedefs)
- if 'setup.py' not in filedefs:
- create_files(base, {'setup.py': '''
- from setuptools import setup
- setup(
- name='%(name)s',
- description='%(name)s project',
- version='%(version)s',
- license='GPLv2 or later',
- platforms=['unix', 'win32'],
- packages=['%(name)s', ],
- )
- ''' % locals()})
- if name not in filedefs:
- create_files(base, {name:
- {'__init__.py': '__version__ = %s' % version}})
- print ("created project in %s" %(base,))
- base.chdir()
- return initproj
-
-def create_files(base, filedefs):
- for key, value in filedefs.items():
- if isinstance(value, dict):
- create_files(base.ensure(key, dir=1), value)
- elif isinstance(value, str):
- s = py.std.textwrap.dedent(value)
- base.join(key).write(s)
+from tox._pytestplugin import *
diff --git a/tests/test_config.py b/tests/test_config.py
index 9ad8398..037fc26 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -1,9 +1,12 @@
import tox
+import pytest
import os, sys
+import subprocess
from textwrap import dedent
import py
from tox._config import IniReader, CommandParser
+from tox._config import parseconfig
class TestVenvConfig:
def test_config_parsing_minimal(self, tmpdir, newconfig):
@@ -22,10 +25,8 @@ class TestVenvConfig:
indexserver =
xyz = xyz_repo
[testenv:py1]
- basepython=xyz
deps=hello
[testenv:py2]
- basepython=hello
deps=
world1
:xyz:http://hello/world
@@ -33,11 +34,9 @@ class TestVenvConfig:
assert config.toxworkdir == tmpdir
assert len(config.envconfigs) == 2
assert config.envconfigs['py1'].envdir == tmpdir.join("py1")
- assert config.envconfigs['py1'].basepython == "xyz"
dep = config.envconfigs['py1'].deps[0]
assert dep.name == "hello"
assert dep.indexserver is None
- assert config.envconfigs['py2'].basepython == "hello"
assert config.envconfigs['py2'].envdir == tmpdir.join("py2")
dep1, dep2 = config.envconfigs['py2'].deps
assert dep1.name == "world1"
@@ -73,6 +72,17 @@ class TestConfigPackage:
""" % tmpdir)
assert config.toxworkdir == tmpdir
+class TestParseconfig:
+ def test_search_parents(self, tmpdir):
+ b = tmpdir.mkdir("a").mkdir("b")
+ toxinipath = tmpdir.ensure("tox.ini")
+ old = b.chdir()
+ try:
+ config = parseconfig([])
+ finally:
+ old.chdir()
+ assert config.toxinipath == toxinipath
+
class TestIniParser:
def test_getdefault_single(self, tmpdir, newconfig):
config = newconfig("""
@@ -166,6 +176,29 @@ class TestIniParser:
py.test.raises(tox.exception.ConfigError,
'reader.getdefault("section", "key2")')
+ def test_getdefault_other_section_substitution(self, newconfig):
+ config = newconfig("""
+ [section]
+ key = rue
+ [testenv]
+ key = t{[section]key}
+ """)
+ reader = IniReader(config._cfg)
+ x = reader.getdefault("testenv", "key")
+ assert x == "true"
+
+ def test_command_substitution_from_other_section(self, newconfig):
+ config = newconfig("""
+ [section]
+ key = whatever
+ [testenv]
+ commands =
+ echo {[section]key}
+ """)
+ reader = IniReader(config._cfg)
+ x = reader.getargvlist("testenv", "commands")
+ assert x == [["echo", "whatever"]]
+
def test_argvlist(self, tmpdir, newconfig):
config = newconfig("""
[section]
@@ -319,7 +352,7 @@ class TestConfigTestEnv:
envconfig = config.envconfigs['python']
assert envconfig.commands == [["xyz", "--abc"]]
assert envconfig.changedir == config.setupdir
- assert envconfig.distribute == True
+ assert envconfig.distribute == False
assert envconfig.sitepackages == False
assert envconfig.envlogdir == envconfig.envdir.join("log")
assert envconfig.setenv is None
@@ -328,13 +361,31 @@ class TestConfigTestEnv:
config = newconfig("""
[testenv]
commands=xyz
- [testenv:py30]
+ [testenv:py]
commands=abc
""")
assert len(config.envconfigs) == 1
- envconfig = config.envconfigs['py30']
+ envconfig = config.envconfigs['py']
assert envconfig.commands == [["abc"]]
+ def test_whitelist_externals(self, tmpdir, newconfig):
+ config = newconfig("""
+ [testenv]
+ whitelist_externals = xyz
+ commands=xyz
+ [testenv:x]
+
+ [testenv:py]
+ whitelist_externals = xyz2
+ commands=abc
+ """)
+ assert len(config.envconfigs) == 2
+ envconfig = config.envconfigs['py']
+ assert envconfig.commands == [["abc"]]
+ assert envconfig.whitelist_externals == ["xyz2"]
+ envconfig = config.envconfigs['x']
+ assert envconfig.whitelist_externals == ["xyz"]
+
def test_changedir(self, tmpdir, newconfig):
config = newconfig("""
[testenv]
@@ -354,14 +405,18 @@ class TestConfigTestEnv:
envconfig = config.envconfigs['python']
assert envconfig.envpython == envconfig.envbindir.join("python")
- def test_envbindir_jython(self, tmpdir, newconfig):
+ @pytest.mark.parametrize("bp", ["jython", "pypy"])
+ def test_envbindir_jython(self, tmpdir, newconfig, bp):
config = newconfig("""
[testenv]
- basepython=jython
- """)
+ basepython=%s
+ """ % bp)
assert len(config.envconfigs) == 1
envconfig = config.envconfigs['python']
- assert envconfig.envpython == envconfig.envbindir.join("jython")
+ # on win32 and linux virtualenv uses "bin" for pypy/jython
+ assert envconfig.envbindir.basename == "bin"
+ if bp == "jython":
+ assert envconfig.envpython == envconfig.envbindir.join(bp)
def test_setenv_overrides(self, tmpdir, newconfig):
config = newconfig("""
@@ -429,7 +484,7 @@ class TestConfigTestEnv:
assert argv[3][0] == conf.envbindir
assert argv[4][0] == conf.envtmpdir
assert argv[5][0] == conf.envpython
- assert argv[6][0] == os.path.expanduser("~")
+ assert argv[6][0] == str(py.path.local._gethomedir())
assert argv[7][0] == config.homedir.join(".tox", "distshare")
assert argv[8][0] == conf.envlogdir
@@ -577,6 +632,35 @@ class TestConfigTestEnv:
assert conf.changedir.basename == 'testing'
assert conf.changedir.dirpath().realpath() == tmpdir.realpath()
+ @pytest.mark.xfailif("sys.platform == 'win32'")
+ def test_substitution_envsitepackagesdir(self, tmpdir, monkeypatch,
+ newconfig):
+ """
+ The envsitepackagesdir property is mostly doing system work,
+ so this test doesn't excercise it very well.
+
+ Usage of envsitepackagesdir on win32/jython will explicitly
+ throw an exception,
+ """
+ class MockPopen(object):
+ returncode = 0
+
+ def __init__(self, *args, **kwargs):
+ pass
+
+ def communicate(self, *args, **kwargs):
+ return 'onevalue', 'othervalue'
+
+ monkeypatch.setattr(subprocess, 'Popen', MockPopen)
+ env = 'py%s' % (''.join(sys.version.split('.')[0:2]))
+ config = newconfig("""
+ [testenv:%s]
+ commands = {envsitepackagesdir}
+ """ % (env))
+ conf = config.envconfigs[env]
+ argv = conf.commands
+ assert argv[0][0] == 'onevalue'
+
class TestGlobalOptions:
def test_notest(self, newconfig):
@@ -655,8 +739,13 @@ class TestGlobalOptions:
config = newconfig(["-eALL"], inisource)
assert config.envlist == ['py26', 'py27', 'py31']
+ def test_py_venv(self, tmpdir, newconfig, monkeypatch):
+ config = newconfig(["-epy"], "")
+ env = config.envconfigs['py']
+ assert str(env.basepython) == sys.executable
+
def test_default_environments(self, tmpdir, newconfig, monkeypatch):
- envs = "py24,py25,py26,py27,py30,py31,py32,jython,pypy"
+ envs = "py24,py25,py26,py27,py31,py32,jython,pypy"
inisource = """
[tox]
envlist = %s
@@ -730,6 +819,26 @@ class TestIndexServer:
assert config.indexserver['default'].url == "qwe2"
assert config.indexserver['name1'].url == "abc"
+ config = newconfig(["-i", "ALL=xzy"], inisource)
+ assert len(config.indexserver) == 2
+ assert config.indexserver["default"].url == "xzy"
+ assert config.indexserver["name1"].url == "xzy"
+
+ def test_multiple_homedir_relative_local_indexservers(self, newconfig):
+ inisource = """
+ [tox]
+ indexserver =
+ default = file://{homedir}/.pip/downloads/simple
+ local1 = file://{homedir}/.pip/downloads/simple
+ local2 = file://{toxinidir}/downloads/simple
+ pypi = http://pypi.python.org/simple
+ """
+ config = newconfig([], inisource)
+ homedir = str(py.path.local._gethomedir())
+ expected = "file://%s/.pip/downloads/simple" % homedir
+ assert config.indexserver['default'].url == expected
+ assert config.indexserver['local1'].url == \
+ config.indexserver['default'].url
class TestParseEnv:
@@ -763,15 +872,28 @@ class TestCmdInvocation:
assert tox.__version__ in stdout
assert "imported from" in stdout
- def test_unkonwn_ini(self, cmd):
- result = cmd.run("tox")
- assert result.ret
- result.stderr.fnmatch_lines([
- "*tox.ini*does not exist*",
- ])
+ def test_listenvs(self, cmd, initproj):
+ initproj('listenvs', filedefs={
+ 'tox.ini': '''
+ [tox]
+ envlist=py26,py27,py33,pypy,docs
+
+ [testenv:notincluded]
+ changedir = whatever
+
+ [testenv:docs]
+ changedir = docs
+ ''',
+ })
+ result = cmd.run("tox", "-l")
+ result.stdout.fnmatch_lines("""
+ *py26*
+ *py27*
+ *py33*
+ *pypy*
+ *docs*
+ """)
- @py.test.mark.xfail("sys.version_info < (2,6)",
- reason="virtualenv3 cannot be imported")
def test_config_specific_ini(self, tmpdir, cmd):
ini = tmpdir.ensure("hello.ini")
result = cmd.run("tox", "-c", ini, "--showconfig")
@@ -785,10 +907,9 @@ class TestCmdInvocation:
result = cmd.run("tox")
assert result.ret
result.stderr.fnmatch_lines([
- "*ERROR*tox.ini*does not exist*",
+ "*ERROR*tox.ini*not*found*",
])
-
class TestCommandParser:
def test_command_parser_for_word(self):
diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py
new file mode 100644
index 0000000..64d8790
--- /dev/null
+++ b/tests/test_quickstart.py
@@ -0,0 +1,362 @@
+import pytest
+import tox._quickstart
+
+
+
+class TestToxQuickstartMain(object):
+ @pytest.fixture(autouse=True)
+ def cleandir(self, tmpdir):
+ tmpdir.chdir()
+
+ def mock_term_input_return_values(self, return_values):
+ for return_val in return_values:
+ yield return_val
+
+ def get_mock_term_input(self, return_values):
+ generator = self.mock_term_input_return_values(return_values)
+
+ def mock_term_input(prompt):
+ try:
+ return next(generator)
+ except NameError:
+ return generator.next()
+
+ return mock_term_input
+
+ def test_quickstart_main_choose_individual_pythons_and_pytest(self, monkeypatch):
+ monkeypatch.setattr(
+ tox._quickstart, 'term_input',
+ self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'N', 'N', 'Y', 'Y', 'Y', 'N', 'py.test', 'pytest']))
+
+ tox._quickstart.main(argv=['tox-quickstart'])
+
+ expected_tox_ini = """
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py24, py25, py26, py27, py32, py33, pypy
+
+[testenv]
+commands = py.test
+deps =
+ pytest
+""".lstrip()
+ result = open('tox.ini').read()
+ assert(result == expected_tox_ini)
+
+ def test_quickstart_main_choose_individual_pythons_and_nose_adds_deps(self, monkeypatch):
+ monkeypatch.setattr(
+ tox._quickstart, 'term_input',
+ self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'N', 'N', 'Y', 'Y', 'Y', 'N', 'nosetests', '']))
+
+ tox._quickstart.main(argv=['tox-quickstart'])
+
+ expected_tox_ini = """
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py24, py25, py26, py27, py32, py33, pypy
+
+[testenv]
+commands = nosetests
+deps =
+ nose
+""".lstrip()
+ result = open('tox.ini').read()
+ assert(result == expected_tox_ini)
+
+ def test_quickstart_main_choose_individual_pythons_and_trial_adds_deps(self, monkeypatch):
+ monkeypatch.setattr(
+ tox._quickstart, 'term_input',
+ self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'N', 'N', 'Y', 'Y', 'Y', 'N', 'trial', '']))
+
+ tox._quickstart.main(argv=['tox-quickstart'])
+
+ expected_tox_ini = """
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py24, py25, py26, py27, py32, py33, pypy
+
+[testenv]
+commands = trial
+deps =
+ twisted
+""".lstrip()
+ result = open('tox.ini').read()
+ assert(result == expected_tox_ini)
+
+ def test_quickstart_main_choose_individual_pythons_and_pytest_adds_deps(self, monkeypatch):
+ monkeypatch.setattr(
+ tox._quickstart, 'term_input',
+ self.get_mock_term_input(['4', 'Y', 'Y', 'Y', 'Y', 'N', 'N', 'Y', 'Y', 'Y', 'N', 'py.test', '']))
+
+ tox._quickstart.main(argv=['tox-quickstart'])
+
+ expected_tox_ini = """
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py24, py25, py26, py27, py32, py33, pypy
+
+[testenv]
+commands = py.test
+deps =
+ pytest
+""".lstrip()
+ result = open('tox.ini').read()
+ assert(result == expected_tox_ini)
+
+ def test_quickstart_main_choose_py27_and_pytest_adds_deps(self, monkeypatch):
+ monkeypatch.setattr(
+ tox._quickstart, 'term_input',
+ self.get_mock_term_input(['1', 'py.test', '']))
+
+ tox._quickstart.main(argv=['tox-quickstart'])
+
+ expected_tox_ini = """
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py27
+
+[testenv]
+commands = py.test
+deps =
+ pytest
+""".lstrip()
+ result = open('tox.ini').read()
+ assert(result == expected_tox_ini)
+
+ def test_quickstart_main_choose_py27_and_py33_and_pytest_adds_deps(self, monkeypatch):
+ monkeypatch.setattr(
+ tox._quickstart, 'term_input',
+ self.get_mock_term_input(['2', 'py.test', '']))
+
+ tox._quickstart.main(argv=['tox-quickstart'])
+
+ expected_tox_ini = """
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py27, py33
+
+[testenv]
+commands = py.test
+deps =
+ pytest
+""".lstrip()
+ result = open('tox.ini').read()
+ assert(result == expected_tox_ini)
+
+ def test_quickstart_main_choose_all_pythons_and_pytest_adds_deps(self, monkeypatch):
+ monkeypatch.setattr(
+ tox._quickstart, 'term_input',
+ self.get_mock_term_input(['3', 'py.test', '']))
+
+ tox._quickstart.main(argv=['tox-quickstart'])
+
+ expected_tox_ini = """
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py24, py25, py26, py27, py30, py31, py32, py33, pypy, jython
+
+[testenv]
+commands = py.test
+deps =
+ pytest
+""".lstrip()
+ result = open('tox.ini').read()
+ assert(result == expected_tox_ini)
+
+ def test_quickstart_main_choose_individual_pythons_and_defaults(self, monkeypatch):
+ monkeypatch.setattr(
+ tox._quickstart, 'term_input',
+ self.get_mock_term_input(['4', '', '', '', '', '', '', '', '', '', '', '', '']))
+
+ tox._quickstart.main(argv=['tox-quickstart'])
+
+ expected_tox_ini = """
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py24, py25, py26, py27, py30, py31, py32, py33, pypy, jython
+
+[testenv]
+commands = {envpython} setup.py test
+deps =
+
+""".lstrip()
+ result = open('tox.ini').read()
+ assert(result == expected_tox_ini)
+
+ def test_quickstart_main_existing_tox_ini(self, monkeypatch):
+ try:
+ f = open('tox.ini', 'w')
+ f.write('foo bar\n')
+ finally:
+ f.close()
+
+ monkeypatch.setattr(
+ tox._quickstart, 'term_input',
+ self.get_mock_term_input(['4', '', '', '', '', '', '', '', '', '', '', '', '', '']))
+
+ tox._quickstart.main(argv=['tox-quickstart'])
+
+ expected_tox_ini = """
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py24, py25, py26, py27, py30, py31, py32, py33, pypy, jython
+
+[testenv]
+commands = {envpython} setup.py test
+deps =
+
+""".lstrip()
+ result = open('tox-generated.ini').read()
+ assert(result == expected_tox_ini)
+
+
+class TestToxQuickstart(object):
+ def test_pytest(self):
+ d = {
+ 'py24': True,
+ 'py25': True,
+ 'py26': True,
+ 'py27': True,
+ 'py32': True,
+ 'py33': True,
+ 'pypy': True,
+ 'commands': 'py.test',
+ 'deps': 'pytest',
+ }
+ expected_tox_ini = """
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py24, py25, py26, py27, py32, py33, pypy
+
+[testenv]
+commands = py.test
+deps =
+ pytest
+""".lstrip()
+ d = tox._quickstart.process_input(d)
+ tox._quickstart.generate(d)
+ result = open('tox.ini').read()
+ # print(result)
+ assert(result == expected_tox_ini)
+
+ def test_setup_py_test(self):
+ d = {
+ 'py26': True,
+ 'py27': True,
+ 'commands': 'python setup.py test',
+ 'deps': '',
+ }
+ expected_tox_ini = """
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py26, py27
+
+[testenv]
+commands = python setup.py test
+deps =
+
+""".lstrip()
+ d = tox._quickstart.process_input(d)
+ tox._quickstart.generate(d)
+ result = open('tox.ini').read()
+ # print(result)
+ assert(result == expected_tox_ini)
+
+ def test_trial(self):
+ d = {
+ 'py27': True,
+ 'commands': 'trial',
+ 'deps': 'Twisted',
+ }
+ expected_tox_ini = """
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py27
+
+[testenv]
+commands = trial
+deps =
+ Twisted
+""".lstrip()
+ d = tox._quickstart.process_input(d)
+ tox._quickstart.generate(d)
+ result = open('tox.ini').read()
+ # print(result)
+ assert(result == expected_tox_ini)
+
+ def test_nosetests(self):
+ d = {
+ 'py27': True,
+ 'py32': True,
+ 'py33': True,
+ 'pypy': True,
+ 'commands': 'nosetests -v',
+ 'deps': 'nose',
+ }
+ expected_tox_ini = """
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py27, py32, py33, pypy
+
+[testenv]
+commands = nosetests -v
+deps =
+ nose
+""".lstrip()
+ d = tox._quickstart.process_input(d)
+ tox._quickstart.generate(d)
+ result = open('tox.ini').read()
+ # print(result)
+ assert(result == expected_tox_ini)
diff --git a/tests/test_venv.py b/tests/test_venv.py
index 3db3a6b..e66aba9 100644
--- a/tests/test_venv.py
+++ b/tests/test_venv.py
@@ -3,6 +3,7 @@ import tox
import pytest
import os, sys
from tox._venv import VirtualEnv, CreationConfig, getdigest
+from tox._venv import find_executable
#def test_global_virtualenv(capfd):
# v = VirtualEnv()
@@ -15,8 +16,24 @@ from tox._venv import VirtualEnv, CreationConfig, getdigest
def test_getdigest(tmpdir):
assert getdigest(tmpdir) == "0"*32
+ at pytest.mark.skipif("sys.platform != 'win32'")
+def test_locate_via_py(monkeypatch):
+ from tox._venv import locate_via_py
+ class PseudoPy:
+ def sysexec(self, *args):
+ assert args[0] == '-3.2'
+ assert args[1] == '-c'
+ # Return value needs to actually exist!
+ return sys.executable
+ @staticmethod
+ def ret_pseudopy(name):
+ assert name == 'py'
+ return PseudoPy()
+ # Monkeypatch py.path.local.sysfind to return PseudoPy
+ monkeypatch.setattr(py.path.local, 'sysfind', ret_pseudopy)
+ assert locate_via_py('3', '2') == sys.executable
+
def test_find_executable():
- from tox._venv import find_executable
p = find_executable(sys.executable)
assert p == py.path.local(sys.executable)
for ver in [""] + "2.4 2.5 2.6 2.7 3.0 3.1 3.2 3.3".split():
@@ -37,6 +54,14 @@ def test_find_executable():
stdout, stderr = popen.communicate()
assert ver in py.builtin._totext(stderr, "ascii")
+def test_find_executable_extra(monkeypatch):
+ @staticmethod
+ def sysfind(x):
+ return "hello"
+ monkeypatch.setattr(py.path.local, "sysfind", sysfind)
+ t = find_executable("qweqwe")
+ assert t == "hello"
+
def test_getsupportedinterpreter(monkeypatch, newconfig, mocksession):
config = newconfig([], """
[testenv:python]
@@ -77,6 +102,20 @@ def test_create(monkeypatch, mocksession, newconfig):
assert interp == venv.getconfigexecutable()
assert venv.path_config.check(exists=False)
+ at pytest.mark.skipif("sys.platform == 'win32'")
+def test_commandpath_venv_precendence(tmpdir, monkeypatch,
+ mocksession, newconfig):
+ config = newconfig([], """
+ [testenv:py123]
+ """)
+ envconfig = config.envconfigs['py123']
+ venv = VirtualEnv(envconfig, session=mocksession)
+ tmpdir.ensure("easy_install")
+ monkeypatch.setenv("PATH", str(tmpdir), prepend=os.pathsep)
+ envconfig.envbindir.ensure("easy_install")
+ p = venv.getcommandpath("easy_install")
+ assert py.path.local(p).relto(envconfig.envbindir), p
+
def test_create_distribute(monkeypatch, mocksession, newconfig):
config = newconfig([], """
[testenv:py123]
@@ -91,6 +130,7 @@ def test_create_distribute(monkeypatch, mocksession, newconfig):
assert len(l) >= 1
args = l[0].args
assert "--distribute" not in map(str, args)
+ assert "--setuptools" in map(str, args)
def test_create_sitepackages(monkeypatch, mocksession, newconfig):
config = newconfig([], """
@@ -138,13 +178,18 @@ def test_install_deps_wildcard(newmocksession):
assert l[1].cwd == venv.envconfig.envlogdir
assert "pip" in str(args[0])
assert args[1] == "install"
- arg = "--download-cache=" + str(venv.envconfig.downloadcache)
- assert arg in args[2:]
+ #arg = "--download-cache=" + str(venv.envconfig.downloadcache)
+ #assert arg in args[2:]
args = [arg for arg in args if str(arg).endswith("dep1-1.1.zip")]
assert len(args) == 1
-def test_install_downloadcache(newmocksession):
+ at pytest.mark.parametrize("envdc", [True, False])
+def test_install_downloadcache(newmocksession, monkeypatch, tmpdir, envdc):
+ if envdc:
+ monkeypatch.setenv("PIP_DOWNLOAD_CACHE", tmpdir)
+ else:
+ monkeypatch.delenv("PIP_DOWNLOAD_CACHE", raising=False)
mocksession = newmocksession([], """
[testenv:py123]
distribute=True
@@ -163,8 +208,10 @@ def test_install_downloadcache(newmocksession):
assert l[1].cwd == venv.envconfig.envlogdir
assert "pip" in str(args[0])
assert args[1] == "install"
- arg = "--download-cache=" + str(venv.envconfig.downloadcache)
- assert arg in args[2:]
+ if envdc:
+ assert venv.envconfig.downloadcache == tmpdir
+ else:
+ assert not venv.envconfig.downloadcache
assert "dep1" in args
assert "dep2" in args
deps = list(filter(None, [x[1] for x in venv._getliveconfig().deps]))
@@ -175,10 +222,12 @@ def test_install_deps_indexserver(newmocksession):
[tox]
indexserver =
abc = ABC
+ abc2 = ABC
[testenv:py123]
deps=
dep1
:abc:dep2
+ :abc2:dep3
""")
venv = mocksession.getenv('py123')
venv.create()
@@ -188,7 +237,7 @@ def test_install_deps_indexserver(newmocksession):
venv.install_deps()
# two different index servers, two calls
- assert len(l) == 2
+ assert len(l) == 3
args = " ".join(l[0].args)
assert "-i" not in args
assert "dep1" in args
@@ -196,8 +245,11 @@ def test_install_deps_indexserver(newmocksession):
args = " ".join(l[1].args)
assert "-i ABC" in args
assert "dep2" in args
+ args = " ".join(l[2].args)
+ assert "-i ABC" in args
+ assert "dep3" in args
-def test_install_sdist_indexserver(newmocksession, tmpdir):
+def test_installpkg_indexserver(newmocksession, tmpdir):
mocksession = newmocksession([], """
[tox]
indexserver =
@@ -206,7 +258,7 @@ def test_install_sdist_indexserver(newmocksession, tmpdir):
venv = mocksession.getenv('python')
l = mocksession._pcalls
p = tmpdir.ensure("distfile.tar.gz")
- mocksession.installsdist(venv, p)
+ mocksession.installpkg(venv, p)
# two different index servers, two calls
assert len(l) == 1
args = " ".join(l[0].args)
@@ -219,11 +271,21 @@ def test_install_recreate(newmocksession):
""")
venv = mocksession.getenv('python')
venv.update()
- mocksession.installsdist(venv, "xz")
+ mocksession.installpkg(venv, "xz")
mocksession.report.expect("verbosity0", "*create*")
venv.update()
mocksession.report.expect("verbosity0", "*recreate*")
+def test_test_runtests_action_command_is_in_output(newmocksession):
+ mocksession = newmocksession([], '''
+ [testenv]
+ commands = echo foo bar
+ ''')
+ venv = mocksession.getenv('python')
+ venv.update()
+ venv.test()
+ mocksession.report.expect("verbosity0", "*runtests*commands?0? | echo foo bar")
+
def test_install_error(newmocksession, monkeypatch):
mocksession = newmocksession(['--recreate'], """
[testenv]
@@ -234,6 +296,7 @@ def test_install_error(newmocksession, monkeypatch):
venv = mocksession.getenv('python')
venv.test()
mocksession.report.expect("error", "*not find*qwelkqw*")
+ assert venv.status == "commands failed"
def test_install_command_not_installed(newmocksession, monkeypatch):
mocksession = newmocksession(['--recreate'], """
@@ -244,6 +307,22 @@ def test_install_command_not_installed(newmocksession, monkeypatch):
venv = mocksession.getenv('python')
venv.test()
mocksession.report.expect("warning", "*test command found but not*")
+ assert venv.status == "commands failed"
+
+def test_install_command_whitelisted(newmocksession, monkeypatch):
+ mocksession = newmocksession(['--recreate'], """
+ [testenv]
+ whitelist_externals = py.test
+ xy*
+ commands=
+ py.test
+ xyz
+ """)
+ venv = mocksession.getenv('python')
+ venv.test()
+ mocksession.report.expect("warning", "*test command found but not*",
+ invert=True)
+ assert venv.status == "commands failed"
@pytest.mark.skipif("not sys.platform.startswith('linux')")
def test_install_command_not_installed(newmocksession):
@@ -354,7 +433,7 @@ class TestCreationConfig:
cconfig = venv._getliveconfig()
venv.update()
assert not venv.path_config.check()
- mocksession.installsdist(venv, "sdist.zip")
+ mocksession.installpkg(venv, "sdist.zip")
assert venv.path_config.check()
assert mocksession._pcalls
args1 = map(str, mocksession._pcalls[0].args)
@@ -388,7 +467,7 @@ class TestCreationConfig:
venv = VirtualEnv(envconfig, session=mocksession)
venv.update()
cconfig = venv._getliveconfig()
- cconfig.distribute = False
+ cconfig.distribute = True
cconfig.writeconfig(venv.path_config)
mocksession._clearmocks()
venv.update()
@@ -439,7 +518,7 @@ def test_setenv_added_to_pcall(mocksession, newconfig):
venv = VirtualEnv(config.envconfigs['python'], session=mocksession)
# import pdb; pdb.set_trace()
- mocksession.installsdist(venv, "xyz")
+ mocksession.installpkg(venv, "xyz")
venv.test()
l = mocksession._pcalls
@@ -454,21 +533,21 @@ def test_setenv_added_to_pcall(mocksession, newconfig):
for e in os.environ:
assert e in env
-def test_install_sdist_no_upgrade(newmocksession):
+def test_installpkg_no_upgrade(newmocksession):
mocksession = newmocksession([], "")
venv = mocksession.getenv('python')
venv.just_created = True
venv.envconfig.envdir.ensure(dir=1)
- mocksession.installsdist(venv, "whatever")
+ mocksession.installpkg(venv, "whatever")
l = mocksession._pcalls
assert len(l) == 1
assert '-U' not in l[0].args
-def test_install_sdist_upgrade(newmocksession):
+def test_installpkg_upgrade(newmocksession):
mocksession = newmocksession([], "")
venv = mocksession.getenv('python')
assert not hasattr(venv, 'just_created')
- mocksession.installsdist(venv, "whatever")
+ mocksession.installpkg(venv, "whatever")
l = mocksession._pcalls
assert len(l) == 1
assert '-U' in l[0].args
diff --git a/tests/test_z_cmdline.py b/tests/test_z_cmdline.py
index 1b397c7..6a57114 100644
--- a/tests/test_z_cmdline.py
+++ b/tests/test_z_cmdline.py
@@ -2,7 +2,7 @@ import tox
import py
import pytest
import sys
-from conftest import ReportExpectMock
+from tox._pytestplugin import ReportExpectMock
pytest_plugins = "pytester"
@@ -64,6 +64,8 @@ def test__resolve_pkg_doubledash(tmpdir, mocksession):
res = mocksession._resolve_pkg(distshare.join("pkg-mine*"))
assert res == p
+
+
class TestSession:
def test_make_sdist(self, initproj):
initproj("example123-0.5", filedefs={
@@ -127,10 +129,10 @@ class TestSession:
envs = session.venvlist
assert len(envs) == 2
env1, env2 = envs
- session.setenvstatus(env1, "FAIL XYZ")
- assert session.venvstatus[env1.path]
- session.setenvstatus(env2, 0)
- assert not session.venvstatus[env2.path]
+ env1.status = "FAIL XYZ"
+ assert env1.status
+ env2.status = 0
+ assert not env2.status
session._summary()
out, err = capfd.readouterr()
exp = "%s: FAIL XYZ" % env1.envconfig.envname
@@ -274,7 +276,7 @@ def test_package_install_fails(cmd, initproj):
name='pkg123',
description='pkg123 project',
version='0.7',
- license='GPLv2 or later',
+ license='MIT',
platforms=['unix', 'win32'],
packages=['pkg123',],
install_requires=['qweqwe123'],
@@ -421,7 +423,7 @@ def test_separate_sdist(cmd, initproj):
result = cmd.run("tox", "-v", "--notest")
assert not result.ret
result.stdout.fnmatch_lines([
- "*sdist-inst*%s*" % sdistfile,
+ "*inst*%s*" % sdistfile,
])
@@ -432,9 +434,30 @@ def test_sdist_latest(tmpdir, newconfig):
distshare=%s
sdistsrc={distshare}/pkg123-*
""" % distshare)
- distshare.ensure("pkg123-1.3.5.zip")
+ p0 = distshare.ensure("pkg123-1.3.5.zip")
p = distshare.ensure("pkg123-1.4.5.zip")
distshare.ensure("pkg123-1.4.5a1.zip")
session = Session(config)
sdist_path = session.sdist()
assert sdist_path == p
+
+def test_installpkg(tmpdir, newconfig):
+ p = tmpdir.ensure("pkg123-1.0.zip")
+ config = newconfig(["--installpkg=%s" % p], "")
+ session = Session(config)
+ sdist_path = session.sdist()
+ assert sdist_path == p
+
+ at pytest.mark.xfail("sys.platform == 'win32'", reason="test needs better impl")
+def test_envsitepackagesdir(cmd, initproj):
+ initproj("pkg512-0.0.5", filedefs={
+ 'tox.ini': """
+ [testenv]
+ commands=
+ echo X:{envsitepackagesdir}
+ """})
+ result = cmd.run("tox")
+ assert result.ret == 0
+ result.stdout.fnmatch_lines("""
+ X:*site-packages*
+ """)
diff --git a/tox.egg-info/PKG-INFO b/tox.egg-info/PKG-INFO
new file mode 100644
index 0000000..3335f00
--- /dev/null
+++ b/tox.egg-info/PKG-INFO
@@ -0,0 +1,52 @@
+Metadata-Version: 1.1
+Name: tox
+Version: 1.5.0
+Summary: virtualenv-based automation of test activities
+Home-page: http://tox.testrun.org/
+Author: holger krekel
+Author-email: holger at merlinux.eu
+License: http://opensource.org/licenses/MIT
+Description:
+ What is Tox?
+ --------------------
+
+ Tox as is a generic virtualenv_ management and test command line tool you can use for:
+
+ * checking your package installs correctly with different Python versions and
+ interpreters
+
+ * running your tests in each of the environments, configuring your test tool of choice
+
+ * acting as a frontend to Continuous Integration servers, greatly
+ reducing boilerplate and merging CI and shell-based testing.
+
+ For more information and the repository please checkout:
+
+ - homepage: http://tox.testrun.org
+
+ - repository: https://bitbucket.org/hpk42/tox
+
+ have fun,
+
+
+ have fun,
+
+ holger krekel, May 2013
+
+
+Platform: unix
+Platform: linux
+Platform: osx
+Platform: cygwin
+Platform: win32
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Topic :: Software Development :: Testing
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: Utilities
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
diff --git a/tox.egg-info/SOURCES.txt b/tox.egg-info/SOURCES.txt
new file mode 100644
index 0000000..f8c73be
--- /dev/null
+++ b/tox.egg-info/SOURCES.txt
@@ -0,0 +1,57 @@
+CHANGELOG
+CONTRIBUTORS
+ISSUES.txt
+LICENSE
+MANIFEST.in
+README.rst
+setup.py
+tox.ini
+toxbootstrap.py
+doc/Makefile
+doc/changelog.txt
+doc/check_sphinx.py
+doc/conf.py
+doc/config-v2.txt
+doc/config.txt
+doc/examples.txt
+doc/index.txt
+doc/install.txt
+doc/links.txt
+doc/support.txt
+doc/_static/sphinxdoc.css
+doc/_templates/indexsidebar.html
+doc/_templates/layout.html
+doc/_templates/localtoc.html
+doc/announce/release-0.5.txt
+doc/announce/release-1.0.txt
+doc/announce/release-1.1.txt
+doc/announce/release-1.2.txt
+doc/announce/release-1.3.txt
+doc/announce/release-1.4.3.txt
+doc/announce/release-1.4.txt
+doc/example/basic.txt
+doc/example/general.txt
+doc/example/jenkins.txt
+doc/example/nose.txt
+doc/example/pytest.txt
+doc/example/unittest.txt
+tests/conftest.py
+tests/test_config.py
+tests/test_quickstart.py
+tests/test_venv.py
+tests/test_z_cmdline.py
+tox/__init__.py
+tox/_cmdline.py
+tox/_config.py
+tox/_exception.py
+tox/_pytestplugin.py
+tox/_quickstart.py
+tox/_venv.py
+tox/_verlib.py
+tox.egg-info/PKG-INFO
+tox.egg-info/SOURCES.txt
+tox.egg-info/dependency_links.txt
+tox.egg-info/entry_points.txt
+tox.egg-info/requires.txt
+tox.egg-info/top_level.txt
+tox.egg-info/zip-safe
\ No newline at end of file
diff --git a/tox.egg-info/dependency_links.txt b/tox.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/tox.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/tox.egg-info/entry_points.txt b/tox.egg-info/entry_points.txt
new file mode 100644
index 0000000..0578f7a
--- /dev/null
+++ b/tox.egg-info/entry_points.txt
@@ -0,0 +1,4 @@
+[console_scripts]
+tox=tox:cmdline
+tox-quickstart=tox._quickstart:main
+
diff --git a/tox.egg-info/requires.txt b/tox.egg-info/requires.txt
new file mode 100644
index 0000000..279c5e3
--- /dev/null
+++ b/tox.egg-info/requires.txt
@@ -0,0 +1,2 @@
+virtualenv>=1.9.1
+py>=1.4.15
\ No newline at end of file
diff --git a/tox.egg-info/top_level.txt b/tox.egg-info/top_level.txt
new file mode 100644
index 0000000..053148f
--- /dev/null
+++ b/tox.egg-info/top_level.txt
@@ -0,0 +1 @@
+tox
diff --git a/tox.egg-info/zip-safe b/tox.egg-info/zip-safe
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/tox.egg-info/zip-safe
@@ -0,0 +1 @@
+
diff --git a/tox.ini b/tox.ini
index 42d93c8..81cbfa0 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,21 +1,22 @@
[tox]
-envlist=py27,py26,py25,py31,py32,docs
-indexserver =
- testrun = http://pypi.testrun.org
- pypi = http://pypi.python.org/simple
+envlist=py27,py26,py25,py32,py33,docs,pypy
[testenv:X]
commands=echo {posargs}
[testenv]
-commands=py.test --junitxml={envlogdir}/junit-{envname}.xml {posargs}
-deps=pytest
- py
+commands=py.test --instafail --junitxml={envlogdir}/junit-{envname}.xml {posargs}
+deps=pytest==2.3.4
+ pytest-instafail
+
+[testenv:py25]
+setenvs =
+ PIP_INSECURE=True
[testenv:docs]
basepython=python
changedir=doc
-deps=:pypi:sphinx
+deps=sphinx
{[testenv]deps}
commands=
py.test -v \
diff --git a/tox/__init__.py b/tox/__init__.py
index 30fb04a..02f0744 100644
--- a/tox/__init__.py
+++ b/tox/__init__.py
@@ -1,5 +1,5 @@
#
-__version__ = '1.4.2'
+__version__ = '1.5.0'
class exception:
class Error(Exception):
diff --git a/tox/_cmdline.py b/tox/_cmdline.py
index 4d78dc7..e34d84f 100644
--- a/tox/_cmdline.py
+++ b/tox/_cmdline.py
@@ -81,7 +81,7 @@ class Action(object):
f.write("actionid=%s\nmsg=%s\ncmdargs=%r\nenv=%s\n" %(
self.id, self.msg, args, env))
f.flush()
- outpath = py.path.local(f.name)
+ self.popen_outpath = outpath = py.path.local(f.name)
if cwd is None:
# XXX cwd = self.session.config.cwd
cwd = py.path.local()
@@ -92,7 +92,7 @@ class Action(object):
popen.action = self
self._popenlist.append(popen)
try:
- self.report.logpopen(popen)
+ self.report.logpopen(popen, env=env)
try:
out, err = popen.communicate()
except KeyboardInterrupt:
@@ -105,10 +105,11 @@ class Action(object):
if ret:
invoked = " ".join(map(str, popen.args))
if outpath:
- self.report.error("invocation failed, logfile: %s" % outpath)
+ self.report.error("invocation failed, logfile: %s" %
+ outpath)
self.report.error(outpath.read())
raise tox.exception.InvocationError(
- "%s (see %s)" %(invoked, outpath))
+ "%s (see %s)" %(invoked, outpath), ret)
else:
raise tox.exception.InvocationError("%r" %(invoked, ))
return out
@@ -119,6 +120,14 @@ class Action(object):
if sys.platform != "win32" and isinstance(arg, py.path.local):
arg = cwd.bestrelpath(arg)
newargs.append(str(arg))
+
+ #subprocess does not always take kindly to .py scripts
+ #so adding the interpreter here.
+ if sys.platform == "win32":
+ ext = os.path.splitext(str(newargs[0]))[1].lower()
+ if ext == '.py' and self.venv:
+ newargs = [str(self.venv.getcommandpath())] + newargs
+
return newargs
def _popen(self, args, cwd, stdout, stderr, env=None):
@@ -128,14 +137,17 @@ class Action(object):
return self.session.popen(args, shell=False, cwd=str(cwd),
stdout=stdout, stderr=stderr, env=env)
+
+
class Reporter(object):
actionchar = "-"
def __init__(self, session):
self.tw = py.io.TerminalWriter()
self.session = session
+ self._reportedlines = []
#self.cumulated_time = 0.0
- def logpopen(self, popen):
+ def logpopen(self, popen, env):
""" log information about the action.popen() created process. """
cmd = " ".join(map(str, popen.args))
if popen.outpath:
@@ -170,7 +182,7 @@ class Reporter(object):
def keyboard_interrupt(self):
- self.tw.line("KEYBOARDINTERRUPT", red=True)
+ self.error("KEYBOARDINTERRUPT")
# def venv_installproject(self, venv, pkg):
# self.logline("installing to %s: %s" % (venv.envconfig.envname, pkg))
@@ -195,26 +207,26 @@ class Reporter(object):
self.logline("ERROR: " + msg, red=True)
def logline(self, msg, **opts):
+ self._reportedlines.append(msg)
self.tw.line("%s" % msg, **opts)
def verbosity0(self, msg, **opts):
if self.session.config.option.verbosity >= 0:
- self.tw.line("%s" % msg, **opts)
+ self.logline("%s" % msg, **opts)
def verbosity1(self, msg, **opts):
if self.session.config.option.verbosity >= 1:
- self.tw.line("%s" % msg, **opts)
+ self.logline("%s" % msg, **opts)
def verbosity2(self, msg, **opts):
if self.session.config.option.verbosity >= 2:
- self.tw.line("%s" % msg, **opts)
+ self.logline("%s" % msg, **opts)
#def log(self, msg):
# py.builtin.print_(msg, file=sys.stderr)
class Session:
- passthroughpossible = True
def __init__(self, config, popen=subprocess.Popen, Report=Reporter):
self.config = config
@@ -224,7 +236,6 @@ class Session:
config.logdir.ensure(dir=1)
#self.report.using("logdir %s" %(self.config.logdir,))
self.report.using("tox.ini: %s" %(self.config.toxinipath,))
- self.venvstatus = {}
self._spec2pkg = {}
self._name2venv = {}
try:
@@ -256,12 +267,11 @@ class Session:
return action
def runcommand(self):
- #tw.sep("-", "tox info from %s" % self.options.configfile)
- self.report.using("tox-%s from %s" %(tox.__version__, tox.__file__))
+ self.report.using("tox-%s from %s" %(tox.__version__,
+ tox.__file__))
if self.config.minversion:
minversion = NormalizedVersion(self.config.minversion)
toxversion = NormalizedVersion(tox.__version__)
- #self.report.using("requires at least %s" %(minversion,))
if toxversion < minversion:
self.report.error(
"tox version is %s, required is at least %s" %(
@@ -269,6 +279,8 @@ class Session:
raise SystemExit(1)
if self.config.option.showconfig:
self.showconfig()
+ elif self.config.option.listenvs:
+ self.showenvs()
else:
return self.subcommand_test()
@@ -282,9 +294,6 @@ class Session:
target.dirpath().ensure(dir=1)
src.copy(target)
- def setenvstatus(self, venv, msg):
- self.venvstatus[venv.path] = msg
-
def _makesdist(self):
setup = self.config.setupdir.join("setup.py")
if not setup.check():
@@ -307,33 +316,36 @@ class Session:
def setupenv(self, venv):
action = self.newaction(venv, "getenv", venv.envconfig.envdir)
with action:
- self.venvstatus[venv.path] = 0
+ venv.status = 0
try:
status = venv.update(action=action)
except tox.exception.InvocationError:
status = sys.exc_info()[1]
if status:
- self.setenvstatus(venv, status)
+ venv.status = status
self.report.error(str(status))
return False
return True
- def installsdist(self, venv, sdist_path):
- action = self.newaction(venv, "sdist-install", sdist_path)
+ def installpkg(self, venv, sdist_path):
+ action = self.newaction(venv, "installpkg", sdist_path)
with action:
try:
- venv.install_sdist(sdist_path, action)
+ venv.installpkg(sdist_path, action)
return True
except tox.exception.InvocationError:
- self.setenvstatus(venv, sys.exc_info()[1])
+ venv.status = sys.exc_info()[1]
return False
def sdist(self):
- if not self.config.option.sdistonly and self.config.sdistsrc:
- self.report.info("using sdistfile %r, skipping 'sdist' activity " %
- str(self.config.sdistsrc))
- sdist_path = self.config.sdistsrc
+ if not self.config.option.sdistonly and (self.config.sdistsrc or
+ self.config.option.installpkg):
+ sdist_path = self.config.option.installpkg
+ if not sdist_path:
+ sdist_path = self.config.sdistsrc
sdist_path = self._resolve_pkg(sdist_path)
+ self.report.info("using package %r, skipping 'sdist' activity " %
+ str(sdist_path))
else:
try:
sdist_path = self._makesdist()
@@ -357,25 +369,24 @@ class Session:
return
for venv in self.venvlist:
if self.setupenv(venv):
- self.installsdist(venv, sdist_path)
+ self.installpkg(venv, sdist_path)
self.runtestenv(venv, sdist_path)
retcode = self._summary()
return retcode
def runtestenv(self, venv, sdist_path, redirect=False):
if not self.config.option.notest:
- if self.venvstatus[venv.path]:
+ if venv.status:
return
- if venv.test(redirect=redirect):
- self.setenvstatus(venv, "commands failed")
+ venv.test(redirect=redirect)
else:
- self.setenvstatus(venv, "skipped tests")
+ venv.status = "skipped tests"
def _summary(self):
self.report.startsummary()
retcode = 0
for venv in self.venvlist:
- status = self.venvstatus[venv.path]
+ status = venv.status
if status and status != "skipped tests":
msg = " %s: %s" %(venv.envconfig.envname, str(status))
self.report.error(msg)
@@ -383,7 +394,8 @@ class Session:
else:
if not status:
status = "commands succeeded"
- self.report.good(" %s: %s" %(venv.envconfig.envname, status))
+ self.report.good(" %s: %s" %(venv.envconfig.envname,
+ status))
if not retcode:
self.report.good(" congratulations :)")
return retcode
@@ -413,6 +425,10 @@ class Session:
self.report.line(" envdir= %s" % envconfig.envdir)
self.report.line(" downloadcache=%s" % envconfig.downloadcache)
+ def showenvs(self):
+ for env in self.config.envlist:
+ self.report.line("%s" % env)
+
def info_versions(self):
versions = ['tox-%s' % tox.__version__]
version = py.process.cmdexec("virtualenv --version")
diff --git a/tox/_config.py b/tox/_config.py
index 80eb38e..78d5fcd 100644
--- a/tox/_config.py
+++ b/tox/_config.py
@@ -1,17 +1,25 @@
import argparse
+import distutils.sysconfig
import os
import sys
import re
import shlex
import string
+import subprocess
+import textwrap
import py
import tox
+
defaultenvs = {'jython': 'jython', 'pypy': 'pypy'}
-for _name in "py24,py25,py26,py27,py30,py31,py32,py33".split(","):
- defaultenvs[_name] = "python%s.%s" %(_name[2], _name[3])
+for _name in "py,py24,py25,py26,py27,py30,py31,py32,py33,py34".split(","):
+ if _name == "py":
+ basepython = sys.executable
+ else:
+ basepython = "python" + ".".join(_name[2:4])
+ defaultenvs[_name] = basepython
def parseconfig(args=None, pkg=None):
if args is None:
@@ -20,7 +28,22 @@ def parseconfig(args=None, pkg=None):
opts = parser.parse_args(args)
config = Config()
config.option = opts
- parseini(config)
+ basename = config.option.configfile
+ if os.path.isabs(basename):
+ inipath = py.path.local(basename)
+ else:
+ for path in py.path.local().parts(reverse=True):
+ inipath = path.join(basename)
+ if inipath.check():
+ break
+ else:
+ feedback("toxini file %r not found" %(basename), sysexit=True)
+ try:
+ parseini(config, inipath)
+ except tox.exception.InterpreterNotFound:
+ exn = sys.exc_info()[1]
+ # Use stdout to match test expectations
+ py.builtin.print_("ERROR: " + str(exn))
return config
def feedback(msg, sysexit=False):
@@ -55,9 +78,11 @@ def prepare_parse(pkgname):
help="increase verbosity of reporting output.")
parser.add_argument("--showconfig", action="store_true", dest="showconfig",
help="show configuration information. ")
+ parser.add_argument("-l", "--listenvs", action="store_true",
+ dest="listenvs", help="show list of test environments")
parser.add_argument("-c", action="store", default="tox.ini",
dest="configfile",
- help="use the specified config file.")
+ help="use the specified config file name.")
parser.add_argument("-e", action="store", dest="env",
metavar="envlist",
help="work against specified environments (ALL selects all).")
@@ -65,6 +90,8 @@ def prepare_parse(pkgname):
help="skip invoking test commands.")
parser.add_argument("--sdistonly", action="store_true", dest="sdistonly",
help="only perform the sdist packaging activity.")
+ parser.add_argument("--installpkg", action="store", default=None,
+ help="use specified package for installation into venv")
parser.add_argument('-i', action="append",
dest="indexurl", metavar="URL",
help="set indexserver url (if URL is of form name=url set the "
@@ -87,7 +114,8 @@ class VenvConfig:
@property
def envbindir(self):
- if sys.platform == "win32" and "jython" not in self.basepython:
+ if (sys.platform == "win32" and "jython" not in self.basepython
+ and "pypy" not in self.basepython):
return self.envdir.join("Scripts")
else:
return self.envdir.join("bin")
@@ -100,17 +128,52 @@ class VenvConfig:
name = "python"
return self.envbindir.join(name)
+ # no @property to avoid early calling (see callable(subst[key]) checks)
+ def envsitepackagesdir(self):
+ print_envsitepackagesdir = textwrap.dedent("""
+ import sys
+ from distutils.sysconfig import get_python_lib
+ sys.stdout.write(get_python_lib(prefix=sys.argv[1]))
+ """)
+
+ exe = self.getsupportedinterpreter()
+ # can't use check_output until py27
+ proc = subprocess.Popen(
+ [str(exe), '-c', print_envsitepackagesdir, str(self.envdir)],
+ stdout=subprocess.PIPE)
+ odata, edata = proc.communicate()
+ if proc.returncode:
+ raise tox.exception.UnsupportedInterpreter(
+ "Error getting site-packages from %s" % self.basepython)
+ return odata
+
+ def getconfigexecutable(self):
+ from tox._venv import find_executable
+
+ python = self.basepython
+ if not python:
+ python = sys.executable
+ x = find_executable(str(python))
+ if x:
+ x = x.realpath()
+ return x
+
+ def getsupportedinterpreter(self):
+ if sys.platform == "win32" and self.basepython and \
+ "jython" in self.basepython:
+ raise tox.exception.UnsupportedInterpreter(
+ "Jython/Windows does not support installing scripts")
+ config_executable = self.getconfigexecutable()
+ if not config_executable:
+ raise tox.exception.InterpreterNotFound(self.basepython)
+ return config_executable
testenvprefix = "testenv:"
class parseini:
- def __init__(self, config):
- config.option.configfile = py.path.local(config.option.configfile)
- config.toxinipath = config.option.configfile
+ def __init__(self, config, inipath):
+ config.toxinipath = inipath
config.toxinidir = toxinidir = config.toxinipath.dirpath()
- if not config.toxinipath.check():
- feedback("toxini file %r does not exist" %(
- str(config.toxinipath)), sysexit=True)
self._cfg = py.iniconfig.IniConfig(config.toxinipath)
config._cfg = self._cfg
self.config = config
@@ -140,9 +203,10 @@ class parseini:
name, url = map(lambda x: x.strip(), line.split("=", 1))
config.indexserver[name] = IndexServerConfig(name, url)
+ override = False
if config.option.indexurl:
for urldef in config.option.indexurl:
- m = re.match(r"(\w+)=(\S+)", urldef)
+ m = re.match(r"\W*(\w+)=(\S+)", urldef)
if m is None:
url = urldef
name = "default"
@@ -150,7 +214,14 @@ class parseini:
name, url = m.groups()
if not url:
url = None
- config.indexserver[name].url = url
+ if name != "ALL":
+ config.indexserver[name].url = url
+ else:
+ override = url
+ # let ALL override all existing entries
+ if override:
+ for name in config.indexserver:
+ config.indexserver[name] = IndexServerConfig(name, override)
reader.addsubstitions(toxworkdir=config.toxworkdir)
config.distdir = reader.getpath(toxsection, "distdir",
@@ -195,7 +266,8 @@ class parseini:
bp = sys.executable
vc.basepython = reader.getdefault(section, "basepython", bp)
reader.addsubstitions(envdir=vc.envdir, envname=vc.envname,
- envbindir=vc.envbindir, envpython=vc.envpython)
+ envbindir=vc.envbindir, envpython=vc.envpython,
+ envsitepackagesdir=vc.envsitepackagesdir)
vc.envtmpdir = reader.getpath(section, "tmpdir", "{envdir}/tmp")
vc.envlogdir = reader.getpath(section, "envlogdir", "{envdir}/log")
reader.addsubstitions(envlogdir=vc.envlogdir, envtmpdir=vc.envtmpdir)
@@ -219,6 +291,8 @@ class parseini:
vc.setenv = None
vc.commands = reader.getargvlist(section, "commands")
+ vc.whitelist_externals = reader.getlist(section,
+ "whitelist_externals")
vc.deps = []
for depline in reader.getlist(section, "deps"):
m = re.match(r":(\w+):\s*(\S+)", depline)
@@ -229,14 +303,14 @@ class parseini:
name = depline.strip()
ixserver = None
vc.deps.append(DepConfig(name, ixserver))
- vc.distribute = reader.getbool(section, "distribute", True)
+ vc.distribute = reader.getbool(section, "distribute", False)
vc.sitepackages = reader.getbool(section, "sitepackages", False)
- downloadcache = reader.getdefault(section, "downloadcache")
- if downloadcache is None:
- downloadcache = os.environ.get("PIP_DOWNLOAD_CACHE", "")
- if not downloadcache:
- downloadcache = self.config.toxworkdir.join("_download")
- vc.downloadcache = py.path.local(downloadcache)
+ vc.downloadcache = None
+ downloadcache = os.environ.get("PIP_DOWNLOAD_CACHE", None)
+ if not downloadcache:
+ downloadcache = reader.getdefault(section, "downloadcache")
+ if downloadcache:
+ vc.downloadcache = py.path.local(downloadcache)
return vc
def _getenvlist(self, reader, toxsection):
@@ -273,6 +347,16 @@ class IndexServerConfig:
self.name = name
self.url = url
+RE_ITEM_REF = re.compile(
+ '''
+ [{]
+ (?:(?P<sub_type>[^[:{}]+):)? # optional sub_type for special rules
+ (?P<substitution_value>[^{}]*) # substitution key
+ [}]
+ ''',
+ re.VERBOSE)
+
+
class IniReader:
def __init__(self, cfgparser, fallbacksections=None):
self._cfg = cfgparser
@@ -339,7 +423,6 @@ class IniReader:
def _processcommand(self, command):
posargs = self._subs.get('_posargs', None)
- pat = r'\{(?:(?P<sub_type>[^:]+):)?(?P<substitution_value>.*)\}'
words = list(CommandParser(command).words())
new_command = ''
for word in words:
@@ -348,9 +431,9 @@ class IniReader:
new_command += ' '.join(posargs)
continue
- new_word = re.sub(pat, self._replace_match, word)
+ new_word = self._replace(word, quote=True)
# two passes; we might have substitutions in the result
- new_word = re.sub(pat, self._replace_match, new_word)
+ new_word = self._replace(new_word, quote=True)
new_command += new_word
return shlex.split(new_command.strip())
@@ -392,35 +475,7 @@ class IniReader:
#print "getdefault", section, name, "returned", repr(x)
return x
- def _sub(self, match):
- key = match.group(0)[1:-1]
- if key.startswith("env:"):
- envkey = key[4:]
- if envkey not in os.environ:
- raise tox.exception.ConfigError(
- "substitution %r: %r not found in environment" %
- (key, envkey))
- return os.environ[envkey]
- if key not in self._subs:
- if key.startswith("[") and "]" in key:
- i = key.find("]")
- section, item = key[1:i], key[i+1:]
- if section in self._cfg and item in self._cfg[section]:
- if (section, item) in self._subststack:
- raise ValueError('%s already in %s' %(
- (section, item), self._subststack))
- x = str(self._cfg[section][item])
- self._subststack.append((section, item))
- try:
- return self._replace(x)
- finally:
- self._subststack.pop()
-
- raise tox.exception.ConfigError(
- "substitution key %r not found" % key)
- return str(self._subs[key])
-
- def _replace_posargs(self, match):
+ def _replace_posargs(self, match, quote):
return self._do_replace_posargs(lambda: match.group('substitution_value'))
def _do_replace_posargs(self, value_func):
@@ -435,7 +490,7 @@ class IniReader:
return ''
- def _replace_env(self, match):
+ def _replace_env(self, match, quote):
envkey = match.group('substitution_value')
if not envkey:
raise tox.exception.ConfigError(
@@ -448,18 +503,41 @@ class IniReader:
return os.environ[envkey]
- def _replace_substitution(self, match):
+ def _substitute_from_other_section(self, key, quote):
+ if key.startswith("[") and "]" in key:
+ i = key.find("]")
+ section, item = key[1:i], key[i+1:]
+ if section in self._cfg and item in self._cfg[section]:
+ if (section, item) in self._subststack:
+ raise ValueError('%s already in %s' %(
+ (section, item), self._subststack))
+ x = str(self._cfg[section][item])
+ self._subststack.append((section, item))
+ try:
+ return self._replace(x, quote=quote)
+ finally:
+ self._subststack.pop()
+
+ raise tox.exception.ConfigError(
+ "substitution key %r not found" % key)
+
+ def _replace_substitution(self, match, quote):
sub_key = match.group('substitution_value')
- if sub_key not in self._subs:
- raise tox.exception.ConfigError(
- "substitution key %r not found" % sub_key)
- return '"%s"' % str(self._subs[sub_key]).replace('"', r'\"')
+ val = self._subs.get(sub_key, None)
+ if val is None:
+ val = self._substitute_from_other_section(sub_key, quote)
+ if py.builtin.callable(val):
+ val = val()
+ if quote:
+ return '"%s"' % str(val).replace('"', r'\"')
+ else:
+ return str(val)
def _is_bare_posargs(self, groupdict):
return groupdict.get('substitution_value', None) == 'posargs' \
and not groupdict.get('sub_type')
- def _replace_match(self, match):
+ def _replace_match(self, match, quote):
g = match.groupdict()
# special case: posargs. If there is a 'posargs' substitution value
@@ -482,11 +560,23 @@ class IniReader:
except KeyError:
raise tox.exception.ConfigError("No support for the %s substitution type" % sub_type)
- return handler(match)
+ # quoting is done in handlers, as at least posargs handling is special:
+ # all of its arguments are inserted as separate parameters
+ return handler(match, quote)
- def _replace(self, x, rexpattern = re.compile("\{.+?\}")):
+ def _replace_match_quote(self, match):
+ return self._replace_match(match, quote=True)
+ def _replace_match_no_quote(self, match):
+ return self._replace_match(match, quote=False)
+
+ def _replace(self, x, rexpattern=RE_ITEM_REF, quote=False):
+ # XXX is rexpattern used by callers? can it be removed?
if '{' in x:
- return rexpattern.sub(self._sub, x)
+ if quote:
+ replace_func = self._replace_match_quote
+ else:
+ replace_func = self._replace_match_no_quote
+ return rexpattern.sub(replace_func, x)
return x
def _parse_command(self, command):
diff --git a/tests/conftest.py b/tox/_pytestplugin.py
similarity index 94%
copy from tests/conftest.py
copy to tox/_pytestplugin.py
index 51bc0a2..1c0024b 100644
--- a/tests/conftest.py
+++ b/tox/_pytestplugin.py
@@ -1,4 +1,4 @@
-import py
+import pytest, py
import tox
import os
import sys
@@ -18,8 +18,7 @@ def pytest_configure():
def pytest_report_header():
return "tox comes from: %r" % (tox.__file__)
-def pytest_funcarg__newconfig(request):
- tmpdir = request.getfuncargvalue("tmpdir")
+def pytest_funcarg__newconfig(request, tmpdir):
def newconfig(args, source=None):
if source is None:
source = args
@@ -34,12 +33,6 @@ def pytest_funcarg__newconfig(request):
old.chdir()
return newconfig
-def pytest_funcarg__tmpdir(request):
- tmpdir = request.getfuncargvalue("tmpdir")
- request.addfinalizer(py.path.local().chdir)
- tmpdir.chdir()
- return tmpdir
-
def pytest_funcarg__cmd(request):
return Cmd(request)
@@ -106,7 +99,8 @@ class ReportExpectMock:
class pcallMock:
def __init__(self, args, cwd, env, stdout, stderr, shell):
- self.args = args
+ self.arg0 = args[0]
+ self.args = args[1:]
self.cwd = cwd
self.env = env
self.stdout = stdout
@@ -249,9 +243,9 @@ class LineMatcher:
else:
assert line == nextline
-def pytest_funcarg__initproj(request):
+ at pytest.fixture
+def initproj(request, tmpdir):
""" create a factory function for creating example projects. """
- tmpdir = request.getfuncargvalue("tmpdir")
def initproj(name, filedefs=None):
if filedefs is None:
filedefs = {}
@@ -268,14 +262,18 @@ def pytest_funcarg__initproj(request):
name='%(name)s',
description='%(name)s project',
version='%(version)s',
- license='GPLv2 or later',
+ license='MIT',
platforms=['unix', 'win32'],
packages=['%(name)s', ],
)
''' % locals()})
if name not in filedefs:
create_files(base, {name:
- {'__init__.py': '__version__ = %s' % version}})
+ {'__init__.py': '__version__ = %r' % version}})
+ manifestlines = []
+ for p in base.visit(lambda x: x.check(file=1)):
+ manifestlines.append("include %s" % p.relto(base))
+ create_files(base, {"MANIFEST.in": "\n".join(manifestlines)})
print ("created project in %s" %(base,))
base.chdir()
return initproj
diff --git a/tox/_quickstart.py b/tox/_quickstart.py
new file mode 100644
index 0000000..4b39d04
--- /dev/null
+++ b/tox/_quickstart.py
@@ -0,0 +1,266 @@
+# -*- coding: utf-8 -*-
+"""
+ tox._quickstart
+ ~~~~~~~~~~~~~~~~~
+
+ Command-line script to quickly setup tox.ini for a Python project
+
+ This file was heavily inspired by and uses code from ``sphinx-quickstart``
+ in the BSD-licensed `Sphinx project`_.
+
+ .. Sphinx project_: http://sphinx.pocoo.org/
+
+ License for Sphinx
+ ==================
+
+ Copyright (c) 2007-2011 by the Sphinx team (see AUTHORS file).
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+import sys
+from os import path
+from codecs import open
+
+TERM_ENCODING = getattr(sys.stdin, 'encoding', None)
+
+from tox import __version__
+
+# function to get input from terminal -- overridden by the test suite
+try:
+ # this raw_input is not converted by 2to3
+ term_input = raw_input
+except NameError:
+ term_input = input
+
+
+all_envs = ['py24', 'py25', 'py26', 'py27', 'py30', 'py31', 'py32', 'py33', 'pypy', 'jython']
+
+PROMPT_PREFIX = '> '
+
+QUICKSTART_CONF = '''\
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = %(envlist)s
+
+[testenv]
+commands = %(commands)s
+deps = %(deps)s
+'''
+
+
+class ValidationError(Exception):
+ """Raised for validation errors."""
+
+def nonempty(x):
+ if not x:
+ raise ValidationError("Please enter some text.")
+ return x
+
+def choice(*l):
+ def val(x):
+ if x not in l:
+ raise ValidationError('Please enter one of %s.' % ', '.join(l))
+ return x
+ return val
+
+def boolean(x):
+ if x.upper() not in ('Y', 'YES', 'N', 'NO'):
+ raise ValidationError("Please enter either 'y' or 'n'.")
+ return x.upper() in ('Y', 'YES')
+
+def suffix(x):
+ if not (x[0:1] == '.' and len(x) > 1):
+ raise ValidationError("Please enter a file suffix, "
+ "e.g. '.rst' or '.txt'.")
+ return x
+
+def ok(x):
+ return x
+
+
+def do_prompt(d, key, text, default=None, validator=nonempty):
+ while True:
+ if default:
+ prompt = PROMPT_PREFIX + '%s [%s]: ' % (text, default)
+ else:
+ prompt = PROMPT_PREFIX + text + ': '
+ x = term_input(prompt)
+ if default and not x:
+ x = default
+ if sys.version_info < (3, ) and not isinstance(x, unicode):
+ # for Python 2.x, try to get a Unicode string out of it
+ if x.decode('ascii', 'replace').encode('ascii', 'replace') != x:
+ if TERM_ENCODING:
+ x = x.decode(TERM_ENCODING)
+ else:
+ print('* Note: non-ASCII characters entered '
+ 'and terminal encoding unknown -- assuming '
+ 'UTF-8 or Latin-1.')
+ try:
+ x = x.decode('utf-8')
+ except UnicodeDecodeError:
+ x = x.decode('latin1')
+ try:
+ x = validator(x)
+ except ValidationError:
+ err = sys.exc_info()[1]
+ print('* ' + str(err))
+ continue
+ break
+ d[key] = x
+
+
+def ask_user(d):
+ """Ask the user for quickstart values missing from *d*.
+
+ """
+
+ print('Welcome to the Tox %s quickstart utility.' % __version__)
+ print('''
+This utility will ask you a few questions and then generate a simple tox.ini
+file to help get you started using tox.
+
+Please enter values for the following settings (just press Enter to
+accept a default value, if one is given in brackets).''')
+
+ sys.stdout.write('\n')
+
+ print('''
+What Python versions do you want to test against? Choices:
+ [1] py27
+ [2] py27, py33
+ [3] (All versions) %s
+ [4] Choose each one-by-one''' % ', '.join(all_envs))
+ do_prompt(d, 'canned_pyenvs', 'Enter the number of your choice',
+ '3', choice('1', '2', '3', '4'))
+
+ if d['canned_pyenvs'] == '1':
+ d['py27'] = True
+ elif d['canned_pyenvs'] == '2':
+ for pyenv in ('py27', 'py33'):
+ d[pyenv] = True
+ elif d['canned_pyenvs'] == '3':
+ for pyenv in all_envs:
+ d[pyenv] = True
+ elif d['canned_pyenvs'] == '4':
+ for pyenv in all_envs:
+ if pyenv not in d:
+ do_prompt(d, pyenv, 'Test your project with %s (Y/n)' % pyenv, 'Y', boolean)
+
+ print('''
+What command should be used to test your project -- examples:
+ - py.test
+ - python setup.py test
+ - nosetests package.module
+ - trial package.module''')
+ do_prompt(d, 'commands', 'Command to run to test project', '{envpython} setup.py test')
+
+ default_deps = ' '
+ if 'py.test' in d['commands']:
+ default_deps = 'pytest'
+ if 'nosetests' in d['commands']:
+ default_deps = 'nose'
+ if 'trial' in d['commands']:
+ default_deps = 'twisted'
+
+ print('''
+What dependencies does your project have?''')
+ do_prompt(d, 'deps', 'Comma-separated list of dependencies', default_deps)
+
+
+def process_input(d):
+ d['envlist'] = ', '.join([env for env in all_envs if d.get(env) is True])
+ d['deps'] = '\n' + '\n'.join([' %s' % dep.strip()
+ for dep in d['deps'].split(',')])
+
+ return d
+
+def rtrim_right(text):
+ lines = []
+ for line in text.split("\n"):
+ lines.append(line.rstrip())
+ return "\n".join(lines)
+
+def generate(d, overwrite=True, silent=False):
+ """Generate project based on values in *d*."""
+
+ conf_text = QUICKSTART_CONF % d
+ conf_text = rtrim_right(conf_text)
+
+ def write_file(fpath, mode, content):
+ print('Creating file %s.' % fpath)
+ f = open(fpath, mode, encoding='utf-8')
+ try:
+ f.write(content)
+ finally:
+ f.close()
+
+ sys.stdout.write('\n')
+
+ fpath = 'tox.ini'
+
+ if path.isfile(fpath) and not overwrite:
+ print('File %s already exists.' % fpath)
+ do_prompt(d, 'fpath', 'Alternative path to write tox.ini contents to', 'tox-generated.ini')
+ fpath = d['fpath']
+
+ write_file(fpath, 'w', conf_text)
+
+ if silent:
+ return
+ sys.stdout.write('\n')
+ print('Finished: A tox.ini file has been created. For information on this file, see http://tox.testrun.org/latest/config.html')
+ print('''
+Execute `tox` to test your project.
+''')
+
+
+def main(argv=sys.argv):
+ d = {}
+
+ if len(argv) > 3:
+ print('Usage: tox-quickstart [root]')
+ sys.exit(1)
+ elif len(argv) == 2:
+ d['path'] = argv[1]
+
+ try:
+ ask_user(d)
+ except (KeyboardInterrupt, EOFError):
+ print()
+ print('[Interrupted.]')
+ return
+
+ d = process_input(d)
+ generate(d, overwrite=False)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tox/_venv.py b/tox/_venv.py
index 2574c21..5008c00 100644
--- a/tox/_venv.py
+++ b/tox/_venv.py
@@ -1,5 +1,5 @@
from __future__ import with_statement
-import sys, os
+import sys, os, re
import py
import tox
from tox._config import DepConfig
@@ -74,21 +74,38 @@ class VirtualEnv(object):
p = cwd.join(name)
if p.check():
return str(p)
+ p = None
+ if venv:
+ p = py.path.local.sysfind(name, paths=[self.envconfig.envbindir])
+ if p is not None:
+ return p
p = py.path.local.sysfind(name)
if p is None:
- raise tox.exception.InvocationError("could not find executable %r"
- % (name,))
- if p.relto(self.envconfig.envdir):
- return p
+ raise tox.exception.InvocationError(
+ "could not find executable %r" % (name,))
+ # p is not found in virtualenv script/bin dir
if venv:
- self.session.report.warning(
- "test command found but not installed in testenv\n"
- " cmd: %s\n"
- " env: %s\n"
- "Maybe forgot to specify a dependency?" % (p,
- self.envconfig.envdir))
+ if not self.is_allowed_external(p):
+ self.session.report.warning(
+ "test command found but not installed in testenv\n"
+ " cmd: %s\n"
+ " env: %s\n"
+ "Maybe forgot to specify a dependency?" % (p,
+ self.envconfig.envdir))
return str(p) # will not be rewritten for reporting
+ def is_allowed_external(self, p):
+ tryadd = [""]
+ if sys.platform == "win32":
+ tryadd += [os.path.normcase(x)
+ for x in os.environ['PATHEXT'].split(os.pathsep)]
+ p = py.path.local(os.path.normcase(str(p)))
+ for x in self.envconfig.whitelist_externals:
+ for add in tryadd:
+ if p.fnmatch(x + add):
+ return True
+ return False
+
def _ispython3(self):
return "python3" in str(self.envconfig.basepython)
@@ -146,23 +163,10 @@ class VirtualEnv(object):
return l
def getconfigexecutable(self):
- python = self.envconfig.basepython
- if not python:
- python = sys.executable
- x = find_executable(str(python))
- if x:
- x = x.realpath()
- return x
+ return self.envconfig.getconfigexecutable()
def getsupportedinterpreter(self):
- if sys.platform == "win32" and self.envconfig.basepython and \
- "jython" in self.envconfig.basepython:
- raise tox.exception.UnsupportedInterpreter(
- "Jython/Windows does not support installing scripts")
- config_executable = self.getconfigexecutable()
- if not config_executable:
- raise tox.exception.InterpreterNotFound(self.envconfig.basepython)
- return config_executable
+ return self.envconfig.getsupportedinterpreter()
def create(self, action=None):
#if self.getcommandpath("activate").dirpath().check():
@@ -175,8 +179,10 @@ class VirtualEnv(object):
venvscript = path.rstrip("co")
#venvscript = py.path.local(tox.__file__).dirpath("virtualenv.py")
args = [config_interpreter, venvscript]
- if not self._ispython3() and self.envconfig.distribute:
- args.append('--distribute')
+ if self.envconfig.distribute:
+ args.append("--distribute")
+ else:
+ args.append("--setuptools")
if self.envconfig.sitepackages:
args.append('--system-site-packages')
#if sys.platform == "win32":
@@ -191,14 +197,14 @@ class VirtualEnv(object):
self._pcall(args, venv=False, action=action, cwd=basepath)
self.just_created = True
- def install_sdist(self, sdistpath, action):
+ def installpkg(self, sdistpath, action):
assert action is not None
if getattr(self, 'just_created', False):
- action.setactivity("sdist-inst", sdistpath)
+ action.setactivity("inst", sdistpath)
self._getliveconfig().writeconfig(self.path_config)
extraopts = []
else:
- action.setactivity("sdist-reinst", sdistpath)
+ action.setactivity("inst-nodeps", sdistpath)
extraopts = ['-U', '--no-deps']
self._install([sdistpath], extraopts=extraopts, action=action)
@@ -224,6 +230,9 @@ class VirtualEnv(object):
def pip_install(self, args, indexserver=None, action=None):
argv = ["pip", "install"] + self._commoninstallopts(indexserver)
+ # use pip-script on win32 to avoid the executable locking
+ if sys.platform == "win32":
+ argv[0] = "pip-script.py"
if self.envconfig.downloadcache:
self.envconfig.downloadcache.ensure(dir=1)
argv.append("--download-cache=%s" %
@@ -251,16 +260,15 @@ class VirtualEnv(object):
ixserver = self.envconfig.config.indexserver['default']
else:
ixserver = dep.indexserver
- url = ixserver.url
- d.setdefault(url, []).append(dep.name)
- if url not in l:
- l.append(url)
- assert url is None or isinstance(url, str)
+ d.setdefault(ixserver, []).append(dep.name)
+ if ixserver not in l:
+ l.append(ixserver)
+ assert ixserver.url is None or isinstance(ixserver.url, str)
extraopts = extraopts or []
- for repo in l:
- args = d[repo] + extraopts
- self.pip_install(args, repo, action)
+ for ixserver in l:
+ args = d[ixserver] + extraopts
+ self.pip_install(args, ixserver.url, action)
def _getenv(self):
env = self.envconfig.setenv
@@ -274,22 +282,25 @@ class VirtualEnv(object):
def test(self, redirect=False):
action = self.session.newaction(self, "runtests")
with action:
+ self.status = 0
self.session.make_emptydir(self.envconfig.envtmpdir)
cwd = self.envconfig.changedir
for i, argv in enumerate(self.envconfig.commands):
- action.setactivity("runtests", "commands[%s]" % i)
+ message = "commands[%s] | %s" % (i, ' '.join(argv))
+ action.setactivity("runtests", message)
try:
self._pcall(argv, cwd=cwd, action=action, redirect=redirect)
except tox.exception.InvocationError:
- self.session.report.error(str(sys.exc_info()[1]))
- return True
+ val = sys.exc_info()[1]
+ self.session.report.error(str(val))
+ self.status = "commands failed"
+ except KeyboardInterrupt:
+ self.status = "keyboardinterrupt"
+ self.session.report.error(self.status)
+ raise
def _pcall(self, args, venv=True, cwd=None, extraenv={},
action=None, redirect=True):
- try:
- del os.environ['PYTHONDONTWRITEBYTECODE']
- except KeyError:
- pass
assert cwd
cwd.ensure(dir=1)
old = self.patchPATH()
@@ -318,22 +329,42 @@ if sys.platform != "win32":
return py.path.local.sysfind(name)
else:
+ # Exceptions to the usual windows mapping
win32map = {
'python': sys.executable,
- 'python2.4': "c:\python24\python.exe",
- 'python2.5': "c:\python25\python.exe",
- 'python2.6': "c:\python26\python.exe",
- 'python2.7': "c:\python27\python.exe",
- 'python3.1': "c:\python31\python.exe",
- 'python3.2': "c:\python32\python.exe",
'jython': "c:\jython2.5.1\jython.bat",
}
+ def locate_via_py(v_maj, v_min):
+ ver = "-%s.%s" % (v_maj, v_min)
+ script = "import sys; print(sys.executable)"
+ py_exe = py.path.local.sysfind('py')
+ if py_exe:
+ try:
+ exe = py_exe.sysexec(ver, '-c', script).strip()
+ except py.process.cmdexec.Error:
+ exe = None
+ if exe:
+ exe = py.path.local(exe)
+ if exe.check():
+ return exe
+
def find_executable(name):
- p = py.path.local(name)
- if p.check(file=1):
+ p = py.path.local.sysfind(name)
+ if p:
return p
- actual = win32map.get(name, None)
+ actual = None
+ # Is this a standard PythonX.Y name?
+ m = re.match(r"python(\d)\.(\d)", name)
+ if m:
+ # The standard names are in predictable places.
+ actual = r"c:\python%s%s\python.exe" % m.groups()
+ if not actual:
+ actual = win32map.get(name, None)
if actual:
actual = py.path.local(actual)
if actual.check():
return actual
+ # The standard executables can be found as a last resort via the
+ # Python launcher py.exe
+ if m:
+ locate_via_py(*m.groups())
diff --git a/toxbootstrap.py b/toxbootstrap.py
index df53f45..37ddef5 100644
--- a/toxbootstrap.py
+++ b/toxbootstrap.py
@@ -58,7 +58,7 @@ ToDo
"""
-__version__ = '1.4.2'
+__version__ = '1.5.0'
import sys
import os
@@ -192,7 +192,7 @@ def cmdline(argv=None):
# XXX: we use --no-site-packages because: if tox is installed in global
# site-packages, then pip will not install it locally. ideal fix for
# this should be to first look for tox in the global scripts/ directory
- run('%s virtualenv.py --no-site-packages --distribute %s' %
+ run('%s virtualenv.py --no-site-packages --setuptools %s' %
(sys.executable, TENV))
logging.info("removing virtualenv.py script after bootstrap venv creation")
for x in ('', 'o', 'c'):
@@ -209,11 +209,6 @@ def cmdline(argv=None):
# install/upgrade tox itself
if USETOXDEV:
- if 'PIP_DOWNLOAD_CACHE' in os.environ:
- cache = ""
- else:
- cache = "--download-cache=_download"
- ensuredir('_download')
run('%s install -q -i http://pypi.testrun.org '
'--upgrade %s tox' % (pip, cache))
elif any([
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/tox.git
More information about the Python-modules-commits
mailing list