Add a module for dynamically loading plugins
Change-Id: I662b5989941b467c78a392098db0cd19ff86201c Signed-off-by: Zane Bitter <zbitter@redhat.com>
This commit is contained in:
parent
a0cf5dfa5c
commit
faca9953c0
|
@ -0,0 +1,64 @@
|
||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import pkgutil
|
||||||
|
import sys
|
||||||
|
import types
|
||||||
|
|
||||||
|
from heat.openstack.common import log as logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _module_name(*components):
|
||||||
|
return '.'.join(components)
|
||||||
|
|
||||||
|
|
||||||
|
def create_subpackage(path, parent_package_name, subpackage_name="plugins"):
|
||||||
|
package_name = _module_name(parent_package_name, subpackage_name)
|
||||||
|
|
||||||
|
package = types.ModuleType(package_name)
|
||||||
|
package.__path__ = [path] if isinstance(path, basestring) else list(path)
|
||||||
|
sys.modules[package_name] = package
|
||||||
|
|
||||||
|
return package
|
||||||
|
|
||||||
|
|
||||||
|
def _import_module(importer, module_name, package):
|
||||||
|
fullname = _module_name(package.__name__, module_name)
|
||||||
|
if fullname in sys.modules:
|
||||||
|
return sys.modules[fullname]
|
||||||
|
|
||||||
|
loader = importer.find_module(fullname)
|
||||||
|
if loader is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
module = loader.load_module(fullname)
|
||||||
|
setattr(package, module_name, module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def load_modules(package, ignore_error=False):
|
||||||
|
path = package.__path__
|
||||||
|
for importer, module_name, is_package in pkgutil.walk_packages(path):
|
||||||
|
try:
|
||||||
|
module = _import_module(importer, module_name, package)
|
||||||
|
except ImportError as ex:
|
||||||
|
logger.error(_('Failed to import module %s') % module_name)
|
||||||
|
if not ignore_error:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
if module is not None:
|
||||||
|
yield module
|
|
@ -0,0 +1,70 @@
|
||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
|
import nose
|
||||||
|
import pkgutil
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
from nose.plugins.attrib import attr
|
||||||
|
|
||||||
|
import heat.engine
|
||||||
|
from heat.common import plugin_loader
|
||||||
|
|
||||||
|
|
||||||
|
@attr(tag=['unit', 'plugin_loader'])
|
||||||
|
@attr(speed='fast')
|
||||||
|
class PluginLoaderTest(unittest.TestCase):
|
||||||
|
def test_module_name(self):
|
||||||
|
self.assertEqual(plugin_loader._module_name('foo.bar', 'blarg.wibble'),
|
||||||
|
'foo.bar.blarg.wibble')
|
||||||
|
|
||||||
|
def test_create_subpackage_single_path(self):
|
||||||
|
pkg_name = 'heat.engine.test_single_path'
|
||||||
|
self.assertFalse(pkg_name in sys.modules)
|
||||||
|
pkg = plugin_loader.create_subpackage('/tmp',
|
||||||
|
'heat.engine',
|
||||||
|
'test_single_path')
|
||||||
|
self.assertTrue(pkg_name in sys.modules)
|
||||||
|
self.assertEqual(sys.modules[pkg_name], pkg)
|
||||||
|
self.assertEqual(pkg.__path__, ['/tmp'])
|
||||||
|
self.assertEqual(pkg.__name__, pkg_name)
|
||||||
|
|
||||||
|
def test_create_subpackage_path_list(self):
|
||||||
|
path_list = ['/tmp']
|
||||||
|
pkg_name = 'heat.engine.test_path_list'
|
||||||
|
self.assertFalse(pkg_name in sys.modules)
|
||||||
|
pkg = plugin_loader.create_subpackage('/tmp',
|
||||||
|
'heat.engine',
|
||||||
|
'test_path_list')
|
||||||
|
self.assertTrue(pkg_name in sys.modules)
|
||||||
|
self.assertEqual(sys.modules[pkg_name], pkg)
|
||||||
|
self.assertEqual(pkg.__path__, path_list)
|
||||||
|
self.assertFalse(pkg.__path__ is path_list)
|
||||||
|
self.assertEqual(pkg.__name__, pkg_name)
|
||||||
|
|
||||||
|
def test_import_module_existing(self):
|
||||||
|
import heat.engine.service
|
||||||
|
importer = pkgutil.ImpImporter(heat.engine.__path__[0])
|
||||||
|
loaded = plugin_loader._import_module(importer,
|
||||||
|
'service',
|
||||||
|
heat.engine)
|
||||||
|
self.assertTrue(loaded is heat.engine.service)
|
||||||
|
|
||||||
|
def test_import_module_garbage(self):
|
||||||
|
importer = pkgutil.ImpImporter(heat.engine.__path__[0])
|
||||||
|
self.assertEqual(plugin_loader._import_module(importer,
|
||||||
|
'wibble',
|
||||||
|
heat.engine),
|
||||||
|
None)
|
Loading…
Reference in New Issue