From 802993dc5d5d2dad591bc0f388f611bdc5a600ed Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Wed, 27 Jul 2022 15:39:35 -0500 Subject: [PATCH] Addon Manager: Clean up Addon.py based on pylint --- src/Mod/AddonManager/Addon.py | 97 ++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 37 deletions(-) diff --git a/src/Mod/AddonManager/Addon.py b/src/Mod/AddonManager/Addon.py index 6a69dbe0ec..4749487801 100644 --- a/src/Mod/AddonManager/Addon.py +++ b/src/Mod/AddonManager/Addon.py @@ -20,15 +20,19 @@ # * * # *************************************************************************** -import FreeCAD +""" Defines the Addon class to encapsulate information about FreeCAD Addons """ import os from urllib.parse import urlparse from typing import Dict, Set from threading import Lock +from enum import IntEnum + +import FreeCAD from addonmanager_macro import Macro import addonmanager_utilities as utils +from addonmanager_utilities import construct_git_url translate = FreeCAD.Qt.translate @@ -51,11 +55,11 @@ INTERNAL_WORKBENCHES["techdraw"] = "TechDraw" class Addon: - "Encapsulate information about a FreeCAD addon" - - from enum import IntEnum + """Encapsulates information about a FreeCAD addon""" class Kind(IntEnum): + """The type of Addon: Workbench, macro, or package""" + WORKBENCH = 1 MACRO = 2 PACKAGE = 3 @@ -63,12 +67,15 @@ class Addon: def __str__(self) -> str: if self.value == 1: return "Workbench" - elif self.value == 2: + if self.value == 2: return "Macro" - elif self.value == 3: + if self.value == 3: return "Package" + return "ERROR_TYPE" class Status(IntEnum): + """The installation status of an Addon""" + NOT_INSTALLED = 0 UNCHECKED = 1 NO_UPDATE_AVAILABLE = 2 @@ -82,20 +89,26 @@ class Addon: return NotImplemented def __str__(self) -> str: + result = "" if self.value == 0: - return "Not installed" + result = "Not installed" elif self.value == 1: - return "Unchecked" + result = "Unchecked" elif self.value == 2: - return "No update available" + result = "No update available" elif self.value == 3: - return "Update available" + result = "Update available" elif self.value == 4: - return "Restart required" + result = "Restart required" elif self.value == 5: - return "Can't check" + result = "Can't check" + else: + result = "ERROR_STATUS" + return result class Dependencies: + """Addon dependency information""" + def __init__(self): self.required_external_addons = [] # A list of Addons self.blockers = [] # A list of Addons @@ -105,8 +118,7 @@ class Addon: self.python_optional: Set[str] = set() class ResolutionFailed(RuntimeError): - def __init__(self, msg): - super().__init__(msg) + """An exception type for dependency resolution failure.""" def __init__(self, name: str, url: str, status: Status, branch: str): self.name = name.strip() @@ -127,8 +139,6 @@ class Addon: self.status_lock = Lock() self.set_status(status) - from addonmanager_utilities import construct_git_url - # The url should never end in ".git", so strip it if it's there parsed_url = urlparse(self.url) if parsed_url.path.endswith(".git"): @@ -172,7 +182,9 @@ class Addon: return result @classmethod - def from_macro(self, macro: Macro): + def from_macro(cls, macro: Macro): + """Create an Addon object from a Macro wrapper object""" + if macro.is_installed(): status = Addon.Status.UNCHECKED else: @@ -184,7 +196,7 @@ class Addon: return instance @classmethod - def from_cache(self, cache_dict: Dict): + def from_cache(cls, cache_dict: Dict): """Load basic data from cached dict data. Does not include Macro or Metadata information, which must be populated separately.""" mod_dir = os.path.join(FreeCAD.getUserAppDataDir(), "Mod", cache_dict["name"]) @@ -240,13 +252,18 @@ class Addon: } 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("Internal error: {} does not exist".format(file)) + FreeCAD.Console.PrintLog(f"Internal error: {file} does not exist") def set_metadata(self, metadata: FreeCAD.Metadata) -> None: + """Set the given metadata object as this object's metadata, updating the object's display name + and package type information to match, as well as updating any dependency information, etc.""" + self.metadata = metadata self.display_name = metadata.Name self.repo_type = Addon.Kind.PACKAGE @@ -262,6 +279,9 @@ class Addon: self.extract_metadata_dependencies(self.metadata) def version_is_ok(self, metadata) -> bool: + """Checks to see if the current running version of FreeCAD meets the requirements set by + the passed-in metadata parameter.""" + dep_fc_min = metadata.FreeCADMin dep_fc_max = metadata.FreeCADMax @@ -273,7 +293,7 @@ class Addon: required_version = dep_fc_min.split(".") if fc_major < int(required_version[0]): return False # Major version is too low - elif fc_major == int(required_version[0]): + if fc_major == int(required_version[0]): if len(required_version) > 1 and fc_minor < int( required_version[1] ): @@ -288,7 +308,7 @@ class Addon: required_version = dep_fc_max.split(".") if fc_major > int(required_version[0]): return False # Major version is too high - elif fc_major == int(required_version[0]): + if fc_major == int(required_version[0]): if len(required_version) > 1 and fc_minor > int( required_version[1] ): @@ -301,6 +321,7 @@ class Addon: return True def extract_metadata_dependencies(self, metadata): + """Read dependency information from a metadata object and store it in this Addon""" # Version check: if this piece of metadata doesn't apply to this version of # FreeCAD, just skip it. @@ -345,6 +366,7 @@ class Addon: ) def extract_tags(self, metadata: FreeCAD.Metadata) -> None: + """Read the tags from the metadata object""" # Version check: if this piece of metadata doesn't apply to this version of # FreeCAD, just skip it. @@ -355,7 +377,7 @@ class Addon: self.tags.add(new_tag) content = metadata.Content - for key, value in content.items(): + for _, value in content.items(): for item in value: self.extract_tags(item) @@ -364,7 +386,7 @@ class Addon: if self.repo_type == Addon.Kind.WORKBENCH: return True - elif self.repo_type == Addon.Kind.PACKAGE: + if self.repo_type == Addon.Kind.PACKAGE: if self.metadata is None: FreeCAD.Console.PrintLog( f"Addon Manager internal error: lost metadata for package {self.name}\n" @@ -377,15 +399,14 @@ class Addon: ) return False return "workbench" in content - else: - return False + return False def contains_macro(self) -> bool: """Determine if this package contains (or is) a macro""" if self.repo_type == Addon.Kind.MACRO: return True - elif self.repo_type == Addon.Kind.PACKAGE: + if self.repo_type == Addon.Kind.PACKAGE: if self.metadata is None: FreeCAD.Console.PrintLog( f"Addon Manager internal error: lost metadata for package {self.name}\n" @@ -393,8 +414,7 @@ class Addon: return False content = self.metadata.Content return "macro" in content - else: - return False + return False def contains_preference_pack(self) -> bool: """Determine if this package contains a preference pack""" @@ -407,8 +427,7 @@ class Addon: return False content = self.metadata.Content return "preferencepack" in content - else: - return False + return False def get_cached_icon_filename(self) -> str: """Get the filename for the locally-cached copy of the icon""" @@ -481,37 +500,41 @@ class Addon: deps.blockers[dep] = all_repos[dep] def status(self): + """Threadsafe access to the current update status""" with self.status_lock: return self.update_status def set_status(self, status): + """Threadsafe setting of the update status""" with self.status_lock: self.update_status = status def is_disabled(self): - # Check for existence of disabling stopfile: + """Check to see if the disabling stopfile exists""" + stopfile = os.path.join( FreeCAD.getUserAppDataDir(), "Mod", self.name, "ADDON_DISABLED" ) - if os.path.exists(stopfile): - return True - else: - return False + return os.path.exists(stopfile) def disable(self): + """Disable this addon from loading when FreeCAD starts up by creating a stopfile""" + stopfile = os.path.join( FreeCAD.getUserAppDataDir(), "Mod", self.name, "ADDON_DISABLED" ) - with open(stopfile, "w") as f: + with open(stopfile, "w", encoding="utf-8") as f: f.write( "The existence of this file prevents FreeCAD from loading this Addon. To re-enable, delete the file." ) def enable(self): + """Re-enable loading this addon by deleting the stopfile""" + stopfile = os.path.join( FreeCAD.getUserAppDataDir(), "Mod", self.name, "ADDON_DISABLED" ) try: os.unlink(stopfile) - except Exception: + except FileNotFoundError: pass