Coverage for nova/loadables.py: 97%
50 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-24 11:16 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-24 11:16 +0000
1# Copyright (c) 2011-2012 OpenStack Foundation
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
16"""
17Generic Loadable class support.
19Meant to be used by such things as scheduler filters and weights where we
20want to load modules from certain directories and find certain types of
21classes within those modules. Note that this is quite different than
22generic plugins and the pluginmanager code that exists elsewhere.
24Usage:
26Create a directory with an __init__.py with code such as:
28class SomeLoadableClass(object):
29 pass
32class MyLoader(nova.loadables.BaseLoader)
33 def __init__(self):
34 super(MyLoader, self).__init__(SomeLoadableClass)
36If you create modules in the same directory and subclass SomeLoadableClass
37within them, MyLoader().get_all_classes() will return a list
38of such classes.
39"""
41import inspect
42import os
43import sys
45from oslo_utils import importutils
47from nova import exception
50class BaseLoader(object):
51 def __init__(self, loadable_cls_type):
52 mod = sys.modules[self.__class__.__module__]
53 self.path = os.path.abspath(mod.__path__[0])
54 self.package = mod.__package__
55 self.loadable_cls_type = loadable_cls_type
57 def _is_correct_class(self, obj):
58 """Return whether an object is a class of the correct type and
59 is not prefixed with an underscore.
60 """
61 return (inspect.isclass(obj) and
62 (not obj.__name__.startswith('_')) and
63 issubclass(obj, self.loadable_cls_type))
65 def _get_classes_from_module(self, module_name):
66 """Get the classes from a module that match the type we want."""
67 classes = []
68 module = importutils.import_module(module_name)
69 for obj_name in dir(module):
70 # Skip objects that are meant to be private.
71 if obj_name.startswith('_'):
72 continue
73 itm = getattr(module, obj_name)
74 if self._is_correct_class(itm):
75 classes.append(itm)
76 return classes
78 def get_all_classes(self):
79 """Get the classes of the type we want from all modules found
80 in the directory that defines this class.
81 """
82 classes = []
83 for dirpath, _, filenames in os.walk(self.path):
84 relpath = os.path.relpath(dirpath, self.path)
85 if relpath == '.': 85 ↛ 88line 85 didn't jump to line 88 because the condition on line 85 was always true
86 relpkg = ''
87 else:
88 relpkg = '.%s' % '.'.join(relpath.split(os.sep))
89 for fname in filenames:
90 root, ext = os.path.splitext(fname)
91 if ext != '.py' or root == '__init__':
92 continue
93 module_name = "%s%s.%s" % (self.package, relpkg, root)
94 mod_classes = self._get_classes_from_module(module_name)
95 classes.extend(mod_classes)
96 return classes
98 def get_matching_classes(self, loadable_class_names):
99 """Get loadable classes from a list of names. Each name can be
100 a full module path or the full path to a method that returns
101 classes to use. The latter behavior is useful to specify a method
102 that returns a list of classes to use in a default case.
103 """
104 classes = []
105 for cls_name in loadable_class_names:
106 obj = importutils.import_class(cls_name)
107 if self._is_correct_class(obj):
108 classes.append(obj)
109 elif inspect.isfunction(obj):
110 # Get list of classes from a function
111 for cls in obj():
112 classes.append(cls)
113 else:
114 error_str = 'Not a class of the correct type'
115 raise exception.ClassNotFound(class_name=cls_name,
116 exception=error_str)
117 return classes