From e6bf01a6b0baf438cdb30129433fda1763bb8909 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Sat, 20 Apr 2013 12:31:14 -0400 Subject: [PATCH] Make plugin loader path operations more efficient. --- bin/ansible-doc | 14 ++++++- lib/ansible/utils/plugins.py | 80 ++++++++++++++++++++++++------------ 2 files changed, 65 insertions(+), 29 deletions(-) diff --git a/bin/ansible-doc b/bin/ansible-doc index c1d3243de02..d3cd614d10f 100755 --- a/bin/ansible-doc +++ b/bin/ansible-doc @@ -170,13 +170,23 @@ def main(): if len(args) == 0: p.print_help() - + + def print_paths(finder): + ''' Returns a string suitable for printing of the search path ''' + + # Uses a list to get the order right + ret = [] + for i in finder._get_paths(): + if i not in ret: + ret.append(i) + return os.pathsep.join(ret) + for module in args: filename = utils.plugins.module_finder.find_plugin(module) if filename is None: sys.stderr.write("module %s not found in %s\n" % (module, - utils.plugins.module_finder.print_paths())) + print_paths(utils.plugins.module_finder))) continue if any(filename.endswith(x) for x in BLACKLIST_EXTS): diff --git a/lib/ansible/utils/plugins.py b/lib/ansible/utils/plugins.py index 33ed750b79e..e756e3bcd56 100644 --- a/lib/ansible/utils/plugins.py +++ b/lib/ansible/utils/plugins.py @@ -23,34 +23,47 @@ import ansible.constants as C from ansible import errors MODULE_CACHE = {} +PATH_CACHE = {} +PLUGIN_PATH_CACHE = {} _basedirs = [] def push_basedir(basedir): _basedirs.insert(0, basedir) class PluginLoader(object): - """PluginLoader loads plugins from the best source + + ''' + PluginLoader loads plugins from the configured plugin directories. It searches for plugins by iterating through the combined list of - play basedirs, configured paths, and the installed package directory. + play basedirs, configured paths, and the python path. The first match is used. - """ + ''' + def __init__(self, class_name, package, config, subdir, aliases={}): - """Create a new PluginLoader""" - self.class_name = class_name - self.package = package - self.config = config - self.subdir = subdir - self.aliases = aliases - + + self.class_name = class_name + self.package = package + self.config = config + self.subdir = subdir + self.aliases = aliases + if not class_name in MODULE_CACHE: MODULE_CACHE[class_name] = {} + if not class_name in PATH_CACHE: + PATH_CACHE[class_name] = None + if not class_name in PLUGIN_PATH_CACHE: + PLUGIN_PATH_CACHE[class_name] = {} + + self._module_cache = MODULE_CACHE[class_name] + self._paths = PATH_CACHE[class_name] + self._plugin_path_cache = PLUGIN_PATH_CACHE[class_name] - self._module_cache = MODULE_CACHE[class_name] self._extra_dirs = [] def _get_package_path(self): - """Gets the path of a Python package""" + ''' Gets the path of a Python package ''' + if not self.package: return [] if not hasattr(self, 'package_path'): @@ -60,9 +73,11 @@ class PluginLoader(object): return [self.package_path] def _get_paths(self): - """Return a list of paths to search for plugins in + ''' Return a list of paths to search for plugins in ''' + + if self._paths is not None: + return self._paths - The list is searched in order.""" ret = [] ret += self._extra_dirs for basedir in _basedirs: @@ -71,41 +86,51 @@ class PluginLoader(object): ret.append(fullpath) ret += self.config.split(os.pathsep) ret += self._get_package_path() + + self._paths = ret + return ret + def add_directory(self, directory, with_subdir=False): - """Adds an additional directory to the search path""" + ''' Adds an additional directory to the search path ''' + + self._paths = None + if directory is not None: if with_subdir: directory = os.path.join(directory, self.subdir) self._extra_dirs.append(directory) - def print_paths(self): - """Returns a string suitable for printing of the search path""" - # Uses a list to get the order right - ret = [] - for i in self._get_paths(): - if i not in ret: - ret.append(i) - return os.pathsep.join(ret) - def find_plugin(self, name): - """Find a plugin named name""" + ''' Find a plugin named name ''' + + if 'name' in self._plugin_path_cache: + return self._plugin_path_cache[name] + suffix = ".py" if not self.class_name: suffix = "" + paths = self._get_paths() + for i in self._get_paths(): path = os.path.join(i, "%s%s" % (name, suffix)) if os.path.exists(path): + self._plugin_path_cache[name] = path return path + return None def has_plugin(self, name): - """Checks if a plugin named name exists""" + ''' Checks if a plugin named name exists ''' + return self.find_plugin(name) is not None + __contains__ = has_plugin def get(self, name, *args, **kwargs): + ''' instantiates a plugin of the given name using arguments ''' + if name in self.aliases: name = self.aliases[name] path = self.find_plugin(name) @@ -116,7 +141,8 @@ class PluginLoader(object): return getattr(self._module_cache[path], self.class_name)(*args, **kwargs) def all(self, *args, **kwargs): - + ''' instantiates all plugins with the same arguments ''' + for i in self._get_paths(): for path in glob.glob(os.path.join(i, "*.py")): name, ext = os.path.splitext(os.path.basename(path))