Make plugin loader path operations more efficient.

This commit is contained in:
Michael DeHaan 2013-04-20 12:31:14 -04:00
parent 5a8dff5bce
commit e6bf01a6b0
2 changed files with 65 additions and 29 deletions

View file

@ -170,13 +170,23 @@ def main():
if len(args) == 0: if len(args) == 0:
p.print_help() 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: for module in args:
filename = utils.plugins.module_finder.find_plugin(module) filename = utils.plugins.module_finder.find_plugin(module)
if filename is None: if filename is None:
sys.stderr.write("module %s not found in %s\n" % (module, sys.stderr.write("module %s not found in %s\n" % (module,
utils.plugins.module_finder.print_paths())) print_paths(utils.plugins.module_finder)))
continue continue
if any(filename.endswith(x) for x in BLACKLIST_EXTS): if any(filename.endswith(x) for x in BLACKLIST_EXTS):

View file

@ -23,34 +23,47 @@ import ansible.constants as C
from ansible import errors from ansible import errors
MODULE_CACHE = {} MODULE_CACHE = {}
PATH_CACHE = {}
PLUGIN_PATH_CACHE = {}
_basedirs = [] _basedirs = []
def push_basedir(basedir): def push_basedir(basedir):
_basedirs.insert(0, basedir) _basedirs.insert(0, basedir)
class PluginLoader(object): 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 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. The first match is used.
""" '''
def __init__(self, class_name, package, config, subdir, aliases={}): def __init__(self, class_name, package, config, subdir, aliases={}):
"""Create a new PluginLoader"""
self.class_name = class_name self.class_name = class_name
self.package = package self.package = package
self.config = config self.config = config
self.subdir = subdir self.subdir = subdir
self.aliases = aliases self.aliases = aliases
if not class_name in MODULE_CACHE: if not class_name in MODULE_CACHE:
MODULE_CACHE[class_name] = {} 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 = [] self._extra_dirs = []
def _get_package_path(self): def _get_package_path(self):
"""Gets the path of a Python package""" ''' Gets the path of a Python package '''
if not self.package: if not self.package:
return [] return []
if not hasattr(self, 'package_path'): if not hasattr(self, 'package_path'):
@ -60,9 +73,11 @@ class PluginLoader(object):
return [self.package_path] return [self.package_path]
def _get_paths(self): 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 = []
ret += self._extra_dirs ret += self._extra_dirs
for basedir in _basedirs: for basedir in _basedirs:
@ -71,41 +86,51 @@ class PluginLoader(object):
ret.append(fullpath) ret.append(fullpath)
ret += self.config.split(os.pathsep) ret += self.config.split(os.pathsep)
ret += self._get_package_path() ret += self._get_package_path()
self._paths = ret
return ret return ret
def add_directory(self, directory, with_subdir=False): 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 directory is not None:
if with_subdir: if with_subdir:
directory = os.path.join(directory, self.subdir) directory = os.path.join(directory, self.subdir)
self._extra_dirs.append(directory) 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): 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" suffix = ".py"
if not self.class_name: if not self.class_name:
suffix = "" suffix = ""
paths = self._get_paths()
for i in self._get_paths(): for i in self._get_paths():
path = os.path.join(i, "%s%s" % (name, suffix)) path = os.path.join(i, "%s%s" % (name, suffix))
if os.path.exists(path): if os.path.exists(path):
self._plugin_path_cache[name] = path
return path return path
return None return None
def has_plugin(self, name): 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 return self.find_plugin(name) is not None
__contains__ = has_plugin __contains__ = has_plugin
def get(self, name, *args, **kwargs): def get(self, name, *args, **kwargs):
''' instantiates a plugin of the given name using arguments '''
if name in self.aliases: if name in self.aliases:
name = self.aliases[name] name = self.aliases[name]
path = self.find_plugin(name) path = self.find_plugin(name)
@ -116,7 +141,8 @@ class PluginLoader(object):
return getattr(self._module_cache[path], self.class_name)(*args, **kwargs) return getattr(self._module_cache[path], self.class_name)(*args, **kwargs)
def all(self, *args, **kwargs): def all(self, *args, **kwargs):
''' instantiates all plugins with the same arguments '''
for i in self._get_paths(): for i in self._get_paths():
for path in glob.glob(os.path.join(i, "*.py")): for path in glob.glob(os.path.join(i, "*.py")):
name, ext = os.path.splitext(os.path.basename(path)) name, ext = os.path.splitext(os.path.basename(path))