summaryrefslogtreecommitdiff
path: root/tox
diff options
context:
space:
mode:
authorholger krekel <holger@merlinux.eu>2015-05-08 13:15:14 +0200
committerholger krekel <holger@merlinux.eu>2015-05-08 13:15:14 +0200
commit6e2e1a62b903a29bfeca35c680789a8e959786fa (patch)
tree10fbc8da4a1c4bed91cd36adc8ca66806f3050f1 /tox
parentecdad82f66a6e6678276d7101f7d0457de27ecd7 (diff)
downloadtox-6e2e1a62b903a29bfeca35c680789a8e959786fa.tar.gz
introduce little plugin system based on pluggy
and refactor/streamline some code with relation to getting executables
Diffstat (limited to 'tox')
-rw-r--r--tox/__init__.py2
-rw-r--r--tox/_cmdline.py5
-rw-r--r--tox/_config.py66
-rw-r--r--tox/_venv.py2
-rw-r--r--tox/interpreters.py55
5 files changed, 84 insertions, 46 deletions
diff --git a/tox/__init__.py b/tox/__init__.py
index 7869fcb..5f3f3ad 100644
--- 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 --git a/tox/_cmdline.py b/tox/_cmdline.py
index 0236d17..3dd9582 100644
--- a/tox/_cmdline.py
+++ b/tox/_cmdline.py
@@ -24,7 +24,7 @@ def now():
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 @@ 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(" _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 --git a/tox/_config.py b/tox/_config.py
index 4468d86..d5e8a31 100644
--- a/tox/_config.py
+++ b/tox/_config.py
@@ -8,8 +8,10 @@ import shlex
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 @@ 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 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 @@ def parseconfig(args=None, pkg=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
@@ -63,10 +92,8 @@ def feedback(msg, sysexit=False):
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 @@ class CountAction(argparse.Action):
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 @@ def prepare_parse(pkgname):
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 @@ class VenvConfig:
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 @@ 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 9114e69..58587e4 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._basepython_info.executable
+ python = self.envconfig.python_info.executable
md5 = getdigest(python)
version = tox.__version__
sitepackages = self.envconfig.sitepackages
diff --git a/tox/interpreters.py b/tox/interpreters.py
index 76075b8..98a5c40 100644
--- a/tox/interpreters.py
+++ b/tox/interpreters.py
@@ -2,12 +2,14 @@ import sys
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 @@ class Interpreters:
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,31 +128,13 @@ class NoInterpreterInfo:
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:
- # 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):
+ @hookimpl
+ def tox_get_python_executable(name):
p = py.path.local.sysfind(name)
if p:
return p
@@ -170,6 +155,26 @@ else:
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