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