remove overhead for tracing of hook calls and remove some old unused code
This commit is contained in:
parent
c7c4f62f77
commit
69ff29bf44
|
@ -98,7 +98,7 @@ class PytestPluginManager(PluginManager):
|
||||||
err = py.io.dupfile(err, encoding=encoding)
|
err = py.io.dupfile(err, encoding=encoding)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
self.trace.root.setwriter(err.write)
|
self.set_tracing(err.write)
|
||||||
|
|
||||||
def pytest_configure(self, config):
|
def pytest_configure(self, config):
|
||||||
config.addinivalue_line("markers",
|
config.addinivalue_line("markers",
|
||||||
|
|
|
@ -70,7 +70,6 @@ class TagTracerSub:
|
||||||
class PluginManager(object):
|
class PluginManager(object):
|
||||||
def __init__(self, hookspecs=None, prefix="pytest_"):
|
def __init__(self, hookspecs=None, prefix="pytest_"):
|
||||||
self._name2plugin = {}
|
self._name2plugin = {}
|
||||||
self._listattrcache = {}
|
|
||||||
self._plugins = []
|
self._plugins = []
|
||||||
self._conftestplugins = []
|
self._conftestplugins = []
|
||||||
self._warnings = []
|
self._warnings = []
|
||||||
|
@ -79,6 +78,26 @@ class PluginManager(object):
|
||||||
self._shutdown = []
|
self._shutdown = []
|
||||||
self.hook = HookRelay(hookspecs or [], pm=self, prefix=prefix)
|
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):
|
def do_configure(self, config):
|
||||||
# backward compatibility
|
# backward compatibility
|
||||||
config.do_configure()
|
config.do_configure()
|
||||||
|
@ -129,7 +148,6 @@ class PluginManager(object):
|
||||||
func()
|
func()
|
||||||
self._plugins = self._conftestplugins = []
|
self._plugins = self._conftestplugins = []
|
||||||
self._name2plugin.clear()
|
self._name2plugin.clear()
|
||||||
self._listattrcache.clear()
|
|
||||||
|
|
||||||
def isregistered(self, plugin, name=None):
|
def isregistered(self, plugin, name=None):
|
||||||
if self.getplugin(name) is not None:
|
if self.getplugin(name) is not None:
|
||||||
|
@ -261,7 +279,6 @@ class PluginManager(object):
|
||||||
l.append(meth)
|
l.append(meth)
|
||||||
l.extend(last)
|
l.extend(last)
|
||||||
l.extend(wrappers)
|
l.extend(wrappers)
|
||||||
#self._listattrcache[key] = list(l)
|
|
||||||
return l
|
return l
|
||||||
|
|
||||||
def call_plugin(self, plugin, methname, kwargs):
|
def call_plugin(self, plugin, methname, kwargs):
|
||||||
|
@ -336,7 +353,7 @@ class MultiCall:
|
||||||
"wrapper contain more than one yield")
|
"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.
|
""" return argument name tuple for a function, method, class or callable.
|
||||||
|
|
||||||
In case of a class, its "__init__" method is considered.
|
In case of a class, its "__init__" method is considered.
|
||||||
|
@ -353,14 +370,16 @@ def varnames(func):
|
||||||
func = func.__init__
|
func = func.__init__
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return ()
|
return ()
|
||||||
ismethod = True
|
startindex = 1
|
||||||
else:
|
else:
|
||||||
if not inspect.isfunction(func) and not inspect.ismethod(func):
|
if not inspect.isfunction(func) and not inspect.ismethod(func):
|
||||||
func = getattr(func, '__call__', func)
|
func = getattr(func, '__call__', func)
|
||||||
ismethod = inspect.ismethod(func)
|
if startindex is None:
|
||||||
|
startindex = int(inspect.ismethod(func))
|
||||||
|
|
||||||
rawcode = py.code.getrawcode(func)
|
rawcode = py.code.getrawcode(func)
|
||||||
try:
|
try:
|
||||||
x = rawcode.co_varnames[ismethod:rawcode.co_argcount]
|
x = rawcode.co_varnames[startindex:rawcode.co_argcount]
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
x = ()
|
x = ()
|
||||||
else:
|
else:
|
||||||
|
@ -388,12 +407,12 @@ class HookRelay:
|
||||||
def _addhooks(self, hookspec, prefix):
|
def _addhooks(self, hookspec, prefix):
|
||||||
self._hookspecs.append(hookspec)
|
self._hookspecs.append(hookspec)
|
||||||
added = False
|
added = False
|
||||||
for name in dir(hookspec):
|
isclass = int(inspect.isclass(hookspec))
|
||||||
|
for name, method in vars(hookspec).items():
|
||||||
if name.startswith(prefix):
|
if name.startswith(prefix):
|
||||||
method = getattr(hookspec, name)
|
|
||||||
firstresult = getattr(method, 'firstresult', False)
|
firstresult = getattr(method, 'firstresult', False)
|
||||||
hc = HookCaller(self, name, firstresult=firstresult,
|
hc = HookCaller(self, name, firstresult=firstresult,
|
||||||
argnames=varnames(method))
|
argnames=varnames(method, startindex=isclass))
|
||||||
setattr(self, name, hc)
|
setattr(self, name, hc)
|
||||||
added = True
|
added = True
|
||||||
#print ("setting new hook", name)
|
#print ("setting new hook", name)
|
||||||
|
@ -438,11 +457,10 @@ class HookCaller:
|
||||||
self.hookrelay = hookrelay
|
self.hookrelay = hookrelay
|
||||||
self.name = name
|
self.name = name
|
||||||
self.firstresult = firstresult
|
self.firstresult = firstresult
|
||||||
self.trace = self.hookrelay.trace
|
|
||||||
self.methods = methods
|
self.methods = methods
|
||||||
self.argnames = ["__multicall__"]
|
self.argnames = ["__multicall__"]
|
||||||
self.argnames.extend(argnames)
|
self.argnames.extend(argnames)
|
||||||
assert "self" not in argnames
|
assert "self" not in argnames # prevent oversights
|
||||||
|
|
||||||
def new_cached_caller(self, methods):
|
def new_cached_caller(self, methods):
|
||||||
return HookCaller(self.hookrelay, self.name, self.firstresult,
|
return HookCaller(self.hookrelay, self.name, self.firstresult,
|
||||||
|
@ -461,22 +479,11 @@ class HookCaller:
|
||||||
return self._docall(methods, kwargs)
|
return self._docall(methods, kwargs)
|
||||||
|
|
||||||
def callextra(self, methods, **kwargs):
|
def callextra(self, methods, **kwargs):
|
||||||
#if self.methods is None:
|
|
||||||
# self.reload_methods()
|
|
||||||
return self._docall(self.methods + methods, kwargs)
|
return self._docall(self.methods + methods, kwargs)
|
||||||
|
|
||||||
def _docall(self, methods, kwargs):
|
def _docall(self, methods, kwargs):
|
||||||
self.trace(self.name, kwargs)
|
return MultiCall(methods, kwargs,
|
||||||
self.trace.root.indent += 1
|
firstresult=self.firstresult).execute()
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PluginValidationError(Exception):
|
class PluginValidationError(Exception):
|
||||||
|
@ -486,13 +493,6 @@ def isgenerichook(name):
|
||||||
return name == "pytest_plugins" or \
|
return name == "pytest_plugins" or \
|
||||||
name.startswith("pytest_funcarg__")
|
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):
|
def formatdef(func):
|
||||||
return "%s%s" % (
|
return "%s%s" % (
|
||||||
func.__name__,
|
func.__name__,
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
""" version info, help messages, tracing configuration. """
|
""" version info, help messages, tracing configuration. """
|
||||||
import py
|
import py
|
||||||
import pytest
|
import pytest
|
||||||
import os, inspect, sys
|
import os, sys
|
||||||
from _pytest.core import varnames
|
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
group = parser.getgroup('debugconfig')
|
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" %(
|
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)),
|
pytest.__version__, py.__version__, ".".join(map(str, sys.version_info)),
|
||||||
os.getcwd(), config._origargs))
|
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)
|
sys.stderr.write("writing pytestdebug information to %s\n" % path)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
|
@ -274,7 +274,7 @@ class TestBootstrapping:
|
||||||
saveindent.append(pm.trace.root.indent)
|
saveindent.append(pm.trace.root.indent)
|
||||||
raise ValueError(42)
|
raise ValueError(42)
|
||||||
l = []
|
l = []
|
||||||
pm.trace.root.setwriter(l.append)
|
pm.set_tracing(l.append)
|
||||||
indent = pm.trace.root.indent
|
indent = pm.trace.root.indent
|
||||||
p = api1()
|
p = api1()
|
||||||
pm.register(p)
|
pm.register(p)
|
||||||
|
@ -405,11 +405,7 @@ class TestPytestPluginInteractions:
|
||||||
pluginmanager.register(p3)
|
pluginmanager.register(p3)
|
||||||
methods = pluginmanager.listattr('m')
|
methods = pluginmanager.listattr('m')
|
||||||
assert methods == [p2.m, p3.m, p1.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']
|
del P1.m.__dict__['tryfirst']
|
||||||
|
|
||||||
pytest.mark.trylast(getattr(P2.m, 'im_func', P2.m))
|
pytest.mark.trylast(getattr(P2.m, 'im_func', P2.m))
|
||||||
methods = pluginmanager.listattr('m')
|
methods = pluginmanager.listattr('m')
|
||||||
assert methods == [p2.m, p1.m, p3.m]
|
assert methods == [p2.m, p1.m, p3.m]
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import py, pytest
|
import pytest
|
||||||
from _pytest.core import collectattr
|
|
||||||
|
|
||||||
def test_version(testdir, pytestconfig):
|
def test_version(testdir, pytestconfig):
|
||||||
result = testdir.runpytest("--version")
|
result = testdir.runpytest("--version")
|
||||||
|
@ -25,18 +24,6 @@ def test_help(testdir):
|
||||||
*to see*fixtures*py.test --fixtures*
|
*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):
|
def test_hookvalidation_unknown(testdir):
|
||||||
testdir.makeconftest("""
|
testdir.makeconftest("""
|
||||||
def pytest_hello(xyz):
|
def pytest_hello(xyz):
|
||||||
|
|
Loading…
Reference in New Issue