remove overhead for tracing of hook calls and remove some old unused code

This commit is contained in:
holger krekel 2014-10-02 15:25:42 +02:00
parent c7c4f62f77
commit 69ff29bf44
5 changed files with 37 additions and 55 deletions

View File

@ -98,7 +98,7 @@ class PytestPluginManager(PluginManager):
err = py.io.dupfile(err, encoding=encoding)
except Exception:
pass
self.trace.root.setwriter(err.write)
self.set_tracing(err.write)
def pytest_configure(self, config):
config.addinivalue_line("markers",

View File

@ -70,7 +70,6 @@ class TagTracerSub:
class PluginManager(object):
def __init__(self, hookspecs=None, prefix="pytest_"):
self._name2plugin = {}
self._listattrcache = {}
self._plugins = []
self._conftestplugins = []
self._warnings = []
@ -79,6 +78,26 @@ class PluginManager(object):
self._shutdown = []
self.hook = HookRelay(hookspecs or [], pm=self, prefix=prefix)
def set_tracing(self, writer):
self.trace.root.setwriter(writer)
# we reconfigure HookCalling to perform tracing
# and we avoid doing the "do we need to trace" check dynamically
# for speed reasons
assert HookCaller._docall.__name__ == "_docall"
real_docall = HookCaller._docall
def docall_tracing(self, methods, kwargs):
trace = self.hookrelay.trace
trace.root.indent += 1
trace(self.name, kwargs)
try:
res = real_docall(self, methods, kwargs)
if res:
trace("finish", self.name, "-->", res)
finally:
trace.root.indent -= 1
return res
HookCaller._docall = docall_tracing
def do_configure(self, config):
# backward compatibility
config.do_configure()
@ -129,7 +148,6 @@ class PluginManager(object):
func()
self._plugins = self._conftestplugins = []
self._name2plugin.clear()
self._listattrcache.clear()
def isregistered(self, plugin, name=None):
if self.getplugin(name) is not None:
@ -261,7 +279,6 @@ class PluginManager(object):
l.append(meth)
l.extend(last)
l.extend(wrappers)
#self._listattrcache[key] = list(l)
return l
def call_plugin(self, plugin, methname, kwargs):
@ -336,7 +353,7 @@ class MultiCall:
"wrapper contain more than one yield")
def varnames(func):
def varnames(func, startindex=None):
""" return argument name tuple for a function, method, class or callable.
In case of a class, its "__init__" method is considered.
@ -353,14 +370,16 @@ def varnames(func):
func = func.__init__
except AttributeError:
return ()
ismethod = True
startindex = 1
else:
if not inspect.isfunction(func) and not inspect.ismethod(func):
func = getattr(func, '__call__', func)
ismethod = inspect.ismethod(func)
if startindex is None:
startindex = int(inspect.ismethod(func))
rawcode = py.code.getrawcode(func)
try:
x = rawcode.co_varnames[ismethod:rawcode.co_argcount]
x = rawcode.co_varnames[startindex:rawcode.co_argcount]
except AttributeError:
x = ()
else:
@ -388,12 +407,12 @@ class HookRelay:
def _addhooks(self, hookspec, prefix):
self._hookspecs.append(hookspec)
added = False
for name in dir(hookspec):
isclass = int(inspect.isclass(hookspec))
for name, method in vars(hookspec).items():
if name.startswith(prefix):
method = getattr(hookspec, name)
firstresult = getattr(method, 'firstresult', False)
hc = HookCaller(self, name, firstresult=firstresult,
argnames=varnames(method))
argnames=varnames(method, startindex=isclass))
setattr(self, name, hc)
added = True
#print ("setting new hook", name)
@ -438,11 +457,10 @@ class HookCaller:
self.hookrelay = hookrelay
self.name = name
self.firstresult = firstresult
self.trace = self.hookrelay.trace
self.methods = methods
self.argnames = ["__multicall__"]
self.argnames.extend(argnames)
assert "self" not in argnames
assert "self" not in argnames # prevent oversights
def new_cached_caller(self, methods):
return HookCaller(self.hookrelay, self.name, self.firstresult,
@ -461,22 +479,11 @@ class HookCaller:
return self._docall(methods, kwargs)
def callextra(self, methods, **kwargs):
#if self.methods is None:
# self.reload_methods()
return self._docall(self.methods + methods, kwargs)
def _docall(self, methods, kwargs):
self.trace(self.name, kwargs)
self.trace.root.indent += 1
mc = MultiCall(methods, kwargs, firstresult=self.firstresult)
try:
res = mc.execute()
if res:
self.trace("finish", self.name, "-->", res)
finally:
self.trace.root.indent -= 1
return res
return MultiCall(methods, kwargs,
firstresult=self.firstresult).execute()
class PluginValidationError(Exception):
@ -486,13 +493,6 @@ def isgenerichook(name):
return name == "pytest_plugins" or \
name.startswith("pytest_funcarg__")
def collectattr(obj):
methods = {}
for apiname in dir(obj):
if apiname.startswith("pytest_"):
methods[apiname] = getattr(obj, apiname)
return methods
def formatdef(func):
return "%s%s" % (
func.__name__,

View File

@ -1,8 +1,7 @@
""" version info, help messages, tracing configuration. """
import py
import pytest
import os, inspect, sys
from _pytest.core import varnames
import os, sys
def pytest_addoption(parser):
group = parser.getgroup('debugconfig')
@ -32,7 +31,7 @@ def pytest_cmdline_parse(__multicall__):
f.write("versions pytest-%s, py-%s, python-%s\ncwd=%s\nargs=%s\n\n" %(
pytest.__version__, py.__version__, ".".join(map(str, sys.version_info)),
os.getcwd(), config._origargs))
config.trace.root.setwriter(f.write)
config.pluginmanager.set_tracing(f.write)
sys.stderr.write("writing pytestdebug information to %s\n" % path)
return config

View File

@ -274,7 +274,7 @@ class TestBootstrapping:
saveindent.append(pm.trace.root.indent)
raise ValueError(42)
l = []
pm.trace.root.setwriter(l.append)
pm.set_tracing(l.append)
indent = pm.trace.root.indent
p = api1()
pm.register(p)
@ -405,11 +405,7 @@ class TestPytestPluginInteractions:
pluginmanager.register(p3)
methods = pluginmanager.listattr('m')
assert methods == [p2.m, p3.m, p1.m]
# listattr keeps a cache and deleting
# a function attribute requires clearing it
pluginmanager._listattrcache.clear()
del P1.m.__dict__['tryfirst']
pytest.mark.trylast(getattr(P2.m, 'im_func', P2.m))
methods = pluginmanager.listattr('m')
assert methods == [p2.m, p1.m, p3.m]

View File

@ -1,5 +1,4 @@
import py, pytest
from _pytest.core import collectattr
import pytest
def test_version(testdir, pytestconfig):
result = testdir.runpytest("--version")
@ -25,18 +24,6 @@ def test_help(testdir):
*to see*fixtures*py.test --fixtures*
""")
def test_collectattr():
class A:
def pytest_hello(self):
pass
class B(A):
def pytest_world(self):
pass
methods = py.builtin.sorted(collectattr(B))
assert list(methods) == ['pytest_hello', 'pytest_world']
methods = py.builtin.sorted(collectattr(B()))
assert list(methods) == ['pytest_hello', 'pytest_world']
def test_hookvalidation_unknown(testdir):
testdir.makeconftest("""
def pytest_hello(xyz):