merge config, pluginmanager, main into one file
--HG-- branch : trunk
This commit is contained in:
parent
6efc6dcb62
commit
6631447161
|
@ -232,7 +232,7 @@ initialisation, command line and configuration hooks
|
|||
generic "runtest" hooks
|
||||
------------------------------
|
||||
|
||||
Almost runtest related hooks receive a :py:class:`pytest.collect.Item` object.
|
||||
All all runtest related hooks receive a :py:class:`pytest.collect.Item` object.
|
||||
|
||||
.. autofunction:: pytest_runtest_protocol
|
||||
.. autofunction:: pytest_runtest_setup
|
||||
|
@ -340,13 +340,13 @@ name. Given a filesystem ``fspath`` it is constructed as follows:
|
|||
Reference of important objects involved in hooks
|
||||
===========================================================
|
||||
|
||||
.. autoclass:: pytest._config.Config
|
||||
.. autoclass:: pytest._core.Config
|
||||
:members:
|
||||
|
||||
.. autoclass:: pytest.collect.Item
|
||||
.. autoclass:: pytest.plugin.session.Item
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: pytest.collect.Node
|
||||
.. autoclass:: pytest.plugin.session.Node
|
||||
:members:
|
||||
|
||||
.. autoclass:: pytest.plugin.runner.CallInfo
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
"""
|
||||
py.test / pytest API for unit and functional testing with Python.
|
||||
|
||||
see http://pytest.org for documentation and details
|
||||
|
||||
(c) Holger Krekel and others, 2004-2010
|
||||
"""
|
||||
__version__ = "2.0.0dev0"
|
||||
|
||||
__all__ = ['collect', 'cmdline', 'config']
|
||||
__all__ = ['config', 'cmdline']
|
||||
|
||||
import pytest._config
|
||||
config = pytest._config.Config()
|
||||
from pytest import main as cmdline
|
||||
from pytest import _core as cmdline
|
||||
from pytest._core import Config
|
||||
config = Config()
|
||||
|
||||
def __main__():
|
||||
raise SystemExit(cmdline.main())
|
||||
|
|
|
@ -1,6 +1,25 @@
|
|||
import py, os
|
||||
from pytest.pluginmanager import PluginManager
|
||||
import optparse
|
||||
import py
|
||||
import sys, os
|
||||
|
||||
default_plugins = (
|
||||
"session terminal python runner pdb capture mark skipping tmpdir monkeypatch "
|
||||
"recwarn pastebin unittest helpconfig nose assertion genscript "
|
||||
"junitxml doctest keyword").split()
|
||||
|
||||
def main(args=None):
|
||||
import sys
|
||||
if args is None:
|
||||
args = sys.argv[1:]
|
||||
config = py.test.config
|
||||
config.parse(args)
|
||||
try:
|
||||
exitstatus = config.hook.pytest_cmdline_main(config=config)
|
||||
except config.Error:
|
||||
e = sys.exc_info()[1]
|
||||
sys.stderr.write("ERROR: %s\n" %(e.args[0],))
|
||||
exitstatus = 3
|
||||
py.test.config = config.__class__()
|
||||
return exitstatus
|
||||
|
||||
class Parser:
|
||||
""" Parser for command line arguments. """
|
||||
|
@ -47,7 +66,7 @@ class Parser:
|
|||
for group in groups:
|
||||
if group.options:
|
||||
desc = group.description or group.name
|
||||
optgroup = optparse.OptionGroup(optparser, desc)
|
||||
optgroup = py.std.optparse.OptionGroup(optparser, desc)
|
||||
optgroup.add_options(group.options)
|
||||
optparser.add_option_group(optgroup)
|
||||
return optparser.parse_args([str(x) for x in args])
|
||||
|
@ -68,11 +87,11 @@ class OptionGroup:
|
|||
|
||||
def addoption(self, *optnames, **attrs):
|
||||
""" add an option to this group. """
|
||||
option = optparse.Option(*optnames, **attrs)
|
||||
option = py.std.optparse.Option(*optnames, **attrs)
|
||||
self._addoption_instance(option, shortupper=False)
|
||||
|
||||
def _addoption(self, *optnames, **attrs):
|
||||
option = optparse.Option(*optnames, **attrs)
|
||||
option = py.std.optparse.Option(*optnames, **attrs)
|
||||
self._addoption_instance(option, shortupper=True)
|
||||
|
||||
def _addoption_instance(self, option, shortupper=False):
|
||||
|
@ -85,10 +104,10 @@ class OptionGroup:
|
|||
self.options.append(option)
|
||||
|
||||
|
||||
class MyOptionParser(optparse.OptionParser):
|
||||
class MyOptionParser(py.std.optparse.OptionParser):
|
||||
def __init__(self, parser):
|
||||
self._parser = parser
|
||||
optparse.OptionParser.__init__(self, usage=parser._usage)
|
||||
py.std.optparse.OptionParser.__init__(self, usage=parser._usage)
|
||||
def format_epilog(self, formatter):
|
||||
hints = self._parser.hints
|
||||
if hints:
|
||||
|
@ -96,6 +115,7 @@ class MyOptionParser(optparse.OptionParser):
|
|||
s = "\n" + s + "\n"
|
||||
return s
|
||||
return ""
|
||||
|
||||
class Conftest(object):
|
||||
""" the single place for accessing values and interacting
|
||||
towards conftest modules from py.test objects.
|
||||
|
@ -386,15 +406,368 @@ class Config(object):
|
|||
except AttributeError:
|
||||
return self._conftest.rget(name, path)
|
||||
|
||||
class PluginManager(object):
|
||||
def __init__(self):
|
||||
from pytest import hookspec
|
||||
self.registry = Registry()
|
||||
self._name2plugin = {}
|
||||
self._hints = []
|
||||
self.hook = HookRelay([hookspec], registry=self.registry)
|
||||
self.register(self)
|
||||
for spec in default_plugins:
|
||||
self.import_plugin(spec)
|
||||
|
||||
def _getpluginname(self, plugin, name):
|
||||
if name is None:
|
||||
if hasattr(plugin, '__name__'):
|
||||
name = plugin.__name__.split(".")[-1]
|
||||
else:
|
||||
name = id(plugin)
|
||||
return name
|
||||
|
||||
def register(self, plugin, name=None, prepend=False):
|
||||
assert not self.isregistered(plugin), plugin
|
||||
assert not self.registry.isregistered(plugin), plugin
|
||||
name = self._getpluginname(plugin, name)
|
||||
if name in self._name2plugin:
|
||||
return False
|
||||
self._name2plugin[name] = plugin
|
||||
self.call_plugin(plugin, "pytest_addhooks", {'pluginmanager': self})
|
||||
self.hook.pytest_plugin_registered(manager=self, plugin=plugin)
|
||||
self.registry.register(plugin, prepend=prepend)
|
||||
return True
|
||||
|
||||
def unregister(self, plugin):
|
||||
self.hook.pytest_plugin_unregistered(plugin=plugin)
|
||||
self.registry.unregister(plugin)
|
||||
for name, value in list(self._name2plugin.items()):
|
||||
if value == plugin:
|
||||
del self._name2plugin[name]
|
||||
|
||||
def isregistered(self, plugin, name=None):
|
||||
if self._getpluginname(plugin, name) in self._name2plugin:
|
||||
return True
|
||||
for val in self._name2plugin.values():
|
||||
if plugin == val:
|
||||
return True
|
||||
|
||||
def addhooks(self, spec):
|
||||
self.hook._addhooks(spec, prefix="pytest_")
|
||||
|
||||
def getplugins(self):
|
||||
return list(self.registry)
|
||||
|
||||
def skipifmissing(self, name):
|
||||
if not self.hasplugin(name):
|
||||
py.test.skip("plugin %r is missing" % name)
|
||||
|
||||
def hasplugin(self, name):
|
||||
try:
|
||||
self.getplugin(name)
|
||||
except KeyError:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def getplugin(self, name):
|
||||
try:
|
||||
return self._name2plugin[name]
|
||||
except KeyError:
|
||||
impname = canonical_importname(name)
|
||||
return self._name2plugin[impname]
|
||||
|
||||
# API for bootstrapping
|
||||
#
|
||||
# helpers
|
||||
def _envlist(self, varname):
|
||||
val = py.std.os.environ.get(varname, None)
|
||||
if val is not None:
|
||||
return val.split(',')
|
||||
return ()
|
||||
|
||||
def consider_env(self):
|
||||
for spec in self._envlist("PYTEST_PLUGINS"):
|
||||
self.import_plugin(spec)
|
||||
|
||||
def consider_setuptools_entrypoints(self):
|
||||
try:
|
||||
from pkg_resources import iter_entry_points
|
||||
except ImportError:
|
||||
return # XXX issue a warning
|
||||
for ep in iter_entry_points('pytest11'):
|
||||
name = canonical_importname(ep.name)
|
||||
if name in self._name2plugin:
|
||||
continue
|
||||
plugin = ep.load()
|
||||
self.register(plugin, name=name)
|
||||
|
||||
def consider_preparse(self, args):
|
||||
for opt1,opt2 in zip(args, args[1:]):
|
||||
if opt1 == "-p":
|
||||
self.import_plugin(opt2)
|
||||
|
||||
def consider_conftest(self, conftestmodule):
|
||||
cls = getattr(conftestmodule, 'ConftestPlugin', None)
|
||||
if cls is not None:
|
||||
raise ValueError("%r: 'ConftestPlugins' only existed till 1.0.0b1, "
|
||||
"were removed in 1.0.0b2" % (cls,))
|
||||
if self.register(conftestmodule, name=conftestmodule.__file__):
|
||||
self.consider_module(conftestmodule)
|
||||
|
||||
def consider_module(self, mod):
|
||||
attr = getattr(mod, "pytest_plugins", ())
|
||||
if attr:
|
||||
if not isinstance(attr, (list, tuple)):
|
||||
attr = (attr,)
|
||||
for spec in attr:
|
||||
self.import_plugin(spec)
|
||||
|
||||
def import_plugin(self, spec):
|
||||
assert isinstance(spec, str)
|
||||
modname = canonical_importname(spec)
|
||||
if modname in self._name2plugin:
|
||||
return
|
||||
try:
|
||||
mod = importplugin(modname)
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
except py.test.skip.Exception:
|
||||
e = py.std.sys.exc_info()[1]
|
||||
self._hints.append("skipped plugin %r: %s" %((modname, e.msg)))
|
||||
else:
|
||||
self.register(mod, modname)
|
||||
self.consider_module(mod)
|
||||
|
||||
def pytest_terminal_summary(self, terminalreporter):
|
||||
tw = terminalreporter._tw
|
||||
if terminalreporter.config.option.traceconfig:
|
||||
for hint in self._hints:
|
||||
tw.line("hint: %s" % hint)
|
||||
|
||||
#
|
||||
#
|
||||
# API for interacting with registered and instantiated plugin objects
|
||||
#
|
||||
#
|
||||
def listattr(self, attrname, plugins=None):
|
||||
return self.registry.listattr(attrname, plugins=plugins)
|
||||
|
||||
def onpytestaccess():
|
||||
# it's enough to have our containing module loaded as
|
||||
# it initializes a per-process config instance
|
||||
# which loads default plugins which add to py.test.*
|
||||
pass
|
||||
def notify_exception(self, excinfo=None):
|
||||
if excinfo is None:
|
||||
excinfo = py.code.ExceptionInfo()
|
||||
excrepr = excinfo.getrepr(funcargs=True, showlocals=True)
|
||||
res = self.hook.pytest_internalerror(excrepr=excrepr)
|
||||
if not py.builtin.any(res):
|
||||
for line in str(excrepr).split("\n"):
|
||||
sys.stderr.write("INTERNALERROR> %s\n" %line)
|
||||
sys.stderr.flush()
|
||||
|
||||
def do_addoption(self, parser):
|
||||
mname = "pytest_addoption"
|
||||
methods = self.registry.listattr(mname, reverse=True)
|
||||
mc = MultiCall(methods, {'parser': parser})
|
||||
mc.execute()
|
||||
|
||||
def _setns(self, obj, dic):
|
||||
for name, value in dic.items():
|
||||
if isinstance(value, dict):
|
||||
mod = getattr(obj, name, None)
|
||||
if mod is None:
|
||||
mod = py.std.types.ModuleType(name)
|
||||
sys.modules['pytest.%s' % name] = mod
|
||||
sys.modules['py.test.%s' % name] = mod
|
||||
mod.__all__ = []
|
||||
setattr(obj, name, mod)
|
||||
self._setns(mod, value)
|
||||
else:
|
||||
#print "setting", name, value, "on", obj
|
||||
setattr(obj, name, value)
|
||||
obj.__all__.append(name)
|
||||
|
||||
def pytest_plugin_registered(self, plugin):
|
||||
dic = self.call_plugin(plugin, "pytest_namespace", {}) or {}
|
||||
if dic:
|
||||
self._setns(py.test, dic)
|
||||
if hasattr(self, '_config'):
|
||||
self.call_plugin(plugin, "pytest_addoption",
|
||||
{'parser': self._config._parser})
|
||||
self.call_plugin(plugin, "pytest_configure",
|
||||
{'config': self._config})
|
||||
|
||||
def call_plugin(self, plugin, methname, kwargs):
|
||||
return MultiCall(
|
||||
methods=self.listattr(methname, plugins=[plugin]),
|
||||
kwargs=kwargs, firstresult=True).execute()
|
||||
|
||||
def do_configure(self, config):
|
||||
assert not hasattr(self, '_config')
|
||||
self._config = config
|
||||
config.hook.pytest_configure(config=self._config)
|
||||
|
||||
def do_unconfigure(self, config):
|
||||
config = self._config
|
||||
del self._config
|
||||
config.hook.pytest_unconfigure(config=config)
|
||||
config.pluginmanager.unregister(self)
|
||||
|
||||
def canonical_importname(name):
|
||||
name = name.lower()
|
||||
modprefix = "pytest_"
|
||||
if not name.startswith(modprefix):
|
||||
name = modprefix + name
|
||||
return name
|
||||
|
||||
def importplugin(importspec):
|
||||
#print "importing", importspec
|
||||
try:
|
||||
return __import__(importspec)
|
||||
except ImportError:
|
||||
e = py.std.sys.exc_info()[1]
|
||||
if str(e).find(importspec) == -1:
|
||||
raise
|
||||
name = importspec
|
||||
try:
|
||||
if name.startswith("pytest_"):
|
||||
name = importspec[7:]
|
||||
return __import__("pytest.plugin.%s" %(name), None, None, '__doc__')
|
||||
except ImportError:
|
||||
e = py.std.sys.exc_info()[1]
|
||||
if str(e).find(name) == -1:
|
||||
raise
|
||||
# show the original exception, not the failing internal one
|
||||
return __import__(importspec)
|
||||
|
||||
|
||||
class MultiCall:
|
||||
""" execute a call into multiple python functions/methods. """
|
||||
|
||||
def __init__(self, methods, kwargs, firstresult=False):
|
||||
self.methods = methods[:]
|
||||
self.kwargs = kwargs.copy()
|
||||
self.kwargs['__multicall__'] = self
|
||||
self.results = []
|
||||
self.firstresult = firstresult
|
||||
|
||||
def __repr__(self):
|
||||
status = "%d results, %d meths" % (len(self.results), len(self.methods))
|
||||
return "<MultiCall %s, kwargs=%r>" %(status, self.kwargs)
|
||||
|
||||
def execute(self):
|
||||
while self.methods:
|
||||
method = self.methods.pop()
|
||||
kwargs = self.getkwargs(method)
|
||||
res = method(**kwargs)
|
||||
if res is not None:
|
||||
self.results.append(res)
|
||||
if self.firstresult:
|
||||
return res
|
||||
if not self.firstresult:
|
||||
return self.results
|
||||
|
||||
def getkwargs(self, method):
|
||||
kwargs = {}
|
||||
for argname in varnames(method):
|
||||
try:
|
||||
kwargs[argname] = self.kwargs[argname]
|
||||
except KeyError:
|
||||
pass # might be optional param
|
||||
return kwargs
|
||||
|
||||
def varnames(func):
|
||||
import inspect
|
||||
if not inspect.isfunction(func) and not inspect.ismethod(func):
|
||||
func = getattr(func, '__call__', func)
|
||||
ismethod = inspect.ismethod(func)
|
||||
rawcode = py.code.getrawcode(func)
|
||||
try:
|
||||
return rawcode.co_varnames[ismethod:]
|
||||
except AttributeError:
|
||||
return ()
|
||||
|
||||
class Registry:
|
||||
"""
|
||||
Manage Plugins: register/unregister call calls to plugins.
|
||||
"""
|
||||
def __init__(self, plugins=None):
|
||||
if plugins is None:
|
||||
plugins = []
|
||||
self._plugins = plugins
|
||||
|
||||
def register(self, plugin, prepend=False):
|
||||
assert not isinstance(plugin, str)
|
||||
assert not plugin in self._plugins
|
||||
if not prepend:
|
||||
self._plugins.append(plugin)
|
||||
else:
|
||||
self._plugins.insert(0, plugin)
|
||||
|
||||
def unregister(self, plugin):
|
||||
self._plugins.remove(plugin)
|
||||
|
||||
def isregistered(self, plugin):
|
||||
return plugin in self._plugins
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._plugins)
|
||||
|
||||
def listattr(self, attrname, plugins=None, reverse=False):
|
||||
l = []
|
||||
if plugins is None:
|
||||
plugins = self._plugins
|
||||
for plugin in plugins:
|
||||
try:
|
||||
l.append(getattr(plugin, attrname))
|
||||
except AttributeError:
|
||||
continue
|
||||
if reverse:
|
||||
l.reverse()
|
||||
return l
|
||||
|
||||
class HookRelay:
|
||||
def __init__(self, hookspecs, registry, prefix="pytest_"):
|
||||
if not isinstance(hookspecs, list):
|
||||
hookspecs = [hookspecs]
|
||||
self._hookspecs = []
|
||||
self._registry = registry
|
||||
for hookspec in hookspecs:
|
||||
self._addhooks(hookspec, prefix)
|
||||
|
||||
def _addhooks(self, hookspecs, prefix):
|
||||
self._hookspecs.append(hookspecs)
|
||||
added = False
|
||||
for name, method in vars(hookspecs).items():
|
||||
if name.startswith(prefix):
|
||||
if not method.__doc__:
|
||||
raise ValueError("docstring required for hook %r, in %r"
|
||||
% (method, hookspecs))
|
||||
firstresult = getattr(method, 'firstresult', False)
|
||||
hc = HookCaller(self, name, firstresult=firstresult)
|
||||
setattr(self, name, hc)
|
||||
added = True
|
||||
#print ("setting new hook", name)
|
||||
if not added:
|
||||
raise ValueError("did not find new %r hooks in %r" %(
|
||||
prefix, hookspecs,))
|
||||
|
||||
|
||||
def _performcall(self, name, multicall):
|
||||
return multicall.execute()
|
||||
|
||||
class HookCaller:
|
||||
def __init__(self, hookrelay, name, firstresult):
|
||||
self.hookrelay = hookrelay
|
||||
self.name = name
|
||||
self.firstresult = firstresult
|
||||
|
||||
def __repr__(self):
|
||||
return "<HookCaller %r>" %(self.name,)
|
||||
|
||||
def __call__(self, **kwargs):
|
||||
methods = self.hookrelay._registry.listattr(self.name)
|
||||
mc = MultiCall(methods, kwargs, firstresult=self.firstresult)
|
||||
return self.hookrelay._performcall(self.name, mc)
|
||||
|
||||
def pcall(self, plugins, **kwargs):
|
||||
methods = self.hookrelay._registry.listattr(self.name, plugins=plugins)
|
||||
mc = MultiCall(methods, kwargs, firstresult=self.firstresult)
|
||||
return self.hookrelay._performcall(self.name, mc)
|
||||
|
|
@ -1,6 +1,4 @@
|
|||
"""
|
||||
hook specifications for py.test plugins
|
||||
"""
|
||||
""" hook specifications for pytest plugins, invoked from _core.py and builtin plugins. """
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Initialization
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
__all__ = []
|
||||
import py, pytest
|
||||
|
||||
def main(args=None):
|
||||
import sys
|
||||
if args is None:
|
||||
args = sys.argv[1:]
|
||||
config = py.test.config
|
||||
config.parse(args)
|
||||
try:
|
||||
exitstatus = config.hook.pytest_cmdline_main(config=config)
|
||||
except config.Error:
|
||||
e = sys.exc_info()[1]
|
||||
sys.stderr.write("ERROR: %s\n" %(e.args[0],))
|
||||
exitstatus = 3
|
||||
py.test.config = config.__class__()
|
||||
return exitstatus
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import py
|
||||
|
||||
from pytest.pluginmanager import HookRelay
|
||||
from pytest._core import HookRelay
|
||||
|
||||
def pytest_funcarg___pytest(request):
|
||||
return PytestArg(request)
|
||||
|
|
|
@ -8,7 +8,7 @@ import re
|
|||
import inspect
|
||||
import time
|
||||
from fnmatch import fnmatch
|
||||
from pytest._config import Config as pytestConfig
|
||||
from pytest._core import Config as pytestConfig
|
||||
from pytest.plugin.session import Collection
|
||||
from py.builtin import print_
|
||||
|
||||
|
@ -222,7 +222,6 @@ class TmpTestdir:
|
|||
""" this is used from tests that want to re-invoke parse(). """
|
||||
if not args:
|
||||
args = [self.tmpdir]
|
||||
from pytest import _config
|
||||
oldconfig = py.test.config
|
||||
try:
|
||||
c = py.test.config = pytestConfig()
|
||||
|
|
|
@ -1,381 +0,0 @@
|
|||
"""
|
||||
managing loading and interacting with pytest plugins.
|
||||
"""
|
||||
import py
|
||||
import sys
|
||||
|
||||
default_plugins = (
|
||||
"session terminal python runner pdb capture mark skipping tmpdir monkeypatch "
|
||||
"recwarn pastebin unittest helpconfig nose assertion genscript "
|
||||
"junitxml doctest keyword").split()
|
||||
|
||||
def check_old_use(mod, modname):
|
||||
clsname = modname[len('pytest_'):].capitalize() + "Plugin"
|
||||
assert not hasattr(mod, clsname), (mod, clsname)
|
||||
|
||||
class PluginManager(object):
|
||||
def __init__(self):
|
||||
from pytest import hookspec
|
||||
self.registry = Registry()
|
||||
self._name2plugin = {}
|
||||
self._hints = []
|
||||
self.hook = HookRelay([hookspec], registry=self.registry)
|
||||
self.register(self)
|
||||
for spec in default_plugins:
|
||||
self.import_plugin(spec)
|
||||
|
||||
def _getpluginname(self, plugin, name):
|
||||
if name is None:
|
||||
if hasattr(plugin, '__name__'):
|
||||
name = plugin.__name__.split(".")[-1]
|
||||
else:
|
||||
name = id(plugin)
|
||||
return name
|
||||
|
||||
def register(self, plugin, name=None, prepend=False):
|
||||
assert not self.isregistered(plugin), plugin
|
||||
assert not self.registry.isregistered(plugin), plugin
|
||||
name = self._getpluginname(plugin, name)
|
||||
if name in self._name2plugin:
|
||||
return False
|
||||
self._name2plugin[name] = plugin
|
||||
self.call_plugin(plugin, "pytest_addhooks", {'pluginmanager': self})
|
||||
self.hook.pytest_plugin_registered(manager=self, plugin=plugin)
|
||||
self.registry.register(plugin, prepend=prepend)
|
||||
return True
|
||||
|
||||
def unregister(self, plugin):
|
||||
self.hook.pytest_plugin_unregistered(plugin=plugin)
|
||||
self.registry.unregister(plugin)
|
||||
for name, value in list(self._name2plugin.items()):
|
||||
if value == plugin:
|
||||
del self._name2plugin[name]
|
||||
|
||||
def isregistered(self, plugin, name=None):
|
||||
if self._getpluginname(plugin, name) in self._name2plugin:
|
||||
return True
|
||||
for val in self._name2plugin.values():
|
||||
if plugin == val:
|
||||
return True
|
||||
|
||||
def addhooks(self, spec):
|
||||
self.hook._addhooks(spec, prefix="pytest_")
|
||||
|
||||
def getplugins(self):
|
||||
return list(self.registry)
|
||||
|
||||
def skipifmissing(self, name):
|
||||
if not self.hasplugin(name):
|
||||
py.test.skip("plugin %r is missing" % name)
|
||||
|
||||
def hasplugin(self, name):
|
||||
try:
|
||||
self.getplugin(name)
|
||||
except KeyError:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def getplugin(self, name):
|
||||
try:
|
||||
return self._name2plugin[name]
|
||||
except KeyError:
|
||||
impname = canonical_importname(name)
|
||||
return self._name2plugin[impname]
|
||||
|
||||
# API for bootstrapping
|
||||
#
|
||||
def _envlist(self, varname):
|
||||
val = py.std.os.environ.get(varname, None)
|
||||
if val is not None:
|
||||
return val.split(',')
|
||||
return ()
|
||||
|
||||
def consider_env(self):
|
||||
for spec in self._envlist("PYTEST_PLUGINS"):
|
||||
self.import_plugin(spec)
|
||||
|
||||
def consider_setuptools_entrypoints(self):
|
||||
try:
|
||||
from pkg_resources import iter_entry_points
|
||||
except ImportError:
|
||||
return # XXX issue a warning
|
||||
for ep in iter_entry_points('pytest11'):
|
||||
name = canonical_importname(ep.name)
|
||||
if name in self._name2plugin:
|
||||
continue
|
||||
plugin = ep.load()
|
||||
self.register(plugin, name=name)
|
||||
|
||||
def consider_preparse(self, args):
|
||||
for opt1,opt2 in zip(args, args[1:]):
|
||||
if opt1 == "-p":
|
||||
self.import_plugin(opt2)
|
||||
|
||||
def consider_conftest(self, conftestmodule):
|
||||
cls = getattr(conftestmodule, 'ConftestPlugin', None)
|
||||
if cls is not None:
|
||||
raise ValueError("%r: 'ConftestPlugins' only existed till 1.0.0b1, "
|
||||
"were removed in 1.0.0b2" % (cls,))
|
||||
if self.register(conftestmodule, name=conftestmodule.__file__):
|
||||
self.consider_module(conftestmodule)
|
||||
|
||||
def consider_module(self, mod):
|
||||
attr = getattr(mod, "pytest_plugins", ())
|
||||
if attr:
|
||||
if not isinstance(attr, (list, tuple)):
|
||||
attr = (attr,)
|
||||
for spec in attr:
|
||||
self.import_plugin(spec)
|
||||
|
||||
def import_plugin(self, spec):
|
||||
assert isinstance(spec, str)
|
||||
modname = canonical_importname(spec)
|
||||
if modname in self._name2plugin:
|
||||
return
|
||||
try:
|
||||
mod = importplugin(modname)
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
except py.test.skip.Exception:
|
||||
e = py.std.sys.exc_info()[1]
|
||||
self._hints.append("skipped plugin %r: %s" %((modname, e.msg)))
|
||||
else:
|
||||
check_old_use(mod, modname)
|
||||
self.register(mod, modname)
|
||||
self.consider_module(mod)
|
||||
|
||||
def pytest_terminal_summary(self, terminalreporter):
|
||||
tw = terminalreporter._tw
|
||||
if terminalreporter.config.option.traceconfig:
|
||||
for hint in self._hints:
|
||||
tw.line("hint: %s" % hint)
|
||||
|
||||
#
|
||||
#
|
||||
# API for interacting with registered and instantiated plugin objects
|
||||
#
|
||||
#
|
||||
def listattr(self, attrname, plugins=None):
|
||||
return self.registry.listattr(attrname, plugins=plugins)
|
||||
|
||||
def notify_exception(self, excinfo=None):
|
||||
if excinfo is None:
|
||||
excinfo = py.code.ExceptionInfo()
|
||||
excrepr = excinfo.getrepr(funcargs=True, showlocals=True)
|
||||
res = self.hook.pytest_internalerror(excrepr=excrepr)
|
||||
if not py.builtin.any(res):
|
||||
for line in str(excrepr).split("\n"):
|
||||
sys.stderr.write("INTERNALERROR> %s\n" %line)
|
||||
sys.stderr.flush()
|
||||
|
||||
def do_addoption(self, parser):
|
||||
mname = "pytest_addoption"
|
||||
methods = self.registry.listattr(mname, reverse=True)
|
||||
mc = MultiCall(methods, {'parser': parser})
|
||||
mc.execute()
|
||||
|
||||
def _setns(self, obj, dic):
|
||||
for name, value in dic.items():
|
||||
if isinstance(value, dict):
|
||||
mod = getattr(obj, name, None)
|
||||
if mod is None:
|
||||
mod = py.std.types.ModuleType(name)
|
||||
sys.modules['pytest.%s' % name] = mod
|
||||
sys.modules['py.test.%s' % name] = mod
|
||||
mod.__all__ = []
|
||||
setattr(obj, name, mod)
|
||||
self._setns(mod, value)
|
||||
else:
|
||||
#print "setting", name, value, "on", obj
|
||||
setattr(obj, name, value)
|
||||
obj.__all__.append(name)
|
||||
|
||||
def pytest_plugin_registered(self, plugin):
|
||||
dic = self.call_plugin(plugin, "pytest_namespace", {}) or {}
|
||||
if dic:
|
||||
self._setns(py.test, dic)
|
||||
if hasattr(self, '_config'):
|
||||
self.call_plugin(plugin, "pytest_addoption",
|
||||
{'parser': self._config._parser})
|
||||
self.call_plugin(plugin, "pytest_configure",
|
||||
{'config': self._config})
|
||||
|
||||
def call_plugin(self, plugin, methname, kwargs):
|
||||
return MultiCall(
|
||||
methods=self.listattr(methname, plugins=[plugin]),
|
||||
kwargs=kwargs, firstresult=True).execute()
|
||||
|
||||
def do_configure(self, config):
|
||||
assert not hasattr(self, '_config')
|
||||
self._config = config
|
||||
config.hook.pytest_configure(config=self._config)
|
||||
|
||||
def do_unconfigure(self, config):
|
||||
config = self._config
|
||||
del self._config
|
||||
config.hook.pytest_unconfigure(config=config)
|
||||
config.pluginmanager.unregister(self)
|
||||
|
||||
def canonical_importname(name):
|
||||
name = name.lower()
|
||||
modprefix = "pytest_"
|
||||
if not name.startswith(modprefix):
|
||||
name = modprefix + name
|
||||
return name
|
||||
|
||||
def importplugin(importspec):
|
||||
#print "importing", importspec
|
||||
try:
|
||||
return __import__(importspec)
|
||||
except ImportError:
|
||||
e = py.std.sys.exc_info()[1]
|
||||
if str(e).find(importspec) == -1:
|
||||
raise
|
||||
name = importspec
|
||||
try:
|
||||
if name.startswith("pytest_"):
|
||||
name = importspec[7:]
|
||||
return __import__("pytest.plugin.%s" %(name), None, None, '__doc__')
|
||||
except ImportError:
|
||||
e = py.std.sys.exc_info()[1]
|
||||
if str(e).find(name) == -1:
|
||||
raise
|
||||
# show the original exception, not the failing internal one
|
||||
return __import__(importspec)
|
||||
|
||||
|
||||
class MultiCall:
|
||||
""" execute a call into multiple python functions/methods. """
|
||||
|
||||
def __init__(self, methods, kwargs, firstresult=False):
|
||||
self.methods = methods[:]
|
||||
self.kwargs = kwargs.copy()
|
||||
self.kwargs['__multicall__'] = self
|
||||
self.results = []
|
||||
self.firstresult = firstresult
|
||||
|
||||
def __repr__(self):
|
||||
status = "%d results, %d meths" % (len(self.results), len(self.methods))
|
||||
return "<MultiCall %s, kwargs=%r>" %(status, self.kwargs)
|
||||
|
||||
def execute(self):
|
||||
while self.methods:
|
||||
method = self.methods.pop()
|
||||
kwargs = self.getkwargs(method)
|
||||
res = method(**kwargs)
|
||||
if res is not None:
|
||||
self.results.append(res)
|
||||
if self.firstresult:
|
||||
return res
|
||||
if not self.firstresult:
|
||||
return self.results
|
||||
|
||||
def getkwargs(self, method):
|
||||
kwargs = {}
|
||||
for argname in varnames(method):
|
||||
try:
|
||||
kwargs[argname] = self.kwargs[argname]
|
||||
except KeyError:
|
||||
pass # might be optional param
|
||||
return kwargs
|
||||
|
||||
def varnames(func):
|
||||
import inspect
|
||||
if not inspect.isfunction(func) and not inspect.ismethod(func):
|
||||
func = getattr(func, '__call__', func)
|
||||
ismethod = inspect.ismethod(func)
|
||||
rawcode = py.code.getrawcode(func)
|
||||
try:
|
||||
return rawcode.co_varnames[ismethod:]
|
||||
except AttributeError:
|
||||
return ()
|
||||
|
||||
class Registry:
|
||||
"""
|
||||
Manage Plugins: register/unregister call calls to plugins.
|
||||
"""
|
||||
def __init__(self, plugins=None):
|
||||
if plugins is None:
|
||||
plugins = []
|
||||
self._plugins = plugins
|
||||
|
||||
def register(self, plugin, prepend=False):
|
||||
assert not isinstance(plugin, str)
|
||||
assert not plugin in self._plugins
|
||||
if not prepend:
|
||||
self._plugins.append(plugin)
|
||||
else:
|
||||
self._plugins.insert(0, plugin)
|
||||
|
||||
def unregister(self, plugin):
|
||||
self._plugins.remove(plugin)
|
||||
|
||||
def isregistered(self, plugin):
|
||||
return plugin in self._plugins
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._plugins)
|
||||
|
||||
def listattr(self, attrname, plugins=None, reverse=False):
|
||||
l = []
|
||||
if plugins is None:
|
||||
plugins = self._plugins
|
||||
for plugin in plugins:
|
||||
try:
|
||||
l.append(getattr(plugin, attrname))
|
||||
except AttributeError:
|
||||
continue
|
||||
if reverse:
|
||||
l.reverse()
|
||||
return l
|
||||
|
||||
class HookRelay:
|
||||
def __init__(self, hookspecs, registry, prefix="pytest_"):
|
||||
if not isinstance(hookspecs, list):
|
||||
hookspecs = [hookspecs]
|
||||
self._hookspecs = []
|
||||
self._registry = registry
|
||||
for hookspec in hookspecs:
|
||||
self._addhooks(hookspec, prefix)
|
||||
|
||||
def _addhooks(self, hookspecs, prefix):
|
||||
self._hookspecs.append(hookspecs)
|
||||
added = False
|
||||
for name, method in vars(hookspecs).items():
|
||||
if name.startswith(prefix):
|
||||
if not method.__doc__:
|
||||
raise ValueError("docstring required for hook %r, in %r"
|
||||
% (method, hookspecs))
|
||||
firstresult = getattr(method, 'firstresult', False)
|
||||
hc = HookCaller(self, name, firstresult=firstresult)
|
||||
setattr(self, name, hc)
|
||||
added = True
|
||||
#print ("setting new hook", name)
|
||||
if not added:
|
||||
raise ValueError("did not find new %r hooks in %r" %(
|
||||
prefix, hookspecs,))
|
||||
|
||||
|
||||
def _performcall(self, name, multicall):
|
||||
return multicall.execute()
|
||||
|
||||
class HookCaller:
|
||||
def __init__(self, hookrelay, name, firstresult):
|
||||
self.hookrelay = hookrelay
|
||||
self.name = name
|
||||
self.firstresult = firstresult
|
||||
|
||||
def __repr__(self):
|
||||
return "<HookCaller %r>" %(self.name,)
|
||||
|
||||
def __call__(self, **kwargs):
|
||||
methods = self.hookrelay._registry.listattr(self.name)
|
||||
mc = MultiCall(methods, kwargs, firstresult=self.firstresult)
|
||||
return self.hookrelay._performcall(self.name, mc)
|
||||
|
||||
def pcall(self, plugins, **kwargs):
|
||||
methods = self.hookrelay._registry.listattr(self.name, plugins=plugins)
|
||||
mc = MultiCall(methods, kwargs, firstresult=self.firstresult)
|
||||
return self.hookrelay._performcall(self.name, mc)
|
||||
|
|
@ -3,7 +3,7 @@ import py
|
|||
pytest_plugins = "pytester"
|
||||
import pytest.plugin
|
||||
plugindir = py.path.local(pytest.plugin.__file__).dirpath()
|
||||
from pytest.pluginmanager import default_plugins
|
||||
from pytest._core import default_plugins
|
||||
|
||||
def pytest_collect_file(path, parent):
|
||||
if path.basename.startswith("pytest_") and path.ext == ".py":
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import py
|
||||
import os, sys
|
||||
from pytest.plugin._pytest import HookRecorder
|
||||
from pytest.pluginmanager import Registry
|
||||
from pytest._core import Registry
|
||||
|
||||
def test_hookrecorder_basic():
|
||||
rec = HookRecorder(Registry())
|
||||
|
@ -29,7 +29,7 @@ def test_hookrecorder_basic_no_args_hook():
|
|||
def test_functional(testdir, linecomp):
|
||||
reprec = testdir.inline_runsource("""
|
||||
import py
|
||||
from pytest.pluginmanager import HookRelay, Registry
|
||||
from pytest._core import HookRelay, Registry
|
||||
pytest_plugins="_pytest"
|
||||
def test_func(_pytest):
|
||||
class ApiClass:
|
||||
|
|
|
@ -2,7 +2,7 @@ import py
|
|||
import os
|
||||
from pytest.plugin.resultlog import generic_path, ResultLog, \
|
||||
pytest_configure, pytest_unconfigure
|
||||
from pytest.collect import Node, Item, FSCollector
|
||||
from pytest.plugin.session import Node, Item, FSCollector
|
||||
|
||||
def test_generic_path(testdir):
|
||||
from pytest.plugin.session import Collection
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import py
|
||||
from pytest._config import Conftest
|
||||
from pytest._core import Conftest
|
||||
|
||||
def pytest_generate_tests(metafunc):
|
||||
if "basedir" in metafunc.funcargnames:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import py
|
||||
from pytest import _config as parseopt
|
||||
from pytest import main as parseopt
|
||||
|
||||
class TestParser:
|
||||
def test_init(self, capsys):
|
||||
|
@ -42,7 +42,7 @@ class TestParser:
|
|||
group = parseopt.OptionGroup("hello")
|
||||
group.addoption("--option1", action="store_true")
|
||||
assert len(group.options) == 1
|
||||
assert isinstance(group.options[0], parseopt.optparse.Option)
|
||||
assert isinstance(group.options[0], py.std.optparse.Option)
|
||||
|
||||
def test_group_shortopt_lowercase(self):
|
||||
parser = parseopt.Parser()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import py, os
|
||||
from pytest.pluginmanager import PluginManager, canonical_importname
|
||||
from pytest.pluginmanager import Registry, MultiCall, HookRelay, varnames
|
||||
from pytest._core import PluginManager, canonical_importname
|
||||
from pytest._core import Registry, MultiCall, HookRelay, varnames
|
||||
|
||||
|
||||
class TestBootstrapping:
|
||||
|
@ -232,7 +232,6 @@ class TestBootstrapping:
|
|||
class TestPytestPluginInteractions:
|
||||
|
||||
def test_addhooks_conftestplugin(self, testdir):
|
||||
from pytest._config import Config
|
||||
newhooks = testdir.makepyfile(newhooks="""
|
||||
def pytest_myhook(xyz):
|
||||
"new hook"
|
||||
|
@ -245,7 +244,7 @@ class TestPytestPluginInteractions:
|
|||
def pytest_myhook(xyz):
|
||||
return xyz + 1
|
||||
""")
|
||||
config = Config()
|
||||
config = testdir.Config()
|
||||
config._conftest.importconftest(conf)
|
||||
print(config.pluginmanager.getplugins())
|
||||
res = config.hook.pytest_myhook(xyz=10)
|
||||
|
@ -283,12 +282,11 @@ class TestPytestPluginInteractions:
|
|||
])
|
||||
|
||||
def test_do_option_conftestplugin(self, testdir):
|
||||
from pytest._config import Config
|
||||
p = testdir.makepyfile("""
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption('--test123', action="store_true")
|
||||
""")
|
||||
config = Config()
|
||||
config = testdir.Config()
|
||||
config._conftest.importconftest(p)
|
||||
print(config.pluginmanager.getplugins())
|
||||
config.parse([])
|
||||
|
@ -321,8 +319,7 @@ class TestPytestPluginInteractions:
|
|||
])
|
||||
|
||||
def test_do_option_postinitialize(self, testdir):
|
||||
from pytest._config import Config
|
||||
config = Config()
|
||||
config = testdir.Config()
|
||||
config.parse([])
|
||||
config.pluginmanager.do_configure(config=config)
|
||||
assert not hasattr(config.option, 'test123')
|
||||
|
|
Loading…
Reference in New Issue