add/update and extend various comments

This commit is contained in:
Andreas Zweili 2019-02-10 17:21:41 +01:00
parent 4c3fb85fcf
commit ab26d952ed
4 changed files with 63 additions and 9 deletions

View File

@ -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

View File

@ -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!")

View File

@ -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)

View File

@ -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.