diff options
author | Brendan McCollam <bmccollam@leapfrogonline.com> | 2011-11-11 14:35:58 -0600 |
---|---|---|
committer | Brendan McCollam <bmccollam@leapfrogonline.com> | 2011-11-11 14:35:58 -0600 |
commit | 0300f58e1ccc12fde9e4046dcbd469dde5e7c8f9 (patch) | |
tree | 72af6fd3eb57706818ff51a81b0ccfc9945cb19d | |
parent | a8f778f6345237dca9de77037b2c46ab8cd5943c (diff) | |
download | nose-0300f58e1ccc12fde9e4046dcbd469dde5e7c8f9.tar.gz |
Refactoring to include functionality preserving 'addplugins' on base PluginManager
-rw-r--r-- | functional_tests/test_defaultpluginmanager.py | 4 | ||||
-rw-r--r-- | nose/core.py | 2 | ||||
-rw-r--r-- | nose/plugins/manager.py | 44 | ||||
-rw-r--r-- | nose/plugins/plugintest.py | 78 |
4 files changed, 59 insertions, 69 deletions
diff --git a/functional_tests/test_defaultpluginmanager.py b/functional_tests/test_defaultpluginmanager.py index 0df2265..09aa992 100644 --- a/functional_tests/test_defaultpluginmanager.py +++ b/functional_tests/test_defaultpluginmanager.py @@ -1,13 +1,13 @@ import unittest from nose.plugins import Plugin -from nose.plugins.manager import DefaultPluginManager, ExtraPluginManager +from nose.plugins.manager import DefaultPluginManager class OverridesSkip(Plugin): """Plugin to override the built-in Skip""" enabled = True name = 'skip' is_overridden = True - + class TestDefaultPluginManager(unittest.TestCase): diff --git a/nose/core.py b/nose/core.py index 36d966d..b78f232 100644 --- a/nose/core.py +++ b/nose/core.py @@ -11,7 +11,7 @@ import unittest from nose.config import Config, all_config_files from nose.loader import defaultTestLoader from nose.plugins.manager import PluginManager, DefaultPluginManager, \ - RestrictedPluginManager, ExtraPluginManager + RestrictedPluginManager from nose.result import TextTestResult from nose.suite import FinalizingSuiteWrapper from nose.util import isclass, tolist diff --git a/nose/plugins/manager.py b/nose/plugins/manager.py index 5f1f1ea..4d2ed22 100644 --- a/nose/plugins/manager.py +++ b/nose/plugins/manager.py @@ -52,6 +52,7 @@ import inspect import logging import os import sys +from itertools import chain as iterchain from warnings import warn import nose.config from nose.failure import Failure @@ -222,8 +223,10 @@ class NoPlugins(object): class PluginManager(object): - """Base class for plugin managers. Does not implement loadPlugins, so it - may only be used with a static list of plugins. + """Base class for plugin managers. PluginManager is intended to be + used only with a static list of plugins. The loadPlugins() implementation + only reloads plugins from _extraplugins to prevent those from being + overridden by a subclass. The basic functionality of a plugin manager is to proxy all unknown attributes through a ``PluginProxy`` to a list of plugins. @@ -235,6 +238,7 @@ class PluginManager(object): def __init__(self, plugins=(), proxyClass=None): self._plugins = [] + self._extraplugins = () self._proxies = {} if plugins: self.addPlugins(plugins) @@ -260,8 +264,13 @@ class PluginManager(object): if getattr(p, 'name', None) != new_name] self._plugins.append(plug) - def addPlugins(self, plugins): - for plug in plugins: + def addPlugins(self, plugins=(), extraplugins=()): + """extraplugins are maintained in a separate list and + re-added by loadPlugins() to prevent their being overwritten + by plugins added by a subclass of PluginManager + """ + self._extraplugins = extraplugins + for plug in iterchain(plugins, extraplugins): self.addPlugin(plug) def configure(self, options, config): @@ -279,7 +288,8 @@ class PluginManager(object): log.debug("Plugins enabled: %s", enabled) def loadPlugins(self): - pass + for plug in self._extraplugins: + self.addPlugin(plug) def sort(self): return sort_list(self._plugins, lambda x: getattr(x, 'score', 1), reverse=True) @@ -404,33 +414,13 @@ class BuiltinPluginManager(PluginManager): self.addPlugin(plug()) super(BuiltinPluginManager, self).loadPlugins() -class ExtraPluginManager(PluginManager): - """Plugin manager that loads extra plugins specified - with the keyword `addplugins` - """ - def __init__(self, plugins=(), proxyClass=None): - super(ExtraPluginManager, self).__init__(plugins, proxyClass) - self._proxies['_extraplugins'] = () - - def addPlugins(self, plugins=(), extraplugins=()): - self._extraplugins = extraplugins - super(ExtraPluginManager, self).addPlugins(plugins) - - def loadPlugins(self): - for plug in self._extraplugins: - self.addPlugin(plug) - super(ExtraPluginManager, self).loadPlugins() - try: import pkg_resources - class DefaultPluginManager(BuiltinPluginManager, - EntryPointPluginManager, - ExtraPluginManager, - ): + class DefaultPluginManager(EntryPointPluginManager, BuiltinPluginManager): pass except ImportError: - class DefaultPluginManager(BuiltinPluginManager, ExtraPluginManager): + class DefaultPluginManager(BuiltinPluginManager): pass class RestrictedPluginManager(DefaultPluginManager): diff --git a/nose/plugins/plugintest.py b/nose/plugins/plugintest.py index 68e8941..9eaebb4 100644 --- a/nose/plugins/plugintest.py +++ b/nose/plugins/plugintest.py @@ -3,8 +3,8 @@ Testing Plugins =============== The plugin interface is well-tested enough to safely unit test your -use of its hooks with some level of confidence. However, there is also -a mixin for unittest.TestCase called PluginTester that's designed to +use of its hooks with some level of confidence. However, there is also +a mixin for unittest.TestCase called PluginTester that's designed to test plugins in their native runtime environment. Here's a simple example with a do-nothing plugin and a composed suite. @@ -20,7 +20,7 @@ Here's a simple example with a do-nothing plugin and a composed suite. ... for line in self.output: ... # i.e. check for patterns ... pass - ... + ... ... # or check for a line containing ... ... assert "ValueError" in self.output ... def makeSuite(self): @@ -43,7 +43,7 @@ Here's a simple example with a do-nothing plugin and a composed suite. And here is a more complex example of testing a plugin that has extra arguments and reads environment variables. - + >>> import unittest, os >>> from nose.plugins import Plugin, PluginTester >>> class FancyOutputter(Plugin): @@ -57,21 +57,21 @@ arguments and reads environment variables. ... self.fanciness = 2 ... if 'EVEN_FANCIER' in self.env: ... self.fanciness = 3 - ... + ... ... def options(self, parser, env=os.environ): ... self.env = env ... parser.add_option('--more-fancy', action='store_true') ... Plugin.options(self, parser, env=env) - ... + ... ... def report(self, stream): ... stream.write("FANCY " * self.fanciness) - ... + ... >>> class TestFancyOutputter(PluginTester, unittest.TestCase): ... activate = '--with-fancy' # enables the plugin ... plugins = [FancyOutputter()] ... args = ['--more-fancy'] ... env = {'EVEN_FANCIER': '1'} - ... + ... ... def test_fancy_output(self): ... assert "FANCY FANCY FANCY" in self.output, ( ... "got: %s" % self.output) @@ -80,7 +80,7 @@ arguments and reads environment variables. ... def runTest(self): ... raise ValueError("I hate fancy stuff") ... return unittest.TestSuite([TC()]) - ... + ... >>> res = unittest.TestResult() >>> case = TestFancyOutputter('test_fancy_output') >>> case(res) @@ -169,7 +169,7 @@ class MultiProcessFile(object): return self.__buffer.getvalue() def __getattr__(self, attr): return getattr(self.__buffer, attr) - + try: from multiprocessing import Manager Buffer = MultiProcessFile @@ -178,35 +178,35 @@ except ImportError: class PluginTester(object): """A mixin for testing nose plugins in their runtime environment. - - Subclass this and mix in unittest.TestCase to run integration/functional - tests on your plugin. When setUp() is called, the stub test suite is - executed with your plugin so that during an actual test you can inspect the + + Subclass this and mix in unittest.TestCase to run integration/functional + tests on your plugin. When setUp() is called, the stub test suite is + executed with your plugin so that during an actual test you can inspect the artifacts of how your plugin interacted with the stub test suite. - + - activate - + - the argument to send nosetests to activate the plugin - + - suitepath - + - if set, this is the path of the suite to test. Otherwise, you will need to use the hook, makeSuite() - + - plugins - the list of plugins to make available during the run. Note that this does not mean these plugins will be *enabled* during the run -- only the plugins enabled by the activate argument or other settings in argv or env will be enabled. - + - args - + - a list of arguments to add to the nosetests command, in addition to the activate argument - + - env - + - optional dict of environment variables to send nosetests """ @@ -217,34 +217,34 @@ class PluginTester(object): argv = None plugins = [] ignoreFiles = None - + def makeSuite(self): """returns a suite object of tests to run (unittest.TestSuite()) - - If self.suitepath is None, this must be implemented. The returned suite - object will be executed with all plugins activated. It may return + + If self.suitepath is None, this must be implemented. The returned suite + object will be executed with all plugins activated. It may return None. - + Here is an example of a basic suite object you can return :: - + >>> import unittest >>> class SomeTest(unittest.TestCase): ... def runTest(self): ... raise ValueError("Now do something, plugin!") - ... + ... >>> unittest.TestSuite([SomeTest()]) # doctest: +ELLIPSIS <unittest...TestSuite tests=[<...SomeTest testMethod=runTest>]> - + """ raise NotImplementedError - + def _execPlugin(self): """execute the plugin on the internal test suite. """ from nose.config import Config from nose.core import TestProgram from nose.plugins.manager import PluginManager - + suite = None stream = Buffer() conf = Config(env=self.env, @@ -254,20 +254,20 @@ class PluginTester(object): conf.ignoreFiles = self.ignoreFiles if not self.suitepath: suite = self.makeSuite() - + self.nose = TestProgram(argv=self.argv, config=conf, suite=suite, exit=False) self.output = AccessDecorator(stream) - + def setUp(self): - """runs nosetests with the specified test suite, all plugins + """runs nosetests with the specified test suite, all plugins activated. """ self.argv = ['nosetests', self.activate] if self.args: self.argv.extend(self.args) if self.suitepath: - self.argv.append(self.suitepath) + self.argv.append(self.suitepath) self._execPlugin() @@ -379,7 +379,7 @@ def run(*arg, **kw): if 'argv' not in kw: kw['argv'] = ['nosetests', '-v'] kw['config'].stream = buffer - + # Set up buffering so that all output goes to our buffer, # or warn user if deprecated behavior is active. If this is not # done, prints and warnings will either be out of place or @@ -406,7 +406,7 @@ def run(*arg, **kw): out = buffer.getvalue() print munge_nose_output_for_doctest(out) - + def run_buffered(*arg, **kw): kw['buffer_all'] = True run(*arg, **kw) |