use bytes to pass backup file around, generate in memory, small fixes

This commit is contained in:
ChrisTerBeke 2018-05-07 15:40:47 +02:00
parent 64819d517e
commit 936de402ec
3 changed files with 17 additions and 20 deletions

View file

@ -1,7 +1,5 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from zipfile import ZipFile
from cura.Backups.BackupsManager import BackupsManager from cura.Backups.BackupsManager import BackupsManager
@ -18,14 +16,14 @@ class Backups:
manager = BackupsManager() # Re-used instance of the backups manager. manager = BackupsManager() # Re-used instance of the backups manager.
def createBackup(self) -> ("ZipFile", dict): def createBackup(self) -> (bytes, dict):
""" """
Create a new backup using the BackupsManager. Create a new backup using the BackupsManager.
:return: Tuple containing a ZIP file with the backup data and a dict with meta data about the backup. :return: Tuple containing a ZIP file with the backup data and a dict with meta data about the backup.
""" """
return self.manager.createBackup() return self.manager.createBackup()
def restoreBackup(self, zip_file: "ZipFile", meta_data: dict) -> None: def restoreBackup(self, zip_file: bytes, meta_data: dict) -> None:
""" """
Restore a backup using the BackupManager. Restore a backup using the BackupManager.
:param zip_file: A ZIP file containing the actual backup data. :param zip_file: A ZIP file containing the actual backup data.

View file

@ -1,7 +1,7 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
import io
import os import os
from datetime import datetime
from typing import Optional from typing import Optional
from zipfile import ZipFile, ZIP_DEFLATED, BadZipfile from zipfile import ZipFile, ZIP_DEFLATED, BadZipfile
@ -16,9 +16,9 @@ class Backup:
It is also responsible for reading and writing the zip file to the user data folder. It is also responsible for reading and writing the zip file to the user data folder.
""" """
def __init__(self, zip_file: "ZipFile" = None, meta_data: dict = None): def __init__(self, zip_file: bytes = None, meta_data: dict = None):
self.zip_file = zip_file # type: Optional[ZipFile] self.zip_file = zip_file # type: Optional[bytes]
self.meta_data = meta_data # type: Optional[dict self.meta_data = meta_data # type: Optional[dict]
def makeFromCurrent(self) -> (bool, Optional[str]): def makeFromCurrent(self) -> (bool, Optional[str]):
""" """
@ -26,13 +26,12 @@ class Backup:
""" """
cura_release = CuraApplication.getInstance().getVersion() cura_release = CuraApplication.getInstance().getVersion()
version_data_dir = Resources.getDataStoragePath() version_data_dir = Resources.getDataStoragePath()
timestamp = datetime.now().isoformat()
Logger.log("d", "Creating backup for Cura %s, using folder %s", cura_release, version_data_dir) Logger.log("d", "Creating backup for Cura %s, using folder %s", cura_release, version_data_dir)
# We're using an easy to parse filename for when we're restoring edge cases: # We're using an easy to parse filename for when we're restoring edge cases:
# TIMESTAMP.backup.VERSION.cura.zip # TIMESTAMP.backup.VERSION.cura.zip
archive = self._makeArchive("{}.backup.{}.cura.zip".format(timestamp, cura_release), version_data_dir) archive = self._makeArchive(version_data_dir)
self.zip_file = archive self.zip_file = archive
self.meta_data = { self.meta_data = {
@ -41,30 +40,30 @@ class Backup:
# TODO: fill meta data with machine/material/etc counts. # TODO: fill meta data with machine/material/etc counts.
@staticmethod @staticmethod
def _makeArchive(root_path: str, archive_name: str) -> Optional[ZipFile]: def _makeArchive(root_path: str) -> Optional[bytes]:
""" """
Make a full archive from the given root path with the given name. Make a full archive from the given root path with the given name.
:param root_path: The root directory to archive recursively. :param root_path: The root directory to archive recursively.
:param archive_name: The name of the archive to create. :return: The archive as bytes.
:return: The archive as ZipFile.
""" """
parent_folder = os.path.dirname(root_path) parent_folder = os.path.dirname(root_path)
contents = os.walk(root_path) contents = os.walk(root_path)
try: try:
archive = ZipFile(archive_name, "w", ZIP_DEFLATED) buffer = io.BytesIO()
archive = ZipFile(buffer, "w", ZIP_DEFLATED)
for root, folders, files in contents: for root, folders, files in contents:
for folder_name in folders: for folder_name in folders:
# Add all folders, even empty ones. # Add all folders, even empty ones.
absolute_path = os.path.join(root, folder_name) absolute_path = os.path.join(root, folder_name)
relative_path = absolute_path.replace(parent_folder + '\\', '') relative_path = absolute_path.replace(parent_folder + '\\', '')
archive.write(absolute_path, relative_path) archive.write(relative_path)
for file_name in files: for file_name in files:
# Add all files. # Add all files.
absolute_path = os.path.join(root, file_name) absolute_path = os.path.join(root, file_name)
relative_path = absolute_path.replace(parent_folder + '\\', '') relative_path = absolute_path.replace(parent_folder + '\\', '')
archive.write(absolute_path, relative_path) archive.write(relative_path)
archive.close() archive.close()
return archive return buffer.getvalue()
except (IOError, OSError, BadZipfile) as error: except (IOError, OSError, BadZipfile) as error:
Logger.log("e", "Could not create archive from user data directory: %s", error) Logger.log("e", "Could not create archive from user data directory: %s", error)
return None return None

View file

@ -1,6 +1,6 @@
# Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from zipfile import ZipFile from typing import Optional
from UM.Logger import Logger from UM.Logger import Logger
from cura.Backups.Backup import Backup from cura.Backups.Backup import Backup
@ -12,7 +12,7 @@ class BackupsManager:
Backups themselves are represented in a different class. Backups themselves are represented in a different class.
""" """
def createBackup(self) -> ("ZipFile", dict): def createBackup(self) -> (Optional[bytes], Optional[dict]):
""" """
Get a backup of the current configuration. Get a backup of the current configuration.
:return: A Tuple containing a ZipFile (the actual backup) and a dict containing some meta data (like version). :return: A Tuple containing a ZipFile (the actual backup) and a dict containing some meta data (like version).
@ -24,7 +24,7 @@ class BackupsManager:
# We don't return a Backup here because we want plugins only to interact with our API and not full objects. # We don't return a Backup here because we want plugins only to interact with our API and not full objects.
return backup.zip_file, backup.meta_data return backup.zip_file, backup.meta_data
def restoreBackup(self, zip_file: "ZipFile", meta_data: dict) -> None: def restoreBackup(self, zip_file: bytes, meta_data: dict) -> None:
""" """
Restore a backup from a given ZipFile. Restore a backup from a given ZipFile.
:param zip_file: A ZipFile containing the actual backup. :param zip_file: A ZipFile containing the actual backup.