add/update and extend various comments
This commit is contained in:
parent
4c3fb85fcf
commit
ab26d952ed
|
@ -9,15 +9,22 @@ from helper import BorgException
|
|||
|
||||
|
||||
class BorgQtThread(QThread):
|
||||
"""Provides the base for interfacing with borg. The method
|
||||
self.create_command needs to be implemented on each child class in order to
|
||||
make it work."""
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.create_process()
|
||||
|
||||
def stop(self):
|
||||
"""Kill the process when the thread stops."""
|
||||
self.p.kill()
|
||||
self.json_err = None
|
||||
|
||||
def create_process(self):
|
||||
"""Creates the process which executes borg."""
|
||||
|
||||
# self.create_command() needs to be implemented on each subclass.
|
||||
self.create_command()
|
||||
self.p = subprocess.Popen(self.command,
|
||||
stdout=subprocess.PIPE,
|
||||
|
@ -30,6 +37,9 @@ class BorgQtThread(QThread):
|
|||
self.process_json_error(self.json_err)
|
||||
|
||||
def process_json_error(self, json_err):
|
||||
"""Looks in the returned json error string for errors and provides them
|
||||
as BorgException in case there are any. Ignores errors about stale
|
||||
locks of borg."""
|
||||
if json_err:
|
||||
error = json_err.splitlines()[0]
|
||||
if 'stale' in error:
|
||||
|
@ -40,6 +50,7 @@ class BorgQtThread(QThread):
|
|||
|
||||
|
||||
class ListThread(BorgQtThread):
|
||||
"""Returns a list of all archives in the repository."""
|
||||
def create_command(self):
|
||||
self.command = ['borg', 'list', '--log-json', '--json']
|
||||
|
||||
|
@ -57,6 +68,7 @@ class ListThread(BorgQtThread):
|
|||
|
||||
|
||||
class InfoThread(BorgQtThread):
|
||||
"""Return the statistics about the current repository."""
|
||||
def create_command(self):
|
||||
self.command = ['borg', 'info', '--log-json', '--json']
|
||||
|
||||
|
@ -72,7 +84,7 @@ class InfoThread(BorgQtThread):
|
|||
|
||||
|
||||
class BackupThread(BorgQtThread):
|
||||
"""A class to create a backup with borg.
|
||||
"""Creates a backup with borg.
|
||||
|
||||
Args:
|
||||
prefix (str) the prefix for the archive name.
|
||||
|
@ -95,12 +107,14 @@ class BackupThread(BorgQtThread):
|
|||
self.command.extend(self.excludes)
|
||||
|
||||
def _process_prefix(self, prefix):
|
||||
"""Prepares the prefix for the final command."""
|
||||
if prefix:
|
||||
self.prefix = prefix + "_"
|
||||
else:
|
||||
self.prefix = ""
|
||||
|
||||
def _process_excludes(self, excludes):
|
||||
"""Pairs every exclude with the required option for borg."""
|
||||
processed_items = []
|
||||
if excludes:
|
||||
for item in excludes:
|
||||
|
@ -111,7 +125,7 @@ class BackupThread(BorgQtThread):
|
|||
|
||||
|
||||
class RestoreThread(BorgQtThread):
|
||||
"""A lass to restore a backup with borg.
|
||||
"""Restores a backup with borg.
|
||||
|
||||
Args:
|
||||
archive_name (str) the name of the archive to restore.
|
||||
|
@ -127,6 +141,9 @@ class RestoreThread(BorgQtThread):
|
|||
('::' + self.archive_name)]
|
||||
|
||||
def create_process(self):
|
||||
"""The create_process needs to get overwritten because borg restores
|
||||
the archive into the current folder. Therefore the process needs to cd
|
||||
into the target path."""
|
||||
self.create_command()
|
||||
self.p = subprocess.Popen(self.command,
|
||||
cwd=self.restore_path,
|
||||
|
@ -137,10 +154,10 @@ class RestoreThread(BorgQtThread):
|
|||
|
||||
|
||||
class DeleteThread(BorgQtThread):
|
||||
"""A lass to restore a backup with borg.
|
||||
"""Deletes an archive from the repository.
|
||||
|
||||
Args:
|
||||
archive_name (str) the name of the archive to restore.
|
||||
archive_name (str) the name of the archive to delete.
|
||||
"""
|
||||
def __init__(self, archive_name):
|
||||
self.archive_name = archive_name
|
||||
|
@ -152,10 +169,11 @@ class DeleteThread(BorgQtThread):
|
|||
|
||||
|
||||
class MountThread(BorgQtThread):
|
||||
"""A lass to restore a backup with borg.
|
||||
"""Mounts an archive at the given path.
|
||||
|
||||
Args:
|
||||
archive_name (str) the name of the archive to restore.
|
||||
mount_path (str) the target path to mount the archive at.
|
||||
"""
|
||||
def __init__(self, archive_name, mount_path):
|
||||
self.archive_name = archive_name
|
||||
|
|
|
@ -33,23 +33,38 @@ def convert_size(size_bytes):
|
|||
|
||||
|
||||
def open_path(target_path):
|
||||
"""Opens the file manager at the given location."""
|
||||
"""Opens the file manager at the given location.
|
||||
|
||||
Args:
|
||||
target_path (str) The path to open in the file manager."""
|
||||
if os.path.exists(target_path):
|
||||
QDesktopServices.openUrl(QUrl.fromLocalFile(
|
||||
os.path.abspath(target_path)))
|
||||
|
||||
|
||||
def create_path(path):
|
||||
"""Creates the given path.
|
||||
|
||||
Args:
|
||||
path (str) The path to create."""
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
|
||||
def remove_path(path):
|
||||
"""Removes the given path recursively.
|
||||
|
||||
Args:
|
||||
path (str) The path to delete."""
|
||||
if os.path.exists(path):
|
||||
shutil.rmtree(path)
|
||||
|
||||
|
||||
def check_path(path):
|
||||
"""Checks if the given path is writeable.
|
||||
|
||||
Args:
|
||||
path (str) The path to check."""
|
||||
if os.access(path, os.W_OK):
|
||||
return True
|
||||
exception = Exception("The selected path isn't writeable!")
|
||||
|
|
|
@ -72,6 +72,8 @@ class MainWindow(QMainWindow):
|
|||
|
||||
def closeEvent(self, *args, **kwargs):
|
||||
super(QMainWindow, self).closeEvent(*args, **kwargs)
|
||||
# When the application gets close unmount all the archives and remove
|
||||
# their paths.
|
||||
if self.mount_paths:
|
||||
for path in self.mount_paths:
|
||||
if os.path.exists(path):
|
||||
|
@ -88,6 +90,7 @@ class MainWindow(QMainWindow):
|
|||
self.src_path = self.treeview_files.model().filePath(signal)
|
||||
|
||||
def _check_path(self):
|
||||
"""Makes sure that the user selected a path to backup."""
|
||||
message = ("Please select a file or directory "
|
||||
"before creating a backup.")
|
||||
if not hasattr(self, 'src_path'):
|
||||
|
@ -130,6 +133,8 @@ class MainWindow(QMainWindow):
|
|||
target_path = None
|
||||
show_error(error)
|
||||
|
||||
# Only restore the backup if the target is writeable and the archive
|
||||
# was selected.
|
||||
if check_path(target_path) and archive_name:
|
||||
try:
|
||||
restore_path = os.path.join(target_path, archive_name)
|
||||
|
@ -153,7 +158,9 @@ class MainWindow(QMainWindow):
|
|||
archive_name = None
|
||||
show_error(error)
|
||||
|
||||
# Only continue if an archive was selected.
|
||||
if archive_name:
|
||||
# Prompt the user before continuing.
|
||||
if self.yes_no("Do you want to delete this archive?"):
|
||||
try:
|
||||
thread = borg.DeleteThread(archive_name)
|
||||
|
@ -175,7 +182,7 @@ class MainWindow(QMainWindow):
|
|||
self.list_archive.addItems(archive_names)
|
||||
|
||||
def update_ui(self):
|
||||
"""Lists all the archive names in the UI."""
|
||||
"""Updates the archive list and repository stats in the UI."""
|
||||
try:
|
||||
self._update_archives()
|
||||
self._update_repository_stats()
|
||||
|
@ -183,6 +190,8 @@ class MainWindow(QMainWindow):
|
|||
show_error(e)
|
||||
|
||||
def _update_repository_stats(self):
|
||||
"""Update the repository stats and display them in a human readable
|
||||
format."""
|
||||
thread = borg.InfoThread()
|
||||
stats = thread.run()
|
||||
self.label_repo_original_size.setText(
|
||||
|
@ -198,6 +207,8 @@ class MainWindow(QMainWindow):
|
|||
+ convert_size(stats['unique_csize']))
|
||||
|
||||
def mount_backup(self):
|
||||
"""Mount the selected archive in the tmp directory. If it succeeds the
|
||||
mount_path gets written to a property of the main_window."""
|
||||
try:
|
||||
archive_name = self.selected_archive
|
||||
except AttributeError:
|
||||
|
@ -205,9 +216,11 @@ class MainWindow(QMainWindow):
|
|||
archive_name = None
|
||||
show_error(error)
|
||||
|
||||
# only continue if the user selected an archive
|
||||
if archive_name:
|
||||
mount_path = os.path.join('/tmp/', archive_name)
|
||||
create_path(mount_path)
|
||||
# only continue if the mount_path is writeable
|
||||
if os.access(mount_path, os.W_OK):
|
||||
thread = borg.MountThread(archive_name, mount_path)
|
||||
try:
|
||||
|
@ -218,9 +231,14 @@ class MainWindow(QMainWindow):
|
|||
show_error(e)
|
||||
remove_path(mount_path)
|
||||
else:
|
||||
# Opens the path in a file manager
|
||||
open_path(mount_path)
|
||||
|
||||
def yes_no(self, question):
|
||||
"""Simple yes/no dialog.
|
||||
|
||||
Args:
|
||||
question (str) The question to display to the user."""
|
||||
button_reply = QMessageBox.question(self, 'Borg-Qt', question,
|
||||
QMessageBox.Yes |
|
||||
QMessageBox.No, QMessageBox.No)
|
||||
|
|
|
@ -6,8 +6,11 @@ from PyQt5 import uic
|
|||
|
||||
|
||||
class ProgressDialog(QDialog):
|
||||
"""The main window of the application. It provides the various functions to
|
||||
control BorgBackup."""
|
||||
"""Displays a progress dialog while a thread is running. When the thread
|
||||
stops, the dialog disappears.
|
||||
|
||||
Args:
|
||||
thread (thread) the thread to execute."""
|
||||
def __init__(self, thread):
|
||||
super(ProgressDialog, self).__init__()
|
||||
# Load the UI file to get the dialogs layout.
|
||||
|
|
Loading…
Reference in New Issue