simplify method to record calls

This commit is contained in:
holger krekel 2014-10-04 15:49:31 +02:00
parent 3d794b6b38
commit 63f070317c
3 changed files with 15 additions and 60 deletions

View File

@ -416,7 +416,6 @@ class HookRelay:
def __init__(self, hookspecs, pm, prefix="pytest_"): def __init__(self, hookspecs, pm, prefix="pytest_"):
if not isinstance(hookspecs, list): if not isinstance(hookspecs, list):
hookspecs = [hookspecs] hookspecs = [hookspecs]
self._hookspecs = []
self._pm = pm self._pm = pm
self.trace = pm.trace.root.get("hook") self.trace = pm.trace.root.get("hook")
self.prefix = prefix self.prefix = prefix
@ -424,7 +423,6 @@ class HookRelay:
self._addhooks(hookspec, prefix) self._addhooks(hookspec, prefix)
def _addhooks(self, hookspec, prefix): def _addhooks(self, hookspec, prefix):
self._hookspecs.append(hookspec)
added = False added = False
isclass = int(inspect.isclass(hookspec)) isclass = int(inspect.isclass(hookspec))
for name, method in vars(hookspec).items(): for name, method in vars(hookspec).items():

View File

@ -12,7 +12,7 @@ import subprocess
import py import py
import pytest import pytest
from py.builtin import print_ from py.builtin import print_
from _pytest.core import HookRelay from _pytest.core import HookRelay, HookCaller, Wrapping
from _pytest.main import Session, EXIT_OK from _pytest.main import Session, EXIT_OK
@ -51,10 +51,8 @@ class PytestArg:
return hookrecorder return hookrecorder
class ParsedCall: class ParsedCall:
def __init__(self, name, locals): def __init__(self, name, kwargs):
assert '_name' not in locals self.__dict__.update(kwargs)
self.__dict__.update(locals)
self.__dict__.pop('self')
self._name = name self._name = name
def __repr__(self): def __repr__(self):
@ -62,64 +60,24 @@ class ParsedCall:
del d['_name'] del d['_name']
return "<ParsedCall %r(**%r)>" %(self._name, d) return "<ParsedCall %r(**%r)>" %(self._name, d)
class HookRecorder: class HookRecorder:
def __init__(self, pluginmanager): def __init__(self, pluginmanager):
self._pluginmanager = pluginmanager self._pluginmanager = pluginmanager
self.calls = [] self.calls = []
self._recorders = {} self.wrapping = Wrapping()
@self.wrapping.method(HookCaller)
hookspecs = self._pluginmanager.hook._hookspecs def _docall(hookcaller, methods, kwargs):
for hookspec in hookspecs: self.calls.append(ParsedCall(hookcaller.name, kwargs))
assert hookspec not in self._recorders yield
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_")
def finish_recording(self): def finish_recording(self):
for recorder in self._recorders.values(): self.wrapping.undo()
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]
def getcalls(self, names): def getcalls(self, names):
if isinstance(names, str): if isinstance(names, str):
names = names.split() names = names.split()
for name in names: return [call for call in self.calls if call._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
def contains(self, entries): def contains(self, entries):
__tracebackhide__ = True __tracebackhide__ = True
@ -228,10 +186,9 @@ class TmpTestdir:
obj = obj.config obj = obj.config
if hasattr(obj, 'hook'): if hasattr(obj, 'hook'):
obj = obj.hook obj = obj.hook
assert hasattr(obj, '_hookspecs'), obj assert isinstance(obj, HookRelay)
reprec = ReportRecorder(obj) reprec = ReportRecorder(obj)
reprec.hookrecorder = self._pytest.gethookrecorder(obj) reprec.hookrecorder = self._pytest.gethookrecorder(obj)
reprec.hook = reprec.hookrecorder.hook
return reprec return reprec
def chdir(self): def chdir(self):

View File

@ -94,12 +94,12 @@ def test_hookrecorder_basic(holder):
pm = PluginManager() pm = PluginManager()
pm.hook._addhooks(holder, "pytest_") pm.hook._addhooks(holder, "pytest_")
rec = HookRecorder(pm) rec = HookRecorder(pm)
rec.hook.pytest_xyz(arg=123) pm.hook.pytest_xyz(arg=123)
call = rec.popcall("pytest_xyz") call = rec.popcall("pytest_xyz")
assert call.arg == 123 assert call.arg == 123
assert call._name == "pytest_xyz" assert call._name == "pytest_xyz"
pytest.raises(pytest.fail.Exception, "rec.popcall('abc')") pytest.raises(pytest.fail.Exception, "rec.popcall('abc')")
rec.hook.pytest_xyz_noarg() pm.hook.pytest_xyz_noarg()
call = rec.popcall("pytest_xyz_noarg") call = rec.popcall("pytest_xyz_noarg")
assert call._name == "pytest_xyz_noarg" assert call._name == "pytest_xyz_noarg"
@ -119,7 +119,7 @@ def test_functional(testdir, linecomp):
def pytest_xyz(self, arg): def pytest_xyz(self, arg):
return arg + 1 return arg + 1
rec._pluginmanager.register(Plugin()) rec._pluginmanager.register(Plugin())
res = rec.hook.pytest_xyz(arg=41) res = pm.hook.pytest_xyz(arg=41)
assert res == [42] assert res == [42]
""") """)
reprec.assertoutcome(passed=1) reprec.assertoutcome(passed=1)