[py-svn] py-trunk commit 70c81c004272: refine and test new hook registration, now it is called "pytest_addhooks"
commits-noreply at bitbucket.org
commits-noreply at bitbucket.org
Sun May 2 16:36:29 CEST 2010
# HG changeset patch -- Bitbucket.org
# Project py-trunk
# URL http://bitbucket.org/hpk42/py-trunk/overview
# User holger krekel <holger at merlinux.eu>
# Date 1272811013 -7200
# Node ID 70c81c0042722c892a653199e024ccf948afda5c
# Parent 2e8ad1a4fd49b2f6c8b8140ee4581e4ca89e3684
refine and test new hook registration, now it is called "pytest_addhooks"
similar to pytest_addoption and raises on bogus input.
--- a/py/_test/pluginmanager.py
+++ b/py/_test/pluginmanager.py
@@ -39,8 +39,7 @@ class PluginManager(object):
if name in self._name2plugin:
return False
self._name2plugin[name] = plugin
- self.call_plugin(plugin, "pytest_registerhooks",
- {'pluginmanager': self})
+ self.call_plugin(plugin, "pytest_addhooks", {'pluginmanager': self})
self.hook.pytest_plugin_registered(manager=self, plugin=plugin)
self.registry.register(plugin)
return True
@@ -59,8 +58,8 @@ class PluginManager(object):
if plugin == val:
return True
- def registerhooks(self, spec):
- self.hook._registerhooks(spec)
+ def addhooks(self, spec):
+ self.hook._addhooks(spec, prefix="pytest_")
def getplugins(self):
return list(self.registry)
@@ -304,22 +303,31 @@ class Registry:
return l
class HookRelay:
- def __init__(self, hookspecs, registry):
+ def __init__(self, hookspecs, registry, prefix="pytest_"):
if not isinstance(hookspecs, list):
hookspecs = [hookspecs]
self._hookspecs = []
self._registry = registry
for hookspec in hookspecs:
- self._registerhooks(hookspec)
+ self._addhooks(hookspec, prefix)
- def _registerhooks(self, hookspecs):
+ def _addhooks(self, hookspecs, prefix):
self._hookspecs.append(hookspecs)
+ added = False
for name, method in vars(hookspecs).items():
- if name[:1] != "_":
+ if name.startswith(prefix):
+ if not method.__doc__:
+ raise ValueError("docstring required for hook %r, in %r"
+ % (method, hookspecs))
firstresult = getattr(method, 'firstresult', False)
hc = HookCaller(self, name, firstresult=firstresult)
setattr(self, name, hc)
+ added = True
#print ("setting new hook", name)
+ if not added:
+ raise ValueError("did not find new %r hooks in %r" %(
+ prefix, hookspecs,))
+
def _performcall(self, name, multicall):
return multicall.execute()
--- a/py/_test/cmdline.py
+++ b/py/_test/cmdline.py
@@ -21,4 +21,3 @@ def main(args=None):
e = sys.exc_info()[1]
sys.stderr.write("ERROR: %s\n" %(e.args[0],))
raise SystemExit(3)
-
--- a/testing/test_pluginmanager.py
+++ b/testing/test_pluginmanager.py
@@ -202,6 +202,58 @@ class TestBootstrapping:
impname = canonical_importname(name)
class TestPytestPluginInteractions:
+
+ def test_addhooks_conftestplugin(self, testdir):
+ from py._test.config import Config
+ newhooks = testdir.makepyfile(newhooks="""
+ def pytest_myhook(xyz):
+ "new hook"
+ """)
+ conf = testdir.makeconftest("""
+ import sys ; sys.path.insert(0, '.')
+ import newhooks
+ def pytest_addhooks(pluginmanager):
+ pluginmanager.addhooks(newhooks)
+ def pytest_myhook(xyz):
+ return xyz + 1
+ """)
+ config = Config()
+ config._conftest.importconftest(conf)
+ print(config.pluginmanager.getplugins())
+ res = config.hook.pytest_myhook(xyz=10)
+ assert res == [11]
+
+ def test_addhooks_docstring_error(self, testdir):
+ newhooks = testdir.makepyfile(newhooks="""
+ class A: # no pytest_ prefix
+ pass
+ def pytest_myhook(xyz):
+ pass
+ """)
+ conf = testdir.makeconftest("""
+ import sys ; sys.path.insert(0, '.')
+ import newhooks
+ def pytest_addhooks(pluginmanager):
+ pluginmanager.addhooks(newhooks)
+ """)
+ res = testdir.runpytest()
+ assert res.ret != 0
+ res.stderr.fnmatch_lines([
+ "*docstring*pytest_myhook*newhooks*"
+ ])
+
+ def test_addhooks_nohooks(self, testdir):
+ conf = testdir.makeconftest("""
+ import sys
+ def pytest_addhooks(pluginmanager):
+ pluginmanager.addhooks(sys)
+ """)
+ res = testdir.runpytest()
+ assert res.ret != 0
+ res.stderr.fnmatch_lines([
+ "*did not find*sys*"
+ ])
+
def test_do_option_conftestplugin(self, testdir):
from py._test.config import Config
p = testdir.makepyfile("""
@@ -401,9 +453,9 @@ class TestHookRelay:
registry = Registry()
class Api:
def hello(self, arg):
- pass
+ "api hook 1"
- mcm = HookRelay(hookspecs=Api, registry=registry)
+ mcm = HookRelay(hookspecs=Api, registry=registry, prefix="he")
assert hasattr(mcm, 'hello')
assert repr(mcm.hello).find("hello") != -1
class Plugin:
@@ -418,17 +470,18 @@ class TestHookRelay:
registry = Registry()
class Api:
def hello(self, arg):
- pass
- mcm = HookRelay(hookspecs=Api, registry=registry)
+ "api hook 1"
+ mcm = HookRelay(hookspecs=Api, registry=registry, prefix="he")
py.test.raises(TypeError, "mcm.hello(3)")
def test_firstresult_definition(self):
registry = Registry()
class Api:
- def hello(self, arg): pass
+ def hello(self, arg):
+ "api hook 1"
hello.firstresult = True
- mcm = HookRelay(hookspecs=Api, registry=registry)
+ mcm = HookRelay(hookspecs=Api, registry=registry, prefix="he")
class Plugin:
def hello(self, arg):
return arg + 1
--- a/py/_plugin/hookspec.py
+++ b/py/_plugin/hookspec.py
@@ -6,14 +6,14 @@ hook specifications for py.test plugins
# Command line and configuration
# -------------------------------------------------------------------------
+def pytest_namespace():
+ "return dict of name->object which will get stored at py.test. namespace"
+
def pytest_addoption(parser):
- """ called before commandline parsing. """
+ "add optparse-style options via parser.addoption."
-def pytest_registerhooks(pluginmanager):
- """ called after commandline parsing before pytest_configure. """
-
-def pytest_namespace():
- """ return dict of name->object which will get stored at py.test. namespace"""
+def pytest_addhooks(pluginmanager):
+ "add hooks via pluginmanager.registerhooks(module)"
def pytest_configure(config):
""" called after command line options have been parsed.
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,9 +1,26 @@
Changes between 1.2.1 and 1.3.0 (release pending)
==================================================
-- new mechanism to allow external plugins to register new hooks
- (a recent pytest-xdist plugin for distributed and looponfailing
- testing requires this feature)
+- allow external plugins to register new hooks via the new
+ pytest_addhooks(pluginmanager) hook. The new release of
+ the pytest-xdist plugin for distributed and looponfailing
+ testing requires this feature.
+- add a new pytest_ignore_collect(path, config) hook to allow projects and
+ plugins to define exclusion behaviour for their directory structure -
+ for example you may define in a conftest.py this method:
+ def pytest_ignore_collect(path):
+ return path.check(link=1)
+ to prevent even a collection try of any tests in symlinked dirs.
+- new pytest_pycollect_makemodule(path, parent) hook for
+ allowing customization of the Module collection object for a
+ matching test module.
+- expose (previously internal) commonly useful methods:
+ py.io.get_terminal_with() -> return terminal width
+ py.io.ansi_print(...) -> print colored/bold text on linux/win32
+ py.io.saferepr(obj) -> return limited representation string
+- expose test outcome related exceptions as py.test.skip.Exception,
+ py.test.raises.Exception etc., useful mostly for plugins
+ doing special outcome interpretation/tweaking
- (issue85) fix junitxml plugin to handle tests with non-ascii output
- fix/refine python3 compatibility (thanks Benjamin Peterson)
- fixes for making the jython/win32 combination work, note however:
@@ -13,22 +30,6 @@ Changes between 1.2.1 and 1.3.0 (release
- fixes for handling of unicode exception values and unprintable objects
- (issue87) fix unboundlocal error in assertionold code
- (issue86) improve documentation for looponfailing
-- add a new pytest_ignore_collect(path, config) hook to allow projects and
- plugins to define exclusion behaviour for their directory structure -
- for example you may define in a conftest.py this method:
- def pytest_ignore_collect(path):
- return path.check(link=1)
- to prevent even a collection try of any tests in symlinked dirs.
-- new pytest_pycollect_makemodule(path, parent) hook for
- allowing customization of the Module collection object for a
- matching test module.
-- expose (previously internal) commonly useful methods:
- py.io.get_terminal_with() -> return terminal width
- py.io.ansi_print(...) -> print colored/bold text on linux/win32
- py.io.saferepr(obj) -> return limited representation string
-- expose test outcome related exceptions as py.test.skip.Exception,
- py.test.raises.Exception etc., useful mostly for plugins
- doing special outcome interpretation/tweaking
- refine IO capturing: stdin-redirect pseudo-file now has a NOP close() method
- ship distribute_setup.py version 0.6.10
- added links to the new capturelog and coverage plugins
More information about the pytest-commit
mailing list