Merged in hpk42/pytest-patches/plugin_no_pytest (pull request #278)
Refactor pluginmanagement
This commit is contained in:
commit
d8e91d9fee
|
@ -18,6 +18,15 @@
|
||||||
from ``inline_run()`` to allow temporary modules to be reloaded.
|
from ``inline_run()`` to allow temporary modules to be reloaded.
|
||||||
Thanks Eduardo Schettino.
|
Thanks Eduardo Schettino.
|
||||||
|
|
||||||
|
- internally refactor pluginmanager API and code so that there
|
||||||
|
is a clear distinction between a pytest-agnostic rather simple
|
||||||
|
pluginmanager and the PytestPluginManager which adds a lot of
|
||||||
|
behaviour, among it handling of the local conftest files.
|
||||||
|
In terms of documented methods this is a backward compatible
|
||||||
|
change but it might still break 3rd party plugins which relied on
|
||||||
|
details like especially the pluginmanager.add_shutdown() API.
|
||||||
|
Thanks Holger Krekel.
|
||||||
|
|
||||||
2.7.1.dev (compared to 2.7.0)
|
2.7.1.dev (compared to 2.7.0)
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
|
|
|
@ -70,12 +70,11 @@ def pytest_configure(config):
|
||||||
config._assertstate = AssertionState(config, mode)
|
config._assertstate = AssertionState(config, mode)
|
||||||
config._assertstate.hook = hook
|
config._assertstate.hook = hook
|
||||||
config._assertstate.trace("configured with mode set to %r" % (mode,))
|
config._assertstate.trace("configured with mode set to %r" % (mode,))
|
||||||
|
def undo():
|
||||||
|
hook = config._assertstate.hook
|
||||||
def pytest_unconfigure(config):
|
if hook is not None and hook in sys.meta_path:
|
||||||
hook = config._assertstate.hook
|
sys.meta_path.remove(hook)
|
||||||
if hook is not None and hook in sys.meta_path:
|
config.add_cleanup(undo)
|
||||||
sys.meta_path.remove(hook)
|
|
||||||
|
|
||||||
|
|
||||||
def pytest_collection(session):
|
def pytest_collection(session):
|
||||||
|
|
|
@ -37,13 +37,13 @@ def pytest_load_initial_conftests(early_config, parser, args):
|
||||||
pluginmanager.register(capman, "capturemanager")
|
pluginmanager.register(capman, "capturemanager")
|
||||||
|
|
||||||
# make sure that capturemanager is properly reset at final shutdown
|
# make sure that capturemanager is properly reset at final shutdown
|
||||||
pluginmanager.add_shutdown(capman.reset_capturings)
|
early_config.add_cleanup(capman.reset_capturings)
|
||||||
|
|
||||||
# make sure logging does not raise exceptions at the end
|
# make sure logging does not raise exceptions at the end
|
||||||
def silence_logging_at_shutdown():
|
def silence_logging_at_shutdown():
|
||||||
if "logging" in sys.modules:
|
if "logging" in sys.modules:
|
||||||
sys.modules["logging"].raiseExceptions = False
|
sys.modules["logging"].raiseExceptions = False
|
||||||
pluginmanager.add_shutdown(silence_logging_at_shutdown)
|
early_config.add_cleanup(silence_logging_at_shutdown)
|
||||||
|
|
||||||
# finally trigger conftest loading but while capturing (issue93)
|
# finally trigger conftest loading but while capturing (issue93)
|
||||||
capman.init_capturings()
|
capman.init_capturings()
|
||||||
|
|
|
@ -53,6 +53,10 @@ default_plugins = (
|
||||||
"tmpdir monkeypatch recwarn pastebin helpconfig nose assertion genscript "
|
"tmpdir monkeypatch recwarn pastebin helpconfig nose assertion genscript "
|
||||||
"junitxml resultlog doctest").split()
|
"junitxml resultlog doctest").split()
|
||||||
|
|
||||||
|
builtin_plugins = set(default_plugins)
|
||||||
|
builtin_plugins.add("pytester")
|
||||||
|
|
||||||
|
|
||||||
def _preloadplugins():
|
def _preloadplugins():
|
||||||
assert not _preinit
|
assert not _preinit
|
||||||
_preinit.append(get_plugin_manager())
|
_preinit.append(get_plugin_manager())
|
||||||
|
@ -77,19 +81,31 @@ def _prepareconfig(args=None, plugins=None):
|
||||||
raise ValueError("not a string or argument list: %r" % (args,))
|
raise ValueError("not a string or argument list: %r" % (args,))
|
||||||
args = shlex.split(args)
|
args = shlex.split(args)
|
||||||
pluginmanager = get_plugin_manager()
|
pluginmanager = get_plugin_manager()
|
||||||
try:
|
if plugins:
|
||||||
if plugins:
|
for plugin in plugins:
|
||||||
for plugin in plugins:
|
pluginmanager.register(plugin)
|
||||||
pluginmanager.register(plugin)
|
return pluginmanager.hook.pytest_cmdline_parse(
|
||||||
return pluginmanager.hook.pytest_cmdline_parse(
|
pluginmanager=pluginmanager, args=args)
|
||||||
pluginmanager=pluginmanager, args=args)
|
|
||||||
except Exception:
|
def exclude_pytest_names(name):
|
||||||
pluginmanager.ensure_shutdown()
|
return not name.startswith(name) or name == "pytest_plugins" or \
|
||||||
raise
|
name.startswith("pytest_funcarg__")
|
||||||
|
|
||||||
|
|
||||||
class PytestPluginManager(PluginManager):
|
class PytestPluginManager(PluginManager):
|
||||||
def __init__(self, hookspecs=[hookspec]):
|
def __init__(self):
|
||||||
super(PytestPluginManager, self).__init__(hookspecs=hookspecs)
|
super(PytestPluginManager, self).__init__(prefix="pytest_",
|
||||||
|
excludefunc=exclude_pytest_names)
|
||||||
|
self._warnings = []
|
||||||
|
self._plugin_distinfo = []
|
||||||
|
self._globalplugins = []
|
||||||
|
|
||||||
|
# state related to local conftest plugins
|
||||||
|
self._path2confmods = {}
|
||||||
|
self._conftestpath2mod = {}
|
||||||
|
self._confcutdir = None
|
||||||
|
|
||||||
|
self.addhooks(hookspec)
|
||||||
self.register(self)
|
self.register(self)
|
||||||
if os.environ.get('PYTEST_DEBUG'):
|
if os.environ.get('PYTEST_DEBUG'):
|
||||||
err = sys.stderr
|
err = sys.stderr
|
||||||
|
@ -100,6 +116,25 @@ class PytestPluginManager(PluginManager):
|
||||||
pass
|
pass
|
||||||
self.set_tracing(err.write)
|
self.set_tracing(err.write)
|
||||||
|
|
||||||
|
def register(self, plugin, name=None, conftest=False):
|
||||||
|
ret = super(PytestPluginManager, self).register(plugin, name)
|
||||||
|
if ret and not conftest:
|
||||||
|
self._globalplugins.append(plugin)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def _do_register(self, plugin, name):
|
||||||
|
# called from core PluginManager class
|
||||||
|
if hasattr(self, "config"):
|
||||||
|
self.config._register_plugin(plugin, name)
|
||||||
|
return super(PytestPluginManager, self)._do_register(plugin, name)
|
||||||
|
|
||||||
|
def unregister(self, plugin):
|
||||||
|
super(PytestPluginManager, self).unregister(plugin)
|
||||||
|
try:
|
||||||
|
self._globalplugins.remove(plugin)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
def pytest_configure(self, config):
|
def pytest_configure(self, config):
|
||||||
config.addinivalue_line("markers",
|
config.addinivalue_line("markers",
|
||||||
"tryfirst: mark a hook implementation function such that the "
|
"tryfirst: mark a hook implementation function such that the "
|
||||||
|
@ -110,6 +145,172 @@ class PytestPluginManager(PluginManager):
|
||||||
for warning in self._warnings:
|
for warning in self._warnings:
|
||||||
config.warn(code="I1", message=warning)
|
config.warn(code="I1", message=warning)
|
||||||
|
|
||||||
|
#
|
||||||
|
# internal API for local conftest plugin handling
|
||||||
|
#
|
||||||
|
def _set_initial_conftests(self, namespace):
|
||||||
|
""" load initial conftest files given a preparsed "namespace".
|
||||||
|
As conftest files may add their own command line options
|
||||||
|
which have arguments ('--my-opt somepath') we might get some
|
||||||
|
false positives. All builtin and 3rd party plugins will have
|
||||||
|
been loaded, however, so common options will not confuse our logic
|
||||||
|
here.
|
||||||
|
"""
|
||||||
|
current = py.path.local()
|
||||||
|
self._confcutdir = current.join(namespace.confcutdir, abs=True) \
|
||||||
|
if namespace.confcutdir else None
|
||||||
|
testpaths = namespace.file_or_dir
|
||||||
|
foundanchor = False
|
||||||
|
for path in testpaths:
|
||||||
|
path = str(path)
|
||||||
|
# remove node-id syntax
|
||||||
|
i = path.find("::")
|
||||||
|
if i != -1:
|
||||||
|
path = path[:i]
|
||||||
|
anchor = current.join(path, abs=1)
|
||||||
|
if exists(anchor): # we found some file object
|
||||||
|
self._try_load_conftest(anchor)
|
||||||
|
foundanchor = True
|
||||||
|
if not foundanchor:
|
||||||
|
self._try_load_conftest(current)
|
||||||
|
|
||||||
|
def _try_load_conftest(self, anchor):
|
||||||
|
self._getconftestmodules(anchor)
|
||||||
|
# let's also consider test* subdirs
|
||||||
|
if anchor.check(dir=1):
|
||||||
|
for x in anchor.listdir("test*"):
|
||||||
|
if x.check(dir=1):
|
||||||
|
self._getconftestmodules(x)
|
||||||
|
|
||||||
|
def _getconftestmodules(self, path):
|
||||||
|
try:
|
||||||
|
return self._path2confmods[path]
|
||||||
|
except KeyError:
|
||||||
|
clist = []
|
||||||
|
for parent in path.parts():
|
||||||
|
if self._confcutdir and self._confcutdir.relto(parent):
|
||||||
|
continue
|
||||||
|
conftestpath = parent.join("conftest.py")
|
||||||
|
if conftestpath.check(file=1):
|
||||||
|
mod = self._importconftest(conftestpath)
|
||||||
|
clist.append(mod)
|
||||||
|
self._path2confmods[path] = clist
|
||||||
|
return clist
|
||||||
|
|
||||||
|
def _rget_with_confmod(self, name, path):
|
||||||
|
modules = self._getconftestmodules(path)
|
||||||
|
for mod in reversed(modules):
|
||||||
|
try:
|
||||||
|
return mod, getattr(mod, name)
|
||||||
|
except AttributeError:
|
||||||
|
continue
|
||||||
|
raise KeyError(name)
|
||||||
|
|
||||||
|
def _importconftest(self, conftestpath):
|
||||||
|
try:
|
||||||
|
return self._conftestpath2mod[conftestpath]
|
||||||
|
except KeyError:
|
||||||
|
pkgpath = conftestpath.pypkgpath()
|
||||||
|
if pkgpath is None:
|
||||||
|
_ensure_removed_sysmodule(conftestpath.purebasename)
|
||||||
|
try:
|
||||||
|
mod = conftestpath.pyimport()
|
||||||
|
except Exception:
|
||||||
|
raise ConftestImportFailure(conftestpath, sys.exc_info())
|
||||||
|
self._conftestpath2mod[conftestpath] = mod
|
||||||
|
dirpath = conftestpath.dirpath()
|
||||||
|
if dirpath in self._path2confmods:
|
||||||
|
for path, mods in self._path2confmods.items():
|
||||||
|
if path and path.relto(dirpath) or path == dirpath:
|
||||||
|
assert mod not in mods
|
||||||
|
mods.append(mod)
|
||||||
|
self.trace("loaded conftestmodule %r" %(mod))
|
||||||
|
self.consider_conftest(mod)
|
||||||
|
return mod
|
||||||
|
|
||||||
|
#
|
||||||
|
# API for bootstrapping plugin loading
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
def consider_setuptools_entrypoints(self):
|
||||||
|
try:
|
||||||
|
from pkg_resources import iter_entry_points, DistributionNotFound
|
||||||
|
except ImportError:
|
||||||
|
return # XXX issue a warning
|
||||||
|
for ep in iter_entry_points('pytest11'):
|
||||||
|
name = ep.name
|
||||||
|
if name.startswith("pytest_"):
|
||||||
|
name = name[7:]
|
||||||
|
if ep.name in self._name2plugin or name in self._name2plugin:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
plugin = ep.load()
|
||||||
|
except DistributionNotFound:
|
||||||
|
continue
|
||||||
|
self._plugin_distinfo.append((ep.dist, plugin))
|
||||||
|
self.register(plugin, name=name)
|
||||||
|
|
||||||
|
def consider_preparse(self, args):
|
||||||
|
for opt1,opt2 in zip(args, args[1:]):
|
||||||
|
if opt1 == "-p":
|
||||||
|
self.consider_pluginarg(opt2)
|
||||||
|
|
||||||
|
def consider_pluginarg(self, arg):
|
||||||
|
if arg.startswith("no:"):
|
||||||
|
name = arg[3:]
|
||||||
|
plugin = self.getplugin(name)
|
||||||
|
if plugin is not None:
|
||||||
|
self.unregister(plugin)
|
||||||
|
self._name2plugin[name] = -1
|
||||||
|
else:
|
||||||
|
if self.getplugin(arg) is None:
|
||||||
|
self.import_plugin(arg)
|
||||||
|
|
||||||
|
def consider_conftest(self, conftestmodule):
|
||||||
|
if self.register(conftestmodule, name=conftestmodule.__file__,
|
||||||
|
conftest=True):
|
||||||
|
self.consider_module(conftestmodule)
|
||||||
|
|
||||||
|
def consider_env(self):
|
||||||
|
self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS"))
|
||||||
|
|
||||||
|
def consider_module(self, mod):
|
||||||
|
self._import_plugin_specs(getattr(mod, "pytest_plugins", None))
|
||||||
|
|
||||||
|
def _import_plugin_specs(self, spec):
|
||||||
|
if spec:
|
||||||
|
if isinstance(spec, str):
|
||||||
|
spec = spec.split(",")
|
||||||
|
for import_spec in spec:
|
||||||
|
self.import_plugin(import_spec)
|
||||||
|
|
||||||
|
def import_plugin(self, modname):
|
||||||
|
# most often modname refers to builtin modules, e.g. "pytester",
|
||||||
|
# "terminal" or "capture". Those plugins are registered under their
|
||||||
|
# basename for historic purposes but must be imported with the
|
||||||
|
# _pytest prefix.
|
||||||
|
assert isinstance(modname, str)
|
||||||
|
if self.getplugin(modname) is not None:
|
||||||
|
return
|
||||||
|
if modname in builtin_plugins:
|
||||||
|
importspec = "_pytest." + modname
|
||||||
|
else:
|
||||||
|
importspec = modname
|
||||||
|
try:
|
||||||
|
__import__(importspec)
|
||||||
|
except ImportError:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
import pytest
|
||||||
|
if not hasattr(pytest, 'skip') or not isinstance(e, pytest.skip.Exception):
|
||||||
|
raise
|
||||||
|
self._warnings.append("skipped plugin %r: %s" %((modname, e.msg)))
|
||||||
|
else:
|
||||||
|
mod = sys.modules[importspec]
|
||||||
|
self.register(mod, modname)
|
||||||
|
self.consider_module(mod)
|
||||||
|
|
||||||
|
|
||||||
class Parser:
|
class Parser:
|
||||||
""" Parser for command line arguments and ini-file values. """
|
""" Parser for command line arguments and ini-file values. """
|
||||||
|
@ -464,96 +665,6 @@ class DropShorterLongHelpFormatter(argparse.HelpFormatter):
|
||||||
return action._formatted_action_invocation
|
return action._formatted_action_invocation
|
||||||
|
|
||||||
|
|
||||||
class Conftest(object):
|
|
||||||
""" the single place for accessing values and interacting
|
|
||||||
towards conftest modules from pytest objects.
|
|
||||||
"""
|
|
||||||
def __init__(self, onimport=None):
|
|
||||||
self._path2confmods = {}
|
|
||||||
self._onimport = onimport
|
|
||||||
self._conftestpath2mod = {}
|
|
||||||
self._confcutdir = None
|
|
||||||
|
|
||||||
def setinitial(self, namespace):
|
|
||||||
""" load initial conftest files given a preparsed "namespace".
|
|
||||||
As conftest files may add their own command line options
|
|
||||||
which have arguments ('--my-opt somepath') we might get some
|
|
||||||
false positives. All builtin and 3rd party plugins will have
|
|
||||||
been loaded, however, so common options will not confuse our logic
|
|
||||||
here.
|
|
||||||
"""
|
|
||||||
current = py.path.local()
|
|
||||||
self._confcutdir = current.join(namespace.confcutdir, abs=True) \
|
|
||||||
if namespace.confcutdir else None
|
|
||||||
testpaths = namespace.file_or_dir
|
|
||||||
foundanchor = False
|
|
||||||
for path in testpaths:
|
|
||||||
path = str(path)
|
|
||||||
# remove node-id syntax
|
|
||||||
i = path.find("::")
|
|
||||||
if i != -1:
|
|
||||||
path = path[:i]
|
|
||||||
anchor = current.join(path, abs=1)
|
|
||||||
if exists(anchor): # we found some file object
|
|
||||||
self._try_load_conftest(anchor)
|
|
||||||
foundanchor = True
|
|
||||||
if not foundanchor:
|
|
||||||
self._try_load_conftest(current)
|
|
||||||
|
|
||||||
def _try_load_conftest(self, anchor):
|
|
||||||
self.getconftestmodules(anchor)
|
|
||||||
# let's also consider test* subdirs
|
|
||||||
if anchor.check(dir=1):
|
|
||||||
for x in anchor.listdir("test*"):
|
|
||||||
if x.check(dir=1):
|
|
||||||
self.getconftestmodules(x)
|
|
||||||
|
|
||||||
def getconftestmodules(self, path):
|
|
||||||
try:
|
|
||||||
return self._path2confmods[path]
|
|
||||||
except KeyError:
|
|
||||||
clist = []
|
|
||||||
for parent in path.parts():
|
|
||||||
if self._confcutdir and self._confcutdir.relto(parent):
|
|
||||||
continue
|
|
||||||
conftestpath = parent.join("conftest.py")
|
|
||||||
if conftestpath.check(file=1):
|
|
||||||
mod = self.importconftest(conftestpath)
|
|
||||||
clist.append(mod)
|
|
||||||
self._path2confmods[path] = clist
|
|
||||||
return clist
|
|
||||||
|
|
||||||
def rget_with_confmod(self, name, path):
|
|
||||||
modules = self.getconftestmodules(path)
|
|
||||||
for mod in reversed(modules):
|
|
||||||
try:
|
|
||||||
return mod, getattr(mod, name)
|
|
||||||
except AttributeError:
|
|
||||||
continue
|
|
||||||
raise KeyError(name)
|
|
||||||
|
|
||||||
def importconftest(self, conftestpath):
|
|
||||||
try:
|
|
||||||
return self._conftestpath2mod[conftestpath]
|
|
||||||
except KeyError:
|
|
||||||
pkgpath = conftestpath.pypkgpath()
|
|
||||||
if pkgpath is None:
|
|
||||||
_ensure_removed_sysmodule(conftestpath.purebasename)
|
|
||||||
try:
|
|
||||||
mod = conftestpath.pyimport()
|
|
||||||
except Exception:
|
|
||||||
raise ConftestImportFailure(conftestpath, sys.exc_info())
|
|
||||||
self._conftestpath2mod[conftestpath] = mod
|
|
||||||
dirpath = conftestpath.dirpath()
|
|
||||||
if dirpath in self._path2confmods:
|
|
||||||
for path, mods in self._path2confmods.items():
|
|
||||||
if path and path.relto(dirpath) or path == dirpath:
|
|
||||||
assert mod not in mods
|
|
||||||
mods.append(mod)
|
|
||||||
if self._onimport:
|
|
||||||
self._onimport(mod)
|
|
||||||
return mod
|
|
||||||
|
|
||||||
|
|
||||||
def _ensure_removed_sysmodule(modname):
|
def _ensure_removed_sysmodule(modname):
|
||||||
try:
|
try:
|
||||||
|
@ -589,13 +700,11 @@ class Config(object):
|
||||||
#: a pluginmanager instance
|
#: a pluginmanager instance
|
||||||
self.pluginmanager = pluginmanager
|
self.pluginmanager = pluginmanager
|
||||||
self.trace = self.pluginmanager.trace.root.get("config")
|
self.trace = self.pluginmanager.trace.root.get("config")
|
||||||
self._conftest = Conftest(onimport=self._onimportconftest)
|
|
||||||
self.hook = self.pluginmanager.hook
|
self.hook = self.pluginmanager.hook
|
||||||
self._inicache = {}
|
self._inicache = {}
|
||||||
self._opt2dest = {}
|
self._opt2dest = {}
|
||||||
self._cleanup = []
|
self._cleanup = []
|
||||||
self.pluginmanager.register(self, "pytestconfig")
|
self.pluginmanager.register(self, "pytestconfig")
|
||||||
self.pluginmanager.set_register_callback(self._register_plugin)
|
|
||||||
self._configured = False
|
self._configured = False
|
||||||
|
|
||||||
def _register_plugin(self, plugin, name):
|
def _register_plugin(self, plugin, name):
|
||||||
|
@ -612,16 +721,23 @@ class Config(object):
|
||||||
if self._configured:
|
if self._configured:
|
||||||
call_plugin(plugin, "pytest_configure", {'config': self})
|
call_plugin(plugin, "pytest_configure", {'config': self})
|
||||||
|
|
||||||
def do_configure(self):
|
def add_cleanup(self, func):
|
||||||
|
""" Add a function to be called when the config object gets out of
|
||||||
|
use (usually coninciding with pytest_unconfigure)."""
|
||||||
|
self._cleanup.append(func)
|
||||||
|
|
||||||
|
def _do_configure(self):
|
||||||
assert not self._configured
|
assert not self._configured
|
||||||
self._configured = True
|
self._configured = True
|
||||||
self.hook.pytest_configure(config=self)
|
self.hook.pytest_configure(config=self)
|
||||||
|
|
||||||
def do_unconfigure(self):
|
def _ensure_unconfigure(self):
|
||||||
assert self._configured
|
if self._configured:
|
||||||
self._configured = False
|
self._configured = False
|
||||||
self.hook.pytest_unconfigure(config=self)
|
self.hook.pytest_unconfigure(config=self)
|
||||||
self.pluginmanager.ensure_shutdown()
|
while self._cleanup:
|
||||||
|
fin = self._cleanup.pop()
|
||||||
|
fin()
|
||||||
|
|
||||||
def warn(self, code, message):
|
def warn(self, code, message):
|
||||||
""" generate a warning for this test session. """
|
""" generate a warning for this test session. """
|
||||||
|
@ -636,11 +752,6 @@ class Config(object):
|
||||||
self.parse(args)
|
self.parse(args)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def pytest_unconfigure(config):
|
|
||||||
while config._cleanup:
|
|
||||||
fin = config._cleanup.pop()
|
|
||||||
fin()
|
|
||||||
|
|
||||||
def notify_exception(self, excinfo, option=None):
|
def notify_exception(self, excinfo, option=None):
|
||||||
if option and option.fulltrace:
|
if option and option.fulltrace:
|
||||||
style = "long"
|
style = "long"
|
||||||
|
@ -675,10 +786,6 @@ class Config(object):
|
||||||
config.pluginmanager.consider_pluginarg(x)
|
config.pluginmanager.consider_pluginarg(x)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
def _onimportconftest(self, conftestmodule):
|
|
||||||
self.trace("loaded conftestmodule %r" %(conftestmodule,))
|
|
||||||
self.pluginmanager.consider_conftest(conftestmodule)
|
|
||||||
|
|
||||||
def _processopt(self, opt):
|
def _processopt(self, opt):
|
||||||
for name in opt._short_opts + opt._long_opts:
|
for name in opt._short_opts + opt._long_opts:
|
||||||
self._opt2dest[name] = opt.dest
|
self._opt2dest[name] = opt.dest
|
||||||
|
@ -688,11 +795,11 @@ class Config(object):
|
||||||
setattr(self.option, opt.dest, opt.default)
|
setattr(self.option, opt.dest, opt.default)
|
||||||
|
|
||||||
def _getmatchingplugins(self, fspath):
|
def _getmatchingplugins(self, fspath):
|
||||||
return self.pluginmanager._plugins + \
|
return self.pluginmanager._globalplugins + \
|
||||||
self._conftest.getconftestmodules(fspath)
|
self.pluginmanager._getconftestmodules(fspath)
|
||||||
|
|
||||||
def pytest_load_initial_conftests(self, early_config):
|
def pytest_load_initial_conftests(self, early_config):
|
||||||
self._conftest.setinitial(early_config.known_args_namespace)
|
self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)
|
||||||
pytest_load_initial_conftests.trylast = True
|
pytest_load_initial_conftests.trylast = True
|
||||||
|
|
||||||
def _initini(self, args):
|
def _initini(self, args):
|
||||||
|
@ -799,7 +906,7 @@ class Config(object):
|
||||||
|
|
||||||
def _getconftest_pathlist(self, name, path):
|
def _getconftest_pathlist(self, name, path):
|
||||||
try:
|
try:
|
||||||
mod, relroots = self._conftest.rget_with_confmod(name, path)
|
mod, relroots = self.pluginmanager._rget_with_confmod(name, path)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return None
|
return None
|
||||||
modpath = py.path.local(mod.__file__).dirpath()
|
modpath = py.path.local(mod.__file__).dirpath()
|
||||||
|
@ -933,3 +1040,4 @@ def setns(obj, dic):
|
||||||
#if obj != pytest:
|
#if obj != pytest:
|
||||||
# pytest.__all__.append(name)
|
# pytest.__all__.append(name)
|
||||||
setattr(pytest, name, value)
|
setattr(pytest, name, value)
|
||||||
|
|
||||||
|
|
350
_pytest/core.py
350
_pytest/core.py
|
@ -1,14 +1,9 @@
|
||||||
"""
|
"""
|
||||||
pytest PluginManager, basic initialization and tracing.
|
PluginManager, basic initialization and tracing.
|
||||||
"""
|
"""
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
import inspect
|
import inspect
|
||||||
import py
|
import py
|
||||||
# don't import pytest to avoid circular imports
|
|
||||||
|
|
||||||
assert py.__version__.split(".")[:2] >= ['1', '4'], ("installation problem: "
|
|
||||||
"%s is too old, remove or upgrade 'py'" % (py.__version__))
|
|
||||||
|
|
||||||
py3 = sys.version_info > (3,0)
|
py3 = sys.version_info > (3,0)
|
||||||
|
|
||||||
|
@ -139,202 +134,133 @@ class CallOutcome:
|
||||||
|
|
||||||
|
|
||||||
class PluginManager(object):
|
class PluginManager(object):
|
||||||
def __init__(self, hookspecs=None, prefix="pytest_"):
|
""" Core Pluginmanager class which manages registration
|
||||||
|
of plugin objects and 1:N hook calling.
|
||||||
|
|
||||||
|
You can register new hooks by calling ``addhooks(module_or_class)``.
|
||||||
|
You can register plugin objects (which contain hooks) by calling
|
||||||
|
``register(plugin)``. The Pluginmanager is initialized with a
|
||||||
|
prefix that is searched for in the names of the dict of registered
|
||||||
|
plugin objects. An optional excludefunc allows to blacklist names which
|
||||||
|
are not considered as hooks despite a matching prefix.
|
||||||
|
|
||||||
|
For debugging purposes you can call ``set_tracing(writer)``
|
||||||
|
which will subsequently send debug information to the specified
|
||||||
|
write function.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, prefix, excludefunc=None):
|
||||||
|
self._prefix = prefix
|
||||||
|
self._excludefunc = excludefunc
|
||||||
self._name2plugin = {}
|
self._name2plugin = {}
|
||||||
self._plugins = []
|
self._plugins = []
|
||||||
self._conftestplugins = []
|
|
||||||
self._plugin2hookcallers = {}
|
self._plugin2hookcallers = {}
|
||||||
self._warnings = []
|
|
||||||
self.trace = TagTracer().get("pluginmanage")
|
self.trace = TagTracer().get("pluginmanage")
|
||||||
self._plugin_distinfo = []
|
self.hook = HookRelay(pm=self)
|
||||||
self._shutdown = []
|
|
||||||
self.hook = HookRelay(hookspecs or [], pm=self, prefix=prefix)
|
|
||||||
|
|
||||||
def set_tracing(self, writer):
|
def set_tracing(self, writer):
|
||||||
|
""" turn on tracing to the given writer method and
|
||||||
|
return an undo function. """
|
||||||
self.trace.root.setwriter(writer)
|
self.trace.root.setwriter(writer)
|
||||||
# reconfigure HookCalling to perform tracing
|
# reconfigure HookCalling to perform tracing
|
||||||
assert not hasattr(self, "_wrapping")
|
assert not hasattr(self, "_wrapping")
|
||||||
self._wrapping = True
|
self._wrapping = True
|
||||||
|
|
||||||
|
hooktrace = self.hook.trace
|
||||||
|
|
||||||
def _docall(self, methods, kwargs):
|
def _docall(self, methods, kwargs):
|
||||||
trace = self.hookrelay.trace
|
hooktrace.root.indent += 1
|
||||||
trace.root.indent += 1
|
hooktrace(self.name, kwargs)
|
||||||
trace(self.name, kwargs)
|
|
||||||
box = yield
|
box = yield
|
||||||
if box.excinfo is None:
|
if box.excinfo is None:
|
||||||
trace("finish", self.name, "-->", box.result)
|
hooktrace("finish", self.name, "-->", box.result)
|
||||||
trace.root.indent -= 1
|
hooktrace.root.indent -= 1
|
||||||
|
|
||||||
undo = add_method_wrapper(HookCaller, _docall)
|
return add_method_wrapper(HookCaller, _docall)
|
||||||
self.add_shutdown(undo)
|
|
||||||
|
|
||||||
def do_configure(self, config):
|
def make_hook_caller(self, name, plugins):
|
||||||
# backward compatibility
|
caller = getattr(self.hook, name)
|
||||||
config.do_configure()
|
methods = self.listattr(name, plugins=plugins)
|
||||||
|
return HookCaller(caller.name, caller.firstresult,
|
||||||
|
argnames=caller.argnames, methods=methods)
|
||||||
|
|
||||||
def set_register_callback(self, callback):
|
def register(self, plugin, name=None):
|
||||||
assert not hasattr(self, "_registercallback")
|
""" Register a plugin with the given name and ensure that all its
|
||||||
self._registercallback = callback
|
hook implementations are integrated. If the name is not specified
|
||||||
|
we use the ``__name__`` attribute of the plugin object or, if that
|
||||||
def register(self, plugin, name=None, prepend=False, conftest=False):
|
doesn't exist, the id of the plugin. This method will raise a
|
||||||
|
ValueError if the eventual name is already registered. """
|
||||||
|
name = name or self._get_canonical_name(plugin)
|
||||||
if self._name2plugin.get(name, None) == -1:
|
if self._name2plugin.get(name, None) == -1:
|
||||||
return
|
return
|
||||||
name = name or getattr(plugin, '__name__', str(id(plugin)))
|
if self.hasplugin(name):
|
||||||
if self.isregistered(plugin, name):
|
|
||||||
raise ValueError("Plugin already registered: %s=%s\n%s" %(
|
raise ValueError("Plugin already registered: %s=%s\n%s" %(
|
||||||
name, plugin, self._name2plugin))
|
name, plugin, self._name2plugin))
|
||||||
#self.trace("registering", name, plugin)
|
#self.trace("registering", name, plugin)
|
||||||
reg = getattr(self, "_registercallback", None)
|
# allow subclasses to intercept here by calling a helper
|
||||||
if reg is not None:
|
return self._do_register(plugin, name)
|
||||||
reg(plugin, name) # may call addhooks
|
|
||||||
hookcallers = list(self.hook._scan_plugin(plugin))
|
def _do_register(self, plugin, name):
|
||||||
|
hookcallers = list(self._scan_plugin(plugin))
|
||||||
self._plugin2hookcallers[plugin] = hookcallers
|
self._plugin2hookcallers[plugin] = hookcallers
|
||||||
self._name2plugin[name] = plugin
|
self._name2plugin[name] = plugin
|
||||||
if conftest:
|
self._plugins.append(plugin)
|
||||||
self._conftestplugins.append(plugin)
|
# rescan all methods for the hookcallers we found
|
||||||
else:
|
|
||||||
if not prepend:
|
|
||||||
self._plugins.append(plugin)
|
|
||||||
else:
|
|
||||||
self._plugins.insert(0, plugin)
|
|
||||||
# finally make sure that the methods of the new plugin take part
|
|
||||||
for hookcaller in hookcallers:
|
for hookcaller in hookcallers:
|
||||||
hookcaller.scan_methods()
|
self._scan_methods(hookcaller)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def unregister(self, plugin):
|
def unregister(self, plugin):
|
||||||
try:
|
""" unregister the plugin object and all its contained hook implementations
|
||||||
self._plugins.remove(plugin)
|
from internal data structures. """
|
||||||
except KeyError:
|
self._plugins.remove(plugin)
|
||||||
self._conftestplugins.remove(plugin)
|
|
||||||
for name, value in list(self._name2plugin.items()):
|
for name, value in list(self._name2plugin.items()):
|
||||||
if value == plugin:
|
if value == plugin:
|
||||||
del self._name2plugin[name]
|
del self._name2plugin[name]
|
||||||
hookcallers = self._plugin2hookcallers.pop(plugin)
|
hookcallers = self._plugin2hookcallers.pop(plugin)
|
||||||
for hookcaller in hookcallers:
|
for hookcaller in hookcallers:
|
||||||
hookcaller.scan_methods()
|
self._scan_methods(hookcaller)
|
||||||
|
|
||||||
def add_shutdown(self, func):
|
def addhooks(self, module_or_class):
|
||||||
self._shutdown.append(func)
|
""" add new hook definitions from the given module_or_class using
|
||||||
|
the prefix/excludefunc with which the PluginManager was initialized. """
|
||||||
def ensure_shutdown(self):
|
isclass = int(inspect.isclass(module_or_class))
|
||||||
while self._shutdown:
|
names = []
|
||||||
func = self._shutdown.pop()
|
for name in dir(module_or_class):
|
||||||
func()
|
if name.startswith(self._prefix):
|
||||||
self._plugins = self._conftestplugins = []
|
method = module_or_class.__dict__[name]
|
||||||
self._name2plugin.clear()
|
firstresult = getattr(method, 'firstresult', False)
|
||||||
|
hc = HookCaller(name, firstresult=firstresult,
|
||||||
def isregistered(self, plugin, name=None):
|
argnames=varnames(method, startindex=isclass))
|
||||||
if self.getplugin(name) is not None:
|
setattr(self.hook, name, hc)
|
||||||
return True
|
names.append(name)
|
||||||
return plugin in self._plugins or plugin in self._conftestplugins
|
if not names:
|
||||||
|
raise ValueError("did not find new %r hooks in %r"
|
||||||
def addhooks(self, spec, prefix="pytest_"):
|
%(self._prefix, module_or_class))
|
||||||
self.hook._addhooks(spec, prefix=prefix)
|
|
||||||
|
|
||||||
def getplugins(self):
|
def getplugins(self):
|
||||||
return self._plugins + self._conftestplugins
|
""" return the complete list of registered plugins. NOTE that
|
||||||
|
you will get the internal list and need to make a copy if you
|
||||||
|
modify the list."""
|
||||||
|
return self._plugins
|
||||||
|
|
||||||
def skipifmissing(self, name):
|
def isregistered(self, plugin):
|
||||||
if not self.hasplugin(name):
|
""" Return True if the plugin is already registered under its
|
||||||
import pytest
|
canonical name. """
|
||||||
pytest.skip("plugin %r is missing" % name)
|
return self.hasplugin(self._get_canonical_name(plugin)) or \
|
||||||
|
plugin in self._plugins
|
||||||
|
|
||||||
def hasplugin(self, name):
|
def hasplugin(self, name):
|
||||||
return bool(self.getplugin(name))
|
""" Return True if there is a registered with the given name. """
|
||||||
|
return name in self._name2plugin
|
||||||
|
|
||||||
def getplugin(self, name):
|
def getplugin(self, name):
|
||||||
if name is None:
|
""" Return a plugin or None for the given name. """
|
||||||
return None
|
return self._name2plugin.get(name)
|
||||||
try:
|
|
||||||
return self._name2plugin[name]
|
|
||||||
except KeyError:
|
|
||||||
return self._name2plugin.get("_pytest." + name, None)
|
|
||||||
|
|
||||||
# API for bootstrapping
|
|
||||||
#
|
|
||||||
def _envlist(self, varname):
|
|
||||||
val = 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, DistributionNotFound
|
|
||||||
except ImportError:
|
|
||||||
return # XXX issue a warning
|
|
||||||
for ep in iter_entry_points('pytest11'):
|
|
||||||
name = ep.name
|
|
||||||
if name.startswith("pytest_"):
|
|
||||||
name = name[7:]
|
|
||||||
if ep.name in self._name2plugin or name in self._name2plugin:
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
plugin = ep.load()
|
|
||||||
except DistributionNotFound:
|
|
||||||
continue
|
|
||||||
self._plugin_distinfo.append((ep.dist, plugin))
|
|
||||||
self.register(plugin, name=name)
|
|
||||||
|
|
||||||
def consider_preparse(self, args):
|
|
||||||
for opt1,opt2 in zip(args, args[1:]):
|
|
||||||
if opt1 == "-p":
|
|
||||||
self.consider_pluginarg(opt2)
|
|
||||||
|
|
||||||
def consider_pluginarg(self, arg):
|
|
||||||
if arg.startswith("no:"):
|
|
||||||
name = arg[3:]
|
|
||||||
plugin = self.getplugin(name)
|
|
||||||
if plugin is not None:
|
|
||||||
self.unregister(plugin)
|
|
||||||
self._name2plugin[name] = -1
|
|
||||||
else:
|
|
||||||
if self.getplugin(arg) is None:
|
|
||||||
self.import_plugin(arg)
|
|
||||||
|
|
||||||
def consider_conftest(self, conftestmodule):
|
|
||||||
if self.register(conftestmodule, name=conftestmodule.__file__,
|
|
||||||
conftest=True):
|
|
||||||
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, modname):
|
|
||||||
assert isinstance(modname, str)
|
|
||||||
if self.getplugin(modname) is not None:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
mod = importplugin(modname)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
raise
|
|
||||||
except ImportError:
|
|
||||||
if modname.startswith("pytest_"):
|
|
||||||
return self.import_plugin(modname[7:])
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
e = sys.exc_info()[1]
|
|
||||||
import pytest
|
|
||||||
if not hasattr(pytest, 'skip') or not isinstance(e, pytest.skip.Exception):
|
|
||||||
raise
|
|
||||||
self._warnings.append("skipped plugin %r: %s" %((modname, e.msg)))
|
|
||||||
else:
|
|
||||||
self.register(mod, modname)
|
|
||||||
self.consider_module(mod)
|
|
||||||
|
|
||||||
def listattr(self, attrname, plugins=None):
|
def listattr(self, attrname, plugins=None):
|
||||||
if plugins is None:
|
if plugins is None:
|
||||||
plugins = self._plugins + self._conftestplugins
|
plugins = self._plugins
|
||||||
l = []
|
l = []
|
||||||
last = []
|
last = []
|
||||||
wrappers = []
|
wrappers = []
|
||||||
|
@ -355,20 +281,43 @@ class PluginManager(object):
|
||||||
l.extend(wrappers)
|
l.extend(wrappers)
|
||||||
return l
|
return l
|
||||||
|
|
||||||
|
def _scan_methods(self, hookcaller):
|
||||||
|
hookcaller.methods = self.listattr(hookcaller.name)
|
||||||
|
|
||||||
def call_plugin(self, plugin, methname, kwargs):
|
def call_plugin(self, plugin, methname, kwargs):
|
||||||
return MultiCall(methods=self.listattr(methname, plugins=[plugin]),
|
return MultiCall(methods=self.listattr(methname, plugins=[plugin]),
|
||||||
kwargs=kwargs, firstresult=True).execute()
|
kwargs=kwargs, firstresult=True).execute()
|
||||||
|
|
||||||
|
|
||||||
def importplugin(importspec):
|
def _scan_plugin(self, plugin):
|
||||||
name = importspec
|
def fail(msg, *args):
|
||||||
try:
|
name = getattr(plugin, '__name__', plugin)
|
||||||
mod = "_pytest." + name
|
raise PluginValidationError("plugin %r\n%s" %(name, msg % args))
|
||||||
__import__(mod)
|
|
||||||
return sys.modules[mod]
|
for name in dir(plugin):
|
||||||
except ImportError:
|
if name[0] == "_" or not name.startswith(self._prefix):
|
||||||
__import__(importspec)
|
continue
|
||||||
return sys.modules[importspec]
|
hook = getattr(self.hook, name, None)
|
||||||
|
method = getattr(plugin, name)
|
||||||
|
if hook is None:
|
||||||
|
if self._excludefunc is not None and self._excludefunc(name):
|
||||||
|
continue
|
||||||
|
if getattr(method, 'optionalhook', False):
|
||||||
|
continue
|
||||||
|
fail("found unknown hook: %r", name)
|
||||||
|
for arg in varnames(method):
|
||||||
|
if arg not in hook.argnames:
|
||||||
|
fail("argument %r not available\n"
|
||||||
|
"actual definition: %s\n"
|
||||||
|
"available hookargs: %s",
|
||||||
|
arg, formatdef(method),
|
||||||
|
", ".join(hook.argnames))
|
||||||
|
yield hook
|
||||||
|
|
||||||
|
def _get_canonical_name(self, plugin):
|
||||||
|
return getattr(plugin, "__name__", None) or str(id(plugin))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class MultiCall:
|
class MultiCall:
|
||||||
""" execute a call into multiple python functions/methods. """
|
""" execute a call into multiple python functions/methods. """
|
||||||
|
@ -441,65 +390,13 @@ def varnames(func, startindex=None):
|
||||||
|
|
||||||
|
|
||||||
class HookRelay:
|
class HookRelay:
|
||||||
def __init__(self, hookspecs, pm, prefix="pytest_"):
|
def __init__(self, pm):
|
||||||
if not isinstance(hookspecs, list):
|
|
||||||
hookspecs = [hookspecs]
|
|
||||||
self._pm = pm
|
self._pm = pm
|
||||||
self.trace = pm.trace.root.get("hook")
|
self.trace = pm.trace.root.get("hook")
|
||||||
self.prefix = prefix
|
|
||||||
for hookspec in hookspecs:
|
|
||||||
self._addhooks(hookspec, prefix)
|
|
||||||
|
|
||||||
def _addhooks(self, hookspec, prefix):
|
|
||||||
added = False
|
|
||||||
isclass = int(inspect.isclass(hookspec))
|
|
||||||
for name, method in vars(hookspec).items():
|
|
||||||
if name.startswith(prefix):
|
|
||||||
firstresult = getattr(method, 'firstresult', False)
|
|
||||||
hc = HookCaller(self, name, firstresult=firstresult,
|
|
||||||
argnames=varnames(method, startindex=isclass))
|
|
||||||
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, hookspec,))
|
|
||||||
|
|
||||||
def _getcaller(self, name, plugins):
|
|
||||||
caller = getattr(self, name)
|
|
||||||
methods = self._pm.listattr(name, plugins=plugins)
|
|
||||||
if methods:
|
|
||||||
return caller.new_cached_caller(methods)
|
|
||||||
return caller
|
|
||||||
|
|
||||||
def _scan_plugin(self, plugin):
|
|
||||||
def fail(msg, *args):
|
|
||||||
name = getattr(plugin, '__name__', plugin)
|
|
||||||
raise PluginValidationError("plugin %r\n%s" %(name, msg % args))
|
|
||||||
|
|
||||||
for name in dir(plugin):
|
|
||||||
if not name.startswith(self.prefix):
|
|
||||||
continue
|
|
||||||
hook = getattr(self, name, None)
|
|
||||||
method = getattr(plugin, name)
|
|
||||||
if hook is None:
|
|
||||||
is_optional = getattr(method, 'optionalhook', False)
|
|
||||||
if not isgenerichook(name) and not is_optional:
|
|
||||||
fail("found unknown hook: %r", name)
|
|
||||||
continue
|
|
||||||
for arg in varnames(method):
|
|
||||||
if arg not in hook.argnames:
|
|
||||||
fail("argument %r not available\n"
|
|
||||||
"actual definition: %s\n"
|
|
||||||
"available hookargs: %s",
|
|
||||||
arg, formatdef(method),
|
|
||||||
", ".join(hook.argnames))
|
|
||||||
yield hook
|
|
||||||
|
|
||||||
|
|
||||||
class HookCaller:
|
class HookCaller:
|
||||||
def __init__(self, hookrelay, name, firstresult, argnames, methods=()):
|
def __init__(self, name, firstresult, argnames, methods=()):
|
||||||
self.hookrelay = hookrelay
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.firstresult = firstresult
|
self.firstresult = firstresult
|
||||||
self.argnames = ["__multicall__"]
|
self.argnames = ["__multicall__"]
|
||||||
|
@ -507,16 +404,9 @@ class HookCaller:
|
||||||
assert "self" not in argnames # sanity check
|
assert "self" not in argnames # sanity check
|
||||||
self.methods = methods
|
self.methods = methods
|
||||||
|
|
||||||
def new_cached_caller(self, methods):
|
|
||||||
return HookCaller(self.hookrelay, self.name, self.firstresult,
|
|
||||||
argnames=self.argnames, methods=methods)
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<HookCaller %r>" %(self.name,)
|
return "<HookCaller %r>" %(self.name,)
|
||||||
|
|
||||||
def scan_methods(self):
|
|
||||||
self.methods = self.hookrelay._pm.listattr(self.name)
|
|
||||||
|
|
||||||
def __call__(self, **kwargs):
|
def __call__(self, **kwargs):
|
||||||
return self._docall(self.methods, kwargs)
|
return self._docall(self.methods, kwargs)
|
||||||
|
|
||||||
|
@ -531,13 +421,9 @@ class HookCaller:
|
||||||
class PluginValidationError(Exception):
|
class PluginValidationError(Exception):
|
||||||
""" plugin failed validation. """
|
""" plugin failed validation. """
|
||||||
|
|
||||||
def isgenerichook(name):
|
|
||||||
return name == "pytest_plugins" or \
|
|
||||||
name.startswith("pytest_funcarg__")
|
|
||||||
|
|
||||||
def formatdef(func):
|
def formatdef(func):
|
||||||
return "%s%s" % (
|
return "%s%s" % (
|
||||||
func.__name__,
|
func.__name__,
|
||||||
inspect.formatargspec(*inspect.getargspec(func))
|
inspect.formatargspec(*inspect.getargspec(func))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -132,7 +132,7 @@ class DoctestModule(pytest.File):
|
||||||
def collect(self):
|
def collect(self):
|
||||||
import doctest
|
import doctest
|
||||||
if self.fspath.basename == "conftest.py":
|
if self.fspath.basename == "conftest.py":
|
||||||
module = self.config._conftest.importconftest(self.fspath)
|
module = self.config._conftest._importconftest(self.fspath)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
module = self.fspath.pyimport()
|
module = self.fspath.pyimport()
|
||||||
|
|
|
@ -28,24 +28,20 @@ def pytest_cmdline_parse():
|
||||||
config = outcome.get_result()
|
config = outcome.get_result()
|
||||||
if config.option.debug:
|
if config.option.debug:
|
||||||
path = os.path.abspath("pytestdebug.log")
|
path = os.path.abspath("pytestdebug.log")
|
||||||
f = open(path, 'w')
|
debugfile = open(path, 'w')
|
||||||
config._debugfile = f
|
debugfile.write("versions pytest-%s, py-%s, "
|
||||||
f.write("versions pytest-%s, py-%s, "
|
|
||||||
"python-%s\ncwd=%s\nargs=%s\n\n" %(
|
"python-%s\ncwd=%s\nargs=%s\n\n" %(
|
||||||
pytest.__version__, py.__version__,
|
pytest.__version__, py.__version__,
|
||||||
".".join(map(str, sys.version_info)),
|
".".join(map(str, sys.version_info)),
|
||||||
os.getcwd(), config._origargs))
|
os.getcwd(), config._origargs))
|
||||||
config.pluginmanager.set_tracing(f.write)
|
config.pluginmanager.set_tracing(debugfile.write)
|
||||||
sys.stderr.write("writing pytestdebug information to %s\n" % path)
|
sys.stderr.write("writing pytestdebug information to %s\n" % path)
|
||||||
|
def unset_tracing():
|
||||||
@pytest.mark.trylast
|
debugfile.close()
|
||||||
def pytest_unconfigure(config):
|
sys.stderr.write("wrote pytestdebug information to %s\n" %
|
||||||
if hasattr(config, '_debugfile'):
|
debugfile.name)
|
||||||
config._debugfile.close()
|
config.trace.root.setwriter(None)
|
||||||
sys.stderr.write("wrote pytestdebug information to %s\n" %
|
config.add_cleanup(unset_tracing)
|
||||||
config._debugfile.name)
|
|
||||||
config.trace.root.setwriter(None)
|
|
||||||
|
|
||||||
|
|
||||||
def pytest_cmdline_main(config):
|
def pytest_cmdline_main(config):
|
||||||
if config.option.version:
|
if config.option.version:
|
||||||
|
@ -58,9 +54,9 @@ def pytest_cmdline_main(config):
|
||||||
sys.stderr.write(line + "\n")
|
sys.stderr.write(line + "\n")
|
||||||
return 0
|
return 0
|
||||||
elif config.option.help:
|
elif config.option.help:
|
||||||
config.do_configure()
|
config._do_configure()
|
||||||
showhelp(config)
|
showhelp(config)
|
||||||
config.do_unconfigure()
|
config._ensure_unconfigure()
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def showhelp(config):
|
def showhelp(config):
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
def pytest_addhooks(pluginmanager):
|
def pytest_addhooks(pluginmanager):
|
||||||
"""called at plugin load time to allow adding new hooks via a call to
|
"""called at plugin load time to allow adding new hooks via a call to
|
||||||
pluginmanager.registerhooks(module)."""
|
pluginmanager.addhooks(module_or_class, prefix)."""
|
||||||
|
|
||||||
|
|
||||||
def pytest_namespace():
|
def pytest_namespace():
|
||||||
|
|
|
@ -77,7 +77,7 @@ def wrap_session(config, doit):
|
||||||
initstate = 0
|
initstate = 0
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
config.do_configure()
|
config._do_configure()
|
||||||
initstate = 1
|
initstate = 1
|
||||||
config.hook.pytest_sessionstart(session=session)
|
config.hook.pytest_sessionstart(session=session)
|
||||||
initstate = 2
|
initstate = 2
|
||||||
|
@ -107,9 +107,7 @@ def wrap_session(config, doit):
|
||||||
config.hook.pytest_sessionfinish(
|
config.hook.pytest_sessionfinish(
|
||||||
session=session,
|
session=session,
|
||||||
exitstatus=session.exitstatus)
|
exitstatus=session.exitstatus)
|
||||||
if initstate >= 1:
|
config._ensure_unconfigure()
|
||||||
config.do_unconfigure()
|
|
||||||
config.pluginmanager.ensure_shutdown()
|
|
||||||
return session.exitstatus
|
return session.exitstatus
|
||||||
|
|
||||||
def pytest_cmdline_main(config):
|
def pytest_cmdline_main(config):
|
||||||
|
@ -160,7 +158,7 @@ class FSHookProxy(object):
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
plugins = self.config._getmatchingplugins(self.fspath)
|
plugins = self.config._getmatchingplugins(self.fspath)
|
||||||
x = self.config.hook._getcaller(name, plugins)
|
x = self.config.pluginmanager.make_hook_caller(name, plugins)
|
||||||
self.__dict__[name] = x
|
self.__dict__[name] = x
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
@ -510,7 +508,7 @@ class Session(FSCollector):
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
FSCollector.__init__(self, config.rootdir, parent=None,
|
FSCollector.__init__(self, config.rootdir, parent=None,
|
||||||
config=config, session=self)
|
config=config, session=self)
|
||||||
self.config.pluginmanager.register(self, name="session", prepend=True)
|
self.config.pluginmanager.register(self, name="session")
|
||||||
self._testsfailed = 0
|
self._testsfailed = 0
|
||||||
self.shouldstop = False
|
self.shouldstop = False
|
||||||
self.trace = config.trace.root.get("collection")
|
self.trace = config.trace.root.get("collection")
|
||||||
|
@ -521,10 +519,12 @@ class Session(FSCollector):
|
||||||
def _makeid(self):
|
def _makeid(self):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@pytest.mark.tryfirst
|
||||||
def pytest_collectstart(self):
|
def pytest_collectstart(self):
|
||||||
if self.shouldstop:
|
if self.shouldstop:
|
||||||
raise self.Interrupted(self.shouldstop)
|
raise self.Interrupted(self.shouldstop)
|
||||||
|
|
||||||
|
@pytest.mark.tryfirst
|
||||||
def pytest_runtest_logreport(self, report):
|
def pytest_runtest_logreport(self, report):
|
||||||
if report.failed and not hasattr(report, 'wasxfail'):
|
if report.failed and not hasattr(report, 'wasxfail'):
|
||||||
self._testsfailed += 1
|
self._testsfailed += 1
|
||||||
|
|
|
@ -44,14 +44,14 @@ def pytest_addoption(parser):
|
||||||
|
|
||||||
def pytest_cmdline_main(config):
|
def pytest_cmdline_main(config):
|
||||||
if config.option.markers:
|
if config.option.markers:
|
||||||
config.do_configure()
|
config._do_configure()
|
||||||
tw = py.io.TerminalWriter()
|
tw = py.io.TerminalWriter()
|
||||||
for line in config.getini("markers"):
|
for line in config.getini("markers"):
|
||||||
name, rest = line.split(":", 1)
|
name, rest = line.split(":", 1)
|
||||||
tw.write("@pytest.mark.%s:" % name, bold=True)
|
tw.write("@pytest.mark.%s:" % name, bold=True)
|
||||||
tw.line(rest)
|
tw.line(rest)
|
||||||
tw.line()
|
tw.line()
|
||||||
config.do_unconfigure()
|
config._ensure_unconfigure()
|
||||||
return 0
|
return 0
|
||||||
pytest_cmdline_main.tryfirst = True
|
pytest_cmdline_main.tryfirst = True
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,8 @@ class HookRecorder:
|
||||||
self.calls.append(ParsedCall(hookcaller.name, kwargs))
|
self.calls.append(ParsedCall(hookcaller.name, kwargs))
|
||||||
yield
|
yield
|
||||||
self._undo_wrapping = add_method_wrapper(HookCaller, _docall)
|
self._undo_wrapping = add_method_wrapper(HookCaller, _docall)
|
||||||
pluginmanager.add_shutdown(self._undo_wrapping)
|
#if hasattr(pluginmanager, "config"):
|
||||||
|
# pluginmanager.add_shutdown(self._undo_wrapping)
|
||||||
|
|
||||||
def finish_recording(self):
|
def finish_recording(self):
|
||||||
self._undo_wrapping()
|
self._undo_wrapping()
|
||||||
|
@ -589,12 +590,7 @@ class TmpTestdir:
|
||||||
# we don't know what the test will do with this half-setup config
|
# we don't know what the test will do with this half-setup config
|
||||||
# object and thus we make sure it gets unconfigured properly in any
|
# object and thus we make sure it gets unconfigured properly in any
|
||||||
# case (otherwise capturing could still be active, for example)
|
# case (otherwise capturing could still be active, for example)
|
||||||
def ensure_unconfigure():
|
self.request.addfinalizer(config._ensure_unconfigure)
|
||||||
if hasattr(config.pluginmanager, "_config"):
|
|
||||||
config.pluginmanager.do_unconfigure(config)
|
|
||||||
config.pluginmanager.ensure_shutdown()
|
|
||||||
|
|
||||||
self.request.addfinalizer(ensure_unconfigure)
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
def parseconfigure(self, *args):
|
def parseconfigure(self, *args):
|
||||||
|
@ -606,8 +602,8 @@ class TmpTestdir:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
config = self.parseconfig(*args)
|
config = self.parseconfig(*args)
|
||||||
config.do_configure()
|
config._do_configure()
|
||||||
self.request.addfinalizer(config.do_unconfigure)
|
self.request.addfinalizer(config._ensure_unconfigure)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
def getitem(self, source, funcname="test_func"):
|
def getitem(self, source, funcname="test_func"):
|
||||||
|
|
|
@ -66,6 +66,7 @@ def check_open_files(config):
|
||||||
error.append(error[0])
|
error.append(error[0])
|
||||||
raise AssertionError("\n".join(error))
|
raise AssertionError("\n".join(error))
|
||||||
|
|
||||||
|
@pytest.mark.trylast
|
||||||
def pytest_runtest_teardown(item, __multicall__):
|
def pytest_runtest_teardown(item, __multicall__):
|
||||||
item.config._basedir.chdir()
|
item.config._basedir.chdir()
|
||||||
if hasattr(item.config, '_openfiles'):
|
if hasattr(item.config, '_openfiles'):
|
||||||
|
|
|
@ -1487,7 +1487,7 @@ class TestAutouseManagement:
|
||||||
reprec = testdir.inline_run("-v","-s")
|
reprec = testdir.inline_run("-v","-s")
|
||||||
reprec.assertoutcome(passed=8)
|
reprec.assertoutcome(passed=8)
|
||||||
config = reprec.getcalls("pytest_unconfigure")[0].config
|
config = reprec.getcalls("pytest_unconfigure")[0].config
|
||||||
l = config._conftest.getconftestmodules(p)[0].l
|
l = config.pluginmanager._getconftestmodules(p)[0].l
|
||||||
assert l == ["fin_a1", "fin_a2", "fin_b1", "fin_b2"] * 2
|
assert l == ["fin_a1", "fin_a2", "fin_b1", "fin_b2"] * 2
|
||||||
|
|
||||||
def test_scope_ordering(self, testdir):
|
def test_scope_ordering(self, testdir):
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
import py, pytest
|
import py, pytest
|
||||||
from _pytest.config import Conftest
|
from _pytest.config import PytestPluginManager
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module", params=["global", "inpackage"])
|
@pytest.fixture(scope="module", params=["global", "inpackage"])
|
||||||
|
@ -16,7 +15,7 @@ def basedir(request):
|
||||||
return tmpdir
|
return tmpdir
|
||||||
|
|
||||||
def ConftestWithSetinitial(path):
|
def ConftestWithSetinitial(path):
|
||||||
conftest = Conftest()
|
conftest = PytestPluginManager()
|
||||||
conftest_setinitial(conftest, [path])
|
conftest_setinitial(conftest, [path])
|
||||||
return conftest
|
return conftest
|
||||||
|
|
||||||
|
@ -25,51 +24,41 @@ def conftest_setinitial(conftest, args, confcutdir=None):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.file_or_dir = args
|
self.file_or_dir = args
|
||||||
self.confcutdir = str(confcutdir)
|
self.confcutdir = str(confcutdir)
|
||||||
conftest.setinitial(Namespace())
|
conftest._set_initial_conftests(Namespace())
|
||||||
|
|
||||||
class TestConftestValueAccessGlobal:
|
class TestConftestValueAccessGlobal:
|
||||||
def test_basic_init(self, basedir):
|
def test_basic_init(self, basedir):
|
||||||
conftest = Conftest()
|
conftest = PytestPluginManager()
|
||||||
p = basedir.join("adir")
|
p = basedir.join("adir")
|
||||||
assert conftest.rget_with_confmod("a", p)[1] == 1
|
assert conftest._rget_with_confmod("a", p)[1] == 1
|
||||||
|
|
||||||
def test_onimport(self, basedir):
|
|
||||||
l = []
|
|
||||||
conftest = Conftest(onimport=l.append)
|
|
||||||
adir = basedir.join("adir")
|
|
||||||
conftest_setinitial(conftest, [adir], confcutdir=basedir)
|
|
||||||
assert len(l) == 1
|
|
||||||
assert conftest.rget_with_confmod("a", adir)[1] == 1
|
|
||||||
assert conftest.rget_with_confmod("b", adir.join("b"))[1] == 2
|
|
||||||
assert len(l) == 2
|
|
||||||
|
|
||||||
def test_immediate_initialiation_and_incremental_are_the_same(self, basedir):
|
def test_immediate_initialiation_and_incremental_are_the_same(self, basedir):
|
||||||
conftest = Conftest()
|
conftest = PytestPluginManager()
|
||||||
len(conftest._path2confmods)
|
len(conftest._path2confmods)
|
||||||
conftest.getconftestmodules(basedir)
|
conftest._getconftestmodules(basedir)
|
||||||
snap1 = len(conftest._path2confmods)
|
snap1 = len(conftest._path2confmods)
|
||||||
#assert len(conftest._path2confmods) == snap1 + 1
|
#assert len(conftest._path2confmods) == snap1 + 1
|
||||||
conftest.getconftestmodules(basedir.join('adir'))
|
conftest._getconftestmodules(basedir.join('adir'))
|
||||||
assert len(conftest._path2confmods) == snap1 + 1
|
assert len(conftest._path2confmods) == snap1 + 1
|
||||||
conftest.getconftestmodules(basedir.join('b'))
|
conftest._getconftestmodules(basedir.join('b'))
|
||||||
assert len(conftest._path2confmods) == snap1 + 2
|
assert len(conftest._path2confmods) == snap1 + 2
|
||||||
|
|
||||||
def test_value_access_not_existing(self, basedir):
|
def test_value_access_not_existing(self, basedir):
|
||||||
conftest = ConftestWithSetinitial(basedir)
|
conftest = ConftestWithSetinitial(basedir)
|
||||||
with pytest.raises(KeyError):
|
with pytest.raises(KeyError):
|
||||||
conftest.rget_with_confmod('a', basedir)
|
conftest._rget_with_confmod('a', basedir)
|
||||||
|
|
||||||
def test_value_access_by_path(self, basedir):
|
def test_value_access_by_path(self, basedir):
|
||||||
conftest = ConftestWithSetinitial(basedir)
|
conftest = ConftestWithSetinitial(basedir)
|
||||||
adir = basedir.join("adir")
|
adir = basedir.join("adir")
|
||||||
assert conftest.rget_with_confmod("a", adir)[1] == 1
|
assert conftest._rget_with_confmod("a", adir)[1] == 1
|
||||||
assert conftest.rget_with_confmod("a", adir.join("b"))[1] == 1.5
|
assert conftest._rget_with_confmod("a", adir.join("b"))[1] == 1.5
|
||||||
|
|
||||||
def test_value_access_with_confmod(self, basedir):
|
def test_value_access_with_confmod(self, basedir):
|
||||||
startdir = basedir.join("adir", "b")
|
startdir = basedir.join("adir", "b")
|
||||||
startdir.ensure("xx", dir=True)
|
startdir.ensure("xx", dir=True)
|
||||||
conftest = ConftestWithSetinitial(startdir)
|
conftest = ConftestWithSetinitial(startdir)
|
||||||
mod, value = conftest.rget_with_confmod("a", startdir)
|
mod, value = conftest._rget_with_confmod("a", startdir)
|
||||||
assert value == 1.5
|
assert value == 1.5
|
||||||
path = py.path.local(mod.__file__)
|
path = py.path.local(mod.__file__)
|
||||||
assert path.dirpath() == basedir.join("adir", "b")
|
assert path.dirpath() == basedir.join("adir", "b")
|
||||||
|
@ -85,9 +74,9 @@ def test_conftest_in_nonpkg_with_init(tmpdir):
|
||||||
def test_doubledash_considered(testdir):
|
def test_doubledash_considered(testdir):
|
||||||
conf = testdir.mkdir("--option")
|
conf = testdir.mkdir("--option")
|
||||||
conf.join("conftest.py").ensure()
|
conf.join("conftest.py").ensure()
|
||||||
conftest = Conftest()
|
conftest = PytestPluginManager()
|
||||||
conftest_setinitial(conftest, [conf.basename, conf.basename])
|
conftest_setinitial(conftest, [conf.basename, conf.basename])
|
||||||
l = conftest.getconftestmodules(conf)
|
l = conftest._getconftestmodules(conf)
|
||||||
assert len(l) == 1
|
assert len(l) == 1
|
||||||
|
|
||||||
def test_issue151_load_all_conftests(testdir):
|
def test_issue151_load_all_conftests(testdir):
|
||||||
|
@ -96,7 +85,7 @@ def test_issue151_load_all_conftests(testdir):
|
||||||
p = testdir.mkdir(name)
|
p = testdir.mkdir(name)
|
||||||
p.ensure("conftest.py")
|
p.ensure("conftest.py")
|
||||||
|
|
||||||
conftest = Conftest()
|
conftest = PytestPluginManager()
|
||||||
conftest_setinitial(conftest, names)
|
conftest_setinitial(conftest, names)
|
||||||
d = list(conftest._conftestpath2mod.values())
|
d = list(conftest._conftestpath2mod.values())
|
||||||
assert len(d) == len(names)
|
assert len(d) == len(names)
|
||||||
|
@ -105,15 +94,15 @@ def test_conftest_global_import(testdir):
|
||||||
testdir.makeconftest("x=3")
|
testdir.makeconftest("x=3")
|
||||||
p = testdir.makepyfile("""
|
p = testdir.makepyfile("""
|
||||||
import py, pytest
|
import py, pytest
|
||||||
from _pytest.config import Conftest
|
from _pytest.config import PytestPluginManager
|
||||||
conf = Conftest()
|
conf = PytestPluginManager()
|
||||||
mod = conf.importconftest(py.path.local("conftest.py"))
|
mod = conf._importconftest(py.path.local("conftest.py"))
|
||||||
assert mod.x == 3
|
assert mod.x == 3
|
||||||
import conftest
|
import conftest
|
||||||
assert conftest is mod, (conftest, mod)
|
assert conftest is mod, (conftest, mod)
|
||||||
subconf = py.path.local().ensure("sub", "conftest.py")
|
subconf = py.path.local().ensure("sub", "conftest.py")
|
||||||
subconf.write("y=4")
|
subconf.write("y=4")
|
||||||
mod2 = conf.importconftest(subconf)
|
mod2 = conf._importconftest(subconf)
|
||||||
assert mod != mod2
|
assert mod != mod2
|
||||||
assert mod2.y == 4
|
assert mod2.y == 4
|
||||||
import conftest
|
import conftest
|
||||||
|
@ -125,27 +114,27 @@ def test_conftest_global_import(testdir):
|
||||||
def test_conftestcutdir(testdir):
|
def test_conftestcutdir(testdir):
|
||||||
conf = testdir.makeconftest("")
|
conf = testdir.makeconftest("")
|
||||||
p = testdir.mkdir("x")
|
p = testdir.mkdir("x")
|
||||||
conftest = Conftest()
|
conftest = PytestPluginManager()
|
||||||
conftest_setinitial(conftest, [testdir.tmpdir], confcutdir=p)
|
conftest_setinitial(conftest, [testdir.tmpdir], confcutdir=p)
|
||||||
l = conftest.getconftestmodules(p)
|
l = conftest._getconftestmodules(p)
|
||||||
assert len(l) == 0
|
assert len(l) == 0
|
||||||
l = conftest.getconftestmodules(conf.dirpath())
|
l = conftest._getconftestmodules(conf.dirpath())
|
||||||
assert len(l) == 0
|
assert len(l) == 0
|
||||||
assert conf not in conftest._conftestpath2mod
|
assert conf not in conftest._conftestpath2mod
|
||||||
# but we can still import a conftest directly
|
# but we can still import a conftest directly
|
||||||
conftest.importconftest(conf)
|
conftest._importconftest(conf)
|
||||||
l = conftest.getconftestmodules(conf.dirpath())
|
l = conftest._getconftestmodules(conf.dirpath())
|
||||||
assert l[0].__file__.startswith(str(conf))
|
assert l[0].__file__.startswith(str(conf))
|
||||||
# and all sub paths get updated properly
|
# and all sub paths get updated properly
|
||||||
l = conftest.getconftestmodules(p)
|
l = conftest._getconftestmodules(p)
|
||||||
assert len(l) == 1
|
assert len(l) == 1
|
||||||
assert l[0].__file__.startswith(str(conf))
|
assert l[0].__file__.startswith(str(conf))
|
||||||
|
|
||||||
def test_conftestcutdir_inplace_considered(testdir):
|
def test_conftestcutdir_inplace_considered(testdir):
|
||||||
conf = testdir.makeconftest("")
|
conf = testdir.makeconftest("")
|
||||||
conftest = Conftest()
|
conftest = PytestPluginManager()
|
||||||
conftest_setinitial(conftest, [conf.dirpath()], confcutdir=conf.dirpath())
|
conftest_setinitial(conftest, [conf.dirpath()], confcutdir=conf.dirpath())
|
||||||
l = conftest.getconftestmodules(conf.dirpath())
|
l = conftest._getconftestmodules(conf.dirpath())
|
||||||
assert len(l) == 1
|
assert len(l) == 1
|
||||||
assert l[0].__file__.startswith(str(conf))
|
assert l[0].__file__.startswith(str(conf))
|
||||||
|
|
||||||
|
@ -153,7 +142,7 @@ def test_conftestcutdir_inplace_considered(testdir):
|
||||||
def test_setinitial_conftest_subdirs(testdir, name):
|
def test_setinitial_conftest_subdirs(testdir, name):
|
||||||
sub = testdir.mkdir(name)
|
sub = testdir.mkdir(name)
|
||||||
subconftest = sub.ensure("conftest.py")
|
subconftest = sub.ensure("conftest.py")
|
||||||
conftest = Conftest()
|
conftest = PytestPluginManager()
|
||||||
conftest_setinitial(conftest, [sub.dirpath()], confcutdir=testdir.tmpdir)
|
conftest_setinitial(conftest, [sub.dirpath()], confcutdir=testdir.tmpdir)
|
||||||
if name not in ('whatever', '.dotdir'):
|
if name not in ('whatever', '.dotdir'):
|
||||||
assert subconftest in conftest._conftestpath2mod
|
assert subconftest in conftest._conftestpath2mod
|
||||||
|
@ -199,9 +188,9 @@ def test_conftest_import_order(testdir, monkeypatch):
|
||||||
ct2.write("")
|
ct2.write("")
|
||||||
def impct(p):
|
def impct(p):
|
||||||
return p
|
return p
|
||||||
conftest = Conftest()
|
conftest = PytestPluginManager()
|
||||||
monkeypatch.setattr(conftest, 'importconftest', impct)
|
monkeypatch.setattr(conftest, '_importconftest', impct)
|
||||||
assert conftest.getconftestmodules(sub) == [ct1, ct2]
|
assert conftest._getconftestmodules(sub) == [ct1, ct2]
|
||||||
|
|
||||||
|
|
||||||
def test_fixture_dependency(testdir, monkeypatch):
|
def test_fixture_dependency(testdir, monkeypatch):
|
||||||
|
|
|
@ -3,234 +3,48 @@ from _pytest.core import * # noqa
|
||||||
from _pytest.config import get_plugin_manager
|
from _pytest.config import get_plugin_manager
|
||||||
|
|
||||||
|
|
||||||
class TestBootstrapping:
|
@pytest.fixture
|
||||||
def test_consider_env_fails_to_import(self, monkeypatch):
|
def pm():
|
||||||
pluginmanager = PluginManager()
|
return PluginManager("he")
|
||||||
monkeypatch.setenv('PYTEST_PLUGINS', 'nonexisting', prepend=",")
|
|
||||||
pytest.raises(ImportError, lambda: pluginmanager.consider_env())
|
|
||||||
|
|
||||||
def test_preparse_args(self):
|
@pytest.fixture
|
||||||
pluginmanager = PluginManager()
|
def pytestpm():
|
||||||
pytest.raises(ImportError, lambda:
|
return PytestPluginManager()
|
||||||
pluginmanager.consider_preparse(["xyz", "-p", "hello123"]))
|
|
||||||
|
|
||||||
def test_plugin_prevent_register(self):
|
|
||||||
pluginmanager = PluginManager()
|
|
||||||
pluginmanager.consider_preparse(["xyz", "-p", "no:abc"])
|
|
||||||
l1 = pluginmanager.getplugins()
|
|
||||||
pluginmanager.register(42, name="abc")
|
|
||||||
l2 = pluginmanager.getplugins()
|
|
||||||
assert len(l2) == len(l1)
|
|
||||||
|
|
||||||
def test_plugin_prevent_register_unregistered_alredy_registered(self):
|
class TestPluginManager:
|
||||||
pluginmanager = PluginManager()
|
def test_plugin_double_register(self, pm):
|
||||||
pluginmanager.register(42, name="abc")
|
|
||||||
l1 = pluginmanager.getplugins()
|
|
||||||
assert 42 in l1
|
|
||||||
pluginmanager.consider_preparse(["xyz", "-p", "no:abc"])
|
|
||||||
l2 = pluginmanager.getplugins()
|
|
||||||
assert 42 not in l2
|
|
||||||
|
|
||||||
def test_plugin_double_register(self):
|
|
||||||
pm = PluginManager()
|
|
||||||
pm.register(42, name="abc")
|
pm.register(42, name="abc")
|
||||||
pytest.raises(ValueError, lambda: pm.register(42, name="abc"))
|
with pytest.raises(ValueError):
|
||||||
|
pm.register(42, name="abc")
|
||||||
|
|
||||||
def test_plugin_skip(self, testdir, monkeypatch):
|
def test_pm(self, pm):
|
||||||
p = testdir.makepyfile(skipping1="""
|
|
||||||
import pytest
|
|
||||||
pytest.skip("hello")
|
|
||||||
""")
|
|
||||||
p.copy(p.dirpath("skipping2.py"))
|
|
||||||
monkeypatch.setenv("PYTEST_PLUGINS", "skipping2")
|
|
||||||
result = testdir.runpytest("-rw", "-p", "skipping1", "--traceconfig")
|
|
||||||
assert result.ret == 0
|
|
||||||
result.stdout.fnmatch_lines([
|
|
||||||
"WI1*skipped plugin*skipping1*hello*",
|
|
||||||
"WI1*skipped plugin*skipping2*hello*",
|
|
||||||
])
|
|
||||||
|
|
||||||
def test_consider_env_plugin_instantiation(self, testdir, monkeypatch):
|
|
||||||
pluginmanager = PluginManager()
|
|
||||||
testdir.syspathinsert()
|
|
||||||
testdir.makepyfile(xy123="#")
|
|
||||||
monkeypatch.setitem(os.environ, 'PYTEST_PLUGINS', 'xy123')
|
|
||||||
l1 = len(pluginmanager.getplugins())
|
|
||||||
pluginmanager.consider_env()
|
|
||||||
l2 = len(pluginmanager.getplugins())
|
|
||||||
assert l2 == l1 + 1
|
|
||||||
assert pluginmanager.getplugin('xy123')
|
|
||||||
pluginmanager.consider_env()
|
|
||||||
l3 = len(pluginmanager.getplugins())
|
|
||||||
assert l2 == l3
|
|
||||||
|
|
||||||
def test_consider_setuptools_instantiation(self, monkeypatch):
|
|
||||||
pkg_resources = pytest.importorskip("pkg_resources")
|
|
||||||
def my_iter(name):
|
|
||||||
assert name == "pytest11"
|
|
||||||
class EntryPoint:
|
|
||||||
name = "pytest_mytestplugin"
|
|
||||||
dist = None
|
|
||||||
def load(self):
|
|
||||||
class PseudoPlugin:
|
|
||||||
x = 42
|
|
||||||
return PseudoPlugin()
|
|
||||||
return iter([EntryPoint()])
|
|
||||||
|
|
||||||
monkeypatch.setattr(pkg_resources, 'iter_entry_points', my_iter)
|
|
||||||
pluginmanager = PluginManager()
|
|
||||||
pluginmanager.consider_setuptools_entrypoints()
|
|
||||||
plugin = pluginmanager.getplugin("mytestplugin")
|
|
||||||
assert plugin.x == 42
|
|
||||||
|
|
||||||
def test_consider_setuptools_not_installed(self, monkeypatch):
|
|
||||||
monkeypatch.setitem(py.std.sys.modules, 'pkg_resources',
|
|
||||||
py.std.types.ModuleType("pkg_resources"))
|
|
||||||
pluginmanager = PluginManager()
|
|
||||||
pluginmanager.consider_setuptools_entrypoints()
|
|
||||||
# ok, we did not explode
|
|
||||||
|
|
||||||
def test_pluginmanager_ENV_startup(self, testdir, monkeypatch):
|
|
||||||
testdir.makepyfile(pytest_x500="#")
|
|
||||||
p = testdir.makepyfile("""
|
|
||||||
import pytest
|
|
||||||
def test_hello(pytestconfig):
|
|
||||||
plugin = pytestconfig.pluginmanager.getplugin('pytest_x500')
|
|
||||||
assert plugin is not None
|
|
||||||
""")
|
|
||||||
monkeypatch.setenv('PYTEST_PLUGINS', 'pytest_x500', prepend=",")
|
|
||||||
result = testdir.runpytest(p)
|
|
||||||
assert result.ret == 0
|
|
||||||
result.stdout.fnmatch_lines(["*1 passed in*"])
|
|
||||||
|
|
||||||
def test_import_plugin_importname(self, testdir):
|
|
||||||
pluginmanager = PluginManager()
|
|
||||||
pytest.raises(ImportError, 'pluginmanager.import_plugin("qweqwex.y")')
|
|
||||||
pytest.raises(ImportError, 'pluginmanager.import_plugin("pytest_qweqwx.y")')
|
|
||||||
|
|
||||||
testdir.syspathinsert()
|
|
||||||
pluginname = "pytest_hello"
|
|
||||||
testdir.makepyfile(**{pluginname: ""})
|
|
||||||
pluginmanager.import_plugin("pytest_hello")
|
|
||||||
len1 = len(pluginmanager.getplugins())
|
|
||||||
pluginmanager.import_plugin("pytest_hello")
|
|
||||||
len2 = len(pluginmanager.getplugins())
|
|
||||||
assert len1 == len2
|
|
||||||
plugin1 = pluginmanager.getplugin("pytest_hello")
|
|
||||||
assert plugin1.__name__.endswith('pytest_hello')
|
|
||||||
plugin2 = pluginmanager.getplugin("pytest_hello")
|
|
||||||
assert plugin2 is plugin1
|
|
||||||
|
|
||||||
def test_import_plugin_dotted_name(self, testdir):
|
|
||||||
pluginmanager = PluginManager()
|
|
||||||
pytest.raises(ImportError, 'pluginmanager.import_plugin("qweqwex.y")')
|
|
||||||
pytest.raises(ImportError, 'pluginmanager.import_plugin("pytest_qweqwex.y")')
|
|
||||||
|
|
||||||
testdir.syspathinsert()
|
|
||||||
testdir.mkpydir("pkg").join("plug.py").write("x=3")
|
|
||||||
pluginname = "pkg.plug"
|
|
||||||
pluginmanager.import_plugin(pluginname)
|
|
||||||
mod = pluginmanager.getplugin("pkg.plug")
|
|
||||||
assert mod.x == 3
|
|
||||||
|
|
||||||
def test_consider_module(self, testdir):
|
|
||||||
pluginmanager = PluginManager()
|
|
||||||
testdir.syspathinsert()
|
|
||||||
testdir.makepyfile(pytest_p1="#")
|
|
||||||
testdir.makepyfile(pytest_p2="#")
|
|
||||||
mod = py.std.types.ModuleType("temp")
|
|
||||||
mod.pytest_plugins = ["pytest_p1", "pytest_p2"]
|
|
||||||
pluginmanager.consider_module(mod)
|
|
||||||
assert pluginmanager.getplugin("pytest_p1").__name__ == "pytest_p1"
|
|
||||||
assert pluginmanager.getplugin("pytest_p2").__name__ == "pytest_p2"
|
|
||||||
|
|
||||||
def test_consider_module_import_module(self, testdir):
|
|
||||||
mod = py.std.types.ModuleType("x")
|
|
||||||
mod.pytest_plugins = "pytest_a"
|
|
||||||
aplugin = testdir.makepyfile(pytest_a="#")
|
|
||||||
pluginmanager = get_plugin_manager()
|
|
||||||
reprec = testdir.make_hook_recorder(pluginmanager)
|
|
||||||
#syspath.prepend(aplugin.dirpath())
|
|
||||||
py.std.sys.path.insert(0, str(aplugin.dirpath()))
|
|
||||||
pluginmanager.consider_module(mod)
|
|
||||||
call = reprec.getcall(pluginmanager.hook.pytest_plugin_registered.name)
|
|
||||||
assert call.plugin.__name__ == "pytest_a"
|
|
||||||
|
|
||||||
# check that it is not registered twice
|
|
||||||
pluginmanager.consider_module(mod)
|
|
||||||
l = reprec.getcalls("pytest_plugin_registered")
|
|
||||||
assert len(l) == 1
|
|
||||||
|
|
||||||
def test_config_sets_conftesthandle_onimport(self, testdir):
|
|
||||||
config = testdir.parseconfig([])
|
|
||||||
assert config._conftest._onimport == config._onimportconftest
|
|
||||||
|
|
||||||
def test_consider_conftest_deps(self, testdir):
|
|
||||||
mod = testdir.makepyfile("pytest_plugins='xyz'").pyimport()
|
|
||||||
pp = PluginManager()
|
|
||||||
pytest.raises(ImportError, lambda: pp.consider_conftest(mod))
|
|
||||||
|
|
||||||
def test_pm(self):
|
|
||||||
pp = PluginManager()
|
|
||||||
class A: pass
|
class A: pass
|
||||||
a1, a2 = A(), A()
|
a1, a2 = A(), A()
|
||||||
pp.register(a1)
|
pm.register(a1)
|
||||||
assert pp.isregistered(a1)
|
assert pm.isregistered(a1)
|
||||||
pp.register(a2, "hello")
|
pm.register(a2, "hello")
|
||||||
assert pp.isregistered(a2)
|
assert pm.isregistered(a2)
|
||||||
l = pp.getplugins()
|
l = pm.getplugins()
|
||||||
assert a1 in l
|
assert a1 in l
|
||||||
assert a2 in l
|
assert a2 in l
|
||||||
assert pp.getplugin('hello') == a2
|
assert pm.getplugin('hello') == a2
|
||||||
pp.unregister(a1)
|
pm.unregister(a1)
|
||||||
assert not pp.isregistered(a1)
|
assert not pm.isregistered(a1)
|
||||||
|
|
||||||
def test_pm_ordering(self):
|
|
||||||
pp = PluginManager()
|
|
||||||
class A: pass
|
|
||||||
a1, a2 = A(), A()
|
|
||||||
pp.register(a1)
|
|
||||||
pp.register(a2, "hello")
|
|
||||||
l = pp.getplugins()
|
|
||||||
assert l.index(a1) < l.index(a2)
|
|
||||||
a3 = A()
|
|
||||||
pp.register(a3, prepend=True)
|
|
||||||
l = pp.getplugins()
|
|
||||||
assert l.index(a3) == 0
|
|
||||||
|
|
||||||
def test_register_imported_modules(self):
|
|
||||||
pp = PluginManager()
|
|
||||||
mod = py.std.types.ModuleType("x.y.pytest_hello")
|
|
||||||
pp.register(mod)
|
|
||||||
assert pp.isregistered(mod)
|
|
||||||
l = pp.getplugins()
|
|
||||||
assert mod in l
|
|
||||||
pytest.raises(ValueError, "pp.register(mod)")
|
|
||||||
pytest.raises(ValueError, lambda: pp.register(mod))
|
|
||||||
#assert not pp.isregistered(mod2)
|
|
||||||
assert pp.getplugins() == l
|
|
||||||
|
|
||||||
def test_canonical_import(self, monkeypatch):
|
|
||||||
mod = py.std.types.ModuleType("pytest_xyz")
|
|
||||||
monkeypatch.setitem(py.std.sys.modules, 'pytest_xyz', mod)
|
|
||||||
pp = PluginManager()
|
|
||||||
pp.import_plugin('pytest_xyz')
|
|
||||||
assert pp.getplugin('pytest_xyz') == mod
|
|
||||||
assert pp.isregistered(mod)
|
|
||||||
|
|
||||||
def test_register_mismatch_method(self):
|
def test_register_mismatch_method(self):
|
||||||
pp = get_plugin_manager()
|
pm = get_plugin_manager()
|
||||||
class hello:
|
class hello:
|
||||||
def pytest_gurgel(self):
|
def pytest_gurgel(self):
|
||||||
pass
|
pass
|
||||||
pytest.raises(Exception, lambda: pp.register(hello()))
|
pytest.raises(Exception, lambda: pm.register(hello()))
|
||||||
|
|
||||||
def test_register_mismatch_arg(self):
|
def test_register_mismatch_arg(self):
|
||||||
pp = get_plugin_manager()
|
pm = get_plugin_manager()
|
||||||
class hello:
|
class hello:
|
||||||
def pytest_configure(self, asd):
|
def pytest_configure(self, asd):
|
||||||
pass
|
pass
|
||||||
pytest.raises(Exception, lambda: pp.register(hello()))
|
pytest.raises(Exception, lambda: pm.register(hello()))
|
||||||
|
|
||||||
def test_register(self):
|
def test_register(self):
|
||||||
pm = get_plugin_manager()
|
pm = get_plugin_manager()
|
||||||
|
@ -250,7 +64,7 @@ class TestBootstrapping:
|
||||||
assert pm.getplugins()[-1:] == [my2]
|
assert pm.getplugins()[-1:] == [my2]
|
||||||
|
|
||||||
def test_listattr(self):
|
def test_listattr(self):
|
||||||
plugins = PluginManager()
|
plugins = PluginManager("xyz")
|
||||||
class api1:
|
class api1:
|
||||||
x = 41
|
x = 41
|
||||||
class api2:
|
class api2:
|
||||||
|
@ -263,27 +77,6 @@ class TestBootstrapping:
|
||||||
l = list(plugins.listattr('x'))
|
l = list(plugins.listattr('x'))
|
||||||
assert l == [41, 42, 43]
|
assert l == [41, 42, 43]
|
||||||
|
|
||||||
def test_hook_tracing(self):
|
|
||||||
pm = get_plugin_manager()
|
|
||||||
saveindent = []
|
|
||||||
class api1:
|
|
||||||
x = 41
|
|
||||||
def pytest_plugin_registered(self, plugin):
|
|
||||||
saveindent.append(pm.trace.root.indent)
|
|
||||||
raise ValueError(42)
|
|
||||||
l = []
|
|
||||||
pm.set_tracing(l.append)
|
|
||||||
indent = pm.trace.root.indent
|
|
||||||
p = api1()
|
|
||||||
pm.register(p)
|
|
||||||
|
|
||||||
assert pm.trace.root.indent == indent
|
|
||||||
assert len(l) == 2
|
|
||||||
assert 'pytest_plugin_registered' in l[0]
|
|
||||||
assert 'finish' in l[1]
|
|
||||||
pytest.raises(ValueError, lambda: pm.register(api1()))
|
|
||||||
assert pm.trace.root.indent == indent
|
|
||||||
assert saveindent[0] > indent
|
|
||||||
|
|
||||||
class TestPytestPluginInteractions:
|
class TestPytestPluginInteractions:
|
||||||
|
|
||||||
|
@ -301,7 +94,7 @@ class TestPytestPluginInteractions:
|
||||||
return xyz + 1
|
return xyz + 1
|
||||||
""")
|
""")
|
||||||
config = get_plugin_manager().config
|
config = get_plugin_manager().config
|
||||||
config._conftest.importconftest(conf)
|
config.pluginmanager._importconftest(conf)
|
||||||
print(config.pluginmanager.getplugins())
|
print(config.pluginmanager.getplugins())
|
||||||
res = config.hook.pytest_myhook(xyz=10)
|
res = config.hook.pytest_myhook(xyz=10)
|
||||||
assert res == [11]
|
assert res == [11]
|
||||||
|
@ -350,7 +143,7 @@ class TestPytestPluginInteractions:
|
||||||
parser.addoption('--test123', action="store_true",
|
parser.addoption('--test123', action="store_true",
|
||||||
default=True)
|
default=True)
|
||||||
""")
|
""")
|
||||||
config._conftest.importconftest(p)
|
config.pluginmanager._importconftest(p)
|
||||||
assert config.option.test123
|
assert config.option.test123
|
||||||
|
|
||||||
def test_configure(self, testdir):
|
def test_configure(self, testdir):
|
||||||
|
@ -362,20 +155,43 @@ class TestPytestPluginInteractions:
|
||||||
|
|
||||||
config.pluginmanager.register(A())
|
config.pluginmanager.register(A())
|
||||||
assert len(l) == 0
|
assert len(l) == 0
|
||||||
config.do_configure()
|
config._do_configure()
|
||||||
assert len(l) == 1
|
assert len(l) == 1
|
||||||
config.pluginmanager.register(A()) # leads to a configured() plugin
|
config.pluginmanager.register(A()) # leads to a configured() plugin
|
||||||
assert len(l) == 2
|
assert len(l) == 2
|
||||||
assert l[0] != l[1]
|
assert l[0] != l[1]
|
||||||
|
|
||||||
config.do_unconfigure()
|
config._ensure_unconfigure()
|
||||||
config.pluginmanager.register(A())
|
config.pluginmanager.register(A())
|
||||||
assert len(l) == 2
|
assert len(l) == 2
|
||||||
|
|
||||||
|
def test_hook_tracing(self):
|
||||||
|
pytestpm = get_plugin_manager() # fully initialized with plugins
|
||||||
|
saveindent = []
|
||||||
|
class api1:
|
||||||
|
x = 41
|
||||||
|
def pytest_plugin_registered(self, plugin):
|
||||||
|
saveindent.append(pytestpm.trace.root.indent)
|
||||||
|
raise ValueError(42)
|
||||||
|
l = []
|
||||||
|
pytestpm.set_tracing(l.append)
|
||||||
|
indent = pytestpm.trace.root.indent
|
||||||
|
p = api1()
|
||||||
|
pytestpm.register(p)
|
||||||
|
|
||||||
|
assert pytestpm.trace.root.indent == indent
|
||||||
|
assert len(l) == 2
|
||||||
|
assert 'pytest_plugin_registered' in l[0]
|
||||||
|
assert 'finish' in l[1]
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
pytestpm.register(api1())
|
||||||
|
assert pytestpm.trace.root.indent == indent
|
||||||
|
assert saveindent[0] > indent
|
||||||
|
|
||||||
# lower level API
|
# lower level API
|
||||||
|
|
||||||
def test_listattr(self):
|
def test_listattr(self):
|
||||||
pluginmanager = PluginManager()
|
pluginmanager = PluginManager("xyz")
|
||||||
class My2:
|
class My2:
|
||||||
x = 42
|
x = 42
|
||||||
pluginmanager.register(My2())
|
pluginmanager.register(My2())
|
||||||
|
@ -395,7 +211,7 @@ class TestPytestPluginInteractions:
|
||||||
def m(self):
|
def m(self):
|
||||||
return 19
|
return 19
|
||||||
|
|
||||||
pluginmanager = PluginManager()
|
pluginmanager = PluginManager("xyz")
|
||||||
p1 = P1()
|
p1 = P1()
|
||||||
p2 = P2()
|
p2 = P2()
|
||||||
p3 = P3()
|
p3 = P3()
|
||||||
|
@ -572,7 +388,7 @@ class TestMultiCall:
|
||||||
def m(self):
|
def m(self):
|
||||||
return 19
|
return 19
|
||||||
|
|
||||||
pluginmanager = PluginManager()
|
pluginmanager = PluginManager("xyz")
|
||||||
p1 = P1()
|
p1 = P1()
|
||||||
p2 = P2()
|
p2 = P2()
|
||||||
p3 = P3()
|
p3 = P3()
|
||||||
|
@ -624,11 +440,12 @@ class TestMultiCall:
|
||||||
|
|
||||||
|
|
||||||
class TestHookRelay:
|
class TestHookRelay:
|
||||||
def test_happypath(self):
|
def test_hapmypath(self):
|
||||||
class Api:
|
class Api:
|
||||||
def hello(self, arg):
|
def hello(self, arg):
|
||||||
"api hook 1"
|
"api hook 1"
|
||||||
pm = PluginManager([Api], prefix="he")
|
pm = PluginManager("he")
|
||||||
|
pm.addhooks(Api)
|
||||||
hook = pm.hook
|
hook = pm.hook
|
||||||
assert hasattr(hook, 'hello')
|
assert hasattr(hook, 'hello')
|
||||||
assert repr(hook.hello).find("hello") != -1
|
assert repr(hook.hello).find("hello") != -1
|
||||||
|
@ -647,7 +464,8 @@ class TestHookRelay:
|
||||||
class Api:
|
class Api:
|
||||||
def hello(self, arg):
|
def hello(self, arg):
|
||||||
"api hook 1"
|
"api hook 1"
|
||||||
pm = PluginManager(Api, prefix="he")
|
pm = PluginManager("he")
|
||||||
|
pm.addhooks(Api)
|
||||||
class Plugin:
|
class Plugin:
|
||||||
def hello(self, argwrong):
|
def hello(self, argwrong):
|
||||||
return arg + 1
|
return arg + 1
|
||||||
|
@ -656,19 +474,20 @@ class TestHookRelay:
|
||||||
assert "argwrong" in str(exc.value)
|
assert "argwrong" in str(exc.value)
|
||||||
|
|
||||||
def test_only_kwargs(self):
|
def test_only_kwargs(self):
|
||||||
pm = PluginManager()
|
pm = PluginManager("he")
|
||||||
class Api:
|
class Api:
|
||||||
def hello(self, arg):
|
def hello(self, arg):
|
||||||
"api hook 1"
|
"api hook 1"
|
||||||
mcm = HookRelay(hookspecs=Api, pm=pm, prefix="he")
|
pm.addhooks(Api)
|
||||||
pytest.raises(TypeError, lambda: mcm.hello(3))
|
pytest.raises(TypeError, lambda: pm.hook.hello(3))
|
||||||
|
|
||||||
def test_firstresult_definition(self):
|
def test_firstresult_definition(self):
|
||||||
class Api:
|
class Api:
|
||||||
def hello(self, arg):
|
def hello(self, arg):
|
||||||
"api hook 1"
|
"api hook 1"
|
||||||
hello.firstresult = True
|
hello.firstresult = True
|
||||||
pm = PluginManager([Api], "he")
|
pm = PluginManager("he")
|
||||||
|
pm.addhooks(Api)
|
||||||
class Plugin:
|
class Plugin:
|
||||||
def hello(self, arg):
|
def hello(self, arg):
|
||||||
return arg + 1
|
return arg + 1
|
||||||
|
@ -771,15 +590,16 @@ def test_default_markers(testdir):
|
||||||
"*trylast*last*",
|
"*trylast*last*",
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_importplugin_issue375(testdir):
|
def test_importplugin_issue375(testdir, pytestpm):
|
||||||
testdir.syspathinsert(testdir.tmpdir)
|
testdir.syspathinsert(testdir.tmpdir)
|
||||||
testdir.makepyfile(qwe="import aaaa")
|
testdir.makepyfile(qwe="import aaaa")
|
||||||
excinfo = pytest.raises(ImportError, lambda: importplugin("qwe"))
|
with pytest.raises(ImportError) as excinfo:
|
||||||
|
pytestpm.import_plugin("qwe")
|
||||||
assert "qwe" not in str(excinfo.value)
|
assert "qwe" not in str(excinfo.value)
|
||||||
assert "aaaa" in str(excinfo.value)
|
assert "aaaa" in str(excinfo.value)
|
||||||
|
|
||||||
class TestWrapMethod:
|
class TestWrapMethod:
|
||||||
def test_basic_happypath(self):
|
def test_basic_hapmypath(self):
|
||||||
class A:
|
class A:
|
||||||
def f(self):
|
def f(self):
|
||||||
return "A.f"
|
return "A.f"
|
||||||
|
@ -880,3 +700,178 @@ class TestWrapMethod:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
A().error()
|
A().error()
|
||||||
assert l == [1]
|
assert l == [1]
|
||||||
|
|
||||||
|
|
||||||
|
### to be shifted to own test file
|
||||||
|
from _pytest.config import PytestPluginManager
|
||||||
|
|
||||||
|
class TestPytestPluginManager:
|
||||||
|
def test_register_imported_modules(self):
|
||||||
|
pm = PytestPluginManager()
|
||||||
|
mod = py.std.types.ModuleType("x.y.pytest_hello")
|
||||||
|
pm.register(mod)
|
||||||
|
assert pm.isregistered(mod)
|
||||||
|
l = pm.getplugins()
|
||||||
|
assert mod in l
|
||||||
|
pytest.raises(ValueError, "pm.register(mod)")
|
||||||
|
pytest.raises(ValueError, lambda: pm.register(mod))
|
||||||
|
#assert not pm.isregistered(mod2)
|
||||||
|
assert pm.getplugins() == l
|
||||||
|
|
||||||
|
def test_canonical_import(self, monkeypatch):
|
||||||
|
mod = py.std.types.ModuleType("pytest_xyz")
|
||||||
|
monkeypatch.setitem(py.std.sys.modules, 'pytest_xyz', mod)
|
||||||
|
pm = PytestPluginManager()
|
||||||
|
pm.import_plugin('pytest_xyz')
|
||||||
|
assert pm.getplugin('pytest_xyz') == mod
|
||||||
|
assert pm.isregistered(mod)
|
||||||
|
|
||||||
|
def test_consider_module(self, testdir, pytestpm):
|
||||||
|
testdir.syspathinsert()
|
||||||
|
testdir.makepyfile(pytest_p1="#")
|
||||||
|
testdir.makepyfile(pytest_p2="#")
|
||||||
|
mod = py.std.types.ModuleType("temp")
|
||||||
|
mod.pytest_plugins = ["pytest_p1", "pytest_p2"]
|
||||||
|
pytestpm.consider_module(mod)
|
||||||
|
assert pytestpm.getplugin("pytest_p1").__name__ == "pytest_p1"
|
||||||
|
assert pytestpm.getplugin("pytest_p2").__name__ == "pytest_p2"
|
||||||
|
|
||||||
|
def test_consider_module_import_module(self, testdir):
|
||||||
|
pytestpm = get_plugin_manager()
|
||||||
|
mod = py.std.types.ModuleType("x")
|
||||||
|
mod.pytest_plugins = "pytest_a"
|
||||||
|
aplugin = testdir.makepyfile(pytest_a="#")
|
||||||
|
reprec = testdir.make_hook_recorder(pytestpm)
|
||||||
|
#syspath.prepend(aplugin.dirpath())
|
||||||
|
py.std.sys.path.insert(0, str(aplugin.dirpath()))
|
||||||
|
pytestpm.consider_module(mod)
|
||||||
|
call = reprec.getcall(pytestpm.hook.pytest_plugin_registered.name)
|
||||||
|
assert call.plugin.__name__ == "pytest_a"
|
||||||
|
|
||||||
|
# check that it is not registered twice
|
||||||
|
pytestpm.consider_module(mod)
|
||||||
|
l = reprec.getcalls("pytest_plugin_registered")
|
||||||
|
assert len(l) == 1
|
||||||
|
|
||||||
|
def test_consider_env_fails_to_import(self, monkeypatch, pytestpm):
|
||||||
|
monkeypatch.setenv('PYTEST_PLUGINS', 'nonexisting', prepend=",")
|
||||||
|
with pytest.raises(ImportError):
|
||||||
|
pytestpm.consider_env()
|
||||||
|
|
||||||
|
def test_plugin_skip(self, testdir, monkeypatch):
|
||||||
|
p = testdir.makepyfile(skipping1="""
|
||||||
|
import pytest
|
||||||
|
pytest.skip("hello")
|
||||||
|
""")
|
||||||
|
p.copy(p.dirpath("skipping2.py"))
|
||||||
|
monkeypatch.setenv("PYTEST_PLUGINS", "skipping2")
|
||||||
|
result = testdir.runpytest("-rw", "-p", "skipping1", "--traceconfig")
|
||||||
|
assert result.ret == 0
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"WI1*skipped plugin*skipping1*hello*",
|
||||||
|
"WI1*skipped plugin*skipping2*hello*",
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_consider_env_plugin_instantiation(self, testdir, monkeypatch, pytestpm):
|
||||||
|
testdir.syspathinsert()
|
||||||
|
testdir.makepyfile(xy123="#")
|
||||||
|
monkeypatch.setitem(os.environ, 'PYTEST_PLUGINS', 'xy123')
|
||||||
|
l1 = len(pytestpm.getplugins())
|
||||||
|
pytestpm.consider_env()
|
||||||
|
l2 = len(pytestpm.getplugins())
|
||||||
|
assert l2 == l1 + 1
|
||||||
|
assert pytestpm.getplugin('xy123')
|
||||||
|
pytestpm.consider_env()
|
||||||
|
l3 = len(pytestpm.getplugins())
|
||||||
|
assert l2 == l3
|
||||||
|
|
||||||
|
def test_consider_setuptools_instantiation(self, monkeypatch, pytestpm):
|
||||||
|
pkg_resources = pytest.importorskip("pkg_resources")
|
||||||
|
def my_iter(name):
|
||||||
|
assert name == "pytest11"
|
||||||
|
class EntryPoint:
|
||||||
|
name = "pytest_mytestplugin"
|
||||||
|
dist = None
|
||||||
|
def load(self):
|
||||||
|
class PseudoPlugin:
|
||||||
|
x = 42
|
||||||
|
return PseudoPlugin()
|
||||||
|
return iter([EntryPoint()])
|
||||||
|
|
||||||
|
monkeypatch.setattr(pkg_resources, 'iter_entry_points', my_iter)
|
||||||
|
pytestpm.consider_setuptools_entrypoints()
|
||||||
|
plugin = pytestpm.getplugin("mytestplugin")
|
||||||
|
assert plugin.x == 42
|
||||||
|
|
||||||
|
def test_consider_setuptools_not_installed(self, monkeypatch, pytestpm):
|
||||||
|
monkeypatch.setitem(py.std.sys.modules, 'pkg_resources',
|
||||||
|
py.std.types.ModuleType("pkg_resources"))
|
||||||
|
pytestpm.consider_setuptools_entrypoints()
|
||||||
|
# ok, we did not explode
|
||||||
|
|
||||||
|
def test_pluginmanager_ENV_startup(self, testdir, monkeypatch):
|
||||||
|
testdir.makepyfile(pytest_x500="#")
|
||||||
|
p = testdir.makepyfile("""
|
||||||
|
import pytest
|
||||||
|
def test_hello(pytestconfig):
|
||||||
|
plugin = pytestconfig.pluginmanager.getplugin('pytest_x500')
|
||||||
|
assert plugin is not None
|
||||||
|
""")
|
||||||
|
monkeypatch.setenv('PYTEST_PLUGINS', 'pytest_x500', prepend=",")
|
||||||
|
result = testdir.runpytest(p)
|
||||||
|
assert result.ret == 0
|
||||||
|
result.stdout.fnmatch_lines(["*1 passed in*"])
|
||||||
|
|
||||||
|
def test_import_plugin_importname(self, testdir, pytestpm):
|
||||||
|
pytest.raises(ImportError, 'pytestpm.import_plugin("qweqwex.y")')
|
||||||
|
pytest.raises(ImportError, 'pytestpm.import_plugin("pytest_qweqwx.y")')
|
||||||
|
|
||||||
|
testdir.syspathinsert()
|
||||||
|
pluginname = "pytest_hello"
|
||||||
|
testdir.makepyfile(**{pluginname: ""})
|
||||||
|
pytestpm.import_plugin("pytest_hello")
|
||||||
|
len1 = len(pytestpm.getplugins())
|
||||||
|
pytestpm.import_plugin("pytest_hello")
|
||||||
|
len2 = len(pytestpm.getplugins())
|
||||||
|
assert len1 == len2
|
||||||
|
plugin1 = pytestpm.getplugin("pytest_hello")
|
||||||
|
assert plugin1.__name__.endswith('pytest_hello')
|
||||||
|
plugin2 = pytestpm.getplugin("pytest_hello")
|
||||||
|
assert plugin2 is plugin1
|
||||||
|
|
||||||
|
def test_import_plugin_dotted_name(self, testdir, pytestpm):
|
||||||
|
pytest.raises(ImportError, 'pytestpm.import_plugin("qweqwex.y")')
|
||||||
|
pytest.raises(ImportError, 'pytestpm.import_plugin("pytest_qweqwex.y")')
|
||||||
|
|
||||||
|
testdir.syspathinsert()
|
||||||
|
testdir.mkpydir("pkg").join("plug.py").write("x=3")
|
||||||
|
pluginname = "pkg.plug"
|
||||||
|
pytestpm.import_plugin(pluginname)
|
||||||
|
mod = pytestpm.getplugin("pkg.plug")
|
||||||
|
assert mod.x == 3
|
||||||
|
|
||||||
|
def test_consider_conftest_deps(self, testdir, pytestpm):
|
||||||
|
mod = testdir.makepyfile("pytest_plugins='xyz'").pyimport()
|
||||||
|
with pytest.raises(ImportError):
|
||||||
|
pytestpm.consider_conftest(mod)
|
||||||
|
|
||||||
|
|
||||||
|
class TestPytestPluginManagerBootstrapming:
|
||||||
|
def test_preparse_args(self, pytestpm):
|
||||||
|
pytest.raises(ImportError, lambda:
|
||||||
|
pytestpm.consider_preparse(["xyz", "-p", "hello123"]))
|
||||||
|
|
||||||
|
def test_plugin_prevent_register(self, pytestpm):
|
||||||
|
pytestpm.consider_preparse(["xyz", "-p", "no:abc"])
|
||||||
|
l1 = pytestpm.getplugins()
|
||||||
|
pytestpm.register(42, name="abc")
|
||||||
|
l2 = pytestpm.getplugins()
|
||||||
|
assert len(l2) == len(l1)
|
||||||
|
|
||||||
|
def test_plugin_prevent_register_unregistered_alredy_registered(self, pytestpm):
|
||||||
|
pytestpm.register(42, name="abc")
|
||||||
|
l1 = pytestpm.getplugins()
|
||||||
|
assert 42 in l1
|
||||||
|
pytestpm.consider_preparse(["xyz", "-p", "no:abc"])
|
||||||
|
l2 = pytestpm.getplugins()
|
||||||
|
assert 42 not in l2
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import pytest
|
import pytest
|
||||||
import os
|
import os
|
||||||
from _pytest.pytester import HookRecorder
|
from _pytest.pytester import HookRecorder
|
||||||
from _pytest.core import PluginManager
|
from _pytest.config import PytestPluginManager
|
||||||
from _pytest.main import EXIT_OK, EXIT_TESTSFAILED
|
from _pytest.main import EXIT_OK, EXIT_TESTSFAILED
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ def test_parseconfig(testdir):
|
||||||
|
|
||||||
def test_testdir_runs_with_plugin(testdir):
|
def test_testdir_runs_with_plugin(testdir):
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
pytest_plugins = "pytest_pytester"
|
pytest_plugins = "pytester"
|
||||||
def test_hello(testdir):
|
def test_hello(testdir):
|
||||||
assert 1
|
assert 1
|
||||||
""")
|
""")
|
||||||
|
@ -93,8 +93,8 @@ def make_holder():
|
||||||
|
|
||||||
@pytest.mark.parametrize("holder", make_holder())
|
@pytest.mark.parametrize("holder", make_holder())
|
||||||
def test_hookrecorder_basic(holder):
|
def test_hookrecorder_basic(holder):
|
||||||
pm = PluginManager()
|
pm = PytestPluginManager()
|
||||||
pm.hook._addhooks(holder, "pytest_")
|
pm.addhooks(holder)
|
||||||
rec = HookRecorder(pm)
|
rec = HookRecorder(pm)
|
||||||
pm.hook.pytest_xyz(arg=123)
|
pm.hook.pytest_xyz(arg=123)
|
||||||
call = rec.popcall("pytest_xyz")
|
call = rec.popcall("pytest_xyz")
|
||||||
|
|
|
@ -22,7 +22,6 @@ def test_WarningRecorder(recwarn):
|
||||||
|
|
||||||
def test_recwarn_functional(testdir):
|
def test_recwarn_functional(testdir):
|
||||||
reprec = testdir.inline_runsource("""
|
reprec = testdir.inline_runsource("""
|
||||||
pytest_plugins = 'pytest_recwarn',
|
|
||||||
import warnings
|
import warnings
|
||||||
oldwarn = warnings.showwarning
|
oldwarn = warnings.showwarning
|
||||||
def test_method(recwarn):
|
def test_method(recwarn):
|
||||||
|
|
|
@ -214,8 +214,8 @@ def test_plugin_specify(testdir):
|
||||||
def test_plugin_already_exists(testdir):
|
def test_plugin_already_exists(testdir):
|
||||||
config = testdir.parseconfig("-p", "terminal")
|
config = testdir.parseconfig("-p", "terminal")
|
||||||
assert config.option.plugins == ['terminal']
|
assert config.option.plugins == ['terminal']
|
||||||
config.do_configure()
|
config._do_configure()
|
||||||
config.do_unconfigure()
|
config._ensure_unconfigure()
|
||||||
|
|
||||||
def test_exclude(testdir):
|
def test_exclude(testdir):
|
||||||
hellodir = testdir.mkdir("hello")
|
hellodir = testdir.mkdir("hello")
|
||||||
|
|
|
@ -457,7 +457,9 @@ class TestTerminalFunctional:
|
||||||
])
|
])
|
||||||
assert result.ret == 1
|
assert result.ret == 1
|
||||||
|
|
||||||
pytestconfig.pluginmanager.skipifmissing("xdist")
|
if not pytestconfig.pluginmanager.hasplugin("xdist"):
|
||||||
|
pytest.skip("xdist plugin not installed")
|
||||||
|
|
||||||
result = testdir.runpytest(p1, '-v', '-n 1')
|
result = testdir.runpytest(p1, '-v', '-n 1')
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"*FAIL*test_verbose_reporting.py::test_fail*",
|
"*FAIL*test_verbose_reporting.py::test_fail*",
|
||||||
|
|
|
@ -3,7 +3,6 @@ import pytest
|
||||||
def test_simple_unittest(testdir):
|
def test_simple_unittest(testdir):
|
||||||
testpath = testdir.makepyfile("""
|
testpath = testdir.makepyfile("""
|
||||||
import unittest
|
import unittest
|
||||||
pytest_plugins = "pytest_unittest"
|
|
||||||
class MyTestCase(unittest.TestCase):
|
class MyTestCase(unittest.TestCase):
|
||||||
def testpassing(self):
|
def testpassing(self):
|
||||||
self.assertEquals('foo', 'foo')
|
self.assertEquals('foo', 'foo')
|
||||||
|
@ -17,7 +16,6 @@ def test_simple_unittest(testdir):
|
||||||
def test_runTest_method(testdir):
|
def test_runTest_method(testdir):
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
import unittest
|
import unittest
|
||||||
pytest_plugins = "pytest_unittest"
|
|
||||||
class MyTestCaseWithRunTest(unittest.TestCase):
|
class MyTestCaseWithRunTest(unittest.TestCase):
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
self.assertEquals('foo', 'foo')
|
self.assertEquals('foo', 'foo')
|
||||||
|
|
Loading…
Reference in New Issue