Addon Manager: Renaming and cleanup

This commit is contained in:
Chris Hennes
2022-02-27 22:50:54 -06:00
parent b5b86863e7
commit 0d6a506447
14 changed files with 274 additions and 262 deletions

View File

@@ -1,6 +1,6 @@
# ***************************************************************************
# * *
# * Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
# * Copyright (c) 2022 FreeCAD Project Association *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
@@ -33,12 +33,12 @@ import addonmanager_utilities as utils
translate = FreeCAD.Qt.translate
class AddonManagerRepo:
class Addon:
"Encapsulate information about a FreeCAD addon"
from enum import IntEnum
class RepoType(IntEnum):
class Kind(IntEnum):
WORKBENCH = 1
MACRO = 2
PACKAGE = 3
@@ -51,7 +51,7 @@ class AddonManagerRepo:
elif self.value == 3:
return "Package"
class UpdateStatus(IntEnum):
class Status(IntEnum):
NOT_INSTALLED = 0
UNCHECKED = 1
NO_UPDATE_AVAILABLE = 2
@@ -91,7 +91,7 @@ class AddonManagerRepo:
def __init__(self, msg):
super().__init__(msg)
def __init__(self, name: str, url: str, status: UpdateStatus, branch: str):
def __init__(self, name: str, url: str, status: Status, branch: str):
self.name = name.strip()
self.display_name = self.name
self.url = url.strip()
@@ -99,7 +99,7 @@ class AddonManagerRepo:
self.python2 = False
self.obsolete = False
self.rejected = False
self.repo_type = AddonManagerRepo.RepoType.WORKBENCH
self.repo_type = Addon.Kind.WORKBENCH
self.description = None
self.tags = set() # Just a cache, loaded from Metadata
@@ -155,12 +155,12 @@ class AddonManagerRepo:
@classmethod
def from_macro(self, macro: Macro):
if macro.is_installed():
status = AddonManagerRepo.UpdateStatus.UNCHECKED
status = Addon.Status.UNCHECKED
else:
status = AddonManagerRepo.UpdateStatus.NOT_INSTALLED
instance = AddonManagerRepo(macro.name, macro.url, status, "master")
status = Addon.Status.NOT_INSTALLED
instance = Addon(macro.name, macro.url, status, "master")
instance.macro = macro
instance.repo_type = AddonManagerRepo.RepoType.MACRO
instance.repo_type = Addon.Kind.MACRO
instance.description = macro.desc
return instance
@@ -170,18 +170,18 @@ class AddonManagerRepo:
mod_dir = os.path.join(FreeCAD.getUserAppDataDir(), "Mod", cache_dict["name"])
if os.path.isdir(mod_dir):
status = AddonManagerRepo.UpdateStatus.UNCHECKED
status = Addon.Status.UNCHECKED
else:
status = AddonManagerRepo.UpdateStatus.NOT_INSTALLED
instance = AddonManagerRepo(
status = Addon.Status.NOT_INSTALLED
instance = Addon(
cache_dict["name"], cache_dict["url"], status, cache_dict["branch"]
)
for key, value in cache_dict.items():
instance.__dict__[key] = value
instance.repo_type = AddonManagerRepo.RepoType(cache_dict["repo_type"])
if instance.repo_type == AddonManagerRepo.RepoType.PACKAGE:
instance.repo_type = Addon.Kind(cache_dict["repo_type"])
if instance.repo_type == Addon.Kind.PACKAGE:
# There must be a cached metadata file, too
cached_package_xml_file = os.path.join(
FreeCAD.getUserCachePath(),
@@ -232,7 +232,7 @@ class AddonManagerRepo:
def set_metadata(self, metadata: FreeCAD.Metadata) -> None:
self.metadata = metadata
self.display_name = metadata.Name
self.repo_type = AddonManagerRepo.RepoType.PACKAGE
self.repo_type = Addon.Kind.PACKAGE
self.description = metadata.Description
for url in metadata.Urls:
if "type" in url and url["type"] == "repository":
@@ -277,9 +277,9 @@ class AddonManagerRepo:
def contains_workbench(self) -> bool:
"""Determine if this package contains (or is) a workbench"""
if self.repo_type == AddonManagerRepo.RepoType.WORKBENCH:
if self.repo_type == Addon.Kind.WORKBENCH:
return True
elif self.repo_type == AddonManagerRepo.RepoType.PACKAGE:
elif self.repo_type == Addon.Kind.PACKAGE:
if self.metadata is None:
FreeCAD.Console.PrintWarning(
f"Addon Manager internal error: lost metadata for package {self.name}\n"
@@ -298,9 +298,9 @@ class AddonManagerRepo:
def contains_macro(self) -> bool:
"""Determine if this package contains (or is) a macro"""
if self.repo_type == AddonManagerRepo.RepoType.MACRO:
if self.repo_type == Addon.Kind.MACRO:
return True
elif self.repo_type == AddonManagerRepo.RepoType.PACKAGE:
elif self.repo_type == Addon.Kind.PACKAGE:
if self.metadata is None:
FreeCAD.Console.PrintWarning(
f"Addon Manager internal error: lost metadata for package {self.name}\n"
@@ -314,7 +314,7 @@ class AddonManagerRepo:
def contains_preference_pack(self) -> bool:
"""Determine if this package contains a preference pack"""
if self.repo_type == AddonManagerRepo.RepoType.PACKAGE:
if self.repo_type == Addon.Kind.PACKAGE:
if self.metadata is None:
FreeCAD.Console.PrintWarning(
f"Addon Manager internal error: lost metadata for package {self.name}\n"

View File

@@ -4,7 +4,7 @@
# ***************************************************************************
# * *
# * Copyright (c) 2015 Yorik van Havre <yorik@uncreated.net> *
# * Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
# * Copyright (c) 2022 FreeCAD Project Association *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
@@ -40,7 +40,7 @@ import addonmanager_utilities as utils
import AddonManager_rc
from package_list import PackageList, PackageListItemModel
from package_details import PackageDetails
from AddonManagerRepo import AddonManagerRepo
from Addon import Addon
from install_to_toolbar import (
ask_to_install_toolbar_button,
remove_custom_toolbar_button,
@@ -496,7 +496,7 @@ class CommandAddonManager:
# Write the cache data if it's safe to do so:
if not worker_killed:
for repo in self.item_model.repos:
if repo.repo_type == AddonManagerRepo.RepoType.MACRO:
if repo.repo_type == Addon.Kind.MACRO:
self.cache_macro(repo)
else:
self.cache_package(repo)
@@ -633,7 +633,7 @@ class CommandAddonManager:
) # Link to step 2
self.update_worker.start()
def cache_package(self, repo: AddonManagerRepo):
def cache_package(self, repo: Addon):
if not hasattr(self, "package_cache"):
self.package_cache = {}
self.package_cache[repo.name] = repo.to_cache()
@@ -671,7 +671,7 @@ class CommandAddonManager:
self.macro_worker.finished.connect(self.do_next_startup_phase)
self.macro_worker.start()
def cache_macro(self, repo: AddonManagerRepo):
def cache_macro(self, repo: Addon):
if not hasattr(self, "macro_cache"):
self.macro_cache = []
if repo.macro is not None:
@@ -724,7 +724,7 @@ class CommandAddonManager:
)
self.startup()
def on_package_updated(self, repo: AddonManagerRepo) -> None:
def on_package_updated(self, repo: Addon) -> None:
"""Called when the named package has either new metadata or a new icon (or both)"""
with self.lock:
@@ -788,12 +788,12 @@ class CommandAddonManager:
self.check_worker.start()
self.enable_updates(len(self.packages_with_updates))
def status_updated(self, repo: AddonManagerRepo) -> None:
def status_updated(self, repo: Addon) -> None:
self.item_model.reload_item(repo)
if repo.status() == AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE:
if repo.status() == Addon.Status.UPDATE_AVAILABLE:
self.packages_with_updates.append(repo)
self.enable_updates(len(self.packages_with_updates))
elif repo.status() == AddonManagerRepo.UpdateStatus.PENDING_RESTART:
elif repo.status() == Addon.Status.PENDING_RESTART:
self.restart_required = True
def enable_updates(self, number_of_updates: int) -> None:
@@ -819,7 +819,7 @@ class CommandAddonManager:
self.enable_updates(len(self.packages_with_updates))
self.dialog.buttonCheckForUpdates.setEnabled(True)
def add_addon_repo(self, addon_repo: AddonManagerRepo) -> None:
def add_addon_repo(self, addon_repo: Addon) -> None:
"""adds a workbench to the list"""
if addon_repo.icon is None or addon_repo.icon.isNull():
@@ -832,17 +832,17 @@ class CommandAddonManager:
return
self.item_model.append_item(addon_repo)
def get_icon(self, repo: AddonManagerRepo, update: bool = False) -> QtGui.QIcon:
def get_icon(self, repo: Addon, update: bool = False) -> QtGui.QIcon:
"""returns an icon for a repo"""
if not update and repo.icon and not repo.icon.isNull() and repo.icon.isValid():
return repo.icon
path = ":/icons/" + repo.name.replace(" ", "_")
if repo.repo_type == AddonManagerRepo.RepoType.WORKBENCH:
if repo.repo_type == Addon.Kind.WORKBENCH:
path += "_workbench_icon.svg"
default_icon = QtGui.QIcon(":/icons/document-package.svg")
elif repo.repo_type == AddonManagerRepo.RepoType.MACRO:
elif repo.repo_type == Addon.Kind.MACRO:
if repo.macro and repo.macro.icon:
if os.path.isabs(repo.macro.icon):
path = repo.macro.icon
@@ -864,7 +864,7 @@ class CommandAddonManager:
else:
path += "_macro_icon.svg"
default_icon = QtGui.QIcon(":/icons/document-python.svg")
elif repo.repo_type == AddonManagerRepo.RepoType.PACKAGE:
elif repo.repo_type == Addon.Kind.PACKAGE:
# The cache might not have been downloaded yet, check to see if it's there...
if os.path.isfile(repo.get_cached_icon_filename()):
path = repo.get_cached_icon_filename()
@@ -885,7 +885,7 @@ class CommandAddonManager:
return addonicon
def table_row_activated(self, selected_repo: AddonManagerRepo) -> None:
def table_row_activated(self, selected_repo: Addon) -> None:
"""a row was activated, show the relevant data"""
self.packageList.hide()
@@ -898,7 +898,7 @@ class CommandAddonManager:
self.dialog.labelStatusInfo.setText(message)
self.dialog.labelStatusInfo.repaint()
def show_workbench(self, repo: AddonManagerRepo) -> None:
def show_workbench(self, repo: Addon) -> None:
self.packageList.hide()
self.packageDetails.show()
self.packageDetails.show_repo(repo)
@@ -907,16 +907,16 @@ class CommandAddonManager:
self.packageDetails.hide()
self.packageList.show()
def append_to_repos_list(self, repo: AddonManagerRepo) -> None:
def append_to_repos_list(self, repo: Addon) -> None:
"""this function allows threads to update the main list of workbenches"""
self.item_model.append_item(repo)
def resolve_dependencies(self, repo: AddonManagerRepo) -> None:
def resolve_dependencies(self, repo: Addon) -> None:
if not repo:
return
deps = AddonManagerRepo.Dependencies()
deps = Addon.Dependencies()
repo_name_dict = dict()
for r in self.item_model.repos:
repo_name_dict[repo.name] = r
@@ -941,7 +941,7 @@ class CommandAddonManager:
missing_external_addons = []
for dep in deps.required_external_addons:
if dep.status() == AddonManagerRepo.UpdateStatus.NOT_INSTALLED:
if dep.status() == Addon.Status.NOT_INSTALLED:
missing_external_addons.append(dep)
# Now check the loaded addons to see if we are missing an internal workbench:
@@ -1069,7 +1069,7 @@ class CommandAddonManager:
else:
self.install(repo)
def dependency_dialog_yes_clicked(self, repo: AddonManagerRepo) -> None:
def dependency_dialog_yes_clicked(self, repo: Addon) -> None:
# Get the lists out of the dialog:
addons = []
for row in range(self.dependency_dialog.listWidgetAddons.count()):
@@ -1116,7 +1116,7 @@ class CommandAddonManager:
self.dependency_installation_dialog.show()
self.dependency_installation_worker.start()
def no_python_exe(self, repo: AddonManagerRepo) -> None:
def no_python_exe(self, repo: Addon) -> None:
if hasattr(self, "dependency_installation_dialog"):
self.dependency_installation_dialog.hide()
result = QtWidgets.QMessageBox.critical(
@@ -1136,7 +1136,7 @@ class CommandAddonManager:
if result == QtWidgets.QMessageBox.Yes:
self.install(repo)
def no_pip(self, command: str, repo: AddonManagerRepo) -> None:
def no_pip(self, command: str, repo: Addon) -> None:
if hasattr(self, "dependency_installation_dialog"):
self.dependency_installation_dialog.hide()
result = QtWidgets.QMessageBox.critical(
@@ -1169,7 +1169,7 @@ class CommandAddonManager:
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
)
def dependency_dialog_ignore_clicked(self, repo: AddonManagerRepo) -> None:
def dependency_dialog_ignore_clicked(self, repo: Addon) -> None:
self.install(repo)
def cancel_dependency_installation(self) -> None:
@@ -1177,7 +1177,7 @@ class CommandAddonManager:
self.dependency_installation_worker.requestInterruption()
self.dependency_installation_dialog.hide()
def install(self, repo: AddonManagerRepo) -> None:
def install(self, repo: Addon) -> None:
"""installs or updates a workbench, macro, or package"""
if hasattr(self, "install_worker") and self.install_worker:
@@ -1191,8 +1191,8 @@ class CommandAddonManager:
return
if (
repo.repo_type == AddonManagerRepo.RepoType.WORKBENCH
or repo.repo_type == AddonManagerRepo.RepoType.PACKAGE
repo.repo_type == Addon.Kind.WORKBENCH
or repo.repo_type == Addon.Kind.PACKAGE
):
self.show_progress_widgets()
self.install_worker = InstallWorkbenchWorker(repo)
@@ -1203,7 +1203,7 @@ class CommandAddonManager:
self.install_worker.success.connect(self.on_package_installed)
self.install_worker.failure.connect(self.on_installation_failed)
self.install_worker.start()
elif repo.repo_type == AddonManagerRepo.RepoType.MACRO:
elif repo.repo_type == Addon.Kind.MACRO:
macro = repo.macro
# To try to ensure atomicity, test the installation into a temp directory first,
@@ -1238,16 +1238,14 @@ class CommandAddonManager:
message += error
self.on_installation_failed(repo, message)
def update(self, repo: AddonManagerRepo) -> None:
def update(self, repo: Addon) -> None:
self.install(repo)
def mark_repo_update_available(
self, repo: AddonManagerRepo, available: bool
) -> None:
def mark_repo_update_available(self, repo: Addon, available: bool) -> None:
if available:
repo.set_status(AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE)
repo.set_status(Addon.Status.UPDATE_AVAILABLE)
else:
repo.set_status(AddonManagerRepo.UpdateStatus.NO_UPDATE_AVAILABLE)
repo.set_status(Addon.Status.NO_UPDATE_AVAILABLE)
self.item_model.reload_item(repo)
self.packageDetails.show_repo(repo)
@@ -1279,9 +1277,7 @@ class CommandAddonManager:
def on_update_all_completed(self) -> None:
self.hide_progress_widgets()
def get_package_list(
message: str, repos: List[AddonManagerRepo], threshold: int
):
def get_package_list(message: str, repos: List[Addon], threshold: int):
"""To ensure that the list doesn't get too long for the dialog, cut it off at some threshold"""
num_updates = len(repos)
if num_updates < threshold:
@@ -1339,11 +1335,9 @@ class CommandAddonManager:
for installed_repo in self.subupdates_succeeded:
if installed_repo.contains_workbench():
self.restart_required = True
installed_repo.set_status(AddonManagerRepo.UpdateStatus.PENDING_RESTART)
installed_repo.set_status(Addon.Status.PENDING_RESTART)
else:
installed_repo.set_status(
AddonManagerRepo.UpdateStatus.NO_UPDATE_AVAILABLE
)
installed_repo.set_status(Addon.Status.NO_UPDATE_AVAILABLE)
self.item_model.reload_item(installed_repo)
for requested_repo in self.packages_with_updates:
if installed_repo.name == requested_repo.name:
@@ -1407,7 +1401,7 @@ class CommandAddonManager:
"AddonManager recaches."
)
def on_package_installed(self, repo: AddonManagerRepo, message: str) -> None:
def on_package_installed(self, repo: Addon, message: str) -> None:
self.hide_progress_widgets()
QtWidgets.QMessageBox.information(
None,
@@ -1416,16 +1410,16 @@ class CommandAddonManager:
QtWidgets.QMessageBox.Close,
)
if repo.contains_workbench():
repo.set_status(AddonManagerRepo.UpdateStatus.PENDING_RESTART)
repo.set_status(Addon.Status.PENDING_RESTART)
self.restart_required = True
else:
repo.set_status(AddonManagerRepo.UpdateStatus.NO_UPDATE_AVAILABLE)
repo.set_status(Addon.Status.NO_UPDATE_AVAILABLE)
self.item_model.reload_item(repo)
self.packageDetails.show_repo(repo)
if repo.repo_type == AddonManagerRepo.RepoType.MACRO:
if repo.repo_type == Addon.Kind.MACRO:
ask_to_install_toolbar_button(repo)
def on_installation_failed(self, _: AddonManagerRepo, message: str) -> None:
def on_installation_failed(self, _: Addon, message: str) -> None:
self.hide_progress_widgets()
QtWidgets.QMessageBox.warning(
None,
@@ -1434,7 +1428,7 @@ class CommandAddonManager:
QtWidgets.QMessageBox.Close,
)
def executemacro(self, repo: AddonManagerRepo) -> None:
def executemacro(self, repo: Addon) -> None:
"""executes a selected macro"""
macro = repo.macro
@@ -1468,7 +1462,7 @@ class CommandAddonManager:
os.chmod(path, stat.S_IWRITE)
func(path)
def remove(self, repo: AddonManagerRepo) -> None:
def remove(self, repo: Addon) -> None:
"""uninstalls a macro or workbench"""
confirm = QtWidgets.QMessageBox.question(
@@ -1483,8 +1477,8 @@ class CommandAddonManager:
return
if (
repo.repo_type == AddonManagerRepo.RepoType.WORKBENCH
or repo.repo_type == AddonManagerRepo.RepoType.PACKAGE
repo.repo_type == Addon.Kind.WORKBENCH
or repo.repo_type == Addon.Kind.PACKAGE
):
basedir = FreeCAD.getUserAppDataDir()
moddir = basedir + os.sep + "Mod"
@@ -1535,7 +1529,7 @@ class CommandAddonManager:
if os.path.exists(clonedir):
shutil.rmtree(clonedir, onerror=self.remove_readonly)
self.item_model.update_item_status(
repo.name, AddonManagerRepo.UpdateStatus.NOT_INSTALLED
repo.name, Addon.Status.NOT_INSTALLED
)
if repo.contains_workbench():
self.restart_required = True
@@ -1548,7 +1542,7 @@ class CommandAddonManager:
)
)
elif repo.repo_type == AddonManagerRepo.RepoType.MACRO:
elif repo.repo_type == Addon.Kind.MACRO:
macro = repo.macro
if macro.remove():
remove_custom_toolbar_button(repo)
@@ -1559,7 +1553,7 @@ class CommandAddonManager:
+ "\n"
)
self.item_model.update_item_status(
repo.name, AddonManagerRepo.UpdateStatus.NOT_INSTALLED
repo.name, Addon.Status.NOT_INSTALLED
)
self.packageDetails.show_repo(repo)
else:

View File

@@ -26,7 +26,7 @@ import unittest
import os
import FreeCAD
from AddonManagerRepo import AddonManagerRepo
from Addon import Addon
from addonmanager_utilities import (
recognized_git_location,
@@ -51,8 +51,8 @@ class TestUtilities(unittest.TestCase):
"https://salsa.debian.org/science-team/freecad",
]
for url in recognized_urls:
repo = AddonManagerRepo(
"Test Repo", url, AddonManagerRepo.UpdateStatus.NOT_INSTALLED, "branch"
repo = Addon(
"Test Repo", url, Addon.Status.NOT_INSTALLED, "branch"
)
self.assertTrue(
recognized_git_location(repo), f"{url} was unexpectedly not recognized"
@@ -65,8 +65,8 @@ class TestUtilities(unittest.TestCase):
"https://github.com.malware.com/",
]
for url in unrecognized_urls:
repo = AddonManagerRepo(
"Test Repo", url, AddonManagerRepo.UpdateStatus.NOT_INSTALLED, "branch"
repo = Addon(
"Test Repo", url, Addon.Status.NOT_INSTALLED, "branch"
)
self.assertFalse(
recognized_git_location(repo), f"{url} was unexpectedly recognized"
@@ -90,8 +90,8 @@ class TestUtilities(unittest.TestCase):
for url in github_urls:
branch = "branchname"
expected_result = f"{url}/raw/{branch}/README.md"
repo = AddonManagerRepo(
"Test Repo", url, AddonManagerRepo.UpdateStatus.NOT_INSTALLED, branch
repo = Addon(
"Test Repo", url, Addon.Status.NOT_INSTALLED, branch
)
actual_result = get_readme_url(repo)
self.assertEqual(actual_result, expected_result)
@@ -99,8 +99,8 @@ class TestUtilities(unittest.TestCase):
for url in gitlab_urls:
branch = "branchname"
expected_result = f"{url}/-/raw/{branch}/README.md"
repo = AddonManagerRepo(
"Test Repo", url, AddonManagerRepo.UpdateStatus.NOT_INSTALLED, branch
repo = Addon(
"Test Repo", url, Addon.Status.NOT_INSTALLED, branch
)
actual_result = get_readme_url(repo)
self.assertEqual(actual_result, expected_result)

View File

@@ -4,13 +4,13 @@ ENDIF (BUILD_GUI)
SET(AddonManager_SRCS
add_toolbar_button_dialog.ui
Addon.py
AddonManager.py
AddonManager.ui
addonmanager_macro.py
addonmanager_utilities.py
addonmanager_workers.py
AddonManagerOptions.ui
AddonManagerRepo.py
ALLOWED_PYTHON_PACKAGES.txt
change_branch.py
change_branch.ui

View File

@@ -4,4 +4,5 @@
# License LGPL
import FreeCAD
FreeCAD.__unit_test__ += ["TestAddonManagerApp"]
FreeCAD.__unit_test__ += ["TestAddonManagerApp"]

View File

@@ -9,4 +9,5 @@ FreeCADGui.addLanguagePath(":/translations")
FreeCADGui.addCommand("Std_AddonMgr", AddonManager.CommandAddonManager())
import FreeCAD
FreeCAD.__unit_test__ += ["TestAddonManagerGui"]
FreeCAD.__unit_test__ += ["TestAddonManagerGui"]

View File

@@ -2,7 +2,7 @@
# ***************************************************************************
# * *
# * Copyright (c) 2022 Chris Hennes <chennes@pioneerlibrarysystem.org> *
# * Copyright (c) 2022 FreeCAD Project Association *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
@@ -163,7 +163,7 @@ if HAVE_QTNETWORK:
self.diskCache.setCacheDirectory(qnam_cache)
self.QNAM.setCache(self.diskCache)
self.monitored_connections:List[int] = []
self.monitored_connections: List[int] = []
# Set up the proxy, if necesssary:
noProxyCheck = True
@@ -269,7 +269,9 @@ if HAVE_QTNETWORK:
except queue.Empty:
pass
def __launch_request(self, index:int, request:QtNetwork.QNetworkRequest) -> None:
def __launch_request(
self, index: int, request: QtNetwork.QNetworkRequest
) -> None:
reply = self.QNAM.get(request)
self.replies[index] = reply
@@ -281,7 +283,6 @@ if HAVE_QTNETWORK:
reply.readyRead.connect(self.__ready_to_read)
reply.downloadProgress.connect(self.__download_progress)
def submit_unmonitored_get(self, url: str) -> int:
"""Adds this request to the queue, and returns an index that can be used by calling code
in conjunction with the completed() signal to handle the results of the call. All data is
@@ -437,7 +438,7 @@ if HAVE_QTNETWORK:
def __follow_redirect(self, url):
sender = self.sender()
if sender:
for index,reply in self.replies.items():
for index, reply in self.replies.items():
if reply == sender:
current_index = index
break
@@ -459,11 +460,11 @@ if HAVE_QTNETWORK:
for error in errors:
print(error)
def __download_progress(self, bytesReceived:int, bytesTotal:int) -> None:
def __download_progress(self, bytesReceived: int, bytesTotal: int) -> None:
sender = self.sender()
if not sender:
return
for index,reply in self.replies.items():
for index, reply in self.replies.items():
if reply == sender:
self.progress_made.emit(index, bytesReceived, bytesTotal)
return
@@ -473,12 +474,12 @@ if HAVE_QTNETWORK:
if not sender:
return
for index,reply in self.replies.items():
for index, reply in self.replies.items():
if reply == sender:
self.__data_incoming(index, reply)
return
def __data_incoming(self, index: int, reply:QtNetwork.QNetworkReply) -> None:
def __data_incoming(self, index: int, reply: QtNetwork.QNetworkReply) -> None:
if not index in self.replies:
# We already finished this reply, this is a vestigial signal
return
@@ -492,28 +493,34 @@ if HAVE_QTNETWORK:
f.write(buffer.data())
except Exception as e:
if HAVE_FREECAD:
FreeCAD.Console.PrintError(f"Network Manager internal error: {str(e)}")
FreeCAD.Console.PrintError(
f"Network Manager internal error: {str(e)}"
)
else:
print (f"Network Manager internal error: {str(e)}")
print(f"Network Manager internal error: {str(e)}")
def __reply_finished(self) -> None:
reply = self.sender()
if not reply:
print ("Network Manager Error: __reply_finished not called by a Qt signal")
print(
"Network Manager Error: __reply_finished not called by a Qt signal"
)
return
if reply.error() == QtNetwork.QNetworkReply.NetworkError.OperationCanceledError:
if (
reply.error()
== QtNetwork.QNetworkReply.NetworkError.OperationCanceledError
):
# Silently do nothing
return
index = None
for key,value in self.replies.items():
for key, value in self.replies.items():
if reply == value:
index = key
break
if index is None:
print (f"Lost net request for {reply.url()}")
print(f"Lost net request for {reply.url()}")
return
response_code = reply.attribute(
@@ -522,7 +529,7 @@ if HAVE_QTNETWORK:
self.queue.task_done()
if reply.error() == QtNetwork.QNetworkReply.NetworkError.NoError:
if index in self.monitored_connections:
# Make sure to read any remaining data
# Make sure to read any remaining data
self.__data_incoming(index, reply)
self.monitored_connections.remove(index)
f = self.file_buffers[index]

View File

@@ -23,7 +23,9 @@
# ***************************************************************************
# Unit test for the Addon Manager module
from AddonManagerTest.app.test_utilities import TestUtilities as AddonManagerTestUtilities
from AddonManagerTest.app.test_utilities import (
TestUtilities as AddonManagerTestUtilities,
)
# dummy usage to get flake8 and lgtm quiet
False if AddonManagerTestUtilities.__name__ else True

View File

@@ -276,13 +276,13 @@ def attention_color_string() -> str:
return "rgb(255,179,64)" if is_darkmode() else "rgb(255,149,0)"
def get_assigned_string_literal(line:str) -> Optional[str]:
def get_assigned_string_literal(line: str) -> Optional[str]:
"""Look for a line of the form my_var = "A string literal" and return the string literal.
If the assignment is of a floating point value, that value is converted to a string
and returned. If neither is true, returns None."""
string_search_regex = re.compile(r"\s*(['\"])(.*)\1")
_,_,after_equals = line.partition("=")
_, _, after_equals = line.partition("=")
match = re.match(string_search_regex, after_equals)
if match:
return str(match.group(2))
@@ -290,9 +290,10 @@ def get_assigned_string_literal(line:str) -> Optional[str]:
return str(after_equals).strip()
return None
def get_macro_version_from_file(filename: str) -> str:
""" Get the version of the macro from a local macro file. Supports strings, ints, and floats, as
well as a reference to __date__ """
"""Get the version of the macro from a local macro file. Supports strings, ints, and floats, as
well as a reference to __date__"""
date = ""
with open(filename, "r", errors="ignore") as f:

View File

@@ -2,7 +2,7 @@
# ***************************************************************************
# * *
# * Copyright (c) 2019 Yorik van Havre <yorik@uncreated.net> *
# * Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
# * Copyright (c) 2022 FreeCAD Project Association *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
@@ -51,7 +51,7 @@ if FreeCAD.GuiUp:
import addonmanager_utilities as utils
from addonmanager_macro import Macro
from AddonManagerRepo import AddonManagerRepo
from Addon import Addon
import NetworkManager
translate = FreeCAD.Qt.translate
@@ -239,10 +239,10 @@ class UpdateWorker(QtCore.QThread):
package_names.append(name)
addondir = moddir + os.sep + name
if os.path.exists(addondir) and os.listdir(addondir):
state = AddonManagerRepo.UpdateStatus.UNCHECKED
state = Addon.Status.UNCHECKED
else:
state = AddonManagerRepo.UpdateStatus.NOT_INSTALLED
repo = AddonManagerRepo(name, addon["url"], state, addon["branch"])
state = Addon.Status.NOT_INSTALLED
repo = Addon(name, addon["url"], state, addon["branch"])
md_file = os.path.join(addondir, "package.xml")
if os.path.isfile(md_file):
repo.load_metadata_file(md_file)
@@ -281,10 +281,10 @@ class UpdateWorker(QtCore.QThread):
addondir = moddir + os.sep + name
if os.path.exists(addondir) and os.listdir(addondir):
# make sure the folder exists and it contains files!
state = AddonManagerRepo.UpdateStatus.UNCHECKED
state = Addon.Status.UNCHECKED
else:
state = AddonManagerRepo.UpdateStatus.NOT_INSTALLED
repo = AddonManagerRepo(name, url, state, branch)
state = Addon.Status.NOT_INSTALLED
repo = Addon(name, url, state, branch)
md_file = os.path.join(addondir, "package.xml")
if os.path.isfile(md_file):
repo.load_metadata_file(md_file)
@@ -323,7 +323,7 @@ class LoadPackagesFromCacheWorker(QtCore.QThread):
for item in dict_data.values():
if QtCore.QThread.currentThread().isInterruptionRequested():
return
repo = AddonManagerRepo.from_cache(item)
repo = Addon.from_cache(item)
repo_metadata_cache_path = os.path.join(
metadata_cache_path, repo.name, "package.xml"
)
@@ -357,7 +357,7 @@ class LoadMacrosFromCacheWorker(QtCore.QThread):
if QtCore.QThread.currentThread().isInterruptionRequested():
return
new_macro = Macro.from_cache(item)
repo = AddonManagerRepo.from_macro(new_macro)
repo = Addon.from_macro(new_macro)
utils.update_macro_installation_details(repo)
self.add_macro_signal.emit(repo)
@@ -368,18 +368,18 @@ class CheckSingleUpdateWorker(QtCore.QObject):
update_status = QtCore.Signal(int)
def __init__(self, repo: AddonManagerRepo, parent: QtCore.QObject = None):
def __init__(self, repo: Addon, parent: QtCore.QObject = None):
super().__init__(parent)
self.repo = repo
def do_work(self):
# Borrow the function from another class:
checker = UpdateChecker()
if self.repo.repo_type == AddonManagerRepo.RepoType.WORKBENCH:
if self.repo.repo_type == Addon.Kind.WORKBENCH:
checker.check_workbench(self.repo)
elif self.repo.repo_type == AddonManagerRepo.RepoType.MACRO:
elif self.repo.repo_type == Addon.Kind.MACRO:
checker.check_macro(self.repo)
elif self.repo.repo_type == AddonManagerRepo.RepoType.PACKAGE:
elif self.repo.repo_type == Addon.Kind.PACKAGE:
checker.check_package(self.repo)
self.update_status.emit(self.repo.update_status)
@@ -388,10 +388,10 @@ class CheckSingleUpdateWorker(QtCore.QObject):
class CheckWorkbenchesForUpdatesWorker(QtCore.QThread):
"""This worker checks for available updates for all workbenches"""
update_status = QtCore.Signal(AddonManagerRepo)
update_status = QtCore.Signal(Addon)
progress_made = QtCore.Signal(int, int)
def __init__(self, repos: List[AddonManagerRepo]):
def __init__(self, repos: List[Addon]):
QtCore.QThread.__init__(self)
self.repos = repos
@@ -408,14 +408,14 @@ class CheckWorkbenchesForUpdatesWorker(QtCore.QThread):
return
self.progress_made.emit(count, len(self.repos))
count += 1
if repo.status() == AddonManagerRepo.UpdateStatus.UNCHECKED:
if repo.repo_type == AddonManagerRepo.RepoType.WORKBENCH:
if repo.status() == Addon.Status.UNCHECKED:
if repo.repo_type == Addon.Kind.WORKBENCH:
checker.check_workbench(repo)
self.update_status.emit(repo)
elif repo.repo_type == AddonManagerRepo.RepoType.MACRO:
elif repo.repo_type == Addon.Kind.MACRO:
checker.check_macro(repo)
self.update_status.emit(repo)
elif repo.repo_type == AddonManagerRepo.RepoType.PACKAGE:
elif repo.repo_type == Addon.Kind.PACKAGE:
checker.check_package(repo)
self.update_status.emit(repo)
@@ -427,7 +427,7 @@ class UpdateChecker:
def check_workbench(self, wb):
if not have_git or NOGIT:
wb.set_status(AddonManagerRepo.UpdateStatus.CANNOT_CHECK)
wb.set_status(Addon.Status.CANNOT_CHECK)
return
clonedir = self.moddir + os.sep + wb.name
if os.path.exists(clonedir):
@@ -441,7 +441,7 @@ class UpdateChecker:
if gitrepo.head.is_detached:
# By definition, in a detached-head state we cannot
# update, so don't even bother checking.
wb.set_status(AddonManagerRepo.UpdateStatus.NO_UPDATE_AVAILABLE)
wb.set_status(Addon.Status.NO_UPDATE_AVAILABLE)
if hasattr(gitrepo.head, "ref"):
wb.branch = gitrepo.head.ref.name
else:
@@ -458,17 +458,13 @@ class UpdateChecker:
+ "\n"
)
FreeCAD.Console.PrintWarning(str(e) + "\n")
wb.set_status(AddonManagerRepo.UpdateStatus.CANNOT_CHECK)
wb.set_status(Addon.Status.CANNOT_CHECK)
else:
try:
if "git pull" in gitrepo.git.status():
wb.set_status(
AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE
)
wb.set_status(Addon.Status.UPDATE_AVAILABLE)
else:
wb.set_status(
AddonManagerRepo.UpdateStatus.NO_UPDATE_AVAILABLE
)
wb.set_status(Addon.Status.NO_UPDATE_AVAILABLE)
except Exception:
FreeCAD.Console.PrintWarning(
translate(
@@ -476,16 +472,16 @@ class UpdateChecker:
).format(wb.name)
+ "\n"
)
wb.set_status(AddonManagerRepo.UpdateStatus.CANNOT_CHECK)
wb.set_status(Addon.Status.CANNOT_CHECK)
def check_package(self, package: AddonManagerRepo) -> None:
def check_package(self, package: Addon) -> None:
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 have_git and not NOGIT:
self.check_workbench(package)
if package.status() != AddonManagerRepo.UpdateStatus.CANNOT_CHECK:
if package.status() != Addon.Status.CANNOT_CHECK:
# It worked, just exit now
return
@@ -495,7 +491,7 @@ class UpdateChecker:
# 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(AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE)
package.set_status(Addon.Status.UPDATE_AVAILABLE)
package.installed_version = None
return
else:
@@ -506,11 +502,9 @@ class UpdateChecker:
# 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(AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE)
package.set_status(Addon.Status.UPDATE_AVAILABLE)
else:
package.set_status(
AddonManagerRepo.UpdateStatus.NO_UPDATE_AVAILABLE
)
package.set_status(Addon.Status.NO_UPDATE_AVAILABLE)
except Exception:
FreeCAD.Console.PrintWarning(
translate(
@@ -519,9 +513,9 @@ class UpdateChecker:
).format(name=installed_metadata_file)
+ "\n"
)
package.set_status(AddonManagerRepo.UpdateStatus.CANNOT_CHECK)
package.set_status(Addon.Status.CANNOT_CHECK)
def check_macro(self, macro_wrapper: AddonManagerRepo) -> None:
def check_macro(self, macro_wrapper: Addon) -> None:
# Make sure this macro has its code downloaded:
try:
if not macro_wrapper.macro.parsed and macro_wrapper.macro.on_git:
@@ -542,7 +536,7 @@ class UpdateChecker:
).format(name=macro_wrapper.macro.name)
+ "\n"
)
macro_wrapper.set_status(AddonManagerRepo.UpdateStatus.CANNOT_CHECK)
macro_wrapper.set_status(Addon.Status.CANNOT_CHECK)
return
hasher1 = hashlib.sha1()
@@ -568,9 +562,9 @@ class UpdateChecker:
else:
return
if new_sha1 == old_sha1:
macro_wrapper.set_status(AddonManagerRepo.UpdateStatus.NO_UPDATE_AVAILABLE)
macro_wrapper.set_status(Addon.Status.NO_UPDATE_AVAILABLE)
else:
macro_wrapper.set_status(AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE)
macro_wrapper.set_status(Addon.Status.UPDATE_AVAILABLE)
class FillMacroListWorker(QtCore.QThread):
@@ -669,7 +663,7 @@ class FillMacroListWorker(QtCore.QThread):
macro = Macro(filename[:-8]) # Remove ".FCMacro".
macro.on_git = True
macro.src_filename = os.path.join(dirpath, filename)
repo = AddonManagerRepo.from_macro(macro)
repo = Addon.from_macro(macro)
repo.url = "https://github.com/FreeCAD/FreeCAD-macros.git"
utils.update_macro_installation_details(repo)
self.add_macro_signal.emit(repo)
@@ -713,7 +707,7 @@ class FillMacroListWorker(QtCore.QThread):
macro_names.append(macname)
macro = Macro(macname)
macro.on_wiki = True
repo = AddonManagerRepo.from_macro(macro)
repo = Addon.from_macro(macro)
repo.url = "https://wiki.freecad.org/Macros_recipes"
utils.update_macro_installation_details(repo)
self.add_macro_signal.emit(repo)
@@ -723,10 +717,10 @@ class CacheMacroCode(QtCore.QThread):
"""Download and cache the macro code, and parse its internal metadata"""
status_message = QtCore.Signal(str)
update_macro = QtCore.Signal(AddonManagerRepo)
update_macro = QtCore.Signal(Addon)
progress_made = QtCore.Signal(int, int)
def __init__(self, repos: List[AddonManagerRepo]) -> None:
def __init__(self, repos: List[Addon]) -> None:
QtCore.QThread.__init__(self)
self.repos = repos
self.workers = []
@@ -797,7 +791,7 @@ class CacheMacroCode(QtCore.QThread):
).format(num_macros=num_macros, num_failed=num_failed)
)
def update_and_advance(self, repo: AddonManagerRepo) -> None:
def update_and_advance(self, repo: Addon) -> None:
if repo is not None:
if repo.macro.name not in self.failed:
self.update_macro.emit(repo)
@@ -906,10 +900,10 @@ class InstallWorkbenchWorker(QtCore.QThread):
status_message = QtCore.Signal(str)
progress_made = QtCore.Signal(int, int)
success = QtCore.Signal(AddonManagerRepo, str)
failure = QtCore.Signal(AddonManagerRepo, str)
success = QtCore.Signal(Addon, str)
failure = QtCore.Signal(Addon, str)
def __init__(self, repo: AddonManagerRepo):
def __init__(self, repo: Addon):
QtCore.QThread.__init__(self)
self.repo = repo
@@ -1094,7 +1088,7 @@ class InstallWorkbenchWorker(QtCore.QThread):
"Addon successfully installed.",
)
if self.repo.repo_type == AddonManagerRepo.RepoType.WORKBENCH:
if self.repo.repo_type == Addon.Kind.WORKBENCH:
# symlink any macro contained in the module to the macros folder
macro_dir = FreeCAD.getUserMacroDir(True)
if not os.path.exists(macro_dir):
@@ -1375,7 +1369,7 @@ class UpdateMetadataCacheWorker(QtCore.QThread):
status_message = QtCore.Signal(str)
progress_made = QtCore.Signal(int, int)
package_updated = QtCore.Signal(AddonManagerRepo)
package_updated = QtCore.Signal(Addon)
class RequestType(Enum):
PACKAGE_XML = auto()
@@ -1387,9 +1381,7 @@ class UpdateMetadataCacheWorker(QtCore.QThread):
QtCore.QThread.__init__(self)
self.repos = repos
self.requests: Dict[
int, (AddonManagerRepo, UpdateMetadataCacheWorker.RequestType)
] = {}
self.requests: Dict[int, (Addon, UpdateMetadataCacheWorker.RequestType)] = {}
NetworkManager.AM_NETWORK_MANAGER.completed.connect(self.download_completed)
self.requests_completed = 0
self.total_requests = 0
@@ -1470,8 +1462,8 @@ class UpdateMetadataCacheWorker(QtCore.QThread):
elif request[1] == UpdateMetadataCacheWorker.RequestType.ICON:
self.process_icon(request[0], data)
def process_package_xml(self, repo: AddonManagerRepo, data: QtCore.QByteArray):
repo.repo_type = AddonManagerRepo.RepoType.PACKAGE # By definition
def process_package_xml(self, repo: Addon, data: QtCore.QByteArray):
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)
@@ -1509,7 +1501,7 @@ class UpdateMetadataCacheWorker(QtCore.QThread):
self.requests[index] = (repo, UpdateMetadataCacheWorker.RequestType.ICON)
self.total_requests += 1
def process_metadata_txt(self, repo: AddonManagerRepo, data: QtCore.QByteArray):
def process_metadata_txt(self, repo: Addon, data: QtCore.QByteArray):
self.status_message.emit(
translate("AddonsInstaller", "Downloaded metadata.txt for {}").format(
repo.display_name
@@ -1557,7 +1549,7 @@ class UpdateMetadataCacheWorker(QtCore.QThread):
with open(new_xml_file, "wb") as f:
f.write(data.data())
def process_requirements_txt(self, repo: AddonManagerRepo, data: QtCore.QByteArray):
def process_requirements_txt(self, repo: Addon, data: QtCore.QByteArray):
self.status_message.emit(
translate(
"AddonsInstaller",
@@ -1583,7 +1575,7 @@ class UpdateMetadataCacheWorker(QtCore.QThread):
with open(new_xml_file, "wb") as f:
f.write(data.data())
def process_icon(self, repo: AddonManagerRepo, data: QtCore.QByteArray):
def process_icon(self, repo: Addon, data: QtCore.QByteArray):
self.status_message.emit(
translate("AddonsInstaller", "Downloaded icon for {}").format(
repo.display_name
@@ -1625,8 +1617,8 @@ class UpdateAllWorker(QtCore.QThread):
progress_made = QtCore.Signal(int, int)
status_message = QtCore.Signal(str)
success = QtCore.Signal(AddonManagerRepo)
failure = QtCore.Signal(AddonManagerRepo)
success = QtCore.Signal(Addon)
failure = QtCore.Signal(Addon)
def __init__(self, repos):
super().__init__()
@@ -1664,13 +1656,13 @@ class UpdateAllWorker(QtCore.QThread):
for worker in workers:
worker.wait()
def on_success(self, repo: AddonManagerRepo) -> None:
def on_success(self, repo: Addon) -> None:
self.progress_made.emit(
len(self.repos) - self.repo_queue.qsize(), len(self.repos)
)
self.success.emit(repo)
def on_failure(self, repo: AddonManagerRepo) -> None:
def on_failure(self, repo: Addon) -> None:
self.progress_made.emit(
len(self.repos) - self.repo_queue.qsize(), len(self.repos)
)
@@ -1678,8 +1670,8 @@ class UpdateAllWorker(QtCore.QThread):
class UpdateSingleWorker(QtCore.QThread):
success = QtCore.Signal(AddonManagerRepo)
failure = QtCore.Signal(AddonManagerRepo)
success = QtCore.Signal(Addon)
failure = QtCore.Signal(Addon)
def __init__(self, repo_queue: queue.Queue):
super().__init__()
@@ -1694,13 +1686,13 @@ class UpdateSingleWorker(QtCore.QThread):
repo = self.repo_queue.get_nowait()
except queue.Empty:
return
if repo.repo_type == AddonManagerRepo.RepoType.MACRO:
if repo.repo_type == Addon.Kind.MACRO:
self.update_macro(repo)
else:
self.update_package(repo)
self.repo_queue.task_done()
def update_macro(self, repo: AddonManagerRepo):
def update_macro(self, repo: Addon):
"""Updating a macro happens in this function, in the current thread"""
cache_path = os.path.join(
@@ -1718,7 +1710,7 @@ class UpdateSingleWorker(QtCore.QThread):
else:
self.failure.emit(repo)
def update_package(self, repo: AddonManagerRepo):
def update_package(self, repo: Addon):
"""Updating a package re-uses the package installation worker, so actually spawns another thread that we block on"""
worker = InstallWorkbenchWorker(repo)

View File

@@ -2,7 +2,7 @@
# ***************************************************************************
# * *
# * Copyright (c) 2022 Chris Hennes <chennes@pioneerlibrarysystem.org> *
# * Copyright (c) 2022 FreeCAD Project Association *
# * *
# * This library is free software; you can redistribute it and/or *
# * modify it under the terms of the GNU Lesser General Public *

View File

@@ -22,14 +22,14 @@
import FreeCAD
import FreeCADGui
from PySide2 import QtCore, QtWidgets
import AddonManagerRepo
import Addon
import os
translate = FreeCAD.Qt.translate
def ask_to_install_toolbar_button(repo: AddonManagerRepo) -> None:
def ask_to_install_toolbar_button(repo: Addon) -> None:
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
do_not_show_dialog = pref.GetBool("dontShowAddMacroButtonDialog", False)
if not do_not_show_dialog:
@@ -46,7 +46,7 @@ def ask_to_install_toolbar_button(repo: AddonManagerRepo) -> None:
def ask_for_toolbar(
repo: AddonManagerRepo, custom_toolbars
repo: Addon, custom_toolbars
) -> object: # Returns the pref group for the new toolbar
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
@@ -158,7 +158,7 @@ def check_for_toolbar(custom_toolbars, toolbar_name: str) -> bool:
return False
def install_toolbar_button(repo: AddonManagerRepo) -> None:
def install_toolbar_button(repo: Addon) -> None:
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
custom_toolbar_name = pref.GetString(
"CustomToolbarName", "Auto-Created Macro Toolbar"
@@ -206,7 +206,7 @@ def install_toolbar_button(repo: AddonManagerRepo) -> None:
)
def install_macro_to_toolbar(repo: AddonManagerRepo, toolbar: object) -> None:
def install_macro_to_toolbar(repo: Addon, toolbar: object) -> None:
menuText = repo.display_name
tooltipText = f"<b>{repo.display_name}</b>"
if repo.macro.comment:
@@ -254,7 +254,7 @@ def install_macro_to_toolbar(repo: AddonManagerRepo, toolbar: object) -> None:
wb.reloadActive()
def remove_custom_toolbar_button(repo: AddonManagerRepo) -> None:
def remove_custom_toolbar_button(repo: Addon) -> None:
"""If this repo contains a macro, look through the custom commands and
see if one is set up for this macro. If so, remove it, including any
toolbar entries."""

View File

@@ -2,7 +2,7 @@
# ***************************************************************************
# * *
# * Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
# * Copyright (c) 2022 FreeCAD Project Association *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
@@ -34,7 +34,7 @@ import FreeCADGui
import addonmanager_utilities as utils
from addonmanager_workers import GetMacroDetailsWorker, CheckSingleUpdateWorker
from AddonManagerRepo import AddonManagerRepo
from Addon import Addon
import NetworkManager
from change_branch import ChangeBranchDialog
@@ -71,12 +71,12 @@ except Exception:
class PackageDetails(QWidget):
back = Signal()
install = Signal(AddonManagerRepo)
uninstall = Signal(AddonManagerRepo)
update = Signal(AddonManagerRepo)
execute = Signal(AddonManagerRepo)
update_status = Signal(AddonManagerRepo)
check_for_update = Signal(AddonManagerRepo)
install = Signal(Addon)
uninstall = Signal(Addon)
update = Signal(Addon)
execute = Signal(Addon)
update_status = Signal(Addon)
check_for_update = Signal(Addon)
def __init__(self, parent=None):
super().__init__(parent)
@@ -110,7 +110,7 @@ class PackageDetails(QWidget):
self.ui.loadingLabel.show()
self.ui.webView.hide()
def show_repo(self, repo: AddonManagerRepo, reload: bool = False) -> None:
def show_repo(self, repo: Addon, reload: bool = False) -> None:
# If this is the same repo we were already showing, we do not have to do the
# expensive refetch unless reload is true
@@ -136,17 +136,17 @@ class PackageDetails(QWidget):
self.worker.requestInterruption()
self.worker.wait()
if repo.repo_type == AddonManagerRepo.RepoType.MACRO:
if repo.repo_type == Addon.Kind.MACRO:
self.show_macro(repo)
self.ui.buttonExecute.show()
elif repo.repo_type == AddonManagerRepo.RepoType.WORKBENCH:
elif repo.repo_type == Addon.Kind.WORKBENCH:
self.show_workbench(repo)
self.ui.buttonExecute.hide()
elif repo.repo_type == AddonManagerRepo.RepoType.PACKAGE:
elif repo.repo_type == Addon.Kind.PACKAGE:
self.show_package(repo)
self.ui.buttonExecute.hide()
if repo.status() == AddonManagerRepo.UpdateStatus.UNCHECKED:
if repo.status() == Addon.Status.UNCHECKED:
if not self.status_update_thread:
self.status_update_thread = QThread()
self.status_update_worker = CheckSingleUpdateWorker(repo)
@@ -165,7 +165,7 @@ class PackageDetails(QWidget):
repo = self.repo
self.set_change_branch_button_state()
self.set_disable_button_state()
if status != AddonManagerRepo.UpdateStatus.NOT_INSTALLED:
if status != Addon.Status.NOT_INSTALLED:
version = repo.installed_version
date = ""
@@ -196,7 +196,7 @@ class PackageDetails(QWidget):
translate("AddonsInstaller", "Installed") + ". "
)
if status == AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE:
if status == Addon.Status.UPDATE_AVAILABLE:
if repo.metadata:
installed_version_string += (
"<b>"
@@ -225,10 +225,10 @@ class PackageDetails(QWidget):
)
+ ".</b>"
)
elif status == AddonManagerRepo.UpdateStatus.NO_UPDATE_AVAILABLE:
elif status == Addon.Status.NO_UPDATE_AVAILABLE:
detached_head = False
branch = repo.branch
if have_git and repo.repo_type != AddonManagerRepo.RepoType.MACRO:
if have_git and repo.repo_type != Addon.Kind.MACRO:
basedir = FreeCAD.getUserAppDataDir()
moddir = os.path.join(basedir, "Mod", repo.name)
if os.path.exists(os.path.join(moddir, ".git")):
@@ -252,14 +252,14 @@ class PackageDetails(QWidget):
).format(branch)
+ "."
)
elif status == AddonManagerRepo.UpdateStatus.PENDING_RESTART:
elif status == Addon.Status.PENDING_RESTART:
installed_version_string += (
translate(
"AddonsInstaller", "Updated, please restart FreeCAD to use"
)
+ "."
)
elif status == AddonManagerRepo.UpdateStatus.UNCHECKED:
elif status == Addon.Status.UNCHECKED:
pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons")
autocheck = pref.GetBool("AutoCheck", False)
@@ -275,7 +275,7 @@ class PackageDetails(QWidget):
installed_version_string += "</h3>"
self.ui.labelPackageDetails.setText(installed_version_string)
if repo.status() == AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE:
if repo.status() == Addon.Status.UPDATE_AVAILABLE:
self.ui.labelPackageDetails.setStyleSheet(
"color:" + utils.attention_color_string()
)
@@ -302,32 +302,32 @@ class PackageDetails(QWidget):
self.ui.labelPackageDetails.hide()
self.ui.labelInstallationLocation.hide()
if status == AddonManagerRepo.UpdateStatus.NOT_INSTALLED:
if status == Addon.Status.NOT_INSTALLED:
self.ui.buttonInstall.show()
self.ui.buttonUninstall.hide()
self.ui.buttonUpdate.hide()
self.ui.buttonCheckForUpdate.hide()
elif status == AddonManagerRepo.UpdateStatus.NO_UPDATE_AVAILABLE:
elif status == Addon.Status.NO_UPDATE_AVAILABLE:
self.ui.buttonInstall.hide()
self.ui.buttonUninstall.show()
self.ui.buttonUpdate.hide()
self.ui.buttonCheckForUpdate.hide()
elif status == AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE:
elif status == Addon.Status.UPDATE_AVAILABLE:
self.ui.buttonInstall.hide()
self.ui.buttonUninstall.show()
self.ui.buttonUpdate.show()
self.ui.buttonCheckForUpdate.hide()
elif status == AddonManagerRepo.UpdateStatus.UNCHECKED:
elif status == Addon.Status.UNCHECKED:
self.ui.buttonInstall.hide()
self.ui.buttonUninstall.show()
self.ui.buttonUpdate.hide()
self.ui.buttonCheckForUpdate.show()
elif status == AddonManagerRepo.UpdateStatus.PENDING_RESTART:
elif status == Addon.Status.PENDING_RESTART:
self.ui.buttonInstall.hide()
self.ui.buttonUninstall.show()
self.ui.buttonUpdate.hide()
self.ui.buttonCheckForUpdate.hide()
elif status == AddonManagerRepo.UpdateStatus.CANNOT_CHECK:
elif status == Addon.Status.CANNOT_CHECK:
self.ui.buttonInstall.hide()
self.ui.buttonUninstall.show()
self.ui.buttonUpdate.show()
@@ -369,7 +369,10 @@ class PackageDetails(QWidget):
self.ui.labelWarningInfo.show()
self.ui.labelWarningInfo.setText(
"<h2>"
+ translate("AddonsInstaller", "WARNING: This addon is currently installed, but disabled. Use the 'enable' button to re-enable.")
+ translate(
"AddonsInstaller",
"WARNING: This addon is currently installed, but disabled. Use the 'enable' button to re-enable.",
)
+ "</h2>"
)
self.ui.labelWarningInfo.setStyleSheet(
@@ -381,10 +384,7 @@ class PackageDetails(QWidget):
def requires_newer_freecad(self) -> Optional[str]:
# If it's not installed, check to see if it's for a newer version of FreeCAD
if (
self.repo.status() == AddonManagerRepo.UpdateStatus.NOT_INSTALLED
and self.repo.metadata
):
if self.repo.status() == Addon.Status.NOT_INSTALLED and self.repo.metadata:
# Only hide if ALL content items require a newer version, otherwise
# it's possible that this package actually provides versions of itself
# for newer and older versions
@@ -416,11 +416,11 @@ class PackageDetails(QWidget):
return
# Is this repo installed? If not, return.
if self.repo.status() == AddonManagerRepo.UpdateStatus.NOT_INSTALLED:
if self.repo.status() == Addon.Status.NOT_INSTALLED:
return
# Is it a Macro? If so, return:
if self.repo.repo_type == AddonManagerRepo.RepoType.MACRO:
if self.repo.repo_type == Addon.Kind.MACRO:
return
# Can we actually switch branches? If not, return.
@@ -441,14 +441,14 @@ class PackageDetails(QWidget):
self.ui.buttonEnable.hide()
self.ui.buttonDisable.hide()
status = self.repo.status()
if status != AddonManagerRepo.UpdateStatus.NOT_INSTALLED:
if status != Addon.Status.NOT_INSTALLED:
disabled = self.repo.is_disabled()
if disabled:
self.ui.buttonEnable.show()
else:
self.ui.buttonDisable.show()
def show_workbench(self, repo: AddonManagerRepo) -> None:
def show_workbench(self, repo: Addon) -> None:
"""loads information of a given workbench"""
url = utils.get_readme_html_url(repo)
if HAS_QTWEBENGINE:
@@ -459,7 +459,7 @@ class PackageDetails(QWidget):
text = readme_data.data().decode("utf8")
self.ui.textBrowserReadMe.setHtml(text)
def show_package(self, repo: AddonManagerRepo) -> None:
def show_package(self, repo: Addon) -> None:
"""Show the details for a package (a repo with a package.xml metadata file)"""
readme_url = None
@@ -479,7 +479,7 @@ class PackageDetails(QWidget):
text = readme_data.data().decode("utf8")
self.ui.textBrowserReadMe.setHtml(text)
def show_macro(self, repo: AddonManagerRepo) -> None:
def show_macro(self, repo: Addon) -> None:
"""loads information of a given macro"""
if not repo.macro.url:
@@ -614,12 +614,13 @@ class PackageDetails(QWidget):
self.ui.labelWarningInfo.show()
self.ui.labelWarningInfo.setText(
"<h3>"
+ translate("AddonsInstaller", "This Addon will be enabled next time you restart FreeCAD.")
+ translate(
"AddonsInstaller",
"This Addon will be enabled next time you restart FreeCAD.",
)
+ "</h3>"
)
self.ui.labelWarningInfo.setStyleSheet(
"color:" + utils.bright_color_string()
)
self.ui.labelWarningInfo.setStyleSheet("color:" + utils.bright_color_string())
def disable_clicked(self) -> None:
self.repo.disable()
@@ -628,7 +629,10 @@ class PackageDetails(QWidget):
self.ui.labelWarningInfo.show()
self.ui.labelWarningInfo.setText(
"<h3>"
+ translate("AddonsInstaller", "This Addon will be disabled next time you restart FreeCAD.")
+ translate(
"AddonsInstaller",
"This Addon will be disabled next time you restart FreeCAD.",
)
+ "</h3>"
)
self.ui.labelWarningInfo.setStyleSheet(
@@ -651,12 +655,12 @@ class PackageDetails(QWidget):
self.repo.load_metadata_file(path_to_metadata)
self.repo.installed_version = self.repo.metadata.Version
else:
self.repo.repo_type = AddonManagerRepo.RepoType.WORKBENCH
self.repo.repo_type = Addon.Kind.WORKBENCH
self.repo.metadata = None
self.repo.installed_version = None
self.repo.updated_timestamp = QDateTime.currentDateTime().toSecsSinceEpoch()
self.repo.branch = name
self.repo.set_status(AddonManagerRepo.UpdateStatus.PENDING_RESTART)
self.repo.set_status(Addon.Status.PENDING_RESTART)
installed_version_string = "<h3>"
installed_version_string += translate(

View File

@@ -2,7 +2,7 @@
# ***************************************************************************
# * *
# * Copyright (c) 2021 Chris Hennes <chennes@pioneerlibrarysystem.org> *
# * Copyright (c) 2022 FreeCAD Project Association *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
@@ -31,7 +31,7 @@ from PySide2.QtWidgets import *
from enum import IntEnum
import threading
from AddonManagerRepo import AddonManagerRepo
from Addon import Addon
from compact_view import Ui_CompactView
from expanded_view import Ui_ExpandedView
@@ -56,7 +56,7 @@ class StatusFilter(IntEnum):
class PackageList(QWidget):
"""A widget that shows a list of packages and various widgets to control the display of the list"""
itemSelected = Signal(AddonManagerRepo)
itemSelected = Signal(Addon)
def __init__(self, parent=None):
super().__init__(parent)
@@ -206,15 +206,15 @@ class PackageListItemModel(QAbstractListModel):
row = index.row()
if role == Qt.ToolTipRole:
tooltip = ""
if self.repos[row].repo_type == AddonManagerRepo.RepoType.PACKAGE:
if self.repos[row].repo_type == Addon.Kind.PACKAGE:
tooltip = translate(
"AddonsInstaller", "Click for details about package {}"
).format(self.repos[row].display_name)
elif self.repos[row].repo_type == AddonManagerRepo.RepoType.WORKBENCH:
elif self.repos[row].repo_type == Addon.Kind.WORKBENCH:
tooltip = translate(
"AddonsInstaller", "Click for details about workbench {}"
).format(self.repos[row].display_name)
elif self.repos[row].repo_type == AddonManagerRepo.RepoType.MACRO:
elif self.repos[row].repo_type == Addon.Kind.MACRO:
tooltip = translate(
"AddonsInstaller", "Click for details about macro {}"
).format(self.repos[row].display_name)
@@ -246,7 +246,7 @@ class PackageListItemModel(QAbstractListModel):
)
self.write_lock.release()
def append_item(self, repo: AddonManagerRepo) -> None:
def append_item(self, repo: Addon) -> None:
if repo in self.repos:
# Cowardly refuse to insert the same repo a second time
return
@@ -264,9 +264,7 @@ class PackageListItemModel(QAbstractListModel):
self.endRemoveRows()
self.write_lock.release()
def update_item_status(
self, name: str, status: AddonManagerRepo.UpdateStatus
) -> None:
def update_item_status(self, name: str, status: Addon.Status) -> None:
for row, item in enumerate(self.repos):
if item.name == name:
self.setData(
@@ -282,7 +280,7 @@ class PackageListItemModel(QAbstractListModel):
)
return
def reload_item(self, repo: AddonManagerRepo) -> None:
def reload_item(self, repo: Addon) -> None:
for index, item in enumerate(self.repos):
if item.name == repo.name:
self.write_lock.acquire()
@@ -414,32 +412,38 @@ class PackageListItemDelegate(QStyledItemDelegate):
self.widget.adjustSize()
def get_compact_update_string(self, repo: AddonManagerRepo) -> str:
def get_compact_update_string(self, repo: Addon) -> str:
"""Get a single-line string listing details about the installed version and date"""
result = ""
if repo.status() == AddonManagerRepo.UpdateStatus.UNCHECKED:
if repo.status() == Addon.Status.UNCHECKED:
result = translate("AddonsInstaller", "Installed")
elif repo.status() == AddonManagerRepo.UpdateStatus.NO_UPDATE_AVAILABLE:
elif repo.status() == Addon.Status.NO_UPDATE_AVAILABLE:
result = translate("AddonsInstaller", "Up-to-date")
elif repo.status() == AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE:
elif repo.status() == Addon.Status.UPDATE_AVAILABLE:
result = translate("AddonsInstaller", "Update available")
elif repo.status() == AddonManagerRepo.UpdateStatus.PENDING_RESTART:
elif repo.status() == Addon.Status.PENDING_RESTART:
result = translate("AddonsInstaller", "Pending restart")
if repo.is_disabled():
style = "style='color:" + utils.warning_color_string() + "; font-weight:bold;'"
result += f"<span {style}> [" + translate("AddonsInstaller","DISABLED") + "]</span>"
style = (
"style='color:" + utils.warning_color_string() + "; font-weight:bold;'"
)
result += (
f"<span {style}> ["
+ translate("AddonsInstaller", "DISABLED")
+ "]</span>"
)
return result
def get_expanded_update_string(self, repo: AddonManagerRepo) -> str:
def get_expanded_update_string(self, repo: Addon) -> str:
"""Get a multi-line string listing details about the installed version and date"""
result = ""
installed_version_string = ""
if repo.status() != AddonManagerRepo.UpdateStatus.NOT_INSTALLED:
if repo.status() != Addon.Status.NOT_INSTALLED:
if repo.installed_version:
installed_version_string = (
"<br/>" + translate("AddonsInstaller", "Installed version") + ": "
@@ -468,25 +472,31 @@ class PackageListItemDelegate(QStyledItemDelegate):
)
available_version_string += repo.metadata.Version
if repo.status() == AddonManagerRepo.UpdateStatus.UNCHECKED:
if repo.status() == Addon.Status.UNCHECKED:
result = translate("AddonsInstaller", "Installed")
result += installed_version_string
result += installed_date_string
elif repo.status() == AddonManagerRepo.UpdateStatus.NO_UPDATE_AVAILABLE:
elif repo.status() == Addon.Status.NO_UPDATE_AVAILABLE:
result = translate("AddonsInstaller", "Up-to-date")
result += installed_version_string
result += installed_date_string
elif repo.status() == AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE:
elif repo.status() == Addon.Status.UPDATE_AVAILABLE:
result = translate("AddonsInstaller", "Update available")
result += installed_version_string
result += installed_date_string
result += available_version_string
elif repo.status() == AddonManagerRepo.UpdateStatus.PENDING_RESTART:
elif repo.status() == Addon.Status.PENDING_RESTART:
result = translate("AddonsInstaller", "Pending restart")
if repo.is_disabled():
style = "style='color:" + utils.warning_color_string() + "; font-weight:bold;'"
result += f"<br/><span {style}>[" + translate("AddonsInstaller","DISABLED") + "]</span>"
style = (
"style='color:" + utils.warning_color_string() + "; font-weight:bold;'"
)
result += (
f"<br/><span {style}>["
+ translate("AddonsInstaller", "DISABLED")
+ "]</span>"
)
return result
@@ -554,18 +564,18 @@ class PackageListFilter(QSortFilterProxyModel):
return False
if self.status == StatusFilter.INSTALLED:
if data.status() == AddonManagerRepo.UpdateStatus.NOT_INSTALLED:
if data.status() == Addon.Status.NOT_INSTALLED:
return False
elif self.status == StatusFilter.NOT_INSTALLED:
if data.status() != AddonManagerRepo.UpdateStatus.NOT_INSTALLED:
if data.status() != Addon.Status.NOT_INSTALLED:
return False
elif self.status == StatusFilter.UPDATE_AVAILABLE:
if data.status() != AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE:
if data.status() != Addon.Status.UPDATE_AVAILABLE:
return False
# If it's not installed, check to see if it's Py2 only
if (
data.status() == AddonManagerRepo.UpdateStatus.NOT_INSTALLED
data.status() == Addon.Status.NOT_INSTALLED
and self.hide_py2
and data.python2
):
@@ -573,7 +583,7 @@ class PackageListFilter(QSortFilterProxyModel):
# If it's not installed, check to see if it's marked obsolete
if (
data.status() == AddonManagerRepo.UpdateStatus.NOT_INSTALLED
data.status() == Addon.Status.NOT_INSTALLED
and self.hide_obsolete
and data.obsolete
):
@@ -581,7 +591,7 @@ class PackageListFilter(QSortFilterProxyModel):
# If it's not installed, check to see if it's for a newer version of FreeCAD
if (
data.status() == AddonManagerRepo.UpdateStatus.NOT_INSTALLED
data.status() == Addon.Status.NOT_INSTALLED
and self.hide_newer_freecad_required
and data.metadata
):