diff --git a/src/Mod/AddonManager/AddonManager.py b/src/Mod/AddonManager/AddonManager.py index c29e921ad2..353af594de 100644 --- a/src/Mod/AddonManager/AddonManager.py +++ b/src/Mod/AddonManager/AddonManager.py @@ -25,8 +25,23 @@ from __future__ import print_function -__title__="FreeCAD Addon Manager Module" -__author__ = "Yorik van Havre","Jonathan Wiedemann","Kurt Kremitzki" +import os +import re +import shutil +import stat +import sys +import tempfile + +from PySide import QtGui, QtCore +import AddonManager_rc +import FreeCADGui + +from addonmanager_utilities import translate # this needs to be as is for pylupdate +from addonmanager_workers import * +import addonmanager_utilities as utils + +__title__ = "FreeCAD Addon Manager Module" +__author__ = "Yorik van Havre", "Jonathan Wiedemann", "Kurt Kremitzki" __url__ = "http://www.freecadweb.org" """ @@ -37,58 +52,54 @@ You need a working internet connection, and optionally the GitPython package installed. """ -import os -import re -import shutil -import stat -import sys -import tempfile - -import addonmanager_utilities as utils -from addonmanager_utilities import translate # this needs to be as is for pylupdate -from addonmanager_workers import * - -def QT_TRANSLATE_NOOP(ctx,txt): - return txt - -## \defgroup ADDONMANAGER AddonManager +# \defgroup ADDONMANAGER AddonManager # \ingroup ADDONMANAGER # \brief The Addon Manager allows to install workbenches and macros made by users # @{ + +def QT_TRANSLATE_NOOP(ctx, txt): + return txt + + class CommandAddonManager: """The main Addon Manager class and FreeCAD command""" def GetResources(self): - - return {'Pixmap': 'AddonManager', - 'MenuText': QT_TRANSLATE_NOOP("Std_AddonMgr", '&Addon manager'), - 'ToolTip': QT_TRANSLATE_NOOP("Std_AddonMgr", 'Manage external workbenches and macros'), - 'Group': 'Tools'} + return {"Pixmap": "AddonManager", + "MenuText": QT_TRANSLATE_NOOP("Std_AddonMgr", "&Addon manager"), + "ToolTip": QT_TRANSLATE_NOOP("Std_AddonMgr", "Manage external workbenches and macros"), + "Group": "Tools"} def Activated(self): # display first use dialog if needed - - from PySide import QtGui - readWarning = FreeCAD.ParamGet('User parameter:Plugins/addonsRepository').GetBool('readWarning',False) + readWarning = FreeCAD.ParamGet("User parameter:Plugins/addonsRepository").GetBool("readWarning", + False) if not readWarning: - if QtGui.QMessageBox.warning(None,"FreeCAD",translate("AddonsInstaller", "The addons that can be installed here are not officially part of FreeCAD, and are not reviewed by the FreeCAD team. Make sure you know what you are installing!"), QtGui.QMessageBox.Cancel | QtGui.QMessageBox.Ok) != QtGui.QMessageBox.StandardButton.Cancel: - FreeCAD.ParamGet('User parameter:Plugins/addonsRepository').SetBool('readWarning',True) + if (QtGui.QMessageBox.warning(None, + "FreeCAD", + translate("AddonsInstaller", + "The addons that can be installed here are not " + "officially part of FreeCAD, and are not reviewed " + "by the FreeCAD team. Make sure you know what you " + "are installing!"), + QtGui.QMessageBox.Cancel | + QtGui.QMessageBox.Ok) != + QtGui.QMessageBox.StandardButton.Cancel): + FreeCAD.ParamGet("User parameter:Plugins/addonsRepository").SetBool("readWarning", + True) readWarning = True if readWarning: self.launch() def launch(self): - """Shows the Addon Manager UI""" - import FreeCADGui - from PySide import QtGui - # create the dialog - self.dialog = FreeCADGui.PySideUic.loadUi(os.path.join(os.path.dirname(__file__),"AddonManager.ui")) + self.dialog = FreeCADGui.PySideUic.loadUi(os.path.join(os.path.dirname(__file__), + "AddonManager.ui")) # cleanup the leftovers from previous runs self.repos = [] @@ -96,12 +107,13 @@ class CommandAddonManager: self.macro_repo_dir = tempfile.mkdtemp() self.doUpdate = [] self.addon_removed = False - for worker in ["update_worker","check_worker","show_worker","showmacro_worker","macro_worker","install_worker"]: - if hasattr(self,worker): - thread = getattr(self,worker) + for worker in ["update_worker", "check_worker", "show_worker", + "showmacro_worker", "macro_worker", "install_worker"]: + if hasattr(self, worker): + thread = getattr(self, worker) if thread: if thread.isFinished(): - setattr(self,worker,None) + setattr(self, worker, None) self.dialog.tabWidget.setCurrentIndex(0) # these 2 settings to prevent loading an addon description on start (let the user click something first) self.firsttime = True @@ -109,24 +121,23 @@ class CommandAddonManager: # restore window geometry and splitter state from stored state pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons") - w = pref.GetInt("WindowWidth",600) - h = pref.GetInt("WindowHeight",480) - self.dialog.resize(w,h) - sl = pref.GetInt("SplitterLeft",298) - sr = pref.GetInt("SplitterRight",274) - self.dialog.splitter.setSizes([sl,sr]) + w = pref.GetInt("WindowWidth", 600) + h = pref.GetInt("WindowHeight", 480) + self.dialog.resize(w, h) + sl = pref.GetInt("SplitterLeft", 298) + sr = pref.GetInt("SplitterRight", 274) + self.dialog.splitter.setSizes([sl, sr]) # set nice icons to everything, by theme with fallback to FreeCAD icons self.dialog.setWindowIcon(QtGui.QIcon(":/icons/AddonManager.svg")) - self.dialog.buttonExecute.setIcon(QtGui.QIcon.fromTheme("execute",QtGui.QIcon(":/icons/button_valid.svg"))) - self.dialog.buttonUninstall.setIcon(QtGui.QIcon.fromTheme("cancel",QtGui.QIcon(":/icons/edit_Cancel.svg"))) - self.dialog.buttonInstall.setIcon(QtGui.QIcon.fromTheme("download",QtGui.QIcon(":/icons/edit_OK.svg"))) + self.dialog.buttonExecute.setIcon(QtGui.QIcon.fromTheme("execute", QtGui.QIcon(":/icons/button_valid.svg"))) + self.dialog.buttonUninstall.setIcon(QtGui.QIcon.fromTheme("cancel", QtGui.QIcon(":/icons/edit_Cancel.svg"))) + self.dialog.buttonInstall.setIcon(QtGui.QIcon.fromTheme("download", QtGui.QIcon(":/icons/edit_OK.svg"))) self.dialog.buttonUpdateAll.setIcon(QtGui.QIcon(":/icons/button_valid.svg")) self.dialog.buttonConfigure.setIcon(QtGui.QIcon(":/icons/preferences-system.svg")) - self.dialog.buttonClose.setIcon(QtGui.QIcon.fromTheme("close",QtGui.QIcon(":/icons/process-stop.svg"))) - self.dialog.tabWidget.setTabIcon(0,QtGui.QIcon.fromTheme("folder",QtGui.QIcon(":/icons/folder.svg"))) - self.dialog.tabWidget.setTabIcon(1,QtGui.QIcon(":/icons/applications-python.svg")) - + self.dialog.buttonClose.setIcon(QtGui.QIcon.fromTheme("close", QtGui.QIcon(":/icons/process-stop.svg"))) + self.dialog.tabWidget.setTabIcon(0, QtGui.QIcon.fromTheme("folder", QtGui.QIcon(":/icons/folder.svg"))) + self.dialog.tabWidget.setTabIcon(1, QtGui.QIcon(":/icons/applications-python.svg")) # enable/disable stuff self.dialog.buttonExecute.setEnabled(False) @@ -161,52 +172,53 @@ class CommandAddonManager: self.dialog.exec_() def reject(self): - """called when the window has been closed""" # save window geometry and splitter state for next use pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons") - pref.SetInt("WindowWidth",self.dialog.width()) - pref.SetInt("WindowHeight",self.dialog.height()) - pref.SetInt("SplitterLeft",self.dialog.splitter.sizes()[0]) - pref.SetInt("SplitterRight",self.dialog.splitter.sizes()[1]) + pref.SetInt("WindowWidth", self.dialog.width()) + pref.SetInt("WindowHeight", self.dialog.height()) + pref.SetInt("SplitterLeft", self.dialog.splitter.sizes()[0]) + pref.SetInt("SplitterRight", self.dialog.splitter.sizes()[1]) # ensure all threads are finished before closing oktoclose = True - for worker in ["update_worker","check_worker","show_worker","showmacro_worker", - "macro_worker","install_worker"]: - if hasattr(self,worker): - thread = getattr(self,worker) + for worker in ["update_worker", "check_worker", "show_worker", "showmacro_worker", + "macro_worker", "install_worker"]: + if hasattr(self, worker): + thread = getattr(self, worker) if thread: if not thread.isFinished(): oktoclose = False # all threads have finished if oktoclose: - if (hasattr(self,"install_worker") and self.install_worker) or (hasattr(self,"addon_removed") and self.addon_removed): + if ((hasattr(self, "install_worker") and self.install_worker) or + (hasattr(self, "addon_removed") and self.addon_removed)): # display restart dialog - from PySide import QtGui,QtCore m = QtGui.QMessageBox() - m.setWindowTitle(translate("AddonsInstaller","Addon manager")) + m.setWindowTitle(translate("AddonsInstaller", "Addon manager")) m.setWindowIcon(QtGui.QIcon(":/icons/AddonManager.svg")) - m.setText(translate("AddonsInstaller","You must restart FreeCAD for changes to take effect. Press Ok to restart FreeCAD now, or Cancel to restart later.")) + m.setText(translate("AddonsInstaller", + "You must restart FreeCAD for changes to take " + "effect. Press Ok to restart FreeCAD now, or " + "Cancel to restart later.")) m.setIcon(m.Warning) m.setStandardButtons(m.Ok | m.Cancel) m.setDefaultButton(m.Cancel) ret = m.exec_() if ret == m.Ok: - shutil.rmtree(self.macro_repo_dir,onerror=self.remove_readonly) + shutil.rmtree(self.macro_repo_dir, onerror=self.remove_readonly) # restart FreeCAD after a delay to give time to this dialog to close - QtCore.QTimer.singleShot(1000,utils.restartFreeCAD) + QtCore.QTimer.singleShot(1000, utils.restart_freecad) try: - shutil.rmtree(self.macro_repo_dir,onerror=self.remove_readonly) - except: + shutil.rmtree(self.macro_repo_dir, onerror=self.remove_readonly) + except Exception: pass return True def update(self): - """updates the list of workbenches""" self.dialog.listWorkbenches.clear() @@ -220,17 +232,16 @@ class CommandAddonManager: self.update_worker.start() def check_updates(self): - "checks every installed addon for available updates" pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons") - if pref.GetBool("AutoCheck",False) and not self.doUpdate: - if hasattr(self,"check_worker"): + if pref.GetBool("AutoCheck", False) and not self.doUpdate: + if hasattr(self, "check_worker"): thread = self.check_worker if thread: if not thread.isFinished(): return - self.dialog.buttonUpdateAll.setText(translate("AddonsInstaller","Checking for updates...")) + self.dialog.buttonUpdateAll.setText(translate("AddonsInstaller", "Checking for updates...")) self.check_worker = CheckWBWorker(self.repos) self.check_worker.mark.connect(self.mark) self.check_worker.enable.connect(self.enable_updates) @@ -238,44 +249,43 @@ class CommandAddonManager: self.check_worker.start() def apply_updates(self): - """apply all available updates""" if self.doUpdate: self.install(self.doUpdate) self.dialog.buttonUpdateAll.setEnabled(False) - def enable_updates(self,num): - + def enable_updates(self, num): """enables the update button""" if num: - self.dialog.buttonUpdateAll.setText(translate("AddonsInstaller","Apply")+" "+str(num)+" "+translate("AddonsInstaller","update(s)")) + self.dialog.buttonUpdateAll.setText(translate("AddonsInstaller", "Apply") + + " " + str(num) + " " + + translate("AddonsInstaller", "update(s)")) self.dialog.buttonUpdateAll.setEnabled(True) else: - self.dialog.buttonUpdateAll.setText(translate("AddonsInstaller","No update available")) + self.dialog.buttonUpdateAll.setText(translate("AddonsInstaller", "No update available")) self.dialog.buttonUpdateAll.setEnabled(False) def add_addon_repo(self, addon_repo): - """adds a workbench to the list""" - from PySide import QtGui self.repos.append(addon_repo) addonicon = self.get_icon(addon_repo[0]) if addon_repo[2] > 0: - item = QtGui.QListWidgetItem(addonicon,str(addon_repo[0]) + str(" ("+translate("AddonsInstaller","Installed")+")")) - item.setForeground(QtGui.QBrush(QtGui.QColor(0,182,41))) + item = QtGui.QListWidgetItem(addonicon, + str(addon_repo[0]) + + str(" (" + + translate("AddonsInstaller", "Installed") + + ")")) + item.setForeground(QtGui.QBrush(QtGui.QColor(0, 182, 41))) self.dialog.listWorkbenches.addItem(item) else: - self.dialog.listWorkbenches.addItem(QtGui.QListWidgetItem(addonicon,str(addon_repo[0]))) - - def get_icon(self,repo): + self.dialog.listWorkbenches.addItem(QtGui.QListWidgetItem(addonicon, str(addon_repo[0]))) + def get_icon(self, repo): """returns an icon for a repo""" - import AddonManager_rc - from PySide import QtGui path = ":/icons/" + repo + "_workbench_icon.svg" if QtCore.QFile.exists(path): addonicon = QtGui.QIcon(path) @@ -286,7 +296,6 @@ class CommandAddonManager: return addonicon def show_information(self, label): - """shows text in the information pane""" self.dialog.description.setText(label) @@ -295,8 +304,7 @@ class CommandAddonManager: else: self.dialog.listMacros.setFocus() - def show(self,idx): - + def show(self, idx): """loads information of a given workbench""" # this function is triggered also when the list is populated, prevent that here @@ -306,7 +314,7 @@ class CommandAddonManager: return if self.repos and idx >= 0: - if hasattr(self,"show_worker"): + if hasattr(self, "show_worker"): # kill existing show worker (might still be busy loading images...) if self.show_worker: self.show_worker.exit() @@ -323,8 +331,7 @@ class CommandAddonManager: self.dialog.buttonInstall.setEnabled(True) self.dialog.buttonUninstall.setEnabled(True) - def show_macro(self,idx): - + def show_macro(self, idx): """loads information of a given macro""" # this function is triggered when the list is populated, prevent that here @@ -334,7 +341,7 @@ class CommandAddonManager: return if self.macros and idx >= 0: - if hasattr(self,"showmacro_worker"): + if hasattr(self, "showmacro_worker"): if self.showmacro_worker: if not self.showmacro_worker.isFinished(): self.showmacro_worker.exit() @@ -351,8 +358,7 @@ class CommandAddonManager: else: self.dialog.buttonExecute.setEnabled(False) - def switchtab(self,idx): - + def switchtab(self, idx): """does what needs to be done when switching tabs""" if idx == 1: @@ -367,13 +373,11 @@ class CommandAddonManager: self.dialog.listMacros.setCurrentRow(0) def update_repos(self, repos): - """this function allows threads to update the main list of workbenches""" self.repos = repos def add_macro(self, macro): - """adds a macro to the list""" if macro.name: @@ -382,10 +386,8 @@ class CommandAddonManager: old_macro = self.macros[self.macros.index(macro)] utils.update_macro_details(old_macro, macro) else: - from PySide import QtGui self.macros.append(macro) - import AddonManager_rc - path = ":/icons/" + macro.name.replace(" ","_") + "_macro_icon.svg" + path = ":/icons/" + macro.name.replace(" ", "_") + "_macro_icon.svg" if QtCore.QFile.exists(path): addonicon = QtGui.QIcon(path) else: @@ -393,14 +395,13 @@ class CommandAddonManager: if addonicon.isNull(): addonicon = QtGui.QIcon(":/icons/document-python.svg") if macro.is_installed(): - item = QtGui.QListWidgetItem(addonicon, macro.name + str(' (Installed)')) - item.setForeground(QtGui.QBrush(QtGui.QColor(0,182,41))) + item = QtGui.QListWidgetItem(addonicon, macro.name + str(" (Installed)")) + item.setForeground(QtGui.QBrush(QtGui.QColor(0, 182, 41))) self.dialog.listMacros.addItem(item) else: - self.dialog.listMacros.addItem(QtGui.QListWidgetItem(addonicon,macro.name)) - - def install(self,repos=None): + self.dialog.listMacros.addItem(QtGui.QListWidgetItem(addonicon, macro.name)) + def install(self, repos=None): """installs a workbench or macro""" if self.dialog.tabWidget.currentIndex() == 0: @@ -409,13 +410,13 @@ class CommandAddonManager: if repos: idx = [] for repo in repos: - for i,r in enumerate(self.repos): + for i, r in enumerate(self.repos): if r[0] == repo: idx.append(i) else: idx = self.dialog.listWorkbenches.currentRow() - if idx != None: - if hasattr(self,"install_worker") and self.install_worker: + if idx is not None: + if hasattr(self, "install_worker") and self.install_worker: if self.install_worker.isRunning(): return self.install_worker = InstallWorker(self.repos, idx) @@ -428,15 +429,16 @@ class CommandAddonManager: # Tab "Macros". macro = self.macros[self.dialog.listMacros.currentRow()] if utils.install_macro(macro, self.macro_repo_dir): - self.dialog.description.setText(translate("AddonsInstaller", "Macro successfully installed. The macro is now available from the Macros dialog.")) + self.dialog.description.setText(translate("AddonsInstaller", + "Macro successfully installed. The macro is " + "now available from the Macros dialog.")) else: self.dialog.description.setText(translate("AddonsInstaller", "Unable to install")) def show_progress_bar(self, state): - """shows or hides the progress bar""" - if state == True: + if state: self.dialog.tabWidget.setEnabled(False) self.dialog.buttonInstall.setEnabled(False) self.dialog.buttonUninstall.setEnabled(False) @@ -453,10 +455,8 @@ class CommandAddonManager: self.dialog.listMacros.setFocus() def executemacro(self): - """executes a selected macro""" - import FreeCADGui if self.dialog.tabWidget.currentIndex() == 1: # Tab "Macros". macro = self.macros[self.dialog.listMacros.currentRow()] @@ -465,7 +465,7 @@ class CommandAddonManager: return macro_path = os.path.join(FreeCAD.getUserMacroDir(True), macro.filename) if os.path.exists(macro_path): - macro_path = macro_path.replace("\\","/") + macro_path = macro_path.replace("\\", "/") FreeCADGui.open(str(macro_path)) self.dialog.hide() @@ -481,7 +481,6 @@ class CommandAddonManager: func(path) def remove(self): - """uninstalls a macro or workbench""" if self.dialog.tabWidget.currentIndex() == 0: @@ -492,7 +491,8 @@ class CommandAddonManager: clonedir = moddir + os.sep + self.repos[idx][0] if os.path.exists(clonedir): shutil.rmtree(clonedir, onerror=self.remove_readonly) - self.dialog.description.setText(translate("AddonsInstaller", "Addon successfully removed. Please restart FreeCAD")) + self.dialog.description.setText(translate("AddonsInstaller", + "Addon successfully removed. Please restart FreeCAD")) else: self.dialog.description.setText(translate("AddonsInstaller", "Unable to remove this addon")) @@ -500,60 +500,59 @@ class CommandAddonManager: # Tab "Macros". macro = self.macros[self.dialog.listMacros.currentRow()] if utils.remove_macro(macro): - self.dialog.description.setText(translate('AddonsInstaller', 'Macro successfully removed.')) + self.dialog.description.setText(translate("AddonsInstaller", "Macro successfully removed.")) else: - self.dialog.description.setText(translate('AddonsInstaller', 'Macro could not be removed.')) + self.dialog.description.setText(translate("AddonsInstaller", "Macro could not be removed.")) self.update_status(soft=True) - self.addon_removed = True # A value to trigger the restart message on dialog close - - def mark_recompute(self,addon): + self.addon_removed = True # A value to trigger the restart message on dialog close + def mark_recompute(self, addon): """marks an addon in the list as installed but needs recompute""" for i in range(self.dialog.listWorkbenches.count()): txt = self.dialog.listWorkbenches.item(i).text().strip() - if txt.endswith(" ("+translate("AddonsInstaller","Installed")+")"): + if txt.endswith(" ("+translate("AddonsInstaller", "Installed")+")"): txt = txt[:-12] - elif txt.endswith(" ("+translate("AddonsInstaller","Update available")+")"): + elif txt.endswith(" ("+translate("AddonsInstaller", "Update available")+")"): txt = txt[:-19] if txt == addon: - from PySide import QtGui - self.dialog.listWorkbenches.item(i).setText(txt+" ("+translate("AddonsInstaller","Restart required")+")") + self.dialog.listWorkbenches.item(i).setText(txt + " (" + + translate("AddonsInstaller", + "Restart required") + + ")") self.dialog.listWorkbenches.item(i).setIcon(QtGui.QIcon(":/icons/edit-undo.svg")) - def update_status(self,soft=False): - + def update_status(self, soft=False): """Updates the list of workbenches/macros. If soft is true, items are not recreated (and therefore display text isn't triggered)" """ moddir = FreeCAD.getUserAppDataDir() + os.sep + "Mod" - from PySide import QtGui if soft: for i in range(self.dialog.listWorkbenches.count()): txt = self.dialog.listWorkbenches.item(i).text().strip() ext = "" - if txt.endswith(" ("+translate("AddonsInstaller","Installed")+")"): + if txt.endswith(" ("+translate("AddonsInstaller", "Installed")+")"): txt = txt[:-12] - ext = " ("+translate("AddonsInstaller","Installed")+")" - elif txt.endswith(" ("+translate("AddonsInstaller","Update available")+")"): + ext = " ("+translate("AddonsInstaller", "Installed")+")" + elif txt.endswith(" ("+translate("AddonsInstaller", "Update available")+")"): txt = txt[:-19] - ext = " ("+translate("AddonsInstaller","Update available")+")" - elif txt.endswith(" ("+translate("AddonsInstaller","Restart required")+")"): + ext = " ("+translate("AddonsInstaller", "Update available")+")" + elif txt.endswith(" ("+translate("AddonsInstaller", "Restart required")+")"): txt = txt[:-19] - ext = " ("+translate("AddonsInstaller","Restart required")+")" - if os.path.exists(os.path.join(moddir,txt)): + ext = " ("+translate("AddonsInstaller", "Restart required")+")" + if os.path.exists(os.path.join(moddir, txt)): self.dialog.listWorkbenches.item(i).setText(txt+ext) else: self.dialog.listWorkbenches.item(i).setText(txt) self.dialog.listWorkbenches.item(i).setIcon(self.get_icon(txt)) for i in range(self.dialog.listMacros.count()): txt = self.dialog.listMacros.item(i).text().strip() - if txt.endswith(" ("+translate("AddonsInstaller","Installed")+")"): + if txt.endswith(" ("+translate("AddonsInstaller", "Installed")+")"): txt = txt[:-12] - elif txt.endswith(" ("+translate("AddonsInstaller","Update available")+")"): + elif txt.endswith(" ("+translate("AddonsInstaller", "Update available")+")"): txt = txt[:-19] - if os.path.exists(os.path.join(moddir,txt)): + if os.path.exists(os.path.join(moddir, txt)): self.dialog.listMacros.item(i).setText(txt+ext) else: self.dialog.listMacros.item(i).setText(txt) @@ -562,69 +561,71 @@ class CommandAddonManager: self.dialog.listWorkbenches.clear() self.dialog.listMacros.clear() for wb in self.repos: - if os.path.exists(os.path.join(moddir,wb[0])): - self.dialog.listWorkbenches.addItem(QtGui.QListWidgetItem(QtGui.QIcon(":/icons/button_valid.svg"),str(wb[0]) + " ("+translate("AddonsInstaller","Installed")+")")) + if os.path.exists(os.path.join(moddir, wb[0])): + self.dialog.listWorkbenches.addItem( + QtGui.QListWidgetItem(QtGui.QIcon(":/icons/button_valid.svg"), + str(wb[0]) + " (" + + translate("AddonsInstaller", "Installed") + ")")) wb[2] = 1 else: - self.dialog.listWorkbenches.addItem(QtGui.QListWidgetItem(QtGui.QIcon(":/icons/document-python.svg"),str(wb[0]))) + self.dialog.listWorkbenches.addItem( + QtGui.QListWidgetItem(QtGui.QIcon(":/icons/document-python.svg"), str(wb[0]))) wb[2] = 0 for macro in self.macros: if macro.is_installed(): self.dialog.listMacros.addItem(item) else: - self.dialog.listMacros.addItem(QtGui.QListWidgetItem(QtGui.QIcon(":/icons/document-python.svg"),+macro.name)) - - def mark(self,repo): + self.dialog.listMacros.addItem( + QtGui.QListWidgetItem(QtGui.QIcon(":/icons/document-python.svg"), macro.name)) + def mark(self, repo): """mark a workbench as updatable""" - from PySide import QtGui for i in range(self.dialog.listWorkbenches.count()): w = self.dialog.listWorkbenches.item(i) if (w.text() == str(repo)) or w.text().startswith(str(repo)+" "): - w.setText(str(repo) + str(" ("+translate("AddonsInstaller","Update available")+")")) - w.setForeground(QtGui.QBrush(QtGui.QColor(182,90,0))) - if not repo in self.doUpdate: + w.setText(str(repo) + str(" ("+translate("AddonsInstaller", "Update available")+")")) + w.setForeground(QtGui.QBrush(QtGui.QColor(182, 90, 0))) + if repo not in self.doUpdate: self.doUpdate.append(repo) def show_config(self): - """shows the configuration dialog""" - import FreeCADGui - from PySide import QtGui - self.config = FreeCADGui.PySideUic.loadUi(os.path.join(os.path.dirname(__file__),"AddonManagerOptions.ui")) + self.config = FreeCADGui.PySideUic.loadUi(os.path.join(os.path.dirname(__file__), "AddonManagerOptions.ui")) # restore stored values pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons") - self.config.checkUpdates.setChecked(pref.GetBool("AutoCheck",False)) - self.config.customRepositories.setPlainText(pref.GetString("CustomRepositories","")) - self.config.radioButtonNoProxy.setChecked(pref.GetBool("NoProxyCheck",True)) - self.config.radioButtonSystemProxy.setChecked(pref.GetBool("SystemProxyCheck",False)) - self.config.radioButtonUserProxy.setChecked(pref.GetBool("UserProxyCheck",False)) - self.config.userProxy.setPlainText(pref.GetString("ProxyUrl","")) + self.config.checkUpdates.setChecked(pref.GetBool("AutoCheck", False)) + self.config.customRepositories.setPlainText(pref.GetString("CustomRepositories", "")) + self.config.radioButtonNoProxy.setChecked(pref.GetBool("NoProxyCheck", True)) + self.config.radioButtonSystemProxy.setChecked(pref.GetBool("SystemProxyCheck", False)) + self.config.radioButtonUserProxy.setChecked(pref.GetBool("UserProxyCheck", False)) + self.config.userProxy.setPlainText(pref.GetString("ProxyUrl", "")) # center the dialog over the Addon Manager - self.config.move(self.dialog.frameGeometry().topLeft() + self.dialog.rect().center() - self.config.rect().center()) + self.config.move(self.dialog.frameGeometry().topLeft() + + self.dialog.rect().center() - + self.config.rect().center()) ret = self.config.exec_() if ret: # OK button has been pressed - pref.SetBool("AutoCheck",self.config.checkUpdates.isChecked()) - pref.SetString("CustomRepositories",self.config.customRepositories.toPlainText()) - pref.SetBool("NoProxyCheck",self.config.radioButtonNoProxy.isChecked()) - pref.SetBool("SystemProxyCheck",self.config.radioButtonSystemProxy.isChecked()) - pref.SetBool("UserProxyCheck",self.config.radioButtonUserProxy.isChecked()) - pref.SetString("ProxyUrl",self.config.userProxy.toPlainText()) + pref.SetBool("AutoCheck", self.config.checkUpdates.isChecked()) + pref.SetString("CustomRepositories", self.config.customRepositories.toPlainText()) + pref.SetBool("NoProxyCheck", self.config.radioButtonNoProxy.isChecked()) + pref.SetBool("SystemProxyCheck", self.config.radioButtonSystemProxy.isChecked()) + pref.SetBool("UserProxyCheck", self.config.radioButtonUserProxy.isChecked()) + pref.SetString("ProxyUrl", self.config.userProxy.toPlainText()) -def check_updates(addon_name,callback): +def check_updates(addon_name, callback): """Checks for updates for a given addon""" oname = "update_checker_"+addon_name - setattr(FreeCAD,oname,CheckSingleWorker(addon_name)) - getattr(FreeCAD,oname).updateAvailable.connect(callback) - getattr(FreeCAD,oname).start() + setattr(FreeCAD, oname, CheckSingleWorker(addon_name)) + getattr(FreeCAD, oname).updateAvailable.connect(callback) + getattr(FreeCAD, oname).start() -## @} +# @} diff --git a/src/Mod/AddonManager/addonmanager_macro.py b/src/Mod/AddonManager/addonmanager_macro.py index e7039ff500..0e6d4800b3 100644 --- a/src/Mod/AddonManager/addonmanager_macro.py +++ b/src/Mod/AddonManager/addonmanager_macro.py @@ -21,18 +21,50 @@ #* * #*************************************************************************** -## @package AddonManager_macro -# \ingroup ADDONMANAGER -# \brief Unified handler for FreeCAD macros that can be obtained from different sources - import os import re import sys + +from PySide import QtCore, QtGui + import FreeCAD +import FreeCADGui from addonmanager_utilities import translate from addonmanager_utilities import urlopen +try: + from HTMLParser import HTMLParser + unescape = HTMLParser().unescape +except ImportError: + from html import unescape + +try: + import StringIO as io + _stringio = io.StringIO +except ImportError: # StringIO is not available with python3 + import io + _stringio = io.BytesIO + +have_git = False +try: + import git + have_git = True +except ImportError: + pass + +have_zip = False +try: + import zipfile + have_zip = True +except ImportError: + pass + +# @package AddonManager_macro +# \ingroup ADDONMANAGER +# \brief Unified handler for FreeCAD macros that can be obtained from different sources +# @{ + class Macro(object): """This class provides a unified way to handle macros coming from different sources""" @@ -41,11 +73,11 @@ class Macro(object): self.name = name self.on_wiki = False self.on_git = False - self.desc = '' - self.code = '' - self.url = '' - self.version = '' - self.src_filename = '' + self.desc = "" + self.code = "" + self.url = "" + self.version = "" + self.src_filename = "" self.other_files = [] self.parsed = False @@ -56,13 +88,13 @@ class Macro(object): def filename(self): if self.on_git: return os.path.basename(self.src_filename) - return (self.name + '.FCMacro').replace(' ', '_') + return (self.name + ".FCMacro").replace(" ", "_") def is_installed(self): if self.on_git and not self.src_filename: return False return (os.path.exists(os.path.join(FreeCAD.getUserMacroDir(True), self.filename)) - or os.path.exists(os.path.join(FreeCAD.getUserMacroDir(True), 'Macro_' + self.filename))) + or os.path.exists(os.path.join(FreeCAD.getUserMacroDir(True), "Macro_" + self.filename))) def fill_details_from_file(self, filename): with open(filename) as f: @@ -73,22 +105,22 @@ class Macro(object): re_url = re.compile(r"^__Web__\s*=\s*(['\"])(.*)\1") re_version = re.compile(r"^__Version__\s*=\s*(['\"])(.*)\1") re_files = re.compile(r"^__Files__\s*=\s*(['\"])(.*)\1") - for l in f.readlines(): - match = re.match(re_desc, l) + for line in f.readlines(): + match = re.match(re_desc, line) if match: self.desc = match.group(2) number_of_required_fields -= 1 - match = re.match(re_url, l) + match = re.match(re_url, line) if match: self.url = match.group(2) number_of_required_fields -= 1 - match = re.match(re_version, l) + match = re.match(re_version, line) if match: self.version = match.group(2) number_of_required_fields -= 1 - match = re.match(re_files, l) + match = re.match(re_files, line) if match: - self.other_files = [of.strip() for of in match.group(2).split(',')] + self.other_files = [of.strip() for of in match.group(2).split(",")] number_of_required_fields -= 1 if number_of_required_fields <= 0: break @@ -98,75 +130,65 @@ class Macro(object): def fill_details_from_wiki(self, url): code = "" - try: - u = urlopen(url) - except: - print("AddonManager: Debug: unable to open URL",url) - return - if u is None : - print("AddonManager: Debug: connection is lost (proxy setting changed?)",url) + u = urlopen(url) + if u is None: + print("AddonManager: Debug: connection is lost (proxy setting changed?)", url) return p = u.read() if sys.version_info.major >= 3 and isinstance(p, bytes): - p = p.decode('utf-8') + p = p.decode("utf-8") u.close() # check if the macro page has its code hosted elsewhere, download if needed if "rawcodeurl" in p: - rawcodeurl = re.findall("rawcodeurl.*?href=\"(http.*?)\">",p) + rawcodeurl = re.findall("rawcodeurl.*?href=\"(http.*?)\">", p) if rawcodeurl: rawcodeurl = rawcodeurl[0] - try: - u2 = urlopen(rawcodeurl) - except: - print("AddonManager: Debug: unable to open URL",rawcodeurl) + u2 = urlopen(rawcodeurl) + if u2 is None: + print("AddonManager: Debug: unable to open URL", rawcodeurl) return # code = u2.read() # github is slow to respond... We need to use this trick below response = "" block = 8192 - #expected = int(u2.headers['content-length']) - while 1: - #print("expected:",expected,"got:",len(response)) + # expected = int(u2.headers["content-length"]) + while True: + # print("expected:", expected, "got:", len(response)) data = u2.read(block) if not data: break if sys.version_info.major >= 3 and isinstance(data, bytes): - data = data.decode('utf-8') + data = data.decode("utf-8") response += data if response: code = response u2.close() if not code: - code = re.findall('
(.*?)<\/pre>', p.replace('\n', '--endl--'))
+ code = re.findall(r"(.*?)
", p.replace("\n", "--endl--"))
if code:
# code = code[0]
# take the biggest code block
code = sorted(code, key=len)[-1]
- code = code.replace('--endl--', '\n')
+ code = code.replace("--endl--", "\n")
else:
FreeCAD.Console.PrintWarning(translate("AddonsInstaller", "Unable to fetch the code of this macro."))
# Clean HTML escape codes.
- try:
- from HTMLParser import HTMLParser
- except ImportError:
- from html.parser import HTMLParser
if sys.version_info.major < 3:
- code = code.decode('utf8')
- try:
- code = HTMLParser().unescape(code)
- code = code.replace(b'\xc2\xa0'.decode("utf-8"), ' ')
- except:
- FreeCAD.Console.PrintWarning(translate("AddonsInstaller", "Unable to clean macro code") + ": "+ code + '\n')
+ code = code.decode("utf8")
+ code = unescape(code)
+ code = code.replace(b"\xc2\xa0".decode("utf-8"), " ")
if sys.version_info.major < 3:
- code = code.encode('utf8')
- desc = re.findall("(.*?)<\/td>", p.replace('\n', ' '))
+ code = code.encode("utf8")
+ desc = re.findall(r" (.*?) ", p.replace("\n", " "))
if desc:
desc = desc[0]
else:
- FreeCAD.Console.PrintWarning(translate("AddonsInstaller", "Unable to retrieve a description for this macro."))
+ FreeCAD.Console.PrintWarning(translate("AddonsInstaller",
+ "Unable to retrieve a description for this macro."))
desc = "No description available"
self.desc = desc
self.url = url
self.code = code
self.parsed = True
+# @}
diff --git a/src/Mod/AddonManager/addonmanager_utilities.py b/src/Mod/AddonManager/addonmanager_utilities.py
index c1d6d081da..e86fff54c2 100644
--- a/src/Mod/AddonManager/addonmanager_utilities.py
+++ b/src/Mod/AddonManager/addonmanager_utilities.py
@@ -21,16 +21,26 @@
#* *
#***************************************************************************
-import os
-import sys
import codecs
-import FreeCAD
-import shutil
+import os
import re
+import shutil
+import sys
+import ctypes
-## @package AddonManager_utilities
-# \ingroup ADDONMANAGER
-# \brief Utilities to work across different platforms, providers and python versions
+if sys.version_info.major < 3:
+ import urllib2
+ from urllib2 import URLError
+ from urlparse import urlparse
+else:
+ import urllib.request as urllib2
+ from urllib.error import URLError
+ from urllib.parse import urlparse
+
+from PySide import QtGui, QtCore
+
+import FreeCAD
+import FreeCADGui
# check for SSL support
@@ -44,14 +54,17 @@ else:
ssl_ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
except AttributeError:
pass
-
-
+
+# @package AddonManager_utilities
+# \ingroup ADDONMANAGER
+# \brief Utilities to work across different platforms, providers and python versions
+# @{
+
+
def translate(context, text, disambig=None):
-
"Main translation function"
-
- from PySide import QtGui
+
try:
_encoding = QtGui.QApplication.UnicodeUTF8
except AttributeError:
@@ -61,18 +74,16 @@ def translate(context, text, disambig=None):
def symlink(source, link_name):
-
"creates a symlink of a file, if possible"
if os.path.exists(link_name) or os.path.lexists(link_name):
- #print("macro already exists")
+ # print("macro already exists")
pass
else:
os_symlink = getattr(os, "symlink", None)
if callable(os_symlink):
os_symlink(source, link_name)
else:
- import ctypes
csl = ctypes.windll.kernel32.CreateSymbolicLinkW
csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32)
csl.restype = ctypes.c_ubyte
@@ -85,59 +96,48 @@ def symlink(source, link_name):
def urlopen(url):
-
"""Opens an url with urllib2"""
timeout = 5
- if sys.version_info.major < 3:
- import urllib2
- else:
- import urllib.request as urllib2
-
# Proxy an ssl configuration
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
- if pref.GetBool("NoProxyCheck",True):
- proxies = {}
+ if pref.GetBool("NoProxyCheck", True):
+ proxies = {}
else:
- if pref.GetBool("SystemProxyCheck",False):
- proxy = urllib2.getproxies()
- proxies = {"http": proxy.get('http'),"https": proxy.get('http')}
- elif pref.GetBool("UserProxyCheck",False):
- proxy = pref.GetString("ProxyUrl","")
- proxies = {"http": proxy, "https": proxy}
+ if pref.GetBool("SystemProxyCheck", False):
+ proxy = urllib2.getproxies()
+ proxies = {"http": proxy.get('http'), "https": proxy.get('http')}
+ elif pref.GetBool("UserProxyCheck", False):
+ proxy = pref.GetString("ProxyUrl", "")
+ proxies = {"http": proxy, "https": proxy}
if ssl_ctx:
handler = urllib2.HTTPSHandler(context=ssl_ctx)
else:
handler = {}
- proxy_support = urllib2.ProxyHandler(proxies)
+ proxy_support = urllib2.ProxyHandler(proxies)
opener = urllib2.build_opener(proxy_support, handler)
- urllib2.install_opener(opener)
-
+ urllib2.install_opener(opener)
+
# Url opening
req = urllib2.Request(url,
- headers={'User-Agent' : "Magic Browser"})
+ headers={'User-Agent': "Magic Browser"})
try:
u = urllib2.urlopen(req, timeout=timeout)
- except:
+ except Exception:
return None
else:
return u
-def getserver(url):
+def getserver(url):
"""returns the server part of an url"""
- if sys.version_info.major < 3:
- from urlparse import urlparse
- else:
- from urllib.parse import urlparse
return '{uri.scheme}://{uri.netloc}/'.format(uri=urlparse(url))
def update_macro_details(old_macro, new_macro):
-
"""Update a macro with information from another one
Update a macro with information from another one, supposedly the same but
@@ -157,7 +157,6 @@ def update_macro_details(old_macro, new_macro):
def install_macro(macro, macro_repo_dir):
-
"""Install a macro and all its related files
Returns True if the macro was installed correctly.
@@ -201,7 +200,6 @@ def install_macro(macro, macro_repo_dir):
def remove_macro(macro):
-
"""Remove a macro and all its related files
Returns True if the macro was removed correctly.
@@ -231,7 +229,6 @@ def remove_macro(macro):
def remove_directory_if_empty(dir):
-
"""Remove the directory if it is empty
Directory FreeCAD.getUserMacroDir(True) will not be removed even if empty.
@@ -243,21 +240,17 @@ def remove_directory_if_empty(dir):
os.rmdir(dir)
-def restartFreeCAD():
-
+def restart_freecad():
"Shuts down and restarts FreeCAD"
- import FreeCADGui
- from PySide import QtGui,QtCore
args = QtGui.QApplication.arguments()[1:]
if FreeCADGui.getMainWindow().close():
- QtCore.QProcess.startDetached(QtGui.QApplication.applicationFilePath(),args)
+ QtCore.QProcess.startDetached(QtGui.QApplication.applicationFilePath(), args)
-def getZipUrl(baseurl):
-
+def get_zip_url(baseurl):
"Returns the location of a zip file from a repo, if available"
-
+
url = getserver(baseurl).strip("/")
if url.endswith("github.com"):
return baseurl+"/archive/master.zip"
@@ -266,59 +259,54 @@ def getZipUrl(baseurl):
reponame = baseurl.strip("/").split("/")[-1]
return baseurl+"/-/archive/master/"+reponame+"-master.zip"
else:
- print("Debug: addonmanager_utilities.getZipUrl: Unknown git host:",url)
+ print("Debug: addonmanager_utilities.get_zip_url: Unknown git host:", url)
return None
-def getReadmeUrl(url):
-
+def get_readme_url(url):
"Returns the location of a readme file"
if "github" in url or "framagit" in url or "gitlab" in url:
return url+"/raw/master/README.md"
else:
- print("Debug: addonmanager_utilities.getReadmeUrl: Unknown git host:",url)
+ print("Debug: addonmanager_utilities.get_readme_url: Unknown git host:", url)
return None
-def getDescRegex(url):
-
+def get_desc_regex(url):
"""Returns a regex string that extracts a WB description to be displayed in the description
panel of the Addon manager, if the README could not be found"""
if "github" in url:
- return ""
- print("Debug: addonmanager_utilities.getDescRegex: Unknown git host:",url)
+ return r''
+ print("Debug: addonmanager_utilities.get_desc_regex: Unknown git host:", url)
return None
-def getReadmeHTMLUrl(url):
-
- "Returns the location of a html file containing readme"
+def get_readme_html_url(url):
+ """Returns the location of a html file containing readme"""
- if ("github" in url):
- return url+"/blob/master/README.md"
+ if "github" in url:
+ return url + "/blob/master/README.md"
else:
- print("Debug: addonmanager_utilities.getReadmeUrl: Unknown git host:",url)
+ print("Debug: addonmanager_utilities.get_readme_html_url: Unknown git host:", url)
return None
-def getReadmeRegex(url):
-
+def get_readme_regex(url):
"""Return a regex string that extracts the contents to be displayed in the description
panel of the Addon manager, from raw HTML data (the readme's html rendering usually)"""
-
+
if ("github" in url):
return "(.*?)"
else:
- print("Debug: addonmanager_utilities.getReadmeRegex: Unknown git host:",url)
+ print("Debug: addonmanager_utilities.get_readme_regex: Unknown git host:", url)
return None
-def fixRelativeLinks(text, base_url):
-
+def fix_relative_links(text, base_url):
"""Replace markdown image relative links with
absolute ones using the base URL"""
@@ -333,3 +321,5 @@ def fixRelativeLinks(text, base_url):
print("Debug: replaced " + link + " with " + newlink)
new_text = new_text + '\n' + line
return new_text
+
+# @}
diff --git a/src/Mod/AddonManager/addonmanager_workers.py b/src/Mod/AddonManager/addonmanager_workers.py
index 3afaf2e08e..26e2ede351 100644
--- a/src/Mod/AddonManager/addonmanager_workers.py
+++ b/src/Mod/AddonManager/addonmanager_workers.py
@@ -27,17 +27,40 @@ import shutil
import stat
import sys
import tempfile
-import FreeCAD
from PySide import QtCore
+import FreeCAD
+
import addonmanager_utilities as utils
-from addonmanager_utilities import translate # this needs to be as is for pylupdate
+from addonmanager_utilities import translate # this needs to be as is for pylupdate
from addonmanager_macro import Macro
-## @package AddonManager_workers
+have_git = False
+try:
+ import git
+ have_git = True
+except ImportError:
+ pass
+
+have_zip = False
+try:
+ import zipfile
+ have_zip = True
+except ImportError:
+ pass
+
+have_markdown = False
+try:
+ import markdown
+ have_markdown = True
+except ImportError:
+ pass
+
+# @package AddonManager_workers
# \ingroup ADDONMANAGER
# \brief Multithread workers for the addon manager
+# @{
# Blacklisted addons
macros_blacklist = []
@@ -48,9 +71,9 @@ obsolete = []
# These addons will print an additional message informing the user Python2 only
py2only = []
-NOGIT = False # for debugging purposes, set this to True to always use http downloads
+NOGIT = False # for debugging purposes, set this to True to always use http downloads
-NOMARKDOWN = False # for debugging purposes, set this to True to disable Markdown lib
+NOMARKDOWN = False # for debugging purposes, set this to True to disable Markdown lib
"""Multithread workers for the Addon Manager"""
@@ -68,7 +91,6 @@ class UpdateWorker(QtCore.QThread):
QtCore.QThread.__init__(self)
def run(self):
-
"populates the list of addons"
self.progressbar_show.emit(True)
@@ -82,17 +104,17 @@ class UpdateWorker(QtCore.QThread):
p = p.decode("utf-8")
u.close()
hit = re.findall(r'"obsolete"[^\{]*?{[^\{]*?"Mod":\[(?P[^\[\]]+?)\]}',
- p.replace('\n', '').replace(' ', ''))
+ p.replace("\n", "").replace(" ", ""))
if hit:
- obsolete = hit[0].replace('"', '').split(',')
+ obsolete = hit[0].replace('"', "").split(",")
hit = re.findall(r'"blacklisted"[^\{]*?{[^\{]*?"Macro":\[(?P[^\[\]]+?)\]}',
- p.replace('\n', '').replace(' ', ''))
+ p.replace("\n", "").replace(" ", ""))
if hit:
- macros_blacklist = hit[0].replace('"', '').split(',')
+ macros_blacklist = hit[0].replace('"', "").split(",")
hit = re.findall(r'"py2only"[^\{]*?{[^\{]*?"Mod":\[(?P[^\[\]]+?)\]}',
- p.replace('\n', '').replace(' ', ''))
+ p.replace("\n", "").replace(" ", ""))
if hit:
- py2only = hit[0].replace('"', '').split(',')
+ py2only = hit[0].replace('"', "").split(",")
else:
print("Debug: addon_flags.json not found")
@@ -106,7 +128,7 @@ class UpdateWorker(QtCore.QThread):
if sys.version_info.major >= 3 and isinstance(p, bytes):
p = p.decode("utf-8")
u.close()
- p = re.findall((r"(?m)\[submodule\s*\"(?P.*)\"\]\s*"
+ p = re.findall((r'(?m)\[submodule\s*"(?P.*)"\]\s*'
r"path\s*=\s*(?P.+)\s*"
r"url\s*=\s*(?Phttps?://.*)"), p)
basedir = FreeCAD.getUserAppDataDir()
@@ -124,7 +146,8 @@ class UpdateWorker(QtCore.QThread):
state = 0
repos.append([name, url, state])
# querying custom addons
- customaddons = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons").GetString("CustomRepositories","").split("\n")
+ customaddons = (FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
+ .GetString("CustomRepositories", "").split("\n"))
for url in customaddons:
if url:
name = url.split("/")[-1]
@@ -135,7 +158,7 @@ class UpdateWorker(QtCore.QThread):
state = 0
else:
state = 1
- repos.append([name,url,state])
+ repos.append([name, url, state])
if not repos:
self.info_label.emit(translate("AddonsInstaller", "Unable to download addon list."))
else:
@@ -170,7 +193,7 @@ class InfoWorker(QtCore.QThread):
if sys.version_info.major >= 3 and isinstance(p, bytes):
p = p.decode("utf-8")
u.close()
- desc = re.findall("= 3 and isinstance(p, bytes):
p = p.decode("utf-8")
macros = re.findall('title="(Macro.*?)"', p)
- macros = [mac for mac in macros if ('translated' not in mac)]
+ macros = [mac for mac in macros if ("translated" not in mac)]
for mac in macros:
macname = mac[6:] # Remove "Macro ".
- macname = macname.replace("&","&")
- if (macname not in macros_blacklist) and ('recipes' not in macname.lower()):
+ macname = macname.replace("&", "&")
+ if (macname not in macros_blacklist) and ("recipes" not in macname.lower()):
macro = Macro(macname)
macro.on_wiki = True
self.macros.append(macro)
@@ -336,7 +358,7 @@ class ShowWorker(QtCore.QThread):
def __init__(self, repos, idx):
- # repos is a list of [name,url,installbit,descr]
+ # repos is a list of [name, url, installbit, descr]
# name : Addon name
# url : Addon repository location
# installbit: 0 = Addon is not installed
@@ -358,51 +380,54 @@ class ShowWorker(QtCore.QThread):
else:
u = None
url = self.repos[self.idx][1]
- self.info_label.emit(translate("AddonsInstaller", "Retrieving info from") + ' ' + str(url))
+ self.info_label.emit(translate("AddonsInstaller", "Retrieving info from") + " " + str(url))
desc = ""
- regex = utils.getReadmeRegex(url)
+ regex = utils.get_readme_regex(url)
if regex:
# extract readme from html via regex
- readmeurl = utils.getReadmeHTMLUrl(url)
+ readmeurl = utils.get_readme_html_url(url)
if not readmeurl:
- print("Debug: README not found for",url)
+ print("Debug: README not found for", url)
u = utils.urlopen(readmeurl)
if not u:
- print("Debug: README not found at",readmeurl)
+ print("Debug: README not found at", readmeurl)
u = utils.urlopen(readmeurl)
if u:
p = u.read()
if sys.version_info.major >= 3 and isinstance(p, bytes):
p = p.decode("utf-8")
u.close()
- readme = re.findall(regex,p,flags=re.MULTILINE|re.DOTALL)
+ readme = re.findall(regex, p, flags=re.MULTILINE | re.DOTALL)
if readme:
desc = readme[0]
else:
- print("Debug: README not found at",readmeurl)
+ print("Debug: README not found at", readmeurl)
else:
# convert raw markdown using lib
- readmeurl = utils.getReadmeUrl(url)
+ readmeurl = utils.get_readme_url(url)
if not readmeurl:
- print("Debug: README not found for",url)
+ print("Debug: README not found for", url)
u = utils.urlopen(readmeurl)
if u:
p = u.read()
if sys.version_info.major >= 3 and isinstance(p, bytes):
p = p.decode("utf-8")
u.close()
- desc = utils.fixRelativeLinks(p,readmeurl.rsplit("/README.md")[0])
- try:
- if NOMARKDOWN:
- raise ImportError
- import markdown # try to use system Markdown lib
- desc = markdown.markdown(desc,extensions=['md_in_html'])
- except ImportError:
- message = " "+translate("AddonsInstaller","Raw markdown displayed")+"
"
- message += translate("AddonsInstaller","Python Markdown library is missing.")+"
" + desc + "
"
+ desc = utils.fix_relative_links(p, readmeurl.rsplit("/README.md")[0])
+ if NOMARKDOWN or not have_markdown:
+ desc = markdown.markdown(desc, extensions=["md_in_html"])
+ else:
+ message = """
+
+
+"""
+ message += translate("AddonsInstaller", "Raw markdown displayed")
+ message += "
"
+ message += translate("AddonsInstaller", "Python Markdown library is missing.")
+ message += "
" + desc + "
"
desc = message
else:
- print("Debug: README not found at",readmeurl)
+ print("Debug: README not found at", readmeurl)
if desc == "":
# fall back to the description text
u = utils.urlopen(url)
@@ -414,9 +439,9 @@ class ShowWorker(QtCore.QThread):
if sys.version_info.major >= 3 and isinstance(p, bytes):
p = p.decode("utf-8")
u.close()
- descregex = utils.getDescRegex(url)
+ descregex = utils.get_desc_regex(url)
if descregex:
- desc = re.findall(descregex,p)
+ desc = re.findall(descregex, p)
if desc:
desc = desc[0]
if not desc:
@@ -427,92 +452,133 @@ class ShowWorker(QtCore.QThread):
if self.repos[self.idx][2] == 1:
upd = False
# checking for updates
- if not NOGIT:
- try:
- import git
- except:
- pass
- else:
- repo = self.repos[self.idx]
- clonedir = FreeCAD.getUserAppDataDir() + os.sep + "Mod" + os.sep + repo[0]
- if os.path.exists(clonedir):
- if not os.path.exists(clonedir + os.sep + '.git'):
- # Repair addon installed with raw download
- bare_repo = git.Repo.clone_from(repo[1], clonedir + os.sep + '.git', bare=True)
- try:
- with bare_repo.config_writer() as cw:
- cw.set('core', 'bare', False)
- except AttributeError:
- FreeCAD.Console.PrintWarning(translate("AddonsInstaller", "Outdated GitPython detected, consider upgrading with pip.")+"\n")
- cw = bare_repo.config_writer()
- cw.set('core', 'bare', False)
- del cw
- repo = git.Repo(clonedir)
- repo.head.reset('--hard')
- gitrepo = git.Git(clonedir)
- gitrepo.fetch()
- if "git pull" in gitrepo.status():
- upd = True
+ if not NOGIT and have_git:
+ repo = self.repos[self.idx]
+ clonedir = FreeCAD.getUserAppDataDir() + os.sep + "Mod" + os.sep + repo[0]
+ if os.path.exists(clonedir):
+ if not os.path.exists(clonedir + os.sep + ".git"):
+ # Repair addon installed with raw download
+ bare_repo = git.Repo.clone_from(repo[1], clonedir + os.sep + ".git", bare=True)
+ try:
+ with bare_repo.config_writer() as cw:
+ cw.set("core", "bare", False)
+ except AttributeError:
+ FreeCAD.Console.PrintWarning(translate("AddonsInstaller",
+ "Outdated GitPython detected, "
+ "consider upgrading with pip.") + "\n")
+ cw = bare_repo.config_writer()
+ cw.set("core", "bare", False)
+ del cw
+ repo = git.Repo(clonedir)
+ repo.head.reset("--hard")
+ gitrepo = git.Git(clonedir)
+ gitrepo.fetch()
+ if "git pull" in gitrepo.status():
+ upd = True
# If there is an update pending, lets user know via the UI
if upd:
- message = "
" + translate("AddonsInstaller", "An update is available for this addon.")
- message += "
" + desc + '
Addon repository: ' + self.repos[self.idx][1] + ''
- self.repos[self.idx][2] = 3 # mark as already installed AND already checked for updates AND update is available
+ message = """
+
+
+
+"""
+ message += translate("AddonsInstaller", "An update is available for this addon.")
+ message += "
" + desc + '
Addon repository: ' + self.repos[self.idx][1] + ""
+ # mark as already installed AND already checked for updates AND update is available
+ self.repos[self.idx][2] = 3
# If there isn't, indicate that this addon is already installed
else:
- message = "
" + translate("AddonsInstaller", "This addon is already installed.") + "
"
- message += desc + '
Addon repository: ' + self.repos[self.idx][1] + ''
- self.repos[self.idx][2] = 2 # mark as already installed AND already checked for updates
+ message = """
+
+
+
+"""
+ message += translate("AddonsInstaller", "This addon is already installed.")
+ message += "
" + desc
+ message += '
Addon repository: ' + self.repos[self.idx][1] + ""
+ self.repos[self.idx][2] = 2 # mark as already installed AND already checked for updates
# Let the user know the install path for this addon
- message += '
' + translate("AddonInstaller","Installed location")+": "+ FreeCAD.getUserAppDataDir() + os.sep + "Mod" + os.sep + self.repos[self.idx][0]
+ message += "
" + translate("AddonInstaller", "Installed location") + ": "
+ message += FreeCAD.getUserAppDataDir() + os.sep + "Mod" + os.sep + self.repos[self.idx][0]
self.addon_repos.emit(self.repos)
elif self.repos[self.idx][2] == 2:
- message = "
" + translate("AddonsInstaller", "This addon is already installed.") + "
"
- message += desc + '
Addon repository: ' + self.repos[self.idx][1] + ''
- message += '
' + translate("AddonInstaller","Installed location")+": "+ FreeCAD.getUserAppDataDir() + os.sep + "Mod" + os.sep + self.repos[self.idx][0]
+ message = """
+
+
+
+"""
+ message += translate("AddonsInstaller", "This addon is already installed.")
+ message += "
" + desc
+ message += '
Addon repository: ' + self.repos[self.idx][1] + ""
+ message += "
" + translate("AddonInstaller", "Installed location") + ": "
+ message += FreeCAD.getUserAppDataDir() + os.sep + "Mod" + os.sep + self.repos[self.idx][0]
elif self.repos[self.idx][2] == 3:
- message = "
" + translate("AddonsInstaller", "An update is available for this addon.")
- message += "
" + desc + '
Addon repository: ' + self.repos[self.idx][1] + ''
- message += '
' + translate("AddonInstaller","Installed location")+": "+ FreeCAD.getUserAppDataDir() + os.sep + "Mod" + os.sep + self.repos[self.idx][0]
+ message = """
+
+
+
+"""
+ message += translate("AddonsInstaller", "An update is available for this addon.")
+ message += "
" + desc + '
Addon repository: ' + self.repos[self.idx][1] + ""
+ message += "
" + translate("AddonInstaller", "Installed location") + ": "
+ message += FreeCAD.getUserAppDataDir() + os.sep + "Mod" + os.sep + self.repos[self.idx][0]
else:
- message = desc + '
Addon repository: ' + self.repos[self.idx][1] + ''
+ message = desc + '
Addon repository: ' + self.repos[self.idx][1] + ''
# If the Addon is obsolete, let the user know through the Addon UI
if self.repos[self.idx][0] in obsolete:
- message = " "+translate("AddonsInstaller","This addon is marked as obsolete")+"
"
- message += translate("AddonsInstaller","This usually means it is no longer maintained, and some more advanced addon in this list provides the same functionality.")+"
" + desc
+ message = """
+
+
+"""
+ message += translate("AddonsInstaller", "This addon is marked as obsolete") + "
"
+ message += translate("AddonsInstaller",
+ "This usually means it is no longer maintained, "
+ "and some more advanced addon in this list "
+ "provides the same functionality.") + "
" + desc
# If the Addon is Python 2 only, let the user know through the Addon UI
if self.repos[self.idx][0] in py2only:
- message = " "+translate("AddonsInstaller","This addon is marked as Python 2 Only")+"
"
- message += translate("AddonsInstaller","This workbench may no longer be maintained and installing it on a Python 3 system will more than likely result in errors at startup or while in use.")+"
" + desc
+ message = """
+
+
+"""
+ message += translate("AddonsInstaller", "This addon is marked as Python 2 Only") + "
"
+ message += translate("AddonsInstaller",
+ "This workbench may no longer be maintained and "
+ "installing it on a Python 3 system will more than "
+ "likely result in errors at startup or while in use.")
+ message += "
" + desc
- self.info_label.emit( message )
+ self.info_label.emit(message)
self.progressbar_show.emit(False)
self.mustLoadImages = True
- l = self.loadImages( message, self.repos[self.idx][1], self.repos[self.idx][0])
- if l:
- self.info_label.emit( l )
+ label = self.loadImages(message, self.repos[self.idx][1], self.repos[self.idx][0])
+ if label:
+ self.info_label.emit(label)
self.stop = True
def stopImageLoading(self):
-
"this stops the image loading process and allow the thread to terminate earlier"
self.mustLoadImages = False
- def loadImages(self,message,url,wbName):
-
+ def loadImages(self, message, url, wbName):
"checks if the given page contains images and downloads them"
# QTextBrowser cannot display online images. So we download them
# here, and replace the image link in the html code with the
# downloaded version
- imagepaths = re.findall("= 260:
remainChars = 259 - (len(store) + len(wbName) + 1)
- storename = os.path.join(store,wbName+name[-remainChars:])
+ storename = os.path.join(store, wbName+name[-remainChars:])
if not os.path.exists(storename):
try:
u = utils.urlopen(path)
imagedata = u.read()
u.close()
- except:
- print("AddonManager: Debug: Error retrieving image from",path)
+ except Exception:
+ print("AddonManager: Debug: Error retrieving image from", path)
else:
- f = open(storename,"wb")
+ f = open(storename, "wb")
f.write(imagedata)
f.close()
# resize the image to 300x300px if needed
- from PySide import QtCore,QtGui
img = QtGui.QImage(storename)
if (img.width() > 300) or (img.height() > 300):
pix = QtGui.QPixmap()
- pix = pix.fromImage(img.scaled(300,300,QtCore.Qt.KeepAspectRatio,QtCore.Qt.FastTransformation))
- pix.save(storename, "jpeg",100)
- message = message.replace("src=\""+origpath,"src=\"file:///"+storename.replace("\\","/"))
- #print(message)
+ pix = pix.fromImage(img.scaled(300, 300,
+ QtCore.Qt.KeepAspectRatio,
+ QtCore.Qt.FastTransformation))
+ pix.save(storename, "jpeg", 100)
+ message = message.replace("src=\""+origpath, "src=\"file:///"+storename.replace("\\", "/"))
+ # print(message)
return message
return None
-
class GetMacroDetailsWorker(QtCore.QThread):
-
"""Retrieve the macro details for a macro"""
info_label = QtCore.Signal(str)
@@ -573,36 +638,35 @@ class GetMacroDetailsWorker(QtCore.QThread):
self.progressbar_show.emit(True)
self.info_label.emit(translate("AddonsInstaller", "Retrieving description..."))
if not self.macro.parsed and self.macro.on_git:
- self.info_label.emit(translate('AddonsInstaller', 'Retrieving info from git'))
+ self.info_label.emit(translate("AddonsInstaller", "Retrieving info from git"))
self.macro.fill_details_from_file(self.macro.src_filename)
if not self.macro.parsed and self.macro.on_wiki:
- self.info_label.emit(translate('AddonsInstaller', 'Retrieving info from wiki'))
- mac = self.macro.name.replace(' ', '_')
- mac = mac.replace('&', '%26')
- mac = mac.replace('+', '%2B')
- url = 'https://www.freecadweb.org/wiki/Macro_' + mac
+ self.info_label.emit(translate("AddonsInstaller", "Retrieving info from wiki"))
+ mac = self.macro.name.replace(" ", "_")
+ mac = mac.replace("&", "%26")
+ mac = mac.replace("+", "%2B")
+ url = "https://www.freecadweb.org/wiki/Macro_" + mac
self.macro.fill_details_from_wiki(url)
if self.macro.is_installed():
already_installed_msg = (''
- + translate("AddonsInstaller", "This macro is already installed.")
- + '
')
+ + translate("AddonsInstaller", "This macro is already installed.")
+ + '
')
else:
- already_installed_msg = ''
+ already_installed_msg = ""
message = (already_installed_msg
- + ""+self.macro.name+"
"
- + self.macro.desc
- + '
Macro location: '
- + self.macro.url
- + '')
+ + ""+self.macro.name+"
"
+ + self.macro.desc
+ + "
Macro location: "
+ + self.macro.url
+ + "")
self.info_label.emit(message)
self.progressbar_show.emit(False)
self.stop = True
class InstallWorker(QtCore.QThread):
-
"This worker installs a workbench"
info_label = QtCore.Signal(str)
@@ -616,35 +680,26 @@ class InstallWorker(QtCore.QThread):
self.repos = repos
def run(self):
-
"installs or updates the selected addon"
- git = None
- try:
- import git
- except Exception as e:
+ if not have_git:
self.info_label.emit("GitPython not found.")
- print(e)
- FreeCAD.Console.PrintWarning(translate("AddonsInstaller","GitPython not found. Using standard download instead.")+"\n")
- try:
- import zipfile
- except:
+ FreeCAD.Console.PrintWarning(translate("AddonsInstaller",
+ "GitPython not found. Using standard download instead.") + "\n")
+ if not have_zip:
self.info_label.emit("no zip support.")
- FreeCAD.Console.PrintError(translate("AddonsInstaller","Your version of python doesn't appear to support ZIP files. Unable to proceed.")+"\n")
+ FreeCAD.Console.PrintError(translate("AddonsInstaller",
+ "Your version of python doesn't appear to support ZIP "
+ "files. Unable to proceed.") + "\n")
return
- try:
- import StringIO as io
- except ImportError: # StringIO is not available with python3
- import io
- if not isinstance(self.idx,list):
+
+ if not isinstance(self.idx, list):
self.idx = [self.idx]
for idx in self.idx:
if idx < 0:
return
if not self.repos:
return
- if NOGIT:
- git = None
basedir = FreeCAD.getUserAppDataDir()
moddir = basedir + os.sep + "Mod"
if not os.path.exists(moddir):
@@ -654,26 +709,33 @@ class InstallWorker(QtCore.QThread):
if os.path.exists(clonedir):
self.info_label.emit("Updating module...")
if sys.version_info.major > 2 and str(self.repos[idx][0]) in py2only:
- FreeCAD.Console.PrintWarning(translate("AddonsInstaller", "User requested updating a Python 2 workbench on a system running Python 3 - ")+str(self.repos[idx][0])+"\n")
- if git:
- if not os.path.exists(clonedir + os.sep + '.git'):
+ FreeCAD.Console.PrintWarning(translate("AddonsInstaller",
+ "User requested updating a Python 2 workbench on "
+ "a system running Python 3 - ") +
+ str(self.repos[idx][0])+"\n")
+ if have_git:
+ if not os.path.exists(clonedir + os.sep + ".git"):
# Repair addon installed with raw download
- bare_repo = git.Repo.clone_from(self.repos[idx][1], clonedir + os.sep + '.git', bare=True)
+ bare_repo = git.Repo.clone_from(self.repos[idx][1], clonedir + os.sep + ".git", bare=True)
try:
with bare_repo.config_writer() as cw:
- cw.set('core', 'bare', False)
+ cw.set("core", "bare", False)
except AttributeError:
- FreeCAD.Console.PrintWarning(translate("AddonsInstaller", "Outdated GitPython detected, consider upgrading with pip.")+"\n")
+ FreeCAD.Console.PrintWarning(translate("AddonsInstaller",
+ "Outdated GitPython detected, consider "
+ "upgrading with pip.") + "\n")
cw = bare_repo.config_writer()
- cw.set('core', 'bare', False)
+ cw.set("core", "bare", False)
del cw
repo = git.Repo(clonedir)
- repo.head.reset('--hard')
+ repo.head.reset("--hard")
repo = git.Git(clonedir)
try:
- answer = repo.pull() + "\n\n" + translate("AddonsInstaller", "Workbench successfully updated. Please restart FreeCAD to apply the changes.")
- except:
- print("Error updating module",self.repos[idx][1]," - Please fix manually")
+ answer = repo.pull() + "\n\n" + translate("AddonsInstaller",
+ "Workbench successfully updated. "
+ "Please restart FreeCAD to apply the changes.")
+ except Exception:
+ print("Error updating module", self.repos[idx][1], " - Please fix manually")
answer = repo.status()
print(answer)
else:
@@ -682,24 +744,32 @@ class InstallWorker(QtCore.QThread):
for submodule in repo_sms.submodules:
submodule.update(init=True, recursive=True)
else:
- answer = self.download(self.repos[idx][1],clonedir) + "\n\n" + translate("AddonsInstaller", "Workbench successfully updated. Please restart FreeCAD to apply the changes.")
+ answer = self.download(self.repos[idx][1], clonedir) + "\n\n"
+ answer += translate("AddonsInstaller",
+ "Workbench successfully updated. Please "
+ "restart FreeCAD to apply the changes.")
else:
self.info_label.emit("Checking module dependencies...")
- depsok,answer = self.checkDependencies(self.repos[idx][1])
+ depsok, answer = self.check_dependencies(self.repos[idx][1])
if depsok:
if sys.version_info.major > 2 and str(self.repos[idx][0]) in py2only:
- FreeCAD.Console.PrintWarning(translate("AddonsInstaller", "User requested installing a Python 2 workbench on a system running Python 3 - ")+str(self.repos[idx][0])+"\n")
- if git:
+ FreeCAD.Console.PrintWarning(translate("AddonsInstaller",
+ "User requested installing a Python 2 "
+ "workbench on a system running Python 3 - ") +
+ str(self.repos[idx][0]) + "\n")
+ if have_git:
self.info_label.emit("Cloning module...")
- repo = git.Repo.clone_from(self.repos[idx][1], clonedir, branch='master')
+ repo = git.Repo.clone_from(self.repos[idx][1], clonedir, branch="master")
# Make sure to clone all the submodules as well
if repo.submodules:
repo.submodule_update(recursive=True)
else:
self.info_label.emit("Downloading module...")
- self.download(self.repos[idx][1],clonedir)
- answer = translate("AddonsInstaller", "Workbench successfully installed. Please restart FreeCAD to apply the changes.")
+ self.download(self.repos[idx][1], clonedir)
+ answer = translate("AddonsInstaller",
+ "Workbench successfully installed. Please restart "
+ "FreeCAD to apply the changes.")
# symlink any macro contained in the module to the macros folder
macro_dir = FreeCAD.getUserMacroDir(True)
if not os.path.exists(macro_dir):
@@ -707,24 +777,25 @@ class InstallWorker(QtCore.QThread):
if os.path.exists(clonedir):
for f in os.listdir(clonedir):
if f.lower().endswith(".fcmacro"):
- print("copying macro:",f)
+ print("copying macro:", f)
utils.symlink(os.path.join(clonedir, f), os.path.join(macro_dir, f))
- FreeCAD.ParamGet('User parameter:Plugins/'+self.repos[idx][0]).SetString("destination",clonedir)
- answer += "\n\n"+translate("AddonsInstaller", "A macro has been installed and is available under Macro -> Macros menu")+":"
- answer += "\n" + f + ""
+ FreeCAD.ParamGet('User parameter:Plugins/' +
+ self.repos[idx][0]).SetString("destination", clonedir)
+ answer += "\n\n" + translate("AddonsInstaller",
+ "A macro has been installed and is available "
+ "under Macro -> Macros menu")
+ answer += ":\n" + f + ""
self.progressbar_show.emit(False)
self.info_label.emit(answer)
self.mark_recompute.emit(self.repos[idx][0])
self.stop = True
- def checkDependencies(self,baseurl):
-
+ def check_dependencies(self, baseurl):
"checks if the repo contains a metadata.txt and check its contents"
- import FreeCADGui
ok = True
message = ""
- depsurl = baseurl.replace("github.com","raw.githubusercontent.com")
+ depsurl = baseurl.replace("github.com", "raw.githubusercontent.com")
if not depsurl.endswith("/"):
depsurl += "/"
depsurl += "master/metadata.txt"
@@ -736,74 +807,69 @@ class InstallWorker(QtCore.QThread):
# urllib2 gives us a bytelike object instead of a string. Have to consider that
try:
- depsfile = depsfile.decode('utf-8')
+ depsfile = depsfile.decode("utf-8")
except AttributeError:
pass
deps = depsfile.split("\n")
- for l in deps:
- if l.startswith("workbenches="):
- depswb = l.split("=")[1].split(",")
+ for line in deps:
+ if line.startswith("workbenches="):
+ depswb = line.split("=")[1].split(",")
for wb in depswb:
if wb.strip():
if not wb.strip() in FreeCADGui.listWorkbenches().keys():
if not wb.strip()+"Workbench" in FreeCADGui.listWorkbenches().keys():
ok = False
- message += translate("AddonsInstaller","Missing workbench") + ": " + wb + ", "
- elif l.startswith("pylibs="):
- depspy = l.split("=")[1].split(",")
+ message += translate("AddonsInstaller", "Missing workbench") + ": " + wb + ", "
+ elif line.startswith("pylibs="):
+ depspy = line.split("=")[1].split(",")
for pl in depspy:
if pl.strip():
try:
__import__(pl.strip())
- except:
+ except ImportError:
ok = False
- message += translate("AddonsInstaller","Missing python module") +": " + pl + ", "
- elif l.startswith("optionalpylibs="):
- opspy = l.split("=")[1].split(",")
+ message += translate("AddonsInstaller", "Missing python module") + ": " + pl + ", "
+ elif line.startswith("optionalpylibs="):
+ opspy = line.split("=")[1].split(",")
for pl in opspy:
if pl.strip():
try:
__import__(pl.strip())
- except:
- message += translate("AddonsInstaller","Missing optional python module (doesn't prevent installing)") +": " + pl + ", "
+ except ImportError:
+ message += translate("AddonsInstaller",
+ "Missing optional python module (doesn't prevent installing)")
+ message += ": " + pl + ", "
if message and (not ok):
- message = translate("AddonsInstaller", "Some errors were found that prevent to install this workbench") + ": " + message + ". "
- message += translate("AddonsInstaller","Please install the missing components first.")
+ message = translate("AddonsInstaller", "Some errors were found that prevent to install this workbench")
+ message += ": " + message + ". "
+ message += translate("AddonsInstaller", "Please install the missing components first.")
return ok, message
- def download(self,baseurl,clonedir):
-
+ def download(self, baseurl, clonedir):
"downloads and unzip a zip version from a git repo"
- import zipfile
bakdir = None
if os.path.exists(clonedir):
bakdir = clonedir+".bak"
if os.path.exists(bakdir):
shutil.rmtree(bakdir)
- os.rename(clonedir,bakdir)
+ os.rename(clonedir, bakdir)
os.makedirs(clonedir)
- zipurl = utils.getZipUrl(baseurl)
+ zipurl = utils.get_zip_url(baseurl)
if not zipurl:
return translate("AddonsInstaller", "Error: Unable to locate zip from") + " " + baseurl
try:
print("Downloading "+zipurl)
u = utils.urlopen(zipurl)
- except:
+ except Exception:
return translate("AddonsInstaller", "Error: Unable to download") + " " + zipurl
if not u:
return translate("AddonsInstaller", "Error: Unable to download") + " " + zipurl
- if sys.version_info.major < 3:
- import StringIO as io
- _stringio = io.StringIO
- else:
- import io
- _stringio = io.BytesIO
zfile = _stringio()
zfile.write(u.read())
zfile = zipfile.ZipFile(zfile)
- master = zfile.namelist()[0] # github will put everything in a subfolder
+ master = zfile.namelist()[0] # github will put everything in a subfolder
zfile.extractall(clonedir)
u.close()
zfile.close()
@@ -816,7 +882,6 @@ class InstallWorker(QtCore.QThread):
class CheckSingleWorker(QtCore.QThread):
-
"""Worker to check for updates for a single addon"""
updateAvailable = QtCore.Signal(bool)
@@ -828,21 +893,21 @@ class CheckSingleWorker(QtCore.QThread):
def run(self):
- try:
- import git
- except:
+ if not have_git:
return
FreeCAD.Console.PrintLog("Checking for available updates of the "+self.name+" addon\n")
- addondir = os.path.join(FreeCAD.getUserAppDataDir(),"Mod",self.name)
+ addondir = os.path.join(FreeCAD.getUserAppDataDir(), "Mod", self.name)
if os.path.exists(addondir):
- if os.path.exists(addondir + os.sep + '.git'):
+ if os.path.exists(addondir + os.sep + ".git"):
gitrepo = git.Git(addondir)
try:
gitrepo.fetch()
if "git pull" in gitrepo.status():
self.updateAvailable.emit(True)
return
- except:
+ except Exception:
# can fail for any number of reasons, ex. not being online
pass
self.updateAvailable.emit(False)
+
+# @}