def run(self): self.progressbar_show.emit(True) self.info_label.emit(translate("AddonsInstaller", "Retrieving description...")) if len(self.repos[self.idx]) == 4: desc = self.repos[self.idx][3] else: url = self.repos[self.idx][1] self.info_label.emit(translate("AddonsInstaller", "Retrieving info from") + ' ' + str(url)) u = urlopen(url) p = u.read() if sys.version_info.major >= 3 and isinstance(p, bytes): p = p.decode("utf-8") u.close() desc = re.findall("<meta property=\"og:description\" content=\"(.*?)\"",p) if desc: desc = desc[0] if self.repos[self.idx][0] in OBSOLETE: desc += " <b>This add-on is marked as obsolete</b> - This usually means it is no longer maintained, and some more advanced add-on in this list provides the same functionality." else: desc = "Unable to retrieve addon description" self.repos[self.idx].append(desc) self.addon_repos.emit(self.repos) 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 upd: message = "<strong>" + translate("AddonsInstaller", "An update is available for this addon.") + "</strong><br>" + desc + ' - <a href="' + self.repos[self.idx][1] + '"><span style="word-wrap: break-word;width:15em;text-decoration: underline; color:#0000ff;">' + self.repos[self.idx][1] + '</span></a>' else: message = "<strong>" + translate("AddonsInstaller", "This addon is already installed.") + "</strong><br>" + desc + ' - <a href="' + self.repos[self.idx][1] + '"><span style="word-wrap: break-word;width:15em;text-decoration: underline; color:#0000ff;">' + self.repos[self.idx][1] + '</span></a>' else: message = desc + ' - <a href="' + self.repos[self.idx][1] + '"><span style="word-wrap: break-word;width:15em;text-decoration: underline; color:#0000ff;">' + self.repos[self.idx][1] + '</span></a>' self.info_label.emit( message ) self.progressbar_show.emit(False) self.stop = True
def run(self): "populates the list of addons" self.progressbar_show.emit(True) u = urlopen("https://github.com/FreeCAD/FreeCAD-addons") p = u.read() if sys.version_info.major >= 3 and isinstance(p, bytes): p = p.decode("utf-8") u.close() p = p.replace("\n"," ") p = re.findall("octicon-file-submodule(.*?)message",p) basedir = FreeCAD.getUserAppDataDir() moddir = basedir + os.sep + "Mod" repos = [] for l in p: #name = re.findall("data-skip-pjax=\"true\">(.*?)<",l)[0] name = re.findall("title=\"(.*?) @",l)[0] self.info_label.emit(name) #url = re.findall("title=\"(.*?) @",l)[0] url = "https://github.com/" + re.findall("href=\"\/(.*?)\/tree",l)[0] addondir = moddir + os.sep + name #print ("found:",name," at ",url) if not os.path.exists(addondir): state = 0 else: state = 1 repos.append([name,url,state]) if not repos: self.info_label.emit(translate("AddonsInstaller", "Unable to download addon list.")) else: repos = sorted(repos, key=lambda s: s[0].lower()) for repo in repos: self.addon_repo.emit(repo) self.info_label.emit(translate("AddonsInstaller", "Workbenches list was updated.")) self.progressbar_show.emit(False) self.stop = True
def download(self,giturl,clonedir): "downloads and unzip from github" 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.makedirs(clonedir) zipurl = giturl+"/archive/master.zip" try: print("Downloading "+zipurl) u = urlopen(zipurl) except: return translate("AddonsInstaller", "Error: Unable to download") + " " + zipurl zfile = _stringio() zfile.write(u.read()) zfile = zipfile.ZipFile(zfile) master = zfile.namelist()[0] # github will put everything in a subfolder zfile.extractall(clonedir) u.close() zfile.close() for filename in os.listdir(clonedir+os.sep+master): shutil.move(clonedir+os.sep+master+os.sep+filename, clonedir+os.sep+filename) os.rmdir(clonedir+os.sep+master) if bakdir: shutil.rmtree(bakdir) return translate("AddonsInstaller", "Successfully installed") + " " + zipurl
def checkDependencies(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") if not depsurl.endswith("/"): depsurl += "/" depsurl += "master/metadata.txt" try: mu = urlopen(depsurl) except urllib2.HTTPError: # no metadata.txt, we just continue without deps checking pass else: # metadata.txt found depsfile = mu.read() mu.close() # urllib2 gives us a bytelike object instead of a string. Have to consider that try: 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 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(",") for pl in depspy: if pl.strip(): try: __import__(pl.strip()) except: ok = False message += translate("AddonsInstaller","Missing python module") +": " + pl + ", " elif l.startswith("optionalpylibs="): opspy = l.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 + ", " if message and (not ok): message = translate("AddonsInstaller", "Some errors were found that prevent to install this workbench") + ": <b>" + message + "</b>. " message += translate("AddonsInstaller","Please install the missing components first.") return ok, message
def run(self): i = 0 for repo in self.repos: url = repo[1] u = urlopen(url) p = u.read() if sys.version_info.major >= 3 and isinstance(p, bytes): p = p.decode("utf-8") u.close() desc = re.findall("<meta property=\"og:description\" content=\"(.*?)\"",p) if desc: desc = desc[0] else: desc = "Unable to retrieve addon description" self.repos[i].append(desc) i += 1 self.addon_repos.emit(self.repos) self.stop = True
def retrieve_macros_from_wiki(self): """Retrieve macros from the wiki Read the wiki and emit a signal for each found macro. Reads only the page https://www.freecadweb.org/wiki/Macros_recipes. """ self.info_label_signal.emit("Downloading list of macros...") self.progressbar_show.emit(True) u = urlopen("https://www.freecadweb.org/wiki/Macros_recipes") p = u.read() u.close() if sys.version_info.major >= 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)] for mac in macros: macname = mac[6:] # Remove "Macro ". 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)
def getIfcOpenShell(force=False): """downloads and installs IfcOpenShell""" ifcok = False if not force: try: import ifcopenshell except: ifcok = False else: ifcok = True if not ifcok: # ifcopenshell not installed import re, json from PySide import QtGui import zipfile import addonmanager_utilities if not FreeCAD.GuiUp: reply = QtGui.QMessageBox.Yes else: reply = QtGui.QMessageBox.question( None, translate("BIM", "IfcOpenShell not found"), translate( "BIM", "IfcOpenShell is needed to import and export IFC files. It appears to be missing on your system. Would you like to download and install it now? It will be installed in FreeCAD's Macros directory." ), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: print( "Loading list of latest IfcOpenBot builds from https://github.com/IfcOpenBot/IfcOpenShell..." ) url1 = "https://api.github.com/repos/IfcOpenBot/IfcOpenShell/comments?per_page=100" u = addonmanager_utilities.urlopen(url1) if u: r = u.read() u.close() d = json.loads(r) l = d[-1]['body'] links = re.findall("http.*?zip", l) pyv = "python-" + str(sys.version_info.major) + str( sys.version_info.minor) if sys.platform.startswith("linux"): plat = "linux" elif sys.platform.startswith("win"): plat = "win" elif sys.platform.startswith("darwin"): plat = "macos" else: print("Error - unknown platform") return if sys.maxsize > 2**32: plat += "64" else: plat += "32" print("Looking for", plat, pyv) for link in links: if ("ifcopenshell-" + pyv in link) and (plat in link): print("Downloading " + link + "...") p = FreeCAD.ParamGet( "User parameter:BaseApp/Preferences/Macro") fp = p.GetString( "MacroPath", os.path.join(FreeCAD.getUserAppDataDir(), "Macros")) u = addonmanager_utilities.urlopen(link) if u: 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) zfile.extractall(fp) u.close() zfile.close() print("Successfully installed IfcOpenShell to", fp) break else: print("Unable to find a build for your version")
def run(self): "populates the list of addons" self.progressbar_show.emit(True) u = utils.urlopen("https://github.com/FreeCAD/FreeCAD-addons") if not u: self.progressbar_show.emit(False) self.done.emit() self.stop = True return p = u.read() if sys.version_info.major >= 3 and isinstance(p, bytes): p = p.decode("utf-8") u.close() p = p.replace("\n"," ") p = re.findall("octicon-file-submodule(.*?)message",p) basedir = FreeCAD.getUserAppDataDir() moddir = basedir + os.sep + "Mod" repos = [] # querying official addons for l in p: #name = re.findall("data-skip-pjax=\"true\">(.*?)<",l)[0] res = re.findall("title=\"(.*?) @",l) if res: name = res[0] else: print("AddonMananger: Debug: couldn't find title in",l) continue self.info_label.emit(name) #url = re.findall("title=\"(.*?) @",l)[0] url = utils.getRepoUrl(l) if url: addondir = moddir + os.sep + name #print ("found:",name," at ",url) if os.path.exists(addondir) and os.listdir(addondir): # make sure the folder exists and it contains files! state = 1 else: state = 0 repos.append([name,url,state]) # querying custom addons customaddons = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons").GetString("CustomRepositories","").split("\n") for url in customaddons: if url: name = url.split("/")[-1] if name.lower().endswith(".git"): name = name[:-4] addondir = moddir + os.sep + name if not os.path.exists(addondir): state = 0 else: state = 1 repos.append([name,url,state]) if not repos: self.info_label.emit(translate("AddonsInstaller", "Unable to download addon list.")) else: repos = sorted(repos, key=lambda s: s[0].lower()) for repo in repos: self.addon_repo.emit(repo) self.info_label.emit(translate("AddonsInstaller", "Workbenches list was updated.")) self.progressbar_show.emit(False) self.done.emit() self.stop = True
def fill_details_from_wiki(self, url): code = "" try: u = urlopen(url) except urllib2.HTTPError: return p = u.read() if sys.version_info.major >= 3 and isinstance(p, bytes): 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) if rawcodeurl: rawcodeurl = rawcodeurl[0] try: u2 = urlopen(rawcodeurl) except urllib2.HTTPError: 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)) data = u2.read(block) if not data: break if sys.version_info.major >= 3 and isinstance(data, bytes): data = data.decode('utf-8') response += data if response: code = response u2.close() if not code: code = re.findall('<pre>(.*?)<\/pre>', 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') 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') if sys.version_info.major < 3: code = code.encode('utf8') desc = re.findall("<td class=\"ctEven left macro-description\">(.*?)<\/td>", p.replace('\n', ' ')) if desc: desc = desc[0] else: 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
def fill_details_from_wiki(self, url): code = "" 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") 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) if rawcodeurl: rawcodeurl = rawcodeurl[0] 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 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") response += data if response: code = response u2.close() if not code: code = re.findall(r"<pre>(.*?)</pre>", 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") else: FreeCAD.Console.PrintWarning( translate("AddonsInstaller", "Unable to fetch the code of this macro.")) # Clean HTML escape codes. if sys.version_info.major < 3: 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( r"<td class=\"ctEven left macro-description\">(.*?)</td>", p.replace("\n", " ")) if desc: desc = desc[0] else: 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
def run(self): self.progressbar_show.emit(True) self.info_label.emit( translate("AddonsInstaller", "Retrieving description...")) if len(self.repos[self.idx]) == 4: desc = self.repos[self.idx][3] else: url = self.repos[self.idx][1] self.info_label.emit( translate("AddonsInstaller", "Retrieving info from ") + str(url)) u = urlopen(url) p = u.read() if sys.version_info.major >= 3 and isinstance(p, bytes): p = p.decode("utf-8") u.close() desc = re.findall( "<meta property=\"og:description\" content=\"(.*?)\"", p) if desc: desc = desc[0] else: desc = "Unable to retrieve addon description" self.repos[self.idx].append(desc) self.addon_repos.emit(self.repos) 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 upd: message = "<strong>" + translate( "AddonsInstaller", "An update is available for this addon." ) + "</strong><br>" + desc + ' - <a href="' + self.repos[self.idx][ 1] + '"><span style="word-wrap: break-word;width:15em;text-decoration: underline; color:#0000ff;">' + self.repos[ self.idx][1] + '</span></a>' else: message = "<strong>" + translate( "AddonsInstaller", "This addon is already installed." ) + "</strong><br>" + desc + ' - <a href="' + self.repos[self.idx][ 1] + '"><span style="word-wrap: break-word;width:15em;text-decoration: underline; color:#0000ff;">' + self.repos[ self.idx][1] + '</span></a>' else: message = desc + ' - <a href="' + self.repos[self.idx][ 1] + '"><span style="word-wrap: break-word;width:15em;text-decoration: underline; color:#0000ff;">' + self.repos[ self.idx][1] + '</span></a>' self.info_label.emit(message) self.progressbar_show.emit(False) self.stop = True
def fill_details_from_wiki(self, url): code = "" try: u = urlopen(url) except urllib2.HTTPError: return p = u.read() if sys.version_info.major >= 3 and isinstance(p, bytes): 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) if rawcodeurl: rawcodeurl = rawcodeurl[0] try: u2 = urlopen(rawcodeurl) except urllib2.HTTPError: 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)) data = u2.read(block) if not data: break response += data if response: code = response if sys.version_info.major >= 3 and isinstance(code, bytes): code = code.decode('utf-8') u2.close() if not code: code = re.findall('<pre>(.*?)<\/pre>', 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') 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') if sys.version_info.major < 3: code = code.encode('utf8') desc = re.findall( "<td class=\"ctEven left macro-description\">(.*?)<\/td>", p.replace('\n', ' ')) if desc: desc = desc[0] else: 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
def run(self): "populates the list of addons" self.progressbar_show.emit(True) # update info lists global obsolete, macros_blacklist, py2only u = utils.urlopen( "https://raw.githubusercontent.com/FreeCAD/FreeCAD-addons/master/addonflags.json" ) if u: p = u.read() if sys.version_info.major >= 3 and isinstance(p, bytes): p = p.decode("utf-8") u.close() hit = re.findall( r'"obsolete"[^\{]*?{[^\{]*?"Mod":\[(?P<obsolete>[^\[\]]+?)\]}', p.replace("\n", "").replace(" ", "")) if hit: obsolete = hit[0].replace('"', "").split(",") hit = re.findall( r'"blacklisted"[^\{]*?{[^\{]*?"Macro":\[(?P<blacklisted>[^\[\]]+?)\]}', p.replace("\n", "").replace(" ", "")) if hit: macros_blacklist = hit[0].replace('"', "").split(",") hit = re.findall( r'"py2only"[^\{]*?{[^\{]*?"Mod":\[(?P<py2only>[^\[\]]+?)\]}', p.replace("\n", "").replace(" ", "")) if hit: py2only = hit[0].replace('"', "").split(",") else: print("Debug: addon_flags.json not found") u = utils.urlopen( "https://raw.githubusercontent.com/FreeCAD/FreeCAD-addons/master/.gitmodules" ) if not u: self.progressbar_show.emit(False) self.done.emit() self.stop = True return p = u.read() 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<name>.*)"\]\s*' r"path\s*=\s*(?P<path>.+)\s*" r"url\s*=\s*(?P<url>https?://.*)"), p) basedir = FreeCAD.getUserAppDataDir() moddir = basedir + os.sep + "Mod" repos = [] # querying official addons for name, path, url in p: self.info_label.emit(name) url = url.split(".git")[0] addondir = moddir + os.sep + name if os.path.exists(addondir) and os.listdir(addondir): # make sure the folder exists and it contains files! state = 1 else: state = 0 repos.append([name, url, state]) # querying custom addons customaddons = (FreeCAD.ParamGet( "User parameter:BaseApp/Preferences/Addons").GetString( "CustomRepositories", "").split("\n")) for url in customaddons: if url: name = url.split("/")[-1] if name.lower().endswith(".git"): name = name[:-4] addondir = moddir + os.sep + name if not os.path.exists(addondir): state = 0 else: state = 1 repos.append([name, url, state]) if not repos: self.info_label.emit( translate("AddonsInstaller", "Unable to download addon list.")) else: repos = sorted(repos, key=lambda s: s[0].lower()) for repo in repos: self.addon_repo.emit(repo) self.info_label.emit( translate("AddonsInstaller", "Workbenches list was updated.")) self.progressbar_show.emit(False) self.done.emit() self.stop = True
def checkDependencies(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") if not depsurl.endswith("/"): depsurl += "/" depsurl += "master/metadata.txt" try: mu = urlopen(depsurl) except urllib2.HTTPError: # no metadata.txt, we just continue without deps checking pass else: # metadata.txt found depsfile = mu.read() mu.close() # urllib2 gives us a bytelike object instead of a string. Have to consider that try: 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 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(",") for pl in depspy: if pl.strip(): try: __import__(pl.strip()) except: ok = False message += translate( "AddonsInstaller", "Missing python module") + ": " + pl + ", " elif l.startswith("optionalpylibs="): opspy = l.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 + ", " if message and (not ok): message = translate( "AddonsInstaller", "Some errors were found that prevent to install this workbench" ) + ": <b>" + message + "</b>. " message += translate( "AddonsInstaller", "Please install the missing components first.") return ok, message
def checkShapeFileLibrary(): """Looks for and/or installs the ShapeFile library""" try: import shapefile except: url = "https://raw.githubusercontent.com/GeospatialPython/pyshp/master/shapefile.py" if FreeCAD.GuiUp: import addonmanager_utilities import FreeCADGui from PySide import QtGui reply = QtGui.QMessageBox.question( FreeCADGui.getMainWindow(), translate("Arch", "Shapefile module not found"), translate( "Arch", "The shapefile python library was not found on your system. Would you like to download it now from <a href=\"https://github.com/GeospatialPython/pyshp\">https://github.com/GeospatialPython/pyshp</a>? It will be placed in your macros folder." ), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: u = addonmanager_utilities.urlopen(url) if not u: FreeCAD.Console.PrintError( translate("Arch", "Error: Unable to download from:") + " " + url + "\n") return False b = u.read() p = FreeCAD.ParamGet( "User parameter:BaseApp/Preferences/Macro") fp = p.GetString( "MacroPath", os.path.join(FreeCAD.getUserAppDataDir(), "Macros")) fp = os.path.join(fp, "shapefile.py") f = pythonopen(fp, "wb") f.write(b) f.close() try: import shapefile except: FreeCAD.Console.PrintError( translate( "Arch", "Could not download shapefile module. Aborting.") + "\n") return False else: FreeCAD.Console.PrintError( translate("Arch", "Shapefile module not downloaded. Aborting.") + "\n") return False else: FreeCAD.Console.PrintError( translate("Arch", "Shapefile module not found. Aborting.") + "\n") FreeCAD.Console.PrintMessage( translate( "Arch", "The shapefile library can be downloaded from the following URL and installed in your macros folder:" ) + "\n") FreeCAD.Console.PrintMessage(url) return False return True
def fill_details_from_wiki(self, url): code = "" u = urlopen(url) if u is None: FreeCAD.Console.PrintWarning( translate( "AddonsInstaller", f"Could not connect to {url} - check connection and proxy settings", ) + "\n") return p = u.read() if isinstance(p, bytes): 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) if rawcodeurl: rawcodeurl = rawcodeurl[0] u2 = urlopen(rawcodeurl) if u2 is None: FreeCAD.Console.PrintWarning( translate( "AddonsInstaller", "Unable to open macro code URL {rawcodeurl}", ) + "\n") return response = "" block = 8192 while True: data = u2.read(block) if not data: break if isinstance(data, bytes): data = data.decode("utf-8") response += data if response: code = response u2.close() if not code: code = re.findall(r"<pre>(.*?)</pre>", p.replace("\n", "--endl--")) if code: # take the biggest code block code = sorted(code, key=len)[-1] code = code.replace("--endl--", "\n") # Clean HTML escape codes. code = unescape(code) code = code.replace(b"\xc2\xa0".decode("utf-8"), " ") else: FreeCAD.Console.PrintWarning( translate("AddonsInstaller", "Unable to fetch the code of this macro.") + "\n") desc = re.findall( r"<td class=\"ctEven left macro-description\">(.*?)</td>", p.replace("\n", " "), ) if desc: desc = desc[0] else: FreeCAD.Console.PrintWarning( translate( "AddonsInstaller", f"Unable to retrieve a description from the wiki for macro {self.name}", ) + "\n") desc = "No description available" self.desc = desc self.comment, _, _ = desc.partition( "<br") # Up to the first line break self.comment = re.sub("<.*?>", "", self.comment) # Strip any tags self.url = url if isinstance(code, list): flat_code = "" for chunk in code: flat_code += chunk code = flat_code self.code = code self.fill_details_from_code(self.code) if not self.author: self.author = self.parse_desc("Author: ") if not self.date: self.date = self.parse_desc("Last modified: ")
def run(self): self.progressbar_show.emit(True) self.info_label.emit(translate("AddonsInstaller", "Retrieving description...")) if len(self.repos[self.idx]) == 4: desc = self.repos[self.idx][3] else: u = None url = self.repos[self.idx][1] self.info_label.emit(translate("AddonsInstaller", "Retrieving info from") + ' ' + str(url)) desc = "" # get the README if possible readmeurl = utils.getReadmeUrl(url) if not readmeurl: print("Debug: README not found for",url) u = utils.urlopen(readmeurl) if not u: print("Debug: README not found at",readmeurl) if u: p = u.read() if sys.version_info.major >= 3 and isinstance(p, bytes): p = p.decode("utf-8") u.close() readmeregex = utils.getReadmeRegex(url) if readmeregex: readme = re.findall(readmeregex,p,flags=re.MULTILINE|re.DOTALL) if readme: desc += readme[0] if not desc: # fall back to the description text u = utils.urlopen(url) if not u: self.progressbar_show.emit(False) self.stop = True return p = u.read() if sys.version_info.major >= 3 and isinstance(p, bytes): p = p.decode("utf-8") u.close() descregex = utils.getDescRegex(url) if descregex: desc = re.findall(descregex,p) if desc: desc = "<br/>"+desc[0] if not desc: desc = "Unable to retrieve addon description" self.repos[self.idx].append(desc) self.addon_repos.emit(self.repos) # Addon is installed so lets check if it has an update 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 there is an update pending, lets user know via the UI if upd: message = "<div style=\"width: 100%;text-align: center;background: #75AFFD;\"><br/><strong style=\"background: #397FF7;color: #FFFFFF;\">" + translate("AddonsInstaller", "An update is available for this addon.") message += "</strong><br/></div><hr/>" + desc + '<br/><br/>Addon repository: <a href="' + self.repos[self.idx][1] + '">' + self.repos[self.idx][1] + '</a>' self.repos[self.idx][2] = 3 # mark as already installed AND already checked for updates AND update is available # If there isn't, indicate that this addon is already installed else: message = "<div style=\"width: 100%;text-align: center;background: #C1FEB2;\"><br/><strong style=\"background: #00B629;color: #FFFFFF;\">" + translate("AddonsInstaller", "This addon is already installed.") + "</strong><br/></div><hr/>" message += desc + '<br/><br/>Addon repository: <a href="' + self.repos[self.idx][1] + '">' + self.repos[self.idx][1] + '</a>' 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 += '<br/>' + translate("AddonInstaller","Installed location")+": "+ 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 = "<div style=\"width: 100%;text-align: center;background: #C1FEB2;\"><br/><strong style=\"background: #00B629;color: #FFFFFF;\">" + translate("AddonsInstaller", "This addon is already installed.") + "</strong><br></div><hr/>" message += desc + '<br/><br/>Addon repository: <a href="' + self.repos[self.idx][1] + '">' + self.repos[self.idx][1] + '</a>' message += '<br/>' + translate("AddonInstaller","Installed location")+": "+ FreeCAD.getUserAppDataDir() + os.sep + "Mod" + os.sep + self.repos[self.idx][0] elif self.repos[self.idx][2] == 3: message = "<div style=\"width: 100%;text-align: center;background: #75AFFD;\"><br/><strong style=\"background: #397FF7;color: #FFFFFF;\">" + translate("AddonsInstaller", "An update is available for this addon.") message += "</strong><br/></div><hr/>" + desc + '<br/><br/>Addon repository: <a href="' + self.repos[self.idx][1] + '">' + self.repos[self.idx][1] + '</a>' message += '<br/>' + translate("AddonInstaller","Installed location")+": "+ FreeCAD.getUserAppDataDir() + os.sep + "Mod" + os.sep + self.repos[self.idx][0] else: message = desc + '<br/><br/>Addon repository: <a href="' + self.repos[self.idx][1] + '">' + self.repos[self.idx][1] + '</a>' # If the Addon is obsolete, let the user know through the Addon UI if self.repos[self.idx][0] in OBSOLETE: message = " <div style=\"width: 100%; text-align:center; background: #FFB3B3;\"><strong style=\"color: #FFFFFF; background: #FF0000;\">"+translate("AddonsInstaller","This addon is marked as obsolete")+"</strong><br/><br/>" message += translate("AddonsInstaller","This usually means it is no longer maintained, and some more advanced addon in this list provides the same functionality.")+"<br/></div><hr/>" + 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 = " <div style=\"width: 100%; text-align:center; background: #ffe9b3;\"><strong style=\"color: #FFFFFF; background: #ff8000;\">"+translate("AddonsInstaller","This addon is marked as Python 2 Only")+"</strong><br/><br/>" 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.")+"<br/></div><hr/>" + desc 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 ) self.stop = True
def run(self): self.progressbar_show.emit(True) self.info_label.emit( translate("AddonsInstaller", "Retrieving description...")) if len(self.repos[self.idx]) == 4: desc = self.repos[self.idx][3] else: u = None url = self.repos[self.idx][1] self.info_label.emit( translate("AddonsInstaller", "Retrieving info from") + ' ' + str(url)) desc = "" # get the README if possible readmeurl = utils.getReadmeUrl(url) if not readmeurl: print("Debug: README not found for", url) u = utils.urlopen(readmeurl) if not u: print("Debug: README not found at", readmeurl) if u: p = u.read() if sys.version_info.major >= 3 and isinstance(p, bytes): p = p.decode("utf-8") u.close() readmeregex = utils.getReadmeRegex(url) if readmeregex: readme = re.findall(readmeregex, p, flags=re.MULTILINE | re.DOTALL) if readme: desc += readme[0] if not desc: # fall back to the description text u = utils.urlopen(url) if not u: self.progressbar_show.emit(False) self.stop = True return p = u.read() if sys.version_info.major >= 3 and isinstance(p, bytes): p = p.decode("utf-8") u.close() descregex = utils.getDescRegex(url) if descregex: desc = re.findall(descregex, p) if desc: desc = "<br/>" + desc[0] if not desc: desc = "Unable to retrieve addon description" self.repos[self.idx].append(desc) self.addon_repos.emit(self.repos) 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 upd: message = "<strong style=\"background: #B65A00;\">" + translate( "AddonsInstaller", "An update is available for this addon." ) + "</strong><br>" + desc + '<br/><br/>Addon repository: <a href="' + self.repos[ self.idx][1] + '">' + self.repos[self.idx][1] + '</a>' else: message = "<strong style=\"background: #00B629;\">" + translate( "AddonsInstaller", "This addon is already installed." ) + "</strong><br>" + desc + '<br/><br/>Addon repository: <a href="' + self.repos[ self.idx][1] + '">' + self.repos[self.idx][1] + '</a>' self.repos[self.idx][ 2] = 2 # mark as already installed AND already checked for updates self.addon_repos.emit(self.repos) else: message = desc + '<br/><br/>Addon repository: <a href="' + self.repos[ self.idx][1] + '">' + self.repos[self.idx][1] + '</a>' if self.repos[self.idx][0] in OBSOLETE: message = " <strong style=\"background: #FF0000;\">" + translate( "AddonsInstaller", "This addon is marked as obsolete" ) + "</strong><br/><br/>" + translate( "AddonsInstaller", "This usually means it is no longer maintained, and some more advanced addon in this list provides the same functionality." ) + "<br/><br/>" + message self.info_label.emit(message) self.progressbar_show.emit(False) l = self.loadImages(message, self.repos[self.idx][1], self.repos[self.idx][0]) if l: self.info_label.emit(l) self.stop = True
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("<img.*?src=\"(.*?)\"", message) if imagepaths: storedimages = [] store = os.path.join(FreeCAD.getUserAppDataDir(), "AddonManager", "Images") if not os.path.exists(store): os.makedirs(store) for path in imagepaths: if not self.mustLoadImages: return None origpath = path if "?" in path: # remove everything after the ? path = path.split("?")[0] if not path.startswith("http"): path = utils.getserver(url) + path name = path.split("/")[-1] if name and path.startswith("http"): storename = os.path.join(store, name) if len(storename) >= 260: remainChars = 259 - (len(store) + len(wbName) + 1) 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 Exception: print( "AddonManager: Debug: Error retrieving image from", path) else: try: f = open(storename, "wb") except OSError: # ecryptfs (and probably not only ecryptfs) has lower length limit for path storename = storename[-140:] f = open(storename, "wb") f.write(imagedata) f.close() # resize the image to 300x300px if needed 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) return message return None
def run(self): self.progressbar_show.emit(True) self.info_label.emit( translate("AddonsInstaller", "Retrieving description...")) if len(self.repos[self.idx]) == 4: desc = self.repos[self.idx][3] else: u = None url = self.repos[self.idx][1] self.info_label.emit( translate("AddonsInstaller", "Retrieving info from") + " " + str(url)) desc = "" regex = utils.get_readme_regex(url) if regex: # extract readme from html via regex readmeurl = utils.get_readme_html_url(url) if not readmeurl: print("Debug: README not found for", url) u = utils.urlopen(readmeurl) if not u: 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) if readme: desc = readme[0] else: print("Debug: README not found at", readmeurl) else: # convert raw markdown using lib readmeurl = utils.get_readme_url(url) if not readmeurl: 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.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 = """ <div style="width: 100%; text-align:center;background: #91bbe0;"> <strong style="color: #FFFFFF;"> """ message += translate("AddonsInstaller", "Raw markdown displayed") message += "</strong><br/><br/>" message += translate( "AddonsInstaller", "Python Markdown library is missing.") message += "<br/></div><hr/><pre>" + desc + "</pre>" desc = message else: print("Debug: README not found at", readmeurl) if desc == "": # fall back to the description text u = utils.urlopen(url) if not u: self.progressbar_show.emit(False) self.stop = True return p = u.read() if sys.version_info.major >= 3 and isinstance(p, bytes): p = p.decode("utf-8") u.close() descregex = utils.get_desc_regex(url) if descregex: desc = re.findall(descregex, p) if desc: desc = desc[0] if not desc: desc = "Unable to retrieve addon description" self.repos[self.idx].append(desc) self.addon_repos.emit(self.repos) # Addon is installed so lets check if it has an update if self.repos[self.idx][2] == 1: upd = False # checking for updates 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 = """ <div style="width: 100%;text-align: center;background: #75AFFD;"> <br/> <strong style="background: #397FF7;color: #FFFFFF;"> """ message += translate("AddonsInstaller", "An update is available for this addon.") message += "</strong><br/></div><hr/>" + desc + '<br/><br/>Addon repository: <a href="' message += self.repos[self.idx][1] + '">' + self.repos[ self.idx][1] + "</a>" # 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 = """ <div style="width: 100%;text-align: center;background: #C1FEB2;"> <br/> <strong style="background: #00B629;color: #FFFFFF;"> """ message += translate("AddonsInstaller", "This addon is already installed.") message += "</strong><br/></div><hr/>" + desc message += '<br/><br/>Addon repository: <a href="' message += self.repos[self.idx][1] + '">' + self.repos[ self.idx][1] + "</a>" 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 += "<br/>" + 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 = """ <div style="width: 100%;text-align: center;background: #C1FEB2;"> <br/> <strong style="background: #00B629;color: #FFFFFF;"> """ message += translate("AddonsInstaller", "This addon is already installed.") message += "</strong><br></div><hr/>" + desc message += '<br/><br/>Addon repository: <a href="' message += self.repos[self.idx][1] + '">' + self.repos[ self.idx][1] + "</a>" message += "<br/>" + 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 = """ <div style="width: 100%;text-align: center;background: #75AFFD;"> <br/> <strong style="background: #397FF7;color: #FFFFFF;"> """ message += translate("AddonsInstaller", "An update is available for this addon.") message += "</strong><br/></div><hr/>" + desc + '<br/><br/>Addon repository: <a href="' message += self.repos[self.idx][1] + '">' + self.repos[ self.idx][1] + "</a>" message += "<br/>" + translate("AddonInstaller", "Installed location") + ": " message += FreeCAD.getUserAppDataDir( ) + os.sep + "Mod" + os.sep + self.repos[self.idx][0] else: message = desc + '<br/><br/>Addon repository: <a href="' message += self.repos[self.idx][1] + '">' + self.repos[ self.idx][1] + '</a>' # If the Addon is obsolete, let the user know through the Addon UI if self.repos[self.idx][0] in obsolete: message = """ <div style="width: 100%; text-align:center; background: #FFB3B3;"> <strong style="color: #FFFFFF; background: #FF0000;"> """ message += translate( "AddonsInstaller", "This addon is marked as obsolete") + "</strong><br/><br/>" message += translate( "AddonsInstaller", "This usually means it is no longer maintained, " "and some more advanced addon in this list " "provides the same functionality.") + "<br/></div><hr/>" + 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 = """ <div style="width: 100%; text-align:center; background: #ffe9b3;"> <strong style="color: #FFFFFF; background: #ff8000;"> """ message += translate("AddonsInstaller", "This addon is marked as Python 2 Only" ) + "</strong><br/><br/>" 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 += "<br/></div><hr/>" + desc self.info_label.emit(message) self.progressbar_show.emit(False) self.mustLoadImages = True 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 run(self): "populates the list of addons" self.progressbar_show.emit(True) u = utils.urlopen("https://github.com/FreeCAD/FreeCAD-addons") if not u: self.progressbar_show.emit(False) self.done.emit() self.stop = True return p = u.read() if sys.version_info.major >= 3 and isinstance(p, bytes): p = p.decode("utf-8") u.close() p = p.replace("\n", " ") p = re.findall("octicon-file-submodule(.*?)message", p) basedir = FreeCAD.getUserAppDataDir() moddir = basedir + os.sep + "Mod" repos = [] # querying official addons for l in p: #name = re.findall("data-skip-pjax=\"true\">(.*?)<",l)[0] res = re.findall("title=\"(.*?) @", l) if res: name = res[0] else: print("AddonMananger: Debug: couldn't find title in", l) continue self.info_label.emit(name) #url = re.findall("title=\"(.*?) @",l)[0] url = utils.getRepoUrl(l) if url: addondir = moddir + os.sep + name #print ("found:",name," at ",url) if os.path.exists(addondir) and os.listdir(addondir): # make sure the folder exists and it contains files! state = 1 else: state = 0 repos.append([name, url, state]) # querying custom addons customaddons = FreeCAD.ParamGet( "User parameter:BaseApp/Preferences/Addons").GetString( "CustomRepositories", "").split("\n") for url in customaddons: if url: name = url.split("/")[-1] if name.lower().endswith(".git"): name = name[:-4] addondir = moddir + os.sep + name if not os.path.exists(addondir): state = 0 else: state = 1 repos.append([name, url, state]) if not repos: self.info_label.emit( translate("AddonsInstaller", "Unable to download addon list.")) else: repos = sorted(repos, key=lambda s: s[0].lower()) for repo in repos: self.addon_repo.emit(repo) self.info_label.emit( translate("AddonsInstaller", "Workbenches list was updated.")) git_exe = utils.checkGitBinary() import platform try: out = os.popen(git_exe + ' --version', 'r') except: if platform.system() == 'Windows': FreeCAD.Console.PrintLog( translate( "AddonsInstaller", "The Git executable has not been found in the path.") + "\n") else: FreeCAD.Console.PrintLog( translate("AddonsInstaller", "The Git binary has not been found.") + "\n") from distutils.version import StrictVersion if 'out' in locals(): out_string = out.read() out.close() result = re.search('(\d+\.\d+\.\d+)', out_string) git_version = StrictVersion(result.group(1)) if platform.system() == 'Windows': FreeCAD.Console.PrintLog( translate("AddonsInstaller", "Found Git executable version ") + str(git_version) + "\n") else: FreeCAD.Console.PrintLog( translate("AddonsInstaller", "Found Git binary version ") + str(git_version) + "\n") else: git_version = StrictVersion("0.0.0") self.progressbar_show.emit(False) self.done.emit() self.stop = True