Merge branch 'master' into feature-backup-manager

This commit is contained in:
ChrisTerBeke 2018-05-08 17:27:54 +02:00
commit c90a958eaf
10 changed files with 1295 additions and 103 deletions

View file

@ -15,12 +15,10 @@ from UM.Logger import Logger
from UM.Resources import Resources
from UM.Version import Version
class CuraPackageManager(QObject):
Version = 1
# The prefix that's added to all files for an installed package to avoid naming conflicts with user created
# files.
# The prefix that's added to all files for an installed package to avoid naming conflicts with user created files.
PREFIX_PLACE_HOLDER = "-CP;"
def __init__(self, parent = None):
@ -31,13 +29,23 @@ class CuraPackageManager(QObject):
self._plugin_registry = self._application.getPluginRegistry()
# JSON file that keeps track of all installed packages.
self._package_management_file_path = os.path.join(os.path.abspath(Resources.getDataStoragePath()),
"packages.json")
self._installed_package_dict = {} # a dict of all installed packages
self._to_remove_package_set = set() # a set of packages that need to be removed at the next start
self._to_install_package_dict = {} # a dict of packages that need to be installed at the next start
self._bundled_package_management_file_path = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"..",
"resources",
"packages.json"
)
self._user_package_management_file_path = os.path.join(
os.path.abspath(Resources.getDataStoragePath()),
"packages.json"
)
installedPackagesChanged = pyqtSignal() # Emitted whenever the installed packages collection have been changed.
self._bundled_package_dict = {} # A dict of all bundled packages
self._installed_package_dict = {} # A dict of all installed packages
self._to_remove_package_set = set() # A set of packages that need to be removed at the next start
self._to_install_package_dict = {} # A dict of packages that need to be installed at the next start
installedPackagesChanged = pyqtSignal() # Emitted whenever the installed packages collection have been changed.
def initialize(self):
self._loadManagementData()
@ -46,34 +54,42 @@ class CuraPackageManager(QObject):
# (for initialize) Loads the package management file if exists
def _loadManagementData(self) -> None:
if not os.path.exists(self._package_management_file_path):
Logger.log("i", "Package management file %s doesn't exist, do nothing", self._package_management_file_path)
if not os.path.exists(self._bundled_package_management_file_path):
Logger.log("w", "Bundled package management file could not be found!")
return
if not os.path.exists(self._user_package_management_file_path):
Logger.log("i", "User package management file %s doesn't exist, do nothing", self._user_package_management_file_path)
return
# Need to use the file lock here to prevent concurrent I/O from other processes/threads
container_registry = self._application.getContainerRegistry()
with container_registry.lockFile():
with open(self._package_management_file_path, "r", encoding = "utf-8") as f:
management_dict = json.load(f, encoding = "utf-8")
# Load the bundled packages:
with open(self._bundled_package_management_file_path, "r", encoding = "utf-8") as f:
self._bundled_package_dict = json.load(f, encoding = "utf-8")
Logger.log("i", "Loaded bundled packages data from %s", self._bundled_package_management_file_path)
# Load the user packages:
with open(self._user_package_management_file_path, "r", encoding="utf-8") as f:
management_dict = json.load(f, encoding="utf-8")
self._installed_package_dict = management_dict.get("installed", {})
self._to_remove_package_set = set(management_dict.get("to_remove", []))
self._to_install_package_dict = management_dict.get("to_install", {})
Logger.log("i", "Package management file %s is loaded", self._package_management_file_path)
Logger.log("i", "Loaded user packages management file from %s", self._user_package_management_file_path)
def _saveManagementData(self) -> None:
# Need to use the file lock here to prevent concurrent I/O from other processes/threads
container_registry = self._application.getContainerRegistry()
with container_registry.lockFile():
with open(self._package_management_file_path, "w", encoding = "utf-8") as f:
with open(self._user_package_management_file_path, "w", encoding = "utf-8") as f:
data_dict = {"version": CuraPackageManager.Version,
"installed": self._installed_package_dict,
"to_remove": list(self._to_remove_package_set),
"to_install": self._to_install_package_dict}
data_dict["to_remove"] = list(data_dict["to_remove"])
json.dump(data_dict, f)
Logger.log("i", "Package management file %s is saved", self._package_management_file_path)
json.dump(data_dict, f, sort_keys = True, indent = 4)
Logger.log("i", "Package management file %s was saved", self._user_package_management_file_path)
# (for initialize) Removes all packages that have been scheduled to be removed.
def _removeAllScheduledPackages(self) -> None:
@ -84,10 +100,13 @@ class CuraPackageManager(QObject):
# (for initialize) Installs all packages that have been scheduled to be installed.
def _installAllScheduledPackages(self) -> None:
for package_id, installation_package_data in self._to_install_package_dict.items():
self._installPackage(installation_package_data)
self._to_install_package_dict.clear()
self._saveManagementData()
while self._to_install_package_dict:
package_id, package_info = list(self._to_install_package_dict.items())[0]
self._installPackage(package_info)
self._installed_package_dict[package_id] = self._to_install_package_dict[package_id]
del self._to_install_package_dict[package_id]
self._saveManagementData()
# Checks the given package is installed. If so, return a dictionary that contains the package's information.
def getInstalledPackageInfo(self, package_id: str) -> Optional[dict]:
@ -99,64 +118,66 @@ class CuraPackageManager(QObject):
return package_info
if package_id in self._installed_package_dict:
package_info = self._installed_package_dict.get(package_id)
package_info = self._installed_package_dict[package_id]["package_info"]
return package_info
for section, packages in self.getAllInstalledPackagesInfo().items():
for package in packages:
if package["package_id"] == package_id:
return package
if package_id in self._bundled_package_dict:
package_info = self._bundled_package_dict[package_id]["package_info"]
return package_info
return None
def getAllInstalledPackagesInfo(self) -> dict:
installed_package_id_set = set(self._installed_package_dict.keys()) | set(self._to_install_package_dict.keys())
installed_package_id_set = installed_package_id_set.difference(self._to_remove_package_set)
managed_package_id_set = installed_package_id_set | self._to_remove_package_set
# Add bundled, installed, and to-install packages to the set of installed package IDs
all_installed_ids = set()
# TODO: For absolutely no reason, this function seems to run in a loop
# even though no loop is ever called with it.
if self._bundled_package_dict.keys():
all_installed_ids = all_installed_ids.union(set(self._bundled_package_dict.keys()))
if self._installed_package_dict.keys():
all_installed_ids = all_installed_ids.union(set(self._installed_package_dict.keys()))
if self._to_install_package_dict.keys():
all_installed_ids = all_installed_ids.union(set(self._to_install_package_dict.keys()))
all_installed_ids = all_installed_ids.difference(self._to_remove_package_set)
# map of <package_type> -> <package_id> -> <package_info>
installed_packages_dict = {}
for package_id in installed_package_id_set:
for package_id in all_installed_ids:
# Skip required plugins as they should not be tampered with
if package_id in Application.getInstance().getRequiredPlugins():
continue
# Add bundled plugins
if package_id in self._bundled_package_dict:
package_info = self._bundled_package_dict[package_id]["package_info"]
# Add installed plugins
if package_id in self._installed_package_dict:
package_info = self._installed_package_dict[package_id]["package_info"]
# Add to install plugins
if package_id in self._to_install_package_dict:
package_info = self._to_install_package_dict[package_id]["package_info"]
else:
package_info = self._installed_package_dict[package_id]
package_info["is_bundled"] = False
package_type = package_info["package_type"]
if package_type not in installed_packages_dict:
installed_packages_dict[package_type] = []
installed_packages_dict[package_type].append( package_info )
# We also need to get information from the plugin registry such as if a plugin is active
package_info["is_active"] = self._plugin_registry.isActivePlugin(package_id)
if package_info["package_type"] == "plugin":
package_info["is_active"] = self._plugin_registry.isActivePlugin(package_id)
else:
package_info["is_active"] = self._plugin_registry.isActivePlugin(package_id)
# Also get all bundled plugins
all_metadata = self._plugin_registry.getAllMetaData()
for item in all_metadata:
if item == {}:
continue
# If the package ID is in bundled, label it as such
if package_info["package_id"] in self._bundled_package_dict.keys():
package_info["is_bundled"] = True
else:
package_info["is_bundled"] = False
plugin_package_info = self.__convertPluginMetadataToPackageMetadata(item)
# Only gather the bundled plugins here.
package_id = plugin_package_info["package_id"]
if package_id in managed_package_id_set:
continue
if package_id in Application.getInstance().getRequiredPlugins():
continue
plugin_package_info["is_bundled"] = True if plugin_package_info["author"]["display_name"] == "Ultimaker B.V." else False
plugin_package_info["is_active"] = self._plugin_registry.isActivePlugin(package_id)
package_type = "plugin"
if package_type not in installed_packages_dict:
installed_packages_dict[package_type] = []
installed_packages_dict[package_type].append( plugin_package_info )
# If there is not a section in the dict for this type, add it
if package_info["package_type"] not in installed_packages_dict:
installed_packages_dict[package_info["package_type"]] = []
# Finally, add the data
installed_packages_dict[package_info["package_type"]].append( package_info )
return installed_packages_dict
@ -176,7 +197,7 @@ class CuraPackageManager(QObject):
"email": "",
"website": "",
},
"tags": ["plugin"],
"tags": ["plugin"]
}
return package_metadata