Addon Manager: Allow .py filenames

Also improve macro removal.
This commit is contained in:
Chris Hennes
2024-01-26 06:46:09 -06:00
committed by Chris Hennes
parent 0ef7f3d0d8
commit bcb60d7d7e
3 changed files with 48 additions and 5 deletions

View File

@@ -24,7 +24,7 @@
""" Contains the classes to manage Addon installation: intended as a stable API, safe for external
code to call and to rely upon existing. See classes AddonInstaller and MacroInstaller for details.
"""
import json
from datetime import datetime, timezone
from enum import IntEnum, auto
import os
@@ -503,16 +503,34 @@ class MacroInstaller(QtCore.QObject):
self.finished.emit()
return False
# If it succeeded, move all of the files to the macro install location
# If it succeeded, move all the files to the macro install location,
# keeping a list of all the files we installed, so they can be removed later
# if this macro is uninstalled.
manifest = []
for item in os.listdir(temp_dir):
src = os.path.join(temp_dir, item)
dst = os.path.join(self.installation_path, item)
shutil.move(src, dst)
manifest.append(dst)
self._write_installation_manifest(manifest)
self.success.emit(self.addon_to_install)
self.addon_to_install.set_status(Addon.Status.NO_UPDATE_AVAILABLE)
self.finished.emit()
return True
def _write_installation_manifest(self, manifest):
manifest_file = os.path.join(
self.installation_path, self.addon_to_install.macro.filename + ".manifest"
)
try:
with open(manifest_file, "w", encoding="utf-8") as f:
f.write(json.dumps(manifest, indent=" "))
except OSError as e:
FreeCAD.Console.PrintWarning(
translate("AddonsInstaller", "Failed to create installation manifest " "file:\n")
)
FreeCAD.Console.PrintWarning(manifest_file)
@classmethod
def _validate_object(cls, addon: object):
"""Make sure this object provides an attribute called "macro" with a method called

View File

@@ -69,6 +69,7 @@ class Macro:
self.version = ""
self.date = ""
self.src_filename = ""
self.filename_from_url = ""
self.author = ""
self.icon = ""
self.icon_source = None
@@ -104,6 +105,8 @@ class Macro:
"""The filename of this macro"""
if self.on_git:
return os.path.basename(self.src_filename)
elif self.filename_from_url:
return self.filename_from_url
return (self.name + ".FCMacro").replace(" ", "_")
def is_installed(self):
@@ -211,8 +214,14 @@ class Macro:
)
return None
code = u2.decode("utf8")
self._set_filename_from_url(self.raw_code_url)
return code
def _set_filename_from_url(self, url: str):
lhs, slash, rhs = url.rpartition("/")
if rhs.endswith(".py") or rhs.lower().endswith(".fcmacro"):
self.filename_from_url = rhs
@staticmethod
def _read_code_from_wiki(p: str) -> Optional[str]:
code = re.findall(r"<pre>(.*?)</pre>", p.replace("\n", "--endl--"))

View File

@@ -24,7 +24,7 @@
""" Contains the classes to manage Addon removal: intended as a stable API, safe for
external code to call and to rely upon existing. See classes AddonUninstaller and
MacroUninstaller for details."""
import json
import os
from typing import List
@@ -228,7 +228,10 @@ class MacroUninstaller(QObject):
directories = set()
for f in self._get_files_to_remove():
normed = os.path.normpath(f)
full_path = os.path.join(self.installation_location, normed)
if os.path.isabs(normed):
full_path = normed
else:
full_path = os.path.join(self.installation_location, normed)
if "/" in f:
directories.add(os.path.dirname(full_path))
try:
@@ -246,6 +249,10 @@ class MacroUninstaller(QObject):
+ str(e)
)
success = False
except Exception:
# Generic catch-all, just in case (because failure to catch an exception
# here can break things pretty badly)
success = False
self._cleanup_directories(directories)
@@ -256,8 +263,17 @@ class MacroUninstaller(QObject):
self.addon_to_remove.set_status(Addon.Status.NOT_INSTALLED)
self.finished.emit()
def _get_files_to_remove(self) -> List[os.PathLike]:
def _get_files_to_remove(self) -> List[str]:
"""Get the list of files that should be removed"""
manifest_file = os.path.join(
self.installation_location, self.addon_to_remove.macro.filename + ".manifest"
)
if os.path.exists(manifest_file):
with open(manifest_file, "r", encoding="utf-8") as f:
manifest_data = f.read()
manifest = json.loads(manifest_data)
manifest.append(manifest_file) # Remove the manifest itself as well
return manifest
files_to_remove = [self.addon_to_remove.macro.filename]
if self.addon_to_remove.macro.icon:
files_to_remove.append(self.addon_to_remove.macro.icon)