Beispiel #1
0
	def getPluginNameAndModuleFromStream(self, infoFileObject, candidate_infofile=None):
		"""
		Extract the name and module of a plugin from the
		content of the info file that describes it and which
		is stored in ``infoFileObject``.
		
		.. note:: Prefer using ``_extractCorePluginInfo``
		          instead, whenever possible...
		
		.. warning:: ``infoFileObject`` must be a file-like object:
		             either an opened file for instance or a string
		             buffer wrapped in a StringIO instance as another
		             example.
		      
		.. note:: ``candidate_infofile`` must be provided
		          whenever possible to get better error messages.
		
		Return a 3-uple with the name of the plugin, its
		module and the config_parser used to gather the core
		data *in a tuple*, if the required info could be
		localised, else return ``(None,None,None)``.
		
		.. note:: This is supposed to be used internally by subclasses
			      and decorators.
		"""
		# parse the information buffer to get info about the plugin
		config_parser = ConfigParser.SafeConfigParser()
		try:
			config_parser.readfp(infoFileObject)
		except Exception,e:
			log.debug("Could not parse the plugin file '%s' (exception raised was '%s')" % (candidate_infofile,e))
			return (None, None, None)
Beispiel #2
0
    def setBehaviour(self, list_of_pmd):
        """
		Set the functionalities handled by the plugin manager by
		giving a list of ``PluginManager`` decorators.
		
		This function shouldn't be called several time in a same
		process, but if it is only the first call will have an effect.

		It also has an effect only if called before the initialisation
		of the singleton.

		In cases where the function is indeed going to change anything
		the ``True`` value is return, in all other cases, the ``False``
		value is returned.
		"""
        if self.__decoration_chain is None and self.__instance is None:
            log.debug(
                "Setting up a specific behaviour for the PluginManagerSingleton"
            )
            self.__decoration_chain = list_of_pmd
            return True
        else:
            log.debug(
                "Useless call to setBehaviour: the singleton is already instanciated of already has a behaviour."
            )
            return False
Beispiel #3
0
	def __init__(self, decorated_object=None,
				 # The following args will only be used if we need to
				 # create a default PluginManager
				 categories_filter=None, 
				 directories_list=None, 
				 plugin_info_ext="yapsy-plugin"):
		"""
		Mimics the PluginManager's __init__ method and wraps an
		instance of this class into this decorator class.
		
		  - *If the decorated_object is not specified*, then we use the
		    PluginManager class to create the 'base' manager, and to do
		    so we will use the arguments: ``categories_filter``,
		    ``directories_list``, and ``plugin_info_ext`` or their
		    default value if they are not given.

		  - *If the decorated object is given*, these last arguments are
		    simply **ignored** !

		All classes (and especially subclasses of this one) that want
		to be a decorator must accept the decorated manager as an
		object passed to the init function under the exact keyword
		``decorated_object``.
		"""
		if directories_list is None:
			directories_list = [os.path.dirname(__file__)]
		if categories_filter is None:
			categories_filter = {"Default": IPlugin}
		if decorated_object is None:
			log.debug("Creating a default PluginManager instance to be decorated.")
			from yapsy.PluginManager import PluginManager
			decorated_object = PluginManager(categories_filter, 
											 directories_list,
											 plugin_info_ext)
		self._component = decorated_object
 def getPluginNameAndModuleFromStream(self, infoFileObject,
                                      candidate_infofile=None):
     # parse the information buffer to get info about the plugin
     config_parser = ConfigParser.SafeConfigParser()
     try:
         config_parser.readfp(infoFileObject)
     except Exception, e:
         log.debug(
             "Could not parse the plugin file '%s' (exception raised was '%s')",  # pylint: disable=line-too-long
             candidate_infofile, e)
         return (None, None)
	def removeAnalyzers(self, name):
		"""
		Removes analyzers of a given name.
		"""
		analyzersListCopy = self._analyzers[:]
		foundAndRemoved = False
		for obj in analyzersListCopy:
			if obj.name == name:
				self._analyzers.remove(obj)
				foundAndRemoved = True
		if not foundAndRemoved:
			log.debug("'%s' is not a known strategy name: can't remove it." % name)
Beispiel #6
0
	def removeAnalyzers(self, name):
		"""
		Removes analyzers of a given name.
		"""
		analyzersListCopy = self._analyzers[:]
		foundAndRemoved = False
		for obj in analyzersListCopy:
			if obj.name == name:
				self._analyzers.remove(obj)
				foundAndRemoved = True
		if not foundAndRemoved:
			log.debug("'%s' is not a known strategy name: can't remove it." % name)
	def activatePluginByName(self,name,category="Default"):
		"""
		Activate a plugin corresponding to a given category + name.
		"""
		pta_item = self.getPluginByName(name,category)
		if pta_item is not None:
			plugin_to_activate = pta_item.plugin_object
			if plugin_to_activate is not None:
				log.debug("Activating plugin: %s.%s"% (category,name))
				plugin_to_activate.activate()
				return plugin_to_activate			
		return None
    def getPluginNameAndModuleFromStream(self,
                                         infoFileObject,
                                         candidate_infofile=None):
        """
		Extract the name and module of a plugin from the
		content of the info file that describes it and which
		is stored in ``infoFileObject``.
		
		.. note:: Prefer using ``_extractCorePluginInfo``
		          instead, whenever possible...
		
		.. warning:: ``infoFileObject`` must be a file-like object:
		             either an opened file for instance or a string
		             buffer wrapped in a StringIO instance as another
		             example.
		      
		.. note:: ``candidate_infofile`` must be provided
		          whenever possible to get better error messages.
		
		Return a 3-uple with the name of the plugin, its
		module and the config_parser used to gather the core
		data *in a tuple*, if the required info could be
		localised, else return ``(None,None,None)``.
		
		.. note:: This is supposed to be used internally by subclasses
			      and decorators.
		"""
        # parse the information buffer to get info about the plugin
        config_parser = ConfigParser()
        try:
            if is_py2:
                config_parser.readfp(infoFileObject)
            else:
                config_parser.read_file(infoFileObject)
        except Exception as e:
            log.debug(
                "Could not parse the plugin file '%s' (exception raised was '%s')"
                % (candidate_infofile, e))
            return (None, None, None)
        # check if the basic info is available
        if not config_parser.has_section("Core"):
            log.debug("Plugin info file has no 'Core' section (in '%s')" %
                      candidate_infofile)
            return (None, None, None)
        if not config_parser.has_option(
                "Core", "Name") or not config_parser.has_option(
                    "Core", "Module"):
            log.debug(
                "Plugin info file has no 'Name' or 'Module' section (in '%s')"
                % candidate_infofile)
            return (None, None, None)
        # check that the given name is valid
        name = config_parser.get("Core", "Name")
        name = name.strip()
        if PLUGIN_NAME_FORBIDEN_STRING in name:
            log.debug("Plugin name contains forbiden character: %s (in '%s')" %
                      (PLUGIN_NAME_FORBIDEN_STRING, candidate_infofile))
            return (None, None, None)
        return (name, config_parser.get("Core", "Module"), config_parser)
	def deactivatePluginByName(self,name,category="Default"):
		"""
		Desactivate a plugin corresponding to a given category + name.
		"""
		if category in self.category_mapping:
			plugin_to_deactivate = None
			for item in self.category_mapping[category]:
				if item.name == name:
					plugin_to_deactivate = item.plugin_object
					break
			if plugin_to_deactivate is not None:
				log.debug("Deactivating plugin: %s.%s"% (category,name))
				plugin_to_deactivate.deactivate()
				return plugin_to_deactivate			
		return None
	def __init__(self, decorated_object=None,
				 # The following args will only be used if we need to
				 # create a default PluginManager
				 categories_filter=None, 
				 directories_list=None, 
				 plugin_info_ext="yapsy-plugin"):
		if directories_list is None:
			directories_list = [os.path.dirname(__file__)]
		if categories_filter is None:
			categories_filter = {"Default": IPlugin}
		if decorated_object is None:
			log.debug("Creating a default PluginManager instance to be decorated.")
			from yapsy.PluginManager import PluginManager
			decorated_object = PluginManager(categories_filter, 
											 directories_list,
											 plugin_info_ext)
		self._component = decorated_object
	def get(self):
		"""
		Actually create an instance
		"""
		if self.__instance is None:
			if self.__decoration_chain is not None:
				# Get the object to be decorated
#				print self.__decoration_chain
				pm = self.__decoration_chain[0]()
				for cls_item in self.__decoration_chain[1:]:
#					print cls_item
					pm = cls_item(decorated_manager=pm)
				# Decorate the whole object
				self.__instance = pm
			else:
				# initialise the 'inner' PluginManagerDecorator
				self.__instance = PluginManager()			
			log.debug("PluginManagerSingleton initialised")
		return self.__instance
    def getInfosDictFromPlugin(self, dirpath, filename):
        path = os.path.join(dirpath, filename)
        key = "%s_get_docstring" % os.path.splitext(filename)[0]
        log.debug(path)

        module = None
        infos = None, None
        try:
            module = imp.load_source(key, path)
            docstring = module.__doc__
            infos = PluginFileAnalyzerWithInfoFile.getInfosDictFromPlugin(
                self, path, StringIO(docstring))
        except Exception as e:
            log.debug(e)
        finally:
            if not module is None:
                del module
            if key in sys.modules:
                del sys.modules[key]
        return infos
Beispiel #13
0
	def getPluginNameAndModuleFromStream(self, infoFileObject, candidate_infofile=None):
		"""
		Extract the name and module of a plugin from the
		content of the info file that describes it and which
		is stored in ``infoFileObject``.
		
		.. note:: Prefer using ``_extractCorePluginInfo``
		          instead, whenever possible...
		
		.. warning:: ``infoFileObject`` must be a file-like object:
		             either an opened file for instance or a string
		             buffer wrapped in a StringIO instance as another
		             example.
		      
		.. note:: ``candidate_infofile`` must be provided
		          whenever possible to get better error messages.
		
		Return a 3-uple with the name of the plugin, its
		module and the config_parser used to gather the core
		data *in a tuple*, if the required info could be
		localised, else return ``(None,None,None)``.
		
		.. note:: This is supposed to be used internally by subclasses
			      and decorators.
		"""
		# parse the information buffer to get info about the plugin
		config_parser = ConfigParser()
		try:
			if is_py2:
				config_parser.readfp(infoFileObject)
			else:
				config_parser.read_file(infoFileObject)
		except Exception as e:
			log.debug("Could not parse the plugin file '%s' (exception raised was '%s')" % (candidate_infofile,e))
			return (None, None, None)
		# check if the basic info is available
		if not config_parser.has_section("Core"):
			log.debug("Plugin info file has no 'Core' section (in '%s')" % candidate_infofile)
			return (None, None, None)
		if not config_parser.has_option("Core","Name") or not config_parser.has_option("Core","Module"):
			log.debug("Plugin info file has no 'Name' or 'Module' section (in '%s')" % candidate_infofile)
			return (None, None, None)
		# check that the given name is valid
		name = config_parser.get("Core", "Name")
		name = name.strip()
		if PLUGIN_NAME_FORBIDEN_STRING in name:
			log.debug("Plugin name contains forbiden character: %s (in '%s')" % (PLUGIN_NAME_FORBIDEN_STRING,
																					candidate_infofile))
			return (None, None, None)
		return (name, config_parser.get("Core", "Module"), config_parser)
Beispiel #14
0
	def setBehaviour(self,list_of_pmd):
		"""
		Set the functionalities handled by the plugin manager by
		giving a list of ``PluginManager`` decorators.
		
		This function shouldn't be called several time in a same
		process, but if it is only the first call will have an effect.

		It also has an effect only if called before the initialisation
		of the singleton.

		In cases where the function is indeed going to change anything
		the ``True`` value is return, in all other cases, the ``False``
		value is returned.
		"""
		if self.__decoration_chain is None and self.__instance is None:
			log.debug("Setting up a specific behaviour for the PluginManagerSingleton")
			self.__decoration_chain = list_of_pmd
			return True
		else:
			log.debug("Useless call to setBehaviour: the singleton is already instanciated of already has a behaviour.")
			return False
Beispiel #15
0
	def locatePlugins(self):
		"""
		Walk through the plugins' places and look for plugins.

		Return the number of plugins found.
		"""
# 		print "%s.locatePlugins" % self.__class__
		self._candidates = []
		for directory in map(os.path.abspath,self.plugins_places):
			# first of all, is it a directory :)
			if not os.path.isdir(directory):
				log.debug("%s skips %s (not a directory)" % (self.__class__.__name__,directory))
				continue
			# iteratively walks through the directory
			log.debug("%s walks into directory: %s" % (self.__class__.__name__,directory))
			for item in os.walk(directory):
				dirpath = item[0]
				for filename in item[2]:
					# eliminate the obvious non plugin files
					if not filename.endswith(".%s" % self.plugin_info_ext):
						continue
					candidate_infofile = os.path.join(dirpath,filename)
					log.debug("""%s found a candidate: 
	%s""" % (self.__class__.__name__, candidate_infofile))
#					print candidate_infofile
					plugin_info = self.gatherBasicPluginInfo(dirpath,filename)
					if plugin_info is None:
						log.info("Plugin candidate rejected: '%s'" % candidate_infofile)
						continue
					# now determine the path of the file to execute,
					# depending on wether the path indicated is a
					# directory or a file
#					print plugin_info.path
					if os.path.isdir(plugin_info.path):
						candidate_filepath = os.path.join(plugin_info.path,"__init__")
					elif os.path.isfile(plugin_info.path+".py"):
						candidate_filepath = plugin_info.path
					else:
						log.info("Plugin candidate rejected: '%s'" % candidate_infofile) 
						continue
#					print candidate_filepath
					self._candidates.append((candidate_infofile, candidate_filepath, plugin_info))
		return len(self._candidates)
Beispiel #16
0
	def locatePlugins(self):
		"""
		Walk through the plugins' places and look for plugins.

		Return the candidates and number of plugins found.
		"""
# 		print "%s.locatePlugins" % self.__class__
		_candidates = []
		_discovered = {}
		for directory in map(os.path.abspath, self.plugins_places):
			# first of all, is it a directory :)
			if not os.path.isdir(directory):
				log.debug("%s skips %s (not a directory)" % (self.__class__.__name__, directory))
				continue
			if self.recursive:
				debug_txt_mode = "recursively"
				walk_iter = os.walk(directory)
			else:
				debug_txt_mode = "non-recursively"
				walk_iter = [(directory,[],os.listdir(directory))]				
			# iteratively walks through the directory
			log.debug("%s walks (%s) into directory: %s" % (self.__class__.__name__, debug_txt_mode, directory))
			for item in walk_iter:
				dirpath = item[0]
				for filename in item[2]:
					# print "testing candidate file %s" % filename
					for analyzer in self._analyzers:
						# print "... with analyzer %s" % analyzer.name
						# eliminate the obvious non plugin files
						if not analyzer.isValidPlugin(filename):
							log.debug("%s is not a valid plugin for strategy %s" % (filename, analyzer.name))
							continue
						candidate_infofile = os.path.join(dirpath, filename)
						if candidate_infofile in _discovered:
							log.debug("%s (with strategy %s) rejected because already discovered" % (candidate_infofile, analyzer.name))
							continue
						log.debug("%s found a candidate:\n    %s" % (self.__class__.__name__, candidate_infofile))
#						print candidate_infofile
						plugin_info = self._getInfoForPluginFromAnalyzer(analyzer, dirpath, filename)
						if plugin_info is None:
							log.warning("Plugin candidate '%s'  rejected by strategy '%s'" % (candidate_infofile, analyzer.name))
							break # we consider this was the good strategy to use for: it failed -> not a plugin -> don't try another strategy
						# now determine the path of the file to execute,
						# depending on wether the path indicated is a
						# directory or a file
#					print plugin_info.path
						# Remember all the files belonging to a discovered
						# plugin, so that strategies (if several in use) won't
						# collide
						if os.path.isdir(plugin_info.path):
							candidate_filepath = os.path.join(plugin_info.path, "__init__")
							# it is a package, adds all the files concerned
							for _file in os.listdir(plugin_info.path):
								if _file.endswith(".py"):
									self._discovered_plugins[os.path.join(plugin_info.path, _file)] = candidate_filepath
									_discovered[os.path.join(plugin_info.path, _file)] = candidate_filepath
						elif (plugin_info.path.endswith(".py") and os.path.isfile(plugin_info.path)) or os.path.isfile(plugin_info.path+".py"):
							candidate_filepath = plugin_info.path
							if candidate_filepath.endswith(".py"):
								candidate_filepath = candidate_filepath[:-3]
							# it is a file, adds it
							self._discovered_plugins[".".join((plugin_info.path, "py"))] = candidate_filepath
							_discovered[".".join((plugin_info.path, "py"))] = candidate_filepath
						else:
							log.error("Plugin candidate rejected: cannot find the file or directory module for '%s'" % (candidate_infofile))
							break
#					print candidate_filepath
						_candidates.append((candidate_infofile, candidate_filepath, plugin_info))
						# finally the candidate_infofile must not be discovered again
						_discovered[candidate_infofile] = candidate_filepath
						self._discovered_plugins[candidate_infofile] = candidate_filepath
#						print "%s found by strategy %s" % (candidate_filepath, analyzer.name)
		return _candidates, len(_candidates)
class PluginFileAnalyzerWithDocString(PluginFileAnalyzerWithInfoFile):
    def __init__(self, name):
        PluginFileAnalyzerWithInfoFile.__init__(self, name, extensions="py")

    def isValidPlugin(self, filename):
        if filename.endswith('_plugin.py'):
            return True
        return False

    def getInfosDictFromPlugin(self, dirpath, filename):
        path = os.path.join(dirpath, filename)
        key = "%s_get_docstring" % os.path.splitext(filename)[0]
        log.debug(path)

        module = None
        infos = None, None
        try:
            module = imp.load_source(key, path)
            docstring = module.__doc__
            infos = PluginFileAnalyzerWithInfoFile.getInfosDictFromPlugin(
                self, path, StringIO(docstring))
        except Exception as e:
            log.debug(e)
        finally:
            if not module is None:
                del module
            if key in sys.modules:
                del sys.modules[key]
        return infos

    def _extractCorePluginInfo(self, candidate_infofile, stream):
        name, config_parser = self.getPluginNameAndModuleFromStream(
            stream, candidate_infofile)

        if (name, config_parser) == (None, None):
            return (None, None)
        infos = {"name": name, "path": candidate_infofile}
        return infos, config_parser

    def getPluginNameAndModuleFromStream(self, infoFileObject,
                                         candidate_infofile=None):
        # parse the information buffer to get info about the plugin
        config_parser = ConfigParser.SafeConfigParser()
        try:
            config_parser.readfp(infoFileObject)
        except Exception, e:
            log.debug(
                "Could not parse the plugin file '%s' (exception raised was '%s')",  # pylint: disable=line-too-long
                candidate_infofile, e)
            return (None, None)
        # check if the basic info is available
        if not config_parser.has_section("Core"):
            log.debug(
                "Plugin info file has no 'Core' section (in '%s')",
                candidate_infofile)
            return (None, None)
        if not config_parser.has_option("Core", "Name"):
            log.debug(
                "Plugin info file has no 'Name' or 'Module' section (in '%s')",
                candidate_infofile)
            return (None, None)
        # check that the given name is valid
        name = config_parser.get("Core", "Name")
        name = name.strip()
        if PLUGIN_NAME_FORBIDEN_STRING in name:
            log.debug(
                "Plugin name contains forbiden character: %s (in '%s')",
                PLUGIN_NAME_FORBIDEN_STRING, candidate_infofile)
            return (None, None)
        return (name, config_parser)
Beispiel #18
0
    def loadPlugins(self, callback=None, callback_after=None):
        """
		Load the candidate plugins that have been identified through a
		previous call to locatePlugins.  For each plugin candidate
		look for its category, load it and store it in the appropriate
		slot of the ``category_mapping``.

		You can specify 2 callbacks: callback, and callback_after. If either of these are passed a function, (in the case of callback), it will get called before each plugin load attempt and (for callback_after), after each 
		attempt.  The ``plugin_info`` instance is passed as an argument to
		each callback. This is meant to facilitate code that needs to run for each plugin, such as adding the directory it resides in to sys.path (so imports of other files in the plugin's directory work correctly). You can use callback_after to remove anything you added to the path.
		"""
        # 		print "%s.loadPlugins" % self.__class__
        if not hasattr(self, '_candidates'):
            raise ValueError("locatePlugins must be called before loadPlugins")

        processed_plugins = []
        for candidate_infofile, candidate_filepath, plugin_info in self._candidates:
            # make sure to attribute a unique module name to the one
            # that is about to be loaded
            plugin_module_name_template = NormalizePluginNameForModuleName(
                "yapsy_loaded_plugin_" + plugin_info.name) + "_%d"
            for plugin_name_suffix in range(len(sys.modules)):
                plugin_module_name = plugin_module_name_template % plugin_name_suffix
                if plugin_module_name not in sys.modules:
                    break

            # tolerance on the presence (or not) of the py extensions
            if candidate_filepath.endswith(".py"):
                candidate_filepath = candidate_filepath[:-3]
            # if a callback exists, call it before attempting to load
            # the plugin so that a message can be displayed to the
            # user
            if callback is not None:
                callback(plugin_info)
            # cover the case when the __init__ of a package has been
            # explicitely indicated
            if "__init__" in os.path.basename(candidate_filepath):
                candidate_filepath = os.path.dirname(candidate_filepath)
            try:
                candidate_module = PluginManager._importModule(
                    plugin_module_name, candidate_filepath)
            except Exception:
                exc_info = sys.exc_info()
                log.error("Unable to import plugin: %s" % candidate_filepath,
                          exc_info=exc_info)
                plugin_info.error = exc_info
                processed_plugins.append(plugin_info)
                continue

            processed_plugins.append(plugin_info)
            if "__init__" in os.path.basename(candidate_filepath):
                sys.path.remove(plugin_info.path)
            # now try to find and initialise the first subclass of the correct plugin interface
            last_failed_attempt_message = None
            for element, element_name in ((getattr(candidate_module,
                                                   name), name)
                                          for name in dir(candidate_module)):
                plugin_info_reference = None
                for category_name in self.categories_interfaces:
                    try:
                        is_correct_subclass = issubclass(
                            element, self.categories_interfaces[category_name])
                    except Exception:
                        exc_info = sys.exc_info()
                        log.debug(
                            "correct subclass tests failed for: %s in %s" %
                            (element_name, candidate_filepath),
                            exc_info=exc_info)
                        continue
                    if is_correct_subclass and element is not self.categories_interfaces[
                            category_name]:
                        current_category = category_name
                        if candidate_infofile not in self._category_file_mapping[
                                current_category]:
                            # we found a new plugin: initialise it and search for the next one
                            if not plugin_info_reference:
                                try:
                                    plugin_info.plugin_object = self.instanciateElementWithImportInfo(
                                        element, element_name,
                                        plugin_module_name, candidate_filepath)
                                    plugin_info_reference = plugin_info
                                except Exception:
                                    exc_info = sys.exc_info()
                                    last_failed_attempt_message = "Unable to create plugin object: %s" % candidate_filepath
                                    log.debug(last_failed_attempt_message,
                                              exc_info=exc_info)
                                    plugin_info.error = exc_info
                                    break  # If it didn't work once it wont again
                                else:
                                    last_failed_attempt_message = None
                            plugin_info.categories.append(current_category)
                            self.category_mapping[current_category].append(
                                plugin_info_reference)
                            self._category_file_mapping[
                                current_category].append(candidate_infofile)
                            #Everything is loaded and instantiated for this plugin now
                            if callback_after is not None:
                                callback_after(plugin_info)
            else:
                if last_failed_attempt_message:
                    log.error(last_failed_attempt_message,
                              exc_info=plugin_info.error)

        # Remove candidates list since we don't need them any more and
        # don't need to take up the space
        delattr(self, '_candidates')
        return processed_plugins
	def locatePlugins(self):
		"""
		Walk through the plugins' places and look for plugins.

		Return the candidates and number of plugins found.
		"""
# 		print "%s.locatePlugins" % self.__class__
		_candidates = []
		_discovered = {}
		for directory in map(os.path.abspath, self.plugins_places):
			# first of all, is it a directory :)
			if not os.path.isdir(directory):
				log.debug("%s skips %s (not a directory)" % (self.__class__.__name__, directory))
				continue
			if self.recursive:
				debug_txt_mode = "recursively"
				walk_iter = os.walk(directory)
			else:
				debug_txt_mode = "non-recursively"
				walk_iter = [(directory,[],os.listdir(directory))]				
			# iteratively walks through the directory
			log.debug("%s walks (%s) into directory: %s" % (self.__class__.__name__, debug_txt_mode, directory))
			for item in walk_iter:
				dirpath = item[0]
				for filename in item[2]:
					# print "testing candidate file %s" % filename
					for analyzer in self._analyzers:
						# print "... with analyzer %s" % analyzer.name
						# eliminate the obvious non plugin files
						if not analyzer.isValidPlugin(filename):
							log.debug("%s is not a valid plugin for strategy %s" % (filename, analyzer.name))
							continue
						candidate_infofile = os.path.join(dirpath, filename)
						if candidate_infofile in _discovered:
							log.debug("%s (with strategy %s) rejected because already discovered" % (candidate_infofile, analyzer.name))
							continue
						log.debug("%s found a candidate:\n    %s" % (self.__class__.__name__, candidate_infofile))
#						print candidate_infofile
						plugin_info = self._getInfoForPluginFromAnalyzer(analyzer, dirpath, filename)
						if plugin_info is None:
							log.warning("Plugin candidate '%s'  rejected by strategy '%s'" % (candidate_infofile, analyzer.name))
							break # we consider this was the good strategy to use for: it failed -> not a plugin -> don't try another strategy
						# now determine the path of the file to execute,
						# depending on wether the path indicated is a
						# directory or a file
#					print plugin_info.path
						# Remember all the files belonging to a discovered
						# plugin, so that strategies (if several in use) won't
						# collide
						if os.path.isdir(plugin_info.path):
							candidate_filepath = os.path.join(plugin_info.path, "__init__")
							# it is a package, adds all the files concerned
							for _file in os.listdir(plugin_info.path):
								if _file.endswith(".py"):
									self._discovered_plugins[os.path.join(plugin_info.path, _file)] = candidate_filepath
									_discovered[os.path.join(plugin_info.path, _file)] = candidate_filepath
						elif (plugin_info.path.endswith(".py") and os.path.isfile(plugin_info.path)) or os.path.isfile(plugin_info.path+".py"):
							candidate_filepath = plugin_info.path
							if candidate_filepath.endswith(".py"):
								candidate_filepath = candidate_filepath[:-3]
							# it is a file, adds it
							self._discovered_plugins[".".join((plugin_info.path, "py"))] = candidate_filepath
							_discovered[".".join((plugin_info.path, "py"))] = candidate_filepath
						else:
							log.error("Plugin candidate rejected: cannot find the file or directory module for '%s'" % (candidate_infofile))
							break
#					print candidate_filepath
						_candidates.append((candidate_infofile, candidate_filepath, plugin_info))
						# finally the candidate_infofile must not be discovered again
						_discovered[candidate_infofile] = candidate_filepath
						self._discovered_plugins[candidate_infofile] = candidate_filepath
#						print "%s found by strategy %s" % (candidate_filepath, analyzer.name)
		return _candidates, len(_candidates)
Beispiel #20
0
class PluginFileAnalyzerWithInfoFile(IPluginFileAnalyzer):
	"""
	Consider plugins described by a textual description file.

	A plugin is expected to be described by a text file ('ini' format) with a specific extension (.yapsy-plugin by default).

	This file must contain at least the following information::
	
	    [Core]
	    Name = name of the module
	    Module = relative_path/to/python_file_or_directory

	Optionnally the description file may also contain the following section (in addition to the above one)::

	    [Documentation]
	    Author = Author Name
	    Version = Major.minor
	    Website = url_for_plugin
	    Description = A simple one-sentence description

	"""
	def __init__(self, name, extensions="yapsy-plugin"):
		"""
		Creates a new analyzer named *name* and dedicated to check and analyze plugins described by a textual "info file".
		
		*name* name of the plugin.

		*extensions* the expected extensions for the plugin info file. May be a string or a tuple of strings if several extensions are expected.
		"""
		IPluginFileAnalyzer.__init__(self,name)
		self.setPluginInfoExtension(extensions)

	
	def setPluginInfoExtension(self,extensions):
		"""
		Set the extension that will identify a plugin info file.

		*extensions* May be a string or a tuple of strings if several extensions are expected.
		"""
		# Make sure extension is a tuple
		if not isinstance(extensions, tuple):
			extensions = (extensions, )
		self.expectedExtensions = extensions
		
	
	def isValidPlugin(self, filename):
		"""
		Check if it is a valid plugin based on the given plugin info file extension(s).
		If several extensions are provided, the first matching will cause the function
		to exit successfully.
		"""
		res = False
		for ext in self.expectedExtensions:
			if filename.endswith(".%s" % ext):
				res = True
				break
		return res
	
	def getPluginNameAndModuleFromStream(self, infoFileObject, candidate_infofile=None):
		"""
		Extract the name and module of a plugin from the
		content of the info file that describes it and which
		is stored in ``infoFileObject``.
		
		.. note:: Prefer using ``_extractCorePluginInfo``
		          instead, whenever possible...
		
		.. warning:: ``infoFileObject`` must be a file-like object:
		             either an opened file for instance or a string
		             buffer wrapped in a StringIO instance as another
		             example.
		      
		.. note:: ``candidate_infofile`` must be provided
		          whenever possible to get better error messages.
		
		Return a 3-uple with the name of the plugin, its
		module and the config_parser used to gather the core
		data *in a tuple*, if the required info could be
		localised, else return ``(None,None,None)``.
		
		.. note:: This is supposed to be used internally by subclasses
			      and decorators.
		"""
		# parse the information buffer to get info about the plugin
		config_parser = ConfigParser.SafeConfigParser()
		try:
			config_parser.readfp(infoFileObject)
		except Exception,e:
			log.debug("Could not parse the plugin file '%s' (exception raised was '%s')" % (candidate_infofile,e))
			return (None, None, None)
		# check if the basic info is available
		if not config_parser.has_section("Core"):
			log.debug("Plugin info file has no 'Core' section (in '%s')" % candidate_infofile)
			return (None, None, None)
		if not config_parser.has_option("Core","Name") or not config_parser.has_option("Core","Module"):
			log.debug("Plugin info file has no 'Name' or 'Module' section (in '%s')" % candidate_infofile)
			return (None, None, None)
		# check that the given name is valid
		name = config_parser.get("Core", "Name")
		name = name.strip()
		if PLUGIN_NAME_FORBIDEN_STRING in name:
			log.debug("Plugin name contains forbiden character: %s (in '%s')" % (PLUGIN_NAME_FORBIDEN_STRING,
																					candidate_infofile))
			return (None, None, None)
		return (name, config_parser.get("Core", "Module"), config_parser)