diff --git a/_pytest/config.py b/_pytest/config.py index 4d37ba4e6..2a3c71201 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -16,6 +16,8 @@ hookspec = HookspecMarker("pytest") # pytest startup # + + class ConftestImportFailure(Exception): def __init__(self, path, excinfo): Exception.__init__(self, path, excinfo) @@ -135,7 +137,6 @@ class PytestPluginManager(PluginManager): """ def __init__(self): super(PytestPluginManager, self).__init__("pytest", implprefix="pytest_") - self._warnings = [] self._conftest_plugins = set() # state related to local conftest plugins @@ -166,7 +167,7 @@ class PytestPluginManager(PluginManager): fslocation=py.code.getfslineno(sys._getframe(1)), message="use pluginmanager.add_hookspecs instead of " "deprecated addhooks() method.") - self._warnings.append(warning) + self._warn(warning) return self.add_hookspecs(module_or_class) def parse_hookimpl_opts(self, plugin, name): @@ -196,9 +197,10 @@ class PytestPluginManager(PluginManager): fslineno = py.code.getfslineno(hookmethod.function) warning = dict(code="I1", fslocation=fslineno, + nodeid=None, message="%r hook uses deprecated __multicall__ " "argument" % (hook.name)) - self._warnings.append(warning) + self._warn(warning) def register(self, plugin, name=None): ret = super(PytestPluginManager, self).register(plugin, name) @@ -224,11 +226,15 @@ class PytestPluginManager(PluginManager): config.addinivalue_line("markers", "trylast: mark a hook implementation function such that the " "plugin machinery will try to call it last/as late as possible.") - for warning in self._warnings: - if isinstance(warning, dict): - config.warn(**warning) - else: - config.warn(code="I1", message=warning) + + def _warn(self, message): + kwargs = message if isinstance(message, dict) else { + 'code': 'I1', + 'message': message, + 'fslocation': None, + 'nodeid': None, + } + self.hook.pytest_logwarning.call_historic(kwargs=kwargs) # # internal API for local conftest plugin handling @@ -381,7 +387,7 @@ class PytestPluginManager(PluginManager): 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))) + self._warn("skipped plugin %r: %s" %((modname, e.msg))) else: mod = sys.modules[importspec] self.register(mod, modname) @@ -713,6 +719,7 @@ class MyOptionParser(argparse.ArgumentParser): getattr(args, FILE_OR_DIR).extend(argv) return args + class DropShorterLongHelpFormatter(argparse.HelpFormatter): """shorten help for long options that differ only in extra hyphens @@ -802,6 +809,7 @@ class Config(object): self._inicache = {} self._opt2dest = {} self._cleanup = [] + self._warn = self.pluginmanager._warn self.pluginmanager.register(self, "pytestconfig") self._configured = False def do_setns(dic): @@ -831,8 +839,9 @@ class Config(object): def warn(self, code, message, fslocation=None): """ generate a warning for this test session. """ - self.hook.pytest_logwarning(code=code, message=message, - fslocation=fslocation, nodeid=None) + self.hook.pytest_logwarning.call_historic(kwargs=dict( + code=code, message=message, + fslocation=fslocation, nodeid=None)) def get_terminal_writer(self): return self.pluginmanager.get_plugin("terminalreporter")._tw @@ -921,8 +930,7 @@ class Config(object): if ns.help or ns.version: # we don't want to prevent --help/--version to work # so just let is pass and print a warning at the end - self.pluginmanager._warnings.append( - "could not load initial conftests (%s)\n" % e.path) + self._warn("could not load initial conftests (%s)\n" % e.path) else: raise diff --git a/_pytest/helpconfig.py b/_pytest/helpconfig.py index af35e241e..5ede8b371 100644 --- a/_pytest/helpconfig.py +++ b/_pytest/helpconfig.py @@ -62,8 +62,8 @@ def pytest_cmdline_main(config): return 0 def showhelp(config): - import _pytest.config - tw = _pytest.config.create_terminal_writer(config) + reporter = config.pluginmanager.get_plugin('terminalreporter') + tw = reporter._tw tw.write(config._parser.optparser.format_help()) tw.line() tw.line() @@ -86,8 +86,9 @@ def showhelp(config): tw.line("to see available fixtures type: py.test --fixtures") tw.line("(shown according to specified file_or_dir or current dir " "if not specified)") - for warning in config.pluginmanager._warnings: - tw.line("warning: %s" % (warning,), red=True) + tw.line(str(reporter.stats)) + for warningreport in reporter.stats.get('warnings', []): + tw.line("warning : " + warningreport.message, red=True) return @@ -126,5 +127,3 @@ def pytest_report_header(config): r = repr(plugin) lines.append(" %-20s: %s" %(name, r)) return lines - - diff --git a/_pytest/hookspec.py b/_pytest/hookspec.py index c75f563f4..a3a481d6a 100644 --- a/_pytest/hookspec.py +++ b/_pytest/hookspec.py @@ -249,6 +249,8 @@ def pytest_report_teststatus(report): def pytest_terminal_summary(terminalreporter): """ add additional section in terminal summary reporting. """ + +@hookspec(historic=True) def pytest_logwarning(message, code, nodeid, fslocation): """ process a warning specified by a message, a code string, a nodeid and fslocation (both of which may be None diff --git a/_pytest/main.py b/_pytest/main.py index 4f3d2625f..6b2ccb6b7 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -279,9 +279,9 @@ class Node(object): else: fslocation = "%s:%s" % fslocation[:2] - self.ihook.pytest_logwarning(code=code, message=message, - nodeid=self.nodeid, - fslocation=fslocation) + self.ihook.pytest_logwarning.call_historic(kwargs=dict( + code=code, message=message, + nodeid=self.nodeid, fslocation=fslocation)) # methods for ordering nodes @property @@ -742,5 +742,3 @@ class Session(FSCollector): for x in self.genitems(subnode): yield x node.ihook.pytest_collectreport(report=rep) - - diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py index 92afba9bc..c49938100 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -129,14 +129,21 @@ class TestPytestPluginInteractions: undo() def test_warn_on_deprecated_multicall(self, pytestpm): + warnings = [] + + class get_warnings: + def pytest_logwarning(self, message): + warnings.append(message) + class Plugin: def pytest_configure(self, __multicall__): pass - before = list(pytestpm._warnings) + pytestpm.register(get_warnings()) + before = list(warnings) pytestpm.register(Plugin()) - assert len(pytestpm._warnings) == len(before) + 1 - assert "deprecated" in pytestpm._warnings[-1]["message"] + assert len(warnings) == len(before) + 1 + assert "deprecated" in warnings[-1] def test_namespace_has_default_and_env_plugins(testdir):