--- a/pyscovery.py Sun Apr 14 01:41:01 2013 -0500
+++ b/pyscovery.py Sun Apr 14 02:12:14 2013 -0500
@@ -102,9 +102,16 @@
-def find(cls, recurse=False):
+def find(cls, recurse, create, *args, **kwargs): Find all plugins that are subclasses of cls in the current search paths
+ If recurse is set to true, all files under a package will be inspected as + If create is set to true, instances of the classes will be returned instead + of the class itself. Note that *args and **kwargs will passed directly to + the classes __init__ method. if not inspect.isclass(cls):
@@ -129,8 +136,14 @@
if inspect.isabstract(symbol):
+ if not issubclass(symbol, cls):
+ yield symbol(*args, **kwargs) --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/modules/create.py Sun Apr 14 02:12:14 2013 -0500
@@ -0,0 +1,69 @@
+# pyscovery - A python plugin finder +# Copyright (C) 2013 Gary Kramlich <grim@reaperworld.com> +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +Unit tests for pyscovery +class CreatePlugin(object): # pylint:disable-msg=R0903 + """ A base class for plugins that take variables to their constructors """ + def __init__(self, *args, **kwargs): +class ArgsPlugin(CreatePlugin): # pylint:disable-msg=R0903 + """ A plugin that stores *args to it's constructor """ + def __init__(self, *args): + CreatePlugin.__init__(self, *args) +class ArgsSubClass(ArgsPlugin): # pylint:disable-msg=R0903 + """ A subclass of ArgsPlugin so we can find it """ + def __init__(self, *args): + ArgsPlugin.__init__(self, *args) +class KwArgsPlugin(CreatePlugin): # pylint:disable-msg=R0903 + """ A plugin that stores *kwargs to it's constructor """ + def __init__(self, **kwargs): + CreatePlugin.__init__(self, **kwargs) +class KwArgsSubClass(KwArgsPlugin): # pylint:disable-msg=R0903 + """ A subclass of KwArgsPlugin so we can find it """ + def __init__(self, **kwargs): + KwArgsPlugin.__init__(self, **kwargs) +class BothArgsPlugin(CreatePlugin): # pylint:disable-msg=R0903 + """ A plugin that stores *args and **kwargs to it's constructor """ + def __init__(self, *args, **kwargs): + CreatePlugin.__init__(self, *args, **kwargs) +class BothArgsSubClass(BothArgsPlugin): # pylint:disable-msg=R0903 + """ A subclass of BothArgsPlugins so we can find it """ + def __init__(self, *args, **kwargs): + BothArgsPlugin.__init__(self, *args, **kwargs) --- a/tests/modules/mixed.py Sun Apr 14 01:41:01 2013 -0500
+++ b/tests/modules/mixed.py Sun Apr 14 02:12:14 2013 -0500
@@ -60,3 +60,10 @@
def abstract(self): # pylint:disable-msg=R0201
+class Other(object): # pylint:disable-msg=R0903 + Another class to make sure we only match what we're looking for --- a/tests/pyscovery_test.py Sun Apr 14 01:41:01 2013 -0500
+++ b/tests/pyscovery_test.py Sun Apr 14 02:12:14 2013 -0500
@@ -23,20 +23,39 @@
+from tests.modules import create -def _test_module(test, module, count, recurse=False):
+# pylint:disable-msg=R0913 +class Plugin(object): # pylint:disable-msg=R0903 + The class all the test plugins descend from +def _test_module(test, module, count, recurse=False, instantiate=False): Given a module, test that we find count plugins
pyscovery.add_module(module)
- gen = pyscovery.find(Plugin, recurse=recurse)
+ gen = pyscovery.find(Plugin, recurse, instantiate) test.assertTrue(inspect.isgenerator(gen))
- test.assertEqual(len(found), count, 'plugins found: {}'.format(found))
+ test.assertEqual(len(found), count, 'plugins found: {}'.format(len(found))) + test.assertIsInstance(plugin, Plugin) def _test_module_count(test, count):
@@ -47,14 +66,6 @@
test.assertEqual(len(modules), count, 'search paths {}'.format(modules))
-class Plugin(object): # pylint:disable-msg=R0903
- The class all the test plugins descend from
class TestModules(unittest.TestCase): # pylint:disable-msg=R0904
Unit tests for path manipulation
@@ -129,7 +140,7 @@
Test that a TypeError is raised when find is called with None
- self.assertRaises(TypeError, pyscovery.find(None))
+ self.assertRaises(TypeError, pyscovery.find(None, False, False)) @@ -137,7 +148,7 @@
Test that a TypeError is raised when find is called with a string
- self.assertRaises(TypeError, pyscovery.find(''))
+ self.assertRaises(TypeError, pyscovery.find('', False, False)) @@ -145,7 +156,7 @@
Test that a TypeError is raised when find is called with an int
- self.assertRaises(TypeError, pyscovery.find(0))
+ self.assertRaises(TypeError, pyscovery.find(0, False, False)) def test_old_style_class(self): # pylint:disable-msg=R0201
@@ -159,7 +170,7 @@
+ pyscovery.find(Test, False, False) def test_new_style_class(self): # pylint:disable-msg=R0201
@@ -173,7 +184,7 @@
+ pyscovery.find(Test, False, False) class TestModule(unittest.TestCase): # pylint:disable-msg=R0904
@@ -229,3 +240,58 @@
""" Test a package with recursion """
_test_module(self, 'tests.modules', 7, recurse=True)
+class TestCreate(unittest.TestCase): # pylint:disable-msg=R0904 + """ Tests the find method with the create option on """ + def setUp(self): # pylint:disable-msg=C0103 + pyscovery.clear_modules() + def test_create_without_recurse(self): + """ Test a package with create but without recursion """ + _test_module(self, 'tests.modules.deep', 1, instantiate=True) + def test_create_with_recursion(self): + """ Test a package with create and recursion """ + _test_module(self, 'tests.modules.deep', 2, recurse=True, + def test_create_with_deep_recursion(self): + """ Test a packge with create and deep recursion """ + _test_module(self, 'tests.modules', 7, recurse=True, instantiate=True) +class TestCreateWithParameters(unittest.TestCase): # pylint:disable-msg=R0904 + """ Tests for finding and creating plugins that take arguments """ + def setUp(self): # pylint:disable-msg=C0103 + pyscovery.clear_modules() + pyscovery.add_module('tests.modules.create') + def test_create_args(self): + """ Tests that create works with args """ + for plugin in pyscovery.find(create.ArgsPlugin, False, True, + self.assertTupleEqual(plugin.args, ('one', 'two')) + def test_create_kwargs(self): + """ Tests that create works with kwargs """ + for plugin in pyscovery.find(create.KwArgsPlugin, False, True, + self.assertDictEqual(plugin.kwargs, {'one': 1, 'two': 2}) + def test_both_args(self): + """ Tests the create works with args and kwargs """ + for plugin in pyscovery.find(create.BothArgsPlugin, False, True, + 'one', 'two', one=1, two=2): + self.assertTupleEqual(plugin.args, ('one', 'two')) + self.assertDictEqual(plugin.kwargs, {'one': 1, 'two': 2})