--- a/pyscovery.py Tue Jun 24 11:21:23 2014 -0500
+++ b/pyscovery.py Tue Jun 24 11:21:48 2014 -0500
@@ -1,5 +1,5 @@
# pyscovery - A python plugin finder
-# Copyright (C) 2013 Gary Kramlich <grim@reaperworld.com>
+# Copyright (C) 2013-2014 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
@@ -25,96 +25,28 @@
to logically group similar plugins in the filesystem.
-from threading import Lock
""" A pyscovery loader """
- def _real_add_module(self, module, modules=None):
- The method that does the real addition of of a module to the search
- if not module in modules:
- def add_module(self, module):
- Adds module to the search path
- self._real_add_module(module)
- def _real_remove_module(self, module, modules=None):
- The method that does the real removal of a module from the search path.
- self.modules.remove(module)
- def remove_module(self, module):
- Removes module from the search paths
- self._real_remove_module(module)
- Gets the search paths for this Loader.
- def clear_modules(self):
- Clears the module search path.
- def find(self, cls, recurse, create, *args, **kwargs):
+ def find(self, cls, path, create, *args, **kwargs): """Find all plugins that are subclasses of cls.
:param cls: The class whose subclasses to find.
- :param recurse: Whether or not to recurse into packages.
+ :param path: The path to look for packages in. :param create: return instances rather than just return their class.
:param args: Additional arguments to pass to each constructor. Ignored if
@@ -131,14 +63,8 @@
- modules.extend(self.modules)
- mod = importlib.import_module(module)
- if recurse and _is_package(mod):
- self._recurse(mod, modules)
+ for _, name, _ in pkgutil.walk_packages(path): + mod = importlib.import_module(name) for symbol_name in dir(mod):
if symbol_name == cls_name:
@@ -160,81 +86,10 @@
- def _is_package(cls, module):
- Returns true if the module's name is __init__, false otherwise
- filename = os.path.basename(module.__file__)
- base, _ = os.path.splitext(filename)
- return base == '__init__'
- def _recurse(self, module, modules):
- Adds additional modules from a package
- dirname = os.path.dirname(module.__file__)
- make_module = lambda name: '.'.join((module.__name__, name))
- for name in os.listdir(dirname):
- base, _ = os.path.splitext(name)
- filename = os.path.join(dirname, name)
- if os.path.isdir(filename):
- self._real_add_module(make_module(base), modules)
- elif os.path.isfile(filename):
- if fnmatch.fnmatch(name, '*.py') or \
- fnmatch.fnmatch(name, '*.py[co]'):
- self._real_add_module(make_module(base), modules)
_DEFAULT_LOADER = Loader()
- """Adds a module to search for plugins under
- :param module: The module to add to the search paths
- _DEFAULT_LOADER.add_module(module)
-def remove_module(module):
- """Removes a module to search for plugins under
- :param module: The module to remove
- _DEFAULT_LOADER.remove_module(module)
- """Returns the list of all module to search under
- :returns: A list of the current modules used for searching
- return _DEFAULT_LOADER.get_modules()
- """Clears all search modules """
- _DEFAULT_LOADER.clear_modules()
def find(cls, recurse, create, *args, **kwargs):
"""Find all plugins that are subclasses of cls in the current search paths
@@ -255,42 +110,3 @@
return _DEFAULT_LOADER.find(cls, recurse, create, *args, **kwargs)
-def _is_package(module):
- Returns true if the module's name is __init__, false otherwise
- filename = os.path.basename(module.__file__)
- base, _ = os.path.splitext(filename)
- return base == '__init__'
- Adds additional modules from a package
- dirname = os.path.dirname(module.__file__)
- make_module = lambda name: '.'.join((module.__name__, name))
- for name in os.listdir(dirname):
- base, _ = os.path.splitext(name)
- filename = os.path.join(dirname, name)
- if os.path.isdir(filename):
- add_module(make_module(base))
- elif os.path.isfile(filename):
- if fnmatch.fnmatch(name, '*.py') or \
- fnmatch.fnmatch(name, '*.py[co]'):
- add_module(make_module(base))
-#__all__ = [add_module, remove_module, get_modules, clear_modules, find]
--- a/tests/pyscovery_test.py Tue Jun 24 11:21:23 2014 -0500
+++ b/tests/pyscovery_test.py Tue Jun 24 11:21:48 2014 -0500
@@ -34,14 +34,12 @@
-def _test_module(test, module, count, recurse=False, instantiate=False):
+def _test_module(test, module, count, path, instantiate=False): Given a module, test that we find count plugins
- pyscovery.add_module(module)
- gen = pyscovery.find(Plugin, recurse, instantiate)
+ gen = pyscovery.find(Plugin, path, instantiate) test.assertTrue(inspect.isgenerator(gen))
@@ -54,79 +52,6 @@
-def _test_module_count(test, count):
- Asserts if the number of paths doesn't match count
- modules = pyscovery.get_modules()
- test.assertEqual(len(modules), count, 'search paths {}'.format(modules))
-class TestModules(unittest.TestCase):
- Unit tests for path manipulation
- pyscovery.clear_modules()
- _test_module_count(self, 0)
- pyscovery.clear_modules()
- def test_add_remove(self):
- Add and remove a single plugin path
- pyscovery.add_module(TestModules.module)
- _test_module_count(self, 1)
- pyscovery.remove_module(TestModules.module)
- def test_add_existing(self):
- Add the same plugin path twice, and make sure it's only in there once
- pyscovery.add_module(TestModules.module)
- pyscovery.add_module(TestModules.module)
- _test_module_count(self, 1)
- pyscovery.remove_module(TestModules.module)
- def test_remove_nonexistant(self):
- Try to remove a non-existant plugin path
- pyscovery.remove_module(TestModules.module)
- def test_add_multiple(self):
- Add multiple paths to the search paths
- second = '{}.1'.format(TestModules.module)
- pyscovery.add_module(TestModules.module)
- _test_module_count(self, 1)
- pyscovery.add_module(second)
- _test_module_count(self, 2)
- pyscovery.remove_module(TestModules.module)
- pyscovery.remove_module(second)
class TestFind(unittest.TestCase):
Basic tests for the find function
@@ -188,14 +113,6 @@
""" Tests the discovery method of pyscovery """
- pyscovery.clear_modules()
- pyscovery.clear_modules()
""" Test a module with a single plugin """
_test_module(self, 'tests.modules.single', 1)
@@ -215,14 +132,6 @@
""" Tests the find method of pyscovery on a package """
- pyscovery.clear_modules()
- pyscovery.clear_modules()
def test_package_without_recurse(self):
""" Test a package without recursion """
_test_module(self, 'tests.modules', 0)
@@ -241,10 +150,6 @@
class TestCreate(unittest.TestCase):
""" Tests the find method with the create option on """
- 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)
@@ -258,16 +163,12 @@
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)
+ _test_module(self, 'tests.modules', 7, path='tests', instantiate=True) class TestCreateWithParameters(unittest.TestCase):
""" Tests for finding and creating plugins that take arguments """
- pyscovery.clear_modules()
- pyscovery.add_module('tests.modules.create')
def test_create_args(self):
""" Tests that create works with args """