Example #1
0
 def test_copy_constructor(self):
     filename = os.path.join(self.test_dir, "basic_metadata.xml")
     md = FreeCAD.Metadata(filename)
     copy_of_md = FreeCAD.Metadata(md)
     self.assertEqual(md.Name, copy_of_md.Name)
     self.assertEqual(md.Description, copy_of_md.Description)
     self.assertEqual(md.Version, copy_of_md.Version)
Example #2
0
    def test_xml_constructor(self):
        try:
            filename = os.path.join(self.test_dir, "basic_metadata.xml")
            md = FreeCAD.Metadata(filename)
        except Exception:
            self.fail("Metadata construction from XML file failed")

        with self.assertRaises(
                FreeCAD.Base.XMLBaseException,
                msg=
                "Metadata construction from XML file with bad root node did not raise an exception"
        ):
            filename = os.path.join(self.test_dir, "bad_root_node.xml")
            md = FreeCAD.Metadata(filename)

        with self.assertRaises(
                FreeCAD.Base.XMLBaseException,
                msg=
                "Metadata construction from invalid XML file did not raise an exception"
        ):
            filename = os.path.join(self.test_dir, "bad_xml.xml")
            md = FreeCAD.Metadata(filename)

        with self.assertRaises(
                FreeCAD.Base.XMLBaseException,
                msg=
                "Metadata construction from XML file with invalid version did not raise an exception"
        ):
            filename = os.path.join(self.test_dir, "bad_version.xml")
            md = FreeCAD.Metadata(filename)
Example #3
0
    def test_xml_constructor(self):
        try:
            filename = os.path.join(self.test_dir, "basic_metadata.xml")
            md = FreeCAD.Metadata(filename)
        except Exception:
            self.fail("Metadata construction from XML file failed")

        try:
            filename = os.path.join(self.test_dir, "bad_root_node.xml")
            md = FreeCAD.Metadata(filename)
        except Exception as e:
            FreeCAD.Console.PrintMessage(str(e) + "\n")
            pass
        else:
            self.fail("Metadata construction from XML file with bad root node did not raise an exception")

        try:
            filename = os.path.join(self.test_dir, "bad_xml.xml")
            md = FreeCAD.Metadata(filename)
        except Exception as e:
            FreeCAD.Console.PrintMessage(str(e) + "\n")
            pass
        else:
            self.fail("Metadata construction from invalid XML file did not raise an exception")

        try:
            filename = os.path.join(self.test_dir, "bad_version.xml")
            md = FreeCAD.Metadata(filename)
        except Exception as e:
            FreeCAD.Console.PrintMessage(str(e) + "\n")
            pass
        else:
            self.fail("Metadata construction from XML file with bad version did not raise an exception")
Example #4
0
 def load_metadata_file(self, file: str) -> None:
     if os.path.exists(file):
         metadata = FreeCAD.Metadata(file)
         self.set_metadata(metadata)
     else:
         FreeCAD.Console.PrintMessage(
             "Internal error: {} does not exist".format(file))
Example #5
0
    def load_metadata_file(self, file: str) -> None:
        """Read a given metadata file and set it as this object's metadata"""

        if os.path.exists(file):
            metadata = FreeCAD.Metadata(file)
            self.set_metadata(metadata)
        else:
            FreeCAD.Console.PrintLog(f"Internal error: {file} does not exist")
Example #6
0
    def test_content_types(self):
        filename = os.path.join(self.test_dir, "content_items.xml")
        md = FreeCAD.Metadata(filename)

        content = md.Content
        self.assertTrue("workbench" in content)
        self.assertTrue("preferencepack" in content)
        self.assertTrue("macro" in content)
        self.assertTrue("other_content_item" in content)

        workbenches = content["workbench"]
        preferencepacks = content["preferencepack"]
        macros = content["macro"]
        other = content["other_content_item"]

        self.assertEqual(len(workbenches), 3)
        self.assertEqual(len(macros), 2)
        self.assertEqual(len(preferencepacks), 1)
    def check_package(self, package: Addon) -> None:
        """Given a packaged Addon package, check it for updates. If git is available that is
        used. If not, the package's metadata is examined, and if the metadata file has changed
        compared to the installed copy, an update is flagged."""

        clonedir = self.moddir + os.sep + package.name
        if os.path.exists(clonedir):

            # First, try to just do a git-based update, which will give the most accurate results:
            if self.git_manager:
                self.check_workbench(package)
                if package.status() != Addon.Status.CANNOT_CHECK:
                    # It worked, just exit now
                    return

            # If we were unable to do a git-based update, try using the package.xml file instead:
            installed_metadata_file = os.path.join(clonedir, "package.xml")
            if not os.path.isfile(installed_metadata_file):
                # If there is no package.xml file, then it's because the package author added it
                # after the last time the local installation was updated. By definition, then,
                # there is an update available, if only to download the new XML file.
                package.set_status(Addon.Status.UPDATE_AVAILABLE)
                package.installed_version = None
                return
            package.updated_timestamp = os.path.getmtime(installed_metadata_file)
            try:
                installed_metadata = FreeCAD.Metadata(installed_metadata_file)
                package.installed_version = installed_metadata.Version
                # Packages are considered up-to-date if the metadata version matches. Authors
                # should update their version string when they want the addon manager to alert
                # users of a new version.
                if package.metadata.Version != installed_metadata.Version:
                    package.set_status(Addon.Status.UPDATE_AVAILABLE)
                else:
                    package.set_status(Addon.Status.NO_UPDATE_AVAILABLE)
            except Exception:
                FreeCAD.Console.PrintWarning(
                    translate(
                        "AddonsInstaller",
                        "Failed to read metadata from {name}",
                    ).format(name=installed_metadata_file)
                    + "\n"
                )
                package.set_status(Addon.Status.CANNOT_CHECK)
    def resolve_fetch(self):
        """Called when the data fetch completed, either with an error, or if it found the metadata file"""

        if self.fetch_task.error(
        ) == QtNetwork.QNetworkReply.NetworkError.NoError:
            FreeCAD.Console.PrintLog(
                f"Found a package.xml file for {self.repo.name}\n")
            self.repo.repo_type = AddonManagerRepo.RepoType.PACKAGE
            new_xml = self.fetch_task.readAll()
            hasher = hashlib.sha1()
            hasher.update(new_xml.data())
            new_sha1 = hasher.hexdigest()
            self.last_sha1 = new_sha1
            # Determine if we need to download the icon: only do that if the
            # package.xml file changed (since
            # a change in the version number will show up as a change in the
            # SHA1, without having to actually
            # read the metadata)
            if self.repo.name in self.index:
                cached_sha1 = self.index[self.repo.name]
                if cached_sha1 != new_sha1:
                    self.update_local_copy(new_xml)
                else:
                    # Assume that if the package.xml file didn't change,
                    # neither did the icon, so don't waste
                    # resources downloading it
                    xml_file = os.path.join(self.store, self.repo.name,
                                            "package.xml")
                    self.repo.metadata = FreeCAD.Metadata(xml_file)
            else:
                # There is no local copy yet, so we definitely have to update
                # the cache
                self.update_local_copy(new_xml)
        elif (self.fetch_task.error() ==
              QtNetwork.QNetworkReply.NetworkError.ContentNotFoundError):
            pass
        elif (self.fetch_task.error() ==
              QtNetwork.QNetworkReply.NetworkError.OperationCanceledError):
            pass
        else:
            FreeCAD.Console.PrintWarning(
                translate("AddonsInstaller", "Failed to connect to URL") +
                f":\n{self.url}\n {self.fetch_task.error()}\n")
    def update_local_copy(self, new_xml):
        # We have to update the local copy of the metadata file and re-download
        # the icon file

        name = self.repo.name
        repo_url = self.repo.url
        package_cache_directory = os.path.join(self.store, name)
        if not os.path.exists(package_cache_directory):
            os.makedirs(package_cache_directory)
        new_xml_file = os.path.join(package_cache_directory, "package.xml")
        with open(new_xml_file, "wb") as f:
            f.write(new_xml.data())
        metadata = FreeCAD.Metadata(new_xml_file)
        self.repo.metadata = metadata
        self.repo.repo_type = AddonManagerRepo.RepoType.PACKAGE
        icon = metadata.Icon

        if not icon:
            # If there is no icon set for the entire package, see if there are
            # any workbenches, which are required to have icons, and grab the first
            # one we find:
            content = self.repo.metadata.Content
            if "workbench" in content:
                wb = content["workbench"][0]
                if wb.Icon:
                    if wb.Subdirectory:
                        subdir = wb.Subdirectory
                    else:
                        subdir = wb.Name
                    self.repo.Icon = subdir + wb.Icon
                    icon = self.repo.Icon

        icon_url = utils.construct_git_url(self.repo, icon)
        icon_stream = utils.urlopen(icon_url)
        if icon and icon_stream and icon_url:
            icon_data = icon_stream.read()
            cache_file = self.repo.get_cached_icon_filename()
            with open(cache_file, "wb") as icon_file:
                icon_file.write(icon_data)
                self.repo.cached_icon_filename = cache_file
        self.updated.emit(self.repo)
    def process_package_xml(self, repo: Addon, data: QtCore.QByteArray):
        """Process the package.xml metadata file"""
        repo.repo_type = Addon.Kind.PACKAGE  # By definition
        package_cache_directory = os.path.join(self.store, repo.name)
        if not os.path.exists(package_cache_directory):
            os.makedirs(package_cache_directory)
        new_xml_file = os.path.join(package_cache_directory, "package.xml")
        with open(new_xml_file, "wb") as f:
            f.write(data.data())
        metadata = FreeCAD.Metadata(new_xml_file)
        repo.metadata = metadata
        self.status_message.emit(
            translate("AddonsInstaller", "Downloaded package.xml for {}").format(
                repo.name
            )
        )

        # Grab a new copy of the icon as well: we couldn't enqueue this earlier because
        # we didn't know the path to it, which is stored in the package.xml file.
        icon = metadata.Icon
        if not icon:
            # If there is no icon set for the entire package, see if there are
            # any workbenches, which are required to have icons, and grab the first
            # one we find:
            content = repo.metadata.Content
            if "workbench" in content:
                wb = content["workbench"][0]
                if wb.Icon:
                    if wb.Subdirectory:
                        subdir = wb.Subdirectory
                    else:
                        subdir = wb.Name
                    repo.Icon = subdir + wb.Icon
                    icon = repo.Icon

        icon_url = utils.construct_git_url(repo, icon)
        index = NetworkManager.AM_NETWORK_MANAGER.submit_unmonitored_get(icon_url)
        self.requests[index] = (repo, UpdateMetadataCacheWorker.RequestType.ICON)
        self.total_requests += 1
Example #11
0
    def test_toplevel_tags(self):
        filename = os.path.join(self.test_dir, "basic_metadata.xml")
        md = FreeCAD.Metadata(filename)

        # Tags with only one element:
        self.assertEqual(md.Name, "Test Workbench")
        self.assertEqual(md.Description, "A package.xml file for unit testing.")
        self.assertEqual(md.Version, "1.0.1")
        #self.assertEqual(md.Date, "2022-01-07")
        self.assertEqual(md.Icon, "Resources/icons/PackageIcon.svg")

        # Tags that are lists of elements:
        maintainers = md.Maintainer
        self.assertEqual(len(maintainers), 2)
        
        authors = md.Author
        self.assertEqual(len(authors), 3)

        urls = md.Urls
        self.assertEqual(len(urls), 4)

        tags = md.Tag
        self.assertEqual(len(tags), 2)
Example #12
0
def InitApplications():
	# Checking on FreeCAD module path ++++++++++++++++++++++++++++++++++++++++++
	ModDir = FreeCAD.getHomePath()+'Mod'
	ModDir = os.path.realpath(ModDir)
	ExtDir = FreeCAD.getHomePath()+'Ext'
	ExtDir = os.path.realpath(ExtDir)
	BinDir = FreeCAD.getHomePath()+'bin'
	BinDir = os.path.realpath(BinDir)
	libpaths = []
	LibDir = FreeCAD.getHomePath()+'lib'
	LibDir = os.path.realpath(LibDir)
	if os.path.exists(LibDir):
		libpaths.append(LibDir)
	Lib64Dir = FreeCAD.getHomePath()+'lib64'
	Lib64Dir = os.path.realpath(Lib64Dir)
	if os.path.exists(Lib64Dir):
		libpaths.append(Lib64Dir)
	LibPyDir = FreeCAD.getHomePath()+'lib-py3'
	LibPyDir = os.path.realpath(LibPyDir)
	if (os.path.exists(LibPyDir)):
		libpaths.append(LibPyDir)
	AddPath = FreeCAD.ConfigGet("AdditionalModulePaths").split(";")
	HomeMod = FreeCAD.getUserAppDataDir()+"Mod"
	HomeMod = os.path.realpath(HomeMod)
	MacroDir = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro").GetString("MacroPath")
	MacroMod = os.path.realpath(MacroDir+"/Mod")
	SystemWideMacroDir = FreeCAD.getHomePath()+'Macro'
	SystemWideMacroDir = os.path.realpath(SystemWideMacroDir)

	#print FreeCAD.getHomePath()
	if os.path.isdir(FreeCAD.getHomePath()+'src\\Tools'):
		sys.path.append(FreeCAD.getHomePath()+'src\\Tools')



	# Searching for module dirs +++++++++++++++++++++++++++++++++++++++++++++++++++
	# Use dict to handle duplicated module names
	ModDict = {}
	if os.path.isdir(ModDir):
		ModDirs = os.listdir(ModDir)
		for i in ModDirs: ModDict[i.lower()] = os.path.join(ModDir,i)
	else:
		Wrn ("No modules found in " + ModDir + "\n")
	# Search for additional modules in the home directory
	if os.path.isdir(HomeMod):
		HomeMods = os.listdir(HomeMod)
		for i in HomeMods: ModDict[i.lower()] = os.path.join(HomeMod,i)
	elif os.path.isdir(os.path.join(os.path.expanduser("~"),".FreeCAD","Mod")):
		# Check if old location exists
		Wrn ("User path has changed to " + FreeCAD.getUserAppDataDir() + ". Please move user modules and macros\n")
	# Search for additional modules in the macro directory
	if os.path.isdir(MacroMod):
		MacroMods = os.listdir(MacroMod)
		for i in MacroMods:
			key = i.lower()
			if key not in ModDict: ModDict[key] = os.path.join(MacroMod,i)
	# Search for additional modules in command line
	for i in AddPath:
		if os.path.isdir(i): ModDict[i] = i
	#AddModPaths = App.ParamGet("System parameter:AdditionalModulePaths")
	#Err( AddModPaths)
	# add also this path so that all modules search for libraries
	# they depend on first here
	PathExtension = []
	PathExtension.append(BinDir)

	# prepend all module paths to Python search path
	Log('Init:   Searching for modules...\n')


	# to have all the module-paths available in FreeCADGuiInit.py:
	FreeCAD.__ModDirs__ = list(ModDict.values())

	# this allows importing with:
	# from FreeCAD.Module import package
	FreeCAD.__path__ = [ModDir] + libpaths + [HomeMod]

	# also add these directories to the sys.path to
	# not change the old behaviour. once we have moved to
	# proper python modules this can eventually be removed.
	sys.path = [ModDir] + libpaths + [ExtDir] + sys.path

    # The AddonManager may install additional Python packages in
    # this path:
	additional_packages_path = os.path.join(FreeCAD.getUserAppDataDir(),"AdditionalPythonPackages")
	if os.path.isdir(additional_packages_path):
		sys.path.append(additional_packages_path)

	def RunInitPy(Dir):
		InstallFile = os.path.join(Dir,"Init.py")
		if (os.path.exists(InstallFile)):
			try:
				with open(file=InstallFile, encoding="utf-8") as f:
					exec(f.read())
			except Exception as inst:
				Log('Init:      Initializing ' + Dir + '... failed\n')
				Log('-'*100+'\n')
				Log(traceback.format_exc())
				Log('-'*100+'\n')
				Err('During initialization the error "' + str(inst) + '" occurred in ' + InstallFile + '\n')
				Err('Please look into the log file for further information\n')
			else:
				Log('Init:      Initializing ' + Dir + '... done\n')
		else:
			Log('Init:      Initializing ' + Dir + '(Init.py not found)... ignore\n')

	for Dir in ModDict.values():
		if ((Dir != '') & (Dir != 'CVS') & (Dir != '__init__.py')):
			sys.path.insert(0,Dir)
			PathExtension.append(Dir)
			MetadataFile = os.path.join(Dir, "package.xml")
			if os.path.exists(MetadataFile):
				meta = FreeCAD.Metadata(MetadataFile)
				content = meta.Content
				if "workbench" in content:
					workbenches = content["workbench"]
					for workbench in workbenches:
						subdirectory = workbench.Name if not workbench.Subdirectory else workbench.Subdirectory
						subdirectory = subdirectory.replace("/",os.path.sep)
						subdirectory = os.path.join(Dir, subdirectory)
						classname = workbench.Classname
						sys.path.insert(0,subdirectory)
						PathExtension.append(subdirectory)
						RunInitPy(subdirectory)
				else:
					continue # The package content says there are no workbenches here, so just skip
			else:
				RunInitPy(Dir)

	extension_modules = []

	try:
		import pkgutil
		import importlib
		import freecad
		for _, freecad_module_name, freecad_module_ispkg in pkgutil.iter_modules(freecad.__path__, "freecad."):
			if freecad_module_ispkg:
				Log('Init: Initializing ' + freecad_module_name + '\n')
				try:
					freecad_module = importlib.import_module(freecad_module_name)
					extension_modules += [freecad_module_name]
					if any (module_name == 'init' for _, module_name, ispkg in pkgutil.iter_modules(freecad_module.__path__)):
						importlib.import_module(freecad_module_name + '.init')
						Log('Init: Initializing ' + freecad_module_name + '... done\n')
					else:
						Log('Init: No init module found in ' + freecad_module_name + ', skipping\n')
				except Exception as inst:
					Err('During initialization the error "' + str(inst) + '" occurred in ' + freecad_module_name + '\n')
					Err('-'*80+'\n')
					Err(traceback.format_exc())
					Err('-'*80+'\n')
					Log('Init:      Initializing ' + freecad_module_name + '... failed\n')
					Log('-'*80+'\n')
					Log(traceback.format_exc())
					Log('-'*80+'\n')
	except ImportError as inst:
		Err('During initialization the error "' + str(inst) + '" occurred\n')

	Log("Using "+ModDir+" as module path!\n")
	# In certain cases the PathExtension list can contain invalid strings. We concatenate them to a single string
	# but check that the output is a valid string
	setupSearchPaths(PathExtension)
	path = os.environ["PATH"].split(os.pathsep)
	Log("System path after init:\n")
	for i in path:
		Log("   " + i + "\n")
	# add MacroDir to path (RFE #0000504)
	sys.path.append(MacroDir)
	# add SystemWideMacroDir to path
	sys.path.append(SystemWideMacroDir)
	# add special path for MacOSX (bug #0000307)
	import platform
	if len(platform.mac_ver()[0]) > 0:
		sys.path.append(os.path.expanduser('~/Library/Application Support/FreeCAD/Mod'))
Example #13
0
def InitApplications():
    import sys, os, traceback
    try:
        # Python3
        import io as cStringIO
    except ImportError:
        # Python2
        import cStringIO
    # Searching modules dirs +++++++++++++++++++++++++++++++++++++++++++++++++++
    # (additional module paths are already cached)
    ModDirs = FreeCAD.__ModDirs__
    #print ModDirs
    Log('Init:   Searching modules...\n')

    def RunInitGuiPy(Dir) -> bool:
        InstallFile = os.path.join(Dir, "InitGui.py")
        if (os.path.exists(InstallFile)):
            try:
                with open(file=InstallFile, encoding="utf-8") as f:
                    exec(f.read())
            except Exception as inst:
                Log('Init:      Initializing ' + Dir + '... failed\n')
                Log('-' * 100 + '\n')
                Log(traceback.format_exc())
                Log('-' * 100 + '\n')
                Err('During initialization the error "' + str(inst) +
                    '" occurred in ' + InstallFile + '\n')
                Err('Please look into the log file for further information\n')
            else:
                Log('Init:      Initializing ' + Dir + '... done\n')
                return True
        else:
            Log('Init:      Initializing ' + Dir +
                '(InitGui.py not found)... ignore\n')
        return False

    for Dir in ModDirs:
        if ((Dir != '') & (Dir != 'CVS') & (Dir != '__init__.py')):
            stopFile = os.path.join(Dir, "ADDON_DISABLED")
            if os.path.exists(stopFile):
                Msg(f'NOTICE: Addon "{Dir}" disabled by presence of ADDON_DISABLED stopfile\n'
                    )
                continue
            MetadataFile = os.path.join(Dir, "package.xml")
            if os.path.exists(MetadataFile):
                meta = FreeCAD.Metadata(MetadataFile)
                if not meta.supportsCurrentFreeCAD():
                    continue
                content = meta.Content
                if "workbench" in content:
                    FreeCAD.Gui.addIconPath(Dir)
                    workbenches = content["workbench"]
                    for workbench_metadata in workbenches:
                        if not workbench_metadata.supportsCurrentFreeCAD():
                            continue
                        subdirectory = workbench_metadata.Name if not workbench_metadata.Subdirectory else workbench_metadata.Subdirectory
                        subdirectory = subdirectory.replace("/", os.path.sep)
                        subdirectory = os.path.join(Dir, subdirectory)
                        ran_init = RunInitGuiPy(subdirectory)

                        if ran_init:
                            # Try to generate a new icon from the metadata-specified information
                            classname = workbench_metadata.Classname
                            if classname:
                                try:
                                    wb_handle = FreeCAD.Gui.getWorkbench(
                                        classname)
                                except Exception:
                                    Log(f"Failed to get handle to {classname} -- no icon can be generated, check classname in package.xml\n"
                                        )
                                else:
                                    GeneratePackageIcon(
                                        dir, subdirectory, workbench_metadata,
                                        wb_handle)
                else:
                    continue  # The package content says there are no workbenches here, so just skip
            else:
                RunInitGuiPy(Dir)
    Log("All modules with GUIs using InitGui.py are now initialized\n")

    extension_modules = []
    try:
        import pkgutil
        import importlib
        import freecad
        freecad.gui = FreeCADGui
        for _, freecad_module_name, freecad_module_ispkg in pkgutil.iter_modules(
                freecad.__path__, "freecad."):
            # Check for a stopfile
            stopFile = os.path.join(FreeCAD.getUserAppDataDir(), "Mod",
                                    freecad_module_name[8:], "ADDON_DISABLED")
            if os.path.exists(stopFile):
                continue

            # Make sure that package.xml (if present) does not exclude this version of FreeCAD
            MetadataFile = os.path.join(FreeCAD.getUserAppDataDir(), "Mod",
                                        freecad_module_name[8:], "package.xml")
            if os.path.exists(MetadataFile):
                meta = FreeCAD.Metadata(MetadataFile)
                if not meta.supportsCurrentFreeCAD():
                    continue

            if freecad_module_ispkg:
                Log('Init: Initializing ' + freecad_module_name + '\n')
                try:
                    freecad_module = importlib.import_module(
                        freecad_module_name)
                    if any(module_name == 'init_gui' for _, module_name, ispkg
                           in pkgutil.iter_modules(freecad_module.__path__)):
                        importlib.import_module(freecad_module_name +
                                                '.init_gui')
                        Log('Init: Initializing ' + freecad_module_name +
                            '... done\n')
                    else:
                        Log('Init: No init_gui module found in ' +
                            freecad_module_name + ', skipping\n')
                except Exception as inst:
                    Err('During initialization the error "' + str(inst) +
                        '" occurred in ' + freecad_module_name + '\n')
                    Err('-' * 80 + '\n')
                    Err(traceback.format_exc())
                    Err('-' * 80 + '\n')
                    Log('Init:      Initializing ' + freecad_module_name +
                        '... failed\n')
                    Log('-' * 80 + '\n')
                    Log(traceback.format_exc())
                    Log('-' * 80 + '\n')
    except ImportError as inst:
        Err('During initialization the error "' + str(inst) + '" occurred\n')

    Log("All modules with GUIs initialized using pkgutil are now initialized\n"
        )
 def load_metadata_file(self, file: str) -> None:
     if os.path.isfile(file):
         metadata = FreeCAD.Metadata(file)
         self.set_metadata(metadata)
Example #15
0
 def test_default_constructor(self):
     try:
         _ = FreeCAD.Metadata()
     except Exception:
         self.fail("Metadata default constructor failed")