initial version, only finds plugins in paths
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.project Sat Mar 23 01:31:37 2013 -0500
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?> + <name>org.python.pydev.PyDevBuilder</name> + <nature>org.python.pydev.pythonNature</nature> --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.pydevproject Sat Mar 23 01:31:37 2013 -0500
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<?eclipse-pydev version="1.0"?> +<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH"> +<path>/pyplugin/src</path> +<path>/pyplugin/tests</path> +<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property> +<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property> --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyplugin.py Sat Mar 23 01:31:37 2013 -0500
@@ -0,0 +1,64 @@
+ Adds a path to search for plugins under + global PATHS # pylint:disable-msg=W0602 + Removes a plugin search path + global PATHS # pylint:disable-msg=W0602 + Returns the list of all plugin paths + Find all plugins that are subclasses of cls in the current search paths + if not inspect.isclass(cls): + raise TypeError('{} is not a class instance') + cls_name = cls.__name__ + mod = importlib.import_module(path) + for symbol_name in dir(mod): + if symbol_name == cls_name: + symbol = getattr(mod, symbol_name, None) + if not inspect.isclass(symbol): + if inspect.isabstract(symbol): +__all__ = [add_path, remove_path, get_paths, find] --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/modules/__init__.py Sat Mar 23 01:31:37 2013 -0500
@@ -0,0 +1,3 @@
+This package contains modules with plugins --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/modules/mixed.py Sat Mar 23 01:31:37 2013 -0500
@@ -0,0 +1,46 @@
+A module with plugins and other stuff +from tests import Plugin +class Simple(Plugin): # pylint:disable-msg=R0903 +class Abstract(Plugin): # pylint:disable-msg=R0903 + A plugin with an abstract method + __metaclass__ = abc.ABCMeta + def abstract(self): # pylint:disable-msg=R0201 + """ an abstract method """ +class Concrete(Abstract): # pylint:disable-msg=R0903 + A concrete implementation of Abstract + def abstract(self): # pylint:disable-msg=R0201 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/modules/multiple.py Sat Mar 23 01:31:37 2013 -0500
@@ -0,0 +1,21 @@
+A module with multiple plugins +from tests import Plugin +class First(Plugin): # pylint:disable-msg=R0903 +class Second(Plugin): # pylint:disable-msg=R0903 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/modules/single.py Sat Mar 23 01:31:37 2013 -0500
@@ -0,0 +1,12 @@
+from tests import Plugin +class Single(Plugin): # pylint:disable-msg=R0903 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/tests.py Sat Mar 23 01:31:37 2013 -0500
@@ -0,0 +1,195 @@
+class Plugin(object): # pylint:disable-msg=R0903 + The class all the test plugins descend from +class TestPaths(unittest.TestCase): # pylint:disable-msg=R0904 + Unit tests for path manipulation + def _test_path_count(self, count): + Modular form to check how many paths are in the search paths + paths = pyplugin.get_paths() + self.assertEqual(len(paths), count, + 'search paths {}'.format(paths)) + def setUp(self): # pylint:disable-msg=C0103 + paths = pyplugin.get_paths() + pyplugin.remove_path(path) + def tearDown(self): # pylint:disable-msg=C0103 + self._test_path_count(0) + def test_add_remove(self): + Add and remove a single plugin path + pyplugin.add_path(PATH) + self._test_path_count(1) + pyplugin.remove_path(PATH) + def test_add_existing(self): + Add the same plugin path twice, and make sure it's only in there once + pyplugin.add_path(PATH) + pyplugin.add_path(PATH) + self._test_path_count(1) + pyplugin.remove_path(PATH) + def test_remove_nonexistant(self): # pylint:disable-msg=R0201 + Try to remove a non-existant plugin path + pyplugin.remove_path(PATH) + def test_add_multiple(self): + Add multiple paths to the search paths + second = '{}.1'.format(PATH) + pyplugin.add_path(PATH) + self._test_path_count(1) + pyplugin.add_path(second) + self._test_path_count(2) + pyplugin.remove_path(PATH) + pyplugin.remove_path(second) +class TestFind(unittest.TestCase): # pylint:disable-msg=R0904 + Basic tests for the find function + Test that a TypeError is raised when find is called with None + self.assertRaises(TypeError, pyplugin.find(None)) + Test that a TypeError is raised when find is called with a string + self.assertRaises(TypeError, pyplugin.find('')) + Test that a TypeError is raised when find is called with an int + self.assertRaises(TypeError, pyplugin.find(0)) + def test_old_style_class(self): # pylint:disable-msg=R0201 + Test that a TypeError is NOT raised when find is called with an old + class Test: # pylint:disable-msg=R0903,W0232 + """ old style class """ + def test_new_style_class(self): # pylint:disable-msg=R0201 + Test that a TypeError is NOT raised when find is called with an new + class Test(object): # pylint:disable-msg=R0903 + """ new style class """ +class TestModule(unittest.TestCase): # pylint:disable-msg=R0904 + Tests the discovery method of pyplugin + def _test_path(self, path, count): + Given a module path, test that we find count plugins + self.assertEqual(len(pyplugin.get_paths()), 0) + pyplugin.add_path(path) + gen = pyplugin.find(Plugin) + self.assertTrue(inspect.isgenerator(gen)) + self.assertEqual(len(list(gen)), count) + def setUp(self): # pylint:disable-msg=C0103 + paths = pyplugin.get_paths() + pyplugin.remove_path(path) + Test a module with a single plugin + self._test_path('modules.single', 1) + def test_multiple(self): + Test a module with multiple plugins + self._test_path('modules.multiple', 2) + Test a module with more than just plugins + self._test_path('modules.mixed', 2)