diff --git a/_pytest/core.py b/_pytest/core.py index cd54072bb..abab607b2 100644 --- a/_pytest/core.py +++ b/_pytest/core.py @@ -416,7 +416,6 @@ class HookRelay: def __init__(self, hookspecs, pm, prefix="pytest_"): if not isinstance(hookspecs, list): hookspecs = [hookspecs] - self._hookspecs = [] self._pm = pm self.trace = pm.trace.root.get("hook") self.prefix = prefix @@ -424,7 +423,6 @@ class HookRelay: self._addhooks(hookspec, prefix) def _addhooks(self, hookspec, prefix): - self._hookspecs.append(hookspec) added = False isclass = int(inspect.isclass(hookspec)) for name, method in vars(hookspec).items(): diff --git a/_pytest/pytester.py b/_pytest/pytester.py index 63e223af2..f1a9e3f8e 100644 --- a/_pytest/pytester.py +++ b/_pytest/pytester.py @@ -12,7 +12,7 @@ import subprocess import py import pytest from py.builtin import print_ -from _pytest.core import HookRelay +from _pytest.core import HookRelay, HookCaller, Wrapping from _pytest.main import Session, EXIT_OK @@ -51,10 +51,8 @@ class PytestArg: return hookrecorder class ParsedCall: - def __init__(self, name, locals): - assert '_name' not in locals - self.__dict__.update(locals) - self.__dict__.pop('self') + def __init__(self, name, kwargs): + self.__dict__.update(kwargs) self._name = name def __repr__(self): @@ -62,64 +60,24 @@ class ParsedCall: del d['_name'] return "" %(self._name, d) + class HookRecorder: def __init__(self, pluginmanager): self._pluginmanager = pluginmanager self.calls = [] - self._recorders = {} - - hookspecs = self._pluginmanager.hook._hookspecs - for hookspec in hookspecs: - assert hookspec not in self._recorders - class RecordCalls: - _recorder = self - for name, method in vars(hookspec).items(): - if name[0] != "_": - setattr(RecordCalls, name, self._makecallparser(method)) - recorder = RecordCalls() - self._recorders[hookspec] = recorder - self._pluginmanager.register(recorder) - self.hook = HookRelay(hookspecs, pm=self._pluginmanager, - prefix="pytest_") + self.wrapping = Wrapping() + @self.wrapping.method(HookCaller) + def _docall(hookcaller, methods, kwargs): + self.calls.append(ParsedCall(hookcaller.name, kwargs)) + yield def finish_recording(self): - for recorder in self._recorders.values(): - if self._pluginmanager.isregistered(recorder): - self._pluginmanager.unregister(recorder) - self._recorders.clear() - - def _makecallparser(self, method): - name = method.__name__ - args, varargs, varkw, default = inspect.getargspec(method) - if not args or args[0] != "self": - args.insert(0, 'self') - fspec = inspect.formatargspec(args, varargs, varkw, default) - # we use exec because we want to have early type - # errors on wrong input arguments, using - # *args/**kwargs delays this and gives errors - # elsewhere - exec (py.code.compile(""" - def %(name)s%(fspec)s: - self._recorder.calls.append( - ParsedCall(%(name)r, locals())) - """ % locals())) - return locals()[name] + self.wrapping.undo() def getcalls(self, names): if isinstance(names, str): names = names.split() - for name in names: - for cls in self._recorders: - if name in vars(cls): - break - else: - raise ValueError("callname %r not found in %r" %( - name, self._recorders.keys())) - l = [] - for call in self.calls: - if call._name in names: - l.append(call) - return l + return [call for call in self.calls if call._name in names] def contains(self, entries): __tracebackhide__ = True @@ -228,10 +186,9 @@ class TmpTestdir: obj = obj.config if hasattr(obj, 'hook'): obj = obj.hook - assert hasattr(obj, '_hookspecs'), obj + assert isinstance(obj, HookRelay) reprec = ReportRecorder(obj) reprec.hookrecorder = self._pytest.gethookrecorder(obj) - reprec.hook = reprec.hookrecorder.hook return reprec def chdir(self): diff --git a/testing/test_pytester.py b/testing/test_pytester.py index 59a294674..ef817e68c 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -94,12 +94,12 @@ def test_hookrecorder_basic(holder): pm = PluginManager() pm.hook._addhooks(holder, "pytest_") rec = HookRecorder(pm) - rec.hook.pytest_xyz(arg=123) + pm.hook.pytest_xyz(arg=123) call = rec.popcall("pytest_xyz") assert call.arg == 123 assert call._name == "pytest_xyz" pytest.raises(pytest.fail.Exception, "rec.popcall('abc')") - rec.hook.pytest_xyz_noarg() + pm.hook.pytest_xyz_noarg() call = rec.popcall("pytest_xyz_noarg") assert call._name == "pytest_xyz_noarg" @@ -119,7 +119,7 @@ def test_functional(testdir, linecomp): def pytest_xyz(self, arg): return arg + 1 rec._pluginmanager.register(Plugin()) - res = rec.hook.pytest_xyz(arg=41) + res = pm.hook.pytest_xyz(arg=41) assert res == [42] """) reprec.assertoutcome(passed=1)