some more separation of core pluginmanager from pytest specific functionality.

Idea is to have the PluginManager be re-useable from other projects at some point.
This commit is contained in:
holger krekel
2013-09-30 13:14:14 +02:00
parent d946299b0a
commit 4b709037ab
9 changed files with 157 additions and 134 deletions
+11 -99
View File
@@ -4,17 +4,10 @@ pytest PluginManager, basic initialization and tracing.
import sys, os
import inspect
import py
from _pytest import hookspec # the extension point definitions
from _pytest.config import Config
assert py.__version__.split(".")[:2] >= ['1', '4'], ("installation problem: "
"%s is too old, remove or upgrade 'py'" % (py.__version__))
default_plugins = (
"mark main terminal runner python pdb unittest capture skipping "
"tmpdir monkeypatch recwarn pastebin helpconfig nose assertion genscript "
"junitxml resultlog doctest").split()
class TagTracer:
def __init__(self):
self._tag2proc = {}
@@ -73,7 +66,7 @@ class TagTracerSub:
return self.__class__(self.root, self.tags + (name,))
class PluginManager(object):
def __init__(self, load=False):
def __init__(self, hookspecs=None):
self._name2plugin = {}
self._listattrcache = {}
self._plugins = []
@@ -81,20 +74,11 @@ class PluginManager(object):
self.trace = TagTracer().get("pluginmanage")
self._plugin_distinfo = []
self._shutdown = []
if os.environ.get('PYTEST_DEBUG'):
err = sys.stderr
encoding = getattr(err, 'encoding', 'utf8')
try:
err = py.io.dupfile(err, encoding=encoding)
except Exception:
pass
self.trace.root.setwriter(err.write)
self.hook = HookRelay([hookspec], pm=self)
self.register(self)
self.config = Config(self) # XXX unclear if the attr is needed
if load:
for spec in default_plugins:
self.import_plugin(spec)
self.hook = HookRelay(hookspecs or [], pm=self)
def set_register_callback(self, callback):
assert not hasattr(self, "_registercallback")
self._registercallback = callback
def register(self, plugin, name=None, prepend=False):
if self._name2plugin.get(name, None) == -1:
@@ -105,8 +89,9 @@ class PluginManager(object):
name, plugin, self._name2plugin))
#self.trace("registering", name, plugin)
self._name2plugin[name] = plugin
self.call_plugin(plugin, "pytest_addhooks", {'pluginmanager': self})
self.hook.pytest_plugin_registered(manager=self, plugin=plugin)
reg = getattr(self, "_registercallback", None)
if reg is not None:
reg(plugin, name)
if not prepend:
self._plugins.append(plugin)
else:
@@ -139,8 +124,8 @@ class PluginManager(object):
if plugin == val:
return True
def addhooks(self, spec):
self.hook._addhooks(spec, prefix="pytest_")
def addhooks(self, spec, prefix="pytest_"):
self.hook._addhooks(spec, prefix=prefix)
def getplugins(self):
return list(self._plugins)
@@ -240,36 +225,6 @@ class PluginManager(object):
self.register(mod, modname)
self.consider_module(mod)
def pytest_configure(self, config):
config.addinivalue_line("markers",
"tryfirst: mark a hook implementation function such that the "
"plugin machinery will try to call it first/as early as possible.")
config.addinivalue_line("markers",
"trylast: mark a hook implementation function such that the "
"plugin machinery will try to call it last/as late as possible.")
def pytest_terminal_summary(self, terminalreporter):
tw = terminalreporter._tw
if terminalreporter.config.option.traceconfig:
for hint in self._hints:
tw.line("hint: %s" % hint)
def notify_exception(self, excinfo, option=None):
if option and option.fulltrace:
style = "long"
else:
style = "native"
excrepr = excinfo.getrepr(funcargs=True,
showlocals=getattr(option, 'showlocals', False),
style=style,
)
res = self.hook.pytest_internalerror(excrepr=excrepr,
excinfo=excinfo)
if not py.builtin.any(res):
for line in str(excrepr).split("\n"):
sys.stderr.write("INTERNALERROR> %s\n" %line)
sys.stderr.flush()
def listattr(self, attrname, plugins=None):
if plugins is None:
plugins = self._plugins
@@ -424,46 +379,3 @@ class HookCaller:
self.trace.root.indent -= 1
return res
_preinit = []
def _preloadplugins():
assert not _preinit
_preinit.append(PluginManager(load=True))
def get_plugin_manager():
if _preinit:
return _preinit.pop(0)
else: # subsequent calls to main will create a fresh instance
return PluginManager(load=True)
def _prepareconfig(args=None, plugins=None):
if args is None:
args = sys.argv[1:]
elif isinstance(args, py.path.local):
args = [str(args)]
elif not isinstance(args, (tuple, list)):
if not isinstance(args, str):
raise ValueError("not a string or argument list: %r" % (args,))
args = py.std.shlex.split(args)
pluginmanager = get_plugin_manager()
if plugins:
for plugin in plugins:
pluginmanager.register(plugin)
return pluginmanager.hook.pytest_cmdline_parse(
pluginmanager=pluginmanager, args=args)
def main(args=None, plugins=None):
""" return exit code, after performing an in-process test run.
:arg args: list of command line arguments.
:arg plugins: list of plugin objects to be auto-registered during
initialization.
"""
config = _prepareconfig(args, plugins)
exitstatus = config.hook.pytest_cmdline_main(config=config)
return exitstatus
class UsageError(Exception):
""" error in py.test usage or invocation"""