shift pytest_configure/unconfigure/addoption/namespace hook calling to config object.

The _pytest.config module itself is no longer a plugin but the actual
config instance is plugin-registered as ``pytestconfig``.
This allows to put most pytest specific logic to _pytest.config instead
of in the core pluginmanager.
This commit is contained in:
holger krekel 2013-09-30 13:14:14 +02:00
parent 694c6fd0e7
commit d946299b0a
8 changed files with 81 additions and 83 deletions

View File

@ -2,20 +2,6 @@
import py
import sys, os
import pytest
def pytest_cmdline_parse(pluginmanager, args):
config = Config(pluginmanager)
config.parse(args)
return config
def pytest_unconfigure(config):
while 1:
try:
fin = config._cleanup.pop()
except IndexError:
break
fin()
class Parser:
""" Parser for command line arguments and ini-file values. """
@ -507,13 +493,46 @@ class Config(object):
self._inicache = {}
self._opt2dest = {}
self._cleanup = []
self.pluginmanager.register(self, "pytestconfig")
self._configured = False
def pytest_plugin_registered(self, plugin):
call_plugin = self.pluginmanager.call_plugin
dic = call_plugin(plugin, "pytest_namespace", {}) or {}
if dic:
import pytest
setns(pytest, dic)
call_plugin(plugin, "pytest_addoption", {'parser': self._parser})
if self._configured:
call_plugin(plugin, "pytest_configure", {'config': self})
def do_configure(self):
assert not self._configured
self._configured = True
self.hook.pytest_configure(config=self)
def do_unconfigure(self):
assert self._configured
self._configured = False
self.hook.pytest_unconfigure(config=self)
self.pluginmanager.ensure_shutdown()
def pytest_cmdline_parse(self, pluginmanager, args):
assert self == pluginmanager.config, (self, pluginmanager.config)
self.parse(args)
return self
def pytest_unconfigure(config):
while config._cleanup:
fin = config._cleanup.pop()
fin()
@classmethod
def fromdictargs(cls, option_dict, args):
""" constructor useable for subprocesses. """
from _pytest.core import get_plugin_manager
pluginmanager = get_plugin_manager()
config = cls(pluginmanager)
config = pluginmanager.config
# XXX slightly crude way to initialize capturing
import _pytest.capture
_pytest.capture.pytest_cmdline_parse(config.pluginmanager, args)
@ -572,11 +591,11 @@ class Config(object):
self.pluginmanager.consider_setuptools_entrypoints()
self.pluginmanager.consider_env()
self._setinitialconftest(args)
self.pluginmanager.do_addoption(self._parser)
if addopts:
self.hook.pytest_cmdline_preparse(config=self, args=args)
def _checkversion(self):
import pytest
minver = self.inicfg.get('minversion', None)
if minver:
ver = minver.split(".")
@ -723,3 +742,23 @@ def getcfg(args, inibasenames):
return iniconfig['pytest']
return {}
def setns(obj, dic):
import pytest
for name, value in dic.items():
if isinstance(value, dict):
mod = getattr(obj, name, None)
if mod is None:
modname = "pytest.%s" % name
mod = py.std.types.ModuleType(modname)
sys.modules[modname] = mod
mod.__all__ = []
setattr(obj, name, mod)
obj.__all__.append(name)
setns(mod, value)
else:
setattr(obj, name, value)
obj.__all__.append(name)
#if obj != pytest:
# pytest.__all__.append(name)
setattr(pytest, name, value)

View File

@ -1,17 +1,17 @@
"""
pytest PluginManager, basic initialization and tracing.
(c) Holger Krekel 2004-2010
"""
import sys, os
import inspect
import py
from _pytest import hookspec # the extension point definitions
from _pytest.config import Config
assert py.__version__.split(".")[:2] >= ['1', '4'], ("installation problem: "
"%s is too old, remove or upgrade 'py'" % (py.__version__))
default_plugins = (
"config mark main terminal runner python pdb unittest capture skipping "
"mark main terminal runner python pdb unittest capture skipping "
"tmpdir monkeypatch recwarn pastebin helpconfig nose assertion genscript "
"junitxml resultlog doctest").split()
@ -91,6 +91,7 @@ class PluginManager(object):
self.trace.root.setwriter(err.write)
self.hook = HookRelay([hookspec], pm=self)
self.register(self)
self.config = Config(self) # XXX unclear if the attr is needed
if load:
for spec in default_plugins:
self.import_plugin(spec)
@ -100,7 +101,8 @@ class PluginManager(object):
return
name = name or getattr(plugin, '__name__', str(id(plugin)))
if self.isregistered(plugin, name):
raise ValueError("Plugin already registered: %s=%s" %(name, plugin))
raise ValueError("Plugin already registered: %s=%s\n%s" %(
name, plugin, self._name2plugin))
#self.trace("registering", name, plugin)
self._name2plugin[name] = plugin
self.call_plugin(plugin, "pytest_addhooks", {'pluginmanager': self})
@ -220,7 +222,6 @@ class PluginManager(object):
if self.getplugin(modname) is not None:
return
try:
#self.trace("importing", modname)
mod = importplugin(modname)
except KeyboardInterrupt:
raise
@ -247,59 +248,12 @@ class PluginManager(object):
"trylast: mark a hook implementation function such that the "
"plugin machinery will try to call it last/as late as possible.")
def pytest_plugin_registered(self, plugin):
import pytest
dic = self.call_plugin(plugin, "pytest_namespace", {}) or {}
if dic:
self._setns(pytest, dic)
if hasattr(self, '_config'):
self.call_plugin(plugin, "pytest_addoption",
{'parser': self._config._parser})
self.call_plugin(plugin, "pytest_configure",
{'config': self._config})
def _setns(self, obj, dic):
import pytest
for name, value in dic.items():
if isinstance(value, dict):
mod = getattr(obj, name, None)
if mod is None:
modname = "pytest.%s" % name
mod = py.std.types.ModuleType(modname)
sys.modules[modname] = mod
mod.__all__ = []
setattr(obj, name, mod)
obj.__all__.append(name)
self._setns(mod, value)
else:
setattr(obj, name, value)
obj.__all__.append(name)
#if obj != pytest:
# pytest.__all__.append(name)
setattr(pytest, name, value)
def pytest_terminal_summary(self, terminalreporter):
tw = terminalreporter._tw
if terminalreporter.config.option.traceconfig:
for hint in self._hints:
tw.line("hint: %s" % hint)
def do_addoption(self, parser):
mname = "pytest_addoption"
methods = reversed(self.listattr(mname))
MultiCall(methods, {'parser': parser}).execute()
def do_configure(self, config):
assert not hasattr(self, '_config')
self._config = config
config.hook.pytest_configure(config=self._config)
def do_unconfigure(self, config):
config = self._config
del self._config
config.hook.pytest_unconfigure(config=config)
config.pluginmanager.ensure_shutdown()
def notify_exception(self, excinfo, option=None):
if option and option.fulltrace:
style = "long"
@ -350,6 +304,7 @@ def importplugin(importspec):
name = importspec
try:
mod = "_pytest." + name
#print >>sys.stderr, "tryimport", mod
__import__(mod)
return sys.modules[mod]
except ImportError:
@ -358,6 +313,7 @@ def importplugin(importspec):
# raise
pass #
try:
#print >>sys.stderr, "tryimport", importspec
__import__(importspec)
except ImportError:
raise ImportError(importspec)

View File

@ -54,9 +54,9 @@ def pytest_cmdline_main(config):
sys.stderr.write(line + "\n")
return 0
elif config.option.help:
config.pluginmanager.do_configure(config)
config.do_configure()
showhelp(config)
config.pluginmanager.do_unconfigure(config)
config.do_unconfigure()
return 0
def showhelp(config):

View File

@ -76,7 +76,7 @@ def wrap_session(config, doit):
initstate = 0
try:
try:
config.pluginmanager.do_configure(config)
config.do_configure()
initstate = 1
config.hook.pytest_sessionstart(session=session)
initstate = 2
@ -105,7 +105,7 @@ def wrap_session(config, doit):
session=session,
exitstatus=session.exitstatus)
if initstate >= 1:
config.pluginmanager.do_unconfigure(config)
config.do_unconfigure()
config.pluginmanager.ensure_shutdown()
return session.exitstatus

View File

@ -1,5 +1,5 @@
""" generic mechanism for marking and selecting python functions. """
import pytest, py
import py
def pytest_namespace():
@ -39,14 +39,14 @@ def pytest_addoption(parser):
def pytest_cmdline_main(config):
if config.option.markers:
config.pluginmanager.do_configure(config)
config.do_configure()
tw = py.io.TerminalWriter()
for line in config.getini("markers"):
name, rest = line.split(":", 1)
tw.write("@pytest.mark.%s:" % name, bold=True)
tw.line(rest)
tw.line()
config.pluginmanager.do_unconfigure(config)
config.do_unconfigure()
return 0
pytest_cmdline_main.tryfirst = True
@ -129,6 +129,7 @@ def matchkeyword(colitem, keywordexpr):
mapped_names = set()
# Add the names of the current item and any parent items
import pytest
for item in colitem.listchain():
if not isinstance(item, pytest.Instance):
mapped_names.add(item.name)
@ -145,6 +146,7 @@ def matchkeyword(colitem, keywordexpr):
def pytest_configure(config):
import pytest
if config.option.strict:
pytest.mark._config = config

View File

@ -390,9 +390,9 @@ class TmpTestdir:
def parseconfigure(self, *args):
config = self.parseconfig(*args)
config.pluginmanager.do_configure(config)
config.do_configure()
self.request.addfinalizer(lambda:
config.pluginmanager.do_unconfigure(config))
config.do_unconfigure())
return config
def getitem(self, source, funcname="test_func"):

View File

@ -261,13 +261,13 @@ class TestBootstrapping:
assert pm.getplugins()
my2 = MyPlugin()
pm.register(my2)
assert pm.getplugins()[1:] == [my, my2]
assert pm.getplugins()[2:] == [my, my2]
assert pm.isregistered(my)
assert pm.isregistered(my2)
pm.unregister(my)
assert not pm.isregistered(my)
assert pm.getplugins()[1:] == [my2]
assert pm.getplugins()[2:] == [my2]
def test_listattr(self):
plugins = PluginManager()
@ -319,7 +319,7 @@ class TestPytestPluginInteractions:
def pytest_myhook(xyz):
return xyz + 1
""")
config = testdir.Config(PluginManager(load=True))
config = PluginManager(load=True).config
config._conftest.importconftest(conf)
print(config.pluginmanager.getplugins())
res = config.hook.pytest_myhook(xyz=10)
@ -383,13 +383,13 @@ class TestPytestPluginInteractions:
config.pluginmanager.register(A())
assert len(l) == 0
config.pluginmanager.do_configure(config=config)
config.do_configure()
assert len(l) == 1
config.pluginmanager.register(A()) # leads to a configured() plugin
assert len(l) == 2
assert l[0] != l[1]
config.pluginmanager.do_unconfigure(config=config)
config.do_unconfigure()
config.pluginmanager.register(A())
assert len(l) == 2

View File

@ -208,13 +208,14 @@ def test_plugin_specify(testdir):
testdir.parseconfig("-p", "nqweotexistent")
""")
#pytest.raises(ImportError,
# "config.pluginmanager.do_configure(config)"
# "config.do_configure(config)"
#)
def test_plugin_already_exists(testdir):
config = testdir.parseconfig("-p", "terminal")
assert config.option.plugins == ['terminal']
config.pluginmanager.do_configure(config)
config.do_configure()
config.do_unconfigure()
def test_exclude(testdir):
hellodir = testdir.mkdir("hello")