Example #1
0
    def __installPlugin(self, archiveFilename):
        """
        Private slot to install the selected plugin.
        
        @param archiveFilename name of the plugin archive 
            file (string or QString)
        @return flag indicating success (boolean), error message
            upon failure (QString) and flag indicating a restart
            of the IDE is required (boolean)
        """
        installedPluginName = ""
        
        archive = unicode(archiveFilename)
        destination = \
            unicode(self.destinationCombo.itemData(self.destinationCombo.currentIndex())\
                .toString())
        
        # check if archive is a local url
        url = urlparse.urlparse(archive)
        if url[0].lower() == 'file':
            archive = url[2]

        # check, if the archive exists
        if not os.path.exists(archive):
            return False, \
                self.trUtf8("""<p>The archive file <b>%1</b> does not exist. """
                            """Aborting...</p>""").arg(archive), \
                False
        
        # check, if the archive is a valid zip file
        if not zipfile.is_zipfile(archive):
            return False, \
                self.trUtf8("""<p>The file <b>%1</b> is not a valid plugin """
                            """ZIP-archive. Aborting...</p>""").arg(archive), \
                False
        
        # check, if the destination is writeable
        if not os.access(destination, os.W_OK):
            return False, \
                self.trUtf8("""<p>The destination directory <b>%1</b> is not """
                            """writeable. Aborting...</p>""").arg(destination), \
                False
        
        zip = zipfile.ZipFile(archive, "r")
        
        # check, if the archive contains a valid plugin
        pluginFound = False
        pluginFileName = ""
        for name in zip.namelist():
            if self.__pluginManager.isValidPluginName(name):
                installedPluginName = name[:-3]
                pluginFound = True
                pluginFileName = name
                break
        
        if not pluginFound:
            return False, \
                self.trUtf8("""<p>The file <b>%1</b> is not a valid plugin """
                            """ZIP-archive. Aborting...</p>""").arg(archive), \
                False
        
        # parse the plugin module's plugin header
        pluginSource = zip.read(pluginFileName)
        packageName = ""
        internalPackages = []
        needsRestart = False
        for line in pluginSource.splitlines():
            if line.startswith("packageName"):
                tokens = line.split("=")
                if tokens[0].strip() == "packageName" and \
                   tokens[1].strip()[1:-1] != "__core__":
                    if tokens[1].strip()[0] in ['"', "'"]:
                        packageName = tokens[1].strip()[1:-1]
                    else:
                        if tokens[1].strip() == "None":
                            packageName = "None"
            elif line.startswith("internalPackages"):
                tokens = line.split("=")
                token = tokens[1].strip()[1:-1]    # it is a comma separated string
                internalPackages = [p.strip() for p in token.split(",")]
            elif line.startswith("needsRestart"):
                tokens = line.split("=")
                needsRestart = tokens[1].strip() == "True"
            elif line.startswith("# End-Of-Header"):
                break
        
        if not packageName:
            return False, \
                self.trUtf8("""<p>The plugin module <b>%1</b> does not contain """
                            """a 'packageName' attribute. Aborting...</p>""")\
                    .arg(pluginFileName), \
                False
        
        # check, if it is a plugin, that collides with others
        if not os.path.exists(os.path.join(destination, pluginFileName)) and \
           packageName != "None" and \
           os.path.exists(os.path.join(destination, packageName)):
            return False, \
                self.trUtf8("""<p>The plugin package <b>%1</b> exists. """
                            """Aborting...</p>""")\
                    .arg(os.path.join(destination, packageName)), \
                False
        
        if os.path.exists(os.path.join(destination, pluginFileName)) and \
           packageName != "None" and \
           not os.path.exists(os.path.join(destination, packageName)):
            return False, \
                self.trUtf8("""<p>The plugin module <b>%1</b> exists. """
                            """Aborting...</p>""")\
                    .arg(os.path.join(destination, pluginFileName)), \
                False
        
        activatePlugin = False
        if not self.__external:
            activatePlugin = \
                not self.__pluginManager.isPluginLoaded(installedPluginName) or \
                (self.__pluginManager.isPluginLoaded(installedPluginName) and \
                 self.__pluginManager.isPluginActive(installedPluginName))
            # try to unload a plugin with the same name
            self.__pluginManager.unloadPlugin(installedPluginName, destination)
        
        # uninstall existing plugin first to get clean conditions
        self.__uninstallPackage(destination, pluginFileName, packageName)
        
        # clean sys.modules
        reload_ = self.__pluginManager.removePluginFromSysModules(
            installedPluginName, packageName, internalPackages)
        
        # now do the installation
        self.__installedDirs = []
        self.__installedFiles = []
        try:
            if packageName != "None":
                packageDirs = ["%s/" % packageName, "%s\\" % packageName]
                namelist = zip.namelist()
                namelist.sort()
                tot = len(namelist)
                prog = 0
                self.progress.setMaximum(tot)
                QApplication.processEvents()
                for name in namelist:
                    self.progress.setValue(prog)
                    QApplication.processEvents()
                    prog += 1
                    if name == pluginFileName or \
                       name.startswith("%s/" % packageName) or \
                       name.startswith("%s\\" % packageName):
                        outname = name.replace("/", os.sep)
                        outname = os.path.join(destination, outname)
                        if outname.endswith("/") or outname.endswith("\\"):
                            # it is a directory entry
                            outname = outname[:-1]
                            if not os.path.exists(outname):
                                self.__makedirs(outname)
                        else:
                            # it is a file
                            d = os.path.dirname(outname)
                            if not os.path.exists(d):
                                self.__makedirs(d)
                            f = open(outname, "wb")
                            f.write(zip.read(name))
                            f.close()
                            self.__installedFiles.append(outname)
                self.progress.setValue(tot)
                # now compile user interface files
                compileUiFiles(os.path.join(destination, packageName), True)
            else:
                outname = os.path.join(destination, pluginFileName)
                f = open(outname, "wb")
                f.write(pluginSource)
                f.close()
                self.__installedFiles.append(outname)
        except os.error, why:
            self.__rollback()
            return False, \
                self.trUtf8("Error installing plugin. Reason: %1").arg(str(why)), \
                False
    def __installPlugin(self, archiveFilename):
        """
        Private slot to install the selected plugin.
        
        @param archiveFilename name of the plugin archive
            file (string)
        @return flag indicating success (boolean), error message
            upon failure (string) and flag indicating a restart
            of the IDE is required (boolean)
        """
        installedPluginName = ""
        
        archive = archiveFilename
        destination = self.destinationCombo.itemData(
            self.destinationCombo.currentIndex())
        
        # check if archive is a local url
        url = parse.urlparse(archive)
        if url[0].lower() == 'file':
            archive = url[2]

        # check, if the archive exists
        if not os.path.exists(archive):
            return False, \
                self.tr(
                    """<p>The archive file <b>{0}</b> does not exist. """
                    """Aborting...</p>""").format(archive), \
                False
        
        # check, if the archive is a valid zip file
        if not zipfile.is_zipfile(archive):
            return False, \
                self.tr(
                    """<p>The file <b>{0}</b> is not a valid plugin """
                    """ZIP-archive. Aborting...</p>""").format(archive), \
                False
        
        # check, if the destination is writeable
        if not os.access(destination, os.W_OK):
            return False, \
                self.tr(
                    """<p>The destination directory <b>{0}</b> is not """
                    """writeable. Aborting...</p>""").format(destination), \
                False
        
        zip = zipfile.ZipFile(archive, "r")
        
        # check, if the archive contains a valid plugin
        pluginFound = False
        pluginFileName = ""
        for name in zip.namelist():
            if self.__pluginManager.isValidPluginName(name):
                installedPluginName = name[:-3]
                pluginFound = True
                pluginFileName = name
                break
        
        if not pluginFound:
            return False, \
                self.tr(
                    """<p>The file <b>{0}</b> is not a valid plugin """
                    """ZIP-archive. Aborting...</p>""").format(archive), \
                False
        
        # parse the plugin module's plugin header
        pluginSource = Utilities.decode(zip.read(pluginFileName))[0]
        packageName = ""
        internalPackages = []
        needsRestart = False
        pyqtApi = 0
        doCompile = True
        for line in pluginSource.splitlines():
            if line.startswith("packageName"):
                tokens = line.split("=")
                if tokens[0].strip() == "packageName" and \
                   tokens[1].strip()[1:-1] != "__core__":
                    if tokens[1].strip()[0] in ['"', "'"]:
                        packageName = tokens[1].strip()[1:-1]
                    else:
                        if tokens[1].strip() == "None":
                            packageName = "None"
            elif line.startswith("internalPackages"):
                tokens = line.split("=")
                token = tokens[1].strip()[1:-1]
                # it is a comma separated string
                internalPackages = [p.strip() for p in token.split(",")]
            elif line.startswith("needsRestart"):
                tokens = line.split("=")
                needsRestart = tokens[1].strip() == "True"
            elif line.startswith("pyqtApi"):
                tokens = line.split("=")
                try:
                    pyqtApi = int(tokens[1].strip())
                except ValueError:
                    pass
            elif line.startswith("doNotCompile"):
                tokens = line.split("=")
                if tokens[1].strip() == "True":
                    doCompile = False
            elif line.startswith("# End-Of-Header"):
                break
        
        if not packageName:
            return False, \
                self.tr(
                    """<p>The plugin module <b>{0}</b> does not contain """
                    """a 'packageName' attribute. Aborting...</p>""")\
                .format(pluginFileName), \
                False
        
        if pyqtApi < 2:
            return False, \
                self.tr(
                    """<p>The plugin module <b>{0}</b> does not conform"""
                    """ with the PyQt v2 API. Aborting...</p>""")\
                .format(pluginFileName), \
                False
        
        # check, if it is a plugin, that collides with others
        if not os.path.exists(os.path.join(destination, pluginFileName)) and \
           packageName != "None" and \
           os.path.exists(os.path.join(destination, packageName)):
            return False, \
                self.tr("""<p>The plugin package <b>{0}</b> exists. """
                        """Aborting...</p>""")\
                    .format(os.path.join(destination, packageName)), \
                False
        
        if os.path.exists(os.path.join(destination, pluginFileName)) and \
           packageName != "None" and \
           not os.path.exists(os.path.join(destination, packageName)):
            return False, \
                self.tr("""<p>The plugin module <b>{0}</b> exists. """
                        """Aborting...</p>""")\
                    .format(os.path.join(destination, pluginFileName)), \
                False
        
        activatePlugin = False
        if not self.__external:
            activatePlugin = \
                not self.__pluginManager.isPluginLoaded(
                    installedPluginName) or \
                (self.__pluginManager.isPluginLoaded(installedPluginName) and
                 self.__pluginManager.isPluginActive(installedPluginName))
            # try to unload a plugin with the same name
            self.__pluginManager.unloadPlugin(installedPluginName)
        
        # uninstall existing plugin first to get clean conditions
        self.__uninstallPackage(destination, pluginFileName, packageName)
        
        # clean sys.modules
        reload_ = self.__pluginManager.removePluginFromSysModules(
            installedPluginName, packageName, internalPackages)
        
        # now do the installation
        self.__installedDirs = []
        self.__installedFiles = []
        try:
            if packageName != "None":
                namelist = sorted(zip.namelist())
                tot = len(namelist)
                prog = 0
                self.progress.setMaximum(tot)
                QApplication.processEvents()
                for name in namelist:
                    self.progress.setValue(prog)
                    QApplication.processEvents()
                    prog += 1
                    if name == pluginFileName or \
                       name.startswith("{0}/".format(packageName)) or \
                       name.startswith("{0}\\".format(packageName)):
                        outname = name.replace("/", os.sep)
                        outname = os.path.join(destination, outname)
                        if outname.endswith("/") or outname.endswith("\\"):
                            # it is a directory entry
                            outname = outname[:-1]
                            if not os.path.exists(outname):
                                self.__makedirs(outname)
                        else:
                            # it is a file
                            d = os.path.dirname(outname)
                            if not os.path.exists(d):
                                self.__makedirs(d)
                            f = open(outname, "wb")
                            f.write(zip.read(name))
                            f.close()
                            self.__installedFiles.append(outname)
                self.progress.setValue(tot)
                # now compile user interface files
                compileUiFiles(os.path.join(destination, packageName), True)
            else:
                outname = os.path.join(destination, pluginFileName)
                f = open(outname, "w", encoding="utf-8")
                f.write(pluginSource)
                f.close()
                self.__installedFiles.append(outname)
        except os.error as why:
            self.__rollback()
            return False, \
                self.tr(
                    "Error installing plugin. Reason: {0}").format(str(why)), \
                False
        except IOError as why:
            self.__rollback()
            return False, \
                self.tr(
                    "Error installing plugin. Reason: {0}").format(str(why)), \
                False
        except OSError as why:
            self.__rollback()
            return False, \
                self.tr(
                    "Error installing plugin. Reason: {0}").format(str(why)), \
                False
        except Exception:
            sys.stderr.write("Unspecific exception installing plugin.\n")
            self.__rollback()
            return False, \
                self.tr("Unspecific exception installing plugin."), \
                False
        
        # now compile the plugins
        if doCompile:
            dir = os.path.join(destination, packageName)
            files = os.path.join(destination, pluginFileName)
            if sys.version_info[0] == 2:
                dir = dir.encode(sys.getfilesystemencoding())
                files = files.encode(sys.getfilesystemencoding())
            os.path.join_unicode = False
            compileall.compile_dir(dir, quiet=True)
            compileall.compile_file(files, quiet=True)
            os.path.join_unicode = True
        
        if not self.__external:
            # now load and activate the plugin
            self.__pluginManager.loadPlugin(installedPluginName, destination,
                                            reload_)
            if activatePlugin:
                self.__pluginManager.activatePlugin(installedPluginName)
        
        return True, "", needsRestart