summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrendan McCollam <bmccollam@leapfrogonline.com>2011-11-11 14:35:58 -0600
committerBrendan McCollam <bmccollam@leapfrogonline.com>2011-11-11 14:35:58 -0600
commit0300f58e1ccc12fde9e4046dcbd469dde5e7c8f9 (patch)
tree72af6fd3eb57706818ff51a81b0ccfc9945cb19d
parenta8f778f6345237dca9de77037b2c46ab8cd5943c (diff)
downloadnose-0300f58e1ccc12fde9e4046dcbd469dde5e7c8f9.tar.gz
Refactoring to include functionality preserving 'addplugins' on base PluginManager
-rw-r--r--functional_tests/test_defaultpluginmanager.py4
-rw-r--r--nose/core.py2
-rw-r--r--nose/plugins/manager.py44
-rw-r--r--nose/plugins/plugintest.py78
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)