diff --git a/doc/customize.txt b/doc/customize.txt index 094bbe336..eaedf5e64 100644 --- a/doc/customize.txt +++ b/doc/customize.txt @@ -232,7 +232,7 @@ initialisation, command line and configuration hooks generic "runtest" hooks ------------------------------ -Almost runtest related hooks receive a :py:class:`pytest.collect.Item` object. +All all runtest related hooks receive a :py:class:`pytest.collect.Item` object. .. autofunction:: pytest_runtest_protocol .. autofunction:: pytest_runtest_setup @@ -340,13 +340,13 @@ name. Given a filesystem ``fspath`` it is constructed as follows: Reference of important objects involved in hooks =========================================================== -.. autoclass:: pytest._config.Config +.. autoclass:: pytest._core.Config :members: -.. autoclass:: pytest.collect.Item +.. autoclass:: pytest.plugin.session.Item :inherited-members: -.. autoclass:: pytest.collect.Node +.. autoclass:: pytest.plugin.session.Node :members: .. autoclass:: pytest.plugin.runner.CallInfo diff --git a/pytest/__init__.py b/pytest/__init__.py index 2d6107fd1..c26945382 100644 --- a/pytest/__init__.py +++ b/pytest/__init__.py @@ -1,14 +1,17 @@ """ py.test / pytest API for unit and functional testing with Python. + +see http://pytest.org for documentation and details + (c) Holger Krekel and others, 2004-2010 """ __version__ = "2.0.0dev0" -__all__ = ['collect', 'cmdline', 'config'] +__all__ = ['config', 'cmdline'] -import pytest._config -config = pytest._config.Config() -from pytest import main as cmdline +from pytest import _core as cmdline +from pytest._core import Config +config = Config() def __main__(): raise SystemExit(cmdline.main()) diff --git a/pytest/_config.py b/pytest/_core.py similarity index 52% rename from pytest/_config.py rename to pytest/_core.py index 01beba3b1..82adcac9f 100644 --- a/pytest/_config.py +++ b/pytest/_core.py @@ -1,6 +1,25 @@ -import py, os -from pytest.pluginmanager import PluginManager -import optparse +import py +import sys, os + +default_plugins = ( + "session terminal python runner pdb capture mark skipping tmpdir monkeypatch " + "recwarn pastebin unittest helpconfig nose assertion genscript " + "junitxml doctest keyword").split() + +def main(args=None): + import sys + if args is None: + args = sys.argv[1:] + config = py.test.config + config.parse(args) + try: + exitstatus = config.hook.pytest_cmdline_main(config=config) + except config.Error: + e = sys.exc_info()[1] + sys.stderr.write("ERROR: %s\n" %(e.args[0],)) + exitstatus = 3 + py.test.config = config.__class__() + return exitstatus class Parser: """ Parser for command line arguments. """ @@ -47,7 +66,7 @@ class Parser: for group in groups: if group.options: desc = group.description or group.name - optgroup = optparse.OptionGroup(optparser, desc) + optgroup = py.std.optparse.OptionGroup(optparser, desc) optgroup.add_options(group.options) optparser.add_option_group(optgroup) return optparser.parse_args([str(x) for x in args]) @@ -68,11 +87,11 @@ class OptionGroup: def addoption(self, *optnames, **attrs): """ add an option to this group. """ - option = optparse.Option(*optnames, **attrs) + option = py.std.optparse.Option(*optnames, **attrs) self._addoption_instance(option, shortupper=False) def _addoption(self, *optnames, **attrs): - option = optparse.Option(*optnames, **attrs) + option = py.std.optparse.Option(*optnames, **attrs) self._addoption_instance(option, shortupper=True) def _addoption_instance(self, option, shortupper=False): @@ -85,10 +104,10 @@ class OptionGroup: self.options.append(option) -class MyOptionParser(optparse.OptionParser): +class MyOptionParser(py.std.optparse.OptionParser): def __init__(self, parser): self._parser = parser - optparse.OptionParser.__init__(self, usage=parser._usage) + py.std.optparse.OptionParser.__init__(self, usage=parser._usage) def format_epilog(self, formatter): hints = self._parser.hints if hints: @@ -96,6 +115,7 @@ class MyOptionParser(optparse.OptionParser): s = "\n" + s + "\n" return s return "" + class Conftest(object): """ the single place for accessing values and interacting towards conftest modules from py.test objects. @@ -386,15 +406,368 @@ class Config(object): except AttributeError: return self._conftest.rget(name, path) -# -# helpers -# +class PluginManager(object): + def __init__(self): + from pytest import hookspec + self.registry = Registry() + self._name2plugin = {} + self._hints = [] + self.hook = HookRelay([hookspec], registry=self.registry) + self.register(self) + for spec in default_plugins: + self.import_plugin(spec) -def onpytestaccess(): - # it's enough to have our containing module loaded as - # it initializes a per-process config instance - # which loads default plugins which add to py.test.* - pass + 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, prepend=False): + assert not self.isregistered(plugin), plugin + assert not self.registry.isregistered(plugin), plugin + name = self._getpluginname(plugin, name) + if name in self._name2plugin: + return False + self._name2plugin[name] = plugin + self.call_plugin(plugin, "pytest_addhooks", {'pluginmanager': self}) + self.hook.pytest_plugin_registered(manager=self, plugin=plugin) + self.registry.register(plugin, prepend=prepend) + return True + + def unregister(self, plugin): + self.hook.pytest_plugin_unregistered(plugin=plugin) + self.registry.unregister(plugin) + for name, value in list(self._name2plugin.items()): + if value == plugin: + del self._name2plugin[name] + + def isregistered(self, plugin, name=None): + if self._getpluginname(plugin, name) in self._name2plugin: + return True + for val in self._name2plugin.values(): + if plugin == val: + return True + + def addhooks(self, spec): + self.hook._addhooks(spec, prefix="pytest_") + + def getplugins(self): + return list(self.registry) + + def skipifmissing(self, name): + if not self.hasplugin(name): + py.test.skip("plugin %r is missing" % name) + + def hasplugin(self, name): + try: + self.getplugin(name) + except KeyError: + return False + else: + return True + + def getplugin(self, name): + try: + return self._name2plugin[name] + except KeyError: + impname = canonical_importname(name) + return self._name2plugin[impname] + + # API for bootstrapping + # + 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_setuptools_entrypoints(self): + try: + from pkg_resources import iter_entry_points + except ImportError: + return # XXX issue a warning + for ep in iter_entry_points('pytest11'): + name = canonical_importname(ep.name) + if name in self._name2plugin: + continue + plugin = ep.load() + self.register(plugin, name=name) + + 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._name2plugin: + return + try: + mod = importplugin(modname) + except KeyboardInterrupt: + raise + except py.test.skip.Exception: + e = py.std.sys.exc_info()[1] + self._hints.append("skipped plugin %r: %s" %((modname, e.msg))) + else: + self.register(mod, modname) + self.consider_module(mod) + + def pytest_terminal_summary(self, terminalreporter): + tw = terminalreporter._tw + if terminalreporter.config.option.traceconfig: + for hint in self._hints: + tw.line("hint: %s" % hint) + + # + # + # API for interacting with registered and instantiated plugin objects + # + # + def listattr(self, attrname, plugins=None): + return self.registry.listattr(attrname, plugins=plugins) + + def notify_exception(self, excinfo=None): + if excinfo is None: + excinfo = py.code.ExceptionInfo() + excrepr = excinfo.getrepr(funcargs=True, showlocals=True) + res = self.hook.pytest_internalerror(excrepr=excrepr) + if not py.builtin.any(res): + for line in str(excrepr).split("\n"): + sys.stderr.write("INTERNALERROR> %s\n" %line) + sys.stderr.flush() + + def do_addoption(self, parser): + mname = "pytest_addoption" + methods = self.registry.listattr(mname, reverse=True) + mc = MultiCall(methods, {'parser': parser}) + mc.execute() + + def _setns(self, obj, dic): + for name, value in dic.items(): + if isinstance(value, dict): + mod = getattr(obj, name, None) + if mod is None: + mod = py.std.types.ModuleType(name) + sys.modules['pytest.%s' % name] = mod + sys.modules['py.test.%s' % name] = mod + mod.__all__ = [] + setattr(obj, name, mod) + self._setns(mod, value) + else: + #print "setting", name, value, "on", obj + setattr(obj, name, value) + obj.__all__.append(name) + + def pytest_plugin_registered(self, plugin): + dic = self.call_plugin(plugin, "pytest_namespace", {}) or {} + if dic: + self._setns(py.test, dic) + 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 MultiCall( + methods=self.listattr(methname, plugins=[plugin]), + kwargs=kwargs, firstresult=True).execute() + + def do_configure(self, config): + assert not hasattr(self, '_config') + 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 canonical_importname(name): + name = name.lower() + modprefix = "pytest_" + if not name.startswith(modprefix): + name = modprefix + name + return name + +def importplugin(importspec): + #print "importing", importspec + try: + return __import__(importspec) + except ImportError: + e = py.std.sys.exc_info()[1] + if str(e).find(importspec) == -1: + raise + name = importspec + try: + if name.startswith("pytest_"): + name = importspec[7:] + return __import__("pytest.plugin.%s" %(name), None, None, '__doc__') + except ImportError: + e = py.std.sys.exc_info()[1] + if str(e).find(name) == -1: + raise + # show the original exception, not the failing internal one + return __import__(importspec) +class MultiCall: + """ execute a call into multiple python functions/methods. """ + + def __init__(self, methods, kwargs, firstresult=False): + self.methods = methods[:] + self.kwargs = kwargs.copy() + self.kwargs['__multicall__'] = self + self.results = [] + self.firstresult = firstresult + + def __repr__(self): + status = "%d results, %d meths" % (len(self.results), len(self.methods)) + return "" %(status, self.kwargs) + + def execute(self): + while self.methods: + method = self.methods.pop() + kwargs = self.getkwargs(method) + res = method(**kwargs) + if res is not None: + self.results.append(res) + if self.firstresult: + return res + if not self.firstresult: + return self.results + + def getkwargs(self, method): + kwargs = {} + for argname in varnames(method): + try: + kwargs[argname] = self.kwargs[argname] + except KeyError: + pass # might be optional param + return kwargs + +def varnames(func): + import inspect + if not inspect.isfunction(func) and not inspect.ismethod(func): + func = getattr(func, '__call__', func) + ismethod = inspect.ismethod(func) + rawcode = py.code.getrawcode(func) + try: + return rawcode.co_varnames[ismethod:] + except AttributeError: + return () + +class Registry: + """ + Manage Plugins: register/unregister call calls to plugins. + """ + def __init__(self, plugins=None): + if plugins is None: + plugins = [] + self._plugins = plugins + + def register(self, plugin, prepend=False): + assert not isinstance(plugin, str) + assert not plugin in self._plugins + if not prepend: + self._plugins.append(plugin) + else: + self._plugins.insert(0, plugin) + + def unregister(self, plugin): + self._plugins.remove(plugin) + + def isregistered(self, plugin): + return plugin in self._plugins + + def __iter__(self): + return iter(self._plugins) + + def listattr(self, attrname, plugins=None, reverse=False): + l = [] + if plugins is None: + plugins = self._plugins + for plugin in plugins: + try: + l.append(getattr(plugin, attrname)) + except AttributeError: + continue + if reverse: + l.reverse() + return l + +class HookRelay: + def __init__(self, hookspecs, registry, prefix="pytest_"): + if not isinstance(hookspecs, list): + hookspecs = [hookspecs] + self._hookspecs = [] + self._registry = registry + for hookspec in hookspecs: + self._addhooks(hookspec, prefix) + + def _addhooks(self, hookspecs, prefix): + self._hookspecs.append(hookspecs) + added = False + for name, method in vars(hookspecs).items(): + if name.startswith(prefix): + if not method.__doc__: + raise ValueError("docstring required for hook %r, in %r" + % (method, hookspecs)) + firstresult = getattr(method, 'firstresult', False) + hc = HookCaller(self, name, firstresult=firstresult) + setattr(self, name, hc) + added = True + #print ("setting new hook", name) + if not added: + raise ValueError("did not find new %r hooks in %r" %( + prefix, hookspecs,)) + + + def _performcall(self, name, multicall): + return multicall.execute() + +class HookCaller: + def __init__(self, hookrelay, name, firstresult): + self.hookrelay = hookrelay + self.name = name + self.firstresult = firstresult + + def __repr__(self): + return "" %(self.name,) + + def __call__(self, **kwargs): + methods = self.hookrelay._registry.listattr(self.name) + mc = MultiCall(methods, kwargs, firstresult=self.firstresult) + return self.hookrelay._performcall(self.name, mc) + + def pcall(self, plugins, **kwargs): + methods = self.hookrelay._registry.listattr(self.name, plugins=plugins) + mc = MultiCall(methods, kwargs, firstresult=self.firstresult) + return self.hookrelay._performcall(self.name, mc) diff --git a/pytest/hookspec.py b/pytest/hookspec.py index b61c0e9a9..fdde570ff 100644 --- a/pytest/hookspec.py +++ b/pytest/hookspec.py @@ -1,6 +1,4 @@ -""" -hook specifications for py.test plugins -""" +""" hook specifications for pytest plugins, invoked from _core.py and builtin plugins. """ # ------------------------------------------------------------------------- # Initialization diff --git a/pytest/main.py b/pytest/main.py deleted file mode 100644 index e4b90065c..000000000 --- a/pytest/main.py +++ /dev/null @@ -1,18 +0,0 @@ -__all__ = [] -import py, pytest - -def main(args=None): - import sys - if args is None: - args = sys.argv[1:] - config = py.test.config - config.parse(args) - try: - exitstatus = config.hook.pytest_cmdline_main(config=config) - except config.Error: - e = sys.exc_info()[1] - sys.stderr.write("ERROR: %s\n" %(e.args[0],)) - exitstatus = 3 - py.test.config = config.__class__() - return exitstatus - diff --git a/pytest/plugin/_pytest.py b/pytest/plugin/_pytest.py index 5bc952073..d8f448da6 100644 --- a/pytest/plugin/_pytest.py +++ b/pytest/plugin/_pytest.py @@ -1,6 +1,6 @@ import py -from pytest.pluginmanager import HookRelay +from pytest._core import HookRelay def pytest_funcarg___pytest(request): return PytestArg(request) diff --git a/pytest/plugin/pytester.py b/pytest/plugin/pytester.py index 9b2faca4c..cfcf5fe74 100644 --- a/pytest/plugin/pytester.py +++ b/pytest/plugin/pytester.py @@ -8,7 +8,7 @@ import re import inspect import time from fnmatch import fnmatch -from pytest._config import Config as pytestConfig +from pytest._core import Config as pytestConfig from pytest.plugin.session import Collection from py.builtin import print_ @@ -222,7 +222,6 @@ class TmpTestdir: """ this is used from tests that want to re-invoke parse(). """ if not args: args = [self.tmpdir] - from pytest import _config oldconfig = py.test.config try: c = py.test.config = pytestConfig() diff --git a/pytest/pluginmanager.py b/pytest/pluginmanager.py deleted file mode 100644 index 8ed18506e..000000000 --- a/pytest/pluginmanager.py +++ /dev/null @@ -1,381 +0,0 @@ -""" -managing loading and interacting with pytest plugins. -""" -import py -import sys - -default_plugins = ( - "session terminal python runner pdb capture mark skipping tmpdir monkeypatch " - "recwarn pastebin unittest helpconfig nose assertion genscript " - "junitxml doctest keyword").split() - -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): - from pytest import hookspec - self.registry = Registry() - self._name2plugin = {} - self._hints = [] - self.hook = HookRelay([hookspec], registry=self.registry) - self.register(self) - for spec in default_plugins: - self.import_plugin(spec) - - 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, prepend=False): - assert not self.isregistered(plugin), plugin - assert not self.registry.isregistered(plugin), plugin - name = self._getpluginname(plugin, name) - if name in self._name2plugin: - return False - self._name2plugin[name] = plugin - self.call_plugin(plugin, "pytest_addhooks", {'pluginmanager': self}) - self.hook.pytest_plugin_registered(manager=self, plugin=plugin) - self.registry.register(plugin, prepend=prepend) - return True - - def unregister(self, plugin): - self.hook.pytest_plugin_unregistered(plugin=plugin) - self.registry.unregister(plugin) - for name, value in list(self._name2plugin.items()): - if value == plugin: - del self._name2plugin[name] - - def isregistered(self, plugin, name=None): - if self._getpluginname(plugin, name) in self._name2plugin: - return True - for val in self._name2plugin.values(): - if plugin == val: - return True - - def addhooks(self, spec): - self.hook._addhooks(spec, prefix="pytest_") - - def getplugins(self): - return list(self.registry) - - def skipifmissing(self, name): - if not self.hasplugin(name): - py.test.skip("plugin %r is missing" % name) - - def hasplugin(self, name): - try: - self.getplugin(name) - except KeyError: - return False - else: - return True - - def getplugin(self, name): - try: - return self._name2plugin[name] - except KeyError: - impname = canonical_importname(name) - return self._name2plugin[impname] - - # API for bootstrapping - # - 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_setuptools_entrypoints(self): - try: - from pkg_resources import iter_entry_points - except ImportError: - return # XXX issue a warning - for ep in iter_entry_points('pytest11'): - name = canonical_importname(ep.name) - if name in self._name2plugin: - continue - plugin = ep.load() - self.register(plugin, name=name) - - 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._name2plugin: - return - try: - mod = importplugin(modname) - except KeyboardInterrupt: - raise - except py.test.skip.Exception: - e = py.std.sys.exc_info()[1] - self._hints.append("skipped plugin %r: %s" %((modname, e.msg))) - else: - check_old_use(mod, modname) - self.register(mod, modname) - self.consider_module(mod) - - def pytest_terminal_summary(self, terminalreporter): - tw = terminalreporter._tw - if terminalreporter.config.option.traceconfig: - for hint in self._hints: - tw.line("hint: %s" % hint) - - # - # - # API for interacting with registered and instantiated plugin objects - # - # - def listattr(self, attrname, plugins=None): - return self.registry.listattr(attrname, plugins=plugins) - - def notify_exception(self, excinfo=None): - if excinfo is None: - excinfo = py.code.ExceptionInfo() - excrepr = excinfo.getrepr(funcargs=True, showlocals=True) - res = self.hook.pytest_internalerror(excrepr=excrepr) - if not py.builtin.any(res): - for line in str(excrepr).split("\n"): - sys.stderr.write("INTERNALERROR> %s\n" %line) - sys.stderr.flush() - - def do_addoption(self, parser): - mname = "pytest_addoption" - methods = self.registry.listattr(mname, reverse=True) - mc = MultiCall(methods, {'parser': parser}) - mc.execute() - - def _setns(self, obj, dic): - for name, value in dic.items(): - if isinstance(value, dict): - mod = getattr(obj, name, None) - if mod is None: - mod = py.std.types.ModuleType(name) - sys.modules['pytest.%s' % name] = mod - sys.modules['py.test.%s' % name] = mod - mod.__all__ = [] - setattr(obj, name, mod) - self._setns(mod, value) - else: - #print "setting", name, value, "on", obj - setattr(obj, name, value) - obj.__all__.append(name) - - def pytest_plugin_registered(self, plugin): - dic = self.call_plugin(plugin, "pytest_namespace", {}) or {} - if dic: - self._setns(py.test, dic) - 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 MultiCall( - methods=self.listattr(methname, plugins=[plugin]), - kwargs=kwargs, firstresult=True).execute() - - def do_configure(self, config): - assert not hasattr(self, '_config') - 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 canonical_importname(name): - name = name.lower() - modprefix = "pytest_" - if not name.startswith(modprefix): - name = modprefix + name - return name - -def importplugin(importspec): - #print "importing", importspec - try: - return __import__(importspec) - except ImportError: - e = py.std.sys.exc_info()[1] - if str(e).find(importspec) == -1: - raise - name = importspec - try: - if name.startswith("pytest_"): - name = importspec[7:] - return __import__("pytest.plugin.%s" %(name), None, None, '__doc__') - except ImportError: - e = py.std.sys.exc_info()[1] - if str(e).find(name) == -1: - raise - # show the original exception, not the failing internal one - return __import__(importspec) - - -class MultiCall: - """ execute a call into multiple python functions/methods. """ - - def __init__(self, methods, kwargs, firstresult=False): - self.methods = methods[:] - self.kwargs = kwargs.copy() - self.kwargs['__multicall__'] = self - self.results = [] - self.firstresult = firstresult - - def __repr__(self): - status = "%d results, %d meths" % (len(self.results), len(self.methods)) - return "" %(status, self.kwargs) - - def execute(self): - while self.methods: - method = self.methods.pop() - kwargs = self.getkwargs(method) - res = method(**kwargs) - if res is not None: - self.results.append(res) - if self.firstresult: - return res - if not self.firstresult: - return self.results - - def getkwargs(self, method): - kwargs = {} - for argname in varnames(method): - try: - kwargs[argname] = self.kwargs[argname] - except KeyError: - pass # might be optional param - return kwargs - -def varnames(func): - import inspect - if not inspect.isfunction(func) and not inspect.ismethod(func): - func = getattr(func, '__call__', func) - ismethod = inspect.ismethod(func) - rawcode = py.code.getrawcode(func) - try: - return rawcode.co_varnames[ismethod:] - except AttributeError: - return () - -class Registry: - """ - Manage Plugins: register/unregister call calls to plugins. - """ - def __init__(self, plugins=None): - if plugins is None: - plugins = [] - self._plugins = plugins - - def register(self, plugin, prepend=False): - assert not isinstance(plugin, str) - assert not plugin in self._plugins - if not prepend: - self._plugins.append(plugin) - else: - self._plugins.insert(0, plugin) - - def unregister(self, plugin): - self._plugins.remove(plugin) - - def isregistered(self, plugin): - return plugin in self._plugins - - def __iter__(self): - return iter(self._plugins) - - def listattr(self, attrname, plugins=None, reverse=False): - l = [] - if plugins is None: - plugins = self._plugins - for plugin in plugins: - try: - l.append(getattr(plugin, attrname)) - except AttributeError: - continue - if reverse: - l.reverse() - return l - -class HookRelay: - def __init__(self, hookspecs, registry, prefix="pytest_"): - if not isinstance(hookspecs, list): - hookspecs = [hookspecs] - self._hookspecs = [] - self._registry = registry - for hookspec in hookspecs: - self._addhooks(hookspec, prefix) - - def _addhooks(self, hookspecs, prefix): - self._hookspecs.append(hookspecs) - added = False - for name, method in vars(hookspecs).items(): - if name.startswith(prefix): - if not method.__doc__: - raise ValueError("docstring required for hook %r, in %r" - % (method, hookspecs)) - firstresult = getattr(method, 'firstresult', False) - hc = HookCaller(self, name, firstresult=firstresult) - setattr(self, name, hc) - added = True - #print ("setting new hook", name) - if not added: - raise ValueError("did not find new %r hooks in %r" %( - prefix, hookspecs,)) - - - def _performcall(self, name, multicall): - return multicall.execute() - -class HookCaller: - def __init__(self, hookrelay, name, firstresult): - self.hookrelay = hookrelay - self.name = name - self.firstresult = firstresult - - def __repr__(self): - return "" %(self.name,) - - def __call__(self, **kwargs): - methods = self.hookrelay._registry.listattr(self.name) - mc = MultiCall(methods, kwargs, firstresult=self.firstresult) - return self.hookrelay._performcall(self.name, mc) - - def pcall(self, plugins, **kwargs): - methods = self.hookrelay._registry.listattr(self.name, plugins=plugins) - mc = MultiCall(methods, kwargs, firstresult=self.firstresult) - return self.hookrelay._performcall(self.name, mc) - diff --git a/testing/plugin/conftest.py b/testing/plugin/conftest.py index 5c12d829c..d2da55eea 100644 --- a/testing/plugin/conftest.py +++ b/testing/plugin/conftest.py @@ -3,7 +3,7 @@ import py pytest_plugins = "pytester" import pytest.plugin plugindir = py.path.local(pytest.plugin.__file__).dirpath() -from pytest.pluginmanager import default_plugins +from pytest._core import default_plugins def pytest_collect_file(path, parent): if path.basename.startswith("pytest_") and path.ext == ".py": diff --git a/testing/plugin/test__pytest.py b/testing/plugin/test__pytest.py index 19d2bdc30..ecb30062e 100644 --- a/testing/plugin/test__pytest.py +++ b/testing/plugin/test__pytest.py @@ -1,7 +1,7 @@ import py import os, sys from pytest.plugin._pytest import HookRecorder -from pytest.pluginmanager import Registry +from pytest._core import Registry def test_hookrecorder_basic(): rec = HookRecorder(Registry()) @@ -29,7 +29,7 @@ def test_hookrecorder_basic_no_args_hook(): def test_functional(testdir, linecomp): reprec = testdir.inline_runsource(""" import py - from pytest.pluginmanager import HookRelay, Registry + from pytest._core import HookRelay, Registry pytest_plugins="_pytest" def test_func(_pytest): class ApiClass: diff --git a/testing/plugin/test_resultlog.py b/testing/plugin/test_resultlog.py index a09ca0234..c1e198a6d 100644 --- a/testing/plugin/test_resultlog.py +++ b/testing/plugin/test_resultlog.py @@ -2,7 +2,7 @@ import py import os from pytest.plugin.resultlog import generic_path, ResultLog, \ pytest_configure, pytest_unconfigure -from pytest.collect import Node, Item, FSCollector +from pytest.plugin.session import Node, Item, FSCollector def test_generic_path(testdir): from pytest.plugin.session import Collection diff --git a/testing/test_conftest.py b/testing/test_conftest.py index c509f6035..93e4976ed 100644 --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -1,5 +1,5 @@ import py -from pytest._config import Conftest +from pytest._core import Conftest def pytest_generate_tests(metafunc): if "basedir" in metafunc.funcargnames: diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index a74a8c061..a7794e3b7 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -1,5 +1,5 @@ import py -from pytest import _config as parseopt +from pytest import main as parseopt class TestParser: def test_init(self, capsys): @@ -42,7 +42,7 @@ class TestParser: group = parseopt.OptionGroup("hello") group.addoption("--option1", action="store_true") assert len(group.options) == 1 - assert isinstance(group.options[0], parseopt.optparse.Option) + assert isinstance(group.options[0], py.std.optparse.Option) def test_group_shortopt_lowercase(self): parser = parseopt.Parser() diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py index 972e29f54..28d2fe1aa 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -1,6 +1,6 @@ import py, os -from pytest.pluginmanager import PluginManager, canonical_importname -from pytest.pluginmanager import Registry, MultiCall, HookRelay, varnames +from pytest._core import PluginManager, canonical_importname +from pytest._core import Registry, MultiCall, HookRelay, varnames class TestBootstrapping: @@ -232,7 +232,6 @@ class TestBootstrapping: class TestPytestPluginInteractions: def test_addhooks_conftestplugin(self, testdir): - from pytest._config import Config newhooks = testdir.makepyfile(newhooks=""" def pytest_myhook(xyz): "new hook" @@ -245,7 +244,7 @@ class TestPytestPluginInteractions: def pytest_myhook(xyz): return xyz + 1 """) - config = Config() + config = testdir.Config() config._conftest.importconftest(conf) print(config.pluginmanager.getplugins()) res = config.hook.pytest_myhook(xyz=10) @@ -283,12 +282,11 @@ class TestPytestPluginInteractions: ]) def test_do_option_conftestplugin(self, testdir): - from pytest._config import Config p = testdir.makepyfile(""" def pytest_addoption(parser): parser.addoption('--test123', action="store_true") """) - config = Config() + config = testdir.Config() config._conftest.importconftest(p) print(config.pluginmanager.getplugins()) config.parse([]) @@ -321,8 +319,7 @@ class TestPytestPluginInteractions: ]) def test_do_option_postinitialize(self, testdir): - from pytest._config import Config - config = Config() + config = testdir.Config() config.parse([]) config.pluginmanager.do_configure(config=config) assert not hasattr(config.option, 'test123')