refine internal management of plugins and conftest files
This commit is contained in:
		
							parent
							
								
									c7a45d6eaf
								
							
						
					
					
						commit
						ea5fb0c153
					
				| 
						 | 
					@ -682,11 +682,8 @@ class Config(object):
 | 
				
			||||||
                setattr(self.option, opt.dest, opt.default)
 | 
					                setattr(self.option, opt.dest, opt.default)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _getmatchingplugins(self, fspath):
 | 
					    def _getmatchingplugins(self, fspath):
 | 
				
			||||||
        allconftests = self._conftest._conftestpath2mod.values()
 | 
					        return self.pluginmanager._plugins + \
 | 
				
			||||||
        plugins = [x for x in self.pluginmanager.getplugins()
 | 
					               self._conftest.getconftestmodules(fspath)
 | 
				
			||||||
                        if x not in allconftests]
 | 
					 | 
				
			||||||
        plugins += self._conftest.getconftestmodules(fspath)
 | 
					 | 
				
			||||||
        return plugins
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def pytest_load_initial_conftests(self, early_config):
 | 
					    def pytest_load_initial_conftests(self, early_config):
 | 
				
			||||||
        self._conftest.setinitial(early_config.known_args_namespace)
 | 
					        self._conftest.setinitial(early_config.known_args_namespace)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -72,6 +72,7 @@ class PluginManager(object):
 | 
				
			||||||
        self._name2plugin = {}
 | 
					        self._name2plugin = {}
 | 
				
			||||||
        self._listattrcache = {}
 | 
					        self._listattrcache = {}
 | 
				
			||||||
        self._plugins = []
 | 
					        self._plugins = []
 | 
				
			||||||
 | 
					        self._conftestplugins = []
 | 
				
			||||||
        self._warnings = []
 | 
					        self._warnings = []
 | 
				
			||||||
        self.trace = TagTracer().get("pluginmanage")
 | 
					        self.trace = TagTracer().get("pluginmanage")
 | 
				
			||||||
        self._plugin_distinfo = []
 | 
					        self._plugin_distinfo = []
 | 
				
			||||||
| 
						 | 
					@ -86,7 +87,7 @@ class PluginManager(object):
 | 
				
			||||||
        assert not hasattr(self, "_registercallback")
 | 
					        assert not hasattr(self, "_registercallback")
 | 
				
			||||||
        self._registercallback = callback
 | 
					        self._registercallback = callback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def register(self, plugin, name=None, prepend=False):
 | 
					    def register(self, plugin, name=None, prepend=False, conftest=False):
 | 
				
			||||||
        if self._name2plugin.get(name, None) == -1:
 | 
					        if self._name2plugin.get(name, None) == -1:
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        name = name or getattr(plugin, '__name__', str(id(plugin)))
 | 
					        name = name or getattr(plugin, '__name__', str(id(plugin)))
 | 
				
			||||||
| 
						 | 
					@ -98,6 +99,9 @@ class PluginManager(object):
 | 
				
			||||||
        reg = getattr(self, "_registercallback", None)
 | 
					        reg = getattr(self, "_registercallback", None)
 | 
				
			||||||
        if reg is not None:
 | 
					        if reg is not None:
 | 
				
			||||||
            reg(plugin, name)
 | 
					            reg(plugin, name)
 | 
				
			||||||
 | 
					        if conftest:
 | 
				
			||||||
 | 
					            self._conftestplugins.append(plugin)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
            if not prepend:
 | 
					            if not prepend:
 | 
				
			||||||
                self._plugins.append(plugin)
 | 
					                self._plugins.append(plugin)
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
| 
						 | 
					@ -107,7 +111,10 @@ class PluginManager(object):
 | 
				
			||||||
    def unregister(self, plugin=None, name=None):
 | 
					    def unregister(self, plugin=None, name=None):
 | 
				
			||||||
        if plugin is None:
 | 
					        if plugin is None:
 | 
				
			||||||
            plugin = self.getplugin(name=name)
 | 
					            plugin = self.getplugin(name=name)
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
            self._plugins.remove(plugin)
 | 
					            self._plugins.remove(plugin)
 | 
				
			||||||
 | 
					        except KeyError:
 | 
				
			||||||
 | 
					            self._conftestplugins.remove(plugin)
 | 
				
			||||||
        for name, value in list(self._name2plugin.items()):
 | 
					        for name, value in list(self._name2plugin.items()):
 | 
				
			||||||
            if value == plugin:
 | 
					            if value == plugin:
 | 
				
			||||||
                del self._name2plugin[name]
 | 
					                del self._name2plugin[name]
 | 
				
			||||||
| 
						 | 
					@ -119,7 +126,7 @@ class PluginManager(object):
 | 
				
			||||||
        while self._shutdown:
 | 
					        while self._shutdown:
 | 
				
			||||||
            func = self._shutdown.pop()
 | 
					            func = self._shutdown.pop()
 | 
				
			||||||
            func()
 | 
					            func()
 | 
				
			||||||
        self._plugins = []
 | 
					        self._plugins = self._conftestplugins = []
 | 
				
			||||||
        self._name2plugin.clear()
 | 
					        self._name2plugin.clear()
 | 
				
			||||||
        self._listattrcache.clear()
 | 
					        self._listattrcache.clear()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -134,7 +141,7 @@ class PluginManager(object):
 | 
				
			||||||
        self.hook._addhooks(spec, prefix=prefix)
 | 
					        self.hook._addhooks(spec, prefix=prefix)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getplugins(self):
 | 
					    def getplugins(self):
 | 
				
			||||||
        return list(self._plugins)
 | 
					        return self._plugins + self._conftestplugins
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def skipifmissing(self, name):
 | 
					    def skipifmissing(self, name):
 | 
				
			||||||
        if not self.hasplugin(name):
 | 
					        if not self.hasplugin(name):
 | 
				
			||||||
| 
						 | 
					@ -198,7 +205,8 @@ class PluginManager(object):
 | 
				
			||||||
                self.import_plugin(arg)
 | 
					                self.import_plugin(arg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def consider_conftest(self, conftestmodule):
 | 
					    def consider_conftest(self, conftestmodule):
 | 
				
			||||||
        if self.register(conftestmodule, name=conftestmodule.__file__):
 | 
					        if self.register(conftestmodule, name=conftestmodule.__file__,
 | 
				
			||||||
 | 
					                         conftest=True):
 | 
				
			||||||
            self.consider_module(conftestmodule)
 | 
					            self.consider_module(conftestmodule)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def consider_module(self, mod):
 | 
					    def consider_module(self, mod):
 | 
				
			||||||
| 
						 | 
					@ -233,12 +241,7 @@ class PluginManager(object):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def listattr(self, attrname, plugins=None):
 | 
					    def listattr(self, attrname, plugins=None):
 | 
				
			||||||
        if plugins is None:
 | 
					        if plugins is None:
 | 
				
			||||||
            plugins = self._plugins
 | 
					            plugins = self._plugins + self._conftestplugins
 | 
				
			||||||
        key = (attrname,) + tuple(plugins)
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            return list(self._listattrcache[key])
 | 
					 | 
				
			||||||
        except KeyError:
 | 
					 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
        l = []
 | 
					        l = []
 | 
				
			||||||
        last = []
 | 
					        last = []
 | 
				
			||||||
        wrappers = []
 | 
					        wrappers = []
 | 
				
			||||||
| 
						 | 
					@ -257,7 +260,7 @@ 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)
 | 
					        #self._listattrcache[key] = list(l)
 | 
				
			||||||
        return l
 | 
					        return l
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def call_plugin(self, plugin, methname, kwargs):
 | 
					    def call_plugin(self, plugin, methname, kwargs):
 | 
				
			||||||
| 
						 | 
					@ -397,6 +400,29 @@ class HookRelay:
 | 
				
			||||||
            raise ValueError("did not find new %r hooks in %r" %(
 | 
					            raise ValueError("did not find new %r hooks in %r" %(
 | 
				
			||||||
                prefix, hookspecs,))
 | 
					                prefix, hookspecs,))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _getcaller(self, name, plugins):
 | 
				
			||||||
 | 
					        caller = getattr(self, name)
 | 
				
			||||||
 | 
					        methods = self._pm.listattr(name, plugins=plugins)
 | 
				
			||||||
 | 
					        return CachedHookCaller(caller, methods)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CachedHookCaller:
 | 
				
			||||||
 | 
					    def __init__(self, hookmethod, methods):
 | 
				
			||||||
 | 
					        self.hookmethod = hookmethod
 | 
				
			||||||
 | 
					        self.methods = methods
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __call__(self, **kwargs):
 | 
				
			||||||
 | 
					        return self.hookmethod._docall(self.methods, kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def callextra(self, methods, **kwargs):
 | 
				
			||||||
 | 
					        # XXX in theory we should respect "tryfirst/trylast" if set
 | 
				
			||||||
 | 
					        # on the added methods but we currently only use it for
 | 
				
			||||||
 | 
					        # pytest_generate_tests and it doesn't make sense there i'd think
 | 
				
			||||||
 | 
					        all = self.methods
 | 
				
			||||||
 | 
					        if methods:
 | 
				
			||||||
 | 
					            all = all + methods
 | 
				
			||||||
 | 
					        return self.hookmethod._docall(all, kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class HookCaller:
 | 
					class HookCaller:
 | 
				
			||||||
    def __init__(self, hookrelay, name, firstresult):
 | 
					    def __init__(self, hookrelay, name, firstresult):
 | 
				
			||||||
| 
						 | 
					@ -412,10 +438,6 @@ class HookCaller:
 | 
				
			||||||
        methods = self.hookrelay._pm.listattr(self.name)
 | 
					        methods = self.hookrelay._pm.listattr(self.name)
 | 
				
			||||||
        return self._docall(methods, kwargs)
 | 
					        return self._docall(methods, kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def pcall(self, plugins, **kwargs):
 | 
					 | 
				
			||||||
        methods = self.hookrelay._pm.listattr(self.name, plugins=plugins)
 | 
					 | 
				
			||||||
        return self._docall(methods, kwargs)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _docall(self, methods, kwargs):
 | 
					    def _docall(self, methods, kwargs):
 | 
				
			||||||
        self.trace(self.name, kwargs)
 | 
					        self.trace(self.name, kwargs)
 | 
				
			||||||
        self.trace.root.indent += 1
 | 
					        self.trace.root.indent += 1
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -153,19 +153,39 @@ def pytest_ignore_collect(path, config):
 | 
				
			||||||
        ignore_paths.extend([py.path.local(x) for x in excludeopt])
 | 
					        ignore_paths.extend([py.path.local(x) for x in excludeopt])
 | 
				
			||||||
    return path in ignore_paths
 | 
					    return path in ignore_paths
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class HookProxy(object):
 | 
					class FSHookProxy(object):
 | 
				
			||||||
    def __init__(self, fspath, config):
 | 
					    def __init__(self, fspath, config):
 | 
				
			||||||
        self.fspath = fspath
 | 
					        self.fspath = fspath
 | 
				
			||||||
        self.config = config
 | 
					        self.config = config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __getattr__(self, name):
 | 
					    def __getattr__(self, name):
 | 
				
			||||||
        config = object.__getattribute__(self, "config")
 | 
					 | 
				
			||||||
        hookmethod = getattr(config.hook, name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def call_matching_hooks(**kwargs):
 | 
					 | 
				
			||||||
        plugins = self.config._getmatchingplugins(self.fspath)
 | 
					        plugins = self.config._getmatchingplugins(self.fspath)
 | 
				
			||||||
            return hookmethod.pcall(plugins, **kwargs)
 | 
					        x = self.config.hook._getcaller(name, plugins)
 | 
				
			||||||
        return call_matching_hooks
 | 
					        self.__dict__[name] = x
 | 
				
			||||||
 | 
					        return x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        hookmethod = getattr(self.config.hook, name)
 | 
				
			||||||
 | 
					        methods = self.config.pluginmanager.listattr(name, plugins=plugins)
 | 
				
			||||||
 | 
					        self.__dict__[name] = x = HookCaller(hookmethod, methods)
 | 
				
			||||||
 | 
					        return x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HookCaller:
 | 
				
			||||||
 | 
					    def __init__(self, hookmethod, methods):
 | 
				
			||||||
 | 
					        self.hookmethod = hookmethod
 | 
				
			||||||
 | 
					        self.methods = methods
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __call__(self, **kwargs):
 | 
				
			||||||
 | 
					        return self.hookmethod._docall(self.methods, kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def callextra(self, methods, **kwargs):
 | 
				
			||||||
 | 
					        # XXX in theory we should respect "tryfirst/trylast" if set
 | 
				
			||||||
 | 
					        # on the added methods but we currently only use it for
 | 
				
			||||||
 | 
					        # pytest_generate_tests and it doesn't make sense there i'd think
 | 
				
			||||||
 | 
					        all = self.methods
 | 
				
			||||||
 | 
					        if methods:
 | 
				
			||||||
 | 
					            all = all + methods
 | 
				
			||||||
 | 
					        return self.hookmethod._docall(all, kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def compatproperty(name):
 | 
					def compatproperty(name):
 | 
				
			||||||
    def fget(self):
 | 
					    def fget(self):
 | 
				
			||||||
| 
						 | 
					@ -520,6 +540,7 @@ class Session(FSCollector):
 | 
				
			||||||
        self.trace = config.trace.root.get("collection")
 | 
					        self.trace = config.trace.root.get("collection")
 | 
				
			||||||
        self._norecursepatterns = config.getini("norecursedirs")
 | 
					        self._norecursepatterns = config.getini("norecursedirs")
 | 
				
			||||||
        self.startdir = py.path.local()
 | 
					        self.startdir = py.path.local()
 | 
				
			||||||
 | 
					        self._fs2hookproxy = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def pytest_collectstart(self):
 | 
					    def pytest_collectstart(self):
 | 
				
			||||||
        if self.shouldstop:
 | 
					        if self.shouldstop:
 | 
				
			||||||
| 
						 | 
					@ -538,7 +559,11 @@ class Session(FSCollector):
 | 
				
			||||||
        return path in self._initialpaths
 | 
					        return path in self._initialpaths
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def gethookproxy(self, fspath):
 | 
					    def gethookproxy(self, fspath):
 | 
				
			||||||
        return HookProxy(fspath, self.config)
 | 
					        try:
 | 
				
			||||||
 | 
					            return self._fs2hookproxy[fspath]
 | 
				
			||||||
 | 
					        except KeyError:
 | 
				
			||||||
 | 
					            self._fs2hookproxy[fspath] = x = FSHookProxy(fspath, self.config)
 | 
				
			||||||
 | 
					            return x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def perform_collect(self, args=None, genitems=True):
 | 
					    def perform_collect(self, args=None, genitems=True):
 | 
				
			||||||
        hook = self.config.hook
 | 
					        hook = self.config.hook
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -353,12 +353,14 @@ class PyCollector(PyobjMixin, pytest.Collector):
 | 
				
			||||||
        fixtureinfo = fm.getfixtureinfo(self, funcobj, cls)
 | 
					        fixtureinfo = fm.getfixtureinfo(self, funcobj, cls)
 | 
				
			||||||
        metafunc = Metafunc(funcobj, fixtureinfo, self.config,
 | 
					        metafunc = Metafunc(funcobj, fixtureinfo, self.config,
 | 
				
			||||||
                            cls=cls, module=module)
 | 
					                            cls=cls, module=module)
 | 
				
			||||||
        gentesthook = self.config.hook.pytest_generate_tests
 | 
					        try:
 | 
				
			||||||
        extra = [module]
 | 
					            methods = [module.pytest_generate_tests]
 | 
				
			||||||
        if cls is not None:
 | 
					        except AttributeError:
 | 
				
			||||||
            extra.append(cls())
 | 
					            methods = []
 | 
				
			||||||
        plugins = self.getplugins() + extra
 | 
					        if hasattr(cls, "pytest_generate_tests"):
 | 
				
			||||||
        gentesthook.pcall(plugins, metafunc=metafunc)
 | 
					            methods.append(cls().pytest_generate_tests)
 | 
				
			||||||
 | 
					        self.ihook.pytest_generate_tests.callextra(methods, metafunc=metafunc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Function = self._getcustomclass("Function")
 | 
					        Function = self._getcustomclass("Function")
 | 
				
			||||||
        if not metafunc._calls:
 | 
					        if not metafunc._calls:
 | 
				
			||||||
            yield Function(name, parent=self)
 | 
					            yield Function(name, parent=self)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue