grim/pyscovery

Parents 020a65b7e258
Children 367b9e9dadad
started moving to pkgutil but the tests are horribly broken right now and I don't have time to look at it
--- 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.
"""
-import fnmatch
import importlib
import inspect
-import os
+import pkgutil
-from threading import Lock
-__version__ = '1.2'
+__version__ = '1.3'
class Loader(object):
""" A pyscovery loader """
def __init__(self):
- self.modules = []
- self.discovered = None
- self.lock = Lock()
-
-
- def _real_add_module(self, module, modules=None):
- """
- The method that does the real addition of of a module to the search
- path.
- """
-
- if modules is None:
- modules = self.modules
-
- if not module in modules:
- modules.append(module)
-
-
- def add_module(self, module):
- """
- Adds module to the search path
- """
-
- self.lock.acquire()
- self._real_add_module(module)
- self.lock.release()
+ pass
- def _real_remove_module(self, module, modules=None):
- """
- The method that does the real removal of a module from the search path.
- """
-
- if modules is None:
- modules = self.modules
-
- try:
- self.modules.remove(module)
- except ValueError:
- pass
-
-
- def remove_module(self, module):
- """
- Removes module from the search paths
- """
-
- self.lock.acquire()
- self._real_remove_module(module)
- self.lock.release()
-
-
- def get_modules(self):
- """
- Gets the search paths for this Loader.
- """
- return self.modules
-
-
- def clear_modules(self):
- """
- Clears the module search path.
- """
-
- self.lock.acquire()
- self.modules[:] = []
- self.lock.release()
-
-
- 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.
:type cls: class
- :param recurse: Whether or not to recurse into packages.
- :type recurse: bool
+ :param path: The path to look for packages in.
+ :type path: string
:param create: return instances rather than just return their class.
:type create: bool
:param args: Additional arguments to pass to each constructor. Ignored if
@@ -131,14 +63,8 @@
cls_name = cls.__name__
- modules = []
- modules.extend(self.modules)
-
- for module in 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 @@
else:
yield symbol
- @classmethod
- 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)
-
- if base == '__init__':
- continue
-
- 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()
-def add_module(module):
- """Adds a module to search for plugins under
-
- :param module: The module to add to the search paths
- :type module: string
- """
-
- _DEFAULT_LOADER.add_module(module)
-
-
-def remove_module(module):
- """Removes a module to search for plugins under
-
- :param module: The module to remove
- :type module: string
- """
-
- _DEFAULT_LOADER.remove_module(module)
-
-
-def get_modules():
- """Returns the list of all module to search under
-
- :returns: A list of the current modules used for searching
- """
-
- return _DEFAULT_LOADER.get_modules()
-
-
-def clear_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__'
-
-
-def _recurse(module):
- """
- 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)
-
- if base == '__init__':
- continue
-
- 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 @@
pass
-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))
found = list(gen)
@@ -54,79 +52,6 @@
return found
-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
- """
-
- module = 'test'
-
-
- def setUp(self):
- pyscovery.clear_modules()
-
-
- def tearDown(self):
- _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 """
- def setUp(self):
- pyscovery.clear_modules()
-
-
- def tearDown(self):
- pyscovery.clear_modules()
-
-
def test_single(self):
""" 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 """
- def setUp(self):
- pyscovery.clear_modules()
-
-
- def tearDown(self):
- 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 """
- def setUp(self):
- 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 """
- def setUp(self):
- pyscovery.clear_modules()
- pyscovery.add_module('tests.modules.create')
-
def test_create_args(self):
""" Tests that create works with args """