""" managing loading and interacting with pytest plugins. """ import py from py.__.test.plugin import api def check_old_use(mod, modname): clsname = modname[len('pytest_'):].capitalize() + "Plugin" assert not hasattr(mod, clsname), (mod, clsname) class PluginManager(object): def __init__(self, comregistry=None): if comregistry is None: comregistry = py._com.Registry() self.comregistry = comregistry self.MultiCall = self.comregistry.MultiCall self.impname2plugin = {} self.hook = py._com.Hooks( hookspecs=api.PluginHooks, registry=self.comregistry) def _getpluginname(self, plugin, name): if name is None: if hasattr(plugin, '__name__'): name = plugin.__name__.split(".")[-1] else: name = id(plugin) return name def register(self, plugin, name=None): assert not self.isregistered(plugin) name = self._getpluginname(plugin, name) assert name not in self.impname2plugin self.impname2plugin[name] = plugin self.hook.pytest_plugin_registered(plugin=plugin) self.comregistry.register(plugin) return True def unregister(self, plugin): self.hook.pytest_plugin_unregistered(plugin=plugin) self.comregistry.unregister(plugin) for name, value in self.impname2plugin.items(): if value == plugin: del self.impname2plugin[name] def isregistered(self, plugin, name=None): return self._getpluginname(plugin, name) in self.impname2plugin def getplugins(self): return list(self.comregistry) # API for bootstrapping # def getplugin(self, importname): impname = canonical_importname(importname) return self.impname2plugin[impname] def _envlist(self, varname): val = py.std.os.environ.get(varname, None) if val is not None: return val.split(',') return () def consider_env(self): for spec in self._envlist("PYTEST_PLUGINS"): self.import_plugin(spec) def consider_preparse(self, args): for opt1,opt2 in zip(args, args[1:]): if opt1 == "-p": self.import_plugin(opt2) def consider_conftest(self, conftestmodule): cls = getattr(conftestmodule, 'ConftestPlugin', None) if cls is not None: raise ValueError("%r: 'ConftestPlugins' only existed till 1.0.0b1, " "were removed in 1.0.0b2" % (cls,)) if self.register(conftestmodule, name=conftestmodule.__file__): self.consider_module(conftestmodule) def consider_module(self, mod): attr = getattr(mod, "pytest_plugins", ()) if attr: if not isinstance(attr, (list, tuple)): attr = (attr,) for spec in attr: self.import_plugin(spec) def import_plugin(self, spec): assert isinstance(spec, str) modname = canonical_importname(spec) if modname in self.impname2plugin: return mod = importplugin(modname) check_old_use(mod, modname) self.register(mod) self.consider_module(mod) # # # API for interacting with registered and instantiated plugin objects # # def getfirst(self, attrname): for x in self.comregistry.listattr(attrname): return x def listattr(self, attrname, plugins=None, extra=()): return self.comregistry.listattr(attrname, plugins=plugins, extra=extra) def notify_exception(self, excinfo=None): if excinfo is None: excinfo = py.code.ExceptionInfo() excrepr = excinfo.getrepr(funcargs=True, showlocals=True) return self.hook.pytest_internalerror(excrepr=excrepr) def do_addoption(self, parser): methods = self.comregistry.listattr("pytest_addoption", reverse=True) mc = py._com.MultiCall(methods, parser=parser) mc.execute() def pytest_plugin_registered(self, plugin): if hasattr(self, '_config'): self.call_plugin(plugin, "pytest_addoption", parser=self._config._parser) self.call_plugin(plugin, "pytest_configure", config=self._config) def call_plugin(self, plugin, methname, **kwargs): return self.MultiCall(self.listattr(methname, plugins=[plugin]), **kwargs).execute(firstresult=True) def do_configure(self, config): assert not hasattr(self, '_config') config.pluginmanager.register(self) self._config = config config.hook.pytest_configure(config=self._config) def do_unconfigure(self, config): config = self._config del self._config config.hook.pytest_unconfigure(config=config) config.pluginmanager.unregister(self) def do_itemrun(self, item, pdb=None): res = self.hook.pytest_itemrun(item=item, pdb=pdb) if res is None: raise ValueError("could not run %r" %(item,)) # # XXX old code to automatically load classes # def canonical_importname(name): name = name.lower() modprefix = "pytest_" if not name.startswith(modprefix): name = modprefix + name return name def importplugin(importspec): try: return __import__(importspec) except ImportError, e: if str(e).find(importspec) == -1: raise try: return __import__("py.__.test.plugin.%s" %(importspec), None, None, '__doc__') except ImportError, e: if str(e).find(importspec) == -1: raise #print "syspath:", py.std.sys.path #print "curdir:", py.std.os.getcwd() return __import__(importspec) # show the original exception