summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorholger krekel <holger@merlinux.eu>2015-12-09 13:13:32 +0100
committerholger krekel <holger@merlinux.eu>2015-12-09 13:13:32 +0100
commitaf509e26f7e2a08b6f6612873ea6983bccd2e4f9 (patch)
tree73f09b09e25801bde7256ff626c7367d75a956d0
parente0f22d570e4c242c64e41bb78b4fcfd57fcb79ca (diff)
downloadtox-af509e26f7e2a08b6f6612873ea6983bccd2e4f9.tar.gz
implement new experimental hooks for venv creation
-rw-r--r--CHANGELOG7
-rw-r--r--doc/plugins.txt6
-rw-r--r--tests/test_venv.py47
-rw-r--r--tox/_pytestplugin.py9
-rw-r--r--tox/config.py10
-rw-r--r--tox/hookspecs.py11
-rw-r--r--tox/session.py2
-rw-r--r--tox/venv.py75
8 files changed, 114 insertions, 53 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 3396dd7..4da6d0f 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -16,6 +16,13 @@
- fix issue252: allow environment names with special characters.
Thanks Julien Castets for initial PR and patience.
+- introduce experimental tox_testenv_create(venv, action) and
+ tox_testenv_install_deps(venv, action) hooks to allow
+ plugins to do additional work on creation or installing
+ deps. These hooks are experimental mainly because of
+ the involved "venv" object whose current public API is not
+ fully guranteed.
+
- internal: push some optional object creation into tests because
tox core doesn't need it.
diff --git a/doc/plugins.txt b/doc/plugins.txt
index 8e7041c..be3665a 100644
--- a/doc/plugins.txt
+++ b/doc/plugins.txt
@@ -80,3 +80,9 @@ tox hook specifications and related API
.. autoclass:: tox.config.TestenvConfig()
:members:
+
+.. autoclass:: tox.venv.VirtualEnv()
+ :members:
+
+.. autoclass:: tox.session.Session()
+ :members:
diff --git a/tests/test_venv.py b/tests/test_venv.py
index 217b21e..4ab3b06 100644
--- a/tests/test_venv.py
+++ b/tests/test_venv.py
@@ -57,7 +57,7 @@ def test_create(monkeypatch, mocksession, newconfig):
assert venv.path == envconfig.envdir
assert not venv.path.check()
action = mocksession.newaction(venv, "getenv")
- venv.create(action)
+ tox_testenv_create(action=action, venv=venv)
l = mocksession._pcalls
assert len(l) >= 1
args = l[0].args
@@ -98,7 +98,7 @@ def test_create_sitepackages(monkeypatch, mocksession, newconfig):
envconfig = config.envconfigs['site']
venv = VirtualEnv(envconfig, session=mocksession)
action = mocksession.newaction(venv, "getenv")
- venv.create(action)
+ tox_testenv_create(action=action, venv=venv)
l = mocksession._pcalls
assert len(l) >= 1
args = l[0].args
@@ -108,7 +108,7 @@ def test_create_sitepackages(monkeypatch, mocksession, newconfig):
envconfig = config.envconfigs['nosite']
venv = VirtualEnv(envconfig, session=mocksession)
action = mocksession.newaction(venv, "getenv")
- venv.create(action)
+ tox_testenv_create(action=action, venv=venv)
l = mocksession._pcalls
assert len(l) >= 1
args = l[0].args
@@ -126,14 +126,14 @@ def test_install_deps_wildcard(newmocksession):
""")
venv = mocksession.getenv("py123")
action = mocksession.newaction(venv, "getenv")
- venv.create(action)
+ tox_testenv_create(action=action, venv=venv)
l = mocksession._pcalls
assert len(l) == 1
distshare = venv.session.config.distshare
distshare.ensure("dep1-1.0.zip")
distshare.ensure("dep1-1.1.zip")
- venv.install_deps(action)
+ tox_testenv_install_deps(action=action, venv=venv)
assert len(l) == 2
args = l[-1].args
assert l[-1].cwd == venv.envconfig.config.toxinidir
@@ -159,11 +159,11 @@ def test_install_downloadcache(newmocksession, monkeypatch, tmpdir, envdc):
""")
venv = mocksession.getenv("py123")
action = mocksession.newaction(venv, "getenv")
- venv.create(action)
+ tox_testenv_create(action=action, venv=venv)
l = mocksession._pcalls
assert len(l) == 1
- venv.install_deps(action)
+ tox_testenv_install_deps(action=action, venv=venv)
assert len(l) == 2
args = l[-1].args
assert l[-1].cwd == venv.envconfig.config.toxinidir
@@ -189,12 +189,12 @@ def test_install_deps_indexserver(newmocksession):
""")
venv = mocksession.getenv('py123')
action = mocksession.newaction(venv, "getenv")
- venv.create(action)
+ tox_testenv_create(action=action, venv=venv)
l = mocksession._pcalls
assert len(l) == 1
l[:] = []
- venv.install_deps(action)
+ tox_testenv_install_deps(action=action, venv=venv)
# two different index servers, two calls
assert len(l) == 3
args = " ".join(l[0].args)
@@ -218,12 +218,12 @@ def test_install_deps_pre(newmocksession):
""")
venv = mocksession.getenv('python')
action = mocksession.newaction(venv, "getenv")
- venv.create(action)
+ tox_testenv_create(action=action, venv=venv)
l = mocksession._pcalls
assert len(l) == 1
l[:] = []
- venv.install_deps(action)
+ tox_testenv_install_deps(action=action, venv=venv)
assert len(l) == 1
args = " ".join(l[0].args)
assert "--pre " in args
@@ -355,7 +355,7 @@ def test_install_python3(tmpdir, newmocksession):
""")
venv = mocksession.getenv('py123')
action = mocksession.newaction(venv, "getenv")
- venv.create(action)
+ tox_testenv_create(action=action, venv=venv)
l = mocksession._pcalls
assert len(l) == 1
args = l[0].args
@@ -649,3 +649,26 @@ def test_ignore_outcome_failing_cmd(newmocksession):
assert venv.status == "ignored failed command"
mocksession.report.expect("warning", "*command failed but result from "
"testenv is ignored*")
+
+
+def test_tox_testenv_create(newmocksession):
+ l = []
+
+ class Plugin:
+ @hookimpl
+ def tox_testenv_create(self, action, venv):
+ l.append(1)
+
+ @hookimpl
+ def tox_testenv_install_deps(self, action, venv):
+ l.append(2)
+
+ mocksession = newmocksession([], """
+ [testenv]
+ commands=testenv_fail
+ ignore_outcome=True
+ """, plugins=[Plugin()])
+
+ venv = mocksession.getenv('python')
+ venv.update(action=mocksession.newaction(venv, "getenv"))
+ assert l == [1, 2]
diff --git a/tox/_pytestplugin.py b/tox/_pytestplugin.py
index ee85acc..f15d2ec 100644
--- a/tox/_pytestplugin.py
+++ b/tox/_pytestplugin.py
@@ -31,7 +31,7 @@ def pytest_report_header():
@pytest.fixture
def newconfig(request, tmpdir):
- def newconfig(args, source=None):
+ def newconfig(args, source=None, plugins=()):
if source is None:
source = args
args = []
@@ -40,7 +40,7 @@ def newconfig(request, tmpdir):
p.write(s)
old = tmpdir.chdir()
try:
- return parseconfig(args)
+ return parseconfig(args, plugins=plugins)
finally:
old.chdir()
return newconfig
@@ -168,9 +168,8 @@ def newmocksession(request):
mocksession = request.getfuncargvalue("mocksession")
newconfig = request.getfuncargvalue("newconfig")
- def newmocksession(args, source):
- config = newconfig(args, source)
- mocksession.config = config
+ def newmocksession(args, source, plugins=()):
+ mocksession.config = newconfig(args, source, plugins=plugins)
return mocksession
return newmocksession
diff --git a/tox/config.py b/tox/config.py
index d34a597..ad80269 100644
--- a/tox/config.py
+++ b/tox/config.py
@@ -29,13 +29,17 @@ hookimpl = pluggy.HookimplMarker("tox")
_dummy = object()
-def get_plugin_manager():
+def get_plugin_manager(plugins=()):
# initialize plugin manager
+ import tox.venv
pm = pluggy.PluginManager("tox")
pm.add_hookspecs(hookspecs)
pm.register(tox.config)
pm.register(tox.interpreters)
+ pm.register(tox.venv)
pm.load_setuptools_entrypoints("tox")
+ for plugin in plugins:
+ pm.register(plugin)
pm.check_pending()
return pm
@@ -186,7 +190,7 @@ class InstallcmdOption:
return value
-def parseconfig(args=None):
+def parseconfig(args=None, plugins=()):
"""
:param list[str] args: Optional list of arguments.
:type pkg: str
@@ -194,7 +198,7 @@ def parseconfig(args=None):
:raise SystemExit: toxinit file is not found
"""
- pm = get_plugin_manager()
+ pm = get_plugin_manager(plugins)
if args is None:
args = sys.argv[1:]
diff --git a/tox/hookspecs.py b/tox/hookspecs.py
index 103bda5..36c539c 100644
--- a/tox/hookspecs.py
+++ b/tox/hookspecs.py
@@ -30,3 +30,14 @@ def tox_get_python_executable(envconfig):
per-testenv configuration, notably the ``.envname`` and ``.basepython``
setting.
"""
+
+
+@hookspec
+def tox_testenv_create(venv, action):
+ """ [experimental] perform creation action for this venv.
+ """
+
+
+@hookspec
+def tox_testenv_install_deps(venv, action):
+ """ [experimental] perform install dependencies action for this venv. """
diff --git a/tox/session.py b/tox/session.py
index e5e654b..ffb52d6 100644
--- a/tox/session.py
+++ b/tox/session.py
@@ -313,6 +313,8 @@ class Reporter(object):
class Session:
+ """ (unstable API). the session object that ties
+ together configuration, reporting, venv creation, testing. """
def __init__(self, config, popen=subprocess.Popen, Report=Reporter):
self.config = config
diff --git a/tox/venv.py b/tox/venv.py
index bed7fb7..2e46098 100644
--- a/tox/venv.py
+++ b/tox/venv.py
@@ -5,7 +5,7 @@ import re
import codecs
import py
import tox
-from .config import DepConfig
+from .config import DepConfig, hookimpl
class CreationConfig:
@@ -58,6 +58,10 @@ class VirtualEnv(object):
self.session = session
@property
+ def hook(self):
+ return self.envconfig.config.pluginmanager.hook
+
+ @property
def path(self):
""" Path to environment base dir. """
return self.envconfig.envdir
@@ -76,7 +80,8 @@ class VirtualEnv(object):
def getcommandpath(self, name, venv=True, cwd=None):
""" return absolute path (str or localpath) for specified
- command name. If venv is True we will check if the
+ command name. If it's a localpath we will rewrite it as
+ as a relative path. If venv is True we will check if the
command is coming from the venv or is whitelisted to come
from external. """
name = str(name)
@@ -138,13 +143,14 @@ class VirtualEnv(object):
else:
action.setactivity("recreate", self.envconfig.envdir)
try:
- self.create(action)
+ self.hook.tox_testenv_create(action=action, venv=self)
+ self.just_created = True
except tox.exception.UnsupportedInterpreter:
return sys.exc_info()[1]
except tox.exception.InterpreterNotFound:
return sys.exc_info()[1]
try:
- self.install_deps(action)
+ self.hook.tox_testenv_install_deps(action=action, venv=self)
except tox.exception.InvocationError:
v = sys.exc_info()[1]
return "could not install deps %s; v = %r" % (
@@ -180,28 +186,6 @@ class VirtualEnv(object):
def matching_platform(self):
return re.match(self.envconfig.platform, sys.platform)
- def create(self, action):
- # if self.getcommandpath("activate").dirpath().check():
- # return
- config_interpreter = self.getsupportedinterpreter()
- args = [sys.executable, '-m', 'virtualenv']
- if self.envconfig.sitepackages:
- args.append('--system-site-packages')
- # add interpreter explicitly, to prevent using
- # default (virtualenv.ini)
- args.extend(['--python', str(config_interpreter)])
- # if sys.platform == "win32":
- # f, path, _ = py.std.imp.find_module("virtualenv")
- # f.close()
- # args[:1] = [str(config_interpreter), str(path)]
- # else:
- self.session.make_emptydir(self.path)
- basepath = self.path.dirpath()
- basepath.ensure(dir=1)
- args.append(self.path.basename)
- self._pcall(args, venv=False, action=action, cwd=basepath)
- self.just_created = True
-
def finish(self):
self._getliveconfig().writeconfig(self.path_config)
@@ -244,13 +228,6 @@ class VirtualEnv(object):
extraopts = ['-U', '--no-deps']
self._install([sdistpath], extraopts=extraopts, action=action)
- def install_deps(self, action):
- deps = self._getresolvedeps()
- if deps:
- depinfo = ", ".join(map(str, deps))
- action.setactivity("installdeps", "%s" % depinfo)
- self._install(deps, action=action)
-
def _installopts(self, indexserver):
l = []
if indexserver:
@@ -390,3 +367,35 @@ def getdigest(path):
if not path.check(file=1):
return "0" * 32
return path.computehash()
+
+
+@hookimpl
+def tox_testenv_create(venv, action):
+ # if self.getcommandpath("activate").dirpath().check():
+ # return
+ config_interpreter = venv.getsupportedinterpreter()
+ args = [sys.executable, '-m', 'virtualenv']
+ if venv.envconfig.sitepackages:
+ args.append('--system-site-packages')
+ # add interpreter explicitly, to prevent using
+ # default (virtualenv.ini)
+ args.extend(['--python', str(config_interpreter)])
+ # if sys.platform == "win32":
+ # f, path, _ = py.std.imp.find_module("virtualenv")
+ # f.close()
+ # args[:1] = [str(config_interpreter), str(path)]
+ # else:
+ venv.session.make_emptydir(venv.path)
+ basepath = venv.path.dirpath()
+ basepath.ensure(dir=1)
+ args.append(venv.path.basename)
+ venv._pcall(args, venv=False, action=action, cwd=basepath)
+
+
+@hookimpl
+def tox_testenv_install_deps(venv, action):
+ deps = venv._getresolvedeps()
+ if deps:
+ depinfo = ", ".join(map(str, deps))
+ action.setactivity("installdeps", "%s" % depinfo)
+ venv._install(deps, action=action)