2 new commits in tox:

https://bitbucket.org/hpk42/tox/commits/850fb37c6062/
Changeset:   850fb37c6062
User:        hpk42
Date:        2015-05-08 11:16:20+00:00
Summary:     Backed out changeset cc1933175162
Affected #:  13 files

diff -r cc1933175162c4bb01669912a57df6fba4be5780 -r 
850fb37c60625112b2bf5ad21373d2804be98336 doc/conf.py
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -48,8 +48,8 @@
 # built documents.
 #
 # The short X.Y version.
-release = "2.0"
-version = "2.0.0"
+release = "1.9"
+version = "1.9.0"
 # The full version, including alpha/beta/rc tags.
 
 # The language for content autogenerated by Sphinx. Refer to documentation

diff -r cc1933175162c4bb01669912a57df6fba4be5780 -r 
850fb37c60625112b2bf5ad21373d2804be98336 doc/index.txt
--- a/doc/index.txt
+++ b/doc/index.txt
@@ -5,7 +5,7 @@
 ---------------------------------------------
 
 ``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 a larger vision of easing the packaging, testing and release process 
 of Python software.
 
 What is Tox?
@@ -21,7 +21,6 @@
 * acting as a frontend to Continuous Integration servers, greatly
   reducing boilerplate and merging CI and shell-based testing.
 
-
 Basic example
 -----------------
 
@@ -63,10 +62,10 @@
 
     - test-tool agnostic: runs py.test, nose or unittests in a uniform manner
 
-* :doc:`(new in 2.0) plugin system <plugins>` to modify tox execution with 
simple hooks.
+* supports :ref:`using different / multiple PyPI index servers  <multiindex>`
 
 * uses pip_ and setuptools_ by default.  Experimental
-  support for configuring the installer command
+  support for configuring the installer command 
   through :confval:`install_command=ARGV`.
 
 * **cross-Python compatible**: CPython-2.6, 2.7, 3.2 and higher,
@@ -75,11 +74,11 @@
 * **cross-platform**: Windows and Unix style environments
 
 * **integrates with continuous integration servers** like Jenkins_
-  (formerly known as Hudson) and helps you to avoid boilerplatish
+  (formerly known as Hudson) and helps you to avoid boilerplatish 
   and platform-specific build-step hacks.
 
 * **full interoperability with devpi**: is integrated with and
-  is used for testing in the devpi_ system, a versatile pypi
+  is used for testing in the devpi_ system, a versatile pypi 
   index server and release managing tool.
 
 * **driven by a simple ini-style config file**
@@ -90,9 +89,6 @@
 
 * **professionally** :doc:`supported <support>`
 
-* supports :ref:`using different / multiple PyPI index servers  <multiindex>`
-
-
 .. _pypy: http://pypy.org
 
 .. _`tox.ini`: :doc:configfile

diff -r cc1933175162c4bb01669912a57df6fba4be5780 -r 
850fb37c60625112b2bf5ad21373d2804be98336 doc/plugins.txt
--- a/doc/plugins.txt
+++ /dev/null
@@ -1,69 +0,0 @@
-.. be in -*- rst -*- mode!
-
-tox plugins
-===========
-
-.. versionadded:: 2.0
-
-With tox-2.0 a few aspects of tox running can be experimentally modified
-by writing hook functions.  We expect the list of hook function to grow
-over time.
-
-writing a setuptools entrypoints plugin
----------------------------------------
-
-If you have a ``tox_MYPLUGIN.py`` module you could use the following
-rough ``setup.py`` to make it into a package which you can upload to the
-Python packaging index::
-
-    # content of setup.py
-    from setuptools import setup
-
-    if __name__ == "__main__":
-        setup(
-            name='tox-MYPLUGIN',
-            description='tox plugin decsription',
-            license="MIT license",
-            version='0.1',
-            py_modules=['tox_MYPLUGIN'],
-            entry_points={'tox': ['MYPLUGIN = tox_MYPLUGIN']},
-            install_requires=['tox>=2.0'],
-        )
-
-You can then install the plugin to develop it via::
-
-    pip install -e .
-
-and later publish it.
-
-The ``entry_points`` part allows tox to see your plugin during startup.
-
-
-Writing hook implementations
-----------------------------
-
-A plugin module needs can define one or more hook implementation functions::
-
-    from tox import hookimpl
-
-    @hookimpl
-    def tox_addoption(parser):
-        # add your own command line options
-
-
-    @hookimpl
-    def tox_configure(config):
-        # post process tox configuration after cmdline/ini file have
-        # been parsed
-
-If you put this into a module and make it pypi-installable with the ``tox``
-entry point you'll get your code executed as part of a tox run.
-
-
-
-tox hook specifications
-----------------------------
-
-.. automodule:: tox.hookspecs
-    :members:
-

diff -r cc1933175162c4bb01669912a57df6fba4be5780 -r 
850fb37c60625112b2bf5ad21373d2804be98336 setup.py
--- a/setup.py
+++ b/setup.py
@@ -18,7 +18,7 @@
 
 def main():
     version = sys.version_info[:2]
-    install_requires = ['virtualenv>=1.11.2', 'py>=1.4.17', 
'pluggy>=0.3.0,<0.4.0']
+    install_requires = ['virtualenv>=1.11.2', 'py>=1.4.17', ]
     if version < (2, 7):
         install_requires += ['argparse']
     setup(

diff -r cc1933175162c4bb01669912a57df6fba4be5780 -r 
850fb37c60625112b2bf5ad21373d2804be98336 tests/test_config.py
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -6,6 +6,7 @@
 import tox
 import tox._config
 from tox._config import *  # noqa
+from tox._config import _split_env
 from tox._venv import VirtualEnv
 
 
@@ -1560,16 +1561,31 @@
         ])
 
 
-@pytest.mark.parametrize("cmdline,envlist", [
-    ("-e py26", ['py26']),
-    ("-e py26,py33", ['py26', 'py33']),
-    ("-e py26,py26", ['py26', 'py26']),
-    ("-e py26,py33 -e py33,py27", ['py26', 'py33', 'py33', 'py27'])
-])
-def test_env_spec(cmdline, envlist):
-    args = cmdline.split()
-    config = parseconfig(args)
-    assert config.envlist == envlist
+class TestArgumentParser:
+
+    def test_dash_e_single_1(self):
+        parser = prepare_parse('testpkg')
+        args = parser.parse_args('-e py26'.split())
+        envlist = _split_env(args.env)
+        assert envlist == ['py26']
+
+    def test_dash_e_single_2(self):
+        parser = prepare_parse('testpkg')
+        args = parser.parse_args('-e py26,py33'.split())
+        envlist = _split_env(args.env)
+        assert envlist == ['py26', 'py33']
+
+    def test_dash_e_same(self):
+        parser = prepare_parse('testpkg')
+        args = parser.parse_args('-e py26,py26'.split())
+        envlist = _split_env(args.env)
+        assert envlist == ['py26', 'py26']
+
+    def test_dash_e_combine(self):
+        parser = prepare_parse('testpkg')
+        args = parser.parse_args('-e py26,py25,py33 -e py33,py27'.split())
+        envlist = _split_env(args.env)
+        assert envlist == ['py26', 'py25', 'py33', 'py33', 'py27']
 
 
 class TestCommandParser:

diff -r cc1933175162c4bb01669912a57df6fba4be5780 -r 
850fb37c60625112b2bf5ad21373d2804be98336 tests/test_interpreters.py
--- a/tests/test_interpreters.py
+++ b/tests/test_interpreters.py
@@ -3,13 +3,11 @@
 
 import pytest
 from tox.interpreters import *  # noqa
-from tox._config import get_plugin_manager
 
 
 @pytest.fixture
 def interpreters():
-    pm = get_plugin_manager()
-    return Interpreters(hook=pm.hook)
+    return Interpreters()
 
 
 @pytest.mark.skipif("sys.platform != 'win32'")
@@ -30,8 +28,8 @@
     assert locate_via_py('3', '2') == sys.executable
 
 
-def test_tox_get_python_executable():
-    p = tox_get_python_executable(sys.executable)
+def test_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():
         name = "python%s" % ver
@@ -44,7 +42,7 @@
         else:
             if not py.path.local.sysfind(name):
                 continue
-        p = tox_get_python_executable(name)
+        p = find_executable(name)
         assert p
         popen = py.std.subprocess.Popen([str(p), '-V'],
                                         stderr=py.std.subprocess.PIPE)
@@ -57,7 +55,7 @@
     def sysfind(x):
         return "hello"
     monkeypatch.setattr(py.path.local, "sysfind", sysfind)
-    t = tox_get_python_executable("qweqwe")
+    t = find_executable("qweqwe")
     assert t == "hello"
 
 

diff -r cc1933175162c4bb01669912a57df6fba4be5780 -r 
850fb37c60625112b2bf5ad21373d2804be98336 tests/test_venv.py
--- a/tests/test_venv.py
+++ b/tests/test_venv.py
@@ -65,7 +65,7 @@
         # assert Envconfig.toxworkdir in args
         assert venv.getcommandpath("easy_install", cwd=py.path.local())
     interp = venv._getliveconfig().python
-    assert interp == venv.envconfig.python_info.executable
+    assert interp == venv.envconfig._basepython_info.executable
     assert venv.path_config.check(exists=False)
 
 

diff -r cc1933175162c4bb01669912a57df6fba4be5780 -r 
850fb37c60625112b2bf5ad21373d2804be98336 tox.ini
--- a/tox.ini
+++ b/tox.ini
@@ -22,9 +22,7 @@
 deps = pytest-flakes>=0.2
        pytest-pep8
 
-commands = 
-    py.test --flakes -m flakes tox tests
-    py.test --pep8 -m pep8 tox tests
+commands = py.test -x --flakes --pep8 tox tests
 
 [testenv:dev]
 # required to make looponfail reload on every source code change

diff -r cc1933175162c4bb01669912a57df6fba4be5780 -r 
850fb37c60625112b2bf5ad21373d2804be98336 tox/__init__.py
--- a/tox/__init__.py
+++ b/tox/__init__.py
@@ -1,8 +1,6 @@
 #
 __version__ = '2.0.0.dev1'
 
-from .hookspecs import hookspec, hookimpl  # noqa
-
 
 class exception:
     class Error(Exception):

diff -r cc1933175162c4bb01669912a57df6fba4be5780 -r 
850fb37c60625112b2bf5ad21373d2804be98336 tox/_cmdline.py
--- a/tox/_cmdline.py
+++ b/tox/_cmdline.py
@@ -24,7 +24,7 @@
 
 def main(args=None):
     try:
-        config = parseconfig(args)
+        config = parseconfig(args, 'tox')
         retcode = Session(config).runcommand()
         raise SystemExit(retcode)
     except KeyboardInterrupt:
@@ -551,7 +551,8 @@
         for envconfig in self.config.envconfigs.values():
             self.report.line("[testenv:%s]" % envconfig.envname, bold=True)
             self.report.line("  basepython=%s" % envconfig.basepython)
-            self.report.line("  pythoninfo=%s" % (envconfig.python_info,))
+            self.report.line("  _basepython_info=%s" %
+                             envconfig._basepython_info)
             self.report.line("  envpython=%s" % envconfig.envpython)
             self.report.line("  envtmpdir=%s" % envconfig.envtmpdir)
             self.report.line("  envbindir=%s" % envconfig.envbindir)

diff -r cc1933175162c4bb01669912a57df6fba4be5780 -r 
850fb37c60625112b2bf5ad21373d2804be98336 tox/_config.py
--- a/tox/_config.py
+++ b/tox/_config.py
@@ -8,10 +8,8 @@
 import string
 import pkg_resources
 import itertools
-import pluggy
 
-import tox.interpreters
-from tox import hookspecs
+from tox.interpreters import Interpreters
 
 import py
 
@@ -24,43 +22,20 @@
 for version in '24,25,26,27,30,31,32,33,34,35'.split(','):
     default_factors['py' + version] = 'python%s.%s' % tuple(version)
 
-hookimpl = pluggy.HookimplMarker("tox")
 
-
-def get_plugin_manager():
-    # initialize plugin manager
-    pm = pluggy.PluginManager("tox")
-    pm.add_hookspecs(hookspecs)
-    pm.register(tox._config)
-    pm.register(tox.interpreters)
-    pm.load_setuptools_entrypoints("tox")
-    pm.check_pending()
-    return pm
-
-
-def parseconfig(args=None):
+def parseconfig(args=None, pkg=None):
     """
     :param list[str] args: Optional list of arguments.
     :type pkg: str
     :rtype: :class:`Config`
     :raise SystemExit: toxinit file is not found
     """
-
-    pm = get_plugin_manager()
-
     if args is None:
         args = sys.argv[1:]
-
-    # prepare command line options
-    parser = argparse.ArgumentParser(description=__doc__)
-    pm.hook.tox_addoption(parser=parser)
-
-    # parse command line options
-    option = parser.parse_args(args)
-    interpreters = tox.interpreters.Interpreters(hook=pm.hook)
-    config = Config(pluginmanager=pm, option=option, interpreters=interpreters)
-
-    # parse ini file
+    parser = prepare_parse(pkg)
+    opts = parser.parse_args(args)
+    config = Config()
+    config.option = opts
     basename = config.option.configfile
     if os.path.isabs(basename):
         inipath = py.path.local(basename)
@@ -77,10 +52,6 @@
         exn = sys.exc_info()[1]
         # Use stdout to match test expectations
         py.builtin.print_("ERROR: " + str(exn))
-
-    # post process config object
-    pm.hook.tox_configure(config=config)
-
     return config
 
 
@@ -92,8 +63,10 @@
 
 class VersionAction(argparse.Action):
     def __call__(self, argparser, *args, **kwargs):
-        version = tox.__version__
-        py.builtin.print_("%s imported from %s" % (version, tox.__file__))
+        name = argparser.pkgname
+        mod = __import__(name)
+        version = mod.__version__
+        py.builtin.print_("%s imported from %s" % (version, mod.__file__))
         raise SystemExit(0)
 
 
@@ -105,9 +78,10 @@
             setattr(namespace, self.dest, 0)
 
 
-@hookimpl
-def tox_addoption(parser):
+def prepare_parse(pkgname):
+    parser = argparse.ArgumentParser(description=__doc__,)
     # formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+    parser.pkgname = pkgname
     parser.add_argument("--version", nargs=0, action=VersionAction,
                         dest="version",
                         help="report version information to stdout.")
@@ -179,12 +153,10 @@
 
 
 class Config(object):
-    def __init__(self, pluginmanager, option, interpreters):
+    def __init__(self):
         self.envconfigs = {}
         self.invocationcwd = py.path.local()
-        self.interpreters = interpreters
-        self.pluginmanager = pluginmanager
-        self.option = option
+        self.interpreters = Interpreters()
 
     @property
     def homedir(self):
@@ -220,14 +192,10 @@
     def envsitepackagesdir(self):
         self.getsupportedinterpreter()  # for throwing exceptions
         x = self.config.interpreters.get_sitepackagesdir(
-            info=self.python_info,
+            info=self._basepython_info,
             envdir=self.envdir)
         return x
 
-    @property
-    def python_info(self):
-        return self.config.interpreters.get_info(self.basepython)
-
     def getsupportedinterpreter(self):
         if sys.platform == "win32" and self.basepython and \
                 "jython" in self.basepython:
@@ -388,7 +356,7 @@
         bp = next((default_factors[f] for f in factors if f in 
default_factors),
                   sys.executable)
         vc.basepython = reader.getdefault(section, "basepython", bp)
-
+        vc._basepython_info = config.interpreters.get_info(vc.basepython)
         reader.addsubstitutions(envdir=vc.envdir, envname=vc.envname,
                                 envbindir=vc.envbindir, envpython=vc.envpython,
                                 envsitepackagesdir=vc.envsitepackagesdir)

diff -r cc1933175162c4bb01669912a57df6fba4be5780 -r 
850fb37c60625112b2bf5ad21373d2804be98336 tox/_venv.py
--- a/tox/_venv.py
+++ b/tox/_venv.py
@@ -143,7 +143,7 @@
                 self.envconfig.deps, v)
 
     def _getliveconfig(self):
-        python = self.envconfig.python_info.executable
+        python = self.envconfig._basepython_info.executable
         md5 = getdigest(python)
         version = tox.__version__
         sitepackages = self.envconfig.sitepackages

diff -r cc1933175162c4bb01669912a57df6fba4be5780 -r 
850fb37c60625112b2bf5ad21373d2804be98336 tox/interpreters.py
--- a/tox/interpreters.py
+++ b/tox/interpreters.py
@@ -2,14 +2,12 @@
 import py
 import re
 import inspect
-from tox import hookimpl
 
 
 class Interpreters:
-    def __init__(self, hook):
+    def __init__(self):
         self.name2executable = {}
         self.executable2info = {}
-        self.hook = hook
 
     def get_executable(self, name):
         """ return path object to the executable for the given
@@ -20,9 +18,8 @@
         try:
             return self.name2executable[name]
         except KeyError:
-            exe = self.hook.tox_get_python_executable(name=name)
-            self.name2executable[name] = exe
-            return exe
+            self.name2executable[name] = e = find_executable(name)
+            return e
 
     def get_info(self, name=None, executable=None):
         if name is None and executable is None:
@@ -128,13 +125,31 @@
             return "<executable not found for: %s>" % self.name
 
 if sys.platform != "win32":
-    @hookimpl
-    def tox_get_python_executable(name):
+    def find_executable(name):
         return py.path.local.sysfind(name)
 
 else:
-    @hookimpl
-    def tox_get_python_executable(name):
+    # Exceptions to the usual windows mapping
+    win32map = {
+        'python': sys.executable,
+        '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.sysfind(name)
         if p:
             return p
@@ -155,26 +170,6 @@
         if m:
             return locate_via_py(*m.groups())
 
-    # Exceptions to the usual windows mapping
-    win32map = {
-        'python': sys.executable,
-        '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 pyinfo():
     import sys


https://bitbucket.org/hpk42/tox/commits/581c658b9b39/
Changeset:   581c658b9b39
Branch:      pluggy
User:        hpk42
Date:        2015-05-08 11:18:23+00:00
Summary:     introduce little plugin system which allows to add command line 
options,
perform extra configuration and determine how python executables are found
(see hookspec)
Affected #:  14 files

diff -r 850fb37c60625112b2bf5ad21373d2804be98336 -r 
581c658b9b3978c753213450cce5b8222100d232 doc/conf.py
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -48,8 +48,8 @@
 # built documents.
 #
 # The short X.Y version.
-release = "1.9"
-version = "1.9.0"
+release = "2.0"
+version = "2.0.0"
 # The full version, including alpha/beta/rc tags.
 
 # The language for content autogenerated by Sphinx. Refer to documentation

diff -r 850fb37c60625112b2bf5ad21373d2804be98336 -r 
581c658b9b3978c753213450cce5b8222100d232 doc/index.txt
--- a/doc/index.txt
+++ b/doc/index.txt
@@ -5,7 +5,7 @@
 ---------------------------------------------
 
 ``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 a larger vision of easing the packaging, testing and release process
 of Python software.
 
 What is Tox?
@@ -21,6 +21,7 @@
 * acting as a frontend to Continuous Integration servers, greatly
   reducing boilerplate and merging CI and shell-based testing.
 
+
 Basic example
 -----------------
 
@@ -62,10 +63,10 @@
 
     - test-tool agnostic: runs py.test, nose or unittests in a uniform manner
 
-* supports :ref:`using different / multiple PyPI index servers  <multiindex>`
+* :doc:`(new in 2.0) plugin system <plugins>` to modify tox execution with 
simple hooks.
 
 * uses pip_ and setuptools_ by default.  Experimental
-  support for configuring the installer command 
+  support for configuring the installer command
   through :confval:`install_command=ARGV`.
 
 * **cross-Python compatible**: CPython-2.6, 2.7, 3.2 and higher,
@@ -74,11 +75,11 @@
 * **cross-platform**: Windows and Unix style environments
 
 * **integrates with continuous integration servers** like Jenkins_
-  (formerly known as Hudson) and helps you to avoid boilerplatish 
+  (formerly known as Hudson) and helps you to avoid boilerplatish
   and platform-specific build-step hacks.
 
 * **full interoperability with devpi**: is integrated with and
-  is used for testing in the devpi_ system, a versatile pypi 
+  is used for testing in the devpi_ system, a versatile pypi
   index server and release managing tool.
 
 * **driven by a simple ini-style config file**
@@ -89,6 +90,9 @@
 
 * **professionally** :doc:`supported <support>`
 
+* supports :ref:`using different / multiple PyPI index servers  <multiindex>`
+
+
 .. _pypy: http://pypy.org
 
 .. _`tox.ini`: :doc:configfile

diff -r 850fb37c60625112b2bf5ad21373d2804be98336 -r 
581c658b9b3978c753213450cce5b8222100d232 doc/plugins.txt
--- /dev/null
+++ b/doc/plugins.txt
@@ -0,0 +1,69 @@
+.. be in -*- rst -*- mode!
+
+tox plugins
+===========
+
+.. versionadded:: 2.0
+
+With tox-2.0 a few aspects of tox running can be experimentally modified
+by writing hook functions.  We expect the list of hook function to grow
+over time.
+
+writing a setuptools entrypoints plugin
+---------------------------------------
+
+If you have a ``tox_MYPLUGIN.py`` module you could use the following
+rough ``setup.py`` to make it into a package which you can upload to the
+Python packaging index::
+
+    # content of setup.py
+    from setuptools import setup
+
+    if __name__ == "__main__":
+        setup(
+            name='tox-MYPLUGIN',
+            description='tox plugin decsription',
+            license="MIT license",
+            version='0.1',
+            py_modules=['tox_MYPLUGIN'],
+            entry_points={'tox': ['MYPLUGIN = tox_MYPLUGIN']},
+            install_requires=['tox>=2.0'],
+        )
+
+You can then install the plugin to develop it via::
+
+    pip install -e .
+
+and later publish it.
+
+The ``entry_points`` part allows tox to see your plugin during startup.
+
+
+Writing hook implementations
+----------------------------
+
+A plugin module needs can define one or more hook implementation functions::
+
+    from tox import hookimpl
+
+    @hookimpl
+    def tox_addoption(parser):
+        # add your own command line options
+
+
+    @hookimpl
+    def tox_configure(config):
+        # post process tox configuration after cmdline/ini file have
+        # been parsed
+
+If you put this into a module and make it pypi-installable with the ``tox``
+entry point you'll get your code executed as part of a tox run.
+
+
+
+tox hook specifications
+----------------------------
+
+.. automodule:: tox.hookspecs
+    :members:
+

diff -r 850fb37c60625112b2bf5ad21373d2804be98336 -r 
581c658b9b3978c753213450cce5b8222100d232 setup.py
--- a/setup.py
+++ b/setup.py
@@ -18,7 +18,7 @@
 
 def main():
     version = sys.version_info[:2]
-    install_requires = ['virtualenv>=1.11.2', 'py>=1.4.17', ]
+    install_requires = ['virtualenv>=1.11.2', 'py>=1.4.17', 
'pluggy>=0.3.0,<0.4.0']
     if version < (2, 7):
         install_requires += ['argparse']
     setup(

diff -r 850fb37c60625112b2bf5ad21373d2804be98336 -r 
581c658b9b3978c753213450cce5b8222100d232 tests/test_config.py
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -6,7 +6,6 @@
 import tox
 import tox._config
 from tox._config import *  # noqa
-from tox._config import _split_env
 from tox._venv import VirtualEnv
 
 
@@ -1561,31 +1560,16 @@
         ])
 
 
-class TestArgumentParser:
-
-    def test_dash_e_single_1(self):
-        parser = prepare_parse('testpkg')
-        args = parser.parse_args('-e py26'.split())
-        envlist = _split_env(args.env)
-        assert envlist == ['py26']
-
-    def test_dash_e_single_2(self):
-        parser = prepare_parse('testpkg')
-        args = parser.parse_args('-e py26,py33'.split())
-        envlist = _split_env(args.env)
-        assert envlist == ['py26', 'py33']
-
-    def test_dash_e_same(self):
-        parser = prepare_parse('testpkg')
-        args = parser.parse_args('-e py26,py26'.split())
-        envlist = _split_env(args.env)
-        assert envlist == ['py26', 'py26']
-
-    def test_dash_e_combine(self):
-        parser = prepare_parse('testpkg')
-        args = parser.parse_args('-e py26,py25,py33 -e py33,py27'.split())
-        envlist = _split_env(args.env)
-        assert envlist == ['py26', 'py25', 'py33', 'py33', 'py27']
+@pytest.mark.parametrize("cmdline,envlist", [
+    ("-e py26", ['py26']),
+    ("-e py26,py33", ['py26', 'py33']),
+    ("-e py26,py26", ['py26', 'py26']),
+    ("-e py26,py33 -e py33,py27", ['py26', 'py33', 'py33', 'py27'])
+])
+def test_env_spec(cmdline, envlist):
+    args = cmdline.split()
+    config = parseconfig(args)
+    assert config.envlist == envlist
 
 
 class TestCommandParser:

diff -r 850fb37c60625112b2bf5ad21373d2804be98336 -r 
581c658b9b3978c753213450cce5b8222100d232 tests/test_interpreters.py
--- a/tests/test_interpreters.py
+++ b/tests/test_interpreters.py
@@ -3,11 +3,13 @@
 
 import pytest
 from tox.interpreters import *  # noqa
+from tox._config import get_plugin_manager
 
 
 @pytest.fixture
 def interpreters():
-    return Interpreters()
+    pm = get_plugin_manager()
+    return Interpreters(hook=pm.hook)
 
 
 @pytest.mark.skipif("sys.platform != 'win32'")
@@ -28,8 +30,8 @@
     assert locate_via_py('3', '2') == sys.executable
 
 
-def test_find_executable():
-    p = find_executable(sys.executable)
+def test_tox_get_python_executable():
+    p = tox_get_python_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():
         name = "python%s" % ver
@@ -42,7 +44,7 @@
         else:
             if not py.path.local.sysfind(name):
                 continue
-        p = find_executable(name)
+        p = tox_get_python_executable(name)
         assert p
         popen = py.std.subprocess.Popen([str(p), '-V'],
                                         stderr=py.std.subprocess.PIPE)
@@ -55,7 +57,7 @@
     def sysfind(x):
         return "hello"
     monkeypatch.setattr(py.path.local, "sysfind", sysfind)
-    t = find_executable("qweqwe")
+    t = tox_get_python_executable("qweqwe")
     assert t == "hello"
 
 

diff -r 850fb37c60625112b2bf5ad21373d2804be98336 -r 
581c658b9b3978c753213450cce5b8222100d232 tests/test_venv.py
--- a/tests/test_venv.py
+++ b/tests/test_venv.py
@@ -65,7 +65,7 @@
         # assert Envconfig.toxworkdir in args
         assert venv.getcommandpath("easy_install", cwd=py.path.local())
     interp = venv._getliveconfig().python
-    assert interp == venv.envconfig._basepython_info.executable
+    assert interp == venv.envconfig.python_info.executable
     assert venv.path_config.check(exists=False)
 
 

diff -r 850fb37c60625112b2bf5ad21373d2804be98336 -r 
581c658b9b3978c753213450cce5b8222100d232 tox.ini
--- a/tox.ini
+++ b/tox.ini
@@ -22,7 +22,9 @@
 deps = pytest-flakes>=0.2
        pytest-pep8
 
-commands = py.test -x --flakes --pep8 tox tests
+commands = 
+    py.test --flakes -m flakes tox tests
+    py.test --pep8 -m pep8 tox tests
 
 [testenv:dev]
 # required to make looponfail reload on every source code change

diff -r 850fb37c60625112b2bf5ad21373d2804be98336 -r 
581c658b9b3978c753213450cce5b8222100d232 tox/__init__.py
--- a/tox/__init__.py
+++ b/tox/__init__.py
@@ -1,6 +1,8 @@
 #
 __version__ = '2.0.0.dev1'
 
+from .hookspecs import hookspec, hookimpl  # noqa
+
 
 class exception:
     class Error(Exception):

diff -r 850fb37c60625112b2bf5ad21373d2804be98336 -r 
581c658b9b3978c753213450cce5b8222100d232 tox/_cmdline.py
--- a/tox/_cmdline.py
+++ b/tox/_cmdline.py
@@ -24,7 +24,7 @@
 
 def main(args=None):
     try:
-        config = parseconfig(args, 'tox')
+        config = parseconfig(args)
         retcode = Session(config).runcommand()
         raise SystemExit(retcode)
     except KeyboardInterrupt:
@@ -551,8 +551,7 @@
         for envconfig in self.config.envconfigs.values():
             self.report.line("[testenv:%s]" % envconfig.envname, bold=True)
             self.report.line("  basepython=%s" % envconfig.basepython)
-            self.report.line("  _basepython_info=%s" %
-                             envconfig._basepython_info)
+            self.report.line("  pythoninfo=%s" % (envconfig.python_info,))
             self.report.line("  envpython=%s" % envconfig.envpython)
             self.report.line("  envtmpdir=%s" % envconfig.envtmpdir)
             self.report.line("  envbindir=%s" % envconfig.envbindir)

diff -r 850fb37c60625112b2bf5ad21373d2804be98336 -r 
581c658b9b3978c753213450cce5b8222100d232 tox/_config.py
--- a/tox/_config.py
+++ b/tox/_config.py
@@ -8,8 +8,10 @@
 import string
 import pkg_resources
 import itertools
+import pluggy
 
-from tox.interpreters import Interpreters
+import tox.interpreters
+from tox import hookspecs
 
 import py
 
@@ -22,20 +24,43 @@
 for version in '24,25,26,27,30,31,32,33,34,35'.split(','):
     default_factors['py' + version] = 'python%s.%s' % tuple(version)
 
+hookimpl = pluggy.HookimplMarker("tox")
 
-def parseconfig(args=None, pkg=None):
+
+def get_plugin_manager():
+    # initialize plugin manager
+    pm = pluggy.PluginManager("tox")
+    pm.add_hookspecs(hookspecs)
+    pm.register(tox._config)
+    pm.register(tox.interpreters)
+    pm.load_setuptools_entrypoints("tox")
+    pm.check_pending()
+    return pm
+
+
+def parseconfig(args=None):
     """
     :param list[str] args: Optional list of arguments.
     :type pkg: str
     :rtype: :class:`Config`
     :raise SystemExit: toxinit file is not found
     """
+
+    pm = get_plugin_manager()
+
     if args is None:
         args = sys.argv[1:]
-    parser = prepare_parse(pkg)
-    opts = parser.parse_args(args)
-    config = Config()
-    config.option = opts
+
+    # prepare command line options
+    parser = argparse.ArgumentParser(description=__doc__)
+    pm.hook.tox_addoption(parser=parser)
+
+    # parse command line options
+    option = parser.parse_args(args)
+    interpreters = tox.interpreters.Interpreters(hook=pm.hook)
+    config = Config(pluginmanager=pm, option=option, interpreters=interpreters)
+
+    # parse ini file
     basename = config.option.configfile
     if os.path.isabs(basename):
         inipath = py.path.local(basename)
@@ -52,6 +77,10 @@
         exn = sys.exc_info()[1]
         # Use stdout to match test expectations
         py.builtin.print_("ERROR: " + str(exn))
+
+    # post process config object
+    pm.hook.tox_configure(config=config)
+
     return config
 
 
@@ -63,10 +92,8 @@
 
 class VersionAction(argparse.Action):
     def __call__(self, argparser, *args, **kwargs):
-        name = argparser.pkgname
-        mod = __import__(name)
-        version = mod.__version__
-        py.builtin.print_("%s imported from %s" % (version, mod.__file__))
+        version = tox.__version__
+        py.builtin.print_("%s imported from %s" % (version, tox.__file__))
         raise SystemExit(0)
 
 
@@ -78,10 +105,9 @@
             setattr(namespace, self.dest, 0)
 
 
-def prepare_parse(pkgname):
-    parser = argparse.ArgumentParser(description=__doc__,)
+@hookimpl
+def tox_addoption(parser):
     # formatter_class=argparse.ArgumentDefaultsHelpFormatter)
-    parser.pkgname = pkgname
     parser.add_argument("--version", nargs=0, action=VersionAction,
                         dest="version",
                         help="report version information to stdout.")
@@ -153,10 +179,12 @@
 
 
 class Config(object):
-    def __init__(self):
+    def __init__(self, pluginmanager, option, interpreters):
         self.envconfigs = {}
         self.invocationcwd = py.path.local()
-        self.interpreters = Interpreters()
+        self.interpreters = interpreters
+        self.pluginmanager = pluginmanager
+        self.option = option
 
     @property
     def homedir(self):
@@ -192,10 +220,14 @@
     def envsitepackagesdir(self):
         self.getsupportedinterpreter()  # for throwing exceptions
         x = self.config.interpreters.get_sitepackagesdir(
-            info=self._basepython_info,
+            info=self.python_info,
             envdir=self.envdir)
         return x
 
+    @property
+    def python_info(self):
+        return self.config.interpreters.get_info(self.basepython)
+
     def getsupportedinterpreter(self):
         if sys.platform == "win32" and self.basepython and \
                 "jython" in self.basepython:
@@ -356,7 +388,7 @@
         bp = next((default_factors[f] for f in factors if f in 
default_factors),
                   sys.executable)
         vc.basepython = reader.getdefault(section, "basepython", bp)
-        vc._basepython_info = config.interpreters.get_info(vc.basepython)
+
         reader.addsubstitutions(envdir=vc.envdir, envname=vc.envname,
                                 envbindir=vc.envbindir, envpython=vc.envpython,
                                 envsitepackagesdir=vc.envsitepackagesdir)

diff -r 850fb37c60625112b2bf5ad21373d2804be98336 -r 
581c658b9b3978c753213450cce5b8222100d232 tox/_venv.py
--- a/tox/_venv.py
+++ b/tox/_venv.py
@@ -143,7 +143,7 @@
                 self.envconfig.deps, v)
 
     def _getliveconfig(self):
-        python = self.envconfig._basepython_info.executable
+        python = self.envconfig.python_info.executable
         md5 = getdigest(python)
         version = tox.__version__
         sitepackages = self.envconfig.sitepackages

diff -r 850fb37c60625112b2bf5ad21373d2804be98336 -r 
581c658b9b3978c753213450cce5b8222100d232 tox/hookspecs.py
--- /dev/null
+++ b/tox/hookspecs.py
@@ -0,0 +1,28 @@
+""" Hook specifications for tox.
+
+"""
+
+from pluggy import HookspecMarker, HookimplMarker
+
+hookspec = HookspecMarker("tox")
+hookimpl = HookimplMarker("tox")
+
+
+@hookspec
+def tox_addoption(parser):
+    """ add command line options to the argparse-style parser object."""
+
+
+@hookspec
+def tox_configure(config):
+    """ called after command line options have been parsed and the ini-file has
+    been read.  Please be aware that the config object layout may change as its
+    API was not designed yet wrt to providing stability (it was an internal
+    thing purely before tox-2.0). """
+
+
+@hookspec(firstresult=True)
+def tox_get_python_executable(name):
+    """ return a python executable for the given python base name.
+    The first plugin/hook which returns an executable path will determine it.
+    """

diff -r 850fb37c60625112b2bf5ad21373d2804be98336 -r 
581c658b9b3978c753213450cce5b8222100d232 tox/interpreters.py
--- a/tox/interpreters.py
+++ b/tox/interpreters.py
@@ -2,12 +2,14 @@
 import py
 import re
 import inspect
+from tox import hookimpl
 
 
 class Interpreters:
-    def __init__(self):
+    def __init__(self, hook):
         self.name2executable = {}
         self.executable2info = {}
+        self.hook = hook
 
     def get_executable(self, name):
         """ return path object to the executable for the given
@@ -18,8 +20,9 @@
         try:
             return self.name2executable[name]
         except KeyError:
-            self.name2executable[name] = e = find_executable(name)
-            return e
+            exe = self.hook.tox_get_python_executable(name=name)
+            self.name2executable[name] = exe
+            return exe
 
     def get_info(self, name=None, executable=None):
         if name is None and executable is None:
@@ -125,10 +128,33 @@
             return "<executable not found for: %s>" % self.name
 
 if sys.platform != "win32":
-    def find_executable(name):
+    @hookimpl
+    def tox_get_python_executable(name):
         return py.path.local.sysfind(name)
 
 else:
+    @hookimpl
+    def tox_get_python_executable(name):
+        p = py.path.local.sysfind(name)
+        if p:
+            return p
+        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:
+            return locate_via_py(*m.groups())
+
     # Exceptions to the usual windows mapping
     win32map = {
         'python': sys.executable,
@@ -149,27 +175,6 @@
                 if exe.check():
                     return exe
 
-    def find_executable(name):
-        p = py.path.local.sysfind(name)
-        if p:
-            return p
-        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:
-            return locate_via_py(*m.groups())
-
 
 def pyinfo():
     import sys

Repository URL: https://bitbucket.org/hpk42/tox/

--

This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
_______________________________________________
pytest-commit mailing list
pytest-commit@python.org
https://mail.python.org/mailman/listinfo/pytest-commit

Reply via email to