Ejemplo n.º 1
0
    def _getPluginNameAndModuleFromStream(self, infoFileObject, candidate_infofile="<buffered info>"):
        """
        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 ``_gatherCorePluginInfo``
        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.warning("Could not parse the plugin file '%s' (exception raised was '%s')" % (candidate_infofile,e))
            return (None, None, None)
	def install(self, directory, plugin_info_filename):
		"""
		Giving the plugin's info file (e.g. ``myplugin.yapsy-plugin``),
		and the directory where it is located, get all the files that
		define the plugin and copy them into the correct directory.
		
		Return ``True`` if the installation is a success, ``False`` if
		it is a failure.
		"""
		# start collecting essential info about the new plugin
		plugin_info, config_parser = self._gatherCorePluginInfo(directory, plugin_info_filename)
		# now determine the path of the file to execute,
		# depending on wether the path indicated is a
		# directory or a file
		if not (os.path.exists(plugin_info.path) or os.path.exists(plugin_info.path+".py") ):
			log.warning("Could not find the plugin's implementation for %s." % plugin_info.name)
			return False
		if os.path.isdir(plugin_info.path):
			try:
				shutil.copytree(plugin_info.path,
								os.path.join(self.install_dir,os.path.basename(plugin_info.path)))
				shutil.copy(os.path.join(directory, plugin_info_filename),
							self.install_dir)
			except:
				log.error("Could not install plugin: %s." % plugin_info.name)
				return False
			else:
				return True
		elif os.path.isfile(plugin_info.path+".py"):
			try:
				shutil.copy(plugin_info.path+".py",
							self.install_dir)
				shutil.copy(os.path.join(directory, plugin_info_filename),
						   self.install_dir)
			except:
				log.error("Could not install plugin: %s." % plugin_info.name)
				return False
			else:
				return True
		else:
			return False
	def installFromZIP(self, plugin_ZIP_filename):
		"""
		Giving the plugin's zip file (e.g. ``myplugin.zip``), check
		that their is a valid info file in it and correct all the
		plugin files into the correct directory.
		
		.. warning:: Only available for python 2.6 and later.
		
		Return ``True`` if the installation is a success, ``False`` if
		it is a failure.
		"""
		if sys.version_info < (2, 6):
			raise NotImplementedError("Installing fom a ZIP file is only supported for Python2.6 and later.")
		if not os.path.isfile(plugin_ZIP_filename):
			log.warning("Could not find the plugin's zip file at '%s'." % plugin_ZIP_filename)
			return False
		try:
			candidateZipFile = zipfile.ZipFile(plugin_ZIP_filename)
			first_bad_file = candidateZipFile.testzip()
			if first_bad_file:
				raise Exception("Corrupted ZIP with first bad file '%s'" % first_bad_file)
		except Exception,e:
			log.warning("Invalid zip file '%s' (error: %s)." % (plugin_ZIP_filename,e))
			return False
Ejemplo n.º 4
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)
	def installFromZIP(self, plugin_ZIP_filename):
		"""
		Giving the plugin's zip file (e.g. ``myplugin.zip``), check
		that their is a valid info file in it and correct all the
		plugin files into the correct directory.
		
		.. warning:: Only available for python 2.6 and later.
		
		Return ``True`` if the installation is a success, ``False`` if
		it is a failure.
		"""
		if not os.path.isfile(plugin_ZIP_filename):
			log.warning("Could not find the plugin's zip file at '%s'." % plugin_ZIP_filename)
			return False
		try:
			candidateZipFile = zipfile.ZipFile(plugin_ZIP_filename)
			first_bad_file = candidateZipFile.testzip()
			if first_bad_file:
				raise Exception("Corrupted ZIP with first bad file '%s'" % first_bad_file)
		except Exception as e:
			log.warning("Invalid zip file '%s' (error: %s)." % (plugin_ZIP_filename,e))
			return False
		zipContent = candidateZipFile.namelist()
		log.info("Investigating the content of a zip file containing: '%s'" % zipContent)
		log.info("Sanity checks on zip's contained files (looking for hazardous path symbols).")	
		# check absence of root path and ".." shortcut that would
		# send the file oustide the desired directory
		for containedFileName in zipContent:
			# WARNING: the sanity checks below are certainly not
			# exhaustive (maybe we could do something a bit smarter by
			# using os.path.expanduser, os.path.expandvars and
			# os.path.normpath)
			if containedFileName.startswith("/"):
				log.warning("Unsecure zip file, rejected because one of its file paths ('%s') starts with '/'" % containedFileName)
				return False
			if containedFileName.startswith(r"\\") or containedFileName.startswith("//"):
				log.warning(r"Unsecure zip file, rejected because one of its file paths ('%s') starts with '\\'" % containedFileName)
				return False
			if os.path.splitdrive(containedFileName)[0]:
				log.warning("Unsecure zip file, rejected because one of its file paths ('%s') starts with a drive letter" % containedFileName)
				return False
			if os.path.isabs(containedFileName):
				log.warning("Unsecure zip file, rejected because one of its file paths ('%s') is absolute" % containedFileName)
				return False
			pathComponent = os.path.split(containedFileName)
			if ".." in pathComponent:
				log.warning("Unsecure zip file, rejected because one of its file paths ('%s') contains '..'" % containedFileName)	
				return False
			if "~" in pathComponent:
				log.warning("Unsecure zip file, rejected because one of its file paths ('%s') contains '~'" % containedFileName)	
				return False
		infoFileCandidates = [filename for filename in zipContent if os.path.dirname(filename)==""]
		if not infoFileCandidates:
			log.warning("Zip file structure seems wrong in '%s', no info file found." % plugin_ZIP_filename)
			return False
		isValid = False
		log.info("Looking for the zipped plugin's info file among '%s'" % infoFileCandidates)
		for infoFileName in infoFileCandidates:
			infoFile = candidateZipFile.read(infoFileName)
			log.info("Assuming the zipped plugin info file to be '%s'" % infoFileName)
			pluginName,moduleName,_ = self._getPluginNameAndModuleFromStream(StringIO(str(infoFile,encoding="utf-8")))
			if moduleName is None:
					continue
			log.info("Checking existence of the expected module '%s' in the zip file" % moduleName)
			if moduleName in zipContent or os.path.join(moduleName,"__init__.py") in zipContent:
				isValid = True
				break
		if not isValid:
			log.warning("Zip file structure seems wrong in '%s', "
							"could not match info file with the implementation of plugin '%s'." % (plugin_ZIP_filename,pluginName))
			return False
		else:
			try:
				candidateZipFile.extractall(self.install_dir)
				return True
			except Exception as e:
				log.error("Could not install plugin '%s' from zip file '%s' (exception: '%s')." % (pluginName,plugin_ZIP_filename,e))
				return False
Ejemplo n.º 6
0
	def loadPlugins(self, callback=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``.

		If a callback function is specified, call it before every load
		attempt.  The ``plugin_info`` instance is passed as an argument
		to the callback.

		Return a list of processed ``plugin_info`` instances. For
		any plugins which were not loaded, the ``error`` attribute will
		be populated with a (type, value, traceback) tuple, from
		sys.exc_info().
		"""
# 		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:
			# 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)
			# now execute the file and get its content into a
			# specific dictionnary
			candidate_globals = {"__file__":candidate_filepath+".py"}
			if "__init__" in  os.path.basename(candidate_filepath):
				sys.path.append(plugin_info.path)				
			try:
				candidateMainFile = open(candidate_filepath+".py","r")	
				exec(candidateMainFile,candidate_globals)
				processed_plugins.append(plugin_info)
			except Exception:
				log.error("Unable to execute the code in plugin: %s" % candidate_filepath, exc_info=True)
				if "__init__" in  os.path.basename(candidate_filepath):
					sys.path.remove(plugin_info.path)
				plugin_info.error = sys.exc_info()
				processed_plugins.append(plugin_info)
				continue
			
			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
			for element in candidate_globals.itervalues():
				current_category = None
				for category_name in self.categories_interfaces:
					try:
						is_correct_subclass = issubclass(element, self.categories_interfaces[category_name])
					except:
						continue
					if is_correct_subclass:
						if element is not self.categories_interfaces[category_name]:
							current_category = category_name
							break
				if current_category is not None:
					if not (candidate_infofile in self._category_file_mapping[current_category]): 
						# we found a new plugin: initialise it and search for the next one
						plugin_info.plugin_object = element()
						plugin_info.category = current_category
						self.category_mapping[current_category].append(plugin_info)
						self._category_file_mapping[current_category].append(candidate_infofile)
						current_category = None
					break

			if not (plugin_info.plugin_object or plugin_info.error):
				log.warning('No plubin object found for "%s": it may not do anything! Check that the plugin '
						+ 'extends IPlugin, and if there is an __init__ file that it imports '
						+ 'the class which extends IPlugin.', plugin_info.name)

		# 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
Ejemplo n.º 7
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)