From 820b005c923aabf9c8724aa6ef5aea6ad4a1ac6b Mon Sep 17 00:00:00 2001 From: holger krekel Date: Fri, 8 May 2015 13:16:20 +0200 Subject: Backed out changeset cc1933175162 --- doc/conf.py | 4 +-- doc/index.txt | 14 ++++------ doc/plugins.txt | 69 ---------------------------------------------- setup.py | 2 +- tests/test_config.py | 36 +++++++++++++++++------- tests/test_interpreters.py | 12 ++++---- tests/test_venv.py | 2 +- tox.ini | 4 +-- tox/__init__.py | 2 -- tox/_cmdline.py | 5 ++-- tox/_config.py | 66 ++++++++++++-------------------------------- tox/_venv.py | 2 +- tox/interpreters.py | 55 +++++++++++++++++------------------- 13 files changed, 87 insertions(+), 186 deletions(-) delete mode 100644 doc/plugins.txt diff --git a/doc/conf.py b/doc/conf.py index 73f5168..9c573d7 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -48,8 +48,8 @@ copyright = u'2013, holger krekel and others' # 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 --git a/doc/index.txt b/doc/index.txt index ed594cd..17d063d 100644 --- a/doc/index.txt +++ b/doc/index.txt @@ -5,7 +5,7 @@ vision: standardize testing in Python --------------------------------------------- ``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 @@ Tox is a generic virtualenv_ management and test command line tool you can use f * acting as a frontend to Continuous Integration servers, greatly reducing boilerplate and merging CI and shell-based testing. - Basic example ----------------- @@ -63,10 +62,10 @@ Current features - test-tool agnostic: runs py.test, nose or unittests in a uniform manner -* :doc:`(new in 2.0) plugin system ` to modify tox execution with simple hooks. +* supports :ref:`using different / multiple PyPI index servers ` * 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 @@ Current features * **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 @@ Current features * **professionally** :doc:`supported ` -* supports :ref:`using different / multiple PyPI index servers ` - - .. _pypy: http://pypy.org .. _`tox.ini`: :doc:configfile diff --git a/doc/plugins.txt b/doc/plugins.txt deleted file mode 100644 index 61e4408..0000000 --- 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 --git a/setup.py b/setup.py index d00fa4c..82c45db 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ class Tox(TestCommand): 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 --git a/tests/test_config.py b/tests/test_config.py index 522a22f..79c98bd 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -6,6 +6,7 @@ import pytest 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 @@ class TestCmdInvocation: ]) -@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 --git a/tests/test_interpreters.py b/tests/test_interpreters.py index a6997e6..1c5a77d 100644 --- a/tests/test_interpreters.py +++ b/tests/test_interpreters.py @@ -3,13 +3,11 @@ import os 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 @@ def test_locate_via_py(monkeypatch): 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 @@ def test_tox_get_python_executable(): 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 test_find_executable_extra(monkeypatch): 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 --git a/tests/test_venv.py b/tests/test_venv.py index 80ec519..538b7e5 100644 --- a/tests/test_venv.py +++ b/tests/test_venv.py @@ -65,7 +65,7 @@ def test_create(monkeypatch, mocksession, newconfig): # 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 --git a/tox.ini b/tox.ini index 607add9..8f255d2 100644 --- a/tox.ini +++ b/tox.ini @@ -22,9 +22,7 @@ commands= 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 --git a/tox/__init__.py b/tox/__init__.py index 5f3f3ad..7869fcb 100644 --- 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 --git a/tox/_cmdline.py b/tox/_cmdline.py index 3dd9582..0236d17 100644 --- a/tox/_cmdline.py +++ b/tox/_cmdline.py @@ -24,7 +24,7 @@ def now(): 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 @@ class Session: 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 --git a/tox/_config.py b/tox/_config.py index d5e8a31..4468d86 100644 --- a/tox/_config.py +++ b/tox/_config.py @@ -8,10 +8,8 @@ import shlex 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 @@ default_factors = {'jython': 'jython', 'pypy': 'pypy', 'pypy3': 'pypy3', 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 @@ def parseconfig(args=None): 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 @@ def feedback(msg, sysexit=False): 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 @@ class CountAction(argparse.Action): 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 @@ def tox_addoption(parser): 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 @@ class VenvConfig: 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 @@ class parseini: 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 --git a/tox/_venv.py b/tox/_venv.py index 58587e4..9114e69 100644 --- a/tox/_venv.py +++ b/tox/_venv.py @@ -143,7 +143,7 @@ class VirtualEnv(object): 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 --git a/tox/interpreters.py b/tox/interpreters.py index 98a5c40..76075b8 100644 --- a/tox/interpreters.py +++ b/tox/interpreters.py @@ -2,14 +2,12 @@ import sys 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 @@ class Interpreters: 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,33 +125,10 @@ class NoInterpreterInfo: return "" % 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): - 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, @@ -175,6 +149,27 @@ else: 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 -- cgit v1.2.1